perf probe: Show accessible local variables
Add -V (--vars) option for listing accessible local variables at given probe point. This will help finding which local variables are available for event arguments. e.g.) # perf probe -V call_timer_fn:23 Available variables at call_timer_fn:23 @<run_timer_softirq+345> function_type* fn int preempt_count long unsigned int data struct list_head work_list struct list_head* head struct timer_list* timer struct tvec_base* base Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> Cc: Ingo Molnar <mingo@elte.hu> Cc: Frederic Weisbecker <fweisbec@gmail.com> LKML-Reference: <20101021101323.3542.40282.stgit@ltc236.sdl.hitachi.co.jp> Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
632941c4f8
commit
cf6eb489e5
6 changed files with 480 additions and 105 deletions
|
@ -17,6 +17,8 @@ or
|
||||||
'perf probe' --list
|
'perf probe' --list
|
||||||
or
|
or
|
||||||
'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
|
'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
|
||||||
|
or
|
||||||
|
'perf probe' --vars='PROBEPOINT'
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
@ -57,6 +59,11 @@ OPTIONS
|
||||||
Show source code lines which can be probed. This needs an argument
|
Show source code lines which can be probed. This needs an argument
|
||||||
which specifies a range of the source code. (see LINE SYNTAX for detail)
|
which specifies a range of the source code. (see LINE SYNTAX for detail)
|
||||||
|
|
||||||
|
-V::
|
||||||
|
--vars=::
|
||||||
|
Show available local variables at given probe point. The argument
|
||||||
|
syntax is same as PROBE SYNTAX, but NO ARGs.
|
||||||
|
|
||||||
-f::
|
-f::
|
||||||
--force::
|
--force::
|
||||||
Forcibly add events with existing name.
|
Forcibly add events with existing name.
|
||||||
|
|
|
@ -50,6 +50,8 @@ static struct {
|
||||||
bool list_events;
|
bool list_events;
|
||||||
bool force_add;
|
bool force_add;
|
||||||
bool show_lines;
|
bool show_lines;
|
||||||
|
bool show_vars;
|
||||||
|
bool mod_events;
|
||||||
int nevents;
|
int nevents;
|
||||||
struct perf_probe_event events[MAX_PROBES];
|
struct perf_probe_event events[MAX_PROBES];
|
||||||
struct strlist *dellist;
|
struct strlist *dellist;
|
||||||
|
@ -57,7 +59,6 @@ static struct {
|
||||||
int max_probe_points;
|
int max_probe_points;
|
||||||
} params;
|
} params;
|
||||||
|
|
||||||
|
|
||||||
/* Parse an event definition. Note that any error must die. */
|
/* Parse an event definition. Note that any error must die. */
|
||||||
static int parse_probe_event(const char *str)
|
static int parse_probe_event(const char *str)
|
||||||
{
|
{
|
||||||
|
@ -92,6 +93,7 @@ static int parse_probe_event_argv(int argc, const char **argv)
|
||||||
len = 0;
|
len = 0;
|
||||||
for (i = 0; i < argc; i++)
|
for (i = 0; i < argc; i++)
|
||||||
len += sprintf(&buf[len], "%s ", argv[i]);
|
len += sprintf(&buf[len], "%s ", argv[i]);
|
||||||
|
params.mod_events = true;
|
||||||
ret = parse_probe_event(buf);
|
ret = parse_probe_event(buf);
|
||||||
free(buf);
|
free(buf);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -100,9 +102,10 @@ static int parse_probe_event_argv(int argc, const char **argv)
|
||||||
static int opt_add_probe_event(const struct option *opt __used,
|
static int opt_add_probe_event(const struct option *opt __used,
|
||||||
const char *str, int unset __used)
|
const char *str, int unset __used)
|
||||||
{
|
{
|
||||||
if (str)
|
if (str) {
|
||||||
|
params.mod_events = true;
|
||||||
return parse_probe_event(str);
|
return parse_probe_event(str);
|
||||||
else
|
} else
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,6 +113,7 @@ static int opt_del_probe_event(const struct option *opt __used,
|
||||||
const char *str, int unset __used)
|
const char *str, int unset __used)
|
||||||
{
|
{
|
||||||
if (str) {
|
if (str) {
|
||||||
|
params.mod_events = true;
|
||||||
if (!params.dellist)
|
if (!params.dellist)
|
||||||
params.dellist = strlist__new(true, NULL);
|
params.dellist = strlist__new(true, NULL);
|
||||||
strlist__add(params.dellist, str);
|
strlist__add(params.dellist, str);
|
||||||
|
@ -130,6 +134,25 @@ static int opt_show_lines(const struct option *opt __used,
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int opt_show_vars(const struct option *opt __used,
|
||||||
|
const char *str, int unset __used)
|
||||||
|
{
|
||||||
|
struct perf_probe_event *pev = ¶ms.events[params.nevents];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!str)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = parse_probe_event(str);
|
||||||
|
if (!ret && pev->nargs != 0) {
|
||||||
|
pr_err(" Error: '--vars' doesn't accept arguments.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
params.show_vars = true;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const char * const probe_usage[] = {
|
static const char * const probe_usage[] = {
|
||||||
|
@ -139,6 +162,7 @@ static const char * const probe_usage[] = {
|
||||||
"perf probe --list",
|
"perf probe --list",
|
||||||
#ifdef DWARF_SUPPORT
|
#ifdef DWARF_SUPPORT
|
||||||
"perf probe --line 'LINEDESC'",
|
"perf probe --line 'LINEDESC'",
|
||||||
|
"perf probe --vars 'PROBEPOINT'",
|
||||||
#endif
|
#endif
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
@ -180,6 +204,9 @@ static const struct option options[] = {
|
||||||
OPT_CALLBACK('L', "line", NULL,
|
OPT_CALLBACK('L', "line", NULL,
|
||||||
"FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]",
|
"FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]",
|
||||||
"Show source code lines.", opt_show_lines),
|
"Show source code lines.", opt_show_lines),
|
||||||
|
OPT_CALLBACK('V', "vars", NULL,
|
||||||
|
"FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT",
|
||||||
|
"Show accessible variables on PROBEDEF", opt_show_vars),
|
||||||
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
|
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
|
||||||
"file", "vmlinux pathname"),
|
"file", "vmlinux pathname"),
|
||||||
OPT_STRING('s', "source", &symbol_conf.source_prefix,
|
OPT_STRING('s', "source", &symbol_conf.source_prefix,
|
||||||
|
@ -217,7 +244,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
|
||||||
usage_with_options(probe_usage, options);
|
usage_with_options(probe_usage, options);
|
||||||
|
|
||||||
if (params.list_events) {
|
if (params.list_events) {
|
||||||
if (params.nevents != 0 || params.dellist) {
|
if (params.mod_events) {
|
||||||
pr_err(" Error: Don't use --list with --add/--del.\n");
|
pr_err(" Error: Don't use --list with --add/--del.\n");
|
||||||
usage_with_options(probe_usage, options);
|
usage_with_options(probe_usage, options);
|
||||||
}
|
}
|
||||||
|
@ -225,6 +252,10 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
|
||||||
pr_err(" Error: Don't use --list with --line.\n");
|
pr_err(" Error: Don't use --list with --line.\n");
|
||||||
usage_with_options(probe_usage, options);
|
usage_with_options(probe_usage, options);
|
||||||
}
|
}
|
||||||
|
if (params.show_vars) {
|
||||||
|
pr_err(" Error: Don't use --list with --vars.\n");
|
||||||
|
usage_with_options(probe_usage, options);
|
||||||
|
}
|
||||||
ret = show_perf_probe_events();
|
ret = show_perf_probe_events();
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
pr_err(" Error: Failed to show event list. (%d)\n",
|
pr_err(" Error: Failed to show event list. (%d)\n",
|
||||||
|
@ -234,9 +265,13 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
|
||||||
|
|
||||||
#ifdef DWARF_SUPPORT
|
#ifdef DWARF_SUPPORT
|
||||||
if (params.show_lines) {
|
if (params.show_lines) {
|
||||||
if (params.nevents != 0 || params.dellist) {
|
if (params.mod_events) {
|
||||||
pr_warning(" Error: Don't use --line with"
|
pr_err(" Error: Don't use --line with"
|
||||||
" --add/--del.\n");
|
" --add/--del.\n");
|
||||||
|
usage_with_options(probe_usage, options);
|
||||||
|
}
|
||||||
|
if (params.show_vars) {
|
||||||
|
pr_err(" Error: Don't use --line with --vars.\n");
|
||||||
usage_with_options(probe_usage, options);
|
usage_with_options(probe_usage, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,6 +280,18 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
|
||||||
pr_err(" Error: Failed to show lines. (%d)\n", ret);
|
pr_err(" Error: Failed to show lines. (%d)\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
if (params.show_vars) {
|
||||||
|
if (params.mod_events) {
|
||||||
|
pr_err(" Error: Don't use --vars with"
|
||||||
|
" --add/--del.\n");
|
||||||
|
usage_with_options(probe_usage, options);
|
||||||
|
}
|
||||||
|
ret = show_available_vars(params.events, params.nevents,
|
||||||
|
params.max_probe_points);
|
||||||
|
if (ret < 0)
|
||||||
|
pr_err(" Error: Failed to show vars. (%d)\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (params.dellist) {
|
if (params.dellist) {
|
||||||
|
|
|
@ -378,6 +378,72 @@ int show_line_range(struct line_range *lr)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int show_available_vars_at(int fd, struct perf_probe_event *pev,
|
||||||
|
int max_vls)
|
||||||
|
{
|
||||||
|
char *buf;
|
||||||
|
int ret, i;
|
||||||
|
struct str_node *node;
|
||||||
|
struct variable_list *vls = NULL, *vl;
|
||||||
|
|
||||||
|
buf = synthesize_perf_probe_point(&pev->point);
|
||||||
|
if (!buf)
|
||||||
|
return -EINVAL;
|
||||||
|
pr_debug("Searching variables at %s\n", buf);
|
||||||
|
|
||||||
|
ret = find_available_vars_at(fd, pev, &vls, max_vls);
|
||||||
|
if (ret > 0) {
|
||||||
|
/* Some variables were found */
|
||||||
|
fprintf(stdout, "Available variables at %s\n", buf);
|
||||||
|
for (i = 0; i < ret; i++) {
|
||||||
|
vl = &vls[i];
|
||||||
|
/*
|
||||||
|
* A probe point might be converted to
|
||||||
|
* several trace points.
|
||||||
|
*/
|
||||||
|
fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol,
|
||||||
|
vl->point.offset);
|
||||||
|
free(vl->point.symbol);
|
||||||
|
if (vl->vars) {
|
||||||
|
strlist__for_each(node, vl->vars)
|
||||||
|
fprintf(stdout, "\t\t%s\n", node->s);
|
||||||
|
strlist__delete(vl->vars);
|
||||||
|
} else
|
||||||
|
fprintf(stdout, "(No variables)\n");
|
||||||
|
}
|
||||||
|
free(vls);
|
||||||
|
} else
|
||||||
|
pr_err("Failed to find variables at %s (%d)\n", buf, ret);
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show available variables on given probe point */
|
||||||
|
int show_available_vars(struct perf_probe_event *pevs, int npevs,
|
||||||
|
int max_vls)
|
||||||
|
{
|
||||||
|
int i, fd, ret = 0;
|
||||||
|
|
||||||
|
ret = init_vmlinux();
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
fd = open_vmlinux();
|
||||||
|
if (fd < 0) {
|
||||||
|
pr_warning("Failed to open debuginfo file.\n");
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_pager();
|
||||||
|
|
||||||
|
for (i = 0; i < npevs && ret >= 0; i++)
|
||||||
|
ret = show_available_vars_at(fd, &pevs[i], max_vls);
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
#else /* !DWARF_SUPPORT */
|
#else /* !DWARF_SUPPORT */
|
||||||
|
|
||||||
static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
|
static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
|
||||||
|
@ -409,6 +475,12 @@ int show_line_range(struct line_range *lr __unused)
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int show_available_vars(struct perf_probe_event *pevs __unused,
|
||||||
|
int npevs __unused, int max_probe_points __unused)
|
||||||
|
{
|
||||||
|
pr_warning("Debuginfo-analysis is not supported.\n");
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int parse_line_range_desc(const char *arg, struct line_range *lr)
|
int parse_line_range_desc(const char *arg, struct line_range *lr)
|
||||||
|
|
|
@ -90,6 +90,12 @@ struct line_range {
|
||||||
struct list_head line_list; /* Visible lines */
|
struct list_head line_list; /* Visible lines */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* List of variables */
|
||||||
|
struct variable_list {
|
||||||
|
struct probe_trace_point point; /* Actual probepoint */
|
||||||
|
struct strlist *vars; /* Available variables */
|
||||||
|
};
|
||||||
|
|
||||||
/* Command string to events */
|
/* Command string to events */
|
||||||
extern int parse_perf_probe_command(const char *cmd,
|
extern int parse_perf_probe_command(const char *cmd,
|
||||||
struct perf_probe_event *pev);
|
struct perf_probe_event *pev);
|
||||||
|
@ -115,6 +121,8 @@ extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
|
||||||
extern int del_perf_probe_events(struct strlist *dellist);
|
extern int del_perf_probe_events(struct strlist *dellist);
|
||||||
extern int show_perf_probe_events(void);
|
extern int show_perf_probe_events(void);
|
||||||
extern int show_line_range(struct line_range *lr);
|
extern int show_line_range(struct line_range *lr);
|
||||||
|
extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
|
||||||
|
int max_probe_points);
|
||||||
|
|
||||||
|
|
||||||
/* Maximum index number of event-name postfix */
|
/* Maximum index number of event-name postfix */
|
||||||
|
|
|
@ -172,8 +172,8 @@ static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get type die, but skip qualifiers and typedef */
|
/* Get a type die, but skip qualifiers */
|
||||||
static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
|
static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
|
||||||
{
|
{
|
||||||
int tag;
|
int tag;
|
||||||
|
|
||||||
|
@ -185,8 +185,17 @@ static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
|
||||||
} while (tag == DW_TAG_const_type ||
|
} while (tag == DW_TAG_const_type ||
|
||||||
tag == DW_TAG_restrict_type ||
|
tag == DW_TAG_restrict_type ||
|
||||||
tag == DW_TAG_volatile_type ||
|
tag == DW_TAG_volatile_type ||
|
||||||
tag == DW_TAG_shared_type ||
|
tag == DW_TAG_shared_type);
|
||||||
tag == DW_TAG_typedef);
|
|
||||||
|
return vr_die;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get a type die, but skip qualifiers and typedef */
|
||||||
|
static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
vr_die = __die_get_real_type(vr_die, die_mem);
|
||||||
|
} while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef);
|
||||||
|
|
||||||
return vr_die;
|
return vr_die;
|
||||||
}
|
}
|
||||||
|
@ -380,6 +389,60 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
|
||||||
die_mem);
|
die_mem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get the name of given variable DIE */
|
||||||
|
static int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
|
||||||
|
{
|
||||||
|
Dwarf_Die type;
|
||||||
|
int tag, ret, ret2;
|
||||||
|
const char *tmp = "";
|
||||||
|
|
||||||
|
if (__die_get_real_type(vr_die, &type) == NULL)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
tag = dwarf_tag(&type);
|
||||||
|
if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)
|
||||||
|
tmp = "*";
|
||||||
|
else if (tag == DW_TAG_subroutine_type) {
|
||||||
|
/* Function pointer */
|
||||||
|
ret = snprintf(buf, len, "(function_type)");
|
||||||
|
return (ret >= len) ? -E2BIG : ret;
|
||||||
|
} else {
|
||||||
|
if (!dwarf_diename(&type))
|
||||||
|
return -ENOENT;
|
||||||
|
if (tag == DW_TAG_union_type)
|
||||||
|
tmp = "union ";
|
||||||
|
else if (tag == DW_TAG_structure_type)
|
||||||
|
tmp = "struct ";
|
||||||
|
/* Write a base name */
|
||||||
|
ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
|
||||||
|
return (ret >= len) ? -E2BIG : ret;
|
||||||
|
}
|
||||||
|
ret = die_get_typename(&type, buf, len);
|
||||||
|
if (ret > 0) {
|
||||||
|
ret2 = snprintf(buf + ret, len - ret, "%s", tmp);
|
||||||
|
ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the name and type of given variable DIE, stored as "type\tname" */
|
||||||
|
static int die_get_varname(Dwarf_Die *vr_die, char *buf, int len)
|
||||||
|
{
|
||||||
|
int ret, ret2;
|
||||||
|
|
||||||
|
ret = die_get_typename(vr_die, buf, len);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_debug("Failed to get type, make it unknown.\n");
|
||||||
|
ret = snprintf(buf, len, "(unknown_type)");
|
||||||
|
}
|
||||||
|
if (ret > 0) {
|
||||||
|
ret2 = snprintf(buf + ret, len - ret, "\t%s",
|
||||||
|
dwarf_diename(vr_die));
|
||||||
|
ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Probe finder related functions
|
* Probe finder related functions
|
||||||
*/
|
*/
|
||||||
|
@ -393,8 +456,13 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
|
||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Show a location */
|
/*
|
||||||
static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
|
* Convert a location into trace_arg.
|
||||||
|
* If tvar == NULL, this just checks variable can be converted.
|
||||||
|
*/
|
||||||
|
static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
|
||||||
|
Dwarf_Op *fb_ops,
|
||||||
|
struct probe_trace_arg *tvar)
|
||||||
{
|
{
|
||||||
Dwarf_Attribute attr;
|
Dwarf_Attribute attr;
|
||||||
Dwarf_Op *op;
|
Dwarf_Op *op;
|
||||||
|
@ -403,7 +471,6 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
|
||||||
Dwarf_Word offs = 0;
|
Dwarf_Word offs = 0;
|
||||||
bool ref = false;
|
bool ref = false;
|
||||||
const char *regs;
|
const char *regs;
|
||||||
struct probe_trace_arg *tvar = pf->tvar;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
|
if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
|
||||||
|
@ -411,16 +478,16 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
|
||||||
|
|
||||||
/* TODO: handle more than 1 exprs */
|
/* TODO: handle more than 1 exprs */
|
||||||
if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
|
if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
|
||||||
dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 ||
|
dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 ||
|
||||||
nops == 0) {
|
nops == 0) {
|
||||||
/* TODO: Support const_value */
|
/* TODO: Support const_value */
|
||||||
pr_err("Failed to find the location of %s at this address.\n"
|
|
||||||
" Perhaps, it has been optimized out.\n", pf->pvar->var);
|
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op->atom == DW_OP_addr) {
|
if (op->atom == DW_OP_addr) {
|
||||||
static_var:
|
static_var:
|
||||||
|
if (!tvar)
|
||||||
|
return 0;
|
||||||
/* Static variables on memory (not stack), make @varname */
|
/* Static variables on memory (not stack), make @varname */
|
||||||
ret = strlen(dwarf_diename(vr_die));
|
ret = strlen(dwarf_diename(vr_die));
|
||||||
tvar->value = zalloc(ret + 2);
|
tvar->value = zalloc(ret + 2);
|
||||||
|
@ -435,14 +502,11 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
|
||||||
|
|
||||||
/* If this is based on frame buffer, set the offset */
|
/* If this is based on frame buffer, set the offset */
|
||||||
if (op->atom == DW_OP_fbreg) {
|
if (op->atom == DW_OP_fbreg) {
|
||||||
if (pf->fb_ops == NULL) {
|
if (fb_ops == NULL)
|
||||||
pr_warning("The attribute of frame base is not "
|
|
||||||
"supported.\n");
|
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
|
||||||
ref = true;
|
ref = true;
|
||||||
offs = op->number;
|
offs = op->number;
|
||||||
op = &pf->fb_ops[0];
|
op = &fb_ops[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) {
|
if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) {
|
||||||
|
@ -458,13 +522,18 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
|
||||||
} else if (op->atom == DW_OP_regx) {
|
} else if (op->atom == DW_OP_regx) {
|
||||||
regn = op->number;
|
regn = op->number;
|
||||||
} else {
|
} else {
|
||||||
pr_warning("DW_OP %x is not supported.\n", op->atom);
|
pr_debug("DW_OP %x is not supported.\n", op->atom);
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tvar)
|
||||||
|
return 0;
|
||||||
|
|
||||||
regs = get_arch_regstr(regn);
|
regs = get_arch_regstr(regn);
|
||||||
if (!regs) {
|
if (!regs) {
|
||||||
pr_warning("Mapping for DWARF register number %u missing on this architecture.", regn);
|
/* This should be a bug in DWARF or this tool */
|
||||||
|
pr_warning("Mapping for DWARF register number %u "
|
||||||
|
"missing on this architecture.", regn);
|
||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -689,8 +758,14 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
|
||||||
pr_debug("Converting variable %s into trace event.\n",
|
pr_debug("Converting variable %s into trace event.\n",
|
||||||
dwarf_diename(vr_die));
|
dwarf_diename(vr_die));
|
||||||
|
|
||||||
ret = convert_variable_location(vr_die, pf);
|
ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
|
||||||
if (ret == 0 && pf->pvar->field) {
|
pf->tvar);
|
||||||
|
if (ret == -ENOENT)
|
||||||
|
pr_err("Failed to find the location of %s at this address.\n"
|
||||||
|
" Perhaps, it has been optimized out.\n", pf->pvar->var);
|
||||||
|
else if (ret == -ENOTSUP)
|
||||||
|
pr_err("Sorry, we don't support this variable location yet.\n");
|
||||||
|
else if (pf->pvar->field) {
|
||||||
ret = convert_variable_fields(vr_die, pf->pvar->var,
|
ret = convert_variable_fields(vr_die, pf->pvar->var,
|
||||||
pf->pvar->field, &pf->tvar->ref,
|
pf->pvar->field, &pf->tvar->ref,
|
||||||
&die_mem);
|
&die_mem);
|
||||||
|
@ -772,34 +847,12 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Show a probe point to output buffer */
|
/* Convert subprogram DIE to trace point */
|
||||||
static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
|
static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr,
|
||||||
|
bool retprobe, struct probe_trace_point *tp)
|
||||||
{
|
{
|
||||||
struct probe_trace_event *tev;
|
|
||||||
Dwarf_Addr eaddr;
|
Dwarf_Addr eaddr;
|
||||||
Dwarf_Die die_mem;
|
|
||||||
const char *name;
|
const char *name;
|
||||||
int ret, i;
|
|
||||||
Dwarf_Attribute fb_attr;
|
|
||||||
size_t nops;
|
|
||||||
|
|
||||||
if (pf->ntevs == pf->max_tevs) {
|
|
||||||
pr_warning("Too many( > %d) probe point found.\n",
|
|
||||||
pf->max_tevs);
|
|
||||||
return -ERANGE;
|
|
||||||
}
|
|
||||||
tev = &pf->tevs[pf->ntevs++];
|
|
||||||
|
|
||||||
/* If no real subprogram, find a real one */
|
|
||||||
if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
|
|
||||||
sp_die = die_find_real_subprogram(&pf->cu_die,
|
|
||||||
pf->addr, &die_mem);
|
|
||||||
if (!sp_die) {
|
|
||||||
pr_warning("Failed to find probe point in any "
|
|
||||||
"functions.\n");
|
|
||||||
return -ENOENT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy the name of probe point */
|
/* Copy the name of probe point */
|
||||||
name = dwarf_diename(sp_die);
|
name = dwarf_diename(sp_die);
|
||||||
|
@ -809,26 +862,45 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
|
||||||
dwarf_diename(sp_die));
|
dwarf_diename(sp_die));
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
tev->point.symbol = strdup(name);
|
tp->symbol = strdup(name);
|
||||||
if (tev->point.symbol == NULL)
|
if (tp->symbol == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
tev->point.offset = (unsigned long)(pf->addr - eaddr);
|
tp->offset = (unsigned long)(paddr - eaddr);
|
||||||
} else
|
} else
|
||||||
/* This function has no name. */
|
/* This function has no name. */
|
||||||
tev->point.offset = (unsigned long)pf->addr;
|
tp->offset = (unsigned long)paddr;
|
||||||
|
|
||||||
/* Return probe must be on the head of a subprogram */
|
/* Return probe must be on the head of a subprogram */
|
||||||
if (pf->pev->point.retprobe) {
|
if (retprobe) {
|
||||||
if (tev->point.offset != 0) {
|
if (eaddr != paddr) {
|
||||||
pr_warning("Return probe must be on the head of"
|
pr_warning("Return probe must be on the head of"
|
||||||
" a real function\n");
|
" a real function\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
tev->point.retprobe = true;
|
tp->retprobe = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
|
return 0;
|
||||||
tev->point.offset);
|
}
|
||||||
|
|
||||||
|
/* Call probe_finder callback with real subprogram DIE */
|
||||||
|
static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
|
||||||
|
{
|
||||||
|
Dwarf_Die die_mem;
|
||||||
|
Dwarf_Attribute fb_attr;
|
||||||
|
size_t nops;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* If no real subprogram, find a real one */
|
||||||
|
if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
|
||||||
|
sp_die = die_find_real_subprogram(&pf->cu_die,
|
||||||
|
pf->addr, &die_mem);
|
||||||
|
if (!sp_die) {
|
||||||
|
pr_warning("Failed to find probe point in any "
|
||||||
|
"functions.\n");
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Get the frame base attribute/ops */
|
/* Get the frame base attribute/ops */
|
||||||
dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
|
dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
|
||||||
|
@ -848,22 +920,13 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find each argument */
|
/* Call finder's callback handler */
|
||||||
tev->nargs = pf->pev->nargs;
|
ret = pf->callback(sp_die, pf);
|
||||||
tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
|
|
||||||
if (tev->args == NULL)
|
|
||||||
return -ENOMEM;
|
|
||||||
for (i = 0; i < pf->pev->nargs; i++) {
|
|
||||||
pf->pvar = &pf->pev->args[i];
|
|
||||||
pf->tvar = &tev->args[i];
|
|
||||||
ret = find_variable(sp_die, pf);
|
|
||||||
if (ret != 0)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* *pf->fb_ops will be cached in libdw. Don't free it. */
|
/* *pf->fb_ops will be cached in libdw. Don't free it. */
|
||||||
pf->fb_ops = NULL;
|
pf->fb_ops = NULL;
|
||||||
return 0;
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find probe point from its line number */
|
/* Find probe point from its line number */
|
||||||
|
@ -899,7 +962,7 @@ static int find_probe_point_by_line(struct probe_finder *pf)
|
||||||
(int)i, lineno, (uintmax_t)addr);
|
(int)i, lineno, (uintmax_t)addr);
|
||||||
pf->addr = addr;
|
pf->addr = addr;
|
||||||
|
|
||||||
ret = convert_probe_point(NULL, pf);
|
ret = call_probe_finder(NULL, pf);
|
||||||
/* Continuing, because target line might be inlined. */
|
/* Continuing, because target line might be inlined. */
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1012,7 +1075,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
|
||||||
(int)i, lineno, (unsigned long long)addr);
|
(int)i, lineno, (unsigned long long)addr);
|
||||||
pf->addr = addr;
|
pf->addr = addr;
|
||||||
|
|
||||||
ret = convert_probe_point(sp_die, pf);
|
ret = call_probe_finder(sp_die, pf);
|
||||||
/* Continuing, because target line might be inlined. */
|
/* Continuing, because target line might be inlined. */
|
||||||
}
|
}
|
||||||
/* TODO: deallocate lines, but how? */
|
/* TODO: deallocate lines, but how? */
|
||||||
|
@ -1047,7 +1110,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
|
||||||
pr_debug("found inline addr: 0x%jx\n",
|
pr_debug("found inline addr: 0x%jx\n",
|
||||||
(uintmax_t)pf->addr);
|
(uintmax_t)pf->addr);
|
||||||
|
|
||||||
param->retval = convert_probe_point(in_die, pf);
|
param->retval = call_probe_finder(in_die, pf);
|
||||||
if (param->retval < 0)
|
if (param->retval < 0)
|
||||||
return DWARF_CB_ABORT;
|
return DWARF_CB_ABORT;
|
||||||
}
|
}
|
||||||
|
@ -1085,7 +1148,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
|
||||||
}
|
}
|
||||||
pf->addr += pp->offset;
|
pf->addr += pp->offset;
|
||||||
/* TODO: Check the address in this function */
|
/* TODO: Check the address in this function */
|
||||||
param->retval = convert_probe_point(sp_die, pf);
|
param->retval = call_probe_finder(sp_die, pf);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
struct dwarf_callback_param _param = {.data = (void *)pf,
|
struct dwarf_callback_param _param = {.data = (void *)pf,
|
||||||
|
@ -1107,70 +1170,229 @@ static int find_probe_point_by_func(struct probe_finder *pf)
|
||||||
return _param.retval;
|
return _param.retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find probe_trace_events specified by perf_probe_event from debuginfo */
|
/* Find probe points from debuginfo */
|
||||||
int find_probe_trace_events(int fd, struct perf_probe_event *pev,
|
static int find_probes(int fd, struct probe_finder *pf)
|
||||||
struct probe_trace_event **tevs, int max_tevs)
|
|
||||||
{
|
{
|
||||||
struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs};
|
struct perf_probe_point *pp = &pf->pev->point;
|
||||||
struct perf_probe_point *pp = &pev->point;
|
|
||||||
Dwarf_Off off, noff;
|
Dwarf_Off off, noff;
|
||||||
size_t cuhl;
|
size_t cuhl;
|
||||||
Dwarf_Die *diep;
|
Dwarf_Die *diep;
|
||||||
Dwarf *dbg;
|
Dwarf *dbg;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
pf.tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
|
|
||||||
if (pf.tevs == NULL)
|
|
||||||
return -ENOMEM;
|
|
||||||
*tevs = pf.tevs;
|
|
||||||
pf.ntevs = 0;
|
|
||||||
|
|
||||||
dbg = dwarf_begin(fd, DWARF_C_READ);
|
dbg = dwarf_begin(fd, DWARF_C_READ);
|
||||||
if (!dbg) {
|
if (!dbg) {
|
||||||
pr_warning("No dwarf info found in the vmlinux - "
|
pr_warning("No dwarf info found in the vmlinux - "
|
||||||
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
|
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
|
||||||
free(pf.tevs);
|
|
||||||
*tevs = NULL;
|
|
||||||
return -EBADF;
|
return -EBADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if _ELFUTILS_PREREQ(0, 142)
|
#if _ELFUTILS_PREREQ(0, 142)
|
||||||
/* Get the call frame information from this dwarf */
|
/* Get the call frame information from this dwarf */
|
||||||
pf.cfi = dwarf_getcfi(dbg);
|
pf->cfi = dwarf_getcfi(dbg);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
off = 0;
|
off = 0;
|
||||||
line_list__init(&pf.lcache);
|
line_list__init(&pf->lcache);
|
||||||
/* Loop on CUs (Compilation Unit) */
|
/* Loop on CUs (Compilation Unit) */
|
||||||
while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) &&
|
while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) &&
|
||||||
ret >= 0) {
|
ret >= 0) {
|
||||||
/* Get the DIE(Debugging Information Entry) of this CU */
|
/* Get the DIE(Debugging Information Entry) of this CU */
|
||||||
diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die);
|
diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die);
|
||||||
if (!diep)
|
if (!diep)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Check if target file is included. */
|
/* Check if target file is included. */
|
||||||
if (pp->file)
|
if (pp->file)
|
||||||
pf.fname = cu_find_realpath(&pf.cu_die, pp->file);
|
pf->fname = cu_find_realpath(&pf->cu_die, pp->file);
|
||||||
else
|
else
|
||||||
pf.fname = NULL;
|
pf->fname = NULL;
|
||||||
|
|
||||||
if (!pp->file || pf.fname) {
|
if (!pp->file || pf->fname) {
|
||||||
if (pp->function)
|
if (pp->function)
|
||||||
ret = find_probe_point_by_func(&pf);
|
ret = find_probe_point_by_func(pf);
|
||||||
else if (pp->lazy_line)
|
else if (pp->lazy_line)
|
||||||
ret = find_probe_point_lazy(NULL, &pf);
|
ret = find_probe_point_lazy(NULL, pf);
|
||||||
else {
|
else {
|
||||||
pf.lno = pp->line;
|
pf->lno = pp->line;
|
||||||
ret = find_probe_point_by_line(&pf);
|
ret = find_probe_point_by_line(pf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
off = noff;
|
off = noff;
|
||||||
}
|
}
|
||||||
line_list__free(&pf.lcache);
|
line_list__free(&pf->lcache);
|
||||||
dwarf_end(dbg);
|
dwarf_end(dbg);
|
||||||
|
|
||||||
return (ret < 0) ? ret : pf.ntevs;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add a found probe point into trace event list */
|
||||||
|
static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf)
|
||||||
|
{
|
||||||
|
struct trace_event_finder *tf =
|
||||||
|
container_of(pf, struct trace_event_finder, pf);
|
||||||
|
struct probe_trace_event *tev;
|
||||||
|
int ret, i;
|
||||||
|
|
||||||
|
/* Check number of tevs */
|
||||||
|
if (tf->ntevs == tf->max_tevs) {
|
||||||
|
pr_warning("Too many( > %d) probe point found.\n",
|
||||||
|
tf->max_tevs);
|
||||||
|
return -ERANGE;
|
||||||
|
}
|
||||||
|
tev = &tf->tevs[tf->ntevs++];
|
||||||
|
|
||||||
|
ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
|
||||||
|
&tev->point);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
|
||||||
|
tev->point.offset);
|
||||||
|
|
||||||
|
/* Find each argument */
|
||||||
|
tev->nargs = pf->pev->nargs;
|
||||||
|
tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
|
||||||
|
if (tev->args == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
for (i = 0; i < pf->pev->nargs; i++) {
|
||||||
|
pf->pvar = &pf->pev->args[i];
|
||||||
|
pf->tvar = &tev->args[i];
|
||||||
|
ret = find_variable(sp_die, pf);
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find probe_trace_events specified by perf_probe_event from debuginfo */
|
||||||
|
int find_probe_trace_events(int fd, struct perf_probe_event *pev,
|
||||||
|
struct probe_trace_event **tevs, int max_tevs)
|
||||||
|
{
|
||||||
|
struct trace_event_finder tf = {
|
||||||
|
.pf = {.pev = pev, .callback = add_probe_trace_event},
|
||||||
|
.max_tevs = max_tevs};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Allocate result tevs array */
|
||||||
|
*tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
|
||||||
|
if (*tevs == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
tf.tevs = *tevs;
|
||||||
|
tf.ntevs = 0;
|
||||||
|
|
||||||
|
ret = find_probes(fd, &tf.pf);
|
||||||
|
if (ret < 0) {
|
||||||
|
free(*tevs);
|
||||||
|
*tevs = NULL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ret < 0) ? ret : tf.ntevs;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_VAR_LEN 64
|
||||||
|
|
||||||
|
/* Collect available variables in this scope */
|
||||||
|
static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
|
||||||
|
{
|
||||||
|
struct available_var_finder *af = data;
|
||||||
|
struct variable_list *vl;
|
||||||
|
char buf[MAX_VAR_LEN];
|
||||||
|
int tag, ret;
|
||||||
|
|
||||||
|
vl = &af->vls[af->nvls - 1];
|
||||||
|
|
||||||
|
tag = dwarf_tag(die_mem);
|
||||||
|
if (tag == DW_TAG_formal_parameter ||
|
||||||
|
tag == DW_TAG_variable) {
|
||||||
|
ret = convert_variable_location(die_mem, af->pf.addr,
|
||||||
|
af->pf.fb_ops, NULL);
|
||||||
|
if (ret == 0) {
|
||||||
|
ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
|
||||||
|
if (ret > 0)
|
||||||
|
strlist__add(vl->vars, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dwarf_haspc(die_mem, af->pf.addr))
|
||||||
|
return DIE_FIND_CB_CONTINUE;
|
||||||
|
else
|
||||||
|
return DIE_FIND_CB_SIBLING;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add a found vars into available variables list */
|
||||||
|
static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
|
||||||
|
{
|
||||||
|
struct available_var_finder *af =
|
||||||
|
container_of(pf, struct available_var_finder, pf);
|
||||||
|
struct variable_list *vl;
|
||||||
|
Dwarf_Die die_mem;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Check number of tevs */
|
||||||
|
if (af->nvls == af->max_vls) {
|
||||||
|
pr_warning("Too many( > %d) probe point found.\n", af->max_vls);
|
||||||
|
return -ERANGE;
|
||||||
|
}
|
||||||
|
vl = &af->vls[af->nvls++];
|
||||||
|
|
||||||
|
ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
|
||||||
|
&vl->point);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
pr_debug("Probe point found: %s+%lu\n", vl->point.symbol,
|
||||||
|
vl->point.offset);
|
||||||
|
|
||||||
|
/* Find local variables */
|
||||||
|
vl->vars = strlist__new(true, NULL);
|
||||||
|
if (vl->vars == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem);
|
||||||
|
|
||||||
|
if (strlist__empty(vl->vars)) {
|
||||||
|
strlist__delete(vl->vars);
|
||||||
|
vl->vars = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find available variables at given probe point */
|
||||||
|
int find_available_vars_at(int fd, struct perf_probe_event *pev,
|
||||||
|
struct variable_list **vls, int max_vls)
|
||||||
|
{
|
||||||
|
struct available_var_finder af = {
|
||||||
|
.pf = {.pev = pev, .callback = add_available_vars},
|
||||||
|
.max_vls = max_vls};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Allocate result vls array */
|
||||||
|
*vls = zalloc(sizeof(struct variable_list) * max_vls);
|
||||||
|
if (*vls == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
af.vls = *vls;
|
||||||
|
af.nvls = 0;
|
||||||
|
|
||||||
|
ret = find_probes(fd, &af.pf);
|
||||||
|
if (ret < 0) {
|
||||||
|
/* Free vlist for error */
|
||||||
|
while (af.nvls--) {
|
||||||
|
if (af.vls[af.nvls].point.symbol)
|
||||||
|
free(af.vls[af.nvls].point.symbol);
|
||||||
|
if (af.vls[af.nvls].vars)
|
||||||
|
strlist__delete(af.vls[af.nvls].vars);
|
||||||
|
}
|
||||||
|
free(af.vls);
|
||||||
|
*vls = NULL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ret < 0) ? ret : af.nvls;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reverse search */
|
/* Reverse search */
|
||||||
|
|
|
@ -25,17 +25,22 @@ extern int find_probe_trace_events(int fd, struct perf_probe_event *pev,
|
||||||
extern int find_perf_probe_point(int fd, unsigned long addr,
|
extern int find_perf_probe_point(int fd, unsigned long addr,
|
||||||
struct perf_probe_point *ppt);
|
struct perf_probe_point *ppt);
|
||||||
|
|
||||||
|
/* Find a line range */
|
||||||
extern int find_line_range(int fd, struct line_range *lr);
|
extern int find_line_range(int fd, struct line_range *lr);
|
||||||
|
|
||||||
|
/* Find available variables */
|
||||||
|
extern int find_available_vars_at(int fd, struct perf_probe_event *pev,
|
||||||
|
struct variable_list **vls, int max_points);
|
||||||
|
|
||||||
#include <dwarf.h>
|
#include <dwarf.h>
|
||||||
#include <libdw.h>
|
#include <libdw.h>
|
||||||
#include <version.h>
|
#include <version.h>
|
||||||
|
|
||||||
struct probe_finder {
|
struct probe_finder {
|
||||||
struct perf_probe_event *pev; /* Target probe event */
|
struct perf_probe_event *pev; /* Target probe event */
|
||||||
struct probe_trace_event *tevs; /* Result trace events */
|
|
||||||
int ntevs; /* Number of trace events */
|
/* Callback when a probe point is found */
|
||||||
int max_tevs; /* Max number of trace events */
|
int (*callback)(Dwarf_Die *sp_die, struct probe_finder *pf);
|
||||||
|
|
||||||
/* For function searching */
|
/* For function searching */
|
||||||
int lno; /* Line number */
|
int lno; /* Line number */
|
||||||
|
@ -53,6 +58,20 @@ struct probe_finder {
|
||||||
struct probe_trace_arg *tvar; /* Current result variable */
|
struct probe_trace_arg *tvar; /* Current result variable */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct trace_event_finder {
|
||||||
|
struct probe_finder pf;
|
||||||
|
struct probe_trace_event *tevs; /* Found trace events */
|
||||||
|
int ntevs; /* Number of trace events */
|
||||||
|
int max_tevs; /* Max number of trace events */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct available_var_finder {
|
||||||
|
struct probe_finder pf;
|
||||||
|
struct variable_list *vls; /* Found variable lists */
|
||||||
|
int nvls; /* Number of variable lists */
|
||||||
|
int max_vls; /* Max no. of variable lists */
|
||||||
|
};
|
||||||
|
|
||||||
struct line_finder {
|
struct line_finder {
|
||||||
struct line_range *lr; /* Target line range */
|
struct line_range *lr; /* Target line range */
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue