From: Mark Wielaard Date: Tue, 21 Jan 2014 15:13:49 +0000 (+0100) Subject: stack: Add -i, --inlines. Show inlined call frames using DWARF debuginfo. X-Git-Tag: elfutils-0.159~44 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=13968d9aa9990d53999b14494ed55c2d68d4ead5;p=thirdparty%2Felfutils.git 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 --- diff --git a/ChangeLog b/ChangeLog index ad8cd0336..a2436ceed 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2014-01-21 Mark Wielaard + + * NEWS (Version 0.159): Add stack -i. + 2014-01-20 Mark Wielaard * NEWS (Version 0.159): New. Add stack -d. diff --git a/NEWS b/NEWS index 5c865e0bd..b774ec4e3 100644 --- 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 diff --git a/src/ChangeLog b/src/ChangeLog index d1b221f20..26a607d8b 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,14 @@ +2014-01-21 Mark Wielaard + + * 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 * stack.c (maxframes): Initialize to 256. diff --git a/src/stack.c b/src/stack.c index e675267dd..c277dfd03 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; @@ -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, diff --git a/tests/ChangeLog b/tests/ChangeLog index 70b055104..aa468c442 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,9 @@ +2014-01-21 Mark Wielaard + + * Makefile.am (TESTS): Add run-stack-i-test.sh. + (EXTRA_DIST): Likewise. + * run-stack-i-test.sh: New test. + 2014-01-20 Mark Wielaard * Makefile.am (TESTS): Add run-stack-d-test.sh. diff --git a/tests/Makefile.am b/tests/Makefile.am index 35f6e87e8..aa13cfa35 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 000000000..2d09ec0d7 --- /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 . + +. $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<