diff --git a/llvm/test/tools/llvm-symbolizer/Inputs/flush-output.py b/llvm/test/tools/llvm-symbolizer/Inputs/flush-output.py --- a/llvm/test/tools/llvm-symbolizer/Inputs/flush-output.py +++ b/llvm/test/tools/llvm-symbolizer/Inputs/flush-output.py @@ -11,7 +11,7 @@ # Pass -f=none and --output-style=GNU to get only one line of output per input. cmd = subprocess.Popen([sys.argv[1], '--obj=' + sys.argv[2], - '-f=none', + '--functions=none', '--output-style=GNU'], stdout=subprocess.PIPE, stdin=subprocess.PIPE) watchdog = threading.Timer(20, kill_subprocess, args=[cmd]) watchdog.start() diff --git a/llvm/test/tools/llvm-symbolizer/basic.s b/llvm/test/tools/llvm-symbolizer/basic.s --- a/llvm/test/tools/llvm-symbolizer/basic.s +++ b/llvm/test/tools/llvm-symbolizer/basic.s @@ -17,8 +17,8 @@ # Check --obj aliases --exe, -e # RUN: llvm-symbolizer 0xa 0xb --exe=%t.o | FileCheck %s +# RUN: llvm-symbolizer 0xa 0xb --exe %t.o | FileCheck %s # RUN: llvm-symbolizer 0xa 0xb -e %t.o | FileCheck %s -# RUN: llvm-symbolizer 0xa 0xb -e=%t.o | FileCheck %s # RUN: llvm-symbolizer 0xa 0xb -e%t.o | FileCheck %s # CHECK: basic.s:5:0 diff --git a/llvm/test/tools/llvm-symbolizer/functions.s b/llvm/test/tools/llvm-symbolizer/functions.s --- a/llvm/test/tools/llvm-symbolizer/functions.s +++ b/llvm/test/tools/llvm-symbolizer/functions.s @@ -5,11 +5,8 @@ # RUN: llvm-symbolizer 0 --obj=%t.o | FileCheck %s --check-prefix=LINKAGE # RUN: llvm-symbolizer 0 -f --obj=%t.o | FileCheck %s --check-prefix=LINKAGE # RUN: llvm-symbolizer 0 --functions --obj=%t.o | FileCheck %s --check-prefix=LINKAGE -# RUN: llvm-symbolizer 0 -f=linkage --obj=%t.o | FileCheck %s --check-prefix=LINKAGE # RUN: llvm-symbolizer 0 --functions=linkage --obj=%t.o | FileCheck %s --check-prefix=LINKAGE -# RUN: llvm-symbolizer 0 -f=short --obj=%t.o | FileCheck %s --check-prefix=SHORT # RUN: llvm-symbolizer 0 --functions=short --obj=%t.o | FileCheck %s --check-prefix=SHORT -# RUN: llvm-symbolizer 0 -f=none --obj=%t.o | FileCheck %s --check-prefix=NONE # RUN: llvm-symbolizer 0 --functions=none --obj=%t.o | FileCheck %s --check-prefix=NONE ## Characterise behaviour for no '=' sign. llvm-symbolizer treats the next option as an diff --git a/llvm/test/tools/llvm-symbolizer/output-style-inlined.test b/llvm/test/tools/llvm-symbolizer/output-style-inlined.test --- a/llvm/test/tools/llvm-symbolizer/output-style-inlined.test +++ b/llvm/test/tools/llvm-symbolizer/output-style-inlined.test @@ -1,16 +1,16 @@ -This test checks that when inlined frames are not shown (-i=0) and the output +This test checks that when inlined frames are not shown (--no-inlines) and the output style is set to GNU (--output-style=GNU) the name of an inlined function is not replaced with the name of the top caller function. At the same time, the current behavior of llvm-symbolizer is preserved with --output-style=LLVM or when the option is not specified. -RUN: llvm-symbolizer -i=0 -e %p/Inputs/addr.exe 0x40054d \ +RUN: llvm-symbolizer --no-inlines -e %p/Inputs/addr.exe 0x40054d \ RUN: | FileCheck %s --check-prefix=LLVM --implicit-check-not=inctwo -RUN: llvm-symbolizer --output-style=LLVM -i=0 -e %p/Inputs/addr.exe 0x40054d \ +RUN: llvm-symbolizer --output-style=LLVM --no-inlines -e %p/Inputs/addr.exe 0x40054d \ RUN: | FileCheck %s --check-prefix=LLVM --implicit-check-not=inctwo -RUN: llvm-symbolizer --output-style=GNU -i=0 -e %p/Inputs/addr.exe 0x40054d \ +RUN: llvm-symbolizer --output-style=GNU --no-inlines -e %p/Inputs/addr.exe 0x40054d \ RUN: | FileCheck %s --check-prefix=GNU --implicit-check-not=main RUN: llvm-addr2line -f -e %p/Inputs/addr.exe 0x40054d \ diff --git a/llvm/test/tools/llvm-symbolizer/split-dwarf.test b/llvm/test/tools/llvm-symbolizer/split-dwarf.test --- a/llvm/test/tools/llvm-symbolizer/split-dwarf.test +++ b/llvm/test/tools/llvm-symbolizer/split-dwarf.test @@ -4,14 +4,14 @@ RUN: cp %p/Inputs/split-dwarf-test.dwo %t RUN: cd %t -RUN: llvm-symbolizer --functions=linkage --inlining --demangle=false \ +RUN: llvm-symbolizer --functions=linkage --inlining --no-demangle \ RUN: --obj=%p/Inputs/split-dwarf-test 0x400504 0x4004f4 | FileCheck --check-prefixes=SPLIT,DWO %s Ensure we get the same results in the absence of gmlt-like data in the executable but the presence of a .dwo file RUN: echo "%p/Inputs/split-dwarf-test-nogmlt 0x400504" >> %t.input RUN: echo "%p/Inputs/split-dwarf-test-nogmlt 0x4004f4" >> %t.input -RUN: llvm-symbolizer --functions=linkage --inlining --demangle=false \ +RUN: llvm-symbolizer --functions=linkage --inlining --no-demangle \ RUN: --default-arch=i386 --obj=%p/Inputs/split-dwarf-test-nogmlt 0x400504 0x4004f4 | FileCheck --check-prefixes=SPLIT,DWO %s Ensure we get gmlt like results in the absence of a .dwo file but the presence of gmlt-like data in the executable @@ -19,7 +19,7 @@ RUN: rm %t/split-dwarf-test.dwo RUN: echo "%p/Inputs/split-dwarf-test 0x400504" >> %t.input RUN: echo "%p/Inputs/split-dwarf-test 0x4004f4" >> %t.input -RUN: llvm-symbolizer --functions=linkage --inlining --demangle=false \ +RUN: llvm-symbolizer --functions=linkage --inlining --no-demangle \ RUN: --default-arch=i386 --obj=%p/Inputs/split-dwarf-test 0x400504 0x4004f4 | FileCheck --check-prefixes=SPLIT,NODWO %s DWO: _Z2f2v diff --git a/llvm/test/tools/llvm-symbolizer/untag-addresses.test b/llvm/test/tools/llvm-symbolizer/untag-addresses.test --- a/llvm/test/tools/llvm-symbolizer/untag-addresses.test +++ b/llvm/test/tools/llvm-symbolizer/untag-addresses.test @@ -2,7 +2,7 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o # RUN: echo DATA %t.o 0 | llvm-symbolizer | FileCheck --check-prefix=UNTAG %s -# RUN: echo DATA %t.o 0 | llvm-symbolizer -untag-addresses=0 | FileCheck --check-prefix=NOUNTAG %s +# RUN: echo DATA %t.o 0 | llvm-symbolizer --no-untag-addresses | FileCheck --check-prefix=NOUNTAG %s # RUN: echo DATA %t.o 0 | llvm-addr2line | FileCheck --check-prefix=NOUNTAG %s # UNTAG: foo diff --git a/llvm/tools/llvm-symbolizer/CMakeLists.txt b/llvm/tools/llvm-symbolizer/CMakeLists.txt --- a/llvm/tools/llvm-symbolizer/CMakeLists.txt +++ b/llvm/tools/llvm-symbolizer/CMakeLists.txt @@ -3,17 +3,24 @@ # This means that we need LLVM libraries to be compiled for these # targets as well. Currently, there is no support for such a build strategy. +set(LLVM_TARGET_DEFINITIONS Opts.td) +tablegen(LLVM Opts.inc -gen-opt-parser-defs) +add_public_tablegen_target(SymbolizerOptsTableGen) + set(LLVM_LINK_COMPONENTS DebugInfoDWARF DebugInfoPDB Demangle Object + Option Support Symbolize ) add_llvm_tool(llvm-symbolizer llvm-symbolizer.cpp + DEPENDS + SymbolizerOptsTableGen ) add_llvm_tool_symlink(llvm-addr2line llvm-symbolizer) diff --git a/llvm/tools/llvm-symbolizer/Opts.td b/llvm/tools/llvm-symbolizer/Opts.td new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-symbolizer/Opts.td @@ -0,0 +1,59 @@ +include "llvm/Option/OptParser.td" + +multiclass B { + def NAME: Flag<["--", "-"], name>, HelpText; + def no_ # NAME: Flag<["--", "-"], "no-" # name>, HelpText; +} + +multiclass Eq { + def NAME #_EQ : Joined<["--", "-"], name #"=">, + HelpText; + def : Separate<["--", "-"], name>, Alias(NAME #_EQ)>; +} + +class F: Flag<["--", "-"], name>, HelpText; + +def addresses : F<"addresses", "Show address before line information">; +defm adjust_vma + : Eq<"adjust-vma", "Add specified offset to object file addresses">, + MetaVarName<"">; +def basenames : Flag<["--"], "basenames">, HelpText<"Strip directory names from paths">; +defm debug_file_directory : Eq<"debug-file-directory", "Path to directory where to look for debug files">; +defm default_arch : Eq<"default-arch", "Default architecture (for multi-arch objects)">; +defm demangle : B<"demangle", "Demangle function names", "Don't demangle function names">; +def functions : F<"functions", "Print function name for a given address">; +def functions_EQ : Joined<["--"], "functions=">, HelpText<"Print function name for a given address">, Values<"none,short,linkage">; +def help : F<"help", "Display this help">; +defm dwp : Eq<"dwp", "Path to DWP file to be use for any split CUs">; +defm dsym_hint : Eq<"dsym-hint", "Path to .dSYM bundles to search for debug info for the object files">; +defm fallback_debug_path : Eq<"fallback-debug-path", "Fallback path for debug binaries">; +defm inlines : B<"inlines", "Print all inlined frames for a given address", + "Do not print inlined frames">; +defm obj + : Eq<"obj", "Path to object file to be symbolized (if not provided, " + "object file should be specified for each input line)">; +defm output_style + : Eq<"output-style", "Specify print style. Supported styles: LLVM, GNU">, + MetaVarName<"style">, + Values<"LLVM,GNU">; +def pretty_print : F<"pretty-print", "Make the output more human friendly">; +defm print_source_context_lines : Eq<"print-source-context-lines", "Print N lines of source file context">; +def relative_address : F<"relative-address", "Interpret addresses as addresses relative to the image base">; +def relativenames : F<"relativenames", "Strip the compilation directory from paths">; +defm untag_addresses : B<"untag-addresses", "", "Remove memory tags from addresses before symbolization">; +def use_native_pdb_reader : F<"use-native-pdb-reader", "Use native PDB functionality">; +def verbose : F<"verbose", "Print verbose line info">; + +def : Flag<["-"], "a">, Alias, HelpText<"Alias for --addresses">; +def : F<"print-address", "Alias for --addresses">, Alias; +def : Flag<["-"], "C">, Alias, HelpText<"Alias for --demangle">; +def : Joined<["--"], "exe=">, Alias; +def : Separate<["--"], "exe">, Alias; +def : Joined<["-"], "e">, Alias, HelpText<"Alias for --exe">; +def : Separate<["-"], "e">, Alias, HelpText<"Alias for --exe">; +def : Flag<["-"], "f">, Alias, HelpText<"Alias for --functions">; +def : Flag<["-"], "h">, Alias; +def : Flag<["-"], "i">, Alias, HelpText<"Alias for --inlines">; +def : F<"inlining", "Alias for --inlines">, Alias; +def : Flag<["-"], "p">, Alias, HelpText<"Alias for --pretty-print">; +def : Flag<["-"], "s">, Alias, HelpText<"Alias for --basenames">; 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 @@ -14,15 +14,22 @@ // //===----------------------------------------------------------------------===// +#include "Opts.inc" #include "llvm/ADT/StringRef.h" #include "llvm/DebugInfo/Symbolize/DIPrinter.h" #include "llvm/DebugInfo/Symbolize/Symbolize.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" #include "llvm/Support/COM.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/StringSaver.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -32,141 +39,42 @@ using namespace llvm; using namespace symbolize; -static cl::opt -ClUseSymbolTable("use-symbol-table", cl::init(true), - cl::desc("Prefer names in symbol table to names " - "in debug info")); - -static cl::opt ClPrintFunctions( - "functions", cl::init(FunctionNameKind::LinkageName), - cl::desc("Print function name for a given address"), cl::ValueOptional, - cl::values(clEnumValN(FunctionNameKind::None, "none", "omit function name"), - clEnumValN(FunctionNameKind::ShortName, "short", - "print short function name"), - clEnumValN(FunctionNameKind::LinkageName, "linkage", - "print function linkage name"), - // Sentinel value for unspecified value. - clEnumValN(FunctionNameKind::LinkageName, "", ""))); -static cl::alias ClPrintFunctionsShort("f", cl::desc("Alias for -functions"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(ClPrintFunctions)); - -static cl::opt - ClUseRelativeAddress("relative-address", cl::init(false), - cl::desc("Interpret addresses as relative addresses"), - cl::ReallyHidden); - -static cl::opt ClUntagAddresses( - "untag-addresses", cl::init(true), - cl::desc("Remove memory tags from addresses before symbolization")); - -static cl::opt - ClPrintInlining("inlining", cl::init(true), - cl::desc("Print all inlined frames for a given address")); -static cl::alias - ClPrintInliningAliasI("i", cl::desc("Alias for -inlining"), - cl::NotHidden, cl::aliasopt(ClPrintInlining), - cl::Grouping); -static cl::alias - ClPrintInliningAliasInlines("inlines", cl::desc("Alias for -inlining"), - cl::NotHidden, cl::aliasopt(ClPrintInlining)); - -static cl::opt ClBasenames("basenames", cl::init(false), - cl::desc("Strip directory names from paths")); -static cl::alias ClBasenamesShort("s", cl::desc("Alias for -basenames"), - cl::NotHidden, cl::aliasopt(ClBasenames)); - -static cl::opt - ClRelativenames("relativenames", cl::init(false), - cl::desc("Strip the compilation directory from paths")); - -static cl::opt -ClDemangle("demangle", cl::init(true), cl::desc("Demangle function names")); -static cl::alias -ClDemangleShort("C", cl::desc("Alias for -demangle"), - cl::NotHidden, cl::aliasopt(ClDemangle), cl::Grouping); -static cl::opt -ClNoDemangle("no-demangle", cl::init(false), - cl::desc("Don't demangle function names")); - -static cl::opt ClDefaultArch("default-arch", cl::init(""), - cl::desc("Default architecture " - "(for multi-arch objects)")); - -static cl::opt -ClBinaryName("obj", cl::init(""), - cl::desc("Path to object file to be symbolized (if not provided, " - "object file should be specified for each input line)")); -static cl::alias -ClBinaryNameAliasExe("exe", cl::desc("Alias for -obj"), - cl::NotHidden, cl::aliasopt(ClBinaryName)); -static cl::alias ClBinaryNameAliasE("e", cl::desc("Alias for -obj"), - cl::NotHidden, cl::Grouping, cl::Prefix, - cl::aliasopt(ClBinaryName)); - -static cl::opt - ClDwpName("dwp", cl::init(""), - cl::desc("Path to DWP file to be use for any split CUs")); - -static cl::list -ClDsymHint("dsym-hint", cl::ZeroOrMore, - cl::desc("Path to .dSYM bundles to search for debug info for the " - "object files")); - -static cl::opt -ClPrintAddress("print-address", cl::init(false), - cl::desc("Show address before line information")); -static cl::alias -ClPrintAddressAliasAddresses("addresses", cl::desc("Alias for -print-address"), - cl::NotHidden, cl::aliasopt(ClPrintAddress)); -static cl::alias -ClPrintAddressAliasA("a", cl::desc("Alias for -print-address"), - cl::NotHidden, cl::aliasopt(ClPrintAddress), cl::Grouping); - -static cl::opt - ClPrettyPrint("pretty-print", cl::init(false), - cl::desc("Make the output more human friendly")); -static cl::alias ClPrettyPrintShort("p", cl::desc("Alias for -pretty-print"), - cl::NotHidden, - cl::aliasopt(ClPrettyPrint), cl::Grouping); - -static cl::opt ClPrintSourceContextLines( - "print-source-context-lines", cl::init(0), - cl::desc("Print N number of source file context")); - -static cl::opt ClVerbose("verbose", cl::init(false), - cl::desc("Print verbose line info")); - -static cl::opt - ClAdjustVMA("adjust-vma", cl::init(0), cl::value_desc("offset"), - cl::desc("Add specified offset to object file addresses")); +namespace { +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Opts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Opts.inc" +#undef PREFIX + +static const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Opts.inc" +#undef OPTION +}; + +class SymbolizerOptTable : public opt::OptTable { +public: + SymbolizerOptTable() : OptTable(InfoTable, true) {} +}; +} // namespace static cl::list ClInputAddresses(cl::Positional, cl::desc("..."), cl::ZeroOrMore); -static cl::opt - ClFallbackDebugPath("fallback-debug-path", cl::init(""), - cl::desc("Fallback path for debug binaries.")); - -static cl::list - ClDebugFileDirectory("debug-file-directory", cl::ZeroOrMore, - cl::value_desc("dir"), - cl::desc("Path to directory where to look for debug " - "files.")); - -static cl::opt - ClOutputStyle("output-style", cl::init(DIPrinter::OutputStyle::LLVM), - cl::desc("Specify print style"), - cl::values(clEnumValN(DIPrinter::OutputStyle::LLVM, "LLVM", - "LLVM default style"), - clEnumValN(DIPrinter::OutputStyle::GNU, "GNU", - "GNU addr2line style"))); - -static cl::opt - ClUseNativePDBReader("use-native-pdb-reader", cl::init(0), - cl::desc("Use native PDB functionality")); - static cl::extrahelp HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); @@ -185,7 +93,8 @@ Frame, }; -static bool parseCommand(bool IsAddr2Line, StringRef InputString, Command &Cmd, +static bool parseCommand(StringRef BinaryName, bool IsAddr2Line, + StringRef InputString, Command &Cmd, std::string &ModuleName, uint64_t &ModuleOffset) { const char kDelimiters[] = " \n\r"; ModuleName = ""; @@ -201,7 +110,7 @@ } const char *Pos = InputString.data(); // Skip delimiters and parse input filename (if needed). - if (ClBinaryName.empty()) { + if (BinaryName.empty()) { Pos += strspn(Pos, kDelimiters); if (*Pos == '"' || *Pos == '\'') { char Quote = *Pos; @@ -217,7 +126,7 @@ Pos += NameLength; } } else { - ModuleName = ClBinaryName; + ModuleName = BinaryName.str(); } // Skip delimiters and parse module offset. Pos += strspn(Pos, kDelimiters); @@ -230,24 +139,26 @@ return !Offset.getAsInteger(IsAddr2Line ? 16 : 0, ModuleOffset); } -static void symbolizeInput(bool IsAddr2Line, StringRef InputString, - LLVMSymbolizer &Symbolizer, DIPrinter &Printer) { +static void symbolizeInput(const opt::InputArgList &Args, uint64_t AdjustVMA, + bool IsAddr2Line, DIPrinter::OutputStyle OutputStyle, + StringRef InputString, LLVMSymbolizer &Symbolizer, + DIPrinter &Printer) { Command Cmd; std::string ModuleName; uint64_t Offset = 0; - if (!parseCommand(IsAddr2Line, StringRef(InputString), Cmd, ModuleName, - Offset)) { + if (!parseCommand(Args.getLastArgValue(OPT_obj_EQ), IsAddr2Line, + StringRef(InputString), Cmd, ModuleName, Offset)) { outs() << InputString << "\n"; return; } - if (ClPrintAddress) { + if (Args.hasArg(OPT_addresses)) { outs() << "0x"; outs().write_hex(Offset); - StringRef Delimiter = ClPrettyPrint ? ": " : "\n"; + StringRef Delimiter = Args.hasArg(OPT_pretty_print) ? ": " : "\n"; outs() << Delimiter; } - Offset -= ClAdjustVMA; + Offset -= AdjustVMA; if (Cmd == Command::Data) { auto ResOrErr = Symbolizer.symbolizeData( ModuleName, {Offset, object::SectionedAddress::UndefSection}); @@ -261,13 +172,13 @@ if (ResOrErr->empty()) outs() << "??\n"; } - } else if (ClPrintInlining) { + } 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 (ClOutputStyle == DIPrinter::OutputStyle::GNU) { - // With ClPrintFunctions == FunctionNameKind::LinkageName (default) - // and ClUseSymbolTable == true (also default), Symbolizer.symbolizeCode() + } else if (OutputStyle == DIPrinter::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 @@ -280,7 +191,7 @@ ModuleName, {Offset, object::SectionedAddress::UndefSection}); Printer << (error(ResOrErr) ? DILineInfo() : ResOrErr.get()); } - if (ClOutputStyle == DIPrinter::OutputStyle::LLVM) + if (OutputStyle == DIPrinter::OutputStyle::LLVM) outs() << "\n"; } @@ -288,59 +199,108 @@ InitLLVM X(argc, argv); bool IsAddr2Line = sys::path::stem(argv[0]).contains("addr2line"); + SmallVector NewArgv; + BumpPtrAllocator A; + StringSaver Saver(A); + auto Tokenize = Triple(sys::getProcessTriple()).isOSWindows() + ? cl::TokenizeWindowsCommandLine + : cl::TokenizeGNUCommandLine; + // The environment variable specifies initial options. + const char *Env = + IsAddr2Line ? "LLVM_ADDR2LINE_OPTS" : "LLVM_SYMBOLIZER_OPTS"; + if (llvm::Optional EnvValue = sys::Process::GetEnv(Env)) + Tokenize(*EnvValue, Saver, NewArgv, /*MarkEOLs=*/false); + // Command line options can override the environment variable. + NewArgv.append(argv + 1, argv + argc); + cl::ExpandResponseFiles(Saver, Tokenize, NewArgv); - if (IsAddr2Line) { - ClDemangle.setInitialValue(false); - ClPrintFunctions.setInitialValue(FunctionNameKind::None); - ClPrintInlining.setInitialValue(false); - ClUntagAddresses.setInitialValue(false); - ClOutputStyle.setInitialValue(DIPrinter::OutputStyle::GNU); + SymbolizerOptTable T; + unsigned MAI, MAC; + opt::InputArgList Args = T.ParseArgs(makeArrayRef(NewArgv), MAI, MAC); + if (Args.hasArg(OPT_help)) { + StringRef ToolName = IsAddr2Line ? "llvm-addr2line" : "llvm-symbolizer"; + T.PrintHelp(outs(), ToolName.str().c_str(), ToolName.str().c_str()); + return 0; } llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded); - cl::ParseCommandLineOptions( - argc, argv, IsAddr2Line ? "llvm-addr2line\n" : "llvm-symbolizer\n", - /*Errs=*/nullptr, - IsAddr2Line ? "LLVM_ADDR2LINE_OPTS" : "LLVM_SYMBOLIZER_OPTS"); - - // If both --demangle and --no-demangle are specified then pick the last one. - if (ClNoDemangle.getPosition() > ClDemangle.getPosition()) - ClDemangle = !ClNoDemangle; + // cl::ParseCommandLineOptions( + // argc, argv, IsAddr2Line ? "llvm-addr2line\n" : "llvm-symbolizer\n", + // /*Errs=*/nullptr, + // IsAddr2Line ? "LLVM_ADDR2LINE_OPTS" : "LLVM_SYMBOLIZER_OPTS"); LLVMSymbolizer::Options Opts; - Opts.PrintFunctions = ClPrintFunctions; - Opts.UseSymbolTable = ClUseSymbolTable; - Opts.Demangle = ClDemangle; - Opts.RelativeAddresses = ClUseRelativeAddress; - Opts.UntagAddresses = ClUntagAddresses; - Opts.DefaultArch = ClDefaultArch; - Opts.FallbackDebugPath = ClFallbackDebugPath; - Opts.DWPName = ClDwpName; - Opts.DebugFileDirectory = ClDebugFileDirectory; - Opts.UseNativePDBReader = ClUseNativePDBReader; - Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath; - // If both --basenames and --relativenames are specified then pick the last - // one. - if (ClBasenames.getPosition() > ClRelativenames.getPosition()) - Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::BaseNameOnly; - else if (ClRelativenames) - Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath; + uint64_t AdjustVMA = 0; + unsigned SourceContextLines = 0; + if (auto *A = Args.getLastArg(OPT_adjust_vma_EQ)) { + StringRef V(A->getValue()); + if (!llvm::to_integer(V, AdjustVMA, 0)) { + errs() << A->getSpelling() + + ": expected a non-negative integer, but got '" + V + "'"; + return 1; + } + } + if (const opt::Arg *A = Args.getLastArg(OPT_basenames, OPT_relativenames)) { + Opts.PathStyle = + A->getOption().matches(OPT_basenames) + ? DILineInfoSpecifier::FileLineInfoKind::BaseNameOnly + : DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath; + } else { + Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath; + } + Opts.DebugFileDirectory = Args.getAllArgValues(OPT_debug_file_directory_EQ); + Opts.DefaultArch = Args.getLastArgValue(OPT_default_arch_EQ).str(); + Opts.Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, !IsAddr2Line); + Opts.DWPName = Args.getLastArgValue(OPT_dwp_EQ).str(); + Opts.FallbackDebugPath = + Args.getLastArgValue(OPT_fallback_debug_path_EQ).str(); + if (Args.hasArg(OPT_functions)) { + Opts.PrintFunctions = FunctionNameKind::LinkageName; + } else if (const opt::Arg *A = Args.getLastArg(OPT_functions_EQ)) { + Opts.PrintFunctions = StringSwitch(A->getValue()) + .Case("none", FunctionNameKind::None) + .Case("short", FunctionNameKind::ShortName) + .Default(FunctionNameKind::LinkageName); + } + if (auto *A = Args.getLastArg(OPT_print_source_context_lines_EQ)) { + StringRef V(A->getValue()); + if (!llvm::to_integer(V, SourceContextLines, 0)) { + errs() << A->getSpelling() + + ": expected a non-negative integer, but got '" + V + "'"; + return 1; + } + } + Opts.RelativeAddresses = Args.hasArg(OPT_relative_address); + Opts.UntagAddresses = + Args.hasFlag(OPT_untag_addresses, OPT_no_untag_addresses, !IsAddr2Line); + Opts.UseNativePDBReader = Args.hasArg(OPT_use_native_pdb_reader); + Opts.UseSymbolTable = true; - for (const auto &hint : ClDsymHint) { - if (sys::path::extension(hint) == ".dSYM") { - Opts.DsymHints.push_back(hint); + for (auto *A : Args.filtered(OPT_dsym_hint_EQ)) { + StringRef Hint(A->getValue()); + if (sys::path::extension(Hint) == ".dSYM") { + Opts.DsymHints.emplace_back(Hint); } else { - errs() << "Warning: invalid dSYM hint: \"" << hint << - "\" (must have the '.dSYM' extension).\n"; + errs() << "Warning: invalid dSYM hint: \"" << Hint + << "\" (must have the '.dSYM' extension).\n"; } } + + auto OutputStyle = + IsAddr2Line ? DIPrinter::OutputStyle::GNU : DIPrinter::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; + } + LLVMSymbolizer Symbolizer(Opts); + DIPrinter Printer(outs(), Opts.PrintFunctions != FunctionNameKind::None, + Args.hasArg(OPT_pretty_print), SourceContextLines, + Args.hasArg(OPT_verbose), OutputStyle); - DIPrinter Printer(outs(), ClPrintFunctions != FunctionNameKind::None, - ClPrettyPrint, ClPrintSourceContextLines, ClVerbose, - ClOutputStyle); - - if (ClInputAddresses.empty()) { + std::vector InputAddresses = Args.getAllArgValues(OPT_INPUT); + if (InputAddresses.empty()) { const int kMaxInputStringLength = 1024; char InputString[kMaxInputStringLength]; @@ -351,12 +311,14 @@ std::remove_if(StrippedInputString.begin(), StrippedInputString.end(), [](char c) { return c == '\r' || c == '\n'; }), StrippedInputString.end()); - symbolizeInput(IsAddr2Line, StrippedInputString, Symbolizer, Printer); + symbolizeInput(Args, AdjustVMA, IsAddr2Line, OutputStyle, + StrippedInputString, Symbolizer, Printer); outs().flush(); } } else { - for (StringRef Address : ClInputAddresses) - symbolizeInput(IsAddr2Line, Address, Symbolizer, Printer); + for (StringRef Address : InputAddresses) + symbolizeInput(Args, AdjustVMA, IsAddr2Line, OutputStyle, Address, + Symbolizer, Printer); } return 0;