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 | Expected<std::unique_ptr<YAMLRemarkParser>> remarks::( |
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(EC: std::errc::illegal_byte_sequence, |
117 | Fmt: "String table unsupported for YAML format." ); |
118 | } |
119 | // If it starts with "---", there is no external file. |
120 | if (!Buf.starts_with(Prefix: "---" )) { |
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(path&: FullPath, a: ExternalFilePath); |
127 | |
128 | // Try to open the file and start parsing from there. |
129 | ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = |
130 | MemoryBuffer::getFile(Filename: FullPath); |
131 | if (std::error_code EC = BufferOrErr.getError()) |
132 | return createFileError(F: 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>(args&: Buf); |
142 | if (SeparateBuf) |
143 | Result->SeparateBuf = std::move(SeparateBuf); |
144 | return std::move(Result); |
145 | } |
146 | |
147 | YAMLRemarkParser::(StringRef Buf) |
148 | : RemarkParser{Format::YAML}, SM(setupSM(LastErrorMessage)), |
149 | Stream(Buf, SM), YAMLIt(Stream.begin()) {} |
150 | |
151 | Error YAMLRemarkParser::(StringRef Message, yaml::Node &Node) { |
152 | return make_error<YAMLParseError>(Args&: Message, Args&: SM, Args&: Stream, Args&: Node); |
153 | } |
154 | |
155 | Error YAMLRemarkParser::() { |
156 | if (LastErrorMessage.empty()) |
157 | return Error::success(); |
158 | Error E = make_error<YAMLParseError>(Args&: LastErrorMessage); |
159 | LastErrorMessage.clear(); |
160 | return E; |
161 | } |
162 | |
163 | Expected<std::unique_ptr<Remark>> |
164 | YAMLRemarkParser::(yaml::Document &) { |
165 | if (Error E = error()) |
166 | return std::move(E); |
167 | |
168 | yaml::Node *YAMLRoot = RemarkEntry.getRoot(); |
169 | if (!YAMLRoot) { |
170 | return createStringError(EC: std::make_error_code(e: std::errc::invalid_argument), |
171 | S: "not a valid YAML file." ); |
172 | } |
173 | |
174 | auto *Root = dyn_cast<yaml::MappingNode>(Val: YAMLRoot); |
175 | if (!Root) |
176 | return error(Message: "document root is not of mapping type." , Node&: *YAMLRoot); |
177 | |
178 | std::unique_ptr<Remark> Result = std::make_unique<Remark>(); |
179 | Remark & = *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(Node&: *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 & : *Root) { |
191 | Expected<StringRef> MaybeKey = parseKey(Node&: RemarkField); |
192 | if (!MaybeKey) |
193 | return MaybeKey.takeError(); |
194 | StringRef KeyName = *MaybeKey; |
195 | |
196 | if (KeyName == "Pass" ) { |
197 | if (Expected<StringRef> MaybeStr = parseStr(Node&: RemarkField)) |
198 | TheRemark.PassName = *MaybeStr; |
199 | else |
200 | return MaybeStr.takeError(); |
201 | } else if (KeyName == "Name" ) { |
202 | if (Expected<StringRef> MaybeStr = parseStr(Node&: RemarkField)) |
203 | TheRemark.RemarkName = *MaybeStr; |
204 | else |
205 | return MaybeStr.takeError(); |
206 | } else if (KeyName == "Function" ) { |
207 | if (Expected<StringRef> MaybeStr = parseStr(Node&: RemarkField)) |
208 | TheRemark.FunctionName = *MaybeStr; |
209 | else |
210 | return MaybeStr.takeError(); |
211 | } else if (KeyName == "Hotness" ) { |
212 | if (Expected<unsigned> MaybeU = parseUnsigned(Node&: RemarkField)) |
213 | TheRemark.Hotness = *MaybeU; |
214 | else |
215 | return MaybeU.takeError(); |
216 | } else if (KeyName == "DebugLoc" ) { |
217 | if (Expected<RemarkLocation> MaybeLoc = parseDebugLoc(Node&: RemarkField)) |
218 | TheRemark.Loc = *MaybeLoc; |
219 | else |
220 | return MaybeLoc.takeError(); |
221 | } else if (KeyName == "Args" ) { |
222 | auto *Args = dyn_cast<yaml::SequenceNode>(Val: RemarkField.getValue()); |
223 | if (!Args) |
224 | return error(Message: "wrong value type for key." , Node&: RemarkField); |
225 | |
226 | for (yaml::Node &Arg : *Args) { |
227 | if (Expected<Argument> MaybeArg = parseArg(Node&: Arg)) |
228 | TheRemark.Args.push_back(Elt: *MaybeArg); |
229 | else |
230 | return MaybeArg.takeError(); |
231 | } |
232 | } else { |
233 | return error(Message: "unknown key." , Node&: 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(Message: "Type, Pass, Name or Function missing." , |
241 | Node&: *RemarkEntry.getRoot()); |
242 | |
243 | return std::move(Result); |
244 | } |
245 | |
246 | Expected<Type> YAMLRemarkParser::(yaml::MappingNode &Node) { |
247 | auto Type = StringSwitch<remarks::Type>(Node.getRawTag()) |
248 | .Case(S: "!Passed" , Value: remarks::Type::Passed) |
249 | .Case(S: "!Missed" , Value: remarks::Type::Missed) |
250 | .Case(S: "!Analysis" , Value: remarks::Type::Analysis) |
251 | .Case(S: "!AnalysisFPCommute" , Value: remarks::Type::AnalysisFPCommute) |
252 | .Case(S: "!AnalysisAliasing" , Value: remarks::Type::AnalysisAliasing) |
253 | .Case(S: "!Failure" , Value: remarks::Type::Failure) |
254 | .Default(Value: remarks::Type::Unknown); |
255 | if (Type == remarks::Type::Unknown) |
256 | return error(Message: "expected a remark tag." , Node); |
257 | return Type; |
258 | } |
259 | |
260 | Expected<StringRef> YAMLRemarkParser::(yaml::KeyValueNode &Node) { |
261 | if (auto *Key = dyn_cast<yaml::ScalarNode>(Val: Node.getKey())) |
262 | return Key->getRawValue(); |
263 | |
264 | return error(Message: "key is not a string." , Node); |
265 | } |
266 | |
267 | Expected<StringRef> YAMLRemarkParser::(yaml::KeyValueNode &Node) { |
268 | auto *Value = dyn_cast<yaml::ScalarNode>(Val: 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>(Val: Node.getValue()); |
274 | if (!ValueBlock) |
275 | return error(Message: "expected a value of scalar type." , Node); |
276 | Result = ValueBlock->getValue(); |
277 | } else |
278 | Result = Value->getRawValue(); |
279 | |
280 | Result.consume_front(Prefix: "\'" ); |
281 | Result.consume_back(Suffix: "\'" ); |
282 | |
283 | return Result; |
284 | } |
285 | |
286 | Expected<unsigned> YAMLRemarkParser::(yaml::KeyValueNode &Node) { |
287 | SmallVector<char, 4> Tmp; |
288 | auto *Value = dyn_cast<yaml::ScalarNode>(Val: Node.getValue()); |
289 | if (!Value) |
290 | return error(Message: "expected a value of scalar type." , Node); |
291 | unsigned UnsignedValue = 0; |
292 | if (Value->getValue(Storage&: Tmp).getAsInteger(Radix: 10, Result&: UnsignedValue)) |
293 | return error(Message: "expected a value of integer type." , Node&: *Value); |
294 | return UnsignedValue; |
295 | } |
296 | |
297 | Expected<RemarkLocation> |
298 | YAMLRemarkParser::(yaml::KeyValueNode &Node) { |
299 | auto *DebugLoc = dyn_cast<yaml::MappingNode>(Val: Node.getValue()); |
300 | if (!DebugLoc) |
301 | return error(Message: "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(Node&: DLNode); |
309 | if (!MaybeKey) |
310 | return MaybeKey.takeError(); |
311 | StringRef KeyName = *MaybeKey; |
312 | |
313 | if (KeyName == "File" ) { |
314 | if (Expected<StringRef> MaybeStr = parseStr(Node&: DLNode)) |
315 | File = *MaybeStr; |
316 | else |
317 | return MaybeStr.takeError(); |
318 | } else if (KeyName == "Column" ) { |
319 | if (Expected<unsigned> MaybeU = parseUnsigned(Node&: DLNode)) |
320 | Column = *MaybeU; |
321 | else |
322 | return MaybeU.takeError(); |
323 | } else if (KeyName == "Line" ) { |
324 | if (Expected<unsigned> MaybeU = parseUnsigned(Node&: DLNode)) |
325 | Line = *MaybeU; |
326 | else |
327 | return MaybeU.takeError(); |
328 | } else { |
329 | return error(Message: "unknown entry in DebugLoc map." , Node&: 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(Message: "DebugLoc node incomplete." , Node); |
336 | |
337 | return RemarkLocation{.SourceFilePath: *File, .SourceLine: *Line, .SourceColumn: *Column}; |
338 | } |
339 | |
340 | Expected<Argument> YAMLRemarkParser::(yaml::Node &Node) { |
341 | auto *ArgMap = dyn_cast<yaml::MappingNode>(Val: &Node); |
342 | if (!ArgMap) |
343 | return error(Message: "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(Node&: 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(Message: "only one DebugLoc entry is allowed per argument." , |
360 | Node&: ArgEntry); |
361 | |
362 | if (Expected<RemarkLocation> MaybeLoc = parseDebugLoc(Node&: 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(Message: "only one string entry is allowed per argument." , Node&: ArgEntry); |
372 | |
373 | // Try to parse the value. |
374 | if (Expected<StringRef> MaybeStr = parseStr(Node&: 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(Message: "argument key is missing." , Node&: *ArgMap); |
385 | if (!ValueStr) |
386 | return error(Message: "argument value is missing." , Node&: *ArgMap); |
387 | |
388 | return Argument{.Key: *KeyStr, .Val: *ValueStr, .Loc: Loc}; |
389 | } |
390 | |
391 | Expected<std::unique_ptr<Remark>> YAMLRemarkParser::() { |
392 | if (YAMLIt == Stream.end()) |
393 | return make_error<EndOfFileError>(); |
394 | |
395 | Expected<std::unique_ptr<Remark>> MaybeResult = parseRemark(RemarkEntry&: *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 | |