Home | History | Annotate | Line # | Download | only in Remarks
      1 //===- YAMLRemarkParser.cpp -----------------------------------------------===//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 //
      9 // This file provides utility methods used by clients that want to use the
     10 // parser for remark diagnostics in LLVM.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "YAMLRemarkParser.h"
     15 #include "llvm/ADT/StringSwitch.h"
     16 #include "llvm/Support/Endian.h"
     17 #include "llvm/Support/Path.h"
     18 
     19 using namespace llvm;
     20 using namespace llvm::remarks;
     21 
     22 char YAMLParseError::ID = 0;
     23 
     24 static void handleDiagnostic(const SMDiagnostic &Diag, void *Ctx) {
     25   assert(Ctx && "Expected non-null Ctx in diagnostic handler.");
     26   std::string &Message = *static_cast<std::string *>(Ctx);
     27   assert(Message.empty() && "Expected an empty string.");
     28   raw_string_ostream OS(Message);
     29   Diag.print(/*ProgName=*/nullptr, OS, /*ShowColors*/ false,
     30              /*ShowKindLabels*/ true);
     31   OS << '\n';
     32   OS.flush();
     33 }
     34 
     35 YAMLParseError::YAMLParseError(StringRef Msg, SourceMgr &SM,
     36                                yaml::Stream &Stream, yaml::Node &Node) {
     37   // 1) Set up a diagnostic handler to avoid errors being printed out to
     38   // stderr.
     39   // 2) Use the stream to print the error with the associated node.
     40   // 3) The stream will use the source manager to print the error, which will
     41   // call the diagnostic handler.
     42   // 4) The diagnostic handler will stream the error directly into this object's
     43   // Message member, which is used when logging is asked for.
     44   auto OldDiagHandler = SM.getDiagHandler();
     45   auto OldDiagCtx = SM.getDiagContext();
     46   SM.setDiagHandler(handleDiagnostic, &Message);
     47   Stream.printError(&Node, Twine(Msg) + Twine('\n'));
     48   // Restore the old handlers.
     49   SM.setDiagHandler(OldDiagHandler, OldDiagCtx);
     50 }
     51 
     52 static SourceMgr setupSM(std::string &LastErrorMessage) {
     53   SourceMgr SM;
     54   SM.setDiagHandler(handleDiagnostic, &LastErrorMessage);
     55   return SM;
     56 }
     57 
     58 // Parse the magic number. This function returns true if this represents remark
     59 // metadata, false otherwise.
     60 static Expected<bool> parseMagic(StringRef &Buf) {
     61   if (!Buf.consume_front(remarks::Magic))
     62     return false;
     63 
     64   if (Buf.size() < 1 || !Buf.consume_front(StringRef("\0", 1)))
     65     return createStringError(std::errc::illegal_byte_sequence,
     66                              "Expecting \\0 after magic number.");
     67   return true;
     68 }
     69 
     70 static Expected<uint64_t> parseVersion(StringRef &Buf) {
     71   if (Buf.size() < sizeof(uint64_t))
     72     return createStringError(std::errc::illegal_byte_sequence,
     73                              "Expecting version number.");
     74 
     75   uint64_t Version =
     76       support::endian::read<uint64_t, support::little, support::unaligned>(
     77           Buf.data());
     78   if (Version != remarks::CurrentRemarkVersion)
     79     return createStringError(std::errc::illegal_byte_sequence,
     80                              "Mismatching remark version. Got %" PRId64
     81                              ", expected %" PRId64 ".",
     82                              Version, remarks::CurrentRemarkVersion);
     83   Buf = Buf.drop_front(sizeof(uint64_t));
     84   return Version;
     85 }
     86 
     87 static Expected<uint64_t> parseStrTabSize(StringRef &Buf) {
     88   if (Buf.size() < sizeof(uint64_t))
     89     return createStringError(std::errc::illegal_byte_sequence,
     90                              "Expecting string table size.");
     91   uint64_t StrTabSize =
     92       support::endian::read<uint64_t, support::little, support::unaligned>(
     93           Buf.data());
     94   Buf = Buf.drop_front(sizeof(uint64_t));
     95   return StrTabSize;
     96 }
     97 
     98 static Expected<ParsedStringTable> parseStrTab(StringRef &Buf,
     99                                                uint64_t StrTabSize) {
    100   if (Buf.size() < StrTabSize)
    101     return createStringError(std::errc::illegal_byte_sequence,
    102                              "Expecting string table.");
    103 
    104   // Attach the string table to the parser.
    105   ParsedStringTable Result(StringRef(Buf.data(), StrTabSize));
    106   Buf = Buf.drop_front(StrTabSize);
    107   return Expected<ParsedStringTable>(std::move(Result));
    108 }
    109 
    110 Expected<std::unique_ptr<YAMLRemarkParser>>
    111 remarks::createYAMLParserFromMeta(StringRef Buf,
    112                                   Optional<ParsedStringTable> StrTab,
    113                                   Optional<StringRef> ExternalFilePrependPath) {
    114   // We now have a magic number. The metadata has to be correct.
    115   Expected<bool> isMeta = parseMagic(Buf);
    116   if (!isMeta)
    117     return isMeta.takeError();
    118   // If it's not recognized as metadata, roll back.
    119   std::unique_ptr<MemoryBuffer> SeparateBuf;
    120   if (*isMeta) {
    121     Expected<uint64_t> Version = parseVersion(Buf);
    122     if (!Version)
    123       return Version.takeError();
    124 
    125     Expected<uint64_t> StrTabSize = parseStrTabSize(Buf);
    126     if (!StrTabSize)
    127       return StrTabSize.takeError();
    128 
    129     // If the size of string table is not 0, try to build one.
    130     if (*StrTabSize != 0) {
    131       if (StrTab)
    132         return createStringError(std::errc::illegal_byte_sequence,
    133                                  "String table already provided.");
    134       Expected<ParsedStringTable> MaybeStrTab = parseStrTab(Buf, *StrTabSize);
    135       if (!MaybeStrTab)
    136         return MaybeStrTab.takeError();
    137       StrTab = std::move(*MaybeStrTab);
    138     }
    139     // If it starts with "---", there is no external file.
    140     if (!Buf.startswith("---")) {
    141       // At this point, we expect Buf to contain the external file path.
    142       StringRef ExternalFilePath = Buf;
    143       SmallString<80> FullPath;
    144       if (ExternalFilePrependPath)
    145         FullPath = *ExternalFilePrependPath;
    146       sys::path::append(FullPath, ExternalFilePath);
    147 
    148       // Try to open the file and start parsing from there.
    149       ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
    150           MemoryBuffer::getFile(FullPath);
    151       if (std::error_code EC = BufferOrErr.getError())
    152         return createFileError(FullPath, EC);
    153 
    154       // Keep the buffer alive.
    155       SeparateBuf = std::move(*BufferOrErr);
    156       Buf = SeparateBuf->getBuffer();
    157     }
    158   }
    159 
    160   std::unique_ptr<YAMLRemarkParser> Result =
    161       StrTab
    162           ? std::make_unique<YAMLStrTabRemarkParser>(Buf, std::move(*StrTab))
    163           : std::make_unique<YAMLRemarkParser>(Buf);
    164   if (SeparateBuf)
    165     Result->SeparateBuf = std::move(SeparateBuf);
    166   return std::move(Result);
    167 }
    168 
    169 YAMLRemarkParser::YAMLRemarkParser(StringRef Buf)
    170     : YAMLRemarkParser(Buf, None) {}
    171 
    172 YAMLRemarkParser::YAMLRemarkParser(StringRef Buf,
    173                                    Optional<ParsedStringTable> StrTab)
    174     : RemarkParser{Format::YAML}, StrTab(std::move(StrTab)), LastErrorMessage(),
    175       SM(setupSM(LastErrorMessage)), Stream(Buf, SM), YAMLIt(Stream.begin()) {}
    176 
    177 Error YAMLRemarkParser::error(StringRef Message, yaml::Node &Node) {
    178   return make_error<YAMLParseError>(Message, SM, Stream, Node);
    179 }
    180 
    181 Error YAMLRemarkParser::error() {
    182   if (LastErrorMessage.empty())
    183     return Error::success();
    184   Error E = make_error<YAMLParseError>(LastErrorMessage);
    185   LastErrorMessage.clear();
    186   return E;
    187 }
    188 
    189 Expected<std::unique_ptr<Remark>>
    190 YAMLRemarkParser::parseRemark(yaml::Document &RemarkEntry) {
    191   if (Error E = error())
    192     return std::move(E);
    193 
    194   yaml::Node *YAMLRoot = RemarkEntry.getRoot();
    195   if (!YAMLRoot) {
    196     return createStringError(std::make_error_code(std::errc::invalid_argument),
    197                              "not a valid YAML file.");
    198   }
    199 
    200   auto *Root = dyn_cast<yaml::MappingNode>(YAMLRoot);
    201   if (!Root)
    202     return error("document root is not of mapping type.", *YAMLRoot);
    203 
    204   std::unique_ptr<Remark> Result = std::make_unique<Remark>();
    205   Remark &TheRemark = *Result;
    206 
    207   // First, the type. It needs special handling since is not part of the
    208   // key-value stream.
    209   Expected<Type> T = parseType(*Root);
    210   if (!T)
    211     return T.takeError();
    212   else
    213     TheRemark.RemarkType = *T;
    214 
    215   // Then, parse the fields, one by one.
    216   for (yaml::KeyValueNode &RemarkField : *Root) {
    217     Expected<StringRef> MaybeKey = parseKey(RemarkField);
    218     if (!MaybeKey)
    219       return MaybeKey.takeError();
    220     StringRef KeyName = *MaybeKey;
    221 
    222     if (KeyName == "Pass") {
    223       if (Expected<StringRef> MaybeStr = parseStr(RemarkField))
    224         TheRemark.PassName = *MaybeStr;
    225       else
    226         return MaybeStr.takeError();
    227     } else if (KeyName == "Name") {
    228       if (Expected<StringRef> MaybeStr = parseStr(RemarkField))
    229         TheRemark.RemarkName = *MaybeStr;
    230       else
    231         return MaybeStr.takeError();
    232     } else if (KeyName == "Function") {
    233       if (Expected<StringRef> MaybeStr = parseStr(RemarkField))
    234         TheRemark.FunctionName = *MaybeStr;
    235       else
    236         return MaybeStr.takeError();
    237     } else if (KeyName == "Hotness") {
    238       if (Expected<unsigned> MaybeU = parseUnsigned(RemarkField))
    239         TheRemark.Hotness = *MaybeU;
    240       else
    241         return MaybeU.takeError();
    242     } else if (KeyName == "DebugLoc") {
    243       if (Expected<RemarkLocation> MaybeLoc = parseDebugLoc(RemarkField))
    244         TheRemark.Loc = *MaybeLoc;
    245       else
    246         return MaybeLoc.takeError();
    247     } else if (KeyName == "Args") {
    248       auto *Args = dyn_cast<yaml::SequenceNode>(RemarkField.getValue());
    249       if (!Args)
    250         return error("wrong value type for key.", RemarkField);
    251 
    252       for (yaml::Node &Arg : *Args) {
    253         if (Expected<Argument> MaybeArg = parseArg(Arg))
    254           TheRemark.Args.push_back(*MaybeArg);
    255         else
    256           return MaybeArg.takeError();
    257       }
    258     } else {
    259       return error("unknown key.", RemarkField);
    260     }
    261   }
    262 
    263   // Check if any of the mandatory fields are missing.
    264   if (TheRemark.RemarkType == Type::Unknown || TheRemark.PassName.empty() ||
    265       TheRemark.RemarkName.empty() || TheRemark.FunctionName.empty())
    266     return error("Type, Pass, Name or Function missing.",
    267                  *RemarkEntry.getRoot());
    268 
    269   return std::move(Result);
    270 }
    271 
    272 Expected<Type> YAMLRemarkParser::parseType(yaml::MappingNode &Node) {
    273   auto Type = StringSwitch<remarks::Type>(Node.getRawTag())
    274                   .Case("!Passed", remarks::Type::Passed)
    275                   .Case("!Missed", remarks::Type::Missed)
    276                   .Case("!Analysis", remarks::Type::Analysis)
    277                   .Case("!AnalysisFPCommute", remarks::Type::AnalysisFPCommute)
    278                   .Case("!AnalysisAliasing", remarks::Type::AnalysisAliasing)
    279                   .Case("!Failure", remarks::Type::Failure)
    280                   .Default(remarks::Type::Unknown);
    281   if (Type == remarks::Type::Unknown)
    282     return error("expected a remark tag.", Node);
    283   return Type;
    284 }
    285 
    286 Expected<StringRef> YAMLRemarkParser::parseKey(yaml::KeyValueNode &Node) {
    287   if (auto *Key = dyn_cast<yaml::ScalarNode>(Node.getKey()))
    288     return Key->getRawValue();
    289 
    290   return error("key is not a string.", Node);
    291 }
    292 
    293 Expected<StringRef> YAMLRemarkParser::parseStr(yaml::KeyValueNode &Node) {
    294   auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue());
    295   if (!Value)
    296     return error("expected a value of scalar type.", Node);
    297   StringRef Result = Value->getRawValue();
    298 
    299   if (Result.front() == '\'')
    300     Result = Result.drop_front();
    301 
    302   if (Result.back() == '\'')
    303     Result = Result.drop_back();
    304 
    305   return Result;
    306 }
    307 
    308 Expected<unsigned> YAMLRemarkParser::parseUnsigned(yaml::KeyValueNode &Node) {
    309   SmallVector<char, 4> Tmp;
    310   auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue());
    311   if (!Value)
    312     return error("expected a value of scalar type.", Node);
    313   unsigned UnsignedValue = 0;
    314   if (Value->getValue(Tmp).getAsInteger(10, UnsignedValue))
    315     return error("expected a value of integer type.", *Value);
    316   return UnsignedValue;
    317 }
    318 
    319 Expected<RemarkLocation>
    320 YAMLRemarkParser::parseDebugLoc(yaml::KeyValueNode &Node) {
    321   auto *DebugLoc = dyn_cast<yaml::MappingNode>(Node.getValue());
    322   if (!DebugLoc)
    323     return error("expected a value of mapping type.", Node);
    324 
    325   Optional<StringRef> File;
    326   Optional<unsigned> Line;
    327   Optional<unsigned> Column;
    328 
    329   for (yaml::KeyValueNode &DLNode : *DebugLoc) {
    330     Expected<StringRef> MaybeKey = parseKey(DLNode);
    331     if (!MaybeKey)
    332       return MaybeKey.takeError();
    333     StringRef KeyName = *MaybeKey;
    334 
    335     if (KeyName == "File") {
    336       if (Expected<StringRef> MaybeStr = parseStr(DLNode))
    337         File = *MaybeStr;
    338       else
    339         return MaybeStr.takeError();
    340     } else if (KeyName == "Column") {
    341       if (Expected<unsigned> MaybeU = parseUnsigned(DLNode))
    342         Column = *MaybeU;
    343       else
    344         return MaybeU.takeError();
    345     } else if (KeyName == "Line") {
    346       if (Expected<unsigned> MaybeU = parseUnsigned(DLNode))
    347         Line = *MaybeU;
    348       else
    349         return MaybeU.takeError();
    350     } else {
    351       return error("unknown entry in DebugLoc map.", DLNode);
    352     }
    353   }
    354 
    355   // If any of the debug loc fields is missing, return an error.
    356   if (!File || !Line || !Column)
    357     return error("DebugLoc node incomplete.", Node);
    358 
    359   return RemarkLocation{*File, *Line, *Column};
    360 }
    361 
    362 Expected<Argument> YAMLRemarkParser::parseArg(yaml::Node &Node) {
    363   auto *ArgMap = dyn_cast<yaml::MappingNode>(&Node);
    364   if (!ArgMap)
    365     return error("expected a value of mapping type.", Node);
    366 
    367   Optional<StringRef> KeyStr;
    368   Optional<StringRef> ValueStr;
    369   Optional<RemarkLocation> Loc;
    370 
    371   for (yaml::KeyValueNode &ArgEntry : *ArgMap) {
    372     Expected<StringRef> MaybeKey = parseKey(ArgEntry);
    373     if (!MaybeKey)
    374       return MaybeKey.takeError();
    375     StringRef KeyName = *MaybeKey;
    376 
    377     // Try to parse debug locs.
    378     if (KeyName == "DebugLoc") {
    379       // Can't have multiple DebugLoc entries per argument.
    380       if (Loc)
    381         return error("only one DebugLoc entry is allowed per argument.",
    382                      ArgEntry);
    383 
    384       if (Expected<RemarkLocation> MaybeLoc = parseDebugLoc(ArgEntry)) {
    385         Loc = *MaybeLoc;
    386         continue;
    387       } else
    388         return MaybeLoc.takeError();
    389     }
    390 
    391     // If we already have a string, error out.
    392     if (ValueStr)
    393       return error("only one string entry is allowed per argument.", ArgEntry);
    394 
    395     // Try to parse the value.
    396     if (Expected<StringRef> MaybeStr = parseStr(ArgEntry))
    397       ValueStr = *MaybeStr;
    398     else
    399       return MaybeStr.takeError();
    400 
    401     // Keep the key from the string.
    402     KeyStr = KeyName;
    403   }
    404 
    405   if (!KeyStr)
    406     return error("argument key is missing.", *ArgMap);
    407   if (!ValueStr)
    408     return error("argument value is missing.", *ArgMap);
    409 
    410   return Argument{*KeyStr, *ValueStr, Loc};
    411 }
    412 
    413 Expected<std::unique_ptr<Remark>> YAMLRemarkParser::next() {
    414   if (YAMLIt == Stream.end())
    415     return make_error<EndOfFileError>();
    416 
    417   Expected<std::unique_ptr<Remark>> MaybeResult = parseRemark(*YAMLIt);
    418   if (!MaybeResult) {
    419     // Avoid garbage input, set the iterator to the end.
    420     YAMLIt = Stream.end();
    421     return MaybeResult.takeError();
    422   }
    423 
    424   ++YAMLIt;
    425 
    426   return std::move(*MaybeResult);
    427 }
    428 
    429 Expected<StringRef> YAMLStrTabRemarkParser::parseStr(yaml::KeyValueNode &Node) {
    430   auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue());
    431   if (!Value)
    432     return error("expected a value of scalar type.", Node);
    433   StringRef Result;
    434   // If we have a string table, parse it as an unsigned.
    435   unsigned StrID = 0;
    436   if (Expected<unsigned> MaybeStrID = parseUnsigned(Node))
    437     StrID = *MaybeStrID;
    438   else
    439     return MaybeStrID.takeError();
    440 
    441   if (Expected<StringRef> Str = (*StrTab)[StrID])
    442     Result = *Str;
    443   else
    444     return Str.takeError();
    445 
    446   if (Result.front() == '\'')
    447     Result = Result.drop_front();
    448 
    449   if (Result.back() == '\'')
    450     Result = Result.drop_back();
    451 
    452   return Result;
    453 }
    454