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/SmallString.h"
16 #include "llvm/ADT/StringSwitch.h"
17 #include "llvm/Support/Endian.h"
18 #include "llvm/Support/Path.h"
22 using namespace llvm::remarks
;
24 char YAMLParseError::ID
= 0;
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);
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
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
);
54 static SourceMgr
setupSM(std::string
&LastErrorMessage
) {
56 SM
.setDiagHandler(handleDiagnostic
, &LastErrorMessage
);
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
))
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.");
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.");
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));
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.");
93 support::endian::read
<uint64_t, llvm::endianness::little
>(Buf
.data());
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
>> remarks::createYAMLParserFromMeta(
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
);
116 return isMeta
.takeError();
117 // If it's not recognized as metadata, roll back.
118 std::unique_ptr
<MemoryBuffer
> SeparateBuf
;
120 Expected
<uint64_t> Version
= parseVersion(Buf
);
122 return Version
.takeError();
124 Expected
<uint64_t> StrTabSize
= parseStrTabSize(Buf
);
126 return StrTabSize
.takeError();
128 // If the size of string table is not 0, try to build one.
129 if (*StrTabSize
!= 0) {
131 return createStringError(std::errc::illegal_byte_sequence
,
132 "String table already provided.");
133 Expected
<ParsedStringTable
> MaybeStrTab
= parseStrTab(Buf
, *StrTabSize
);
135 return MaybeStrTab
.takeError();
136 StrTab
= std::move(*MaybeStrTab
);
138 // If it starts with "---", there is no external file.
139 if (!Buf
.startswith("---")) {
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(FullPath
, ExternalFilePath
);
147 // Try to open the file and start parsing from there.
148 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> BufferOrErr
=
149 MemoryBuffer::getFile(FullPath
);
150 if (std::error_code EC
= BufferOrErr
.getError())
151 return createFileError(FullPath
, EC
);
153 // Keep the buffer alive.
154 SeparateBuf
= std::move(*BufferOrErr
);
155 Buf
= SeparateBuf
->getBuffer();
159 std::unique_ptr
<YAMLRemarkParser
> Result
=
161 ? std::make_unique
<YAMLStrTabRemarkParser
>(Buf
, std::move(*StrTab
))
162 : std::make_unique
<YAMLRemarkParser
>(Buf
);
164 Result
->SeparateBuf
= std::move(SeparateBuf
);
165 return std::move(Result
);
168 YAMLRemarkParser::YAMLRemarkParser(StringRef Buf
)
169 : YAMLRemarkParser(Buf
, std::nullopt
) {}
171 YAMLRemarkParser::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()) {}
176 Error
YAMLRemarkParser::error(StringRef Message
, yaml::Node
&Node
) {
177 return make_error
<YAMLParseError
>(Message
, SM
, Stream
, Node
);
180 Error
YAMLRemarkParser::error() {
181 if (LastErrorMessage
.empty())
182 return Error::success();
183 Error E
= make_error
<YAMLParseError
>(LastErrorMessage
);
184 LastErrorMessage
.clear();
188 Expected
<std::unique_ptr
<Remark
>>
189 YAMLRemarkParser::parseRemark(yaml::Document
&RemarkEntry
) {
190 if (Error E
= error())
193 yaml::Node
*YAMLRoot
= RemarkEntry
.getRoot();
195 return createStringError(std::make_error_code(std::errc::invalid_argument
),
196 "not a valid YAML file.");
199 auto *Root
= dyn_cast
<yaml::MappingNode
>(YAMLRoot
);
201 return error("document root is not of mapping type.", *YAMLRoot
);
203 std::unique_ptr
<Remark
> Result
= std::make_unique
<Remark
>();
204 Remark
&TheRemark
= *Result
;
206 // First, the type. It needs special handling since is not part of the
208 Expected
<Type
> T
= parseType(*Root
);
210 return T
.takeError();
212 TheRemark
.RemarkType
= *T
;
214 // Then, parse the fields, one by one.
215 for (yaml::KeyValueNode
&RemarkField
: *Root
) {
216 Expected
<StringRef
> MaybeKey
= parseKey(RemarkField
);
218 return MaybeKey
.takeError();
219 StringRef KeyName
= *MaybeKey
;
221 if (KeyName
== "Pass") {
222 if (Expected
<StringRef
> MaybeStr
= parseStr(RemarkField
))
223 TheRemark
.PassName
= *MaybeStr
;
225 return MaybeStr
.takeError();
226 } else if (KeyName
== "Name") {
227 if (Expected
<StringRef
> MaybeStr
= parseStr(RemarkField
))
228 TheRemark
.RemarkName
= *MaybeStr
;
230 return MaybeStr
.takeError();
231 } else if (KeyName
== "Function") {
232 if (Expected
<StringRef
> MaybeStr
= parseStr(RemarkField
))
233 TheRemark
.FunctionName
= *MaybeStr
;
235 return MaybeStr
.takeError();
236 } else if (KeyName
== "Hotness") {
237 if (Expected
<unsigned> MaybeU
= parseUnsigned(RemarkField
))
238 TheRemark
.Hotness
= *MaybeU
;
240 return MaybeU
.takeError();
241 } else if (KeyName
== "DebugLoc") {
242 if (Expected
<RemarkLocation
> MaybeLoc
= parseDebugLoc(RemarkField
))
243 TheRemark
.Loc
= *MaybeLoc
;
245 return MaybeLoc
.takeError();
246 } else if (KeyName
== "Args") {
247 auto *Args
= dyn_cast
<yaml::SequenceNode
>(RemarkField
.getValue());
249 return error("wrong value type for key.", RemarkField
);
251 for (yaml::Node
&Arg
: *Args
) {
252 if (Expected
<Argument
> MaybeArg
= parseArg(Arg
))
253 TheRemark
.Args
.push_back(*MaybeArg
);
255 return MaybeArg
.takeError();
258 return error("unknown key.", RemarkField
);
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("Type, Pass, Name or Function missing.",
266 *RemarkEntry
.getRoot());
268 return std::move(Result
);
271 Expected
<Type
> YAMLRemarkParser::parseType(yaml::MappingNode
&Node
) {
272 auto Type
= StringSwitch
<remarks::Type
>(Node
.getRawTag())
273 .Case("!Passed", remarks::Type::Passed
)
274 .Case("!Missed", remarks::Type::Missed
)
275 .Case("!Analysis", remarks::Type::Analysis
)
276 .Case("!AnalysisFPCommute", remarks::Type::AnalysisFPCommute
)
277 .Case("!AnalysisAliasing", remarks::Type::AnalysisAliasing
)
278 .Case("!Failure", remarks::Type::Failure
)
279 .Default(remarks::Type::Unknown
);
280 if (Type
== remarks::Type::Unknown
)
281 return error("expected a remark tag.", Node
);
285 Expected
<StringRef
> YAMLRemarkParser::parseKey(yaml::KeyValueNode
&Node
) {
286 if (auto *Key
= dyn_cast
<yaml::ScalarNode
>(Node
.getKey()))
287 return Key
->getRawValue();
289 return error("key is not a string.", Node
);
292 Expected
<StringRef
> YAMLRemarkParser::parseStr(yaml::KeyValueNode
&Node
) {
293 auto *Value
= dyn_cast
<yaml::ScalarNode
>(Node
.getValue());
294 yaml::BlockScalarNode
*ValueBlock
;
297 // Try to parse the value as a block node.
298 ValueBlock
= dyn_cast
<yaml::BlockScalarNode
>(Node
.getValue());
300 return error("expected a value of scalar type.", Node
);
301 Result
= ValueBlock
->getValue();
303 Result
= Value
->getRawValue();
305 if (Result
.front() == '\'')
306 Result
= Result
.drop_front();
308 if (Result
.back() == '\'')
309 Result
= Result
.drop_back();
314 Expected
<unsigned> YAMLRemarkParser::parseUnsigned(yaml::KeyValueNode
&Node
) {
315 SmallVector
<char, 4> Tmp
;
316 auto *Value
= dyn_cast
<yaml::ScalarNode
>(Node
.getValue());
318 return error("expected a value of scalar type.", Node
);
319 unsigned UnsignedValue
= 0;
320 if (Value
->getValue(Tmp
).getAsInteger(10, UnsignedValue
))
321 return error("expected a value of integer type.", *Value
);
322 return UnsignedValue
;
325 Expected
<RemarkLocation
>
326 YAMLRemarkParser::parseDebugLoc(yaml::KeyValueNode
&Node
) {
327 auto *DebugLoc
= dyn_cast
<yaml::MappingNode
>(Node
.getValue());
329 return error("expected a value of mapping type.", Node
);
331 std::optional
<StringRef
> File
;
332 std::optional
<unsigned> Line
;
333 std::optional
<unsigned> Column
;
335 for (yaml::KeyValueNode
&DLNode
: *DebugLoc
) {
336 Expected
<StringRef
> MaybeKey
= parseKey(DLNode
);
338 return MaybeKey
.takeError();
339 StringRef KeyName
= *MaybeKey
;
341 if (KeyName
== "File") {
342 if (Expected
<StringRef
> MaybeStr
= parseStr(DLNode
))
345 return MaybeStr
.takeError();
346 } else if (KeyName
== "Column") {
347 if (Expected
<unsigned> MaybeU
= parseUnsigned(DLNode
))
350 return MaybeU
.takeError();
351 } else if (KeyName
== "Line") {
352 if (Expected
<unsigned> MaybeU
= parseUnsigned(DLNode
))
355 return MaybeU
.takeError();
357 return error("unknown entry in DebugLoc map.", DLNode
);
361 // If any of the debug loc fields is missing, return an error.
362 if (!File
|| !Line
|| !Column
)
363 return error("DebugLoc node incomplete.", Node
);
365 return RemarkLocation
{*File
, *Line
, *Column
};
368 Expected
<Argument
> YAMLRemarkParser::parseArg(yaml::Node
&Node
) {
369 auto *ArgMap
= dyn_cast
<yaml::MappingNode
>(&Node
);
371 return error("expected a value of mapping type.", Node
);
373 std::optional
<StringRef
> KeyStr
;
374 std::optional
<StringRef
> ValueStr
;
375 std::optional
<RemarkLocation
> Loc
;
377 for (yaml::KeyValueNode
&ArgEntry
: *ArgMap
) {
378 Expected
<StringRef
> MaybeKey
= parseKey(ArgEntry
);
380 return MaybeKey
.takeError();
381 StringRef KeyName
= *MaybeKey
;
383 // Try to parse debug locs.
384 if (KeyName
== "DebugLoc") {
385 // Can't have multiple DebugLoc entries per argument.
387 return error("only one DebugLoc entry is allowed per argument.",
390 if (Expected
<RemarkLocation
> MaybeLoc
= parseDebugLoc(ArgEntry
)) {
394 return MaybeLoc
.takeError();
397 // If we already have a string, error out.
399 return error("only one string entry is allowed per argument.", ArgEntry
);
401 // Try to parse the value.
402 if (Expected
<StringRef
> MaybeStr
= parseStr(ArgEntry
))
403 ValueStr
= *MaybeStr
;
405 return MaybeStr
.takeError();
407 // Keep the key from the string.
412 return error("argument key is missing.", *ArgMap
);
414 return error("argument value is missing.", *ArgMap
);
416 return Argument
{*KeyStr
, *ValueStr
, Loc
};
419 Expected
<std::unique_ptr
<Remark
>> YAMLRemarkParser::next() {
420 if (YAMLIt
== Stream
.end())
421 return make_error
<EndOfFileError
>();
423 Expected
<std::unique_ptr
<Remark
>> MaybeResult
= parseRemark(*YAMLIt
);
425 // Avoid garbage input, set the iterator to the end.
426 YAMLIt
= Stream
.end();
427 return MaybeResult
.takeError();
432 return std::move(*MaybeResult
);
435 Expected
<StringRef
> YAMLStrTabRemarkParser::parseStr(yaml::KeyValueNode
&Node
) {
436 auto *Value
= dyn_cast
<yaml::ScalarNode
>(Node
.getValue());
437 yaml::BlockScalarNode
*ValueBlock
;
440 // Try to parse the value as a block node.
441 ValueBlock
= dyn_cast
<yaml::BlockScalarNode
>(Node
.getValue());
443 return error("expected a value of scalar type.", Node
);
444 Result
= ValueBlock
->getValue();
446 Result
= Value
->getRawValue();
447 // If we have a string table, parse it as an unsigned.
449 if (Expected
<unsigned> MaybeStrID
= parseUnsigned(Node
))
452 return MaybeStrID
.takeError();
454 if (Expected
<StringRef
> Str
= (*StrTab
)[StrID
])
457 return Str
.takeError();
459 if (Result
.front() == '\'')
460 Result
= Result
.drop_front();
462 if (Result
.back() == '\'')
463 Result
= Result
.drop_back();