Files
clang-p2996/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp
Mitch Phillips cead4eceb0 [symbolizer] Parse DW_TAG_variable DIs to show line info for globals
Currently, llvm-symbolizer doesn't like to parse .debug_info in order to
show the line info for global variables. addr2line does this. In the
future, I'm looking to migrate AddressSanitizer off of internal metadata
over to using debuginfo, and this is predicated on being able to get the
line info for global variables.

This patch adds the requisite support for getting the line info from the
.debug_info section for symbolizing global variables. This only happens
when you ask for a global variable to be symbolized as data.

Reviewed By: dblaikie

Differential Revision: https://reviews.llvm.org/D123538
2022-05-23 13:30:22 -07:00

401 lines
12 KiB
C++

//===- lib/DebugInfo/Symbolize/DIPrinter.cpp ------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines the DIPrinter class, which is responsible for printing
// structures defined in DebugInfo/DIContext.h
//
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/Symbolize/DIPrinter.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/DebugInfo/DIContext.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
namespace llvm {
namespace symbolize {
class SourceCode {
std::unique_ptr<MemoryBuffer> MemBuf;
Optional<StringRef> load(StringRef FileName,
const Optional<StringRef> &EmbeddedSource) {
if (Lines <= 0)
return None;
if (EmbeddedSource)
return EmbeddedSource;
else {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
MemoryBuffer::getFile(FileName);
if (!BufOrErr)
return None;
MemBuf = std::move(*BufOrErr);
return MemBuf->getBuffer();
}
}
Optional<StringRef> pruneSource(const Optional<StringRef> &Source) {
if (!Source)
return None;
size_t FirstLinePos = StringRef::npos, Pos = 0;
for (int64_t L = 1; L <= LastLine; ++L, ++Pos) {
if (L == FirstLine)
FirstLinePos = Pos;
Pos = Source->find('\n', Pos);
if (Pos == StringRef::npos)
break;
}
if (FirstLinePos == StringRef::npos)
return None;
return Source->substr(FirstLinePos, (Pos == StringRef::npos)
? StringRef::npos
: Pos - FirstLinePos);
}
public:
const int64_t Line;
const int Lines;
const int64_t FirstLine;
const int64_t LastLine;
const Optional<StringRef> PrunedSource;
SourceCode(
StringRef FileName, int64_t Line, int Lines,
const Optional<StringRef> &EmbeddedSource = Optional<StringRef>(None))
: Line(Line), Lines(Lines),
FirstLine(std::max(static_cast<int64_t>(1), Line - Lines / 2)),
LastLine(FirstLine + Lines - 1),
PrunedSource(pruneSource(load(FileName, EmbeddedSource))) {}
void format(raw_ostream &OS) {
if (!PrunedSource)
return;
size_t MaxLineNumberWidth = std::ceil(std::log10(LastLine));
int64_t L = FirstLine;
for (size_t Pos = 0; Pos < PrunedSource->size(); ++L) {
size_t PosEnd = PrunedSource->find('\n', Pos);
StringRef String = PrunedSource->substr(
Pos, (PosEnd == StringRef::npos) ? StringRef::npos : (PosEnd - Pos));
if (String.endswith("\r"))
String = String.drop_back(1);
OS << format_decimal(L, MaxLineNumberWidth);
if (L == Line)
OS << " >: ";
else
OS << " : ";
OS << String << '\n';
if (PosEnd == StringRef::npos)
break;
Pos = PosEnd + 1;
}
}
};
void PlainPrinterBase::printHeader(uint64_t Address) {
if (Config.PrintAddress) {
OS << "0x";
OS.write_hex(Address);
StringRef Delimiter = Config.Pretty ? ": " : "\n";
OS << Delimiter;
}
}
// Prints source code around in the FileName the Line.
void PlainPrinterBase::printContext(SourceCode SourceCode) {
SourceCode.format(OS);
}
void PlainPrinterBase::printFunctionName(StringRef FunctionName, bool Inlined) {
if (Config.PrintFunctions) {
if (FunctionName == DILineInfo::BadString)
FunctionName = DILineInfo::Addr2LineBadString;
StringRef Delimiter = Config.Pretty ? " at " : "\n";
StringRef Prefix = (Config.Pretty && Inlined) ? " (inlined by) " : "";
OS << Prefix << FunctionName << Delimiter;
}
}
void LLVMPrinter::printSimpleLocation(StringRef Filename,
const DILineInfo &Info) {
OS << Filename << ':' << Info.Line << ':' << Info.Column << '\n';
printContext(
SourceCode(Filename, Info.Line, Config.SourceContextLines, Info.Source));
}
void GNUPrinter::printSimpleLocation(StringRef Filename,
const DILineInfo &Info) {
OS << Filename << ':' << Info.Line;
if (Info.Discriminator)
OS << " (discriminator " << Info.Discriminator << ')';
OS << '\n';
printContext(
SourceCode(Filename, Info.Line, Config.SourceContextLines, Info.Source));
}
void PlainPrinterBase::printVerbose(StringRef Filename,
const DILineInfo &Info) {
OS << " Filename: " << Filename << '\n';
if (Info.StartLine) {
OS << " Function start filename: " << Info.StartFileName << '\n';
OS << " Function start line: " << Info.StartLine << '\n';
}
printStartAddress(Info);
OS << " Line: " << Info.Line << '\n';
OS << " Column: " << Info.Column << '\n';
if (Info.Discriminator)
OS << " Discriminator: " << Info.Discriminator << '\n';
}
void LLVMPrinter::printStartAddress(const DILineInfo &Info) {
if (Info.StartAddress) {
OS << " Function start address: 0x";
OS.write_hex(*Info.StartAddress);
OS << '\n';
}
}
void LLVMPrinter::printFooter() { OS << '\n'; }
void PlainPrinterBase::print(const DILineInfo &Info, bool Inlined) {
printFunctionName(Info.FunctionName, Inlined);
StringRef Filename = Info.FileName;
if (Filename == DILineInfo::BadString)
Filename = DILineInfo::Addr2LineBadString;
if (Config.Verbose)
printVerbose(Filename, Info);
else
printSimpleLocation(Filename, Info);
}
void PlainPrinterBase::print(const Request &Request, const DILineInfo &Info) {
printHeader(*Request.Address);
print(Info, false);
printFooter();
}
void PlainPrinterBase::print(const Request &Request,
const DIInliningInfo &Info) {
printHeader(*Request.Address);
uint32_t FramesNum = Info.getNumberOfFrames();
if (FramesNum == 0)
print(DILineInfo(), false);
else
for (uint32_t I = 0; I < FramesNum; ++I)
print(Info.getFrame(I), I > 0);
printFooter();
}
void PlainPrinterBase::print(const Request &Request, const DIGlobal &Global) {
printHeader(*Request.Address);
StringRef Name = Global.Name;
if (Name == DILineInfo::BadString)
Name = DILineInfo::Addr2LineBadString;
OS << Name << "\n";
OS << Global.Start << " " << Global.Size << "\n";
if (Global.DeclFile.empty())
OS << "??:?\n";
else
OS << Global.DeclFile << ":" << Global.DeclLine << "\n";
printFooter();
}
void PlainPrinterBase::print(const Request &Request,
const std::vector<DILocal> &Locals) {
printHeader(*Request.Address);
if (Locals.empty())
OS << DILineInfo::Addr2LineBadString << '\n';
else
for (const DILocal &L : Locals) {
if (L.FunctionName.empty())
OS << DILineInfo::Addr2LineBadString;
else
OS << L.FunctionName;
OS << '\n';
if (L.Name.empty())
OS << DILineInfo::Addr2LineBadString;
else
OS << L.Name;
OS << '\n';
if (L.DeclFile.empty())
OS << DILineInfo::Addr2LineBadString;
else
OS << L.DeclFile;
OS << ':' << L.DeclLine << '\n';
if (L.FrameOffset)
OS << *L.FrameOffset;
else
OS << DILineInfo::Addr2LineBadString;
OS << ' ';
if (L.Size)
OS << *L.Size;
else
OS << DILineInfo::Addr2LineBadString;
OS << ' ';
if (L.TagOffset)
OS << *L.TagOffset;
else
OS << DILineInfo::Addr2LineBadString;
OS << '\n';
}
printFooter();
}
void PlainPrinterBase::printInvalidCommand(const Request &Request,
StringRef Command) {
OS << Command << '\n';
}
bool PlainPrinterBase::printError(const Request &Request,
const ErrorInfoBase &ErrorInfo,
StringRef ErrorBanner) {
ES << ErrorBanner;
ErrorInfo.log(ES);
ES << '\n';
// Print an empty struct too.
return true;
}
static std::string toHex(uint64_t V) {
return ("0x" + Twine::utohexstr(V)).str();
}
static json::Object toJSON(const Request &Request, StringRef ErrorMsg = "") {
json::Object Json({{"ModuleName", Request.ModuleName.str()}});
if (Request.Address)
Json["Address"] = toHex(*Request.Address);
if (!ErrorMsg.empty())
Json["Error"] = json::Object({{"Message", ErrorMsg.str()}});
return Json;
}
void JSONPrinter::print(const Request &Request, const DILineInfo &Info) {
DIInliningInfo InliningInfo;
InliningInfo.addFrame(Info);
print(Request, InliningInfo);
}
void JSONPrinter::print(const Request &Request, const DIInliningInfo &Info) {
json::Array Array;
for (uint32_t I = 0, N = Info.getNumberOfFrames(); I < N; ++I) {
const DILineInfo &LineInfo = Info.getFrame(I);
json::Object Object(
{{"FunctionName", LineInfo.FunctionName != DILineInfo::BadString
? LineInfo.FunctionName
: ""},
{"StartFileName", LineInfo.StartFileName != DILineInfo::BadString
? LineInfo.StartFileName
: ""},
{"StartLine", LineInfo.StartLine},
{"StartAddress",
LineInfo.StartAddress ? toHex(*LineInfo.StartAddress) : ""},
{"FileName",
LineInfo.FileName != DILineInfo::BadString ? LineInfo.FileName : ""},
{"Line", LineInfo.Line},
{"Column", LineInfo.Column},
{"Discriminator", LineInfo.Discriminator}});
SourceCode SourceCode(LineInfo.FileName, LineInfo.Line,
Config.SourceContextLines, LineInfo.Source);
std::string FormattedSource;
raw_string_ostream Stream(FormattedSource);
SourceCode.format(Stream);
if (!FormattedSource.empty())
Object["Source"] = std::move(FormattedSource);
Array.push_back(std::move(Object));
}
json::Object Json = toJSON(Request);
Json["Symbol"] = std::move(Array);
if (ObjectList)
ObjectList->push_back(std::move(Json));
else
printJSON(std::move(Json));
}
void JSONPrinter::print(const Request &Request, const DIGlobal &Global) {
json::Object Data(
{{"Name", Global.Name != DILineInfo::BadString ? Global.Name : ""},
{"Start", toHex(Global.Start)},
{"Size", toHex(Global.Size)}});
json::Object Json = toJSON(Request);
Json["Data"] = std::move(Data);
if (ObjectList)
ObjectList->push_back(std::move(Json));
else
printJSON(std::move(Json));
}
void JSONPrinter::print(const Request &Request,
const std::vector<DILocal> &Locals) {
json::Array Frame;
for (const DILocal &Local : Locals) {
json::Object FrameObject(
{{"FunctionName", Local.FunctionName},
{"Name", Local.Name},
{"DeclFile", Local.DeclFile},
{"DeclLine", int64_t(Local.DeclLine)},
{"Size", Local.Size ? toHex(*Local.Size) : ""},
{"TagOffset", Local.TagOffset ? toHex(*Local.TagOffset) : ""}});
if (Local.FrameOffset)
FrameObject["FrameOffset"] = *Local.FrameOffset;
Frame.push_back(std::move(FrameObject));
}
json::Object Json = toJSON(Request);
Json["Frame"] = std::move(Frame);
if (ObjectList)
ObjectList->push_back(std::move(Json));
else
printJSON(std::move(Json));
}
void JSONPrinter::printInvalidCommand(const Request &Request,
StringRef Command) {
printError(Request,
StringError("unable to parse arguments: " + Command,
std::make_error_code(std::errc::invalid_argument)),
"");
}
bool JSONPrinter::printError(const Request &Request,
const ErrorInfoBase &ErrorInfo,
StringRef ErrorBanner) {
json::Object Json = toJSON(Request, ErrorInfo.message());
if (ObjectList)
ObjectList->push_back(std::move(Json));
else
printJSON(std::move(Json));
return false;
}
void JSONPrinter::listBegin() {
assert(!ObjectList);
ObjectList = std::make_unique<json::Array>();
}
void JSONPrinter::listEnd() {
assert(ObjectList);
printJSON(std::move(*ObjectList));
ObjectList.reset();
}
} // end namespace symbolize
} // end namespace llvm