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/LineIterator.h"
20 #include "llvm/Support/MemoryBuffer.h"
21 #include "llvm/Support/Path.h"
22 #include "llvm/Support/raw_ostream.h"
23 #include <algorithm>
24 #include <cmath>
25 #include <cstddef>
26 #include <cstdint>
27 #include <memory>
28 #include <string>
29 
30 namespace llvm {
31 namespace symbolize {
32 
33 void PlainPrinterBase::printHeader(uint64_t Address) {
34   if (Config.PrintAddress) {
35     OS << "0x";
36     OS.write_hex(Address);
37     StringRef Delimiter = Config.Pretty ? ": " : "\n";
38     OS << Delimiter;
39   }
40 }
41 
42 // Prints source code around in the FileName the Line.
43 void PlainPrinterBase::printContext(StringRef FileName, int64_t Line) {
44   if (Config.SourceContextLines <= 0)
45     return;
46 
47   ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
48       MemoryBuffer::getFile(FileName);
49   if (!BufOrErr)
50     return;
51 
52   std::unique_ptr<MemoryBuffer> Buf = std::move(BufOrErr.get());
53   int64_t FirstLine =
54       std::max(static_cast<int64_t>(1), Line - Config.SourceContextLines / 2);
55   int64_t LastLine = FirstLine + Config.SourceContextLines;
56   size_t MaxLineNumberWidth = std::ceil(std::log10(LastLine));
57 
58   for (line_iterator I = line_iterator(*Buf, false);
59        !I.is_at_eof() && I.line_number() <= LastLine; ++I) {
60     int64_t L = I.line_number();
61     if (L >= FirstLine && L <= LastLine) {
62       OS << format_decimal(L, MaxLineNumberWidth);
63       if (L == Line)
64         OS << " >: ";
65       else
66         OS << "  : ";
67       OS << *I << "\n";
68     }
69   }
70 }
71 
72 void PlainPrinterBase::printFunctionName(StringRef FunctionName, bool Inlined) {
73   if (Config.PrintFunctions) {
74     if (FunctionName == DILineInfo::BadString)
75       FunctionName = DILineInfo::Addr2LineBadString;
76     StringRef Delimiter = Config.Pretty ? " at " : "\n";
77     StringRef Prefix = (Config.Pretty && Inlined) ? " (inlined by) " : "";
78     OS << Prefix << FunctionName << Delimiter;
79   }
80 }
81 
82 void LLVMPrinter::printSimpleLocation(StringRef Filename,
83                                       const DILineInfo &Info) {
84   OS << Filename << ':' << Info.Line << ':' << Info.Column << '\n';
85   printContext(Filename, Info.Line);
86 }
87 
88 void GNUPrinter::printSimpleLocation(StringRef Filename,
89                                      const DILineInfo &Info) {
90   OS << Filename << ':' << Info.Line;
91   if (Info.Discriminator)
92     OS << " (discriminator " << Info.Discriminator << ')';
93   OS << '\n';
94   printContext(Filename, Info.Line);
95 }
96 
97 void PlainPrinterBase::printVerbose(StringRef Filename,
98                                     const DILineInfo &Info) {
99   OS << "  Filename: " << Filename << '\n';
100   if (Info.StartLine) {
101     OS << "  Function start filename: " << Info.StartFileName << '\n';
102     OS << "  Function start line: " << Info.StartLine << '\n';
103   }
104   OS << "  Line: " << Info.Line << '\n';
105   OS << "  Column: " << Info.Column << '\n';
106   if (Info.Discriminator)
107     OS << "  Discriminator: " << Info.Discriminator << '\n';
108 }
109 
110 void LLVMPrinter::printFooter() { OS << '\n'; }
111 
112 void PlainPrinterBase::print(const DILineInfo &Info, bool Inlined) {
113   printFunctionName(Info.FunctionName, Inlined);
114   StringRef Filename = Info.FileName;
115   if (Filename == DILineInfo::BadString)
116     Filename = DILineInfo::Addr2LineBadString;
117   if (Config.Verbose)
118     printVerbose(Filename, Info);
119   else
120     printSimpleLocation(Filename, Info);
121 }
122 
123 void PlainPrinterBase::print(const Request &Request, const DILineInfo &Info) {
124   printHeader(*Request.Address);
125   print(Info, false);
126   printFooter();
127 }
128 
129 void PlainPrinterBase::print(const Request &Request,
130                              const DIInliningInfo &Info) {
131   printHeader(*Request.Address);
132   uint32_t FramesNum = Info.getNumberOfFrames();
133   if (FramesNum == 0)
134     print(DILineInfo(), false);
135   else
136     for (uint32_t I = 0; I < FramesNum; ++I)
137       print(Info.getFrame(I), I > 0);
138   printFooter();
139 }
140 
141 void PlainPrinterBase::print(const Request &Request, const DIGlobal &Global) {
142   printHeader(*Request.Address);
143   StringRef Name = Global.Name;
144   if (Name == DILineInfo::BadString)
145     Name = DILineInfo::Addr2LineBadString;
146   OS << Name << "\n";
147   OS << Global.Start << " " << Global.Size << "\n";
148   printFooter();
149 }
150 
151 void PlainPrinterBase::print(const Request &Request,
152                              const std::vector<DILocal> &Locals) {
153   printHeader(*Request.Address);
154   if (Locals.empty())
155     OS << DILineInfo::Addr2LineBadString << '\n';
156   else
157     for (const DILocal &L : Locals) {
158       if (L.FunctionName.empty())
159         OS << DILineInfo::Addr2LineBadString;
160       else
161         OS << L.FunctionName;
162       OS << '\n';
163 
164       if (L.Name.empty())
165         OS << DILineInfo::Addr2LineBadString;
166       else
167         OS << L.Name;
168       OS << '\n';
169 
170       if (L.DeclFile.empty())
171         OS << DILineInfo::Addr2LineBadString;
172       else
173         OS << L.DeclFile;
174 
175       OS << ':' << L.DeclLine << '\n';
176 
177       if (L.FrameOffset)
178         OS << *L.FrameOffset;
179       else
180         OS << DILineInfo::Addr2LineBadString;
181       OS << ' ';
182 
183       if (L.Size)
184         OS << *L.Size;
185       else
186         OS << DILineInfo::Addr2LineBadString;
187       OS << ' ';
188 
189       if (L.TagOffset)
190         OS << *L.TagOffset;
191       else
192         OS << DILineInfo::Addr2LineBadString;
193       OS << '\n';
194     }
195   printFooter();
196 }
197 
198 void PlainPrinterBase::printInvalidCommand(const Request &Request,
199                                            StringRef Command) {
200   OS << Command << '\n';
201 }
202 
203 bool PlainPrinterBase::printError(const Request &Request,
204                                   const ErrorInfoBase &ErrorInfo,
205                                   StringRef ErrorBanner) {
206   ES << ErrorBanner;
207   ErrorInfo.log(ES);
208   ES << '\n';
209   // Print an empty struct too.
210   return true;
211 }
212 
213 static std::string toHex(uint64_t V) {
214   return ("0x" + Twine::utohexstr(V)).str();
215 }
216 
217 static json::Object toJSON(const Request &Request, StringRef ErrorMsg = "") {
218   json::Object Json({{"ModuleName", Request.ModuleName.str()}});
219   if (Request.Address)
220     Json["Address"] = toHex(*Request.Address);
221   if (!ErrorMsg.empty())
222     Json["Error"] = json::Object({{"Message", ErrorMsg.str()}});
223   return Json;
224 }
225 
226 void JSONPrinter::print(const Request &Request, const DILineInfo &Info) {
227   DIInliningInfo InliningInfo;
228   InliningInfo.addFrame(Info);
229   print(Request, InliningInfo);
230 }
231 
232 void JSONPrinter::print(const Request &Request, const DIInliningInfo &Info) {
233   json::Array Array;
234   for (uint32_t I = 0, N = Info.getNumberOfFrames(); I < N; ++I) {
235     const DILineInfo &LineInfo = Info.getFrame(I);
236     Array.push_back(json::Object(
237         {{"FunctionName", LineInfo.FunctionName != DILineInfo::BadString
238                               ? LineInfo.FunctionName
239                               : ""},
240          {"StartFileName", LineInfo.StartFileName != DILineInfo::BadString
241                                ? LineInfo.StartFileName
242                                : ""},
243          {"StartLine", LineInfo.StartLine},
244          {"FileName",
245           LineInfo.FileName != DILineInfo::BadString ? LineInfo.FileName : ""},
246          {"Line", LineInfo.Line},
247          {"Column", LineInfo.Column},
248          {"Discriminator", LineInfo.Discriminator}}));
249   }
250   json::Object Json = toJSON(Request);
251   Json["Symbol"] = std::move(Array);
252   if (ObjectList)
253     ObjectList->push_back(std::move(Json));
254   else
255     printJSON(std::move(Json));
256 }
257 
258 void JSONPrinter::print(const Request &Request, const DIGlobal &Global) {
259   json::Object Data(
260       {{"Name", Global.Name != DILineInfo::BadString ? Global.Name : ""},
261        {"Start", toHex(Global.Start)},
262        {"Size", toHex(Global.Size)}});
263   json::Object Json = toJSON(Request);
264   Json["Data"] = std::move(Data);
265   if (ObjectList)
266     ObjectList->push_back(std::move(Json));
267   else
268     printJSON(std::move(Json));
269 }
270 
271 void JSONPrinter::print(const Request &Request,
272                         const std::vector<DILocal> &Locals) {
273   json::Array Frame;
274   for (const DILocal &Local : Locals) {
275     json::Object FrameObject(
276         {{"FunctionName", Local.FunctionName},
277          {"Name", Local.Name},
278          {"DeclFile", Local.DeclFile},
279          {"DeclLine", int64_t(Local.DeclLine)},
280          {"Size", Local.Size ? toHex(*Local.Size) : ""},
281          {"TagOffset", Local.TagOffset ? toHex(*Local.TagOffset) : ""}});
282     if (Local.FrameOffset)
283       FrameObject["FrameOffset"] = *Local.FrameOffset;
284     Frame.push_back(std::move(FrameObject));
285   }
286   json::Object Json = toJSON(Request);
287   Json["Frame"] = std::move(Frame);
288   if (ObjectList)
289     ObjectList->push_back(std::move(Json));
290   else
291     printJSON(std::move(Json));
292 }
293 
294 void JSONPrinter::printInvalidCommand(const Request &Request,
295                                       StringRef Command) {
296   printError(Request,
297              StringError("unable to parse arguments: " + Command,
298                          std::make_error_code(std::errc::invalid_argument)),
299              "");
300 }
301 
302 bool JSONPrinter::printError(const Request &Request,
303                              const ErrorInfoBase &ErrorInfo,
304                              StringRef ErrorBanner) {
305   json::Object Json = toJSON(Request, ErrorInfo.message());
306   if (ObjectList)
307     ObjectList->push_back(std::move(Json));
308   else
309     printJSON(std::move(Json));
310   return false;
311 }
312 
313 void JSONPrinter::listBegin() {
314   assert(!ObjectList);
315   ObjectList = std::make_unique<json::Array>();
316 }
317 
318 void JSONPrinter::listEnd() {
319   assert(ObjectList);
320   printJSON(std::move(*ObjectList));
321   ObjectList.release();
322 }
323 
324 } // end namespace symbolize
325 } // end namespace llvm
326