Hi,
eu-addr2line is mostly nicer to use than binutils addr2line IMHO, because it can work with symbol offsets, separate debuginfo, pids, core files, process maps, etc. But binutils addr2line has a couple of options that are also useful that prevented using eu-addr2line as a drop in replacement.
This patch set cleans up a few things and adds three new options.
[PATCH 1/5] addr2line: Split options in input and output format Is a simple cleanup of the --help output.
[PATCH 2/5] addr2line: Always parse addresses as hex numbers. Matches what binutils addr2line does, which IMHO makes more sense than trying to parse the input as octal or decimal if we aren't sure they are hex.
[PATCH 3/5] addr2line: Add -a, --address. Print address before for each entry. Is even more useful with our implementation since we accept symbol+offset as input. So this then returns what address that really translates to.
[PATCH 4/5] addr2line: Add demangler support. Implemented as was done for eu-stack and eu-nm earlier.
[PATCH 5/5] addr2line: Add --pretty-print option A more compact output format that is easier to parse.
A couple of testcases were added. All patches can also be found on the mjw/addr2line git branch.
Cheers,
Mark
This makes it clear that -j, --section=NAME is about how input is treated, not how the output is formatted.
Signed-off-by: Mark Wielaard mjw@redhat.com --- src/ChangeLog | 5 +++++ src/addr2line.c | 8 +++++--- 2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog index 02f6584..f4ff1fb 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,8 @@ +2015-05-20 Mark Wielaard mjw@redhat.com + + * addr2line (argp_option): Group 'section' under "Input format + options". + 2015-05-12 Mark Wielaard mjw@redhat.com
* strip.c (debug_fd): New static variable. diff --git a/src/addr2line.c b/src/addr2line.c index 281a91e..c7ff7f7 100644 --- a/src/addr2line.c +++ b/src/addr2line.c @@ -54,7 +54,11 @@ ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT; /* Definitions of arguments for argp functions. */ static const struct argp_option options[] = { - { NULL, 0, NULL, 0, N_("Output selection options:"), 2 }, + { NULL, 0, NULL, 0, N_("Input format options:"), 2 }, + { "section", 'j', "NAME", 0, + N_("Treat addresses as offsets relative to NAME section."), 0 }, + + { NULL, 0, NULL, 0, N_("Output format options:"), 3 }, { "basenames", 's', NULL, 0, N_("Show only base names of source files"), 0 }, { "absolute", 'A', NULL, 0, N_("Show absolute file names using compilation directory"), 0 }, @@ -62,8 +66,6 @@ static const struct argp_option options[] = { "symbols", 'S', NULL, 0, N_("Also show symbol or section names"), 0 }, { "symbols-sections", 'x', NULL, 0, N_("Also show symbol and the section names"), 0 }, { "flags", 'F', NULL, 0, N_("Also show line table flags"), 0 }, - { "section", 'j', "NAME", 0, - N_("Treat addresses as offsets relative to NAME section."), 0 }, { "inlines", 'i', NULL, 0, N_("Show all source locations that caused inline expansion of subroutines at the address."), 0 },
With a patch like this it's nice to post the new --help output so we can easily see the effect without trying the patch ourselves. But even going blind, this looks fine.
On Wed, 2015-05-20 at 11:18 -0700, Roland McGrath wrote:
With a patch like this it's nice to post the new --help output so we can easily see the effect without trying the patch ourselves. But even going blind, this looks fine.
Good point. Sorry for not posting. This is the full help text after the whole patch series:
Usage: addr2line [OPTION...] [ADDR...] Locate source files and line information for ADDRs (in a.out by default).
Input selection options: --core=COREFILE Find addresses from signatures found in COREFILE --debuginfo-path=PATH Search path for separate debuginfo files -e, --executable=FILE Find addresses in FILE -k, --kernel Find addresses in the running kernel -K, --offline-kernel[=RELEASE] Kernel with all modules -M, --linux-process-map=FILE Find addresses in files mapped as read from FILE in Linux /proc/PID/maps format -p, --pid=PID Find addresses in files mapped into process PID
Input format options: -j, --section=NAME Treat addresses as offsets relative to NAME section.
Output format options: -a, --addresses Print address before each entry -A, --absolute Show absolute file names using compilation directory -C, --demangle[=ARG] Show demangled symbols (ARG is always ignored) -f, --functions Also show function names -F, --flags Also show line table flags -i, --inlines Show all source locations that caused inline expansion of subroutines at the address. --pretty-print Print all information on one line, and indent inlines -s, --basenames Show only base names of source files -S, --symbols Also show symbol or section names -x, --symbols-sections Also show symbol and the section names
Miscellaneous:
-?, --help Give this help list --usage Give a short usage message -V, --version Print program version
Mandatory or optional arguments to long options are also mandatory or optional for any corresponding short options.
Report bugs to https://bugzilla.redhat.com/.
We would sometimes interpret input addresses as decimal or octal. That could be confusing and isn't what binutils addr2line does. Be consistent and always treat input addresses as hex.
Signed-off-by: Mark Wielaard mjw@redhat.com --- src/ChangeLog | 5 +++++ src/addr2line.c | 4 ++-- tests/ChangeLog | 4 ++++ tests/run-addrname-test.sh | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog index f4ff1fb..284b886 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,5 +1,10 @@ 2015-05-20 Mark Wielaard mjw@redhat.com
+ * addr2line.c (handle_address): Call strtoumax with base 16. Make + sure all input has been processed. + +2015-05-20 Mark Wielaard mjw@redhat.com + * addr2line (argp_option): Group 'section' under "Input format options".
diff --git a/src/addr2line.c b/src/addr2line.c index c7ff7f7..b1ff368 100644 --- a/src/addr2line.c +++ b/src/addr2line.c @@ -532,8 +532,8 @@ static int handle_address (const char *string, Dwfl *dwfl) { char *endp; - uintmax_t addr = strtoumax (string, &endp, 0); - if (endp == string) + uintmax_t addr = strtoumax (string, &endp, 16); + if (endp == string || *endp != '\0') { bool parsed = false; int i, j; diff --git a/tests/ChangeLog b/tests/ChangeLog index a899947..0e31012 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,7 @@ +2015-05-20 Mark Wielaard mjw@redhat.com + + * run-addrname-test.sh: Make sure all input addresses are hex. + 2015-05-04 Max Filippov jcmvbkbc@gmail.com
* backtrace-child.c (stdarg, main): Replace assert_perror with assert. diff --git a/tests/run-addrname-test.sh b/tests/run-addrname-test.sh index f954ee4..90e19df 100755 --- a/tests/run-addrname-test.sh +++ b/tests/run-addrname-test.sh @@ -277,7 +277,7 @@ EOF # local l0local2, 0 # offset 12 testfiles testfile64 -testrun_compare ${abs_top_builddir}/src/addr2line -S -e testfile64 1 4 5 8 9 12 <<\EOF +testrun_compare ${abs_top_builddir}/src/addr2line -S -e testfile64 1 4 5 8 9 c <<\EOF gglobal2 ??:0 g0global2
That looks fine but it should get a NEWS item for the incompatible UI change.
On Wed, 2015-05-20 at 11:18 -0700, Roland McGrath wrote:
That looks fine but it should get a NEWS item for the incompatible UI change.
Yeah, I guess technically it is an incompatible change. Though I think it was just a bug and nobody actually expected the addresses to be interpreted as octal or decimal. At least it was a surprise to me. I added the following to NEWS:
diff --git a/NEWS b/NEWS index 60aa995..8cc184a 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ Version 0.162
libdw: Install new header elfutils/known-dwarf.h.
+addr2line: Input addresses are now always interpreted as hexadecimal + numbers, never as octal or decimal numbers. + Version 0.161
libdw: New function dwarf_peel_type. dwarf_aggregate_size now uses
Adds test cases with sample output.
Signed-off-by: Mark Wielaard mjw@redhat.com --- NEWS | 2 ++ src/ChangeLog | 8 +++++++ src/addr2line.c | 38 ++++++++++++++++++++++++++++++- tests/ChangeLog | 5 ++++ tests/run-addr2line-i-test.sh | 53 +++++++++++++++++++++++++++++++++++++++++++ tests/run-addr2line-test.sh | 36 +++++++++++++++++++++++++++++ 6 files changed, 141 insertions(+), 1 deletion(-)
diff --git a/NEWS b/NEWS index 60aa995..6d81e59 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,8 @@ Version 0.162
libdw: Install new header elfutils/known-dwarf.h.
+addr2line: New option -a, --addresses to print address before each entry. + Version 0.161
libdw: New function dwarf_peel_type. dwarf_aggregate_size now uses diff --git a/src/ChangeLog b/src/ChangeLog index 284b886..127e601 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,5 +1,13 @@ 2015-05-20 Mark Wielaard mjw@redhat.com
+ * addr2line.c (argp_option): Add "addresses", 'a'. + (print_addresses): New static bool. + (parse_opt): Set print_addresses. + (get_addr_width): New static function. + (handle_address): Print address if print_addresses is true. + +2015-05-20 Mark Wielaard mjw@redhat.com + * addr2line.c (handle_address): Call strtoumax with base 16. Make sure all input has been processed.
diff --git a/src/addr2line.c b/src/addr2line.c index b1ff368..4416c7a 100644 --- a/src/addr2line.c +++ b/src/addr2line.c @@ -1,5 +1,5 @@ /* Locate source files and line information for given addresses - Copyright (C) 2005-2010, 2012, 2013 Red Hat, Inc. + Copyright (C) 2005-2010, 2012, 2013, 2015 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper drepper@redhat.com, 2005.
@@ -59,6 +59,7 @@ static const struct argp_option options[] = N_("Treat addresses as offsets relative to NAME section."), 0 },
{ NULL, 0, NULL, 0, N_("Output format options:"), 3 }, + { "addresses", 'a', NULL, 0, N_("Print address before each entry"), 0 }, { "basenames", 's', NULL, 0, N_("Show only base names of source files"), 0 }, { "absolute", 'A', NULL, 0, N_("Show absolute file names using compilation directory"), 0 }, @@ -100,6 +101,8 @@ static const struct argp argp = /* Handle ADDR. */ static int handle_address (const char *addr, Dwfl *dwfl);
+/* True when we should print the address for each entry. */ +static bool print_addresses;
/* True if only base names of files should be shown. */ static bool only_basenames; @@ -210,6 +213,10 @@ parse_opt (int key, char *arg, struct argp_state *state) state->child_inputs[0] = state->input; break;
+ case 'a': + print_addresses = true; + break; + case 'b': case 'C': case OPT_DEMANGLER: @@ -529,6 +536,29 @@ print_src (const char *src, int lineno, int linecol, Dwarf_Die *cu) }
static int +get_addr_width (Dwfl_Module *mod) +{ + // Try to find the address wide if possible. + static int width = 0; + if (width == 0 && mod) + { + Dwarf_Addr bias; + Elf *elf = dwfl_module_getelf (mod, &bias); + if (elf) + { + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); + if (ehdr) + width = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16; + } + } + if (width == 0) + width = 16; + + return width; +} + +static int handle_address (const char *string, Dwfl *dwfl) { char *endp; @@ -582,6 +612,12 @@ handle_address (const char *string, Dwfl *dwfl)
Dwfl_Module *mod = dwfl_addrmodule (dwfl, addr);
+ if (print_addresses) + { + int width = get_addr_width (mod); + printf ("0x%0*" PRIx64 "\n", width, addr); + } + if (show_functions) { /* First determine the function name. Use the DWARF information if diff --git a/tests/ChangeLog b/tests/ChangeLog index 0e31012..d3a0e4b 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,5 +1,10 @@ 2015-05-20 Mark Wielaard mjw@redhat.com
+ * run-addr2line-test.sh: Add -a test variants. + * run-addr2line-i-test.sh: Likewise. + +2015-05-20 Mark Wielaard mjw@redhat.com + * run-addrname-test.sh: Make sure all input addresses are hex.
2015-05-04 Max Filippov jcmvbkbc@gmail.com diff --git a/tests/run-addr2line-i-test.sh b/tests/run-addr2line-i-test.sh index 183916c..e62aa20 100755 --- a/tests/run-addr2line-i-test.sh +++ b/tests/run-addr2line-i-test.sh @@ -144,4 +144,57 @@ _Z2fuv /tmp/x.cpp:33 EOF
+# All together now (plus function names plus addresses). +testrun_compare ${abs_top_builddir}/src/addr2line -a -f -i -e testfile-inlines 0x00000000000005a0 0x00000000000005a1 0x00000000000005b0 0x00000000000005b1 0x00000000000005c0 0x00000000000005d0 0x00000000000005e0 0x00000000000005e1 0x00000000000005f0 0x00000000000005f1 0x00000000000005f2 <<\EOF +0x00000000000005a0 +foobar +/tmp/x.cpp:5 +0x00000000000005a1 +foobar +/tmp/x.cpp:6 +0x00000000000005b0 +fubar +/tmp/x.cpp:10 +0x00000000000005b1 +fubar +/tmp/x.cpp:11 +0x00000000000005c0 +foobar inlined at /tmp/x.cpp:15 in _Z3barv +/tmp/x.cpp:5 +bar +/tmp/x.cpp:15 +0x00000000000005d0 +fubar inlined at /tmp/x.cpp:20 in _Z3bazv +/tmp/x.cpp:10 +baz +/tmp/x.cpp:20 +0x00000000000005e0 +foobar inlined at /tmp/x.cpp:15 in _Z3foov +/tmp/x.cpp:5 +bar +/tmp/x.cpp:15 +_Z3foov +/tmp/x.cpp:25 +0x00000000000005e1 +fubar inlined at /tmp/x.cpp:20 in _Z3foov +/tmp/x.cpp:10 +baz +/tmp/x.cpp:20 +_Z3foov +/tmp/x.cpp:26 +0x00000000000005f0 +_Z2fuv +/tmp/x.cpp:31 +0x00000000000005f1 +fubar inlined at /tmp/x.cpp:32 in _Z2fuv +/tmp/x.cpp:10 +_Z2fuv +/tmp/x.cpp:32 +0x00000000000005f2 +foobar inlined at /tmp/x.cpp:33 in _Z2fuv +/tmp/x.cpp:5 +_Z2fuv +/tmp/x.cpp:33 +EOF + exit 0 diff --git a/tests/run-addr2line-test.sh b/tests/run-addr2line-test.sh index 768006b..8d06064 100755 --- a/tests/run-addr2line-test.sh +++ b/tests/run-addr2line-test.sh @@ -71,4 +71,40 @@ echo "# stdin without newline symbol, just EOF." echo -n "foo" | testrun ${abs_top_builddir}/src/addr2line -f -e testfile > stdin.nl.out || exit 1 cmp foo.out stdin.nonl.out || exit 1
+tempfiles good.addr.out + +cat > good.addr.out <<\EOF +0x08048468 +foo +/home/drepper/gnu/new-bu/build/ttt/f.c:3 +0x0804845c +bar +/home/drepper/gnu/new-bu/build/ttt/b.c:4 +0x08048468 +foo +/home/drepper/gnu/new-bu/build/ttt/f.c:3 +0x0804845c +bar +/home/drepper/gnu/new-bu/build/ttt/b.c:4 +0x08048468 +foo +/home/drepper/gnu/new-bu/build/ttt/f.c:3 +0x0804845c +bar +/home/drepper/gnu/new-bu/build/ttt/b.c:4 +0x08048468 +foo +/home/drepper/gnu/new-bu/build/ttt/f.c:3 +0x0804845c +bar +/home/drepper/gnu/new-bu/build/ttt/b.c:4 +EOF + +echo "# Everything on the command line with addresses" +cat good.addr.out | testrun_compare ${abs_top_builddir}/src/addr2line -a -f -e testfile 0x08048468 0x0804845c foo bar foo+0x0 bar+0x0 foo-0x0 bar-0x0 + +echo "# Everything from stdin (with newlines) with addresses." +cat stdin.nl | testrun ${abs_top_builddir}/src/addr2line -a -f -e testfile > stdin.nl.out || exit 1 +cmp good.addr.out stdin.nl.out || exit 1 + exit 0
static int +get_addr_width (Dwfl_Module *mod) +{
- // Try to find the address wide if possible.
s/wide/width/
- if (width == 0 && mod)
mod != NULL please, and other cases like that below.
- if (print_addresses)
- {
int width = get_addr_width (mod);
printf ("0x%0*" PRIx64 "\n", width, addr);
- }
I tend to prefer %.* though it has the same effect for hex. I also tend to prefer %# to 0x%. IIRC, that includes the 0x in the width but not in the precision, so "%#.*" PRIx64 is probably best. The only real difference is 0x0 vs 0, but for some reason I really hate seeing "0x%".
On Wed, 2015-05-20 at 11:22 -0700, Roland McGrath wrote:
static int +get_addr_width (Dwfl_Module *mod) +{
- // Try to find the address wide if possible.
s/wide/width/
Fixed. One day I will learn this funky English language...
- if (width == 0 && mod)
mod != NULL please, and other cases like that below.
Fixed 3 times.
- if (print_addresses)
- {
int width = get_addr_width (mod);
printf ("0x%0*" PRIx64 "\n", width, addr);
- }
I tend to prefer %.* though it has the same effect for hex. I also tend to prefer %# to 0x%. IIRC, that includes the 0x in the width but not in the precision, so "%#.*" PRIx64 is probably best. The only real difference is 0x0 vs 0, but for some reason I really hate seeing "0x%".
Changed to use %.* but kept the 0x sorry. The reason is indeed the different treatment of 0. With %# that is just inconsistent. You could write printf ("%s%#0*" PRIx64 "\n", addr == 0 ? "0x" : "", width, addr); but that seems silly.
Cheers,
Mark
Makes the -C, --demangle option visible and implements it (ignoring the demangle style argument). Adds a new test with sample output.
Signed-off-by: Mark Wielaard mjw@redhat.com --- NEWS | 1 + src/ChangeLog | 15 ++++++++ src/Makefile.am | 2 +- src/addr2line.c | 48 ++++++++++++++++++++--- tests/ChangeLog | 6 +++ tests/Makefile.am | 2 + tests/run-addr2line-i-demangle-test.sh | 69 ++++++++++++++++++++++++++++++++++ 7 files changed, 136 insertions(+), 7 deletions(-) create mode 100755 tests/run-addr2line-i-demangle-test.sh
diff --git a/NEWS b/NEWS index 6d81e59..46ff872 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,7 @@ Version 0.162 libdw: Install new header elfutils/known-dwarf.h.
addr2line: New option -a, --addresses to print address before each entry. + New option -C, --demangle to show demangled symbols.
Version 0.161
diff --git a/src/ChangeLog b/src/ChangeLog index 127e601..fe6f6f1 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,5 +1,20 @@ 2015-05-20 Mark Wielaard mjw@redhat.com
+ * Makefile.am (addr2line_LDADD): Add demanglelib. + * addr2line.c (argp_option): Move demangle under output format. + (demangle): New static bool. + (demangle_buffer_len): New static size_t. + (demangle_buffer): New static char *. + (main): free demangle_buffer. + (parse_opt): Set demangle. + (symname): New static function. + (get_diename): Use symname. + (print_dwarf_function): Likewise. + (print_addrsym): Likewise. + (handle_address): Likewise. + +2015-05-20 Mark Wielaard mjw@redhat.com + * addr2line.c (argp_option): Add "addresses", 'a'. (print_addresses): New static bool. (parse_opt): Set print_addresses. diff --git a/src/Makefile.am b/src/Makefile.am index ab5364f..58cbe76 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -102,7 +102,7 @@ endif ld_LDFLAGS = -rdynamic elflint_LDADD = $(libebl) $(libelf) $(libeu) $(argp_LDADD) -ldl findtextrel_LDADD = $(libdw) $(libelf) $(argp_LDADD) -addr2line_LDADD = $(libdw) $(libelf) $(argp_LDADD) +addr2line_LDADD = $(libdw) $(libelf) $(argp_LDADD) $(demanglelib) elfcmp_LDADD = $(libebl) $(libelf) $(argp_LDADD) -ldl objdump_LDADD = $(libasm) $(libebl) $(libelf) $(libeu) $(argp_LDADD) -ldl ranlib_LDADD = libar.a $(libelf) $(libeu) $(argp_LDADD) diff --git a/src/addr2line.c b/src/addr2line.c index 4416c7a..3f64011 100644 --- a/src/addr2line.c +++ b/src/addr2line.c @@ -70,11 +70,12 @@ static const struct argp_option options[] = { "inlines", 'i', NULL, 0, N_("Show all source locations that caused inline expansion of subroutines at the address."), 0 }, + { "demangle", 'C', "ARG", OPTION_ARG_OPTIONAL, + N_("Show demangled symbols (ARG is always ignored)"), 0 },
{ NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 }, /* Unsupported options. */ { "target", 'b', "ARG", OPTION_HIDDEN, NULL, 0 }, - { "demangle", 'C', "ARG", OPTION_HIDDEN | OPTION_ARG_OPTIONAL, NULL, 0 }, { "demangler", OPT_DEMANGLER, "ARG", OPTION_HIDDEN, NULL, 0 }, { NULL, 0, NULL, 0, NULL, 0 } }; @@ -128,6 +129,13 @@ static const char *just_section; /* True if all inlined subroutines of the current address should be shown. */ static bool show_inlines;
+/* True if all names need to be demangled. */ +static bool demangle; + +#ifdef USE_DEMANGLE +static size_t demangle_buffer_len = 0; +static char *demangle_buffer = NULL; +#endif
int main (int argc, char *argv[]) @@ -185,6 +193,11 @@ main (int argc, char *argv[]) }
dwfl_end (dwfl); + +#ifdef USE_DEMANGLE + free (demangle_buffer); +#endif + return result; }
@@ -220,7 +233,7 @@ parse_opt (int key, char *arg, struct argp_state *state) case 'b': case 'C': case OPT_DEMANGLER: - /* Ignored for compatibility. */ + demangle = true; break;
case 's': @@ -262,6 +275,22 @@ parse_opt (int key, char *arg, struct argp_state *state) return 0; }
+static const char * +symname (const char *name) +{ +#ifdef USE_DEMANGLE + // Require GNU v3 ABI by the "_Z" prefix. + if (demangle && name[0] == '_' && name[1] == 'Z') + { + int status = -1; + char *dsymname = __cxa_demangle (name, demangle_buffer, + &demangle_buffer_len, &status); + if (status == 0) + name = demangle_buffer = dsymname; + } +#endif + return name; +}
static const char * get_diename (Dwarf_Die *die) @@ -299,7 +328,7 @@ print_dwarf_function (Dwfl_Module *mod, Dwarf_Addr addr) const char *name = get_diename (&scopes[i]); if (name == NULL) return false; - puts (name); + puts (symname (name)); return true; }
@@ -308,7 +337,7 @@ print_dwarf_function (Dwfl_Module *mod, Dwarf_Addr addr) const char *name = get_diename (&scopes[i]); if (name == NULL) return false; - printf ("%s inlined", name); + printf ("%s inlined", symname (name));
Dwarf_Files *files; if (dwarf_getsrcfiles (cudie, &files, NULL) == 0) @@ -389,6 +418,7 @@ print_addrsym (Dwfl_Module *mod, GElf_Addr addr) } else { + name = symname (name); if (off == 0) printf ("%s", name); else @@ -623,7 +653,13 @@ handle_address (const char *string, Dwfl *dwfl) /* First determine the function name. Use the DWARF information if possible. */ if (! print_dwarf_function (mod, addr) && !show_symbols) - puts (dwfl_module_addrname (mod, addr) ?: "??"); + { + const char *name = dwfl_module_addrname (mod, addr); + if (name != NULL) + puts (symname (name)); + else + puts ("??"); + } }
if (show_symbols) @@ -718,7 +754,7 @@ handle_address (const char *string, Dwfl *dwfl) || tag == DW_TAG_entry_point || tag == DW_TAG_subprogram) { - puts (get_diename (parent)); + puts (symname (get_diename (parent))); break; } } diff --git a/tests/ChangeLog b/tests/ChangeLog index d3a0e4b..30ed5f5 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,5 +1,11 @@ 2015-05-20 Mark Wielaard mjw@redhat.com
+ * run-addr2line-i-demangle-test.sh: New test. + * Makefile.am (TESTS): Add run-addr2line-i-demangle-test.sh. + (EXTRA_DIST): Likewise. + +2015-05-20 Mark Wielaard mjw@redhat.com + * run-addr2line-test.sh: Add -a test variants. * run-addr2line-i-test.sh: Likewise.
diff --git a/tests/Makefile.am b/tests/Makefile.am index fdbf5bf..55241c7 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -104,6 +104,7 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \ run-readelf-mixed-corenote.sh run-dwfllines.sh \ run-dwfl-report-elf-align.sh run-addr2line-test.sh \ run-addr2line-i-test.sh run-addr2line-i-lex-test.sh \ + run-addr2line-i-demangle-test.sh \ run-varlocs.sh run-funcretval.sh \ run-backtrace-native.sh run-backtrace-data.sh run-backtrace-dwarf.sh \ run-backtrace-native-biarch.sh run-backtrace-native-core.sh \ @@ -254,6 +255,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ test-core.exec.bz2 run-addr2line-test.sh \ run-addr2line-i-test.sh testfile-inlines.bz2 \ run-addr2line-i-lex-test.sh testfile-lex-inlines.bz2 \ + run-addr2line-i-demangle-test.sh \ testfileppc32.bz2 testfileppc64.bz2 \ testfiles390.bz2 testfiles390x.bz2 \ testfilearm.bz2 testfileaarch64.bz2 \ diff --git a/tests/run-addr2line-i-demangle-test.sh b/tests/run-addr2line-i-demangle-test.sh new file mode 100755 index 0000000..e709acf --- /dev/null +++ b/tests/run-addr2line-i-demangle-test.sh @@ -0,0 +1,69 @@ +#! /bin/sh +# Copyright (C) 2015 Red Hat, Inc. +# This file is part of elfutils. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# elfutils is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. + +if test -n "$ELFUTILS_DISABLE_DEMANGLE"; then + exit 77 +fi + +. $srcdir/test-subr.sh + +# See run-addr2line-i-test.sh for how to generate test files. +testfiles testfile-inlines + +# All together now plus (demangled) function names. +testrun_compare ${abs_top_builddir}/src/addr2line -C -f -i -e testfile-inlines 0x00000000000005a0 0x00000000000005a1 0x00000000000005b0 0x00000000000005b1 0x00000000000005c0 0x00000000000005d0 0x00000000000005e0 0x00000000000005e1 0x00000000000005f0 0x00000000000005f1 0x00000000000005f2 <<\EOF +foobar +/tmp/x.cpp:5 +foobar +/tmp/x.cpp:6 +fubar +/tmp/x.cpp:10 +fubar +/tmp/x.cpp:11 +foobar inlined at /tmp/x.cpp:15 in bar() +/tmp/x.cpp:5 +bar +/tmp/x.cpp:15 +fubar inlined at /tmp/x.cpp:20 in baz() +/tmp/x.cpp:10 +baz +/tmp/x.cpp:20 +foobar inlined at /tmp/x.cpp:15 in foo() +/tmp/x.cpp:5 +bar +/tmp/x.cpp:15 +foo() +/tmp/x.cpp:25 +fubar inlined at /tmp/x.cpp:20 in foo() +/tmp/x.cpp:10 +baz +/tmp/x.cpp:20 +foo() +/tmp/x.cpp:26 +fu() +/tmp/x.cpp:31 +fubar inlined at /tmp/x.cpp:32 in fu() +/tmp/x.cpp:10 +fu() +/tmp/x.cpp:32 +foobar inlined at /tmp/x.cpp:33 in fu() +/tmp/x.cpp:5 +fu() +/tmp/x.cpp:33 +EOF + +exit 0
This adds an option --pretty-print to eu-addr2line to show all information on one line and all inlines on a line of their own. This mimics the same option from binutils addr2line, but without the short option variant -p. Since we already use -p to select the process.
Example output:
eu-addr2line --pretty-print -s -i -f -C -p$(pidof firefox) 0x00007f368c6f8915 mozilla::ReentrantMonitor::Wait(unsigned int) at ReentrantMonitor.h:92 (inlined by) mozilla::ReentrantMonitorAutoEnter::Wait(unsigned int) at ReentrantMonitor.h:190
A couple of tests were added to check the output matches that of binutils addr2line.
Signed-off-by: Mark Wielaard mjw@redhat.com --- NEWS | 1 + src/ChangeLog | 10 +++++++++ src/addr2line.c | 47 +++++++++++++++++++++++++++++++++---------- tests/ChangeLog | 5 +++++ tests/run-addr2line-i-test.sh | 23 +++++++++++++++++++++ tests/run-addr2line-test.sh | 6 ++++++ 6 files changed, 81 insertions(+), 11 deletions(-)
diff --git a/NEWS b/NEWS index 46ff872..fc24c5e 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ libdw: Install new header elfutils/known-dwarf.h.
addr2line: New option -a, --addresses to print address before each entry. New option -C, --demangle to show demangled symbols. + New option --pretty-print to print all information on one line.
Version 0.161
diff --git a/src/ChangeLog b/src/ChangeLog index fe6f6f1..c93d54d 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,5 +1,15 @@ 2015-05-20 Mark Wielaard mjw@redhat.com
+ * addr2line.c (OPT_PRETTY): New constant define. + (argp_option): Add "pretty-print". + (pretty): New static bool. + (parse_opt): Set pretty. + (print_dwarf_function): Adjust output when pretty is set. + (print_addrsym): Likewise. + (handle_address): Likewise. + +2015-05-20 Mark Wielaard mjw@redhat.com + * Makefile.am (addr2line_LDADD): Add demanglelib. * addr2line.c (argp_option): Move demangle under output format. (demangle): New static bool. diff --git a/src/addr2line.c b/src/addr2line.c index 3f64011..1967bcb 100644 --- a/src/addr2line.c +++ b/src/addr2line.c @@ -50,6 +50,7 @@ ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
/* Values for the parameters which have no short form. */ #define OPT_DEMANGLER 0x100 +#define OPT_PRETTY 0x101 /* 'p' is already used to select the process. */
/* Definitions of arguments for argp functions. */ static const struct argp_option options[] = @@ -72,6 +73,8 @@ static const struct argp_option options[] = 0 }, { "demangle", 'C', "ARG", OPTION_ARG_OPTIONAL, N_("Show demangled symbols (ARG is always ignored)"), 0 }, + { "pretty-print", OPT_PRETTY, NULL, 0, + N_("Print all information on one line, and indent inlines"), 0 },
{ NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 }, /* Unsupported options. */ @@ -132,6 +135,9 @@ static bool show_inlines; /* True if all names need to be demangled. */ static bool demangle;
+/* True if all information should be printed on one line. */ +static bool pretty; + #ifdef USE_DEMANGLE static size_t demangle_buffer_len = 0; static char *demangle_buffer = NULL; @@ -269,6 +275,10 @@ parse_opt (int key, char *arg, struct argp_state *state) show_inlines = true; break;
+ case OPT_PRETTY: + pretty = true; + break; + default: return ARGP_ERR_UNKNOWN; } @@ -328,7 +338,7 @@ print_dwarf_function (Dwfl_Module *mod, Dwarf_Addr addr) const char *name = get_diename (&scopes[i]); if (name == NULL) return false; - puts (symname (name)); + printf ("%s%s", symname (name), pretty ? " " : "\n"); return true; }
@@ -337,7 +347,16 @@ print_dwarf_function (Dwfl_Module *mod, Dwarf_Addr addr) const char *name = get_diename (&scopes[i]); if (name == NULL) return false; - printf ("%s inlined", symname (name)); + + /* When using --pretty-print we only show inlines on their + own line. Just print the first subroutine name. */ + if (pretty) + { + printf ("%s ", symname (name)); + return true; + } + else + printf ("%s inlined", symname (name));
Dwarf_Files *files; if (dwarf_getsrcfiles (cudie, &files, NULL) == 0) @@ -412,9 +431,9 @@ print_addrsym (Dwfl_Module *mod, GElf_Addr addr) if (i >= 0) name = dwfl_module_relocation_info (mod, i, NULL); if (name == NULL) - puts ("??"); + printf ("??%s", pretty ? " ": "\n"); else - printf ("(%s)+%#" PRIx64 "\n", name, addr); + printf ("(%s)+%#" PRIx64 "%s", name, addr, pretty ? " " : "\n"); } else { @@ -443,7 +462,7 @@ print_addrsym (Dwfl_Module *mod, GElf_Addr addr) } } } - puts (""); + printf ("%s", pretty ? " " : "\n"); } }
@@ -645,7 +664,7 @@ handle_address (const char *string, Dwfl *dwfl) if (print_addresses) { int width = get_addr_width (mod); - printf ("0x%0*" PRIx64 "\n", width, addr); + printf ("0x%0*" PRIx64 "%s", width, addr, pretty ? ": " : "\n"); }
if (show_functions) @@ -655,16 +674,17 @@ handle_address (const char *string, Dwfl *dwfl) if (! print_dwarf_function (mod, addr) && !show_symbols) { const char *name = dwfl_module_addrname (mod, addr); - if (name != NULL) - puts (symname (name)); - else - puts ("??"); + name = (name != NULL) ? symname (name) : "??"; + printf ("%s%s", name, pretty ? " " : "\n"); } }
if (show_symbols) print_addrsym (mod, addr);
+ if ((show_functions || show_symbols) && pretty) + printf ("at "); + Dwfl_Line *line = dwfl_module_getsrc (mod, addr);
const char *src; @@ -741,6 +761,9 @@ handle_address (const char *string, Dwfl *dwfl) if (dwarf_tag (die) != DW_TAG_inlined_subroutine) continue;
+ if (pretty) + printf (" (inlined by) "); + if (show_functions) { /* Search for the parent inline or function. It @@ -754,7 +777,9 @@ handle_address (const char *string, Dwfl *dwfl) || tag == DW_TAG_entry_point || tag == DW_TAG_subprogram) { - puts (symname (get_diename (parent))); + printf ("%s%s", + symname (get_diename (parent)), + pretty ? " at " : "\n"); break; } } diff --git a/tests/ChangeLog b/tests/ChangeLog index 30ed5f5..94ee1f7 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,5 +1,10 @@ 2015-05-20 Mark Wielaard mjw@redhat.com
+ * run-addr2line-i-test.sh: Add pretty test. + * run-addr2line-test.sh: Likewise. + +2015-05-20 Mark Wielaard mjw@redhat.com + * run-addr2line-i-demangle-test.sh: New test. * Makefile.am (TESTS): Add run-addr2line-i-demangle-test.sh. (EXTRA_DIST): Likewise. diff --git a/tests/run-addr2line-i-test.sh b/tests/run-addr2line-i-test.sh index e62aa20..d08f3cb 100755 --- a/tests/run-addr2line-i-test.sh +++ b/tests/run-addr2line-i-test.sh @@ -197,4 +197,27 @@ _Z2fuv /tmp/x.cpp:33 EOF
+# All together now (plus function names and addresses and pretty) +testrun_compare ${abs_top_builddir}/src/addr2line --pretty-print -a -f -i -e testfile-inlines 0x00000000000005a0 0x00000000000005a1 0x00000000000005b0 0x00000000000005b1 0x00000000000005c0 0x00000000000005d0 0x00000000000005e0 0x00000000000005e1 0x00000000000005f0 0x00000000000005f1 0x00000000000005f2 <<\EOF +0x00000000000005a0: foobar at /tmp/x.cpp:5 +0x00000000000005a1: foobar at /tmp/x.cpp:6 +0x00000000000005b0: fubar at /tmp/x.cpp:10 +0x00000000000005b1: fubar at /tmp/x.cpp:11 +0x00000000000005c0: foobar at /tmp/x.cpp:5 + (inlined by) bar at /tmp/x.cpp:15 +0x00000000000005d0: fubar at /tmp/x.cpp:10 + (inlined by) baz at /tmp/x.cpp:20 +0x00000000000005e0: foobar at /tmp/x.cpp:5 + (inlined by) bar at /tmp/x.cpp:15 + (inlined by) _Z3foov at /tmp/x.cpp:25 +0x00000000000005e1: fubar at /tmp/x.cpp:10 + (inlined by) baz at /tmp/x.cpp:20 + (inlined by) _Z3foov at /tmp/x.cpp:26 +0x00000000000005f0: _Z2fuv at /tmp/x.cpp:31 +0x00000000000005f1: fubar at /tmp/x.cpp:10 + (inlined by) _Z2fuv at /tmp/x.cpp:32 +0x00000000000005f2: foobar at /tmp/x.cpp:5 + (inlined by) _Z2fuv at /tmp/x.cpp:33 +EOF + exit 0 diff --git a/tests/run-addr2line-test.sh b/tests/run-addr2line-test.sh index 8d06064..1079c3e 100755 --- a/tests/run-addr2line-test.sh +++ b/tests/run-addr2line-test.sh @@ -107,4 +107,10 @@ echo "# Everything from stdin (with newlines) with addresses." cat stdin.nl | testrun ${abs_top_builddir}/src/addr2line -a -f -e testfile > stdin.nl.out || exit 1 cmp good.addr.out stdin.nl.out || exit 1
+echo "# Pretty with functions and addresses." +testrun_compare ${abs_top_builddir}/src/addr2line --pretty -a -f -e testfile 0x08048468 0x0804845c << EOF +0x08048468: foo at /home/drepper/gnu/new-bu/build/ttt/f.c:3 +0x0804845c: bar at /home/drepper/gnu/new-bu/build/ttt/b.c:4 +EOF + exit 0
If we're starting to aim for compatibility with binutils, then perhaps we should change -p to match. I think having only --pid would be OK.
printf ("%s%s", symname (name), pretty ? " " : "\n");
Use %c. Several other instances below.
name = (name != NULL) ? symname (name) : "??";
No need for parens.
On Wed, 2015-05-20 at 11:28 -0700, Roland McGrath wrote:
If we're starting to aim for compatibility with binutils, then perhaps we should change -p to match. I think having only --pid would be OK.
I admit that was my goal at first, but there are just too many minor differences.
I made sure the output of --pretty-print matches that of binutils addr2line, but in the none pretty case there are still differences. We expand the inlines on the first line, binutils only shows the function symbol with -i. bintuils always prints line table flags, we only with --flags (and then we do print statements, but binutils seems to ignore them), we handle symbols as input, binutils doesn't, etc. eu-addr2line also has many more options and handles separate debuginfo, processes, core files, maps, etc.
And binutils addr2line doesn't seem to have any test cases so it is hard to know whether there are other differences or that binutils addr2line will keep consistent (for this patch series I generated new tests for eu-addr2line, where possible using binutils addr2line for the expected output).
Since other elfutils tools do support --pid, -p and it is handled by dwfl_standard_argp () I rather keep the elfutils tools consistent than adjust just this one short option for this one utility.
printf ("%s%s", symname (name), pretty ? " " : "\n");
Use %c. Several other instances below.
Fixed 5 times.
name = (name != NULL) ? symname (name) : "??";
No need for parens.
Removed.
Cheers,
Mark
Since other elfutils tools do support --pid, -p and it is handled by dwfl_standard_argp () I rather keep the elfutils tools consistent than adjust just this one short option for this one utility.
I meant dropping -p from the shared argp. But I don't disagree with the rest of your rationale.
On Fri, 2015-05-22 at 10:02 -0700, Roland McGrath wrote:
Since other elfutils tools do support --pid, -p and it is handled by dwfl_standard_argp () I rather keep the elfutils tools consistent than adjust just this one short option for this one utility.
I meant dropping -p from the shared argp.
Hmmm. I must admit I am not a fan. It feels natural to have --pid, -p for tools. And it looks like breaking API if someone is using dwfl_standard_argp in their own programs now. It is certainly a pity if that means we aren't command line compatible with some of the binutils tools. But I am not sure it really justifies just dropping -p from dwfl_standard_argp.
Cheers,
Mark
elfutils-devel@lists.fedorahosted.org