diff options
author | Mark Wielaard <mjw@redhat.com> | 2013-08-13 11:40:21 +0200 |
---|---|---|
committer | Mark Wielaard <mjw@redhat.com> | 2013-08-13 12:15:22 +0200 |
commit | 4b9e639d314e1ba63dbf661eb1fd7df1a8ee7d4a (patch) | |
tree | a5babe86dda3e4c7ed09633874350cadb284b6e3 | |
parent | d0f8501761b15b35dc52eaf5709a638276270077 (diff) | |
download | elfutils-4b9e639d314e1ba63dbf661eb1fd7df1a8ee7d4a.tar.gz |
addr2line: Support -i, --inlines output option.
Show all source locations that caused inline expansion of subroutines
at the given address. This can easily be supported by using libdw
dwarf_getscopes_die which will give all nested inlined subroutines.
When -f, --functions is given also show the function names where the
subroutines were inlined.
The output matches that of binutils addr2line --inlines.
Signed-off-by: Mark Wielaard <mjw@redhat.com>
-rw-r--r-- | src/ChangeLog | 9 | ||||
-rw-r--r-- | src/addr2line.c | 140 | ||||
-rw-r--r-- | tests/ChangeLog | 8 | ||||
-rw-r--r-- | tests/Makefile.am | 6 | ||||
-rwxr-xr-x | tests/run-addr2line-i-test.sh | 145 | ||||
-rwxr-xr-x | tests/testfile-inlines.bz2 | bin | 0 -> 2815 bytes |
6 files changed, 286 insertions, 22 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index eacadbcd..6788087d 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,12 @@ +2013-08-13 Mark Wielaard <mjw@redhat.com> + + * addr2line.c (options): Add "inlines", 'i'. + (show_inlines): New bool. + (parse_opt): Handle 'i'. + (print_diesym): New static function. + (print_src): New function taking code from... + (handle_address): here. Call print_src. Print inlines. + 2013-08-12 Mark Wielaard <mjw@redhat.com> * addr2line.c (main): If there is a newline char at end of buf, diff --git a/src/addr2line.c b/src/addr2line.c index f2bc3255..82e80b12 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 Red Hat, Inc. + Copyright (C) 2005-2010, 2012, 2013 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2005. @@ -64,6 +64,9 @@ static const struct argp_option options[] = { "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 }, { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 }, /* Unsupported options. */ @@ -114,6 +117,9 @@ static bool show_symbols; /* If non-null, take address parameters as relative to named section. */ static const char *just_section; +/* True if all inlined subroutines of the current address should be shown. */ +static bool show_inlines; + int main (int argc, char *argv[]) @@ -232,6 +238,10 @@ parse_opt (int key, char *arg, struct argp_state *state) just_section = arg; break; + case 'i': + show_inlines = true; + break; + default: return ARGP_ERR_UNKNOWN; } @@ -351,6 +361,23 @@ print_addrsym (Dwfl_Module *mod, GElf_Addr addr) printf ("%s+%#" PRIx64 "\n", name, addr - s.st_value); } +static void +print_diesym (Dwarf_Die *die) +{ + Dwarf_Attribute attr; + const char *name; + + name = dwarf_formstring (dwarf_attr_integrate (die, DW_AT_MIPS_linkage_name, + &attr) + ?: dwarf_attr_integrate (die, DW_AT_linkage_name, + &attr)); + + if (name == NULL) + name = dwarf_diename (die) ?: "??"; + + puts (name); +} + static int see_one_module (Dwfl_Module *mod, void **userdata __attribute__ ((unused)), @@ -442,6 +469,30 @@ adjust_to_section (const char *name, uintmax_t *addr, Dwfl *dwfl) return false; } +static void +print_src (const char *src, int lineno, int linecol, Dwarf_Die *cu) +{ + const char *comp_dir = ""; + const char *comp_dir_sep = ""; + + if (only_basenames) + src = basename (src); + else if (use_comp_dir && src[0] != '/') + { + Dwarf_Attribute attr; + comp_dir = dwarf_formstring (dwarf_attr (cu, DW_AT_comp_dir, &attr)); + if (comp_dir != NULL) + comp_dir_sep = "/"; + } + + if (linecol != 0) + printf ("%s%s%s:%d:%d", + comp_dir, comp_dir_sep, src, lineno, linecol); + else + printf ("%s%s%s:%d", + comp_dir, comp_dir_sep, src, lineno); +} + static int handle_address (const char *string, Dwfl *dwfl) { @@ -510,28 +561,11 @@ handle_address (const char *string, Dwfl *dwfl) const char *src; int lineno, linecol; + if (line != NULL && (src = dwfl_lineinfo (line, &addr, &lineno, &linecol, NULL, NULL)) != NULL) { - const char *comp_dir = ""; - const char *comp_dir_sep = ""; - - if (only_basenames) - src = basename (src); - else if (use_comp_dir && src[0] != '/') - { - comp_dir = dwfl_line_comp_dir (line); - if (comp_dir != NULL) - comp_dir_sep = "/"; - } - - if (linecol != 0) - printf ("%s%s%s:%d:%d", - comp_dir, comp_dir_sep, src, lineno, linecol); - else - printf ("%s%s%s:%d", - comp_dir, comp_dir_sep, src, lineno); - + print_src (src, lineno, linecol, dwfl_linecu (line)); if (show_flags) { Dwarf_Addr bias; @@ -565,6 +599,72 @@ handle_address (const char *string, Dwfl *dwfl) else puts ("??:0"); + if (show_inlines) + { + Dwarf_Addr bias = 0; + Dwarf_Die *cudie = dwfl_module_addrdie (mod, addr, &bias); + + Dwarf_Die *scopes; + int nscopes = dwarf_getscopes (cudie, addr - bias, &scopes); + if (nscopes < 0) + return 1; + + if (nscopes > 0) + { + Dwarf_Die subroutine; + Dwarf_Off dieoff = dwarf_dieoffset (&scopes[0]); + dwarf_offdie (dwfl_module_getdwarf (mod, &bias), + dieoff, &subroutine); + free (scopes); + + nscopes = dwarf_getscopes_die (&subroutine, &scopes); + if (nscopes > 1) + { + Dwarf_Die cu; + Dwarf_Files *files; + if (dwarf_diecu (&scopes[0], &cu, NULL, NULL) != NULL + && dwarf_getsrcfiles (cudie, &files, NULL) == 0) + { + for (int i = 0; i < nscopes - 1; i++) + { + Dwarf_Word val; + Dwarf_Attribute attr; + Dwarf_Die *die = &scopes[i]; + if (dwarf_tag (die) != DW_TAG_inlined_subroutine) + continue; + + if (show_functions) + print_diesym (&scopes[i + 1]); + + src = NULL; + lineno = 0; + linecol = 0; + if (dwarf_formudata (dwarf_attr (die, DW_AT_call_file, + &attr), &val) == 0) + src = dwarf_filesrc (files, val, NULL, NULL); + + if (dwarf_formudata (dwarf_attr (die, DW_AT_call_line, + &attr), &val) == 0) + lineno = val; + + if (dwarf_formudata (dwarf_attr (die, DW_AT_call_column, + &attr), &val) == 0) + linecol = val; + + if (src != NULL) + { + print_src (src, lineno, linecol, &cu); + putchar ('\n'); + } + else + puts ("??:0"); + } + } + } + } + free (scopes); + } + return 0; } diff --git a/tests/ChangeLog b/tests/ChangeLog index 3475d7bd..9808ce73 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,11 @@ +2013-08-13 Mark Wielaard <mjw@redhat.com> + + * run-addr2line-i-test.sh: New test. + * testfile-inlines.bz2: New testfile. + * Makefile.am (EXTRA_DIST): Add run-addr2line-i-test.sh and + testfile-inlines.bz2. + (TESTS): Add run-addr2line-i-test.sh. + 2013-08-12 Mark Wielaard <mjw@redhat.com> * run-addr2line-test.sh: New test. diff --git a/tests/Makefile.am b/tests/Makefile.am index ac99e3e4..9aa06a6a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -88,7 +88,8 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \ run-low_high_pc.sh run-macro-test.sh run-elf_cntl_gelf_getshdr.sh \ run-test-archive64.sh run-readelf-vmcoreinfo.sh \ run-readelf-mixed-corenote.sh run-dwfllines.sh \ - run-dwfl-report-elf-align.sh run-addr2line-test.sh + run-dwfl-report-elf-align.sh run-addr2line-test.sh \ + run-addr2line-i-test.sh if !STANDALONE check_PROGRAMS += msg_tst md5-sha1-test @@ -200,7 +201,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ run-dwfllines.sh run-dwfl-report-elf-align.sh \ testfile-dwfl-report-elf-align-shlib.so.bz2 \ testfilenolines.bz2 test-core-lib.so.bz2 test-core.core.bz2 \ - test-core.exec.bz2 run-addr2line-test.sh + test-core.exec.bz2 run-addr2line-test.sh \ + run-addr2line-i-test.sh testfile-inlines.bz2 if USE_VALGRIND valgrind_cmd='valgrind -q --trace-children=yes --error-exitcode=1 --run-libc-freeres=no' diff --git a/tests/run-addr2line-i-test.sh b/tests/run-addr2line-i-test.sh new file mode 100755 index 00000000..e98adda3 --- /dev/null +++ b/tests/run-addr2line-i-test.sh @@ -0,0 +1,145 @@ +#! /bin/sh +# Copyright (C) 2013 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/>. + +. $srcdir/test-subr.sh + +# // g++ x.cpp -g -fPIC -olibx.so -shared -O3 -fvisibility=hidden +# +# void foobar() +# { +# __asm__ ( "nop" ::: ); +# } +# +# void fubar() +# { +# __asm__ ( "nop" ::: ); +# } +# +# void bar() +# { +# foobar(); +# } +# +# void baz() +# { +# fubar(); +# } +# +# void foo() +# { +# bar(); +# baz(); +# } +# +# void fu() +# { +# __asm__ ( "nop" ::: ); +# fubar(); +# foobar(); +# } + +testfiles testfile-inlines + +testrun_compare ${abs_top_builddir}/src/addr2line -i -e testfile-inlines 0x00000000000005a0 <<\EOF +/tmp/x.cpp:5 +EOF + +testrun_compare ${abs_top_builddir}/src/addr2line -i -e testfile-inlines 0x00000000000005a1 <<\EOF +/tmp/x.cpp:6 +EOF + +testrun_compare ${abs_top_builddir}/src/addr2line -i -e testfile-inlines 0x00000000000005b0 <<\EOF +/tmp/x.cpp:10 +EOF + +testrun_compare ${abs_top_builddir}/src/addr2line -i -e testfile-inlines 0x00000000000005b1 <<\EOF +/tmp/x.cpp:11 +EOF + +testrun_compare ${abs_top_builddir}/src/addr2line -i -e testfile-inlines 0x00000000000005c0 <<\EOF +/tmp/x.cpp:5 +/tmp/x.cpp:15 +EOF + +testrun_compare ${abs_top_builddir}/src/addr2line -i -e testfile-inlines 0x00000000000005d0 <<\EOF +/tmp/x.cpp:10 +/tmp/x.cpp:20 +EOF + +testrun_compare ${abs_top_builddir}/src/addr2line -i -e testfile-inlines 0x00000000000005e0 <<\EOF +/tmp/x.cpp:5 +/tmp/x.cpp:15 +/tmp/x.cpp:25 +EOF + +testrun_compare ${abs_top_builddir}/src/addr2line -i -e testfile-inlines 0x00000000000005e1 <<\EOF +/tmp/x.cpp:10 +/tmp/x.cpp:20 +/tmp/x.cpp:26 +EOF + +testrun_compare ${abs_top_builddir}/src/addr2line -i -e testfile-inlines 0x00000000000005f1 <<\EOF +/tmp/x.cpp:10 +/tmp/x.cpp:32 +EOF + +testrun_compare ${abs_top_builddir}/src/addr2line -i -e testfile-inlines 0x00000000000005f2 <<\EOF +/tmp/x.cpp:5 +/tmp/x.cpp:33 +EOF + +# All together now (plus function names). +testrun_compare ${abs_top_builddir}/src/addr2line -f -i -e testfile-inlines 0x00000000000005a0 0x00000000000005a1 0x00000000000005b0 0x00000000000005b1 0x00000000000005c0 0x00000000000005d0 0x00000000000005e0 0x00000000000005e1 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 _Z3barv +/tmp/x.cpp:5 +bar +/tmp/x.cpp:15 +fubar inlined at /tmp/x.cpp:20 in _Z3bazv +/tmp/x.cpp:10 +baz +/tmp/x.cpp:20 +foobar inlined at /tmp/x.cpp:15 in _Z3foov +/tmp/x.cpp:5 +bar +/tmp/x.cpp:15 +_Z3foov +/tmp/x.cpp:25 +fubar inlined at /tmp/x.cpp:20 in _Z3foov +/tmp/x.cpp:10 +baz +/tmp/x.cpp:20 +_Z3foov +/tmp/x.cpp:26 +fubar inlined at /tmp/x.cpp:32 in _Z2fuv +/tmp/x.cpp:10 +_Z2fuv +/tmp/x.cpp:32 +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/testfile-inlines.bz2 b/tests/testfile-inlines.bz2 Binary files differnew file mode 100755 index 00000000..6a0c7c57 --- /dev/null +++ b/tests/testfile-inlines.bz2 |