]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
addr2line: Support -i, --inlines output option.
authorMark Wielaard <mjw@redhat.com>
Tue, 13 Aug 2013 09:40:21 +0000 (11:40 +0200)
committerMark Wielaard <mjw@redhat.com>
Tue, 13 Aug 2013 10:15:22 +0000 (12:15 +0200)
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>
src/ChangeLog
src/addr2line.c
tests/ChangeLog
tests/Makefile.am
tests/run-addr2line-i-test.sh [new file with mode: 0755]
tests/testfile-inlines.bz2 [new file with mode: 0755]

index eacadbcd8334ea4dc53ebaa646379632362b1fbf..6788087d0d01e712d5bb97a06e2c79198699df6f 100644 (file)
@@ -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,
index f2bc3255ef40d98ed88e08910c1e60d654bc4b3d..82e80b12d97f36d64adcea50778da959a417b9ef 100644 (file)
@@ -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;
 }
 
index 3475d7bd788c2cab131c4b4045537f56c1bd24c4..9808ce737dde7a88624277b9eded936a6802c7c6 100644 (file)
@@ -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.
index ac99e3e476d1a6fb50a746c55201e86deb7a7d04..9aa06a6a79b6198e2961c100d2490f67cc3e6475 100644 (file)
@@ -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 (executable)
index 0000000..e98adda
--- /dev/null
@@ -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 (executable)
index 0000000..6a0c7c5
Binary files /dev/null and b/tests/testfile-inlines.bz2 differ