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