1 //===- lib/DebugInfo/Symbolize/DIPrinter.cpp ------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file defines the DIPrinter class, which is responsible for printing
10 // structures defined in DebugInfo/DIContext.h
12 //===----------------------------------------------------------------------===//
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"
32 std::unique_ptr
<MemoryBuffer
> MemBuf
;
34 std::optional
<StringRef
>
35 load(StringRef FileName
, const std::optional
<StringRef
> &EmbeddedSource
) {
40 return EmbeddedSource
;
42 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> BufOrErr
=
43 MemoryBuffer::getFile(FileName
);
46 MemBuf
= std::move(*BufOrErr
);
47 return MemBuf
->getBuffer();
51 std::optional
<StringRef
> pruneSource(const std::optional
<StringRef
> &Source
) {
54 size_t FirstLinePos
= StringRef::npos
, Pos
= 0;
55 for (int64_t L
= 1; L
<= LastLine
; ++L
, ++Pos
) {
58 Pos
= Source
->find('\n', Pos
);
59 if (Pos
== StringRef::npos
)
62 if (FirstLinePos
== StringRef::npos
)
64 return Source
->substr(FirstLinePos
, (Pos
== StringRef::npos
)
66 : Pos
- FirstLinePos
);
72 const int64_t FirstLine
;
73 const int64_t LastLine
;
74 const std::optional
<StringRef
> PrunedSource
;
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
))) {}
84 void format(raw_ostream
&OS
) {
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
);
100 OS
<< String
<< '\n';
101 if (PosEnd
== StringRef::npos
)
108 void PlainPrinterBase::printHeader(std::optional
<uint64_t> Address
) {
109 if (Address
.has_value() && Config
.PrintAddress
) {
111 OS
.write_hex(*Address
);
112 StringRef Delimiter
= Config
.Pretty
? ": " : "\n";
117 // Prints source code around in the FileName the Line.
118 void PlainPrinterBase::printContext(SourceCode SourceCode
) {
119 SourceCode
.format(OS
);
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
;
132 void LLVMPrinter::printSimpleLocation(StringRef Filename
,
133 const DILineInfo
&Info
) {
134 OS
<< Filename
<< ':' << Info
.Line
<< ':' << Info
.Column
<< '\n';
136 SourceCode(Filename
, Info
.Line
, Config
.SourceContextLines
, Info
.Source
));
139 void GNUPrinter::printSimpleLocation(StringRef Filename
,
140 const DILineInfo
&Info
) {
141 OS
<< Filename
<< ':' << Info
.Line
;
142 if (Info
.Discriminator
)
143 OS
<< " (discriminator " << Info
.Discriminator
<< ')';
146 SourceCode(Filename
, Info
.Line
, Config
.SourceContextLines
, Info
.Source
));
149 void PlainPrinterBase::printVerbose(StringRef Filename
,
150 const DILineInfo
&Info
) {
151 OS
<< " Filename: " << Filename
<< '\n';
152 if (Info
.StartLine
) {
153 OS
<< " Function start filename: " << Info
.StartFileName
<< '\n';
154 OS
<< " Function start line: " << Info
.StartLine
<< '\n';
156 printStartAddress(Info
);
157 OS
<< " Line: " << Info
.Line
<< '\n';
158 OS
<< " Column: " << Info
.Column
<< '\n';
159 if (Info
.Discriminator
)
160 OS
<< " Discriminator: " << Info
.Discriminator
<< '\n';
163 void LLVMPrinter::printStartAddress(const DILineInfo
&Info
) {
164 if (Info
.StartAddress
) {
165 OS
<< " Function start address: 0x";
166 OS
.write_hex(*Info
.StartAddress
);
171 void LLVMPrinter::printFooter() { OS
<< '\n'; }
173 void PlainPrinterBase::print(const DILineInfo
&Info
, bool Inlined
) {
174 printFunctionName(Info
.FunctionName
, Inlined
);
175 StringRef Filename
= Info
.FileName
;
176 if (Filename
== DILineInfo::BadString
)
177 Filename
= DILineInfo::Addr2LineBadString
;
179 printVerbose(Filename
, Info
);
181 printSimpleLocation(Filename
, Info
);
184 void PlainPrinterBase::print(const Request
&Request
, const DILineInfo
&Info
) {
185 printHeader(Request
.Address
);
190 void PlainPrinterBase::print(const Request
&Request
,
191 const DIInliningInfo
&Info
) {
192 printHeader(*Request
.Address
);
193 uint32_t FramesNum
= Info
.getNumberOfFrames();
195 print(DILineInfo(), false);
197 for (uint32_t I
= 0; I
< FramesNum
; ++I
)
198 print(Info
.getFrame(I
), I
> 0);
202 void PlainPrinterBase::print(const Request
&Request
, const DIGlobal
&Global
) {
203 printHeader(*Request
.Address
);
204 StringRef Name
= Global
.Name
;
205 if (Name
== DILineInfo::BadString
)
206 Name
= DILineInfo::Addr2LineBadString
;
208 OS
<< Global
.Start
<< " " << Global
.Size
<< "\n";
209 if (Global
.DeclFile
.empty())
212 OS
<< Global
.DeclFile
<< ":" << Global
.DeclLine
<< "\n";
216 void PlainPrinterBase::print(const Request
&Request
,
217 const std::vector
<DILocal
> &Locals
) {
218 printHeader(*Request
.Address
);
220 OS
<< DILineInfo::Addr2LineBadString
<< '\n';
222 for (const DILocal
&L
: Locals
) {
223 if (L
.FunctionName
.empty())
224 OS
<< DILineInfo::Addr2LineBadString
;
226 OS
<< L
.FunctionName
;
230 OS
<< DILineInfo::Addr2LineBadString
;
235 if (L
.DeclFile
.empty())
236 OS
<< DILineInfo::Addr2LineBadString
;
240 OS
<< ':' << L
.DeclLine
<< '\n';
243 OS
<< *L
.FrameOffset
;
245 OS
<< DILineInfo::Addr2LineBadString
;
251 OS
<< DILineInfo::Addr2LineBadString
;
257 OS
<< DILineInfo::Addr2LineBadString
;
263 void PlainPrinterBase::print(const Request
&Request
,
264 const std::vector
<DILineInfo
> &Locations
) {
265 if (Locations
.empty()) {
266 print(Request
, DILineInfo());
268 for (const DILineInfo
&L
: Locations
)
274 bool PlainPrinterBase::printError(const Request
&Request
,
275 const ErrorInfoBase
&ErrorInfo
) {
276 ErrHandler(ErrorInfo
, Request
.ModuleName
);
277 // Print an empty struct too.
281 static std::string
toHex(uint64_t V
) {
282 return ("0x" + Twine::utohexstr(V
)).str();
285 static json::Object
toJSON(const Request
&Request
, StringRef ErrorMsg
= "") {
286 json::Object
Json({{"ModuleName", Request
.ModuleName
.str()}});
287 if (!Request
.Symbol
.empty())
288 Json
["SymName"] = Request
.Symbol
.str();
290 Json
["Address"] = toHex(*Request
.Address
);
291 if (!ErrorMsg
.empty())
292 Json
["Error"] = json::Object({{"Message", ErrorMsg
.str()}});
296 static json::Object
toJSON(const DILineInfo
&LineInfo
) {
298 {{"FunctionName", LineInfo
.FunctionName
!= DILineInfo::BadString
299 ? LineInfo
.FunctionName
301 {"StartFileName", LineInfo
.StartFileName
!= DILineInfo::BadString
302 ? LineInfo
.StartFileName
304 {"StartLine", LineInfo
.StartLine
},
306 LineInfo
.StartAddress
? toHex(*LineInfo
.StartAddress
) : ""},
308 LineInfo
.FileName
!= DILineInfo::BadString
? LineInfo
.FileName
: ""},
309 {"Line", LineInfo
.Line
},
310 {"Column", LineInfo
.Column
},
311 {"Discriminator", LineInfo
.Discriminator
}});
314 void JSONPrinter::print(const Request
&Request
, const DILineInfo
&Info
) {
315 DIInliningInfo InliningInfo
;
316 InliningInfo
.addFrame(Info
);
317 print(Request
, InliningInfo
);
320 void JSONPrinter::print(const Request
&Request
, const DIInliningInfo
&Info
) {
322 for (uint32_t I
= 0, N
= Info
.getNumberOfFrames(); I
< N
; ++I
) {
323 const DILineInfo
&LineInfo
= Info
.getFrame(I
);
324 json::Object Object
= toJSON(LineInfo
);
325 SourceCode
SourceCode(LineInfo
.FileName
, LineInfo
.Line
,
326 Config
.SourceContextLines
, LineInfo
.Source
);
327 std::string FormattedSource
;
328 raw_string_ostream
Stream(FormattedSource
);
329 SourceCode
.format(Stream
);
330 if (!FormattedSource
.empty())
331 Object
["Source"] = std::move(FormattedSource
);
332 Array
.push_back(std::move(Object
));
334 json::Object Json
= toJSON(Request
);
335 Json
["Symbol"] = std::move(Array
);
337 ObjectList
->push_back(std::move(Json
));
339 printJSON(std::move(Json
));
342 void JSONPrinter::print(const Request
&Request
, const DIGlobal
&Global
) {
344 {{"Name", Global
.Name
!= DILineInfo::BadString
? Global
.Name
: ""},
345 {"Start", toHex(Global
.Start
)},
346 {"Size", toHex(Global
.Size
)}});
347 json::Object Json
= toJSON(Request
);
348 Json
["Data"] = std::move(Data
);
350 ObjectList
->push_back(std::move(Json
));
352 printJSON(std::move(Json
));
355 void JSONPrinter::print(const Request
&Request
,
356 const std::vector
<DILocal
> &Locals
) {
358 for (const DILocal
&Local
: Locals
) {
359 json::Object
FrameObject(
360 {{"FunctionName", Local
.FunctionName
},
361 {"Name", Local
.Name
},
362 {"DeclFile", Local
.DeclFile
},
363 {"DeclLine", int64_t(Local
.DeclLine
)},
364 {"Size", Local
.Size
? toHex(*Local
.Size
) : ""},
365 {"TagOffset", Local
.TagOffset
? toHex(*Local
.TagOffset
) : ""}});
366 if (Local
.FrameOffset
)
367 FrameObject
["FrameOffset"] = *Local
.FrameOffset
;
368 Frame
.push_back(std::move(FrameObject
));
370 json::Object Json
= toJSON(Request
);
371 Json
["Frame"] = std::move(Frame
);
373 ObjectList
->push_back(std::move(Json
));
375 printJSON(std::move(Json
));
378 void JSONPrinter::print(const Request
&Request
,
379 const std::vector
<DILineInfo
> &Locations
) {
380 json::Array Definitions
;
381 for (const DILineInfo
&L
: Locations
)
382 Definitions
.push_back(toJSON(L
));
383 json::Object Json
= toJSON(Request
);
384 Json
["Loc"] = std::move(Definitions
);
386 ObjectList
->push_back(std::move(Json
));
388 printJSON(std::move(Json
));
391 bool JSONPrinter::printError(const Request
&Request
,
392 const ErrorInfoBase
&ErrorInfo
) {
393 json::Object Json
= toJSON(Request
, ErrorInfo
.message());
395 ObjectList
->push_back(std::move(Json
));
397 printJSON(std::move(Json
));
401 void JSONPrinter::listBegin() {
403 ObjectList
= std::make_unique
<json::Array
>();
406 void JSONPrinter::listEnd() {
408 printJSON(std::move(*ObjectList
));
412 } // end namespace symbolize
413 } // end namespace llvm