Path: blob/main/contrib/llvm-project/llvm/lib/Remarks/YAMLRemarkParser.cpp
35262 views
//===- YAMLRemarkParser.cpp -----------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file provides utility methods used by clients that want to use the9// parser for remark diagnostics in LLVM.10//11//===----------------------------------------------------------------------===//1213#include "YAMLRemarkParser.h"14#include "llvm/ADT/SmallString.h"15#include "llvm/ADT/StringSwitch.h"16#include "llvm/Support/Endian.h"17#include "llvm/Support/Path.h"18#include <optional>1920using namespace llvm;21using namespace llvm::remarks;2223char YAMLParseError::ID = 0;2425static void handleDiagnostic(const SMDiagnostic &Diag, void *Ctx) {26assert(Ctx && "Expected non-null Ctx in diagnostic handler.");27std::string &Message = *static_cast<std::string *>(Ctx);28assert(Message.empty() && "Expected an empty string.");29raw_string_ostream OS(Message);30Diag.print(/*ProgName=*/nullptr, OS, /*ShowColors*/ false,31/*ShowKindLabels*/ true);32OS << '\n';33OS.flush();34}3536YAMLParseError::YAMLParseError(StringRef Msg, SourceMgr &SM,37yaml::Stream &Stream, yaml::Node &Node) {38// 1) Set up a diagnostic handler to avoid errors being printed out to39// stderr.40// 2) Use the stream to print the error with the associated node.41// 3) The stream will use the source manager to print the error, which will42// call the diagnostic handler.43// 4) The diagnostic handler will stream the error directly into this object's44// Message member, which is used when logging is asked for.45auto OldDiagHandler = SM.getDiagHandler();46auto OldDiagCtx = SM.getDiagContext();47SM.setDiagHandler(handleDiagnostic, &Message);48Stream.printError(&Node, Twine(Msg) + Twine('\n'));49// Restore the old handlers.50SM.setDiagHandler(OldDiagHandler, OldDiagCtx);51}5253static SourceMgr setupSM(std::string &LastErrorMessage) {54SourceMgr SM;55SM.setDiagHandler(handleDiagnostic, &LastErrorMessage);56return SM;57}5859// Parse the magic number. This function returns true if this represents remark60// metadata, false otherwise.61static Expected<bool> parseMagic(StringRef &Buf) {62if (!Buf.consume_front(remarks::Magic))63return false;6465if (Buf.size() < 1 || !Buf.consume_front(StringRef("\0", 1)))66return createStringError(std::errc::illegal_byte_sequence,67"Expecting \\0 after magic number.");68return true;69}7071static Expected<uint64_t> parseVersion(StringRef &Buf) {72if (Buf.size() < sizeof(uint64_t))73return createStringError(std::errc::illegal_byte_sequence,74"Expecting version number.");7576uint64_t Version =77support::endian::read<uint64_t, llvm::endianness::little>(Buf.data());78if (Version != remarks::CurrentRemarkVersion)79return createStringError(std::errc::illegal_byte_sequence,80"Mismatching remark version. Got %" PRId6481", expected %" PRId64 ".",82Version, remarks::CurrentRemarkVersion);83Buf = Buf.drop_front(sizeof(uint64_t));84return Version;85}8687static Expected<uint64_t> parseStrTabSize(StringRef &Buf) {88if (Buf.size() < sizeof(uint64_t))89return createStringError(std::errc::illegal_byte_sequence,90"Expecting string table size.");91uint64_t StrTabSize =92support::endian::read<uint64_t, llvm::endianness::little>(Buf.data());93Buf = Buf.drop_front(sizeof(uint64_t));94return StrTabSize;95}9697static Expected<ParsedStringTable> parseStrTab(StringRef &Buf,98uint64_t StrTabSize) {99if (Buf.size() < StrTabSize)100return createStringError(std::errc::illegal_byte_sequence,101"Expecting string table.");102103// Attach the string table to the parser.104ParsedStringTable Result(StringRef(Buf.data(), StrTabSize));105Buf = Buf.drop_front(StrTabSize);106return Expected<ParsedStringTable>(std::move(Result));107}108109Expected<std::unique_ptr<YAMLRemarkParser>> remarks::createYAMLParserFromMeta(110StringRef Buf, std::optional<ParsedStringTable> StrTab,111std::optional<StringRef> ExternalFilePrependPath) {112// We now have a magic number. The metadata has to be correct.113Expected<bool> isMeta = parseMagic(Buf);114if (!isMeta)115return isMeta.takeError();116// If it's not recognized as metadata, roll back.117std::unique_ptr<MemoryBuffer> SeparateBuf;118if (*isMeta) {119Expected<uint64_t> Version = parseVersion(Buf);120if (!Version)121return Version.takeError();122123Expected<uint64_t> StrTabSize = parseStrTabSize(Buf);124if (!StrTabSize)125return StrTabSize.takeError();126127// If the size of string table is not 0, try to build one.128if (*StrTabSize != 0) {129if (StrTab)130return createStringError(std::errc::illegal_byte_sequence,131"String table already provided.");132Expected<ParsedStringTable> MaybeStrTab = parseStrTab(Buf, *StrTabSize);133if (!MaybeStrTab)134return MaybeStrTab.takeError();135StrTab = std::move(*MaybeStrTab);136}137// If it starts with "---", there is no external file.138if (!Buf.starts_with("---")) {139// At this point, we expect Buf to contain the external file path.140StringRef ExternalFilePath = Buf;141SmallString<80> FullPath;142if (ExternalFilePrependPath)143FullPath = *ExternalFilePrependPath;144sys::path::append(FullPath, ExternalFilePath);145146// Try to open the file and start parsing from there.147ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =148MemoryBuffer::getFile(FullPath);149if (std::error_code EC = BufferOrErr.getError())150return createFileError(FullPath, EC);151152// Keep the buffer alive.153SeparateBuf = std::move(*BufferOrErr);154Buf = SeparateBuf->getBuffer();155}156}157158std::unique_ptr<YAMLRemarkParser> Result =159StrTab160? std::make_unique<YAMLStrTabRemarkParser>(Buf, std::move(*StrTab))161: std::make_unique<YAMLRemarkParser>(Buf);162if (SeparateBuf)163Result->SeparateBuf = std::move(SeparateBuf);164return std::move(Result);165}166167YAMLRemarkParser::YAMLRemarkParser(StringRef Buf)168: YAMLRemarkParser(Buf, std::nullopt) {}169170YAMLRemarkParser::YAMLRemarkParser(StringRef Buf,171std::optional<ParsedStringTable> StrTab)172: RemarkParser{Format::YAML}, StrTab(std::move(StrTab)),173SM(setupSM(LastErrorMessage)), Stream(Buf, SM), YAMLIt(Stream.begin()) {}174175Error YAMLRemarkParser::error(StringRef Message, yaml::Node &Node) {176return make_error<YAMLParseError>(Message, SM, Stream, Node);177}178179Error YAMLRemarkParser::error() {180if (LastErrorMessage.empty())181return Error::success();182Error E = make_error<YAMLParseError>(LastErrorMessage);183LastErrorMessage.clear();184return E;185}186187Expected<std::unique_ptr<Remark>>188YAMLRemarkParser::parseRemark(yaml::Document &RemarkEntry) {189if (Error E = error())190return std::move(E);191192yaml::Node *YAMLRoot = RemarkEntry.getRoot();193if (!YAMLRoot) {194return createStringError(std::make_error_code(std::errc::invalid_argument),195"not a valid YAML file.");196}197198auto *Root = dyn_cast<yaml::MappingNode>(YAMLRoot);199if (!Root)200return error("document root is not of mapping type.", *YAMLRoot);201202std::unique_ptr<Remark> Result = std::make_unique<Remark>();203Remark &TheRemark = *Result;204205// First, the type. It needs special handling since is not part of the206// key-value stream.207Expected<Type> T = parseType(*Root);208if (!T)209return T.takeError();210else211TheRemark.RemarkType = *T;212213// Then, parse the fields, one by one.214for (yaml::KeyValueNode &RemarkField : *Root) {215Expected<StringRef> MaybeKey = parseKey(RemarkField);216if (!MaybeKey)217return MaybeKey.takeError();218StringRef KeyName = *MaybeKey;219220if (KeyName == "Pass") {221if (Expected<StringRef> MaybeStr = parseStr(RemarkField))222TheRemark.PassName = *MaybeStr;223else224return MaybeStr.takeError();225} else if (KeyName == "Name") {226if (Expected<StringRef> MaybeStr = parseStr(RemarkField))227TheRemark.RemarkName = *MaybeStr;228else229return MaybeStr.takeError();230} else if (KeyName == "Function") {231if (Expected<StringRef> MaybeStr = parseStr(RemarkField))232TheRemark.FunctionName = *MaybeStr;233else234return MaybeStr.takeError();235} else if (KeyName == "Hotness") {236if (Expected<unsigned> MaybeU = parseUnsigned(RemarkField))237TheRemark.Hotness = *MaybeU;238else239return MaybeU.takeError();240} else if (KeyName == "DebugLoc") {241if (Expected<RemarkLocation> MaybeLoc = parseDebugLoc(RemarkField))242TheRemark.Loc = *MaybeLoc;243else244return MaybeLoc.takeError();245} else if (KeyName == "Args") {246auto *Args = dyn_cast<yaml::SequenceNode>(RemarkField.getValue());247if (!Args)248return error("wrong value type for key.", RemarkField);249250for (yaml::Node &Arg : *Args) {251if (Expected<Argument> MaybeArg = parseArg(Arg))252TheRemark.Args.push_back(*MaybeArg);253else254return MaybeArg.takeError();255}256} else {257return error("unknown key.", RemarkField);258}259}260261// Check if any of the mandatory fields are missing.262if (TheRemark.RemarkType == Type::Unknown || TheRemark.PassName.empty() ||263TheRemark.RemarkName.empty() || TheRemark.FunctionName.empty())264return error("Type, Pass, Name or Function missing.",265*RemarkEntry.getRoot());266267return std::move(Result);268}269270Expected<Type> YAMLRemarkParser::parseType(yaml::MappingNode &Node) {271auto Type = StringSwitch<remarks::Type>(Node.getRawTag())272.Case("!Passed", remarks::Type::Passed)273.Case("!Missed", remarks::Type::Missed)274.Case("!Analysis", remarks::Type::Analysis)275.Case("!AnalysisFPCommute", remarks::Type::AnalysisFPCommute)276.Case("!AnalysisAliasing", remarks::Type::AnalysisAliasing)277.Case("!Failure", remarks::Type::Failure)278.Default(remarks::Type::Unknown);279if (Type == remarks::Type::Unknown)280return error("expected a remark tag.", Node);281return Type;282}283284Expected<StringRef> YAMLRemarkParser::parseKey(yaml::KeyValueNode &Node) {285if (auto *Key = dyn_cast<yaml::ScalarNode>(Node.getKey()))286return Key->getRawValue();287288return error("key is not a string.", Node);289}290291Expected<StringRef> YAMLRemarkParser::parseStr(yaml::KeyValueNode &Node) {292auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue());293yaml::BlockScalarNode *ValueBlock;294StringRef Result;295if (!Value) {296// Try to parse the value as a block node.297ValueBlock = dyn_cast<yaml::BlockScalarNode>(Node.getValue());298if (!ValueBlock)299return error("expected a value of scalar type.", Node);300Result = ValueBlock->getValue();301} else302Result = Value->getRawValue();303304Result.consume_front("\'");305Result.consume_back("\'");306307return Result;308}309310Expected<unsigned> YAMLRemarkParser::parseUnsigned(yaml::KeyValueNode &Node) {311SmallVector<char, 4> Tmp;312auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue());313if (!Value)314return error("expected a value of scalar type.", Node);315unsigned UnsignedValue = 0;316if (Value->getValue(Tmp).getAsInteger(10, UnsignedValue))317return error("expected a value of integer type.", *Value);318return UnsignedValue;319}320321Expected<RemarkLocation>322YAMLRemarkParser::parseDebugLoc(yaml::KeyValueNode &Node) {323auto *DebugLoc = dyn_cast<yaml::MappingNode>(Node.getValue());324if (!DebugLoc)325return error("expected a value of mapping type.", Node);326327std::optional<StringRef> File;328std::optional<unsigned> Line;329std::optional<unsigned> Column;330331for (yaml::KeyValueNode &DLNode : *DebugLoc) {332Expected<StringRef> MaybeKey = parseKey(DLNode);333if (!MaybeKey)334return MaybeKey.takeError();335StringRef KeyName = *MaybeKey;336337if (KeyName == "File") {338if (Expected<StringRef> MaybeStr = parseStr(DLNode))339File = *MaybeStr;340else341return MaybeStr.takeError();342} else if (KeyName == "Column") {343if (Expected<unsigned> MaybeU = parseUnsigned(DLNode))344Column = *MaybeU;345else346return MaybeU.takeError();347} else if (KeyName == "Line") {348if (Expected<unsigned> MaybeU = parseUnsigned(DLNode))349Line = *MaybeU;350else351return MaybeU.takeError();352} else {353return error("unknown entry in DebugLoc map.", DLNode);354}355}356357// If any of the debug loc fields is missing, return an error.358if (!File || !Line || !Column)359return error("DebugLoc node incomplete.", Node);360361return RemarkLocation{*File, *Line, *Column};362}363364Expected<Argument> YAMLRemarkParser::parseArg(yaml::Node &Node) {365auto *ArgMap = dyn_cast<yaml::MappingNode>(&Node);366if (!ArgMap)367return error("expected a value of mapping type.", Node);368369std::optional<StringRef> KeyStr;370std::optional<StringRef> ValueStr;371std::optional<RemarkLocation> Loc;372373for (yaml::KeyValueNode &ArgEntry : *ArgMap) {374Expected<StringRef> MaybeKey = parseKey(ArgEntry);375if (!MaybeKey)376return MaybeKey.takeError();377StringRef KeyName = *MaybeKey;378379// Try to parse debug locs.380if (KeyName == "DebugLoc") {381// Can't have multiple DebugLoc entries per argument.382if (Loc)383return error("only one DebugLoc entry is allowed per argument.",384ArgEntry);385386if (Expected<RemarkLocation> MaybeLoc = parseDebugLoc(ArgEntry)) {387Loc = *MaybeLoc;388continue;389} else390return MaybeLoc.takeError();391}392393// If we already have a string, error out.394if (ValueStr)395return error("only one string entry is allowed per argument.", ArgEntry);396397// Try to parse the value.398if (Expected<StringRef> MaybeStr = parseStr(ArgEntry))399ValueStr = *MaybeStr;400else401return MaybeStr.takeError();402403// Keep the key from the string.404KeyStr = KeyName;405}406407if (!KeyStr)408return error("argument key is missing.", *ArgMap);409if (!ValueStr)410return error("argument value is missing.", *ArgMap);411412return Argument{*KeyStr, *ValueStr, Loc};413}414415Expected<std::unique_ptr<Remark>> YAMLRemarkParser::next() {416if (YAMLIt == Stream.end())417return make_error<EndOfFileError>();418419Expected<std::unique_ptr<Remark>> MaybeResult = parseRemark(*YAMLIt);420if (!MaybeResult) {421// Avoid garbage input, set the iterator to the end.422YAMLIt = Stream.end();423return MaybeResult.takeError();424}425426++YAMLIt;427428return std::move(*MaybeResult);429}430431Expected<StringRef> YAMLStrTabRemarkParser::parseStr(yaml::KeyValueNode &Node) {432auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue());433yaml::BlockScalarNode *ValueBlock;434StringRef Result;435if (!Value) {436// Try to parse the value as a block node.437ValueBlock = dyn_cast<yaml::BlockScalarNode>(Node.getValue());438if (!ValueBlock)439return error("expected a value of scalar type.", Node);440Result = ValueBlock->getValue();441} else442Result = Value->getRawValue();443// If we have a string table, parse it as an unsigned.444unsigned StrID = 0;445if (Expected<unsigned> MaybeStrID = parseUnsigned(Node))446StrID = *MaybeStrID;447else448return MaybeStrID.takeError();449450if (Expected<StringRef> Str = (*StrTab)[StrID])451Result = *Str;452else453return Str.takeError();454455Result.consume_front("\'");456Result.consume_back("\'");457458return Result;459}460461462