loc2c-test.c - systemtap
Data types defined
Functions defined
Macros defined
Source code
#include <config.h>
#include <assert.h>
#include <inttypes.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdio_ext.h>
#include <stdlib.h>
#include <string.h>
#include <error.h>
#if ENABLE_NLS
#include <locale.h>
#endif
#include <argp.h>
#include <elfutils/libdwfl.h>
#include <elfutils/version.h>
#include <dwarf.h>
#include <obstack.h>
#include <unistd.h>
#include <stdarg.h>
#include "loc2c.h"
#define _(msg) msg
struct dwfunc
{
Dwfl *dwfl;
Dwarf_Die *cu;
Dwarf_Addr dwbias;
char * funcname;
uintmax_t pc;
bool matchdebug;
};
static void __attribute__ ((noreturn, format (printf, 2, 3)))
fail (void *arg __attribute__ ((unused)), const char *fmt, ...)
{
va_list ap;
fprintf (stderr, "%s: ", program_invocation_short_name);
va_start (ap, fmt);
vfprintf (stderr, _(fmt), ap);
va_end (ap);
fprintf (stderr, "\n");
exit (2);
}
static const Dwarf_Op *
get_location (Dwarf_Addr dwbias, Dwarf_Addr pc, Dwarf_Attribute *loc_attr,
size_t *len)
{
Dwarf_Op *expr;
switch (dwarf_getlocation_addr (loc_attr, pc - dwbias, &expr, len, 1))
{
case 1: if (*len == 0)
goto inaccessible;
break;
default: case -1:
fail (NULL, _("dwarf_getlocation_addr (form %#x): %s"),
dwarf_whatform (loc_attr), dwarf_errmsg (-1));
return NULL;
case 0: inaccessible:
fail (NULL, _("not accessible at this address"));
return NULL;
}
return expr;
}
#define obstack_chunk_alloc malloc
#define obstack_chunk_free free
static void
handle_fields (struct obstack *pool,
struct location *head, struct location *tail,
Dwarf_Addr cubias, Dwarf_Die *vardie, Dwarf_Addr pc,
char **fields)
{
Dwarf_Attribute attr_mem;
if (dwarf_attr_integrate (vardie, DW_AT_type, &attr_mem) == NULL)
error (2, 0, _("cannot get type of variable: %s"),
dwarf_errmsg (-1));
bool store = false;
Dwarf_Die die_mem, *die = vardie;
while (*fields != NULL)
{
if (!strcmp (*fields, "="))
{
store = true;
if (fields[1] != NULL)
error (2, 0, _("extra fields after ="));
break;
}
die = dwarf_formref_die (&attr_mem, &die_mem);
const int typetag = dwarf_tag (die);
switch (typetag)
{
case DW_TAG_typedef:
case DW_TAG_const_type:
case DW_TAG_volatile_type:
case DW_TAG_restrict_type:
break;
case DW_TAG_pointer_type:
if (**fields == '+')
goto subscript;
if (**fields == '\0')
++fields;
c_translate_pointer (pool, 1, cubias, die, &tail);
break;
case DW_TAG_array_type:
if (**fields == '+')
{
subscript:;
char *endp = *fields + 1;
uintmax_t idx = strtoumax (*fields + 1, &endp, 0);
if (endp == NULL || endp == *fields || *endp != '\0')
c_translate_array (pool, 1, cubias, die, &tail,
*fields + 1, 0);
else
c_translate_array (pool, 1, cubias, die, &tail,
NULL, idx);
++fields;
}
else
error (2, 0, _("bad field for array type: %s"), *fields);
break;
case DW_TAG_structure_type:
case DW_TAG_union_type:
switch (dwarf_child (die, &die_mem))
{
case 1: error (2, 0, _("empty struct %s"),
dwarf_diename (die) ?: "<anonymous>");
break;
case -1: default: error (2, 0, _("%s %s: %s"),
typetag == DW_TAG_union_type ? "union" : "struct",
dwarf_diename (die) ?: "<anonymous>",
dwarf_errmsg (-1));
break;
case 0:
break;
}
while (dwarf_tag (die) != DW_TAG_member
|| ({ const char *member = dwarf_diename (die);
member == NULL || strcmp (member, *fields); }))
if (dwarf_siblingof (die, &die_mem) != 0)
error (2, 0, _("field name %s not found"), *fields);
if (dwarf_attr_integrate (die, DW_AT_data_member_location,
&attr_mem) == NULL)
{
if (typetag != DW_TAG_union_type)
error (2, 0, _("no location for field %s: %s"),
*fields, dwarf_errmsg (-1));
}
else
{
size_t locexpr_len = 0;
const Dwarf_Op *locexpr;
locexpr = get_location (cubias, pc, &attr_mem, &locexpr_len);
c_translate_location (pool, NULL, NULL, NULL,
1, cubias, pc, &attr_mem,
locexpr, locexpr_len,
&tail, NULL, NULL);
}
++fields;
break;
case DW_TAG_base_type:
error (2, 0, _("field %s vs base type %s"),
*fields, dwarf_diename (die) ?: "<anonymous type>");
break;
case -1:
error (2, 0, _("cannot find type: %s"), dwarf_errmsg (-1));
break;
default:
error (2, 0, _("%s: unexpected type tag %#x"),
dwarf_diename (die) ?: "<anonymous type>",
dwarf_tag (die));
break;
}
if (dwarf_attr_integrate (die, DW_AT_type, &attr_mem) == NULL)
error (2, 0, _("cannot get type of field: %s"), dwarf_errmsg (-1));
}
Dwarf_Die typedie_mem;
Dwarf_Die *typedie;
int typetag;
while (1)
{
typedie = dwarf_formref_die (&attr_mem, &typedie_mem);
if (typedie == NULL)
error (2, 0, _("cannot get type of field: %s"), dwarf_errmsg (-1));
typetag = dwarf_tag (typedie);
if (typetag != DW_TAG_typedef &&
typetag != DW_TAG_const_type &&
typetag != DW_TAG_volatile_type &&
typetag != DW_TAG_restrict_type)
break;
if (dwarf_attr_integrate (typedie, DW_AT_type, &attr_mem) == NULL)
error (2, 0, _("cannot get type of field: %s"), dwarf_errmsg (-1));
}
switch (typetag)
{
case DW_TAG_base_type:
if (store)
c_translate_store (pool, 1, cubias, die, typedie, &tail, "value");
else
c_translate_fetch (pool, 1, cubias, die, typedie, &tail, "value");
break;
case DW_TAG_pointer_type:
case DW_TAG_reference_type:
if (store)
error (2, 0, _("store not supported for pointer type"));
c_translate_pointer (pool, 1, cubias, typedie, &tail);
c_translate_addressof (pool, 1, cubias, die, typedie, &tail, "value");
break;
default:
if (store)
error (2, 0, _("store supported only for base type"));
else
error (2, 0, _("fetch supported only for base type or pointer"));
break;
}
printf ("#define PROBEADDR %#" PRIx64 "ULL\n", pc);
puts (store
? "static void set_value(struct pt_regs *regs, intptr_t value)\n{"
: "static void print_value(struct pt_regs *regs)\n"
"{\n"
" intptr_t value;");
unsigned int stack_depth;
bool deref = c_emit_location (stdout, head, 1, &stack_depth);
obstack_free (pool, NULL);
printf (" /* max expression stack depth %u */\n", stack_depth);
puts (store ? " return;" :
" printk (\" ---> %ld\\n\", (unsigned long) value);\n"
" return;");
if (deref)
puts ("\n"
" deref_fault:\n"
" printk (\" => BAD ACCESS\\n\");");
puts ("}");
}
static void
handle_variable (Dwarf_Die *lscopes, int lnscopes, int out,
Dwarf_Addr cubias, Dwarf_Die *vardie, Dwarf_Addr pc,
Dwarf_Op *cfa_ops, char **fields)
{
struct obstack pool;
obstack_init (&pool);
XXX Dwarf_Attribute fb_attr_mem, *fb_attr = NULL;
int inner;
Dwarf_Die *scopes = lscopes;
int nscopes = lnscopes;
for (inner = out; inner < nscopes && fb_attr == NULL; ++inner)
{
switch (dwarf_tag (&scopes[inner]))
{
default:
continue;
case DW_TAG_subprogram:
case DW_TAG_entry_point:
fb_attr = dwarf_attr_integrate (&scopes[inner],
DW_AT_frame_base,
&fb_attr_mem);
break;
case DW_TAG_inlined_subroutine:
if (out != -1)
{
nscopes = dwarf_getscopes_die (&scopes[inner], &scopes);
if (nscopes == -1)
error (2, 0, _("cannot get die scopes inlined_subroutine: %s"),
dwarf_errmsg (-1));
inner = 0; out = -1;
}
break;
}
}
struct location *head, *tail = NULL;
Dwarf_Attribute attr_mem;
if (dwarf_attr_integrate (vardie, DW_AT_const_value, &attr_mem) != NULL)
head = tail = c_translate_constant (&pool, &fail, NULL, NULL,
1, cubias, &attr_mem);
else
{
if (dwarf_attr_integrate (vardie, DW_AT_location, &attr_mem) == NULL)
error (2, 0, _("cannot get location of variable: %s"),
dwarf_errmsg (-1));
size_t locexpr_len = 0;
const Dwarf_Op *locexpr = get_location (cubias, pc,
&attr_mem, &locexpr_len);
head = c_translate_location (&pool, &fail, NULL, NULL,
1, cubias, pc, &attr_mem,
locexpr, locexpr_len,
&tail, fb_attr, cfa_ops);
}
handle_fields (&pool, head, tail, cubias, vardie, pc, fields);
}
static void
paddr (const char *prefix, Dwarf_Addr addr, Dwfl_Line *line)
{
const char *src;
int lineno, linecol;
if (line != NULL
&& (src = dwfl_lineinfo (line, &addr, &lineno, &linecol,
NULL, NULL)) != NULL)
{
if (linecol != 0)
printf ("%s%#" PRIx64 " (%s:%d:%d)",
prefix, addr, src, lineno, linecol);
else
printf ("%s%#" PRIx64 " (%s:%d)",
prefix, addr, src, lineno);
}
else
printf ("%s%#" PRIx64, prefix, addr);
}
static void
print_type (Dwarf_Die *typedie, char space)
{
if (typedie == NULL)
printf ("%c<no type>", space);
else
{
const char *name = dwarf_diename (typedie);
if (name != NULL)
printf ("%c%s", space, name);
else
{
Dwarf_Attribute attr_mem;
Dwarf_Die die_mem;
Dwarf_Die *die = dwarf_formref_die
(dwarf_attr_integrate (typedie, DW_AT_type, &attr_mem), &die_mem);
int tag = dwarf_tag (typedie);
switch (tag)
{
case DW_TAG_pointer_type:
print_type (die, space);
putchar ('*');
break;
case DW_TAG_array_type:
print_type (die, space);
printf ("[]");
break;
case DW_TAG_const_type:
print_type (die, space);
printf (" const");
break;
case DW_TAG_volatile_type:
print_type (die, space);
printf (" volatile");
break;
case DW_TAG_restrict_type:
print_type (die, space);
printf (" restrict");
break;
default:
printf ("%c<unknown %#x>", space, tag);
break;
}
}
}
}
static void
print_vars (unsigned int indent, Dwarf_Die *die)
{
Dwarf_Die child;
Dwarf_Attribute attr_mem;
Dwarf_Die typedie_mem;
Dwarf_Die *typedie;
if (dwarf_child (die, &child) == 0)
do
switch (dwarf_tag (&child))
{
case DW_TAG_variable:
case DW_TAG_formal_parameter:
printf ("%*s%-30s[%6" PRIx64 "]", indent, "",
dwarf_diename (&child),
(uint64_t) dwarf_dieoffset (&child));
typedie = dwarf_formref_die
(dwarf_attr_integrate (&child, DW_AT_type, &attr_mem),
&typedie_mem);
print_type (typedie, '\t');
puts ("");
break;
default:
break;
}
while (dwarf_siblingof (&child, &child) == 0);
}
static int
handle_function (Dwarf_Die *funcdie, void *arg)
{
struct dwfunc *a = arg;
Dwarf_Addr entrypc;
int result;
int i = 0;
Dwarf_Addr *bkpts = NULL;
if (arg == NULL)
error (2, 0, "Error, dwfl structure empty");
const char *name = dwarf_diename (funcdie);
if (name == NULL)
error (2, 0, "Error, dwarf_diename returned NULL from Dwarf_die");
if (dwarf_entrypc (funcdie, &entrypc) == 0)
{
entrypc += a->dwbias;
result = dwarf_entry_breakpoints (funcdie, &bkpts);
if (a->funcname == NULL)
{
printf ("%-35s %#.16" PRIx64, name, entrypc);
if (result > 0)
{
for (i = 0; i < result; i++)
printf (" %#.16" PRIx64 "%s", bkpts[i] + a->dwbias,
i == result - 1 ? "\n" : "");
free (bkpts);
}
}
else if (a->funcname != NULL && (!strcmp (a->funcname, name)))
{
if (!a->matchdebug)
{
entrypc += a->dwbias;
a->pc = entrypc;
free (bkpts);
return DWARF_CB_ABORT;
}
else if (result == 1 && a->matchdebug)
{
a->pc = (bkpts[0] + a->dwbias);
free (bkpts);
return DWARF_CB_ABORT;
}
else if (result > 1 && a->matchdebug)
{
printf ("Function: %-35s has multiple breakpoints: ", name);
for (i = 0; i < result; i++)
printf (" %#.16" PRIx64 "%s", bkpts[i] + a->dwbias,
i == result - 1 ? "\n" : "");
free (bkpts);
return DWARF_CB_ABORT;
}
else
{
error (2, 0, "Error, dwarf_entry_breakpoints returned an error( %s )\n",
dwarf_errmsg (result));
}
}
}
return 0;
}
#define INDENT 4
int
main (int argc, char **argv)
{
(void) __fsetlocking (stdout, FSETLOCKING_BYCALLER);
#if ENABLE_NLS
(void) setlocale (LC_ALL, "");
#endif
const struct argp_child argp_children[] =
{
{ .argp = dwfl_standard_argp () },
{ .argp = NULL }
};
const struct argp argp =
{
.children = argp_children,
.args_doc = "ADDRESS\n\
FUNCTIONNAME\n\
ADDRESS VARIABLE [FIELD...]\n\
FUNCTIONNAME VARIABLE [FIELD...]\n\
ADDRESS VARIABLE [FIELD...] =",
.doc = "Process DWARF locations visible with PC at ADDRESS.\v\
ADDRESS must be in %i format (i.e. \"0x...\").\n\
FUNCTIONNAME must be exact or prepended with '@' character to specify \
breakpoint entry.\n\
In the first and second form, display the scope entries containing \
ADDRESS or FUNCTIONNAME, and the variables/parameters visible there, \
with their type names. Hex numbers within \"[...]\" are DIE offsets.\n\
In the third and fourth form, emit a C code fragment to access a \
variable. Each FIELD argument is the name of a member of an aggregate \
type (or pointer to one);\
\"+INDEX\" to index into an array type (or offset a pointer type);\
or \"\" (an empty argument) to dereference a pointer type.\n\
In the fifth form, the access is a store rather than a fetch."
};
Dwfl *dwfl = NULL;
int argi;
(void) argp_parse (&argp, argc, argv, 0, &argi, &dwfl);
assert (dwfl != NULL);
struct dwfunc a = { .dwfl = dwfl, .funcname = NULL,
.pc = 0, .matchdebug = 0 };
if (argi == argc)
{
printf ("%-35s %-18s %s\n",
"Function", "Address", "Debug Entry Address(s)");
while ((a.cu = dwfl_nextcu(a.dwfl, a.cu, &a.dwbias)) != NULL)
dwarf_getfuncs (a.cu, &handle_function, &a.dwfl, 0);
return 0;
}
char *endp;
uintmax_t pc = strtoumax (argv[argi], &endp, 0);
if (endp == argv[argi])
{
a.funcname = argv[argi];
if (a.funcname[0] == '@')
{
a.funcname++;
a.matchdebug = 1;
}
while ((a.cu = dwfl_nextcu(a.dwfl, a.cu, &a.dwbias)) != NULL)
dwarf_getfuncs (a.cu, &handle_function, &a.dwfl, 0);
if (!a.pc)
error (EXIT_FAILURE, 0, "function: '%s' not found\n", argv[argi]);
pc = a.pc;
}
Dwarf_Addr cubias;
Dwarf_Die *cudie = dwfl_addrdie (dwfl, pc, &cubias);
if (cudie == NULL)
error (EXIT_FAILURE, 0, "dwfl_addrdie: %s", dwfl_errmsg (-1));
Dwarf_Die *scopes;
int n = dwarf_getscopes (cudie, pc - cubias, &scopes);
if (n < 0)
error (EXIT_FAILURE, 0, "dwarf_getscopes: %s", dwarf_errmsg (-1));
else if (n == 0)
error (EXIT_FAILURE, 0, "%#" PRIx64 ": not in any scope\n", pc);
if (++argi == argc)
{
unsigned int indent = 0;
while (n-- > 0)
{
Dwarf_Die *const die = &scopes[n];
indent += INDENT;
printf ("%*s[%6" PRIx64 "] %s (%#x)", indent, "",
dwarf_dieoffset (die),
dwarf_diename (die) ?: "<unnamed>",
dwarf_tag (die));
Dwarf_Addr lowpc, highpc;
if (dwarf_lowpc (die, &lowpc) == 0
&& dwarf_highpc (die, &highpc) == 0)
{
lowpc += cubias;
highpc += cubias;
Dwfl_Line *loline = dwfl_getsrc (dwfl, lowpc);
Dwfl_Line *hiline = dwfl_getsrc (dwfl, highpc);
paddr (": ", lowpc, loline);
if (highpc != lowpc)
paddr (" .. ", lowpc, hiline == loline ? NULL : hiline);
}
puts ("");
print_vars (indent + INDENT, die);
}
}
else
{
char *spec = argv[argi++];
if (!strcmp (spec, "return"))
{
int i;
for (i = 0; i < n; ++i)
if (dwarf_tag (&scopes[i]) == DW_TAG_subprogram)
{
const Dwarf_Op *locexpr;
int locexpr_len = dwfl_module_return_value_location
(dwfl_addrmodule (dwfl, pc), &scopes[i], &locexpr);
if (locexpr_len < 0)
error (EXIT_FAILURE, 0,
"dwfl_module_return_value_location: %s",
dwfl_errmsg (-1));
struct obstack pool;
obstack_init (&pool);
struct location *head, *tail = NULL;
head = c_translate_location (&pool, &fail, NULL, NULL,
1, cubias, pc, NULL,
locexpr, locexpr_len,
&tail, NULL, NULL);
handle_fields (&pool, head, tail, cubias, &scopes[i], pc,
&argv[argi]);
free (scopes);
dwfl_end (dwfl);
return 0;
}
error (EXIT_FAILURE, 0, "no subprogram to have a return value!");
}
int lineno = 0, colno = 0, shadow = 0;
char *at = strchr (spec, '@');
if (at != NULL)
{
*at++ = '\0';
if (sscanf (at, "%*[^:]:%i:%i", &lineno, &colno) < 1)
lineno = 0;
}
else
{
int len;
if (sscanf (spec, "%*[^+]%n+%i", &len, &shadow) == 2)
spec[len] = '\0';
}
Dwarf_Die vardie;
int out = dwarf_getscopevar (scopes, n, spec, shadow, at, lineno, colno,
&vardie);
if (out == -2)
error (0, 0, "no match for %s (+%d, %s:%d:%d)",
spec, shadow, at, lineno, colno);
else if (out < 0)
error (0, 0, "dwarf_getscopevar: %s (+%d, %s:%d:%d): %s",
spec, shadow, at, lineno, colno, dwarf_errmsg (-1));
else
{
Dwarf_Op *cfa_ops = NULL;
size_t cfa_nops;
Dwarf_Addr bias;
Dwfl_Module *module = dwfl_addrmodule (dwfl, pc);
if (module != NULL)
{
Dwarf_CFI *cfi = dwfl_module_dwarf_cfi (module, &bias);
if (cfi != NULL)
{
Dwarf_Frame *frame = NULL;
if (dwarf_cfi_addrframe (cfi, pc - bias, &frame) == 0)
dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops);
}
if (cfa_ops == NULL)
{
cfi = dwfl_module_eh_cfi (module, &bias);
if (cfi != NULL)
{
Dwarf_Frame *frame = NULL;
if (dwarf_cfi_addrframe (cfi, pc - bias, &frame) == 0)
dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops);
}
}
}
handle_variable (scopes, n, out, cubias, &vardie, pc, cfa_ops,
&argv[argi]);
}
}
free (scopes);
dwfl_end (dwfl);
return 0;
}