123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- //===- YAMLRemarkParser.cpp -----------------------------------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // This file provides utility methods used by clients that want to use the
- // parser for remark diagnostics in LLVM.
- //
- //===----------------------------------------------------------------------===//
- #include "YAMLRemarkParser.h"
- #include "llvm/ADT/StringSwitch.h"
- #include "llvm/Support/Endian.h"
- #include "llvm/Support/Path.h"
- #include <optional>
- using namespace llvm;
- using namespace llvm::remarks;
- char YAMLParseError::ID = 0;
- static void handleDiagnostic(const SMDiagnostic &Diag, void *Ctx) {
- assert(Ctx && "Expected non-null Ctx in diagnostic handler.");
- std::string &Message = *static_cast<std::string *>(Ctx);
- assert(Message.empty() && "Expected an empty string.");
- raw_string_ostream OS(Message);
- Diag.print(/*ProgName=*/nullptr, OS, /*ShowColors*/ false,
- /*ShowKindLabels*/ true);
- OS << '\n';
- OS.flush();
- }
- YAMLParseError::YAMLParseError(StringRef Msg, SourceMgr &SM,
- yaml::Stream &Stream, yaml::Node &Node) {
- // 1) Set up a diagnostic handler to avoid errors being printed out to
- // stderr.
- // 2) Use the stream to print the error with the associated node.
- // 3) The stream will use the source manager to print the error, which will
- // call the diagnostic handler.
- // 4) The diagnostic handler will stream the error directly into this object's
- // Message member, which is used when logging is asked for.
- auto OldDiagHandler = SM.getDiagHandler();
- auto OldDiagCtx = SM.getDiagContext();
- SM.setDiagHandler(handleDiagnostic, &Message);
- Stream.printError(&Node, Twine(Msg) + Twine('\n'));
- // Restore the old handlers.
- SM.setDiagHandler(OldDiagHandler, OldDiagCtx);
- }
- static SourceMgr setupSM(std::string &LastErrorMessage) {
- SourceMgr SM;
- SM.setDiagHandler(handleDiagnostic, &LastErrorMessage);
- return SM;
- }
- // Parse the magic number. This function returns true if this represents remark
- // metadata, false otherwise.
- static Expected<bool> parseMagic(StringRef &Buf) {
- if (!Buf.consume_front(remarks::Magic))
- return false;
- if (Buf.size() < 1 || !Buf.consume_front(StringRef("\0", 1)))
- return createStringError(std::errc::illegal_byte_sequence,
- "Expecting \\0 after magic number.");
- return true;
- }
- static Expected<uint64_t> parseVersion(StringRef &Buf) {
- if (Buf.size() < sizeof(uint64_t))
- return createStringError(std::errc::illegal_byte_sequence,
- "Expecting version number.");
- uint64_t Version =
- support::endian::read<uint64_t, support::little, support::unaligned>(
- Buf.data());
- if (Version != remarks::CurrentRemarkVersion)
- return createStringError(std::errc::illegal_byte_sequence,
- "Mismatching remark version. Got %" PRId64
- ", expected %" PRId64 ".",
- Version, remarks::CurrentRemarkVersion);
- Buf = Buf.drop_front(sizeof(uint64_t));
- return Version;
- }
- static Expected<uint64_t> parseStrTabSize(StringRef &Buf) {
- if (Buf.size() < sizeof(uint64_t))
- return createStringError(std::errc::illegal_byte_sequence,
- "Expecting string table size.");
- uint64_t StrTabSize =
- support::endian::read<uint64_t, support::little, support::unaligned>(
- Buf.data());
- Buf = Buf.drop_front(sizeof(uint64_t));
- return StrTabSize;
- }
- static Expected<ParsedStringTable> parseStrTab(StringRef &Buf,
- uint64_t StrTabSize) {
- if (Buf.size() < StrTabSize)
- return createStringError(std::errc::illegal_byte_sequence,
- "Expecting string table.");
- // Attach the string table to the parser.
- ParsedStringTable Result(StringRef(Buf.data(), StrTabSize));
- Buf = Buf.drop_front(StrTabSize);
- return Expected<ParsedStringTable>(std::move(Result));
- }
- Expected<std::unique_ptr<YAMLRemarkParser>> remarks::createYAMLParserFromMeta(
- StringRef Buf, std::optional<ParsedStringTable> StrTab,
- std::optional<StringRef> ExternalFilePrependPath) {
- // We now have a magic number. The metadata has to be correct.
- Expected<bool> isMeta = parseMagic(Buf);
- if (!isMeta)
- return isMeta.takeError();
- // If it's not recognized as metadata, roll back.
- std::unique_ptr<MemoryBuffer> SeparateBuf;
- if (*isMeta) {
- Expected<uint64_t> Version = parseVersion(Buf);
- if (!Version)
- return Version.takeError();
- Expected<uint64_t> StrTabSize = parseStrTabSize(Buf);
- if (!StrTabSize)
- return StrTabSize.takeError();
- // If the size of string table is not 0, try to build one.
- if (*StrTabSize != 0) {
- if (StrTab)
- return createStringError(std::errc::illegal_byte_sequence,
- "String table already provided.");
- Expected<ParsedStringTable> MaybeStrTab = parseStrTab(Buf, *StrTabSize);
- if (!MaybeStrTab)
- return MaybeStrTab.takeError();
- StrTab = std::move(*MaybeStrTab);
- }
- // If it starts with "---", there is no external file.
- if (!Buf.startswith("---")) {
- // At this point, we expect Buf to contain the external file path.
- StringRef ExternalFilePath = Buf;
- SmallString<80> FullPath;
- if (ExternalFilePrependPath)
- FullPath = *ExternalFilePrependPath;
- sys::path::append(FullPath, ExternalFilePath);
- // Try to open the file and start parsing from there.
- ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
- MemoryBuffer::getFile(FullPath);
- if (std::error_code EC = BufferOrErr.getError())
- return createFileError(FullPath, EC);
- // Keep the buffer alive.
- SeparateBuf = std::move(*BufferOrErr);
- Buf = SeparateBuf->getBuffer();
- }
- }
- std::unique_ptr<YAMLRemarkParser> Result =
- StrTab
- ? std::make_unique<YAMLStrTabRemarkParser>(Buf, std::move(*StrTab))
- : std::make_unique<YAMLRemarkParser>(Buf);
- if (SeparateBuf)
- Result->SeparateBuf = std::move(SeparateBuf);
- return std::move(Result);
- }
- YAMLRemarkParser::YAMLRemarkParser(StringRef Buf)
- : YAMLRemarkParser(Buf, std::nullopt) {}
- YAMLRemarkParser::YAMLRemarkParser(StringRef Buf,
- std::optional<ParsedStringTable> StrTab)
- : RemarkParser{Format::YAML}, StrTab(std::move(StrTab)),
- SM(setupSM(LastErrorMessage)), Stream(Buf, SM), YAMLIt(Stream.begin()) {}
- Error YAMLRemarkParser::error(StringRef Message, yaml::Node &Node) {
- return make_error<YAMLParseError>(Message, SM, Stream, Node);
- }
- Error YAMLRemarkParser::error() {
- if (LastErrorMessage.empty())
- return Error::success();
- Error E = make_error<YAMLParseError>(LastErrorMessage);
- LastErrorMessage.clear();
- return E;
- }
- Expected<std::unique_ptr<Remark>>
- YAMLRemarkParser::parseRemark(yaml::Document &RemarkEntry) {
- if (Error E = error())
- return std::move(E);
- yaml::Node *YAMLRoot = RemarkEntry.getRoot();
- if (!YAMLRoot) {
- return createStringError(std::make_error_code(std::errc::invalid_argument),
- "not a valid YAML file.");
- }
- auto *Root = dyn_cast<yaml::MappingNode>(YAMLRoot);
- if (!Root)
- return error("document root is not of mapping type.", *YAMLRoot);
- std::unique_ptr<Remark> Result = std::make_unique<Remark>();
- Remark &TheRemark = *Result;
- // First, the type. It needs special handling since is not part of the
- // key-value stream.
- Expected<Type> T = parseType(*Root);
- if (!T)
- return T.takeError();
- else
- TheRemark.RemarkType = *T;
- // Then, parse the fields, one by one.
- for (yaml::KeyValueNode &RemarkField : *Root) {
- Expected<StringRef> MaybeKey = parseKey(RemarkField);
- if (!MaybeKey)
- return MaybeKey.takeError();
- StringRef KeyName = *MaybeKey;
- if (KeyName == "Pass") {
- if (Expected<StringRef> MaybeStr = parseStr(RemarkField))
- TheRemark.PassName = *MaybeStr;
- else
- return MaybeStr.takeError();
- } else if (KeyName == "Name") {
- if (Expected<StringRef> MaybeStr = parseStr(RemarkField))
- TheRemark.RemarkName = *MaybeStr;
- else
- return MaybeStr.takeError();
- } else if (KeyName == "Function") {
- if (Expected<StringRef> MaybeStr = parseStr(RemarkField))
- TheRemark.FunctionName = *MaybeStr;
- else
- return MaybeStr.takeError();
- } else if (KeyName == "Hotness") {
- if (Expected<unsigned> MaybeU = parseUnsigned(RemarkField))
- TheRemark.Hotness = *MaybeU;
- else
- return MaybeU.takeError();
- } else if (KeyName == "DebugLoc") {
- if (Expected<RemarkLocation> MaybeLoc = parseDebugLoc(RemarkField))
- TheRemark.Loc = *MaybeLoc;
- else
- return MaybeLoc.takeError();
- } else if (KeyName == "Args") {
- auto *Args = dyn_cast<yaml::SequenceNode>(RemarkField.getValue());
- if (!Args)
- return error("wrong value type for key.", RemarkField);
- for (yaml::Node &Arg : *Args) {
- if (Expected<Argument> MaybeArg = parseArg(Arg))
- TheRemark.Args.push_back(*MaybeArg);
- else
- return MaybeArg.takeError();
- }
- } else {
- return error("unknown key.", RemarkField);
- }
- }
- // Check if any of the mandatory fields are missing.
- if (TheRemark.RemarkType == Type::Unknown || TheRemark.PassName.empty() ||
- TheRemark.RemarkName.empty() || TheRemark.FunctionName.empty())
- return error("Type, Pass, Name or Function missing.",
- *RemarkEntry.getRoot());
- return std::move(Result);
- }
- Expected<Type> YAMLRemarkParser::parseType(yaml::MappingNode &Node) {
- auto Type = StringSwitch<remarks::Type>(Node.getRawTag())
- .Case("!Passed", remarks::Type::Passed)
- .Case("!Missed", remarks::Type::Missed)
- .Case("!Analysis", remarks::Type::Analysis)
- .Case("!AnalysisFPCommute", remarks::Type::AnalysisFPCommute)
- .Case("!AnalysisAliasing", remarks::Type::AnalysisAliasing)
- .Case("!Failure", remarks::Type::Failure)
- .Default(remarks::Type::Unknown);
- if (Type == remarks::Type::Unknown)
- return error("expected a remark tag.", Node);
- return Type;
- }
- Expected<StringRef> YAMLRemarkParser::parseKey(yaml::KeyValueNode &Node) {
- if (auto *Key = dyn_cast<yaml::ScalarNode>(Node.getKey()))
- return Key->getRawValue();
- return error("key is not a string.", Node);
- }
- Expected<StringRef> YAMLRemarkParser::parseStr(yaml::KeyValueNode &Node) {
- auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue());
- if (!Value)
- return error("expected a value of scalar type.", Node);
- StringRef Result = Value->getRawValue();
- if (Result.front() == '\'')
- Result = Result.drop_front();
- if (Result.back() == '\'')
- Result = Result.drop_back();
- return Result;
- }
- Expected<unsigned> YAMLRemarkParser::parseUnsigned(yaml::KeyValueNode &Node) {
- SmallVector<char, 4> Tmp;
- auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue());
- if (!Value)
- return error("expected a value of scalar type.", Node);
- unsigned UnsignedValue = 0;
- if (Value->getValue(Tmp).getAsInteger(10, UnsignedValue))
- return error("expected a value of integer type.", *Value);
- return UnsignedValue;
- }
- Expected<RemarkLocation>
- YAMLRemarkParser::parseDebugLoc(yaml::KeyValueNode &Node) {
- auto *DebugLoc = dyn_cast<yaml::MappingNode>(Node.getValue());
- if (!DebugLoc)
- return error("expected a value of mapping type.", Node);
- std::optional<StringRef> File;
- std::optional<unsigned> Line;
- std::optional<unsigned> Column;
- for (yaml::KeyValueNode &DLNode : *DebugLoc) {
- Expected<StringRef> MaybeKey = parseKey(DLNode);
- if (!MaybeKey)
- return MaybeKey.takeError();
- StringRef KeyName = *MaybeKey;
- if (KeyName == "File") {
- if (Expected<StringRef> MaybeStr = parseStr(DLNode))
- File = *MaybeStr;
- else
- return MaybeStr.takeError();
- } else if (KeyName == "Column") {
- if (Expected<unsigned> MaybeU = parseUnsigned(DLNode))
- Column = *MaybeU;
- else
- return MaybeU.takeError();
- } else if (KeyName == "Line") {
- if (Expected<unsigned> MaybeU = parseUnsigned(DLNode))
- Line = *MaybeU;
- else
- return MaybeU.takeError();
- } else {
- return error("unknown entry in DebugLoc map.", DLNode);
- }
- }
- // If any of the debug loc fields is missing, return an error.
- if (!File || !Line || !Column)
- return error("DebugLoc node incomplete.", Node);
- return RemarkLocation{*File, *Line, *Column};
- }
- Expected<Argument> YAMLRemarkParser::parseArg(yaml::Node &Node) {
- auto *ArgMap = dyn_cast<yaml::MappingNode>(&Node);
- if (!ArgMap)
- return error("expected a value of mapping type.", Node);
- std::optional<StringRef> KeyStr;
- std::optional<StringRef> ValueStr;
- std::optional<RemarkLocation> Loc;
- for (yaml::KeyValueNode &ArgEntry : *ArgMap) {
- Expected<StringRef> MaybeKey = parseKey(ArgEntry);
- if (!MaybeKey)
- return MaybeKey.takeError();
- StringRef KeyName = *MaybeKey;
- // Try to parse debug locs.
- if (KeyName == "DebugLoc") {
- // Can't have multiple DebugLoc entries per argument.
- if (Loc)
- return error("only one DebugLoc entry is allowed per argument.",
- ArgEntry);
- if (Expected<RemarkLocation> MaybeLoc = parseDebugLoc(ArgEntry)) {
- Loc = *MaybeLoc;
- continue;
- } else
- return MaybeLoc.takeError();
- }
- // If we already have a string, error out.
- if (ValueStr)
- return error("only one string entry is allowed per argument.", ArgEntry);
- // Try to parse the value.
- if (Expected<StringRef> MaybeStr = parseStr(ArgEntry))
- ValueStr = *MaybeStr;
- else
- return MaybeStr.takeError();
- // Keep the key from the string.
- KeyStr = KeyName;
- }
- if (!KeyStr)
- return error("argument key is missing.", *ArgMap);
- if (!ValueStr)
- return error("argument value is missing.", *ArgMap);
- return Argument{*KeyStr, *ValueStr, Loc};
- }
- Expected<std::unique_ptr<Remark>> YAMLRemarkParser::next() {
- if (YAMLIt == Stream.end())
- return make_error<EndOfFileError>();
- Expected<std::unique_ptr<Remark>> MaybeResult = parseRemark(*YAMLIt);
- if (!MaybeResult) {
- // Avoid garbage input, set the iterator to the end.
- YAMLIt = Stream.end();
- return MaybeResult.takeError();
- }
- ++YAMLIt;
- return std::move(*MaybeResult);
- }
- Expected<StringRef> YAMLStrTabRemarkParser::parseStr(yaml::KeyValueNode &Node) {
- auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue());
- if (!Value)
- return error("expected a value of scalar type.", Node);
- StringRef Result;
- // If we have a string table, parse it as an unsigned.
- unsigned StrID = 0;
- if (Expected<unsigned> MaybeStrID = parseUnsigned(Node))
- StrID = *MaybeStrID;
- else
- return MaybeStrID.takeError();
- if (Expected<StringRef> Str = (*StrTab)[StrID])
- Result = *Str;
- else
- return Str.takeError();
- if (Result.front() == '\'')
- Result = Result.drop_front();
- if (Result.back() == '\'')
- Result = Result.drop_back();
- return Result;
- }
|