LLVM 22.0.0git
YAMLRemarkParser.cpp
Go to the documentation of this file.
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"
17#include "llvm/Support/Endian.h"
18#include "llvm/Support/Path.h"
19#include <optional>
20
21using namespace llvm;
22using namespace llvm::remarks;
23
24char YAMLParseError::ID = 0;
25
26static 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);
33 OS << '\n';
34 OS.flush();
35}
36
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(handleDiagnostic, &Message);
49 Stream.printError(&Node, Twine(Msg) + Twine('\n'));
50 // Restore the old handlers.
51 SM.setDiagHandler(OldDiagHandler, OldDiagCtx);
52}
53
54static SourceMgr setupSM(std::string &LastErrorMessage) {
55 SourceMgr SM;
56 SM.setDiagHandler(handleDiagnostic, &LastErrorMessage);
57 return SM;
58}
59
60// Parse the magic number. This function returns true if this represents remark
61// metadata, false otherwise.
64 return false;
65
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.");
69 return true;
70}
71
73 if (Buf.size() < sizeof(uint64_t))
74 return createStringError(std::errc::illegal_byte_sequence,
75 "Expecting version number.");
76
78 support::endian::read<uint64_t, llvm::endianness::little>(Buf.data());
80 return createStringError(std::errc::illegal_byte_sequence,
81 "Mismatching remark version. Got %" PRId64
82 ", expected %" PRId64 ".",
84 Buf = Buf.drop_front(sizeof(uint64_t));
85 return Version;
86}
87
89 if (Buf.size() < sizeof(uint64_t))
90 return createStringError(std::errc::illegal_byte_sequence,
91 "Expecting string table size.");
92 uint64_t StrTabSize =
93 support::endian::read<uint64_t, llvm::endianness::little>(Buf.data());
94 Buf = Buf.drop_front(sizeof(uint64_t));
95 return StrTabSize;
96}
97
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) {
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(std::errc::illegal_byte_sequence,
117 "String table unsupported for YAML format.");
118 }
119 // If it starts with "---", there is no external file.
120 if (!Buf.starts_with("---")) {
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(FullPath, ExternalFilePath);
127
128 // Try to open the file and start parsing from there.
130 MemoryBuffer::getFile(FullPath);
131 if (std::error_code EC = BufferOrErr.getError())
132 return createFileError(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>(Buf);
142 if (SeparateBuf)
143 Result->SeparateBuf = std::move(SeparateBuf);
144 return std::move(Result);
145}
146
148 : RemarkParser{Format::YAML}, SM(setupSM(LastErrorMessage)),
149 Stream(Buf, SM), YAMLIt(Stream.begin()) {}
150
152 return make_error<YAMLParseError>(Message, SM, Stream, Node);
153}
154
156 if (LastErrorMessage.empty())
157 return Error::success();
158 Error E = make_error<YAMLParseError>(LastErrorMessage);
159 LastErrorMessage.clear();
160 return E;
161}
162
165 if (Error E = error())
166 return std::move(E);
167
168 yaml::Node *YAMLRoot = RemarkEntry.getRoot();
169 if (!YAMLRoot) {
170 return createStringError(std::make_error_code(std::errc::invalid_argument),
171 "not a valid YAML file.");
172 }
173
174 auto *Root = dyn_cast<yaml::MappingNode>(YAMLRoot);
175 if (!Root)
176 return error("document root is not of mapping type.", *YAMLRoot);
177
178 std::unique_ptr<Remark> Result = std::make_unique<Remark>();
179 Remark &TheRemark = *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(*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 &RemarkField : *Root) {
191 Expected<StringRef> MaybeKey = parseKey(RemarkField);
192 if (!MaybeKey)
193 return MaybeKey.takeError();
194 StringRef KeyName = *MaybeKey;
195
196 if (KeyName == "Pass") {
197 if (Expected<StringRef> MaybeStr = parseStr(RemarkField))
198 TheRemark.PassName = *MaybeStr;
199 else
200 return MaybeStr.takeError();
201 } else if (KeyName == "Name") {
202 if (Expected<StringRef> MaybeStr = parseStr(RemarkField))
203 TheRemark.RemarkName = *MaybeStr;
204 else
205 return MaybeStr.takeError();
206 } else if (KeyName == "Function") {
207 if (Expected<StringRef> MaybeStr = parseStr(RemarkField))
208 TheRemark.FunctionName = *MaybeStr;
209 else
210 return MaybeStr.takeError();
211 } else if (KeyName == "Hotness") {
212 if (Expected<unsigned> MaybeU = parseUnsigned(RemarkField))
213 TheRemark.Hotness = *MaybeU;
214 else
215 return MaybeU.takeError();
216 } else if (KeyName == "DebugLoc") {
217 if (Expected<RemarkLocation> MaybeLoc = parseDebugLoc(RemarkField))
218 TheRemark.Loc = *MaybeLoc;
219 else
220 return MaybeLoc.takeError();
221 } else if (KeyName == "Args") {
222 auto *Args = dyn_cast<yaml::SequenceNode>(RemarkField.getValue());
223 if (!Args)
224 return error("wrong value type for key.", RemarkField);
225
226 for (yaml::Node &Arg : *Args) {
227 if (Expected<Argument> MaybeArg = parseArg(Arg))
228 TheRemark.Args.push_back(*MaybeArg);
229 else
230 return MaybeArg.takeError();
231 }
232 } else {
233 return error("unknown key.", 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("Type, Pass, Name or Function missing.",
241 *RemarkEntry.getRoot());
242
243 return std::move(Result);
244}
245
247 auto Type = StringSwitch<remarks::Type>(Node.getRawTag())
248 .Case("!Passed", remarks::Type::Passed)
249 .Case("!Missed", remarks::Type::Missed)
250 .Case("!Analysis", remarks::Type::Analysis)
251 .Case("!AnalysisFPCommute", remarks::Type::AnalysisFPCommute)
252 .Case("!AnalysisAliasing", remarks::Type::AnalysisAliasing)
253 .Case("!Failure", remarks::Type::Failure)
256 return error("expected a remark tag.", Node);
257 return Type;
258}
259
261 if (auto *Key = dyn_cast<yaml::ScalarNode>(Node.getKey()))
262 return Key->getRawValue();
263
264 return error("key is not a string.", Node);
265}
266
268 auto *Value = dyn_cast<yaml::ScalarNode>(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>(Node.getValue());
274 if (!ValueBlock)
275 return error("expected a value of scalar type.", Node);
276 Result = ValueBlock->getValue();
277 } else
278 Result = Value->getRawValue();
279
280 Result.consume_front("\'");
281 Result.consume_back("\'");
282
283 return Result;
284}
285
288 auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue());
289 if (!Value)
290 return error("expected a value of scalar type.", Node);
291 unsigned UnsignedValue = 0;
292 if (Value->getValue(Tmp).getAsInteger(10, UnsignedValue))
293 return error("expected a value of integer type.", *Value);
294 return UnsignedValue;
295}
296
299 auto *DebugLoc = dyn_cast<yaml::MappingNode>(Node.getValue());
300 if (!DebugLoc)
301 return error("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(DLNode);
309 if (!MaybeKey)
310 return MaybeKey.takeError();
311 StringRef KeyName = *MaybeKey;
312
313 if (KeyName == "File") {
314 if (Expected<StringRef> MaybeStr = parseStr(DLNode))
315 File = *MaybeStr;
316 else
317 return MaybeStr.takeError();
318 } else if (KeyName == "Column") {
319 if (Expected<unsigned> MaybeU = parseUnsigned(DLNode))
320 Column = *MaybeU;
321 else
322 return MaybeU.takeError();
323 } else if (KeyName == "Line") {
324 if (Expected<unsigned> MaybeU = parseUnsigned(DLNode))
325 Line = *MaybeU;
326 else
327 return MaybeU.takeError();
328 } else {
329 return error("unknown entry in DebugLoc map.", 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("DebugLoc node incomplete.", Node);
336
337 return RemarkLocation{*File, *Line, *Column};
338}
339
341 auto *ArgMap = dyn_cast<yaml::MappingNode>(&Node);
342 if (!ArgMap)
343 return error("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(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("only one DebugLoc entry is allowed per argument.",
360 ArgEntry);
361
362 if (Expected<RemarkLocation> MaybeLoc = parseDebugLoc(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("only one string entry is allowed per argument.", ArgEntry);
372
373 // Try to parse the value.
374 if (Expected<StringRef> MaybeStr = parseStr(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("argument key is missing.", *ArgMap);
385 if (!ValueStr)
386 return error("argument value is missing.", *ArgMap);
387
388 return Argument{*KeyStr, *ValueStr, Loc};
389}
390
392 if (YAMLIt == Stream.end())
393 return make_error<EndOfFileError>();
394
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}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static Version parseVersion(StringRef Name)
raw_pwrite_stream & OS
This file defines the SmallString class.
This file implements the StringSwitch template, which mimics a switch() statement whose cases are str...
static SourceMgr setupSM(std::string &LastErrorMessage)
static Expected< uint64_t > parseStrTabSize(StringRef &Buf)
static void handleDiagnostic(const SMDiagnostic &Diag, void *Ctx)
static Expected< bool > parseMagic(StringRef &Buf)
A debug info location.
Definition: DebugLoc.h:124
Represents either an error or a value T.
Definition: ErrorOr.h:56
std::error_code getError() const
Definition: ErrorOr.h:152
Lightweight error class with error context and mandatory checking.
Definition: Error.h:159
static ErrorSuccess success()
Create a success value.
Definition: Error.h:336
Tagged union holding either a T or a Error.
Definition: Error.h:485
Error takeError()
Take ownership of the stored error.
Definition: Error.h:612
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFile(const Twine &Filename, bool IsText=false, bool RequiresNullTerminator=true, bool IsVolatile=false, std::optional< Align > Alignment=std::nullopt)
Open the specified file as a MemoryBuffer, returning a new MemoryBuffer if successful,...
Instances of this class encapsulate one diagnostic report, allowing printing to a raw_ostream as a ca...
Definition: SourceMgr.h:282
LLVM_ABI void print(const char *ProgName, raw_ostream &S, bool ShowColors=true, bool ShowKindLabel=true, bool ShowLocation=true) const
Definition: SourceMgr.cpp:484
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition: SmallString.h:26
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1197
This owns the files read by a parser, handles include stacks, and handles diagnostic wrangling.
Definition: SourceMgr.h:32
void * getDiagContext() const
Definition: SourceMgr.h:119
DiagHandlerTy getDiagHandler() const
Definition: SourceMgr.h:118
void setDiagHandler(DiagHandlerTy DH, void *Ctx=nullptr)
Specify a diagnostic handler to be invoked every time PrintMessage is called.
Definition: SourceMgr.h:113
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:55
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition: StringRef.h:269
constexpr bool empty() const
empty - Check if the string is empty.
Definition: StringRef.h:151
StringRef drop_front(size_t N=1) const
Return a StringRef equal to 'this' but with the first N elements dropped.
Definition: StringRef.h:619
constexpr size_t size() const
size - Get the string size.
Definition: StringRef.h:154
constexpr const char * data() const
data - Get a pointer to the start of the string (which may not be null terminated).
Definition: StringRef.h:148
bool consume_front(StringRef Prefix)
Returns true if this StringRef has the given prefix and removes that prefix.
Definition: StringRef.h:645
A switch()-like statement whose cases are string literals.
Definition: StringSwitch.h:43
StringSwitch & Case(StringLiteral S, T Value)
Definition: StringSwitch.h:68
R Default(T Value)
Definition: StringSwitch.h:177
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition: Twine.h:82
LLVM Value Representation.
Definition: Value.h:75
A raw_ostream that writes to an std::string.
Definition: raw_ostream.h:662
YAMLParseError(StringRef Message, SourceMgr &SM, yaml::Stream &Stream, yaml::Node &Node)
A block scalar node is an opaque datum that can be presented as a series of zero or more Unicode scal...
Definition: YAMLParser.h:262
StringRef getValue() const
Gets the value of this node as a StringRef.
Definition: YAMLParser.h:275
A YAML Stream is a sequence of Documents.
Definition: YAMLParser.h:538
Node * getRoot()
Parse and return the root level node.
Definition: YAMLParser.h:550
A key and value pair.
Definition: YAMLParser.h:292
Represents a YAML map created from either a block map for a flow map.
Definition: YAMLParser.h:421
Abstract base class for all Nodes.
Definition: YAMLParser.h:121
This class represents a YAML stream potentially containing multiple documents.
Definition: YAMLParser.h:88
LLVM_ABI document_iterator end()
LLVM_ABI void printError(Node *N, const Twine &Msg, SourceMgr::DiagKind Kind=SourceMgr::DK_Error)
constexpr StringLiteral Magic("REMARKS")
Format
The format used for serializing/deserializing remarks.
Definition: RemarkFormat.h:26
constexpr uint64_t CurrentRemarkVersion
The current version of the remark entry.
Definition: Remark.h:29
Expected< std::unique_ptr< YAMLRemarkParser > > createYAMLParserFromMeta(StringRef Buf, std::optional< StringRef > ExternalFilePrependPath=std::nullopt)
Type
The type of the remark.
Definition: Remark.h:66
LLVM_ABI void append(SmallVectorImpl< char > &path, const Twine &a, const Twine &b="", const Twine &c="", const Twine &d="")
Append to path.
Definition: Path.cpp:456
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
Error createFileError(const Twine &F, Error E)
Concatenate a source file path and/or name with an Error.
Definition: Error.h:1399
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition: Error.h:1305
A key-value pair with a debug location that is used to display the remarks at the right place in the ...
Definition: Remark.h:47
The debug location used to track a remark back to the source file.
Definition: Remark.h:32
Parser used to parse a raw buffer to remarks::Remark objects.
Definition: RemarkParser.h:41
A remark type used for both emission and parsing.
Definition: Remark.h:98
Type RemarkType
The type of the remark.
Definition: Remark.h:100
StringRef PassName
Name of the pass that triggers the emission of this remark.
Definition: Remark.h:103
std::optional< RemarkLocation > Loc
The location in the source file of the remark.
Definition: Remark.h:114
std::optional< uint64_t > Hotness
If profile information is available, this is the number of times the corresponding code was executed ...
Definition: Remark.h:118
StringRef RemarkName
Textual identifier for the remark (single-word, camel-case).
Definition: Remark.h:108
StringRef FunctionName
Mangled name of the function that triggers the emssion of this remark.
Definition: Remark.h:111
SmallVector< Argument, 5 > Args
Arguments collected via the streaming interface.
Definition: Remark.h:121
yaml::document_iterator YAMLIt
Iterator in the YAML stream.
Error error()
Create a YAMLParseError error from an existing error generated by the YAML parser.
Expected< unsigned > parseUnsigned(yaml::KeyValueNode &Node)
Parse one value to an unsigned.
yaml::Stream Stream
Stream for yaml parsing.
Expected< RemarkLocation > parseDebugLoc(yaml::KeyValueNode &Node)
Parse a debug location.
virtual Expected< StringRef > parseStr(yaml::KeyValueNode &Node)
Parse one value to a string.
Expected< StringRef > parseKey(yaml::KeyValueNode &Node)
Parse one key to a string.
Expected< Argument > parseArg(yaml::Node &Node)
Parse an argument.
Expected< std::unique_ptr< Remark > > parseRemark(yaml::Document &Remark)
Parse a YAML remark to a remarks::Remark object.
Expected< std::unique_ptr< Remark > > next() override
If no error occurs, this returns a valid Remark object.
SourceMgr SM
Source manager for better error messages.
std::string LastErrorMessage
Last error message that can come from the YAML parser diagnostics.
Expected< Type > parseType(yaml::MappingNode &Node)
Parse the type of a remark to an enum type.