summaryrefslogtreecommitdiff
path: root/src/stack.c
diff options
context:
space:
mode:
authorMark Wielaard <mjw@redhat.com>2014-01-21 16:13:49 +0100
committerMark Wielaard <mjw@redhat.com>2014-01-27 16:07:54 +0100
commit13968d9aa9990d53999b14494ed55c2d68d4ead5 (patch)
tree094ddb18e2ed671a1ad1c59d6bbe526e28938aee /src/stack.c
parent8520f70163866b64f8e9822972dd9c26151455ea (diff)
downloadelfutils-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.c254
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,