xref: /llvm-project/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp (revision 0886440ef0ed0ad553522b731c841b81dc36944c)
1 //===- lib/DebugInfo/Symbolize/DIPrinter.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 defines the DIPrinter class, which is responsible for printing
10 // structures defined in DebugInfo/DIContext.h
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "llvm/DebugInfo/Symbolize/DIPrinter.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/DebugInfo/DIContext.h"
17 #include "llvm/Support/ErrorOr.h"
18 #include "llvm/Support/Format.h"
19 #include "llvm/Support/MemoryBuffer.h"
20 #include "llvm/Support/raw_ostream.h"
21 #include <algorithm>
22 #include <cmath>
23 #include <cstddef>
24 #include <cstdint>
25 #include <memory>
26 #include <string>
27 
28 namespace llvm {
29 namespace symbolize {
30 
31 class SourceCode {
32   std::unique_ptr<MemoryBuffer> MemBuf;
33 
34   std::optional<StringRef>
35   load(StringRef FileName, const std::optional<StringRef> &EmbeddedSource) {
36     if (Lines <= 0)
37       return std::nullopt;
38 
39     if (EmbeddedSource)
40       return EmbeddedSource;
41     else {
42       ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
43           MemoryBuffer::getFile(FileName);
44       if (!BufOrErr)
45         return std::nullopt;
46       MemBuf = std::move(*BufOrErr);
47       return MemBuf->getBuffer();
48     }
49   }
50 
51   std::optional<StringRef> pruneSource(const std::optional<StringRef> &Source) {
52     if (!Source)
53       return std::nullopt;
54     size_t FirstLinePos = StringRef::npos, Pos = 0;
55     for (int64_t L = 1; L <= LastLine; ++L, ++Pos) {
56       if (L == FirstLine)
57         FirstLinePos = Pos;
58       Pos = Source->find('\n', Pos);
59       if (Pos == StringRef::npos)
60         break;
61     }
62     if (FirstLinePos == StringRef::npos)
63       return std::nullopt;
64     return Source->substr(FirstLinePos, (Pos == StringRef::npos)
65                                             ? StringRef::npos
66                                             : Pos - FirstLinePos);
67   }
68 
69 public:
70   const int64_t Line;
71   const int Lines;
72   const int64_t FirstLine;
73   const int64_t LastLine;
74   const std::optional<StringRef> PrunedSource;
75 
76   SourceCode(StringRef FileName, int64_t Line, int Lines,
77              const std::optional<StringRef> &EmbeddedSource =
78                  std::optional<StringRef>())
79       : Line(Line), Lines(Lines),
80         FirstLine(std::max(static_cast<int64_t>(1), Line - Lines / 2)),
81         LastLine(FirstLine + Lines - 1),
82         PrunedSource(pruneSource(load(FileName, EmbeddedSource))) {}
83 
84   void format(raw_ostream &OS) {
85     if (!PrunedSource)
86       return;
87     size_t MaxLineNumberWidth = std::ceil(std::log10(LastLine));
88     int64_t L = FirstLine;
89     for (size_t Pos = 0; Pos < PrunedSource->size(); ++L) {
90       size_t PosEnd = PrunedSource->find('\n', Pos);
91       StringRef String = PrunedSource->substr(
92           Pos, (PosEnd == StringRef::npos) ? StringRef::npos : (PosEnd - Pos));
93       if (String.ends_with("\r"))
94         String = String.drop_back(1);
95       OS << format_decimal(L, MaxLineNumberWidth);
96       if (L == Line)
97         OS << " >: ";
98       else
99         OS << "  : ";
100       OS << String << '\n';
101       if (PosEnd == StringRef::npos)
102         break;
103       Pos = PosEnd + 1;
104     }
105   }
106 };
107 
108 void PlainPrinterBase::printHeader(std::optional<uint64_t> Address) {
109   if (Address.has_value() && Config.PrintAddress) {
110     OS << "0x";
111     OS.write_hex(*Address);
112     StringRef Delimiter = Config.Pretty ? ": " : "\n";
113     OS << Delimiter;
114   }
115 }
116 
117 // Prints source code around in the FileName the Line.
118 void PlainPrinterBase::printContext(SourceCode SourceCode) {
119   SourceCode.format(OS);
120 }
121 
122 void PlainPrinterBase::printFunctionName(StringRef FunctionName, bool Inlined) {
123   if (Config.PrintFunctions) {
124     if (FunctionName == DILineInfo::BadString)
125       FunctionName = DILineInfo::Addr2LineBadString;
126     StringRef Delimiter = Config.Pretty ? " at " : "\n";
127     StringRef Prefix = (Config.Pretty && Inlined) ? " (inlined by) " : "";
128     OS << Prefix << FunctionName << Delimiter;
129   }
130 }
131 
132 void LLVMPrinter::printSimpleLocation(StringRef Filename,
133                                       const DILineInfo &Info) {
134   OS << Filename << ':' << Info.Line << ':' << Info.Column;
135   if (Info.IsApproximateLine)
136     OS << " " << Info.ApproxString;
137   OS << "\n";
138   printContext(
139       SourceCode(Filename, Info.Line, Config.SourceContextLines, Info.Source));
140 }
141 
142 void GNUPrinter::printSimpleLocation(StringRef Filename,
143                                      const DILineInfo &Info) {
144   OS << Filename << ':' << Info.Line;
145   if (Info.IsApproximateLine)
146     OS << " " << Info.ApproxString;
147   if (Info.Discriminator)
148     OS << " (discriminator " << Info.Discriminator << ')';
149   OS << '\n';
150   printContext(
151       SourceCode(Filename, Info.Line, Config.SourceContextLines, Info.Source));
152 }
153 
154 void PlainPrinterBase::printVerbose(StringRef Filename,
155                                     const DILineInfo &Info) {
156   OS << "  Filename: " << Filename << '\n';
157   if (Info.StartLine) {
158     OS << "  Function start filename: " << Info.StartFileName << '\n';
159     OS << "  Function start line: " << Info.StartLine << '\n';
160   }
161   printStartAddress(Info);
162   OS << "  Line: " << Info.Line << '\n';
163   OS << "  Column: " << Info.Column << '\n';
164   if (Info.Discriminator)
165     OS << "  Discriminator: " << Info.Discriminator << '\n';
166   if (Info.IsApproximateLine)
167     OS << "  Approximate: true" << '\n';
168 }
169 
170 void LLVMPrinter::printStartAddress(const DILineInfo &Info) {
171   if (Info.StartAddress) {
172     OS << "  Function start address: 0x";
173     OS.write_hex(*Info.StartAddress);
174     OS << '\n';
175   }
176 }
177 
178 void LLVMPrinter::printFooter() { OS << '\n'; }
179 
180 void PlainPrinterBase::print(const DILineInfo &Info, bool Inlined) {
181   printFunctionName(Info.FunctionName, Inlined);
182   StringRef Filename = Info.FileName;
183   if (Filename == DILineInfo::BadString)
184     Filename = DILineInfo::Addr2LineBadString;
185   if (Config.Verbose)
186     printVerbose(Filename, Info);
187   else
188     printSimpleLocation(Filename, Info);
189 }
190 
191 void PlainPrinterBase::print(const Request &Request, const DILineInfo &Info) {
192   printHeader(Request.Address);
193   print(Info, false);
194   printFooter();
195 }
196 
197 void PlainPrinterBase::print(const Request &Request,
198                              const DIInliningInfo &Info) {
199   printHeader(*Request.Address);
200   uint32_t FramesNum = Info.getNumberOfFrames();
201   if (FramesNum == 0)
202     print(DILineInfo(), false);
203   else
204     for (uint32_t I = 0; I < FramesNum; ++I)
205       print(Info.getFrame(I), I > 0);
206   printFooter();
207 }
208 
209 void PlainPrinterBase::print(const Request &Request, const DIGlobal &Global) {
210   printHeader(*Request.Address);
211   StringRef Name = Global.Name;
212   if (Name == DILineInfo::BadString)
213     Name = DILineInfo::Addr2LineBadString;
214   OS << Name << "\n";
215   OS << Global.Start << " " << Global.Size << "\n";
216   if (Global.DeclFile.empty())
217     OS << "??:?\n";
218   else
219     OS << Global.DeclFile << ":" << Global.DeclLine << "\n";
220   printFooter();
221 }
222 
223 void PlainPrinterBase::print(const Request &Request,
224                              const std::vector<DILocal> &Locals) {
225   printHeader(*Request.Address);
226   if (Locals.empty())
227     OS << DILineInfo::Addr2LineBadString << '\n';
228   else
229     for (const DILocal &L : Locals) {
230       if (L.FunctionName.empty())
231         OS << DILineInfo::Addr2LineBadString;
232       else
233         OS << L.FunctionName;
234       OS << '\n';
235 
236       if (L.Name.empty())
237         OS << DILineInfo::Addr2LineBadString;
238       else
239         OS << L.Name;
240       OS << '\n';
241 
242       if (L.DeclFile.empty())
243         OS << DILineInfo::Addr2LineBadString;
244       else
245         OS << L.DeclFile;
246 
247       OS << ':' << L.DeclLine << '\n';
248 
249       if (L.FrameOffset)
250         OS << *L.FrameOffset;
251       else
252         OS << DILineInfo::Addr2LineBadString;
253       OS << ' ';
254 
255       if (L.Size)
256         OS << *L.Size;
257       else
258         OS << DILineInfo::Addr2LineBadString;
259       OS << ' ';
260 
261       if (L.TagOffset)
262         OS << *L.TagOffset;
263       else
264         OS << DILineInfo::Addr2LineBadString;
265       OS << '\n';
266     }
267   printFooter();
268 }
269 
270 void PlainPrinterBase::print(const Request &Request,
271                              const std::vector<DILineInfo> &Locations) {
272   if (Locations.empty()) {
273     print(Request, DILineInfo());
274   } else {
275     for (const DILineInfo &L : Locations)
276       print(L, false);
277     printFooter();
278   }
279 }
280 
281 bool PlainPrinterBase::printError(const Request &Request,
282                                   const ErrorInfoBase &ErrorInfo) {
283   ErrHandler(ErrorInfo, Request.ModuleName);
284   // Print an empty struct too.
285   return true;
286 }
287 
288 static std::string toHex(uint64_t V) {
289   return ("0x" + Twine::utohexstr(V)).str();
290 }
291 
292 static json::Object toJSON(const Request &Request, StringRef ErrorMsg = "") {
293   json::Object Json({{"ModuleName", Request.ModuleName.str()}});
294   if (!Request.Symbol.empty())
295     Json["SymName"] = Request.Symbol.str();
296   if (Request.Address)
297     Json["Address"] = toHex(*Request.Address);
298   if (!ErrorMsg.empty())
299     Json["Error"] = json::Object({{"Message", ErrorMsg.str()}});
300   return Json;
301 }
302 
303 static json::Object toJSON(const DILineInfo &LineInfo) {
304   json::Object Obj = json::Object(
305       {{"FunctionName", LineInfo.FunctionName != DILineInfo::BadString
306                             ? LineInfo.FunctionName
307                             : ""},
308        {"StartFileName", LineInfo.StartFileName != DILineInfo::BadString
309                              ? LineInfo.StartFileName
310                              : ""},
311        {"StartLine", LineInfo.StartLine},
312        {"StartAddress",
313         LineInfo.StartAddress ? toHex(*LineInfo.StartAddress) : ""},
314        {"FileName",
315         LineInfo.FileName != DILineInfo::BadString ? LineInfo.FileName : ""},
316        {"Line", LineInfo.Line},
317        {"Column", LineInfo.Column},
318        {"Discriminator", LineInfo.Discriminator}});
319   if (LineInfo.IsApproximateLine)
320     Obj.insert({"Approximate", LineInfo.IsApproximateLine});
321   return Obj;
322 }
323 
324 void JSONPrinter::print(const Request &Request, const DILineInfo &Info) {
325   DIInliningInfo InliningInfo;
326   InliningInfo.addFrame(Info);
327   print(Request, InliningInfo);
328 }
329 
330 void JSONPrinter::print(const Request &Request, const DIInliningInfo &Info) {
331   json::Array Array;
332   for (uint32_t I = 0, N = Info.getNumberOfFrames(); I < N; ++I) {
333     const DILineInfo &LineInfo = Info.getFrame(I);
334     json::Object Object = toJSON(LineInfo);
335     SourceCode SourceCode(LineInfo.FileName, LineInfo.Line,
336                           Config.SourceContextLines, LineInfo.Source);
337     std::string FormattedSource;
338     raw_string_ostream Stream(FormattedSource);
339     SourceCode.format(Stream);
340     if (!FormattedSource.empty())
341       Object["Source"] = std::move(FormattedSource);
342     Array.push_back(std::move(Object));
343   }
344   json::Object Json = toJSON(Request);
345   Json["Symbol"] = std::move(Array);
346   if (ObjectList)
347     ObjectList->push_back(std::move(Json));
348   else
349     printJSON(std::move(Json));
350 }
351 
352 void JSONPrinter::print(const Request &Request, const DIGlobal &Global) {
353   json::Object Data(
354       {{"Name", Global.Name != DILineInfo::BadString ? Global.Name : ""},
355        {"Start", toHex(Global.Start)},
356        {"Size", toHex(Global.Size)}});
357   json::Object Json = toJSON(Request);
358   Json["Data"] = std::move(Data);
359   if (ObjectList)
360     ObjectList->push_back(std::move(Json));
361   else
362     printJSON(std::move(Json));
363 }
364 
365 void JSONPrinter::print(const Request &Request,
366                         const std::vector<DILocal> &Locals) {
367   json::Array Frame;
368   for (const DILocal &Local : Locals) {
369     json::Object FrameObject(
370         {{"FunctionName", Local.FunctionName},
371          {"Name", Local.Name},
372          {"DeclFile", Local.DeclFile},
373          {"DeclLine", int64_t(Local.DeclLine)},
374          {"Size", Local.Size ? toHex(*Local.Size) : ""},
375          {"TagOffset", Local.TagOffset ? toHex(*Local.TagOffset) : ""}});
376     if (Local.FrameOffset)
377       FrameObject["FrameOffset"] = *Local.FrameOffset;
378     Frame.push_back(std::move(FrameObject));
379   }
380   json::Object Json = toJSON(Request);
381   Json["Frame"] = std::move(Frame);
382   if (ObjectList)
383     ObjectList->push_back(std::move(Json));
384   else
385     printJSON(std::move(Json));
386 }
387 
388 void JSONPrinter::print(const Request &Request,
389                         const std::vector<DILineInfo> &Locations) {
390   json::Array Definitions;
391   for (const DILineInfo &L : Locations)
392     Definitions.push_back(toJSON(L));
393   json::Object Json = toJSON(Request);
394   Json["Loc"] = std::move(Definitions);
395   if (ObjectList)
396     ObjectList->push_back(std::move(Json));
397   else
398     printJSON(std::move(Json));
399 }
400 
401 bool JSONPrinter::printError(const Request &Request,
402                              const ErrorInfoBase &ErrorInfo) {
403   json::Object Json = toJSON(Request, ErrorInfo.message());
404   if (ObjectList)
405     ObjectList->push_back(std::move(Json));
406   else
407     printJSON(std::move(Json));
408   return false;
409 }
410 
411 void JSONPrinter::listBegin() {
412   assert(!ObjectList);
413   ObjectList = std::make_unique<json::Array>();
414 }
415 
416 void JSONPrinter::listEnd() {
417   assert(ObjectList);
418   printJSON(std::move(*ObjectList));
419   ObjectList.reset();
420 }
421 
422 } // end namespace symbolize
423 } // end namespace llvm
424