diff --git a/llvm/include/llvm/DebugInfo/Symbolize/DIPrinter.h b/llvm/include/llvm/DebugInfo/Symbolize/DIPrinter.h --- a/llvm/include/llvm/DebugInfo/Symbolize/DIPrinter.h +++ b/llvm/include/llvm/DebugInfo/Symbolize/DIPrinter.h @@ -14,46 +14,109 @@ #ifndef LLVM_DEBUGINFO_SYMBOLIZE_DIPRINTER_H #define LLVM_DEBUGINFO_SYMBOLIZE_DIPRINTER_H +#include "llvm/ADT/StringRef.h" #include +#include namespace llvm { struct DILineInfo; class DIInliningInfo; struct DIGlobal; struct DILocal; +class ErrorInfoBase; class raw_ostream; namespace symbolize { +struct Request { + StringRef ModuleName; + uint64_t Address = 0; + Request(const StringRef ModuleName, uint64_t Address) + : ModuleName(ModuleName), Address(Address){}; +}; + class DIPrinter { public: enum class OutputStyle { LLVM, GNU }; -private: - raw_ostream &OS; +protected: + raw_ostream &OutputStream; + raw_ostream &ErrorStream; + +public: + DIPrinter(raw_ostream &OS, raw_ostream &ES) + : OutputStream(OS), ErrorStream(ES) {} + virtual ~DIPrinter(){}; + + virtual void print(const Request &Request, const DILineInfo &Info) = 0; + virtual void print(const Request &Request, const DIInliningInfo &Info) = 0; + virtual void print(const Request &Request, const DIGlobal &Global) = 0; + virtual void print(const Request &Request, + const std::vector &Locals) = 0; + + virtual bool printError(const Request &Request, + const ErrorInfoBase &ErrorInfo, + const StringRef ErrorBanner = "") = 0; +}; + +class PlainPrinterBase : public DIPrinter { +protected: + bool PrintAddress; bool PrintFunctionNames; bool PrintPretty; int PrintSourceContext; bool Verbose; - OutputStyle Style; - void print(const DILineInfo &Info, bool Inlined); - void printContext(const std::string &FileName, int64_t Line); + virtual void print(const DILineInfo &Info, bool Inlined) = 0; + virtual void printFooter() {} + void printContext(const StringRef FileName, int64_t Line); + +private: + void printHeader(uint64_t Address); + +public: + PlainPrinterBase(raw_ostream &OS, raw_ostream &ES, bool PrintAddress = false, + bool PrintFunctionNames = true, bool PrintPretty = false, + int PrintSourceContext = 0, bool Verbose = false) + : DIPrinter(OS, ES), PrintAddress(PrintAddress), + PrintFunctionNames(PrintFunctionNames), PrintPretty(PrintPretty), + PrintSourceContext(PrintSourceContext), Verbose(Verbose) {} + + void print(const Request &Request, const DILineInfo &Info) override; + void print(const Request &Request, const DIInliningInfo &Info) override; + void print(const Request &Request, const DIGlobal &Global) override; + void print(const Request &Request, + const std::vector &Locals) override; + + bool printError(const Request &Request, const ErrorInfoBase &ErrorInfo, + const StringRef ErrorBanner = "") override; +}; + +class LLVMPrinter : public PlainPrinterBase { +private: + void print(const DILineInfo &Info, bool Inlined) override; + void printFooter() override; + +public: + LLVMPrinter(raw_ostream &OS, raw_ostream &ES, bool PrintAddress = false, + bool PrintFunctionNames = true, bool PrintPretty = false, + int PrintSourceContext = 0, bool Verbose = false) + : PlainPrinterBase(OS, ES, PrintAddress, PrintFunctionNames, PrintPretty, + PrintSourceContext, Verbose) {} +}; + +class GNUPrinter : public PlainPrinterBase { +private: + void print(const DILineInfo &Info, bool Inlined) override; public: - DIPrinter(raw_ostream &OS, bool PrintFunctionNames = true, - bool PrintPretty = false, int PrintSourceContext = 0, - bool Verbose = false, OutputStyle Style = OutputStyle::LLVM) - : OS(OS), PrintFunctionNames(PrintFunctionNames), - PrintPretty(PrintPretty), PrintSourceContext(PrintSourceContext), - Verbose(Verbose), Style(Style) {} - - DIPrinter &operator<<(const DILineInfo &Info); - DIPrinter &operator<<(const DIInliningInfo &Info); - DIPrinter &operator<<(const DIGlobal &Global); - DIPrinter &operator<<(const DILocal &Local); + GNUPrinter(raw_ostream &OS, raw_ostream &ES, bool PrintAddress = false, + bool PrintFunctionNames = true, bool PrintPretty = false, + int PrintSourceContext = 0, bool Verbose = false) + : PlainPrinterBase(OS, ES, PrintAddress, PrintFunctionNames, PrintPretty, + PrintSourceContext, Verbose) {} }; -} -} +} // namespace symbolize +} // namespace llvm #endif diff --git a/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp b/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp --- a/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp +++ b/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp @@ -31,7 +31,7 @@ namespace symbolize { // Prints source code around in the FileName the Line. -void DIPrinter::printContext(const std::string &FileName, int64_t Line) { +void PlainPrinterBase::printContext(const StringRef FileName, int64_t Line) { if (PrintSourceContext <= 0) return; @@ -50,107 +50,176 @@ !I.is_at_eof() && I.line_number() <= LastLine; ++I) { int64_t L = I.line_number(); if (L >= FirstLine && L <= LastLine) { - OS << format_decimal(L, MaxLineNumberWidth); + OutputStream << format_decimal(L, MaxLineNumberWidth); if (L == Line) - OS << " >: "; + OutputStream << " >: "; else - OS << " : "; - OS << *I << "\n"; + OutputStream << " : "; + OutputStream << *I << "\n"; } } } -void DIPrinter::print(const DILineInfo &Info, bool Inlined) { +void LLVMPrinter::print(const DILineInfo &Info, bool Inlined) { if (PrintFunctionNames) { - std::string FunctionName = Info.FunctionName; + StringRef FunctionName = Info.FunctionName; if (FunctionName == DILineInfo::BadString) FunctionName = DILineInfo::Addr2LineBadString; StringRef Delimiter = PrintPretty ? " at " : "\n"; StringRef Prefix = (PrintPretty && Inlined) ? " (inlined by) " : ""; - OS << Prefix << FunctionName << Delimiter; + OutputStream << Prefix << FunctionName << Delimiter; } - std::string Filename = Info.FileName; + StringRef Filename = Info.FileName; if (Filename == DILineInfo::BadString) Filename = DILineInfo::Addr2LineBadString; if (!Verbose) { - OS << Filename << ":" << Info.Line; - if (Style == OutputStyle::LLVM) - OS << ":" << Info.Column; - else if (Style == OutputStyle::GNU && Info.Discriminator != 0) - OS << " (discriminator " << Info.Discriminator << ")"; - OS << "\n"; + OutputStream << Filename << ":" << Info.Line; + OutputStream << ":" << Info.Column << "\n"; printContext(Filename, Info.Line); return; } - OS << " Filename: " << Filename << "\n"; + OutputStream << " Filename: " << Filename << "\n"; if (Info.StartLine) { - OS << " Function start filename: " << Info.StartFileName << "\n"; - OS << " Function start line: " << Info.StartLine << "\n"; + OutputStream << " Function start filename: " << Info.StartFileName << "\n"; + OutputStream << " Function start line: " << Info.StartLine << "\n"; } - OS << " Line: " << Info.Line << "\n"; - OS << " Column: " << Info.Column << "\n"; + OutputStream << " Line: " << Info.Line << "\n"; + OutputStream << " Column: " << Info.Column << "\n"; if (Info.Discriminator) - OS << " Discriminator: " << Info.Discriminator << "\n"; + OutputStream << " Discriminator: " << Info.Discriminator << "\n"; } -DIPrinter &DIPrinter::operator<<(const DILineInfo &Info) { +void GNUPrinter::print(const DILineInfo &Info, bool Inlined) { + if (PrintFunctionNames) { + StringRef FunctionName = Info.FunctionName; + if (FunctionName == DILineInfo::BadString) + FunctionName = DILineInfo::Addr2LineBadString; + + StringRef Delimiter = PrintPretty ? " at " : "\n"; + StringRef Prefix = (PrintPretty && Inlined) ? " (inlined by) " : ""; + OutputStream << Prefix << FunctionName << Delimiter; + } + StringRef Filename = Info.FileName; + if (Filename == DILineInfo::BadString) + Filename = DILineInfo::Addr2LineBadString; + if (!Verbose) { + OutputStream << Filename << ":" << Info.Line; + if (Info.Discriminator) + OutputStream << " (discriminator " << Info.Discriminator << ")"; + OutputStream << "\n"; + printContext(Filename, Info.Line); + return; + } + OutputStream << " Filename: " << Filename << "\n"; + if (Info.StartLine) { + OutputStream << " Function start filename: " << Info.StartFileName << "\n"; + OutputStream << " Function start line: " << Info.StartLine << "\n"; + } + OutputStream << " Line: " << Info.Line << "\n"; + OutputStream << " Column: " << Info.Column << "\n"; + if (Info.Discriminator) + OutputStream << " Discriminator: " << Info.Discriminator << "\n"; +} + +void PlainPrinterBase::printHeader(uint64_t Address) { + if (PrintAddress) { + OutputStream << "0x"; + OutputStream.write_hex(Address); + StringRef Delimiter = PrintPretty ? ": " : "\n"; + OutputStream << Delimiter; + } +} + +void LLVMPrinter::printFooter() { OutputStream << '\n'; } + +void PlainPrinterBase::print(const Request &Request, const DILineInfo &Info) { + printHeader(Request.Address); print(Info, false); - return *this; + printFooter(); } -DIPrinter &DIPrinter::operator<<(const DIInliningInfo &Info) { +void PlainPrinterBase::print(const Request &Request, + const DIInliningInfo &Info) { + printHeader(Request.Address); uint32_t FramesNum = Info.getNumberOfFrames(); - if (FramesNum == 0) { + if (FramesNum == 0) print(DILineInfo(), false); - return *this; - } - for (uint32_t i = 0; i < FramesNum; i++) - print(Info.getFrame(i), i > 0); - return *this; + else + for (uint32_t I = 0; I < FramesNum; ++I) + print(Info.getFrame(I), I > 0); + printFooter(); } -DIPrinter &DIPrinter::operator<<(const DIGlobal &Global) { - std::string Name = Global.Name; +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"; - return *this; + OutputStream << Name << "\n"; + OutputStream << Global.Start << " " << Global.Size << "\n"; + printFooter(); } -DIPrinter &DIPrinter::operator<<(const DILocal &Local) { - if (Local.FunctionName.empty()) - OS << "??\n"; +void PlainPrinterBase::print(const Request &Request, + const std::vector &Locals) { + printHeader(Request.Address); + if (Locals.empty()) + OutputStream << DILineInfo::Addr2LineBadString << '\n'; else - OS << Local.FunctionName << '\n'; + for (const DILocal &L : Locals) { + if (L.FunctionName.empty()) + OutputStream << DILineInfo::Addr2LineBadString; + else + OutputStream << L.FunctionName; + OutputStream << '\n'; - if (Local.Name.empty()) - OS << "??\n"; - else - OS << Local.Name << '\n'; + if (L.Name.empty()) + OutputStream << DILineInfo::Addr2LineBadString; + else + OutputStream << L.Name; + OutputStream << '\n'; - if (Local.DeclFile.empty()) - OS << "??"; - else - OS << Local.DeclFile; - OS << ':' << Local.DeclLine << '\n'; + if (L.DeclFile.empty()) + OutputStream << DILineInfo::Addr2LineBadString; + else + OutputStream << L.DeclFile; - if (Local.FrameOffset) - OS << *Local.FrameOffset << ' '; - else - OS << "?? "; + OutputStream << ':' << L.DeclLine << '\n'; - if (Local.Size) - OS << *Local.Size << ' '; - else - OS << "?? "; + if (L.FrameOffset) + OutputStream << *L.FrameOffset; + else + OutputStream << DILineInfo::Addr2LineBadString; + OutputStream << ' '; - if (Local.TagOffset) - OS << *Local.TagOffset << '\n'; - else - OS << "??\n"; - return *this; + if (L.Size) + OutputStream << *L.Size; + else + OutputStream << DILineInfo::Addr2LineBadString; + OutputStream << ' '; + + if (L.TagOffset) + OutputStream << *L.TagOffset; + else + OutputStream << DILineInfo::Addr2LineBadString; + OutputStream << '\n'; + } + printFooter(); +} + +bool PlainPrinterBase::printError(const Request &Request, + const ErrorInfoBase &ErrorInfo, + const StringRef ErrorBanner) { + if (!ErrorBanner.empty()) { + ErrorStream << ErrorBanner; + ErrorInfo.log(ErrorStream); + ErrorStream << '\n'; + // Print an empty struct too. + return true; + } + OutputStream << ErrorInfo.message() << '\n'; + return false; } } // end namespace symbolize diff --git a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp --- a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp @@ -74,13 +74,25 @@ cl::desc("..."), cl::ZeroOrMore); -template -static bool error(Expected &ResOrErr) { - if (ResOrErr) - return false; - logAllUnhandledErrors(ResOrErr.takeError(), errs(), - "LLVMSymbolizer: error reading file: "); - return true; +template +static void print(const StringRef ModuleName, uint64_t Address, + Expected &ResOrErr, DIPrinter &Printer) { + if (ResOrErr) { + // No error, print the result. + Printer.print(Request(ModuleName, Address), *ResOrErr); + return; + } + + // Handle the error. + bool PrintEmpty = true; + handleAllErrors( + std::move(ResOrErr.takeError()), [&](const ErrorInfoBase &EI) { + PrintEmpty = Printer.printError(Request(ModuleName, Address), EI, + "LLVMSymbolizer: error reading file: "); + }); + + if (PrintEmpty) + Printer.print(Request(ModuleName, Address), T()); } enum class Command { @@ -144,34 +156,26 @@ uint64_t Offset = 0; if (!parseCommand(Args.getLastArgValue(OPT_obj_EQ), IsAddr2Line, StringRef(InputString), Cmd, ModuleName, Offset)) { - outs() << InputString << "\n"; + Printer.printError( + Request(ModuleName, Offset), + StringError(InputString, + std::make_error_code(std::errc::invalid_argument))); return; } - if (Args.hasArg(OPT_addresses)) { - outs() << "0x"; - outs().write_hex(Offset); - StringRef Delimiter = Args.hasArg(OPT_pretty_print) ? ": " : "\n"; - outs() << Delimiter; - } - Offset -= AdjustVMA; + uint64_t AdjustedOffset = Offset - AdjustVMA; if (Cmd == Command::Data) { - auto ResOrErr = Symbolizer.symbolizeData( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - Printer << (error(ResOrErr) ? DIGlobal() : ResOrErr.get()); + Expected ResOrErr = Symbolizer.symbolizeData( + ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection}); + print(ModuleName, Offset, ResOrErr, Printer); } else if (Cmd == Command::Frame) { - auto ResOrErr = Symbolizer.symbolizeFrame( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - if (!error(ResOrErr)) { - for (DILocal Local : *ResOrErr) - Printer << Local; - if (ResOrErr->empty()) - outs() << "??\n"; - } + Expected> ResOrErr = Symbolizer.symbolizeFrame( + ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection}); + print(ModuleName, Offset, ResOrErr, Printer); } else if (Args.hasFlag(OPT_inlines, OPT_no_inlines, !IsAddr2Line)) { - auto ResOrErr = Symbolizer.symbolizeInlinedCode( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - Printer << (error(ResOrErr) ? DIInliningInfo() : ResOrErr.get()); + Expected ResOrErr = Symbolizer.symbolizeInlinedCode( + ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection}); + print(ModuleName, Offset, ResOrErr, Printer); } else if (OutputStyle == DIPrinter::OutputStyle::GNU) { // With PrintFunctions == FunctionNameKind::LinkageName (default) // and UseSymbolTable == true (also default), Symbolizer.symbolizeCode() @@ -179,21 +183,19 @@ // caller function in the inlining chain. This contradicts the existing // behavior of addr2line. Symbolizer.symbolizeInlinedCode() overrides only // the topmost function, which suits our needs better. - auto ResOrErr = Symbolizer.symbolizeInlinedCode( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - if (!ResOrErr || ResOrErr->getNumberOfFrames() == 0) { - error(ResOrErr); - Printer << DILineInfo(); - } else { - Printer << ResOrErr->getFrame(0); - } + Expected ResOrErr = Symbolizer.symbolizeInlinedCode( + ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection}); + Expected Res0OrErr = + !ResOrErr + ? Expected(ResOrErr.takeError()) + : ((ResOrErr->getNumberOfFrames() == 0) ? DILineInfo() + : ResOrErr->getFrame(0)); + print(ModuleName, Offset, Res0OrErr, Printer); } else { - auto ResOrErr = Symbolizer.symbolizeCode( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - Printer << (error(ResOrErr) ? DILineInfo() : ResOrErr.get()); + Expected ResOrErr = Symbolizer.symbolizeCode( + ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection}); + print(ModuleName, Offset, ResOrErr, Printer); } - if (OutputStyle == DIPrinter::OutputStyle::LLVM) - outs() << "\n"; } static void printHelp(StringRef ToolName, const SymbolizerOptTable &Tbl, @@ -322,9 +324,18 @@ } LLVMSymbolizer Symbolizer(Opts); - DIPrinter Printer(outs(), Opts.PrintFunctions != FunctionNameKind::None, - Args.hasArg(OPT_pretty_print), SourceContextLines, - Args.hasArg(OPT_verbose), OutputStyle); + std::unique_ptr Printer; + if (OutputStyle == DIPrinter::OutputStyle::GNU) + Printer.reset(new GNUPrinter(outs(), errs(), Args.hasArg(OPT_addresses), + Opts.PrintFunctions != FunctionNameKind::None, + Args.hasArg(OPT_pretty_print), + SourceContextLines, Args.hasArg(OPT_verbose))); + else + Printer.reset(new LLVMPrinter(outs(), errs(), Args.hasArg(OPT_addresses), + Opts.PrintFunctions != FunctionNameKind::None, + Args.hasArg(OPT_pretty_print), + SourceContextLines, + Args.hasArg(OPT_verbose))); std::vector InputAddresses = Args.getAllArgValues(OPT_INPUT); if (InputAddresses.empty()) { @@ -337,13 +348,13 @@ llvm::erase_if(StrippedInputString, [](char c) { return c == '\r' || c == '\n'; }); symbolizeInput(Args, AdjustVMA, IsAddr2Line, OutputStyle, - StrippedInputString, Symbolizer, Printer); + StrippedInputString, Symbolizer, *Printer); outs().flush(); } } else { for (StringRef Address : InputAddresses) symbolizeInput(Args, AdjustVMA, IsAddr2Line, OutputStyle, Address, - Symbolizer, Printer); + Symbolizer, *Printer); } return 0;