diff options
author | Mark Wielaard <[email protected]> | 2014-01-21 16:13:49 +0100 |
---|---|---|
committer | Mark Wielaard <[email protected]> | 2014-01-27 16:07:54 +0100 |
commit | 13968d9aa9990d53999b14494ed55c2d68d4ead5 (patch) | |
tree | 094ddb18e2ed671a1ad1c59d6bbe526e28938aee | |
parent | 8520f70163866b64f8e9822972dd9c26151455ea (diff) |
stack: Add -i, --inlines. Show inlined call frames using DWARF debuginfo.
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 <[email protected]>
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | src/ChangeLog | 11 | ||||
-rw-r--r-- | src/stack.c | 254 | ||||
-rw-r--r-- | tests/ChangeLog | 6 | ||||
-rw-r--r-- | tests/Makefile.am | 4 | ||||
-rwxr-xr-x | tests/run-stack-i-test.sh | 70 |
7 files changed, 271 insertions, 79 deletions
@@ -1,3 +1,7 @@ +2014-01-21 Mark Wielaard <[email protected]> + + * NEWS (Version 0.159): Add stack -i. + 2014-01-20 Mark Wielaard <[email protected]> * NEWS (Version 0.159): New. Add stack -d. @@ -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 diff --git a/src/ChangeLog b/src/ChangeLog index d1b221f2..26a607d8 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,14 @@ +2014-01-21 Mark Wielaard <[email protected]> + + * 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 <[email protected]> * stack.c (maxframes): Initialize to 256. diff --git a/src/stack.c b/src/stack.c index e675267d..c277dfd0 100644 --- a/src/stack.c +++ b/src/stack.c @@ -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; @@ -212,13 +213,159 @@ die_name (Dwarf_Die *die) } 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) { if (frames->frames > 0) 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, diff --git a/tests/ChangeLog b/tests/ChangeLog index 70b05510..aa468c44 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,9 @@ +2014-01-21 Mark Wielaard <[email protected]> + + * Makefile.am (TESTS): Add run-stack-i-test.sh. + (EXTRA_DIST): Likewise. + * run-stack-i-test.sh: New test. + 2014-01-20 Mark Wielaard <[email protected]> * Makefile.am (TESTS): Add run-stack-d-test.sh. diff --git a/tests/Makefile.am b/tests/Makefile.am index 35f6e87e..aa13cfa3 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 index 00000000..2d09ec0d --- /dev/null +++ b/tests/run-stack-i-test.sh @@ -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 |