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