Skip to content

Commit 593e196

Browse files
committed
[llvm-symbolizer] Switch command line parsing from llvm::cl to OptTable
for the advantage outlined by D83639 ([OptTable] Support grouped short options) Some behavior changes: * -i={0,false} is removed. Use --no-inlines instead. * --demangle={0,false} is removed. Use --no-demangle instead * -untag-addresses={0,false} is removed. Use --no-untag-addresses instead Added a higher level API OptTable::parseArgs which handles optional initial options populated from an environment variable, expands response files recursively, and parses options. Reviewed By: jhenderson Differential Revision: https://reviews.llvm.org/D83530
1 parent 4a04bc8 commit 593e196

File tree

15 files changed

+339
-217
lines changed

15 files changed

+339
-217
lines changed

llvm/docs/CommandGuide/llvm-symbolizer.rst

+8-8
Original file line numberDiff line numberDiff line change
@@ -220,16 +220,16 @@ OPTIONS
220220

221221
Show help and usage for this command.
222222

223-
.. option:: --help-list
224-
225-
Show help and usage for this command without grouping the options into categories.
226-
227223
.. _llvm-symbolizer-opt-i:
228224

229225
.. option:: --inlining, --inlines, -i
230226

231227
If a source code location is in an inlined function, prints all the inlined
232-
frames. Defaults to true.
228+
frames. This is the default.
229+
230+
.. option:: --no-inlines
231+
232+
Don't print inlined frames.
233233

234234
.. option:: --no-demangle
235235

@@ -267,17 +267,17 @@ OPTIONS
267267
268268
foo() at /tmp/test.cpp:6:3
269269
270-
$ llvm-symbolizer --output-style=LLVM --obj=inlined.elf 0x4004be 0x400486 -p -i=0
270+
$ llvm-symbolizer --output-style=LLVM --obj=inlined.elf 0x4004be 0x400486 -p --no-inlines
271271
main at /tmp/test.cpp:11:18
272272
273273
foo() at /tmp/test.cpp:6:3
274274
275-
$ llvm-symbolizer --output-style=GNU --obj=inlined.elf 0x4004be 0x400486 -p -i=0
275+
$ llvm-symbolizer --output-style=GNU --obj=inlined.elf 0x4004be 0x400486 -p --no-inlines
276276
baz() at /tmp/test.cpp:11
277277
foo() at /tmp/test.cpp:6
278278
279279
$ clang -g -fdebug-info-for-profiling test.cpp -o profiling.elf
280-
$ llvm-symbolizer --output-style=GNU --obj=profiling.elf 0x401167 -p -i=0
280+
$ llvm-symbolizer --output-style=GNU --obj=profiling.elf 0x401167 -p --no-inlines
281281
main at /tmp/test.cpp:15 (discriminator 2)
282282
283283
.. option:: --pretty-print, -p

llvm/include/llvm/Option/OptTable.h

+18
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313
#include "llvm/ADT/StringRef.h"
1414
#include "llvm/ADT/StringSet.h"
1515
#include "llvm/Option/OptSpecifier.h"
16+
#include "llvm/Support/StringSaver.h"
1617
#include <cassert>
1718
#include <string>
1819
#include <vector>
1920

2021
namespace llvm {
2122

2223
class raw_ostream;
24+
template <typename Fn> class function_ref;
2325

2426
namespace opt {
2527

@@ -60,6 +62,7 @@ class OptTable {
6062
std::vector<Info> OptionInfos;
6163
bool IgnoreCase;
6264
bool GroupedShortOptions = false;
65+
const char *EnvVar = nullptr;
6366

6467
unsigned TheInputOptionID = 0;
6568
unsigned TheUnknownOptionID = 0;
@@ -123,6 +126,9 @@ class OptTable {
123126
return getInfo(id).MetaVar;
124127
}
125128

129+
/// Specify the environment variable where initial options should be read.
130+
void setInitialOptionsFromEnvironment(const char *E) { EnvVar = E; }
131+
126132
/// Support grouped short options. e.g. -ab represents -a -b.
127133
void setGroupedShortOptions(bool Value) { GroupedShortOptions = Value; }
128134

@@ -219,6 +225,18 @@ class OptTable {
219225
unsigned &MissingArgCount, unsigned FlagsToInclude = 0,
220226
unsigned FlagsToExclude = 0) const;
221227

228+
/// A convenience helper which handles optional initial options populated from
229+
/// an environment variable, expands response files recursively and parses
230+
/// options.
231+
///
232+
/// \param ErrorFn - Called on a formatted error message for missing arguments
233+
/// or unknown options.
234+
/// \return An InputArgList; on error this will contain all the options which
235+
/// could be parsed.
236+
InputArgList parseArgs(int Argc, char *const *Argv, OptSpecifier Unknown,
237+
StringSaver &Saver,
238+
function_ref<void(StringRef)> ErrorFn) const;
239+
222240
/// Render the help text for an option table.
223241
///
224242
/// \param OS - The stream to write the help text to.

llvm/include/llvm/Support/CommandLine.h

+8
Original file line numberDiff line numberDiff line change
@@ -2085,6 +2085,14 @@ bool ExpandResponseFiles(
20852085
llvm::vfs::FileSystem &FS = *llvm::vfs::getRealFileSystem(),
20862086
llvm::Optional<llvm::StringRef> CurrentDir = llvm::None);
20872087

2088+
/// A convenience helper which concatenates the options specified by the
2089+
/// environment variable EnvVar and command line options, then expands response
2090+
/// files recursively. The tokenizer is a predefined GNU or Windows one.
2091+
/// \return true if all @files were expanded successfully or there were none.
2092+
bool expandResponseFiles(int Argc, const char *const *Argv, const char *EnvVar,
2093+
StringSaver &Saver,
2094+
SmallVectorImpl<const char *> &NewArgv);
2095+
20882096
/// Mark all options not part of this category as cl::ReallyHidden.
20892097
///
20902098
/// \param Category the category of options to keep displaying

llvm/lib/Option/OptTable.cpp

+30-2
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
#include "llvm/Option/OptTable.h"
910
#include "llvm/ADT/STLExtras.h"
1011
#include "llvm/ADT/StringRef.h"
1112
#include "llvm/ADT/StringSet.h"
1213
#include "llvm/Option/Arg.h"
1314
#include "llvm/Option/ArgList.h"
14-
#include "llvm/Option/Option.h"
1515
#include "llvm/Option/OptSpecifier.h"
16-
#include "llvm/Option/OptTable.h"
16+
#include "llvm/Option/Option.h"
17+
#include "llvm/Support/CommandLine.h" // for expandResponseFiles
1718
#include "llvm/Support/Compiler.h"
1819
#include "llvm/Support/ErrorHandling.h"
1920
#include "llvm/Support/raw_ostream.h"
@@ -490,6 +491,33 @@ InputArgList OptTable::ParseArgs(ArrayRef<const char *> ArgArr,
490491
return Args;
491492
}
492493

494+
InputArgList OptTable::parseArgs(int Argc, char *const *Argv,
495+
OptSpecifier Unknown, StringSaver &Saver,
496+
function_ref<void(StringRef)> ErrorFn) const {
497+
SmallVector<const char *, 0> NewArgv;
498+
// The environment variable specifies initial options which can be overridden
499+
// by commnad line options.
500+
cl::expandResponseFiles(Argc, Argv, EnvVar, Saver, NewArgv);
501+
502+
unsigned MAI, MAC;
503+
opt::InputArgList Args = ParseArgs(makeArrayRef(NewArgv), MAI, MAC);
504+
if (MAC)
505+
ErrorFn((Twine(Args.getArgString(MAI)) + ": missing argument").str());
506+
507+
// For each unknwon option, call ErrorFn with a formatted error message. The
508+
// message includes a suggested alternative option spelling if available.
509+
std::string Nearest;
510+
for (const opt::Arg *A : Args.filtered(Unknown)) {
511+
std::string Spelling = A->getAsString(Args);
512+
if (findNearest(Spelling, Nearest) > 1)
513+
ErrorFn("unknown argument '" + A->getAsString(Args) + "'");
514+
else
515+
ErrorFn("unknown argument '" + A->getAsString(Args) +
516+
"', did you mean '" + Nearest + "'?");
517+
}
518+
return Args;
519+
}
520+
493521
static std::string getOptionHelpName(const OptTable &Opts, OptSpecifier Id) {
494522
const Option O = Opts.getOption(Id);
495523
std::string Name = O.getPrefixedName();

llvm/lib/Support/CommandLine.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,22 @@ bool cl::ExpandResponseFiles(StringSaver &Saver, TokenizerCallback Tokenizer,
12511251
return AllExpanded;
12521252
}
12531253

1254+
bool cl::expandResponseFiles(int Argc, const char *const *Argv,
1255+
const char *EnvVar, StringSaver &Saver,
1256+
SmallVectorImpl<const char *> &NewArgv) {
1257+
auto Tokenize = Triple(sys::getProcessTriple()).isOSWindows()
1258+
? cl::TokenizeWindowsCommandLine
1259+
: cl::TokenizeGNUCommandLine;
1260+
// The environment variable specifies initial options.
1261+
if (EnvVar)
1262+
if (llvm::Optional<std::string> EnvValue = sys::Process::GetEnv(EnvVar))
1263+
Tokenize(*EnvValue, Saver, NewArgv, /*MarkEOLs=*/false);
1264+
1265+
// Command line options can override the environment variable.
1266+
NewArgv.append(Argv + 1, Argv + Argc);
1267+
return ExpandResponseFiles(Saver, Tokenize, NewArgv);
1268+
}
1269+
12541270
bool cl::readConfigFile(StringRef CfgFile, StringSaver &Saver,
12551271
SmallVectorImpl<const char *> &Argv) {
12561272
SmallString<128> AbsPath;

llvm/test/DebugInfo/debuglineinfo-path.ll

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
; RUN: llvm-nm --radix=o %t | grep posix_absolute_func > %t.posix_absolute_func
99
; RUN: llvm-nm --radix=o %t | grep posix_relative_func > %t.posix_relative_func
1010
; RUN: llvm-nm --radix=o %t | grep win_func > %t.win_func
11-
; RUN: llvm-symbolizer --functions=linkage --inlining --demangle=false --obj %t < %t.posix_absolute_func | FileCheck %s --check-prefix=POSIX_A
12-
; RUN: llvm-symbolizer --functions=linkage --inlining --demangle=false --obj %t < %t.posix_relative_func | FileCheck %s --check-prefix=POSIX_R
13-
; RUN: llvm-symbolizer --functions=linkage --inlining --demangle=false --obj %t < %t.win_func | FileCheck %s --check-prefix=WIN
11+
; RUN: llvm-symbolizer --functions=linkage --inlining --no-demangle --obj %t < %t.posix_absolute_func | FileCheck %s --check-prefix=POSIX_A
12+
; RUN: llvm-symbolizer --functions=linkage --inlining --no-demangle --obj %t < %t.posix_relative_func | FileCheck %s --check-prefix=POSIX_R
13+
; RUN: llvm-symbolizer --functions=linkage --inlining --no-demangle --obj %t < %t.win_func | FileCheck %s --check-prefix=WIN
1414

1515
;POSIX_A: posix_absolute_func
1616
;POSIX_A: /absolute/posix/path{{[\/]}}posix.c

llvm/test/tools/llvm-symbolizer/basic.s

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ foo:
1717

1818
# Check --obj aliases --exe, -e
1919
# RUN: llvm-symbolizer 0xa 0xb --exe=%t.o | FileCheck %s
20+
# RUN: llvm-symbolizer 0xa 0xb --exe %t.o | FileCheck %s
2021
# RUN: llvm-symbolizer 0xa 0xb -e %t.o | FileCheck %s
2122
# RUN: llvm-symbolizer 0xa 0xb -e=%t.o | FileCheck %s
2223
# RUN: llvm-symbolizer 0xa 0xb -e%t.o | FileCheck %s

llvm/test/tools/llvm-symbolizer/help.test

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ RUN: llvm-addr2line -h | FileCheck %s --check-prefix=ADDR2LINE
44
RUN: llvm-addr2line --help | FileCheck %s --check-prefix=ADDR2LINE
55

66
SYMBOLIZER: OVERVIEW: llvm-symbolizer
7-
SYMBOLIZER: USAGE: llvm-symbolizer{{(.exe)?}} [options] <input addresses>...
7+
SYMBOLIZER: USAGE: llvm-symbolizer{{(.exe)?}} [options] addresses...
88
SYMBOLIZER: @FILE
99

1010
ADDR2LINE: OVERVIEW: llvm-addr2line
11-
ADDR2LINE: USAGE: llvm-addr2line{{(.exe)?}} [options] <input addresses>...
11+
ADDR2LINE: USAGE: llvm-addr2line{{(.exe)?}} [options] addresses...
1212
ADDR2LINE: @FILE

llvm/test/tools/llvm-symbolizer/output-style-inlined.test

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
This test checks that when inlined frames are not shown (-i=0) and the output
1+
This test checks that when inlined frames are not shown (--no-inlines) and the output
22
style is set to GNU (--output-style=GNU) the name of an inlined function is not
33
replaced with the name of the top caller function. At the same time, the current
44
behavior of llvm-symbolizer is preserved with --output-style=LLVM or when
55
the option is not specified.
66

7-
RUN: llvm-symbolizer -i=0 -e %p/Inputs/addr.exe 0x40054d \
7+
RUN: llvm-symbolizer --no-inlines -e %p/Inputs/addr.exe 0x40054d \
88
RUN: | FileCheck %s --check-prefix=LLVM --implicit-check-not=inctwo
99

10-
RUN: llvm-symbolizer --output-style=LLVM -i=0 -e %p/Inputs/addr.exe 0x40054d \
10+
RUN: llvm-symbolizer --output-style=LLVM --no-inlines -e %p/Inputs/addr.exe 0x40054d \
1111
RUN: | FileCheck %s --check-prefix=LLVM --implicit-check-not=inctwo
1212

13-
RUN: llvm-symbolizer --output-style=GNU -i=0 -e %p/Inputs/addr.exe 0x40054d \
13+
RUN: llvm-symbolizer --output-style=GNU --no-inlines -e %p/Inputs/addr.exe 0x40054d \
1414
RUN: | FileCheck %s --check-prefix=GNU --implicit-check-not=main
1515

1616
RUN: llvm-addr2line -f -e %p/Inputs/addr.exe 0x40054d \

llvm/test/tools/llvm-symbolizer/split-dwarf.test

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,22 @@ RUN: mkdir -p %t
44
RUN: cp %p/Inputs/split-dwarf-test.dwo %t
55

66
RUN: cd %t
7-
RUN: llvm-symbolizer --functions=linkage --inlining --demangle=false \
7+
RUN: llvm-symbolizer --functions=linkage --inlining --no-demangle \
88
RUN: --obj=%p/Inputs/split-dwarf-test 0x400504 0x4004f4 | FileCheck --check-prefixes=SPLIT,DWO %s
99

1010
Ensure we get the same results in the absence of gmlt-like data in the executable but the presence of a .dwo file
1111

1212
RUN: echo "%p/Inputs/split-dwarf-test-nogmlt 0x400504" >> %t.input
1313
RUN: echo "%p/Inputs/split-dwarf-test-nogmlt 0x4004f4" >> %t.input
14-
RUN: llvm-symbolizer --functions=linkage --inlining --demangle=false \
14+
RUN: llvm-symbolizer --functions=linkage --inlining --no-demangle \
1515
RUN: --default-arch=i386 --obj=%p/Inputs/split-dwarf-test-nogmlt 0x400504 0x4004f4 | FileCheck --check-prefixes=SPLIT,DWO %s
1616

1717
Ensure we get gmlt like results in the absence of a .dwo file but the presence of gmlt-like data in the executable
1818

1919
RUN: rm %t/split-dwarf-test.dwo
2020
RUN: echo "%p/Inputs/split-dwarf-test 0x400504" >> %t.input
2121
RUN: echo "%p/Inputs/split-dwarf-test 0x4004f4" >> %t.input
22-
RUN: llvm-symbolizer --functions=linkage --inlining --demangle=false \
22+
RUN: llvm-symbolizer --functions=linkage --inlining --no-demangle \
2323
RUN: --default-arch=i386 --obj=%p/Inputs/split-dwarf-test 0x400504 0x4004f4 | FileCheck --check-prefixes=SPLIT,NODWO %s
2424

2525
DWO: _Z2f2v
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# RUN: not llvm-symbolizer -x --flag 2>&1 | FileCheck %s
2+
3+
# CHECK: error: unknown argument '-x'{{$}}
4+
# CHECK-NEXT: error: unknown argument '--flag'{{$}}
5+
6+
# RUN: not llvm-symbolizer --inline 2>&1 | FileCheck %s --check-prefix=SUGGEST
7+
8+
# SUGGEST: error: unknown argument '--inline', did you mean '--inlines'?
9+
10+
# RUN: not llvm-symbolizer -e 2>&1 | FileCheck %s --check-prefix=MISSING
11+
12+
# MISSING: error: -e: missing argument

llvm/test/tools/llvm-symbolizer/untag-addresses.test

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
44
# RUN: echo DATA %t.o 0 | llvm-symbolizer | FileCheck --check-prefix=UNTAG %s
5-
# RUN: echo DATA %t.o 0 | llvm-symbolizer -untag-addresses=0 | FileCheck --check-prefix=NOUNTAG %s
5+
# RUN: echo DATA %t.o 0 | llvm-symbolizer --no-untag-addresses | FileCheck --check-prefix=NOUNTAG %s
66
# RUN: echo DATA %t.o 0 | llvm-addr2line | FileCheck --check-prefix=NOUNTAG %s
77

88
# UNTAG: foo

llvm/tools/llvm-symbolizer/CMakeLists.txt

+7
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,24 @@
33
# This means that we need LLVM libraries to be compiled for these
44
# targets as well. Currently, there is no support for such a build strategy.
55

6+
set(LLVM_TARGET_DEFINITIONS Opts.td)
7+
tablegen(LLVM Opts.inc -gen-opt-parser-defs)
8+
add_public_tablegen_target(SymbolizerOptsTableGen)
9+
610
set(LLVM_LINK_COMPONENTS
711
DebugInfoDWARF
812
DebugInfoPDB
913
Demangle
1014
Object
15+
Option
1116
Support
1217
Symbolize
1318
)
1419

1520
add_llvm_tool(llvm-symbolizer
1621
llvm-symbolizer.cpp
22+
DEPENDS
23+
SymbolizerOptsTableGen
1724
)
1825

1926
add_llvm_tool_symlink(llvm-addr2line llvm-symbolizer)

llvm/tools/llvm-symbolizer/Opts.td

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
include "llvm/Option/OptParser.td"
2+
3+
multiclass B<string name, string help1, string help2> {
4+
def NAME: Flag<["--", "-"], name>, HelpText<help1>;
5+
def no_ # NAME: Flag<["--", "-"], "no-" # name>, HelpText<help2>;
6+
}
7+
8+
multiclass Eq<string name, string help> {
9+
def NAME #_EQ : Joined<["--", "-"], name #"=">,
10+
HelpText<help>;
11+
def : Separate<["--", "-"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
12+
}
13+
14+
class F<string name, string help>: Flag<["--", "-"], name>, HelpText<help>;
15+
16+
def addresses : F<"addresses", "Show address before line information">;
17+
defm adjust_vma
18+
: Eq<"adjust-vma", "Add specified offset to object file addresses">,
19+
MetaVarName<"<offset>">;
20+
def basenames : Flag<["--"], "basenames">, HelpText<"Strip directory names from paths">;
21+
defm debug_file_directory : Eq<"debug-file-directory", "Path to directory where to look for debug files">, MetaVarName<"<dir>">;
22+
defm default_arch : Eq<"default-arch", "Default architecture (for multi-arch objects)">;
23+
defm demangle : B<"demangle", "Demangle function names", "Don't demangle function names">;
24+
def functions : F<"functions", "Print function name for a given address">;
25+
def functions_EQ : Joined<["--"], "functions=">, HelpText<"Print function name for a given address">, Values<"none,short,linkage">;
26+
def help : F<"help", "Display this help">;
27+
defm dwp : Eq<"dwp", "Path to DWP file to be use for any split CUs">, MetaVarName<"<file>">;
28+
defm dsym_hint : Eq<"dsym-hint", "Path to .dSYM bundles to search for debug info for the object files">, MetaVarName<"<dir>">;
29+
defm fallback_debug_path : Eq<"fallback-debug-path", "Fallback path for debug binaries">, MetaVarName<"<dir>">;
30+
defm inlines : B<"inlines", "Print all inlined frames for a given address",
31+
"Do not print inlined frames">;
32+
defm obj
33+
: Eq<"obj", "Path to object file to be symbolized (if not provided, "
34+
"object file should be specified for each input line)">, MetaVarName<"<file>">;
35+
defm output_style
36+
: Eq<"output-style", "Specify print style. Supported styles: LLVM, GNU">,
37+
MetaVarName<"style">,
38+
Values<"LLVM,GNU">;
39+
def pretty_print : F<"pretty-print", "Make the output more human friendly">;
40+
defm print_source_context_lines : Eq<"print-source-context-lines", "Print N lines of source file context">;
41+
def relative_address : F<"relative-address", "Interpret addresses as addresses relative to the image base">;
42+
def relativenames : F<"relativenames", "Strip the compilation directory from paths">;
43+
defm untag_addresses : B<"untag-addresses", "", "Remove memory tags from addresses before symbolization">;
44+
def use_native_pdb_reader : F<"use-native-pdb-reader", "Use native PDB functionality">;
45+
def verbose : F<"verbose", "Print verbose line info">;
46+
47+
def : Flag<["-"], "a">, Alias<addresses>, HelpText<"Alias for --addresses">;
48+
def : F<"print-address", "Alias for --addresses">, Alias<addresses>;
49+
def : Flag<["-"], "C">, Alias<demangle>, HelpText<"Alias for --demangle">;
50+
def : Joined<["--"], "exe=">, Alias<obj_EQ>, HelpText<"Alias for --obj">, MetaVarName<"<file>">;
51+
def : Separate<["--"], "exe">, Alias<obj_EQ>, HelpText<"Alias for --obj">, MetaVarName<"<file>">;
52+
def : JoinedOrSeparate<["-"], "e">, Alias<obj_EQ>, HelpText<"Alias for --obj">, MetaVarName<"<file>">;
53+
def : Joined<["-"], "e=">, Alias<obj_EQ>, HelpText<"Alias for --obj">, MetaVarName<"<file>">;
54+
def : Flag<["-"], "f">, Alias<functions>, HelpText<"Alias for --functions">;
55+
def : Joined<["-"], "f=">, Alias<functions_EQ>, HelpText<"Alias for --functions=">;
56+
def : Flag<["-"], "h">, Alias<help>;
57+
def : Flag<["-"], "i">, Alias<inlines>, HelpText<"Alias for --inlines">;
58+
def : F<"inlining", "Alias for --inlines">, Alias<inlines>;
59+
def : Flag<["-"], "p">, Alias<pretty_print>, HelpText<"Alias for --pretty-print">;
60+
def : Flag<["-"], "s">, Alias<basenames>, HelpText<"Alias for --basenames">;

0 commit comments

Comments
 (0)