1 //===- YAMLRemarkParser.cpp -----------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file provides utility methods used by clients that want to use the
10 // parser for remark diagnostics in LLVM.
12 //===----------------------------------------------------------------------===//
14 #include "YAMLRemarkParser.h"
15 #include "llvm/ADT/StringSwitch.h"
16 #include "llvm/Support/Endian.h"
17 #include "llvm/Support/Path.h"
20 using namespace llvm::remarks
;
22 char YAMLParseError::ID
= 0;
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);
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
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
);
52 static SourceMgr
setupSM(std::string
&LastErrorMessage
) {
54 SM
.setDiagHandler(handleDiagnostic
, &LastErrorMessage
);
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
))
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.");
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.");
76 support::endian::read
<uint64_t, support::little
, support::unaligned
>(
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));
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.");
92 support::endian::read
<uint64_t, support::little
, support::unaligned
>(
94 Buf
= Buf
.drop_front(sizeof(uint64_t));
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.");
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
));
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
);
117 return isMeta
.takeError();
118 // If it's not recognized as metadata, roll back.
119 std::unique_ptr
<MemoryBuffer
> SeparateBuf
;
121 Expected
<uint64_t> Version
= parseVersion(Buf
);
123 return Version
.takeError();
125 Expected
<uint64_t> StrTabSize
= parseStrTabSize(Buf
);
127 return StrTabSize
.takeError();
129 // If the size of string table is not 0, try to build one.
130 if (*StrTabSize
!= 0) {
132 return createStringError(std::errc::illegal_byte_sequence
,
133 "String table already provided.");
134 Expected
<ParsedStringTable
> MaybeStrTab
= parseStrTab(Buf
, *StrTabSize
);
136 return MaybeStrTab
.takeError();
137 StrTab
= std::move(*MaybeStrTab
);
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
);
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
);
154 // Keep the buffer alive.
155 SeparateBuf
= std::move(*BufferOrErr
);
156 Buf
= SeparateBuf
->getBuffer();
160 std::unique_ptr
<YAMLRemarkParser
> Result
=
162 ? std::make_unique
<YAMLStrTabRemarkParser
>(Buf
, std::move(*StrTab
))
163 : std::make_unique
<YAMLRemarkParser
>(Buf
);
165 Result
->SeparateBuf
= std::move(SeparateBuf
);
166 return std::move(Result
);
169 YAMLRemarkParser::YAMLRemarkParser(StringRef Buf
)
170 : YAMLRemarkParser(Buf
, None
) {}
172 YAMLRemarkParser::YAMLRemarkParser(StringRef Buf
,
173 Optional
<ParsedStringTable
> StrTab
)
174 : RemarkParser
{Format::YAML
}, StrTab(std::move(StrTab
)),
175 SM(setupSM(LastErrorMessage
)), Stream(Buf
, SM
), YAMLIt(Stream
.begin()) {}
177 Error
YAMLRemarkParser::error(StringRef Message
, yaml::Node
&Node
) {
178 return make_error
<YAMLParseError
>(Message
, SM
, Stream
, Node
);
181 Error
YAMLRemarkParser::error() {
182 if (LastErrorMessage
.empty())
183 return Error::success();
184 Error E
= make_error
<YAMLParseError
>(LastErrorMessage
);
185 LastErrorMessage
.clear();
189 Expected
<std::unique_ptr
<Remark
>>
190 YAMLRemarkParser::parseRemark(yaml::Document
&RemarkEntry
) {
191 if (Error E
= error())
194 yaml::Node
*YAMLRoot
= RemarkEntry
.getRoot();
196 return createStringError(std::make_error_code(std::errc::invalid_argument
),
197 "not a valid YAML file.");
200 auto *Root
= dyn_cast
<yaml::MappingNode
>(YAMLRoot
);
202 return error("document root is not of mapping type.", *YAMLRoot
);
204 std::unique_ptr
<Remark
> Result
= std::make_unique
<Remark
>();
205 Remark
&TheRemark
= *Result
;
207 // First, the type. It needs special handling since is not part of the
209 Expected
<Type
> T
= parseType(*Root
);
211 return T
.takeError();
213 TheRemark
.RemarkType
= *T
;
215 // Then, parse the fields, one by one.
216 for (yaml::KeyValueNode
&RemarkField
: *Root
) {
217 Expected
<StringRef
> MaybeKey
= parseKey(RemarkField
);
219 return MaybeKey
.takeError();
220 StringRef KeyName
= *MaybeKey
;
222 if (KeyName
== "Pass") {
223 if (Expected
<StringRef
> MaybeStr
= parseStr(RemarkField
))
224 TheRemark
.PassName
= *MaybeStr
;
226 return MaybeStr
.takeError();
227 } else if (KeyName
== "Name") {
228 if (Expected
<StringRef
> MaybeStr
= parseStr(RemarkField
))
229 TheRemark
.RemarkName
= *MaybeStr
;
231 return MaybeStr
.takeError();
232 } else if (KeyName
== "Function") {
233 if (Expected
<StringRef
> MaybeStr
= parseStr(RemarkField
))
234 TheRemark
.FunctionName
= *MaybeStr
;
236 return MaybeStr
.takeError();
237 } else if (KeyName
== "Hotness") {
238 if (Expected
<unsigned> MaybeU
= parseUnsigned(RemarkField
))
239 TheRemark
.Hotness
= *MaybeU
;
241 return MaybeU
.takeError();
242 } else if (KeyName
== "DebugLoc") {
243 if (Expected
<RemarkLocation
> MaybeLoc
= parseDebugLoc(RemarkField
))
244 TheRemark
.Loc
= *MaybeLoc
;
246 return MaybeLoc
.takeError();
247 } else if (KeyName
== "Args") {
248 auto *Args
= dyn_cast
<yaml::SequenceNode
>(RemarkField
.getValue());
250 return error("wrong value type for key.", RemarkField
);
252 for (yaml::Node
&Arg
: *Args
) {
253 if (Expected
<Argument
> MaybeArg
= parseArg(Arg
))
254 TheRemark
.Args
.push_back(*MaybeArg
);
256 return MaybeArg
.takeError();
259 return error("unknown key.", RemarkField
);
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());
269 return std::move(Result
);
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
);
286 Expected
<StringRef
> YAMLRemarkParser::parseKey(yaml::KeyValueNode
&Node
) {
287 if (auto *Key
= dyn_cast
<yaml::ScalarNode
>(Node
.getKey()))
288 return Key
->getRawValue();
290 return error("key is not a string.", Node
);
293 Expected
<StringRef
> YAMLRemarkParser::parseStr(yaml::KeyValueNode
&Node
) {
294 auto *Value
= dyn_cast
<yaml::ScalarNode
>(Node
.getValue());
296 return error("expected a value of scalar type.", Node
);
297 StringRef Result
= Value
->getRawValue();
299 if (Result
.front() == '\'')
300 Result
= Result
.drop_front();
302 if (Result
.back() == '\'')
303 Result
= Result
.drop_back();
308 Expected
<unsigned> YAMLRemarkParser::parseUnsigned(yaml::KeyValueNode
&Node
) {
309 SmallVector
<char, 4> Tmp
;
310 auto *Value
= dyn_cast
<yaml::ScalarNode
>(Node
.getValue());
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
;
319 Expected
<RemarkLocation
>
320 YAMLRemarkParser::parseDebugLoc(yaml::KeyValueNode
&Node
) {
321 auto *DebugLoc
= dyn_cast
<yaml::MappingNode
>(Node
.getValue());
323 return error("expected a value of mapping type.", Node
);
325 Optional
<StringRef
> File
;
326 Optional
<unsigned> Line
;
327 Optional
<unsigned> Column
;
329 for (yaml::KeyValueNode
&DLNode
: *DebugLoc
) {
330 Expected
<StringRef
> MaybeKey
= parseKey(DLNode
);
332 return MaybeKey
.takeError();
333 StringRef KeyName
= *MaybeKey
;
335 if (KeyName
== "File") {
336 if (Expected
<StringRef
> MaybeStr
= parseStr(DLNode
))
339 return MaybeStr
.takeError();
340 } else if (KeyName
== "Column") {
341 if (Expected
<unsigned> MaybeU
= parseUnsigned(DLNode
))
344 return MaybeU
.takeError();
345 } else if (KeyName
== "Line") {
346 if (Expected
<unsigned> MaybeU
= parseUnsigned(DLNode
))
349 return MaybeU
.takeError();
351 return error("unknown entry in DebugLoc map.", DLNode
);
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
);
359 return RemarkLocation
{*File
, *Line
, *Column
};
362 Expected
<Argument
> YAMLRemarkParser::parseArg(yaml::Node
&Node
) {
363 auto *ArgMap
= dyn_cast
<yaml::MappingNode
>(&Node
);
365 return error("expected a value of mapping type.", Node
);
367 Optional
<StringRef
> KeyStr
;
368 Optional
<StringRef
> ValueStr
;
369 Optional
<RemarkLocation
> Loc
;
371 for (yaml::KeyValueNode
&ArgEntry
: *ArgMap
) {
372 Expected
<StringRef
> MaybeKey
= parseKey(ArgEntry
);
374 return MaybeKey
.takeError();
375 StringRef KeyName
= *MaybeKey
;
377 // Try to parse debug locs.
378 if (KeyName
== "DebugLoc") {
379 // Can't have multiple DebugLoc entries per argument.
381 return error("only one DebugLoc entry is allowed per argument.",
384 if (Expected
<RemarkLocation
> MaybeLoc
= parseDebugLoc(ArgEntry
)) {
388 return MaybeLoc
.takeError();
391 // If we already have a string, error out.
393 return error("only one string entry is allowed per argument.", ArgEntry
);
395 // Try to parse the value.
396 if (Expected
<StringRef
> MaybeStr
= parseStr(ArgEntry
))
397 ValueStr
= *MaybeStr
;
399 return MaybeStr
.takeError();
401 // Keep the key from the string.
406 return error("argument key is missing.", *ArgMap
);
408 return error("argument value is missing.", *ArgMap
);
410 return Argument
{*KeyStr
, *ValueStr
, Loc
};
413 Expected
<std::unique_ptr
<Remark
>> YAMLRemarkParser::next() {
414 if (YAMLIt
== Stream
.end())
415 return make_error
<EndOfFileError
>();
417 Expected
<std::unique_ptr
<Remark
>> MaybeResult
= parseRemark(*YAMLIt
);
419 // Avoid garbage input, set the iterator to the end.
420 YAMLIt
= Stream
.end();
421 return MaybeResult
.takeError();
426 return std::move(*MaybeResult
);
429 Expected
<StringRef
> YAMLStrTabRemarkParser::parseStr(yaml::KeyValueNode
&Node
) {
430 auto *Value
= dyn_cast
<yaml::ScalarNode
>(Node
.getValue());
432 return error("expected a value of scalar type.", Node
);
434 // If we have a string table, parse it as an unsigned.
436 if (Expected
<unsigned> MaybeStrID
= parseUnsigned(Node
))
439 return MaybeStrID
.takeError();
441 if (Expected
<StringRef
> Str
= (*StrTab
)[StrID
])
444 return Str
.takeError();
446 if (Result
.front() == '\'')
447 Result
= Result
.drop_front();
449 if (Result
.back() == '\'')
450 Result
= Result
.drop_back();