This is a recommit of75f1f15881, reverted in7a443b1c49, because it caused compilation error in compiler-rt/lib/sanitizer_common/symbolizer/sanitizer_symbolize.cpp. The error was fixed by Kasimir Georgiev inde4c038c7b, but this commit was reverted inde088dd3a0, because the initial commit was reverted. This commit reverts both the reverting commits,7a443b1c49andde088dd3a0. Original commit message is below. If llvm-symbolize did not find module, the error looked like: LLVMSymbolizer: error reading file: No such file or directory This message does not follow common practice: LLVMSymbolizer is not an utility name. Also the message did not not contain the name of missed file. With this change the error message looks differently: llvm-symbolizer: error: 'abc': No such file or directory This format is closer to messages produced by other utilities and allow proper coloring. Differential Revision: https://reviews.llvm.org/D148032
400 lines
12 KiB
C++
400 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;
|
|
|
|
std::optional<StringRef>
|
|
load(StringRef FileName, const std::optional<StringRef> &EmbeddedSource) {
|
|
if (Lines <= 0)
|
|
return std::nullopt;
|
|
|
|
if (EmbeddedSource)
|
|
return EmbeddedSource;
|
|
else {
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
|
|
MemoryBuffer::getFile(FileName);
|
|
if (!BufOrErr)
|
|
return std::nullopt;
|
|
MemBuf = std::move(*BufOrErr);
|
|
return MemBuf->getBuffer();
|
|
}
|
|
}
|
|
|
|
std::optional<StringRef> pruneSource(const std::optional<StringRef> &Source) {
|
|
if (!Source)
|
|
return std::nullopt;
|
|
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 std::nullopt;
|
|
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 std::optional<StringRef> PrunedSource;
|
|
|
|
SourceCode(StringRef FileName, int64_t Line, int Lines,
|
|
const std::optional<StringRef> &EmbeddedSource =
|
|
std::optional<StringRef>())
|
|
: 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) {
|
|
ErrHandler(ErrorInfo, Request.ModuleName);
|
|
// 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;
|
|
}
|
|
|
|
static json::Object toJSON(const DILineInfo &LineInfo) {
|
|
return json::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}});
|
|
}
|
|
|
|
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 = toJSON(LineInfo);
|
|
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) {
|
|
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
|