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/LineIterator.h"
20 #include "llvm/Support/MemoryBuffer.h"
21 #include "llvm/Support/Path.h"
22 #include "llvm/Support/raw_ostream.h"
34 std::unique_ptr
<MemoryBuffer
> MemBuf
;
36 const Optional
<StringRef
> load(StringRef FileName
,
37 const Optional
<StringRef
> &EmbeddedSource
) {
42 return EmbeddedSource
;
44 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> BufOrErr
=
45 MemoryBuffer::getFile(FileName
);
48 MemBuf
= std::move(*BufOrErr
);
49 return MemBuf
->getBuffer();
53 const Optional
<StringRef
> pruneSource(const Optional
<StringRef
> &Source
) {
56 size_t FirstLinePos
= StringRef::npos
, Pos
= 0;
57 for (int64_t L
= 1; L
<= LastLine
; ++L
, ++Pos
) {
60 Pos
= Source
->find('\n', Pos
);
61 if (Pos
== StringRef::npos
)
64 if (FirstLinePos
== StringRef::npos
)
66 return Source
->substr(FirstLinePos
, (Pos
== StringRef::npos
)
68 : Pos
- FirstLinePos
);
74 const int64_t FirstLine
;
75 const int64_t LastLine
;
76 const Optional
<StringRef
> PrunedSource
;
79 StringRef FileName
, int64_t Line
, int Lines
,
80 const Optional
<StringRef
> &EmbeddedSource
= Optional
<StringRef
>(None
))
81 : Line(Line
), Lines(Lines
),
82 FirstLine(std::max(static_cast<int64_t>(1), Line
- Lines
/ 2)),
83 LastLine(FirstLine
+ Lines
- 1),
84 PrunedSource(pruneSource(load(FileName
, EmbeddedSource
))) {}
86 void format(raw_ostream
&OS
) {
89 size_t MaxLineNumberWidth
= std::ceil(std::log10(LastLine
));
90 int64_t L
= FirstLine
;
91 for (size_t Pos
= 0; Pos
< PrunedSource
->size(); ++L
) {
92 size_t PosEnd
= PrunedSource
->find('\n', Pos
);
93 StringRef String
= PrunedSource
->substr(
94 Pos
, (PosEnd
== StringRef::npos
) ? StringRef::npos
: (PosEnd
- Pos
));
95 if (String
.endswith("\r"))
96 String
= String
.drop_back(1);
97 OS
<< format_decimal(L
, MaxLineNumberWidth
);
102 OS
<< String
<< '\n';
103 if (PosEnd
== StringRef::npos
)
110 void PlainPrinterBase::printHeader(uint64_t Address
) {
111 if (Config
.PrintAddress
) {
113 OS
.write_hex(Address
);
114 StringRef Delimiter
= Config
.Pretty
? ": " : "\n";
119 // Prints source code around in the FileName the Line.
120 void PlainPrinterBase::printContext(SourceCode SourceCode
) {
121 SourceCode
.format(OS
);
124 void PlainPrinterBase::printFunctionName(StringRef FunctionName
, bool Inlined
) {
125 if (Config
.PrintFunctions
) {
126 if (FunctionName
== DILineInfo::BadString
)
127 FunctionName
= DILineInfo::Addr2LineBadString
;
128 StringRef Delimiter
= Config
.Pretty
? " at " : "\n";
129 StringRef Prefix
= (Config
.Pretty
&& Inlined
) ? " (inlined by) " : "";
130 OS
<< Prefix
<< FunctionName
<< Delimiter
;
134 void LLVMPrinter::printSimpleLocation(StringRef Filename
,
135 const DILineInfo
&Info
) {
136 OS
<< Filename
<< ':' << Info
.Line
<< ':' << Info
.Column
<< '\n';
138 SourceCode(Filename
, Info
.Line
, Config
.SourceContextLines
, Info
.Source
));
141 void GNUPrinter::printSimpleLocation(StringRef Filename
,
142 const DILineInfo
&Info
) {
143 OS
<< Filename
<< ':' << Info
.Line
;
144 if (Info
.Discriminator
)
145 OS
<< " (discriminator " << Info
.Discriminator
<< ')';
148 SourceCode(Filename
, Info
.Line
, Config
.SourceContextLines
, Info
.Source
));
151 void PlainPrinterBase::printVerbose(StringRef Filename
,
152 const DILineInfo
&Info
) {
153 OS
<< " Filename: " << Filename
<< '\n';
154 if (Info
.StartLine
) {
155 OS
<< " Function start filename: " << Info
.StartFileName
<< '\n';
156 OS
<< " Function start line: " << Info
.StartLine
<< '\n';
158 printStartAddress(Info
);
159 OS
<< " Line: " << Info
.Line
<< '\n';
160 OS
<< " Column: " << Info
.Column
<< '\n';
161 if (Info
.Discriminator
)
162 OS
<< " Discriminator: " << Info
.Discriminator
<< '\n';
165 void LLVMPrinter::printStartAddress(const DILineInfo
&Info
) {
166 if (Info
.StartAddress
) {
167 OS
<< " Function start address: 0x";
168 OS
.write_hex(*Info
.StartAddress
);
173 void LLVMPrinter::printFooter() { OS
<< '\n'; }
175 void PlainPrinterBase::print(const DILineInfo
&Info
, bool Inlined
) {
176 printFunctionName(Info
.FunctionName
, Inlined
);
177 StringRef Filename
= Info
.FileName
;
178 if (Filename
== DILineInfo::BadString
)
179 Filename
= DILineInfo::Addr2LineBadString
;
181 printVerbose(Filename
, Info
);
183 printSimpleLocation(Filename
, Info
);
186 void PlainPrinterBase::print(const Request
&Request
, const DILineInfo
&Info
) {
187 printHeader(*Request
.Address
);
192 void PlainPrinterBase::print(const Request
&Request
,
193 const DIInliningInfo
&Info
) {
194 printHeader(*Request
.Address
);
195 uint32_t FramesNum
= Info
.getNumberOfFrames();
197 print(DILineInfo(), false);
199 for (uint32_t I
= 0; I
< FramesNum
; ++I
)
200 print(Info
.getFrame(I
), I
> 0);
204 void PlainPrinterBase::print(const Request
&Request
, const DIGlobal
&Global
) {
205 printHeader(*Request
.Address
);
206 StringRef Name
= Global
.Name
;
207 if (Name
== DILineInfo::BadString
)
208 Name
= DILineInfo::Addr2LineBadString
;
210 OS
<< Global
.Start
<< " " << Global
.Size
<< "\n";
214 void PlainPrinterBase::print(const Request
&Request
,
215 const std::vector
<DILocal
> &Locals
) {
216 printHeader(*Request
.Address
);
218 OS
<< DILineInfo::Addr2LineBadString
<< '\n';
220 for (const DILocal
&L
: Locals
) {
221 if (L
.FunctionName
.empty())
222 OS
<< DILineInfo::Addr2LineBadString
;
224 OS
<< L
.FunctionName
;
228 OS
<< DILineInfo::Addr2LineBadString
;
233 if (L
.DeclFile
.empty())
234 OS
<< DILineInfo::Addr2LineBadString
;
238 OS
<< ':' << L
.DeclLine
<< '\n';
241 OS
<< *L
.FrameOffset
;
243 OS
<< DILineInfo::Addr2LineBadString
;
249 OS
<< DILineInfo::Addr2LineBadString
;
255 OS
<< DILineInfo::Addr2LineBadString
;
261 void PlainPrinterBase::printInvalidCommand(const Request
&Request
,
263 OS
<< Command
<< '\n';
266 bool PlainPrinterBase::printError(const Request
&Request
,
267 const ErrorInfoBase
&ErrorInfo
,
268 StringRef ErrorBanner
) {
272 // Print an empty struct too.
276 static std::string
toHex(uint64_t V
) {
277 return ("0x" + Twine::utohexstr(V
)).str();
280 static json::Object
toJSON(const Request
&Request
, StringRef ErrorMsg
= "") {
281 json::Object
Json({{"ModuleName", Request
.ModuleName
.str()}});
283 Json
["Address"] = toHex(*Request
.Address
);
284 if (!ErrorMsg
.empty())
285 Json
["Error"] = json::Object({{"Message", ErrorMsg
.str()}});
289 void JSONPrinter::print(const Request
&Request
, const DILineInfo
&Info
) {
290 DIInliningInfo InliningInfo
;
291 InliningInfo
.addFrame(Info
);
292 print(Request
, InliningInfo
);
295 void JSONPrinter::print(const Request
&Request
, const DIInliningInfo
&Info
) {
297 for (uint32_t I
= 0, N
= Info
.getNumberOfFrames(); I
< N
; ++I
) {
298 const DILineInfo
&LineInfo
= Info
.getFrame(I
);
300 {{"FunctionName", LineInfo
.FunctionName
!= DILineInfo::BadString
301 ? LineInfo
.FunctionName
303 {"StartFileName", LineInfo
.StartFileName
!= DILineInfo::BadString
304 ? LineInfo
.StartFileName
306 {"StartLine", LineInfo
.StartLine
},
308 LineInfo
.StartAddress
? toHex(*LineInfo
.StartAddress
) : ""},
310 LineInfo
.FileName
!= DILineInfo::BadString
? LineInfo
.FileName
: ""},
311 {"Line", LineInfo
.Line
},
312 {"Column", LineInfo
.Column
},
313 {"Discriminator", LineInfo
.Discriminator
}});
314 SourceCode
SourceCode(LineInfo
.FileName
, LineInfo
.Line
,
315 Config
.SourceContextLines
, LineInfo
.Source
);
316 std::string FormattedSource
;
317 raw_string_ostream
Stream(FormattedSource
);
318 SourceCode
.format(Stream
);
319 if (!FormattedSource
.empty())
320 Object
["Source"] = std::move(FormattedSource
);
321 Array
.push_back(std::move(Object
));
323 json::Object Json
= toJSON(Request
);
324 Json
["Symbol"] = std::move(Array
);
326 ObjectList
->push_back(std::move(Json
));
328 printJSON(std::move(Json
));
331 void JSONPrinter::print(const Request
&Request
, const DIGlobal
&Global
) {
333 {{"Name", Global
.Name
!= DILineInfo::BadString
? Global
.Name
: ""},
334 {"Start", toHex(Global
.Start
)},
335 {"Size", toHex(Global
.Size
)}});
336 json::Object Json
= toJSON(Request
);
337 Json
["Data"] = std::move(Data
);
339 ObjectList
->push_back(std::move(Json
));
341 printJSON(std::move(Json
));
344 void JSONPrinter::print(const Request
&Request
,
345 const std::vector
<DILocal
> &Locals
) {
347 for (const DILocal
&Local
: Locals
) {
348 json::Object
FrameObject(
349 {{"FunctionName", Local
.FunctionName
},
350 {"Name", Local
.Name
},
351 {"DeclFile", Local
.DeclFile
},
352 {"DeclLine", int64_t(Local
.DeclLine
)},
353 {"Size", Local
.Size
? toHex(*Local
.Size
) : ""},
354 {"TagOffset", Local
.TagOffset
? toHex(*Local
.TagOffset
) : ""}});
355 if (Local
.FrameOffset
)
356 FrameObject
["FrameOffset"] = *Local
.FrameOffset
;
357 Frame
.push_back(std::move(FrameObject
));
359 json::Object Json
= toJSON(Request
);
360 Json
["Frame"] = std::move(Frame
);
362 ObjectList
->push_back(std::move(Json
));
364 printJSON(std::move(Json
));
367 void JSONPrinter::printInvalidCommand(const Request
&Request
,
370 StringError("unable to parse arguments: " + Command
,
371 std::make_error_code(std::errc::invalid_argument
)),
375 bool JSONPrinter::printError(const Request
&Request
,
376 const ErrorInfoBase
&ErrorInfo
,
377 StringRef ErrorBanner
) {
378 json::Object Json
= toJSON(Request
, ErrorInfo
.message());
380 ObjectList
->push_back(std::move(Json
));
382 printJSON(std::move(Json
));
386 void JSONPrinter::listBegin() {
388 ObjectList
= std::make_unique
<json::Array
>();
391 void JSONPrinter::listEnd() {
393 printJSON(std::move(*ObjectList
));
397 } // end namespace symbolize
398 } // end namespace llvm