Files
clang-p2996/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp
Serge Pavlov 1d5fa4f88f [symbolizer] Change error message if module not found (recommit)
This is a recommit of 75f1f15881, reverted in 7a443b1c49, because
it caused compilation error in
compiler-rt/lib/sanitizer_common/symbolizer/sanitizer_symbolize.cpp.
The error was fixed by Kasimir Georgiev in de4c038c7b, but this
commit was reverted in de088dd3a0, because the initial commit was
reverted.

This commit reverts both the reverting commits, 7a443b1c49 and
de088dd3a0.

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
2023-04-23 06:35:35 +00:00

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