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:: = 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, S&: OS, /*ShowColors*/ false, |
32 | /*ShowKindLabels*/ ShowKindLabel: true); |
33 | OS << '\n'; |
34 | OS.flush(); |
35 | } |
36 | |
37 | 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(DH: handleDiagnostic, Ctx: &Message); |
49 | Stream.printError(N: &Node, Msg: Twine(Msg) + Twine('\n')); |
50 | // Restore the old handlers. |
51 | SM.setDiagHandler(DH: OldDiagHandler, Ctx: OldDiagCtx); |
52 | } |
53 | |
54 | static SourceMgr setupSM(std::string &LastErrorMessage) { |
55 | SourceMgr SM; |
56 | SM.setDiagHandler(DH: handleDiagnostic, Ctx: &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(Prefix: remarks::Magic)) |
64 | return false; |
65 | |
66 | if (Buf.size() < 1 || !Buf.consume_front(Prefix: StringRef("\0" , 1))) |
67 | return createStringError(EC: std::errc::illegal_byte_sequence, |
68 | Fmt: "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(EC: std::errc::illegal_byte_sequence, |
75 | Fmt: "Expecting version number." ); |
76 | |
77 | uint64_t Version = |
78 | support::endian::read<uint64_t, llvm::endianness::little>(P: Buf.data()); |
79 | if (Version != remarks::CurrentRemarkVersion) |
80 | return createStringError(EC: std::errc::illegal_byte_sequence, |
81 | Fmt: "Mismatching remark version. Got %" PRId64 |
82 | ", expected %" PRId64 "." , |
83 | Vals: Version, Vals: remarks::CurrentRemarkVersion); |
84 | Buf = Buf.drop_front(N: 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(EC: std::errc::illegal_byte_sequence, |
91 | Fmt: "Expecting string table size." ); |
92 | uint64_t StrTabSize = |
93 | support::endian::read<uint64_t, llvm::endianness::little>(P: Buf.data()); |
94 | Buf = Buf.drop_front(N: 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(EC: std::errc::illegal_byte_sequence, |
102 | Fmt: "Expecting string table." ); |
103 | |
104 | // Attach the string table to the parser. |
105 | ParsedStringTable Result(StringRef(Buf.data(), StrTabSize)); |
106 | Buf = Buf.drop_front(N: StrTabSize); |
107 | return Expected<ParsedStringTable>(std::move(Result)); |
108 | } |
109 | |
110 | Expected<std::unique_ptr<YAMLRemarkParser>> remarks::( |
111 | StringRef Buf, std::optional<ParsedStringTable> StrTab, |
112 | std::optional<StringRef> ExternalFilePrependPath) { |
113 | // We now have a magic number. The metadata has to be correct. |
114 | Expected<bool> isMeta = parseMagic(Buf); |
115 | if (!isMeta) |
116 | return isMeta.takeError(); |
117 | // If it's not recognized as metadata, roll back. |
118 | std::unique_ptr<MemoryBuffer> SeparateBuf; |
119 | if (*isMeta) { |
120 | Expected<uint64_t> Version = parseVersion(Buf); |
121 | if (!Version) |
122 | return Version.takeError(); |
123 | |
124 | Expected<uint64_t> StrTabSize = parseStrTabSize(Buf); |
125 | if (!StrTabSize) |
126 | return StrTabSize.takeError(); |
127 | |
128 | // If the size of string table is not 0, try to build one. |
129 | if (*StrTabSize != 0) { |
130 | if (StrTab) |
131 | return createStringError(EC: std::errc::illegal_byte_sequence, |
132 | Fmt: "String table already provided." ); |
133 | Expected<ParsedStringTable> MaybeStrTab = parseStrTab(Buf, StrTabSize: *StrTabSize); |
134 | if (!MaybeStrTab) |
135 | return MaybeStrTab.takeError(); |
136 | StrTab = std::move(*MaybeStrTab); |
137 | } |
138 | // If it starts with "---", there is no external file. |
139 | if (!Buf.starts_with(Prefix: "---" )) { |
140 | // At this point, we expect Buf to contain the external file path. |
141 | StringRef ExternalFilePath = Buf; |
142 | SmallString<80> FullPath; |
143 | if (ExternalFilePrependPath) |
144 | FullPath = *ExternalFilePrependPath; |
145 | sys::path::append(path&: FullPath, a: ExternalFilePath); |
146 | |
147 | // Try to open the file and start parsing from there. |
148 | ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = |
149 | MemoryBuffer::getFile(Filename: FullPath); |
150 | if (std::error_code EC = BufferOrErr.getError()) |
151 | return createFileError(F: FullPath, EC); |
152 | |
153 | // Keep the buffer alive. |
154 | SeparateBuf = std::move(*BufferOrErr); |
155 | Buf = SeparateBuf->getBuffer(); |
156 | } |
157 | } |
158 | |
159 | std::unique_ptr<YAMLRemarkParser> Result = |
160 | StrTab |
161 | ? std::make_unique<YAMLStrTabRemarkParser>(args&: Buf, args: std::move(*StrTab)) |
162 | : std::make_unique<YAMLRemarkParser>(args&: Buf); |
163 | if (SeparateBuf) |
164 | Result->SeparateBuf = std::move(SeparateBuf); |
165 | return std::move(Result); |
166 | } |
167 | |
168 | YAMLRemarkParser::(StringRef Buf) |
169 | : YAMLRemarkParser(Buf, std::nullopt) {} |
170 | |
171 | YAMLRemarkParser::(StringRef Buf, |
172 | std::optional<ParsedStringTable> StrTab) |
173 | : RemarkParser{Format::YAML}, StrTab(std::move(StrTab)), |
174 | SM(setupSM(LastErrorMessage)), Stream(Buf, SM), YAMLIt(Stream.begin()) {} |
175 | |
176 | Error YAMLRemarkParser::(StringRef Message, yaml::Node &Node) { |
177 | return make_error<YAMLParseError>(Args&: Message, Args&: SM, Args&: Stream, Args&: Node); |
178 | } |
179 | |
180 | Error YAMLRemarkParser::() { |
181 | if (LastErrorMessage.empty()) |
182 | return Error::success(); |
183 | Error E = make_error<YAMLParseError>(Args&: LastErrorMessage); |
184 | LastErrorMessage.clear(); |
185 | return E; |
186 | } |
187 | |
188 | Expected<std::unique_ptr<Remark>> |
189 | YAMLRemarkParser::(yaml::Document &) { |
190 | if (Error E = error()) |
191 | return std::move(E); |
192 | |
193 | yaml::Node *YAMLRoot = RemarkEntry.getRoot(); |
194 | if (!YAMLRoot) { |
195 | return createStringError(EC: std::make_error_code(e: std::errc::invalid_argument), |
196 | S: "not a valid YAML file." ); |
197 | } |
198 | |
199 | auto *Root = dyn_cast<yaml::MappingNode>(Val: YAMLRoot); |
200 | if (!Root) |
201 | return error(Message: "document root is not of mapping type." , Node&: *YAMLRoot); |
202 | |
203 | std::unique_ptr<Remark> Result = std::make_unique<Remark>(); |
204 | Remark & = *Result; |
205 | |
206 | // First, the type. It needs special handling since is not part of the |
207 | // key-value stream. |
208 | Expected<Type> T = parseType(Node&: *Root); |
209 | if (!T) |
210 | return T.takeError(); |
211 | else |
212 | TheRemark.RemarkType = *T; |
213 | |
214 | // Then, parse the fields, one by one. |
215 | for (yaml::KeyValueNode & : *Root) { |
216 | Expected<StringRef> MaybeKey = parseKey(Node&: RemarkField); |
217 | if (!MaybeKey) |
218 | return MaybeKey.takeError(); |
219 | StringRef KeyName = *MaybeKey; |
220 | |
221 | if (KeyName == "Pass" ) { |
222 | if (Expected<StringRef> MaybeStr = parseStr(Node&: RemarkField)) |
223 | TheRemark.PassName = *MaybeStr; |
224 | else |
225 | return MaybeStr.takeError(); |
226 | } else if (KeyName == "Name" ) { |
227 | if (Expected<StringRef> MaybeStr = parseStr(Node&: RemarkField)) |
228 | TheRemark.RemarkName = *MaybeStr; |
229 | else |
230 | return MaybeStr.takeError(); |
231 | } else if (KeyName == "Function" ) { |
232 | if (Expected<StringRef> MaybeStr = parseStr(Node&: RemarkField)) |
233 | TheRemark.FunctionName = *MaybeStr; |
234 | else |
235 | return MaybeStr.takeError(); |
236 | } else if (KeyName == "Hotness" ) { |
237 | if (Expected<unsigned> MaybeU = parseUnsigned(Node&: RemarkField)) |
238 | TheRemark.Hotness = *MaybeU; |
239 | else |
240 | return MaybeU.takeError(); |
241 | } else if (KeyName == "DebugLoc" ) { |
242 | if (Expected<RemarkLocation> MaybeLoc = parseDebugLoc(Node&: RemarkField)) |
243 | TheRemark.Loc = *MaybeLoc; |
244 | else |
245 | return MaybeLoc.takeError(); |
246 | } else if (KeyName == "Args" ) { |
247 | auto *Args = dyn_cast<yaml::SequenceNode>(Val: RemarkField.getValue()); |
248 | if (!Args) |
249 | return error(Message: "wrong value type for key." , Node&: RemarkField); |
250 | |
251 | for (yaml::Node &Arg : *Args) { |
252 | if (Expected<Argument> MaybeArg = parseArg(Node&: Arg)) |
253 | TheRemark.Args.push_back(Elt: *MaybeArg); |
254 | else |
255 | return MaybeArg.takeError(); |
256 | } |
257 | } else { |
258 | return error(Message: "unknown key." , Node&: RemarkField); |
259 | } |
260 | } |
261 | |
262 | // Check if any of the mandatory fields are missing. |
263 | if (TheRemark.RemarkType == Type::Unknown || TheRemark.PassName.empty() || |
264 | TheRemark.RemarkName.empty() || TheRemark.FunctionName.empty()) |
265 | return error(Message: "Type, Pass, Name or Function missing." , |
266 | Node&: *RemarkEntry.getRoot()); |
267 | |
268 | return std::move(Result); |
269 | } |
270 | |
271 | Expected<Type> YAMLRemarkParser::(yaml::MappingNode &Node) { |
272 | auto Type = StringSwitch<remarks::Type>(Node.getRawTag()) |
273 | .Case(S: "!Passed" , Value: remarks::Type::Passed) |
274 | .Case(S: "!Missed" , Value: remarks::Type::Missed) |
275 | .Case(S: "!Analysis" , Value: remarks::Type::Analysis) |
276 | .Case(S: "!AnalysisFPCommute" , Value: remarks::Type::AnalysisFPCommute) |
277 | .Case(S: "!AnalysisAliasing" , Value: remarks::Type::AnalysisAliasing) |
278 | .Case(S: "!Failure" , Value: remarks::Type::Failure) |
279 | .Default(Value: remarks::Type::Unknown); |
280 | if (Type == remarks::Type::Unknown) |
281 | return error(Message: "expected a remark tag." , Node); |
282 | return Type; |
283 | } |
284 | |
285 | Expected<StringRef> YAMLRemarkParser::(yaml::KeyValueNode &Node) { |
286 | if (auto *Key = dyn_cast<yaml::ScalarNode>(Val: Node.getKey())) |
287 | return Key->getRawValue(); |
288 | |
289 | return error(Message: "key is not a string." , Node); |
290 | } |
291 | |
292 | Expected<StringRef> YAMLRemarkParser::(yaml::KeyValueNode &Node) { |
293 | auto *Value = dyn_cast<yaml::ScalarNode>(Val: Node.getValue()); |
294 | yaml::BlockScalarNode *ValueBlock; |
295 | StringRef Result; |
296 | if (!Value) { |
297 | // Try to parse the value as a block node. |
298 | ValueBlock = dyn_cast<yaml::BlockScalarNode>(Val: Node.getValue()); |
299 | if (!ValueBlock) |
300 | return error(Message: "expected a value of scalar type." , Node); |
301 | Result = ValueBlock->getValue(); |
302 | } else |
303 | Result = Value->getRawValue(); |
304 | |
305 | Result.consume_front(Prefix: "\'" ); |
306 | Result.consume_back(Suffix: "\'" ); |
307 | |
308 | return Result; |
309 | } |
310 | |
311 | Expected<unsigned> YAMLRemarkParser::(yaml::KeyValueNode &Node) { |
312 | SmallVector<char, 4> Tmp; |
313 | auto *Value = dyn_cast<yaml::ScalarNode>(Val: Node.getValue()); |
314 | if (!Value) |
315 | return error(Message: "expected a value of scalar type." , Node); |
316 | unsigned UnsignedValue = 0; |
317 | if (Value->getValue(Storage&: Tmp).getAsInteger(Radix: 10, Result&: UnsignedValue)) |
318 | return error(Message: "expected a value of integer type." , Node&: *Value); |
319 | return UnsignedValue; |
320 | } |
321 | |
322 | Expected<RemarkLocation> |
323 | YAMLRemarkParser::(yaml::KeyValueNode &Node) { |
324 | auto *DebugLoc = dyn_cast<yaml::MappingNode>(Val: Node.getValue()); |
325 | if (!DebugLoc) |
326 | return error(Message: "expected a value of mapping type." , Node); |
327 | |
328 | std::optional<StringRef> File; |
329 | std::optional<unsigned> Line; |
330 | std::optional<unsigned> Column; |
331 | |
332 | for (yaml::KeyValueNode &DLNode : *DebugLoc) { |
333 | Expected<StringRef> MaybeKey = parseKey(Node&: DLNode); |
334 | if (!MaybeKey) |
335 | return MaybeKey.takeError(); |
336 | StringRef KeyName = *MaybeKey; |
337 | |
338 | if (KeyName == "File" ) { |
339 | if (Expected<StringRef> MaybeStr = parseStr(Node&: DLNode)) |
340 | File = *MaybeStr; |
341 | else |
342 | return MaybeStr.takeError(); |
343 | } else if (KeyName == "Column" ) { |
344 | if (Expected<unsigned> MaybeU = parseUnsigned(Node&: DLNode)) |
345 | Column = *MaybeU; |
346 | else |
347 | return MaybeU.takeError(); |
348 | } else if (KeyName == "Line" ) { |
349 | if (Expected<unsigned> MaybeU = parseUnsigned(Node&: DLNode)) |
350 | Line = *MaybeU; |
351 | else |
352 | return MaybeU.takeError(); |
353 | } else { |
354 | return error(Message: "unknown entry in DebugLoc map." , Node&: DLNode); |
355 | } |
356 | } |
357 | |
358 | // If any of the debug loc fields is missing, return an error. |
359 | if (!File || !Line || !Column) |
360 | return error(Message: "DebugLoc node incomplete." , Node); |
361 | |
362 | return RemarkLocation{.SourceFilePath: *File, .SourceLine: *Line, .SourceColumn: *Column}; |
363 | } |
364 | |
365 | Expected<Argument> YAMLRemarkParser::(yaml::Node &Node) { |
366 | auto *ArgMap = dyn_cast<yaml::MappingNode>(Val: &Node); |
367 | if (!ArgMap) |
368 | return error(Message: "expected a value of mapping type." , Node); |
369 | |
370 | std::optional<StringRef> KeyStr; |
371 | std::optional<StringRef> ValueStr; |
372 | std::optional<RemarkLocation> Loc; |
373 | |
374 | for (yaml::KeyValueNode &ArgEntry : *ArgMap) { |
375 | Expected<StringRef> MaybeKey = parseKey(Node&: ArgEntry); |
376 | if (!MaybeKey) |
377 | return MaybeKey.takeError(); |
378 | StringRef KeyName = *MaybeKey; |
379 | |
380 | // Try to parse debug locs. |
381 | if (KeyName == "DebugLoc" ) { |
382 | // Can't have multiple DebugLoc entries per argument. |
383 | if (Loc) |
384 | return error(Message: "only one DebugLoc entry is allowed per argument." , |
385 | Node&: ArgEntry); |
386 | |
387 | if (Expected<RemarkLocation> MaybeLoc = parseDebugLoc(Node&: ArgEntry)) { |
388 | Loc = *MaybeLoc; |
389 | continue; |
390 | } else |
391 | return MaybeLoc.takeError(); |
392 | } |
393 | |
394 | // If we already have a string, error out. |
395 | if (ValueStr) |
396 | return error(Message: "only one string entry is allowed per argument." , Node&: ArgEntry); |
397 | |
398 | // Try to parse the value. |
399 | if (Expected<StringRef> MaybeStr = parseStr(Node&: ArgEntry)) |
400 | ValueStr = *MaybeStr; |
401 | else |
402 | return MaybeStr.takeError(); |
403 | |
404 | // Keep the key from the string. |
405 | KeyStr = KeyName; |
406 | } |
407 | |
408 | if (!KeyStr) |
409 | return error(Message: "argument key is missing." , Node&: *ArgMap); |
410 | if (!ValueStr) |
411 | return error(Message: "argument value is missing." , Node&: *ArgMap); |
412 | |
413 | return Argument{.Key: *KeyStr, .Val: *ValueStr, .Loc: Loc}; |
414 | } |
415 | |
416 | Expected<std::unique_ptr<Remark>> YAMLRemarkParser::() { |
417 | if (YAMLIt == Stream.end()) |
418 | return make_error<EndOfFileError>(); |
419 | |
420 | Expected<std::unique_ptr<Remark>> MaybeResult = parseRemark(RemarkEntry&: *YAMLIt); |
421 | if (!MaybeResult) { |
422 | // Avoid garbage input, set the iterator to the end. |
423 | YAMLIt = Stream.end(); |
424 | return MaybeResult.takeError(); |
425 | } |
426 | |
427 | ++YAMLIt; |
428 | |
429 | return std::move(*MaybeResult); |
430 | } |
431 | |
432 | Expected<StringRef> YAMLStrTabRemarkParser::(yaml::KeyValueNode &Node) { |
433 | auto *Value = dyn_cast<yaml::ScalarNode>(Val: Node.getValue()); |
434 | yaml::BlockScalarNode *ValueBlock; |
435 | StringRef Result; |
436 | if (!Value) { |
437 | // Try to parse the value as a block node. |
438 | ValueBlock = dyn_cast<yaml::BlockScalarNode>(Val: Node.getValue()); |
439 | if (!ValueBlock) |
440 | return error(Message: "expected a value of scalar type." , Node); |
441 | Result = ValueBlock->getValue(); |
442 | } else |
443 | Result = Value->getRawValue(); |
444 | // If we have a string table, parse it as an unsigned. |
445 | unsigned StrID = 0; |
446 | if (Expected<unsigned> MaybeStrID = parseUnsigned(Node)) |
447 | StrID = *MaybeStrID; |
448 | else |
449 | return MaybeStrID.takeError(); |
450 | |
451 | if (Expected<StringRef> Str = (*StrTab)[StrID]) |
452 | Result = *Str; |
453 | else |
454 | return Str.takeError(); |
455 | |
456 | Result.consume_front(Prefix: "\'" ); |
457 | Result.consume_back(Suffix: "\'" ); |
458 | |
459 | return Result; |
460 | } |
461 | |