1 //===- RemarkParser.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 "llvm-c/Remarks.h"
15 #include "llvm/ADT/STLExtras.h"
16 #include "llvm/Support/SourceMgr.h"
17 #include "llvm/Support/YAMLTraits.h"
18 
19 using namespace llvm;
20 
21 namespace {
22 struct YAMLRemarkParser {
23   /// Source manager for better error messages.
24   SourceMgr SM;
25   /// Stream for yaml parsing.
26   yaml::Stream Stream;
27   /// Storage for the error stream.
28   std::string ErrorString;
29   /// The error stream.
30   raw_string_ostream ErrorStream;
31   /// Iterator in the YAML stream.
32   yaml::document_iterator DI;
33   /// The parsed remark (if any).
34   Optional<LLVMRemarkEntry> LastRemark;
35   /// Temporary parsing buffer for the arguments.
36   SmallVector<LLVMRemarkArg, 8> TmpArgs;
37   /// The state used by the parser to parse a remark entry. Invalidated with
38   /// every call to `parseYAMLElement`.
39   struct ParseState {
40     /// Temporary parsing buffer for the arguments.
41     SmallVectorImpl<LLVMRemarkArg> *Args;
42     StringRef Type;
43     StringRef Pass;
44     StringRef Name;
45     StringRef Function;
46     /// Optional.
47     Optional<StringRef> File;
48     Optional<unsigned> Line;
49     Optional<unsigned> Column;
50     Optional<unsigned> Hotness;
51 
52     ParseState(SmallVectorImpl<LLVMRemarkArg> &Args) : Args(&Args) {}
53     /// Use Args only as a **temporary** buffer.
54     ~ParseState() { Args->clear(); }
55   };
56 
57   ParseState State;
58 
59   /// Set to `true` if we had any errors during parsing.
60   bool HadAnyErrors = false;
61 
62   YAMLRemarkParser(StringRef Buf)
63       : SM(), Stream(Buf, SM), ErrorString(), ErrorStream(ErrorString),
64         DI(Stream.begin()), LastRemark(), TmpArgs(), State(TmpArgs) {
65     SM.setDiagHandler(YAMLRemarkParser::HandleDiagnostic, this);
66   }
67 
68   /// Parse a YAML element.
69   Error parseYAMLElement(yaml::Document &Remark);
70 
71 private:
72   /// Parse one key to a string.
73   /// otherwise.
74   Error parseKey(StringRef &Result, yaml::KeyValueNode &Node);
75   /// Parse one value to a string.
76   Error parseValue(StringRef &Result, yaml::KeyValueNode &Node);
77   /// Parse one value to an unsigned.
78   Error parseValue(Optional<unsigned> &Result, yaml::KeyValueNode &Node);
79   /// Parse a debug location.
80   Error parseDebugLoc(Optional<StringRef> &File, Optional<unsigned> &Line,
81                       Optional<unsigned> &Column, yaml::KeyValueNode &Node);
82   /// Parse an argument.
83   Error parseArg(SmallVectorImpl<LLVMRemarkArg> &TmpArgs, yaml::Node &Node);
84 
85   /// Handle a diagnostic from the YAML stream. Records the error in the
86   /// YAMLRemarkParser class.
87   static void HandleDiagnostic(const SMDiagnostic &Diag, void *Ctx) {
88     assert(Ctx && "Expected non-null Ctx in diagnostic handler.");
89     auto *Parser = static_cast<YAMLRemarkParser *>(Ctx);
90     Diag.print(/*ProgName=*/nullptr, Parser->ErrorStream, /*ShowColors*/ false,
91                /*ShowKindLabels*/ true);
92   }
93 };
94 
95 class ParseError : public ErrorInfo<ParseError> {
96 public:
97   static char ID;
98 
99   ParseError(StringRef Message, yaml::Node &Node)
100       : Message(Message), Node(Node) {}
101 
102   void log(raw_ostream &OS) const override { OS << Message; }
103   std::error_code convertToErrorCode() const override {
104     return inconvertibleErrorCode();
105   }
106 
107   StringRef getMessage() const { return Message; }
108   yaml::Node &getNode() const { return Node; }
109 
110 private:
111   StringRef Message; // No need to hold a full copy of the buffer.
112   yaml::Node &Node;
113 };
114 
115 char ParseError::ID = 0;
116 
117 static LLVMRemarkStringRef toRemarkStr(StringRef Str) {
118   return {Str.data(), static_cast<uint32_t>(Str.size())};
119 }
120 
121 Error YAMLRemarkParser::parseKey(StringRef &Result, yaml::KeyValueNode &Node) {
122   auto *Key = dyn_cast<yaml::ScalarNode>(Node.getKey());
123   if (!Key)
124     return make_error<ParseError>("key is not a string.", Node);
125 
126   Result = Key->getRawValue();
127   return Error::success();
128 }
129 
130 Error YAMLRemarkParser::parseValue(StringRef &Result,
131                                    yaml::KeyValueNode &Node) {
132   auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue());
133   if (!Value)
134     return make_error<ParseError>("expected a value of scalar type.", Node);
135   Result = Value->getRawValue();
136 
137   if (Result.front() == '\'')
138     Result = Result.drop_front();
139 
140   if (Result.back() == '\'')
141     Result = Result.drop_back();
142 
143   return Error::success();
144 }
145 
146 Error YAMLRemarkParser::parseValue(Optional<unsigned> &Result,
147                                    yaml::KeyValueNode &Node) {
148   SmallVector<char, 4> Tmp;
149   auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue());
150   if (!Value)
151     return make_error<ParseError>("expected a value of scalar type.", Node);
152   unsigned UnsignedValue = 0;
153   if (Value->getValue(Tmp).getAsInteger(10, UnsignedValue))
154     return make_error<ParseError>("expected a value of integer type.", *Value);
155   Result = UnsignedValue;
156   return Error::success();
157 }
158 
159 Error YAMLRemarkParser::parseDebugLoc(Optional<StringRef> &File,
160                                       Optional<unsigned> &Line,
161                                       Optional<unsigned> &Column,
162                                       yaml::KeyValueNode &Node) {
163   auto *DebugLoc = dyn_cast<yaml::MappingNode>(Node.getValue());
164   if (!DebugLoc)
165     return make_error<ParseError>("expected a value of mapping type.", Node);
166 
167   for (yaml::KeyValueNode &DLNode : *DebugLoc) {
168     StringRef KeyName;
169     if (Error E = parseKey(KeyName, DLNode))
170       return E;
171     if (KeyName == "File") {
172       File = StringRef(); // Set the optional to contain a default constructed
173                           // value, to be passed to the parsing function.
174       if (Error E = parseValue(*File, DLNode))
175         return E;
176     } else if (KeyName == "Column") {
177       if (Error E = parseValue(Column, DLNode))
178         return E;
179     } else if (KeyName == "Line") {
180       if (Error E = parseValue(Line, DLNode))
181         return E;
182     } else {
183       return make_error<ParseError>("unknown entry in DebugLoc map.", DLNode);
184     }
185   }
186 
187   // If any of the debug loc fields is missing, return an error.
188   if (!File || !Line || !Column)
189     return make_error<ParseError>("DebugLoc node incomplete.", Node);
190 
191   return Error::success();
192 }
193 
194 Error YAMLRemarkParser::parseArg(SmallVectorImpl<LLVMRemarkArg> &Args,
195                                  yaml::Node &Node) {
196   auto *ArgMap = dyn_cast<yaml::MappingNode>(&Node);
197   if (!ArgMap)
198     return make_error<ParseError>("expected a value of mapping type.", Node);
199 
200   StringRef ValueStr;
201   StringRef KeyStr;
202   Optional<StringRef> File;
203   Optional<unsigned> Line;
204   Optional<unsigned> Column;
205 
206   for (yaml::KeyValueNode &ArgEntry : *ArgMap) {
207     StringRef KeyName;
208     if (Error E = parseKey(KeyName, ArgEntry))
209       return E;
210 
211     // Try to parse debug locs.
212     if (KeyName == "DebugLoc") {
213       // Can't have multiple DebugLoc entries per argument.
214       if (File || Line || Column)
215         return make_error<ParseError>(
216             "only one DebugLoc entry is allowed per argument.", ArgEntry);
217 
218       if (Error E = parseDebugLoc(File, Line, Column, ArgEntry))
219         return E;
220       continue;
221     }
222 
223     // If we already have a string, error out.
224     if (!ValueStr.empty())
225       return make_error<ParseError>(
226           "only one string entry is allowed per argument.", ArgEntry);
227 
228     // Try to parse a string.
229     if (Error E = parseValue(ValueStr, ArgEntry))
230       return E;
231 
232     // Keep the key from the string.
233     KeyStr = KeyName;
234   }
235 
236   if (KeyStr.empty())
237     return make_error<ParseError>("argument key is missing.", *ArgMap);
238   if (ValueStr.empty())
239     return make_error<ParseError>("argument value is missing.", *ArgMap);
240 
241   Args.push_back(LLVMRemarkArg{
242       toRemarkStr(KeyStr), toRemarkStr(ValueStr),
243       LLVMRemarkDebugLoc{toRemarkStr(File.getValueOr(StringRef())),
244                          Line.getValueOr(0), Column.getValueOr(0)}});
245 
246   return Error::success();
247 }
248 
249 Error YAMLRemarkParser::parseYAMLElement(yaml::Document &Remark) {
250   // Parsing a new remark, clear the previous one.
251   LastRemark = None;
252   State = ParseState(TmpArgs);
253 
254   auto *Root = dyn_cast<yaml::MappingNode>(Remark.getRoot());
255   if (!Root)
256     return make_error<ParseError>("document root is not of mapping type.",
257                                   *Remark.getRoot());
258 
259   State.Type = Root->getRawTag();
260 
261   for (yaml::KeyValueNode &RemarkField : *Root) {
262     StringRef KeyName;
263     if (Error E = parseKey(KeyName, RemarkField))
264       return E;
265 
266     if (KeyName == "Pass") {
267       if (Error E = parseValue(State.Pass, RemarkField))
268         return E;
269     } else if (KeyName == "Name") {
270       if (Error E = parseValue(State.Name, RemarkField))
271         return E;
272     } else if (KeyName == "Function") {
273       if (Error E = parseValue(State.Function, RemarkField))
274         return E;
275     } else if (KeyName == "Hotness") {
276       if (Error E = parseValue(State.Hotness, RemarkField))
277         return E;
278     } else if (KeyName == "DebugLoc") {
279       if (Error E =
280               parseDebugLoc(State.File, State.Line, State.Column, RemarkField))
281         return E;
282     } else if (KeyName == "Args") {
283       auto *Args = dyn_cast<yaml::SequenceNode>(RemarkField.getValue());
284       if (!Args)
285         return make_error<ParseError>("wrong value type for key.", RemarkField);
286 
287       for (yaml::Node &Arg : *Args)
288         if (Error E = parseArg(*State.Args, Arg))
289           return E;
290     } else {
291       return make_error<ParseError>("unknown key.", RemarkField);
292     }
293   }
294 
295   // If the YAML parsing failed, don't even continue parsing. We might
296   // encounter malformed YAML.
297   if (Stream.failed())
298     return make_error<ParseError>("YAML parsing failed.", *Remark.getRoot());
299 
300   // Check if any of the mandatory fields are missing.
301   if (State.Type.empty() || State.Pass.empty() || State.Name.empty() ||
302       State.Function.empty())
303     return make_error<ParseError>("Type, Pass, Name or Function missing.",
304                                   *Remark.getRoot());
305 
306   LastRemark = LLVMRemarkEntry{
307       toRemarkStr(State.Type),
308       toRemarkStr(State.Pass),
309       toRemarkStr(State.Name),
310       toRemarkStr(State.Function),
311       LLVMRemarkDebugLoc{toRemarkStr(State.File.getValueOr(StringRef())),
312                          State.Line.getValueOr(0), State.Column.getValueOr(0)},
313       State.Hotness.getValueOr(0),
314       static_cast<uint32_t>(State.Args->size()),
315       State.Args->data()};
316 
317   return Error::success();
318 }
319 } // namespace
320 
321 // Create wrappers for C Binding types (see CBindingWrapping.h).
322 DEFINE_SIMPLE_CONVERSION_FUNCTIONS(YAMLRemarkParser, LLVMRemarkParserRef)
323 
324 extern "C" LLVMRemarkParserRef LLVMRemarkParserCreate(const void *Buf,
325                                                       uint64_t Size) {
326   return wrap(
327       new YAMLRemarkParser(StringRef(static_cast<const char *>(Buf), Size)));
328 }
329 
330 extern "C" LLVMRemarkEntry *
331 LLVMRemarkParserGetNext(LLVMRemarkParserRef Parser) {
332   YAMLRemarkParser &TheParser = *unwrap(Parser);
333   // Check for EOF.
334   if (TheParser.HadAnyErrors || TheParser.DI == TheParser.Stream.end())
335     return nullptr;
336 
337   // Try to parse an entry.
338   if (Error E = TheParser.parseYAMLElement(*TheParser.DI)) {
339     handleAllErrors(std::move(E), [&](const ParseError &PE) {
340       TheParser.Stream.printError(&PE.getNode(),
341                                   Twine(PE.getMessage()) + Twine('\n'));
342       TheParser.HadAnyErrors = true;
343     });
344     return nullptr;
345   }
346 
347   // Move on.
348   ++TheParser.DI;
349 
350   // Return the just-parsed remark.
351   if (Optional<LLVMRemarkEntry> &Entry = TheParser.LastRemark)
352     return &*Entry;
353   return nullptr;
354 }
355 
356 extern "C" LLVMBool LLVMRemarkParserHasError(LLVMRemarkParserRef Parser) {
357   return unwrap(Parser)->HadAnyErrors;
358 }
359 
360 extern "C" const char *
361 LLVMRemarkParserGetErrorMessage(LLVMRemarkParserRef Parser) {
362   return unwrap(Parser)->ErrorStream.str().c_str();
363 }
364 
365 extern "C" void LLVMRemarkParserDispose(LLVMRemarkParserRef Parser) {
366   delete unwrap(Parser);
367 }
368