summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wielaard <mjw@redhat.com>2013-08-13 11:40:21 +0200
committerMark Wielaard <mjw@redhat.com>2013-08-13 12:15:22 +0200
commit4b9e639d314e1ba63dbf661eb1fd7df1a8ee7d4a (patch)
treea5babe86dda3e4c7ed09633874350cadb284b6e3
parentd0f8501761b15b35dc52eaf5709a638276270077 (diff)
downloadelfutils-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/ChangeLog9
-rw-r--r--src/addr2line.c140
-rw-r--r--tests/ChangeLog8
-rw-r--r--tests/Makefile.am6
-rwxr-xr-xtests/run-addr2line-i-test.sh145
-rwxr-xr-xtests/testfile-inlines.bz2bin0 -> 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
new file mode 100755
index 00000000..6a0c7c57
--- /dev/null
+++ b/tests/testfile-inlines.bz2
Binary files differ