diff options
author | Mark Wielaard <mjw@redhat.com> | 2014-01-21 16:13:49 +0100 |
---|---|---|
committer | Mark Wielaard <mjw@redhat.com> | 2014-01-27 16:07:54 +0100 |
commit | 13968d9aa9990d53999b14494ed55c2d68d4ead5 (patch) | |
tree | 094ddb18e2ed671a1ad1c59d6bbe526e28938aee /src/stack.c | |
parent | 8520f70163866b64f8e9822972dd9c26151455ea (diff) | |
download | elfutils-13968d9aa9990d53999b14494ed55c2d68d4ead5.tar.gz |
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 <mjw@redhat.com>
Diffstat (limited to 'src/stack.c')
-rw-r--r-- | src/stack.c | 254 |
1 files changed, 177 insertions, 77 deletions
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, |