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::ID = 0;
25
handleDiagnostic(const SMDiagnostic & Diag,void * Ctx)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);
33 OS << '\n';
34 OS.flush();
35 }
36
YAMLParseError(StringRef Msg,SourceMgr & SM,yaml::Stream & Stream,yaml::Node & Node)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
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
setupSM(std::string & LastErrorMessage)54 static 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.
parseMagic(StringRef & Buf)62 static Expected<bool> parseMagic(StringRef &Buf) {
63 if (!Buf.consume_front(remarks::Magic))
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
parseVersion(StringRef & Buf)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.");
76
77 uint64_t Version =
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));
85 return Version;
86 }
87
parseStrTabSize(StringRef & Buf)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.");
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
createYAMLParserFromMeta(StringRef Buf,std::optional<StringRef> ExternalFilePrependPath)98 Expected<std::unique_ptr<YAMLRemarkParser>> remarks::createYAMLParserFromMeta(
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(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.
129 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
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
YAMLRemarkParser(StringRef Buf)147 YAMLRemarkParser::YAMLRemarkParser(StringRef Buf)
148 : RemarkParser{Format::YAML}, SM(setupSM(LastErrorMessage)),
149 Stream(Buf, SM), YAMLIt(Stream.begin()) {}
150
error(StringRef Message,yaml::Node & Node)151 Error YAMLRemarkParser::error(StringRef Message, yaml::Node &Node) {
152 return make_error<YAMLParseError>(Message, SM, Stream, Node);
153 }
154
error()155 Error YAMLRemarkParser::error() {
156 if (LastErrorMessage.empty())
157 return Error::success();
158 Error E = make_error<YAMLParseError>(LastErrorMessage);
159 LastErrorMessage.clear();
160 return E;
161 }
162
163 Expected<std::unique_ptr<Remark>>
parseRemark(yaml::Document & RemarkEntry)164 YAMLRemarkParser::parseRemark(yaml::Document &RemarkEntry) {
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
parseType(yaml::MappingNode & Node)246 Expected<Type> YAMLRemarkParser::parseType(yaml::MappingNode &Node) {
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)
254 .Default(remarks::Type::Unknown);
255 if (Type == remarks::Type::Unknown)
256 return error("expected a remark tag.", Node);
257 return Type;
258 }
259
parseKey(yaml::KeyValueNode & Node)260 Expected<StringRef> YAMLRemarkParser::parseKey(yaml::KeyValueNode &Node) {
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
parseStr(yaml::KeyValueNode & Node)267 Expected<StringRef> YAMLRemarkParser::parseStr(yaml::KeyValueNode &Node) {
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
parseUnsigned(yaml::KeyValueNode & Node)286 Expected<unsigned> YAMLRemarkParser::parseUnsigned(yaml::KeyValueNode &Node) {
287 SmallVector<char, 4> Tmp;
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
297 Expected<RemarkLocation>
parseDebugLoc(yaml::KeyValueNode & Node)298 YAMLRemarkParser::parseDebugLoc(yaml::KeyValueNode &Node) {
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
parseArg(yaml::Node & Node)340 Expected<Argument> YAMLRemarkParser::parseArg(yaml::Node &Node) {
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
next()391 Expected<std::unique_ptr<Remark>> YAMLRemarkParser::next() {
392 if (YAMLIt == Stream.end())
393 return make_error<EndOfFileError>();
394
395 Expected<std::unique_ptr<Remark>> MaybeResult = parseRemark(*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