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
.endswith("\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(uint64_t Address
) {
109 if (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::printInvalidCommand(const Request
&Request
,
265 OS
<< Command
<< '\n';
268 bool PlainPrinterBase::printError(const Request
&Request
,
269 const ErrorInfoBase
&ErrorInfo
) {
270 ErrHandler(ErrorInfo
, Request
.ModuleName
);
271 // Print an empty struct too.
275 static std::string
toHex(uint64_t V
) {
276 return ("0x" + Twine::utohexstr(V
)).str();
279 static json::Object
toJSON(const Request
&Request
, StringRef ErrorMsg
= "") {
280 json::Object
Json({{"ModuleName", Request
.ModuleName
.str()}});
282 Json
["Address"] = toHex(*Request
.Address
);
283 if (!ErrorMsg
.empty())
284 Json
["Error"] = json::Object({{"Message", ErrorMsg
.str()}});
288 static json::Object
toJSON(const DILineInfo
&LineInfo
) {
290 {{"FunctionName", LineInfo
.FunctionName
!= DILineInfo::BadString
291 ? LineInfo
.FunctionName
293 {"StartFileName", LineInfo
.StartFileName
!= DILineInfo::BadString
294 ? LineInfo
.StartFileName
296 {"StartLine", LineInfo
.StartLine
},
298 LineInfo
.StartAddress
? toHex(*LineInfo
.StartAddress
) : ""},
300 LineInfo
.FileName
!= DILineInfo::BadString
? LineInfo
.FileName
: ""},
301 {"Line", LineInfo
.Line
},
302 {"Column", LineInfo
.Column
},
303 {"Discriminator", LineInfo
.Discriminator
}});
306 void JSONPrinter::print(const Request
&Request
, const DILineInfo
&Info
) {
307 DIInliningInfo InliningInfo
;
308 InliningInfo
.addFrame(Info
);
309 print(Request
, InliningInfo
);
312 void JSONPrinter::print(const Request
&Request
, const DIInliningInfo
&Info
) {
314 for (uint32_t I
= 0, N
= Info
.getNumberOfFrames(); I
< N
; ++I
) {
315 const DILineInfo
&LineInfo
= Info
.getFrame(I
);
316 json::Object Object
= toJSON(LineInfo
);
317 SourceCode
SourceCode(LineInfo
.FileName
, LineInfo
.Line
,
318 Config
.SourceContextLines
, LineInfo
.Source
);
319 std::string FormattedSource
;
320 raw_string_ostream
Stream(FormattedSource
);
321 SourceCode
.format(Stream
);
322 if (!FormattedSource
.empty())
323 Object
["Source"] = std::move(FormattedSource
);
324 Array
.push_back(std::move(Object
));
326 json::Object Json
= toJSON(Request
);
327 Json
["Symbol"] = std::move(Array
);
329 ObjectList
->push_back(std::move(Json
));
331 printJSON(std::move(Json
));
334 void JSONPrinter::print(const Request
&Request
, const DIGlobal
&Global
) {
336 {{"Name", Global
.Name
!= DILineInfo::BadString
? Global
.Name
: ""},
337 {"Start", toHex(Global
.Start
)},
338 {"Size", toHex(Global
.Size
)}});
339 json::Object Json
= toJSON(Request
);
340 Json
["Data"] = std::move(Data
);
342 ObjectList
->push_back(std::move(Json
));
344 printJSON(std::move(Json
));
347 void JSONPrinter::print(const Request
&Request
,
348 const std::vector
<DILocal
> &Locals
) {
350 for (const DILocal
&Local
: Locals
) {
351 json::Object
FrameObject(
352 {{"FunctionName", Local
.FunctionName
},
353 {"Name", Local
.Name
},
354 {"DeclFile", Local
.DeclFile
},
355 {"DeclLine", int64_t(Local
.DeclLine
)},
356 {"Size", Local
.Size
? toHex(*Local
.Size
) : ""},
357 {"TagOffset", Local
.TagOffset
? toHex(*Local
.TagOffset
) : ""}});
358 if (Local
.FrameOffset
)
359 FrameObject
["FrameOffset"] = *Local
.FrameOffset
;
360 Frame
.push_back(std::move(FrameObject
));
362 json::Object Json
= toJSON(Request
);
363 Json
["Frame"] = std::move(Frame
);
365 ObjectList
->push_back(std::move(Json
));
367 printJSON(std::move(Json
));
370 void JSONPrinter::printInvalidCommand(const Request
&Request
,
373 StringError("unable to parse arguments: " + Command
,
374 std::make_error_code(std::errc::invalid_argument
)));
377 bool JSONPrinter::printError(const Request
&Request
,
378 const ErrorInfoBase
&ErrorInfo
) {
379 json::Object Json
= toJSON(Request
, ErrorInfo
.message());
381 ObjectList
->push_back(std::move(Json
));
383 printJSON(std::move(Json
));
387 void JSONPrinter::listBegin() {
389 ObjectList
= std::make_unique
<json::Array
>();
392 void JSONPrinter::listEnd() {
394 printJSON(std::move(*ObjectList
));
398 } // end namespace symbolize
399 } // end namespace llvm