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,9 +14,13 @@ #ifndef LLVM_DEBUGINFO_SYMBOLIZE_DIPRINTER_H #define LLVM_DEBUGINFO_SYMBOLIZE_DIPRINTER_H +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" #include +#include namespace llvm { +class ErrorInfoBase; struct DILineInfo; class DIInliningInfo; struct DIGlobal; @@ -25,35 +29,92 @@ namespace symbolize { +struct DIRequest { + StringRef ModuleName; + uint64_t Address = 0; + DIRequest(const StringRef &ModuleName, uint64_t Address) + : ModuleName(ModuleName), Address(Address){}; +}; + class DIPrinter { public: enum class OutputStyle { LLVM, GNU }; -private: +protected: raw_ostream &OS; + raw_ostream &ES; + +public: + DIPrinter(raw_ostream &OS, raw_ostream &ES) : OS(OS), ES(ES) {} + virtual ~DIPrinter(){}; + + virtual void print(const DIRequest &Request, const DILineInfo &Info) = 0; + virtual void print(const DIRequest &Request, const DIInliningInfo &Info) = 0; + virtual void print(const DIRequest &Request, const DIGlobal &Global) = 0; + virtual void print(const DIRequest &Request, + const std::vector &Locals) = 0; + + virtual bool printError(const DIRequest &Request, + const ErrorInfoBase &ErrorInfo, + const Twine ErrorBanner = {}) = 0; +}; + +class DIPrinterGeneric : public DIPrinter { +protected: + bool PrintAddress; bool PrintFunctionNames; bool PrintPretty; int PrintSourceContext; bool Verbose; - OutputStyle Style; + virtual void print(const DILineInfo &Info, bool Inlined) = 0; + virtual void printHeader(uint64_t Address); + virtual void printFooter() {} + void printContext(const Twine &FileName, int64_t Line); + +public: + DIPrinterGeneric(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) {} + virtual ~DIPrinterGeneric() {} + + void print(const DIRequest &Request, const DILineInfo &Info); + void print(const DIRequest &Request, const DIInliningInfo &Info); + void print(const DIRequest &Request, const DIGlobal &Global); + void print(const DIRequest &Request, const std::vector &Locals); + + bool printError(const DIRequest &Request, const ErrorInfoBase &ErrorInfo, + const Twine ErrorBanner = {}); +}; + +class DIPrinterLLVM : public DIPrinterGeneric { +private: + void print(const DILineInfo &Info, bool Inlined); + void printFooter(); + +public: + DIPrinterLLVM(raw_ostream &OS, raw_ostream &ES, bool PrintAddress = false, + bool PrintFunctionNames = true, bool PrintPretty = false, + int PrintSourceContext = 0, bool Verbose = false) + : DIPrinterGeneric(OS, ES, PrintAddress, PrintFunctionNames, PrintPretty, + PrintSourceContext, Verbose) {} +}; + +class DIPrinterGNU : public DIPrinterGeneric { +private: void print(const DILineInfo &Info, bool Inlined); - void printContext(const std::string &FileName, int64_t Line); 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); + DIPrinterGNU(raw_ostream &OS, raw_ostream &ES, bool PrintAddress = false, + bool PrintFunctionNames = true, bool PrintPretty = false, + int PrintSourceContext = 0, bool Verbose = false) + : DIPrinterGeneric(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 DIPrinterGeneric::printContext(const Twine &FileName, int64_t Line) { if (PrintSourceContext <= 0) return; @@ -60,9 +60,9 @@ } } -void DIPrinter::print(const DILineInfo &Info, bool Inlined) { +void DIPrinterLLVM::print(const DILineInfo &Info, bool Inlined) { if (PrintFunctionNames) { - std::string FunctionName = Info.FunctionName; + StringRef FunctionName = Info.FunctionName; if (FunctionName == DILineInfo::BadString) FunctionName = DILineInfo::Addr2LineBadString; @@ -70,14 +70,42 @@ StringRef Prefix = (PrintPretty && Inlined) ? " (inlined by) " : ""; OS << 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 << ":" << Info.Column << "\n"; + printContext(Filename, Info.Line); + return; + } + OS << " Filename: " << Filename << "\n"; + if (Info.StartLine) { + OS << " Function start filename: " << Info.StartFileName << "\n"; + OS << " Function start line: " << Info.StartLine << "\n"; + } + OS << " Line: " << Info.Line << "\n"; + OS << " Column: " << Info.Column << "\n"; + if (Info.Discriminator) + OS << " Discriminator: " << Info.Discriminator << "\n"; +} + +void DIPrinterGNU::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) " : ""; + OS << Prefix << FunctionName << Delimiter; + } + StringRef Filename = Info.FileName; + if (Filename == DILineInfo::BadString) + Filename = DILineInfo::Addr2LineBadString; + if (!Verbose) { + OS << Filename << ":" << Info.Line; + if (Info.Discriminator) OS << " (discriminator " << Info.Discriminator << ")"; OS << "\n"; printContext(Filename, Info.Line); @@ -94,63 +122,106 @@ OS << " Discriminator: " << Info.Discriminator << "\n"; } -DIPrinter &DIPrinter::operator<<(const DILineInfo &Info) { +void DIPrinterGeneric::printHeader(uint64_t Address) { + if (PrintAddress) { + OS << "0x"; + OS.write_hex(Address); + StringRef Delimiter = PrintPretty ? ": " : "\n"; + OS << Delimiter; + } +} + +void DIPrinterLLVM::printFooter() { OS << '\n'; } + +void DIPrinterGeneric::print(const DIRequest &Request, const DILineInfo &Info) { + printHeader(Request.Address); print(Info, false); - return *this; + printFooter(); } -DIPrinter &DIPrinter::operator<<(const DIInliningInfo &Info) { +void DIPrinterGeneric::print(const DIRequest &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 DIPrinterGeneric::print(const DIRequest &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 DIPrinterGeneric::print(const DIRequest &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(); +} + +bool DIPrinterGeneric::printError(const DIRequest &Request, + const ErrorInfoBase &ErrorInfo, + const Twine ErrorBanner) { + + if (ErrorInfo.convertToErrorCode().value() == + int(std::errc::invalid_argument)) { + OS << ErrorInfo.message() << '\n'; + return false; + } + 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,13 +74,26 @@ 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 printResOrErr(const StringRef &ModuleName, uint64_t Address, + Expected &ResOrErr, DIPrinter &Printer) { + if (ResOrErr) { + // No error, print the result. + Printer.print(DIRequest(ModuleName, Address), *ResOrErr); + return; + } + + // Handle the error. + bool PrintEmpty = true; + handleAllErrors( + std::move(ResOrErr.takeError()), [&](const ErrorInfoBase &EI) { + PrintEmpty = Printer.printError(DIRequest(ModuleName, Address), EI, + "LLVMSymbolizer: error reading file: "); + }); + + // Print an empty struct in case of Cmd != Command::Frame. + if (PrintEmpty && !std::is_same>::value) + Printer.print(DIRequest(ModuleName, Address), T()); } enum class Command { @@ -144,34 +157,26 @@ uint64_t Offset = 0; if (!parseCommand(Args.getLastArgValue(OPT_obj_EQ), IsAddr2Line, StringRef(InputString), Cmd, ModuleName, Offset)) { - outs() << InputString << "\n"; + Printer.printError( + DIRequest(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}); + printResOrErr(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}); + printResOrErr(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}); + printResOrErr(ModuleName, Offset, ResOrErr, Printer); } else if (OutputStyle == DIPrinter::OutputStyle::GNU) { // With PrintFunctions == FunctionNameKind::LinkageName (default) // and UseSymbolTable == true (also default), Symbolizer.symbolizeCode() @@ -179,21 +184,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)); + printResOrErr(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}); + printResOrErr(ModuleName, Offset, ResOrErr, Printer); } - if (OutputStyle == DIPrinter::OutputStyle::LLVM) - outs() << "\n"; } static void printHelp(StringRef ToolName, const SymbolizerOptTable &Tbl, @@ -322,9 +325,19 @@ } 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 DIPrinterGNU(outs(), errs(), Args.hasArg(OPT_addresses), + Opts.PrintFunctions != FunctionNameKind::None, + Args.hasArg(OPT_pretty_print), SourceContextLines, + Args.hasArg(OPT_verbose))); + else + Printer.reset( + new DIPrinterLLVM(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 +350,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;