]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
stack: Add -i, --inlines. Show inlined call frames using DWARF debuginfo.
authorMark Wielaard <mjw@redhat.com>
Tue, 21 Jan 2014 15:13:49 +0000 (16:13 +0100)
committerMark Wielaard <mjw@redhat.com>
Mon, 27 Jan 2014 15:07:54 +0000 (16:07 +0100)
Using dwarf_getscopes_die we can get all scopes that make up the current
subprogram representing an address. Using the call_file/line/column
attributes we can also show the source locations of these "inlined" calls.
Includes a test that shows that when DWARF debuginfo is available all
inlined function call frames and their source location can be shown.

Signed-off-by: Mark Wielaard <mjw@redhat.com>
ChangeLog
NEWS
src/ChangeLog
src/stack.c
tests/ChangeLog
tests/Makefile.am
tests/run-stack-i-test.sh [new file with mode: 0755]

index ad8cd0336432167a653ef10b13652ea35e8f3c19..a2436ceedfde5397c5a4fd05b0d630d0089397d3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2014-01-21  Mark Wielaard  <mjw@redhat.com>
+
+       * NEWS (Version 0.159): Add stack -i.
+
 2014-01-20  Mark Wielaard  <mjw@redhat.com>
 
        * NEWS (Version 0.159): New. Add stack -d.
diff --git a/NEWS b/NEWS
index 5c865e0bdcdd5d5b745d4ab9a2aef138e0879c0d..b774ec4e3078cf3f753db4290e8f24b6a8c20381 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,7 @@
 Version 0.159
 
 stack: New option -d, --debugname to lookup DWARF debuginfo name for frame.
+       New option -i, --inlines to show inlined frames using DWARF debuginfo.
 
 Version 0.158
 
index d1b221f20c74eeecd35e9fb58b74928b4bbe8bee..26a607d8b4f63fc6ba846f9895024954bed825fc 100644 (file)
@@ -1,3 +1,14 @@
+2014-01-21  Mark Wielaard  <mjw@redhat.com>
+
+       * stack.c (show_inlines): New static boolean.
+       (print_frame): New function split out from...
+       (print_frames): ..here. If show_inlines is true and we found a
+       DIE for the frame address, call print_inline_frames otherwise
+       call print_frame. Keep track of and track frame_nr.
+       (print_inline_frames): New function.
+       (parse_opt): Handle '-i'.
+       (main): Add 'i' to options.
+
 2014-01-27  Mark Wielaard  <mjw@redhat.com>
 
        * stack.c (maxframes): Initialize to 256.
index e675267dd2c0c5737deeebf7910f056490ed793d..c277dfd03f775a53a6d4704b1525aa0adac23522 100644 (file)
@@ -51,6 +51,7 @@ static bool show_quiet = false;
 static bool show_raw = false;
 static bool show_modules = false;
 static bool show_debugname = false;
+static bool show_inlines = false;
 
 static int maxframes = 256;
 
@@ -211,6 +212,150 @@ die_name (Dwarf_Die *die)
   return name;
 }
 
+static void
+print_frame (int nr, Dwarf_Addr pc, bool isactivation,
+            Dwarf_Addr pc_adjusted, Dwfl_Module *mod,
+            const char *symname, Dwarf_Die *cudie,
+            Dwarf_Die *die)
+{
+  int width = get_addr_width (mod);
+  printf ("#%-2u 0x%0*" PRIx64, nr, width, (uint64_t) pc);
+
+  if (show_activation)
+    printf ("%4s", ! isactivation ? "- 1" : "");
+
+  if (symname != NULL)
+    {
+#ifdef USE_DEMANGLE
+      // Require GNU v3 ABI by the "_Z" prefix.
+      if (! show_raw && symname[0] == '_' && symname[1] == 'Z')
+       {
+         int status = -1;
+         char *dsymname = __cxa_demangle (symname, demangle_buffer,
+                                          &demangle_buffer_len, &status);
+         if (status == 0)
+           symname = demangle_buffer = dsymname;
+       }
+#endif
+      printf (" %s", symname);
+    }
+
+  const char* fname;
+  Dwarf_Addr start;
+  fname = dwfl_module_info(mod, NULL, &start,
+                          NULL, NULL, NULL, NULL, NULL);
+  if (show_module)
+    {
+      if (fname != NULL)
+       printf (" - %s", fname);
+    }
+
+  if (show_build_id)
+    {
+      const unsigned char *id;
+      GElf_Addr id_vaddr;
+      int id_len = dwfl_module_build_id (mod, &id, &id_vaddr);
+      if (id_len > 0)
+       {
+         printf ("\n    [");
+         do
+           printf ("%02" PRIx8, *id++);
+         while (--id_len > 0);
+         printf ("]@0x%0" PRIx64 "+0x%" PRIx64,
+                 start, pc_adjusted - start);
+       }
+    }
+
+  if (show_source)
+    {
+      int line, col;
+      const char* sname;
+      line = col = -1;
+      sname = NULL;
+      if (die != NULL)
+       {
+         Dwarf_Files *files;
+         if (dwarf_getsrcfiles (cudie, &files, NULL) == 0)
+           {
+             Dwarf_Attribute attr;
+             Dwarf_Word val;
+             if (dwarf_formudata (dwarf_attr (die, DW_AT_call_file, &attr),
+                                  &val) == 0)
+               {
+                 sname = dwarf_filesrc (files, val, NULL, NULL);
+                 if (dwarf_formudata (dwarf_attr (die, DW_AT_call_line,
+                                                  &attr), &val) == 0)
+                   {
+                     line = val;
+                     if (dwarf_formudata (dwarf_attr (die, DW_AT_call_column,
+                                                      &attr), &val) == 0)
+                       col = val;
+                   }
+               }
+           }
+       }
+      else
+       {
+         Dwfl_Line *lineobj = dwfl_module_getsrc(mod, pc_adjusted);
+         if (lineobj)
+           sname = dwfl_lineinfo (lineobj, NULL, &line, &col, NULL, NULL);
+       }
+
+      if (sname != NULL)
+       {
+         printf ("\n    %s", sname);
+         if (line > 0)
+           {
+             printf (":%d", line);
+             if (col > 0)
+               printf (":%d", col);
+           }
+       }
+    }
+  printf ("\n");
+}
+
+static void
+print_inline_frames (int *nr, Dwarf_Addr pc, bool isactivation,
+                    Dwarf_Addr pc_adjusted, Dwfl_Module *mod,
+                    const char *symname, Dwarf_Die *cudie, Dwarf_Die *die)
+{
+  Dwarf_Die *scopes = NULL;
+  int nscopes = dwarf_getscopes_die (die, &scopes);
+  if (nscopes > 0)
+    {
+      /* scopes[0] == die, the lowest level, for which we already have
+        the name.  This is the actual source location where it
+        happened.  */
+      print_frame ((*nr)++, pc, isactivation, pc_adjusted, mod, symname,
+                  NULL, NULL);
+
+      /* last_scope is the source location where the next frame/function
+        call was done. */
+      Dwarf_Die *last_scope = &scopes[0];
+      for (int i = 1; i < nscopes && (maxframes == 0 || *nr < maxframes); i++)
+       {
+         Dwarf_Die *scope = &scopes[i];
+         int tag = dwarf_tag (scope);
+         if (tag != DW_TAG_inlined_subroutine
+             && tag != DW_TAG_entry_point
+             && tag != DW_TAG_subprogram)
+           continue;
+
+         symname = die_name (scope);
+         print_frame ((*nr)++, pc, isactivation, pc_adjusted, mod, symname,
+                      cudie, last_scope);
+
+         /* Found the "top-level" in which everything was inlined?  */
+         if (tag == DW_TAG_subprogram)
+           break;
+
+         last_scope = scope;
+       }
+    }
+  free (scopes);
+}
+
 static void
 print_frames (struct frames *frames, pid_t tid, int dwflerr, const char *what)
 {
@@ -218,7 +363,9 @@ print_frames (struct frames *frames, pid_t tid, int dwflerr, const char *what)
     frames_shown = true;
 
   printf ("TID %d:\n", tid);
-  for (int nr = 0; nr < frames->frames; nr++)
+  int frame_nr = 0;
+  for (int nr = 0; nr < frames->frames && (maxframes == 0
+                                          || frame_nr < maxframes); nr++)
     {
       Dwarf_Addr pc = frames->frame[nr].pc;
       bool isactivation = frames->frame[nr].isactivation;
@@ -227,13 +374,16 @@ print_frames (struct frames *frames, pid_t tid, int dwflerr, const char *what)
       /* Get PC->SYMNAME.  */
       Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted);
       const char *symname = NULL;
+      Dwarf_Die die_mem;
+      Dwarf_Die *die = NULL;
+      Dwarf_Die *cudie = NULL;
       if (mod && ! show_quiet)
        {
          if (show_debugname)
            {
              Dwarf_Addr bias = 0;
-             Dwarf_Die *cudie = dwfl_module_addrdie (mod, pc_adjusted, &bias);
              Dwarf_Die *scopes = NULL;
+             cudie = dwfl_module_addrdie (mod, pc_adjusted, &bias);
              int nscopes = dwarf_getscopes (cudie, pc_adjusted - bias,
                                             &scopes);
 
@@ -246,6 +396,12 @@ print_frames (struct frames *frames, pid_t tid, int dwflerr, const char *what)
                      || tag == DW_TAG_inlined_subroutine
                      || tag == DW_TAG_entry_point)
                    symname = die_name (scope);
+
+                 if (symname != NULL)
+                   {
+                     die_mem = *scope;
+                     die = &die_mem;
+                   }
                }
              free (scopes);
            }
@@ -254,78 +410,18 @@ print_frames (struct frames *frames, pid_t tid, int dwflerr, const char *what)
            symname = dwfl_module_addrname (mod, pc_adjusted);
        }
 
-      int width = get_addr_width (mod);
-      printf ("#%-2u 0x%0*" PRIx64, nr, width, (uint64_t) pc);
-
-      if (show_activation)
-       printf ("%4s", ! isactivation ? "- 1" : "");
-
-      if (symname != NULL)
-       {
-#ifdef USE_DEMANGLE
-         // Require GNU v3 ABI by the "_Z" prefix.
-         if (! show_raw && symname[0] == '_' && symname[1] == 'Z')
-           {
-             int status = -1;
-             char *dsymname = __cxa_demangle (symname, demangle_buffer,
-                                              &demangle_buffer_len, &status);
-             if (status == 0)
-               symname = demangle_buffer = dsymname;
-           }
-#endif
-         printf (" %s", symname);
-       }
-
-      const char* fname;
-      Dwarf_Addr start;
-      fname = dwfl_module_info(mod, NULL, &start,
-                              NULL, NULL, NULL, NULL, NULL);
-      if (show_module)
-       {
-         if (fname != NULL)
-           printf (" - %s", fname);
-       }
-
-      if (show_build_id)
-       {
-         const unsigned char *id;
-         GElf_Addr id_vaddr;
-         int id_len = dwfl_module_build_id (mod, &id, &id_vaddr);
-         if (id_len > 0)
-           {
-             printf ("\n    [");
-             do
-               printf ("%02" PRIx8, *id++);
-             while (--id_len > 0);
-             printf ("]@0x%0" PRIx64 "+0x%" PRIx64,
-                     start, pc_adjusted - start);
-           }
-       }
-
-      if (show_source)
-       {
-         Dwfl_Line *lineobj = dwfl_module_getsrc(mod, pc_adjusted);
-         if (lineobj)
-           {
-             int line, col;
-             const char* sname;
-             line = col = -1;
-             sname = dwfl_lineinfo (lineobj, NULL, &line, &col, NULL, NULL);
-             if (sname != NULL)
-               {
-                 printf ("\n    %s", sname);
-                 if (line > 0)
-                   {
-                     printf (":%d", line);
-                     if (col > 0)
-                       printf (":%d", col);
-                   }
-               }
-           }
-       }
-      printf ("\n");
+      if (show_inlines && die != NULL)
+       print_inline_frames (&frame_nr, pc, isactivation, pc_adjusted, mod,
+                            symname, cudie, die);
+      else
+       print_frame (frame_nr++, pc, isactivation, pc_adjusted, mod, symname,
+                    NULL, NULL);
     }
-  if (dwflerr != 0)
+
+  if (frames->frames > 0 && frame_nr == maxframes)
+    error (0, 0, "tid %d: shown max number of frames "
+          "(%d, use -n 0 for unlimited)", tid, maxframes);
+  else if (dwflerr != 0)
     {
       if (frames->frames > 0)
        {
@@ -350,9 +446,6 @@ print_frames (struct frames *frames, pid_t tid, int dwflerr, const char *what)
       else
        error (0, 0, "%s tid %d: %s", what, tid, dwfl_errmsg (dwflerr));
     }
-  else if (frames->frames > 0 && frames->frames == maxframes)
-    error (0, 0, "tid %d: shown max number of frames "
-          "(%d, use -n 0 for unlimited)", tid, maxframes);
 }
 
 static int
@@ -429,8 +522,13 @@ parse_opt (int key, char *arg __attribute__ ((unused)),
       show_debugname = true;
       break;
 
+    case 'i':
+      show_inlines = show_debugname = true;
+      break;
+
     case 'v':
       show_activation = show_source = show_module = show_debugname = true;
+      show_inlines = true;
       break;
 
     case 'b':
@@ -556,12 +654,14 @@ main (int argc, char **argv)
       { "debugname",  'd', NULL, 0,
        N_("Additionally try to lookup DWARF debuginfo name for frame address"),
        0 },
+      { "inlines",  'i', NULL, 0,
+       N_("Additionally show inlined function frames using DWARF debuginfo if available (implies -d)"), 0 },
       { "module",  'm', NULL, 0,
        N_("Additionally show module file information"), 0 },
       { "source",  's', NULL, 0,
        N_("Additionally show source file information"), 0 },
       { "verbose", 'v', NULL, 0,
-       N_("Show all additional information (activation, debugname, module and source)"), 0 },
+       N_("Show all additional information (activation, debugname, inlines, module and source)"), 0 },
       { "quiet", 'q', NULL, 0,
        N_("Do not resolve address to function symbol name"), 0 },
       { "raw", 'r', NULL, 0,
index 70b055104d87d8cba2f4379d14a9ed3ed73f4edf..aa468c44271dc4ac822160f40d85c3628e39cbdf 100644 (file)
@@ -1,3 +1,9 @@
+2014-01-21  Mark Wielaard  <mjw@redhat.com>
+
+       * Makefile.am (TESTS): Add run-stack-i-test.sh.
+       (EXTRA_DIST): Likewise.
+       * run-stack-i-test.sh: New test.
+
 2014-01-20  Mark Wielaard  <mjw@redhat.com>
 
        * Makefile.am (TESTS): Add run-stack-d-test.sh.
index 35f6e87e87007fb7fb018277fadede6f4b598eea..aa13cfa35a1b3132664c3f8a988e56f812807cd9 100644 (file)
@@ -108,7 +108,7 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
        run-backtrace-native-core-biarch.sh run-backtrace-core-x86_64.sh \
        run-backtrace-core-i386.sh run-backtrace-core-ppc.sh \
        run-backtrace-core-s390x.sh run-backtrace-core-s390.sh \
-       run-backtrace-demangle.sh run-stack-d-test.sh
+       run-backtrace-demangle.sh run-stack-d-test.sh run-stack-i-test.sh
 
 if !BIARCH
 export ELFUTILS_DISABLE_BIARCH = 1
@@ -265,7 +265,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
             run-backtrace-demangle.sh testfile-backtrace-demangle.bz2 \
             testfile-backtrace-demangle.cc \
             testfile-backtrace-demangle.core.bz2 \
-            run-stack-d-test.sh \
+            run-stack-d-test.sh run-stack-i-test.sh \
             testfiledwarfinlines.bz2 testfiledwarfinlines.core.bz2
 
 if USE_VALGRIND
diff --git a/tests/run-stack-i-test.sh b/tests/run-stack-i-test.sh
new file mode 100755 (executable)
index 0000000..2d09ec0
--- /dev/null
@@ -0,0 +1,70 @@
+#! /bin/sh
+# Copyright (C) 2014 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
+
+# See run-stack-d-test.sh for dwarfinlines.cpp source.
+testfiles testfiledwarfinlines testfiledwarfinlines.core
+
+# Depending on whether we are running make check or make installcheck
+# the actual binary name under test might be different. It is used in
+# the error message, which we also try to match.
+if test "$elfutils_testrun" = "installed"; then
+STACKCMD=${bindir}/`program_transform stack`
+else
+STACKCMD=${abs_top_builddir}/src/stack
+fi
+
+# Compare with run-stack-d-test.sh to see the output without --inlines.
+# Only two call frames are visible (there is a jump from main to fu or
+# fubar).
+
+# With --inlines we get all inlined calls. Note they share the same
+# address.
+testrun_compare ${abs_top_builddir}/src/stack -n 6 -i -e testfiledwarfinlines --core testfiledwarfinlines.core<<EOF
+PID 13654 - core
+TID 13654:
+#0  0x00000000004006c8 fubar
+#1  0x00000000004006c8 foobar
+#2  0x00000000004006c8 bar
+#3  0x00000000004006c8 foo
+#4  0x00000000004006c8 fu(int)
+#5  0x00000000004004c5 main
+$STACKCMD: tid 13654: shown max number of frames (6, use -n 0 for unlimited)
+EOF
+
+# With --source we can also see where in the source the inlined frames
+# where originally called from.
+testrun_compare ${abs_top_builddir}/src/stack -n 6 -s -i -e testfiledwarfinlines --core testfiledwarfinlines.core<<EOF
+PID 13654 - core
+TID 13654:
+#0  0x00000000004006c8 fubar
+    /home/mark/src/tests/dwarfinlines.cpp:6
+#1  0x00000000004006c8 foobar
+    /home/mark/src/tests/dwarfinlines.cpp:14
+#2  0x00000000004006c8 bar
+    /home/mark/src/tests/dwarfinlines.cpp:21
+#3  0x00000000004006c8 foo
+    /home/mark/src/tests/dwarfinlines.cpp:27
+#4  0x00000000004006c8 fu(int)
+    /home/mark/src/tests/dwarfinlines.cpp:33
+#5  0x00000000004004c5 main
+    /home/mark/src/tests/dwarfinlines.cpp:39
+$STACKCMD: tid 13654: shown max number of frames (6, use -n 0 for unlimited)
+EOF
+
+exit 0