/* Locate source files and line information for given addresses
- Copyright (C) 2005-2010, 2012, 2013 Red Hat, Inc.
+ Copyright (C) 2005-2010, 2012, 2013, 2015 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper@redhat.com>, 2005.
#include <dwarf.h>
#include <libintl.h>
#include <locale.h>
-#include <mcheck.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdio_ext.h>
/* Values for the parameters which have no short form. */
#define OPT_DEMANGLER 0x100
+#define OPT_PRETTY 0x101 /* 'p' is already used to select the process. */
/* Definitions of arguments for argp functions. */
static const struct argp_option options[] =
{
- { NULL, 0, NULL, 0, N_("Output selection options:"), 2 },
+ { NULL, 0, NULL, 0, N_("Input format options:"), 2 },
+ { "section", 'j', "NAME", 0,
+ N_("Treat addresses as offsets relative to NAME section."), 0 },
+
+ { NULL, 0, NULL, 0, N_("Output format options:"), 3 },
+ { "addresses", 'a', NULL, 0, N_("Print address before each entry"), 0 },
{ "basenames", 's', NULL, 0, N_("Show only base names of source files"), 0 },
{ "absolute", 'A', NULL, 0,
N_("Show absolute file names using compilation directory"), 0 },
{ "symbols", 'S', NULL, 0, N_("Also show symbol or section names"), 0 },
{ "symbols-sections", 'x', NULL, 0, N_("Also show symbol and the section names"), 0 },
{ "flags", 'F', NULL, 0, N_("Also show line table flags"), 0 },
- { "section", 'j', "NAME", 0,
- N_("Treat addresses as offsets relative to NAME section."), 0 },
{ "inlines", 'i', NULL, 0,
N_("Show all source locations that caused inline expansion of subroutines at the address."),
0 },
+ { "demangle", 'C', "ARG", OPTION_ARG_OPTIONAL,
+ N_("Show demangled symbols (ARG is always ignored)"), 0 },
+ { "pretty-print", OPT_PRETTY, NULL, 0,
+ N_("Print all information on one line, and indent inlines"), 0 },
{ NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
/* Unsupported options. */
{ "target", 'b', "ARG", OPTION_HIDDEN, NULL, 0 },
- { "demangle", 'C', "ARG", OPTION_HIDDEN | OPTION_ARG_OPTIONAL, NULL, 0 },
{ "demangler", OPT_DEMANGLER, "ARG", OPTION_HIDDEN, NULL, 0 },
{ NULL, 0, NULL, 0, NULL, 0 }
};
/* Handle ADDR. */
static int handle_address (const char *addr, Dwfl *dwfl);
+/* True when we should print the address for each entry. */
+static bool print_addresses;
/* True if only base names of files should be shown. */
static bool only_basenames;
/* True if all inlined subroutines of the current address should be shown. */
static bool show_inlines;
+/* True if all names need to be demangled. */
+static bool demangle;
+
+/* True if all information should be printed on one line. */
+static bool pretty;
+
+#ifdef USE_DEMANGLE
+static size_t demangle_buffer_len = 0;
+static char *demangle_buffer = NULL;
+#endif
int
main (int argc, char *argv[])
int remaining;
int result = 0;
- /* Make memory leak detection possible. */
- mtrace ();
-
/* We use no threads here which can interfere with handling a stream. */
(void) __fsetlocking (stdout, FSETLOCKING_BYCALLER);
}
dwfl_end (dwfl);
+
+#ifdef USE_DEMANGLE
+ free (demangle_buffer);
+#endif
+
return result;
}
state->child_inputs[0] = state->input;
break;
+ case 'a':
+ print_addresses = true;
+ break;
+
case 'b':
case 'C':
case OPT_DEMANGLER:
- /* Ignored for compatibility. */
+ demangle = true;
break;
case 's':
show_inlines = true;
break;
+ case OPT_PRETTY:
+ pretty = true;
+ break;
+
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
+static const char *
+symname (const char *name)
+{
+#ifdef USE_DEMANGLE
+ // Require GNU v3 ABI by the "_Z" prefix.
+ if (demangle && name[0] == '_' && name[1] == 'Z')
+ {
+ int status = -1;
+ char *dsymname = __cxa_demangle (name, demangle_buffer,
+ &demangle_buffer_len, &status);
+ if (status == 0)
+ name = demangle_buffer = dsymname;
+ }
+#endif
+ return name;
+}
+
+static const char *
+get_diename (Dwarf_Die *die)
+{
+ Dwarf_Attribute attr;
+ const char *name;
+
+ name = dwarf_formstring (dwarf_attr_integrate (die, DW_AT_MIPS_linkage_name,
+ &attr)
+ ?: dwarf_attr_integrate (die, DW_AT_linkage_name,
+ &attr));
+
+ if (name == NULL)
+ name = dwarf_diename (die) ?: "??";
+
+ return name;
+}
static bool
print_dwarf_function (Dwfl_Module *mod, Dwarf_Addr addr)
if (nscopes <= 0)
return false;
+ bool res = false;
for (int i = 0; i < nscopes; ++i)
switch (dwarf_tag (&scopes[i]))
{
case DW_TAG_subprogram:
{
- const char *name = dwarf_diename (&scopes[i]);
+ const char *name = get_diename (&scopes[i]);
if (name == NULL)
- return false;
- puts (name);
- return true;
+ goto done;
+ printf ("%s%c", symname (name), pretty ? ' ' : '\n');
+ res = true;
+ goto done;
}
case DW_TAG_inlined_subroutine:
{
- const char *name = dwarf_diename (&scopes[i]);
+ const char *name = get_diename (&scopes[i]);
if (name == NULL)
- return false;
- printf ("%s inlined", name);
+ goto done;
+
+ /* When using --pretty-print we only show inlines on their
+ own line. Just print the first subroutine name. */
+ if (pretty)
+ {
+ printf ("%s ", symname (name));
+ res = true;
+ goto done;
+ }
+ else
+ printf ("%s inlined", symname (name));
Dwarf_Files *files;
if (dwarf_getsrcfiles (cudie, &files, NULL) == 0)
}
}
- return false;
+done:
+ free (scopes);
+ return res;
}
static void
if (i >= 0)
name = dwfl_module_relocation_info (mod, i, NULL);
if (name == NULL)
- puts ("??");
+ printf ("??%c", pretty ? ' ': '\n');
else
- printf ("(%s)+%#" PRIx64 "\n", name, addr);
+ printf ("(%s)+%#" PRIx64 "%c", name, addr, pretty ? ' ' : '\n');
}
else
{
+ name = symname (name);
if (off == 0)
printf ("%s", name);
else
}
}
}
- puts ("");
+ printf ("%c", pretty ? ' ' : '\n');
}
}
-static void
-print_diesym (Dwarf_Die *die)
-{
- Dwarf_Attribute attr;
- const char *name;
-
- name = dwarf_formstring (dwarf_attr_integrate (die, DW_AT_MIPS_linkage_name,
- &attr)
- ?: dwarf_attr_integrate (die, DW_AT_linkage_name,
- &attr));
-
- if (name == NULL)
- name = dwarf_diename (die) ?: "??";
-
- puts (name);
-}
-
static int
see_one_module (Dwfl_Module *mod,
void **userdata __attribute__ ((unused)),
}
static int
+get_addr_width (Dwfl_Module *mod)
+{
+ // Try to find the address width if possible.
+ static int width = 0;
+ if (width == 0 && mod != NULL)
+ {
+ Dwarf_Addr bias;
+ Elf *elf = dwfl_module_getelf (mod, &bias);
+ if (elf != NULL)
+ {
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ if (ehdr != NULL)
+ width = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16;
+ }
+ }
+ if (width == 0)
+ width = 16;
+
+ return width;
+}
+
+static int
handle_address (const char *string, Dwfl *dwfl)
{
char *endp;
- uintmax_t addr = strtoumax (string, &endp, 0);
- if (endp == string)
+ uintmax_t addr = strtoumax (string, &endp, 16);
+ if (endp == string || *endp != '\0')
{
bool parsed = false;
int i, j;
Dwfl_Module *mod = dwfl_addrmodule (dwfl, addr);
+ if (print_addresses)
+ {
+ int width = get_addr_width (mod);
+ printf ("0x%.*" PRIx64 "%s", width, addr, pretty ? ": " : "\n");
+ }
+
if (show_functions)
{
/* First determine the function name. Use the DWARF information if
possible. */
if (! print_dwarf_function (mod, addr) && !show_symbols)
- puts (dwfl_module_addrname (mod, addr) ?: "??");
+ {
+ const char *name = dwfl_module_addrname (mod, addr);
+ name = name != NULL ? symname (name) : "??";
+ printf ("%s%c", name, pretty ? ' ' : '\n');
+ }
}
if (show_symbols)
print_addrsym (mod, addr);
+ if ((show_functions || show_symbols) && pretty)
+ printf ("at ");
+
Dwfl_Line *line = dwfl_module_getsrc (mod, addr);
const char *src;
dwarf_offdie (dwfl_module_getdwarf (mod, &bias),
dieoff, &subroutine);
free (scopes);
+ scopes = NULL;
nscopes = dwarf_getscopes_die (&subroutine, &scopes);
if (nscopes > 1)
if (dwarf_tag (die) != DW_TAG_inlined_subroutine)
continue;
+ if (pretty)
+ printf (" (inlined by) ");
+
if (show_functions)
- print_diesym (&scopes[i + 1]);
+ {
+ /* Search for the parent inline or function. It
+ might not be directly above this inline -- e.g.
+ there could be a lexical_block in between. */
+ for (int j = i + 1; j < nscopes; j++)
+ {
+ Dwarf_Die *parent = &scopes[j];
+ int tag = dwarf_tag (parent);
+ if (tag == DW_TAG_inlined_subroutine
+ || tag == DW_TAG_entry_point
+ || tag == DW_TAG_subprogram)
+ {
+ printf ("%s%s",
+ symname (get_diename (parent)),
+ pretty ? " at " : "\n");
+ break;
+ }
+ }
+ }
src = NULL;
lineno = 0;