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,105 @@ #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; +}; + class DIPrinter { public: - enum class OutputStyle { LLVM, GNU }; + DIPrinter(){}; + virtual ~DIPrinter(){}; -private: - raw_ostream &OS; - bool PrintFunctionNames; - bool PrintPretty; - int PrintSourceContext; + 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 void printInvalidCommand(const Request &Request, + const ErrorInfoBase &ErrorInfo) = 0; + + virtual bool printError(const Request &Request, + const ErrorInfoBase &ErrorInfo, + StringRef ErrorBanner) = 0; +}; + +struct PrinterConfig { + bool PrintAddress; + bool PrintFunctions; + bool Pretty; bool Verbose; - OutputStyle Style; + int SourceContextLines; +}; + +class PlainPrinterBase : public DIPrinter { +protected: + raw_ostream &OS; + raw_ostream &ES; + PrinterConfig Config; void print(const DILineInfo &Info, bool Inlined); - void printContext(const std::string &FileName, int64_t Line); + void printFunctionName(StringRef FunctionName, bool Inlined); + virtual void printSimpleLocation(StringRef Filename, + const DILineInfo &Info) = 0; + void printContext(StringRef FileName, int64_t Line); + void printVerbose(StringRef Filename, const DILineInfo &Info); + virtual void printFooter() {} + +private: + void printHeader(uint64_t Address); + +public: + PlainPrinterBase(raw_ostream &OS, raw_ostream &ES, PrinterConfig &Config) + : DIPrinter(), OS(OS), ES(ES), Config(Config) {} + + 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; + + void printInvalidCommand(const Request &Request, + const ErrorInfoBase &ErrorInfo) override; + + bool printError(const Request &Request, const ErrorInfoBase &ErrorInfo, + StringRef ErrorBanner) override; +}; + +class LLVMPrinter : public PlainPrinterBase { +private: + void printSimpleLocation(StringRef Filename, const DILineInfo &Info) override; + void printFooter() override; + +public: + LLVMPrinter(raw_ostream &OS, raw_ostream &ES, PrinterConfig &Config) + : PlainPrinterBase(OS, ES, Config) {} +}; + +class GNUPrinter : public PlainPrinterBase { +private: + void printSimpleLocation(StringRef Filename, const DILineInfo &Info) 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, PrinterConfig &Config) + : PlainPrinterBase(OS, ES, Config) {} }; -} -} +} // 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 @@ -30,9 +30,18 @@ namespace llvm { namespace symbolize { +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 DIPrinter::printContext(const std::string &FileName, int64_t Line) { - if (PrintSourceContext <= 0) +void PlainPrinterBase::printContext(StringRef FileName, int64_t Line) { + if (Config.SourceContextLines <= 0) return; ErrorOr> BufOrErr = @@ -42,8 +51,8 @@ std::unique_ptr Buf = std::move(BufOrErr.get()); int64_t FirstLine = - std::max(static_cast(1), Line - PrintSourceContext / 2); - int64_t LastLine = FirstLine + PrintSourceContext; + std::max(static_cast(1), Line - Config.SourceContextLines / 2); + int64_t LastLine = FirstLine + Config.SourceContextLines; size_t MaxLineNumberWidth = std::ceil(std::log10(LastLine)); for (line_iterator I = line_iterator(*Buf, false); @@ -60,97 +69,145 @@ } } -void DIPrinter::print(const DILineInfo &Info, bool Inlined) { - if (PrintFunctionNames) { - std::string FunctionName = Info.FunctionName; +void PlainPrinterBase::printFunctionName(StringRef FunctionName, bool Inlined) { + if (Config.PrintFunctions) { if (FunctionName == DILineInfo::BadString) FunctionName = DILineInfo::Addr2LineBadString; - - StringRef Delimiter = PrintPretty ? " at " : "\n"; - StringRef Prefix = (PrintPretty && Inlined) ? " (inlined by) " : ""; + StringRef Delimiter = Config.Pretty ? " at " : "\n"; + StringRef Prefix = (Config.Pretty && Inlined) ? " (inlined by) " : ""; OS << Prefix << FunctionName << Delimiter; } - std::string 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"; - printContext(Filename, Info.Line); - return; - } - OS << " Filename: " << Filename << "\n"; +} + +void LLVMPrinter::printSimpleLocation(StringRef Filename, + const DILineInfo &Info) { + OS << Filename << ':' << Info.Line << ':' << Info.Column << '\n'; + printContext(Filename, Info.Line); +} + +void GNUPrinter::printSimpleLocation(StringRef Filename, + const DILineInfo &Info) { + OS << Filename << ':' << Info.Line; + if (Info.Discriminator) + OS << " (discriminator " << Info.Discriminator << ')'; + OS << '\n'; + printContext(Filename, Info.Line); +} + +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"; + OS << " Function start filename: " << Info.StartFileName << '\n'; + OS << " Function start line: " << Info.StartLine << '\n'; } - OS << " Line: " << Info.Line << "\n"; - OS << " Column: " << Info.Column << "\n"; + OS << " Line: " << Info.Line << '\n'; + OS << " Column: " << Info.Column << '\n'; if (Info.Discriminator) - OS << " Discriminator: " << Info.Discriminator << "\n"; + OS << " Discriminator: " << Info.Discriminator << '\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); } -DIPrinter &DIPrinter::operator<<(const DILineInfo &Info) { +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; + 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()) + OS << DILineInfo::Addr2LineBadString << '\n'; else - OS << Local.FunctionName << '\n'; + for (const DILocal &L : Locals) { + if (L.FunctionName.empty()) + OS << DILineInfo::Addr2LineBadString; + else + OS << L.FunctionName; + OS << '\n'; - if (Local.Name.empty()) - OS << "??\n"; - else - OS << Local.Name << '\n'; + if (L.Name.empty()) + OS << DILineInfo::Addr2LineBadString; + else + OS << L.Name; + OS << '\n'; - if (Local.DeclFile.empty()) - OS << "??"; - else - OS << Local.DeclFile; - OS << ':' << Local.DeclLine << '\n'; + if (L.DeclFile.empty()) + OS << DILineInfo::Addr2LineBadString; + else + OS << L.DeclFile; - if (Local.FrameOffset) - OS << *Local.FrameOffset << ' '; - else - OS << "?? "; + OS << ':' << L.DeclLine << '\n'; - if (Local.Size) - OS << *Local.Size << ' '; - else - OS << "?? "; + if (L.FrameOffset) + OS << *L.FrameOffset; + else + OS << DILineInfo::Addr2LineBadString; + OS << ' '; - if (Local.TagOffset) - OS << *Local.TagOffset << '\n'; - else - OS << "??\n"; - return *this; + 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, + const ErrorInfoBase &ErrorInfo) { + OS << ErrorInfo.message() << '\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; } } // 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,15 +74,29 @@ 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 Request &Request, Expected &ResOrErr, + DIPrinter &Printer) { + if (ResOrErr) { + // No error, print the result. + Printer.print(Request, *ResOrErr); + return; + } + + // Handle the error. + bool PrintEmpty = true; + handleAllErrors(std::move(ResOrErr.takeError()), + [&](const ErrorInfoBase &EI) { + PrintEmpty = Printer.printError( + Request, EI, "LLVMSymbolizer: error reading file: "); + }); + + if (PrintEmpty) + Printer.print(Request, T()); } +enum class OutputStyle { LLVM, GNU }; + enum class Command { Code, Data, @@ -136,7 +150,7 @@ } static void symbolizeInput(const opt::InputArgList &Args, uint64_t AdjustVMA, - bool IsAddr2Line, DIPrinter::OutputStyle OutputStyle, + bool IsAddr2Line, OutputStyle OutputStyle, StringRef InputString, LLVMSymbolizer &Symbolizer, DIPrinter &Printer) { Command Cmd; @@ -144,56 +158,46 @@ uint64_t Offset = 0; if (!parseCommand(Args.getLastArgValue(OPT_obj_EQ), IsAddr2Line, StringRef(InputString), Cmd, ModuleName, Offset)) { - outs() << InputString << "\n"; + Printer.printInvalidCommand( + {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()); - } else if (OutputStyle == DIPrinter::OutputStyle::GNU) { + Expected ResOrErr = Symbolizer.symbolizeInlinedCode( + ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection}); + print({ModuleName, Offset}, ResOrErr, Printer); + } else if (OutputStyle == OutputStyle::GNU) { // With PrintFunctions == FunctionNameKind::LinkageName (default) // and UseSymbolTable == true (also default), Symbolizer.symbolizeCode() // may override the name of an inlined function with the name of the topmost // 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, @@ -273,7 +277,7 @@ LLVMSymbolizer::Options Opts; uint64_t AdjustVMA; - unsigned SourceContextLines; + PrinterConfig Config; parseIntArg(Args, OPT_adjust_vma_EQ, AdjustVMA); if (const opt::Arg *A = Args.getLastArg(OPT_basenames, OPT_relativenames)) { Opts.PathStyle = @@ -290,7 +294,8 @@ Opts.FallbackDebugPath = Args.getLastArgValue(OPT_fallback_debug_path_EQ).str(); Opts.PrintFunctions = decideHowToPrintFunctions(Args, IsAddr2Line); - parseIntArg(Args, OPT_print_source_context_lines_EQ, SourceContextLines); + parseIntArg(Args, OPT_print_source_context_lines_EQ, + Config.SourceContextLines); Opts.RelativeAddresses = Args.hasArg(OPT_relative_address); Opts.UntagAddresses = Args.hasFlag(OPT_untag_addresses, OPT_no_untag_addresses, !IsAddr2Line); @@ -302,6 +307,10 @@ } #endif Opts.UseSymbolTable = true; + Config.PrintAddress = Args.hasArg(OPT_addresses); + Config.PrintFunctions = Opts.PrintFunctions != FunctionNameKind::None; + Config.Pretty = Args.hasArg(OPT_pretty_print); + Config.Verbose = Args.hasArg(OPT_verbose); for (const opt::Arg *A : Args.filtered(OPT_dsym_hint_EQ)) { StringRef Hint(A->getValue()); @@ -313,18 +322,18 @@ } } - auto OutputStyle = - IsAddr2Line ? DIPrinter::OutputStyle::GNU : DIPrinter::OutputStyle::LLVM; + auto OutputStyle = IsAddr2Line ? OutputStyle::GNU : OutputStyle::LLVM; if (const opt::Arg *A = Args.getLastArg(OPT_output_style_EQ)) { - OutputStyle = strcmp(A->getValue(), "GNU") == 0 - ? DIPrinter::OutputStyle::GNU - : DIPrinter::OutputStyle::LLVM; + OutputStyle = strcmp(A->getValue(), "GNU") == 0 ? OutputStyle::GNU + : OutputStyle::LLVM; } 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 == OutputStyle::GNU) + Printer = std::make_unique(outs(), errs(), Config); + else + Printer = std::make_unique(outs(), errs(), Config); std::vector InputAddresses = Args.getAllArgValues(OPT_INPUT); if (InputAddresses.empty()) { @@ -337,13 +346,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;