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
;
135 if (Info
.IsApproximateLine
)
136 OS
<< " " << Info
.ApproxString
;
139 SourceCode(Filename
, Info
.Line
, Config
.SourceContextLines
, Info
.Source
));
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
<< ')';
151 SourceCode(Filename
, Info
.Line
, Config
.SourceContextLines
, Info
.Source
));
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';
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';
170 void LLVMPrinter::printStartAddress(const DILineInfo
&Info
) {
171 if (Info
.StartAddress
) {
172 OS
<< " Function start address: 0x";
173 OS
.write_hex(*Info
.StartAddress
);
178 void LLVMPrinter::printFooter() { OS
<< '\n'; }
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
;
186 printVerbose(Filename
, Info
);
188 printSimpleLocation(Filename
, Info
);
191 void PlainPrinterBase::print(const Request
&Request
, const DILineInfo
&Info
) {
192 printHeader(Request
.Address
);
197 void PlainPrinterBase::print(const Request
&Request
,
198 const DIInliningInfo
&Info
) {
199 printHeader(*Request
.Address
);
200 uint32_t FramesNum
= Info
.getNumberOfFrames();
202 print(DILineInfo(), false);
204 for (uint32_t I
= 0; I
< FramesNum
; ++I
)
205 print(Info
.getFrame(I
), I
> 0);
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
;
215 OS
<< Global
.Start
<< " " << Global
.Size
<< "\n";
216 if (Global
.DeclFile
.empty())
219 OS
<< Global
.DeclFile
<< ":" << Global
.DeclLine
<< "\n";
223 void PlainPrinterBase::print(const Request
&Request
,
224 const std::vector
<DILocal
> &Locals
) {
225 printHeader(*Request
.Address
);
227 OS
<< DILineInfo::Addr2LineBadString
<< '\n';
229 for (const DILocal
&L
: Locals
) {
230 if (L
.FunctionName
.empty())
231 OS
<< DILineInfo::Addr2LineBadString
;
233 OS
<< L
.FunctionName
;
237 OS
<< DILineInfo::Addr2LineBadString
;
242 if (L
.DeclFile
.empty())
243 OS
<< DILineInfo::Addr2LineBadString
;
247 OS
<< ':' << L
.DeclLine
<< '\n';
250 OS
<< *L
.FrameOffset
;
252 OS
<< DILineInfo::Addr2LineBadString
;
258 OS
<< DILineInfo::Addr2LineBadString
;
264 OS
<< DILineInfo::Addr2LineBadString
;
270 void PlainPrinterBase::print(const Request
&Request
,
271 const std::vector
<DILineInfo
> &Locations
) {
272 if (Locations
.empty()) {
273 print(Request
, DILineInfo());
275 for (const DILineInfo
&L
: Locations
)
281 bool PlainPrinterBase::printError(const Request
&Request
,
282 const ErrorInfoBase
&ErrorInfo
) {
283 ErrHandler(ErrorInfo
, Request
.ModuleName
);
284 // Print an empty struct too.
288 static std::string
toHex(uint64_t V
) {
289 return ("0x" + Twine::utohexstr(V
)).str();
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();
297 Json
["Address"] = toHex(*Request
.Address
);
298 if (!ErrorMsg
.empty())
299 Json
["Error"] = json::Object({{"Message", ErrorMsg
.str()}});
303 static json::Object
toJSON(const DILineInfo
&LineInfo
) {
304 json::Object Obj
= json::Object(
305 {{"FunctionName", LineInfo
.FunctionName
!= DILineInfo::BadString
306 ? LineInfo
.FunctionName
308 {"StartFileName", LineInfo
.StartFileName
!= DILineInfo::BadString
309 ? LineInfo
.StartFileName
311 {"StartLine", LineInfo
.StartLine
},
313 LineInfo
.StartAddress
? toHex(*LineInfo
.StartAddress
) : ""},
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
});
324 void JSONPrinter::print(const Request
&Request
, const DILineInfo
&Info
) {
325 DIInliningInfo InliningInfo
;
326 InliningInfo
.addFrame(Info
);
327 print(Request
, InliningInfo
);
330 void JSONPrinter::print(const Request
&Request
, const DIInliningInfo
&Info
) {
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
));
344 json::Object Json
= toJSON(Request
);
345 Json
["Symbol"] = std::move(Array
);
347 ObjectList
->push_back(std::move(Json
));
349 printJSON(std::move(Json
));
352 void JSONPrinter::print(const Request
&Request
, const DIGlobal
&Global
) {
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
);
360 ObjectList
->push_back(std::move(Json
));
362 printJSON(std::move(Json
));
365 void JSONPrinter::print(const Request
&Request
,
366 const std::vector
<DILocal
> &Locals
) {
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
));
380 json::Object Json
= toJSON(Request
);
381 Json
["Frame"] = std::move(Frame
);
383 ObjectList
->push_back(std::move(Json
));
385 printJSON(std::move(Json
));
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
);
396 ObjectList
->push_back(std::move(Json
));
398 printJSON(std::move(Json
));
401 bool JSONPrinter::printError(const Request
&Request
,
402 const ErrorInfoBase
&ErrorInfo
) {
403 json::Object Json
= toJSON(Request
, ErrorInfo
.message());
405 ObjectList
->push_back(std::move(Json
));
407 printJSON(std::move(Json
));
411 void JSONPrinter::listBegin() {
413 ObjectList
= std::make_unique
<json::Array
>();
416 void JSONPrinter::listEnd() {
418 printJSON(std::move(*ObjectList
));
422 } // end namespace symbolize
423 } // end namespace llvm