Commit cf6eb489 authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Arnaldo Carvalho de Melo

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: default avatarMasami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 632941c4
...@@ -17,6 +17,8 @@ or ...@@ -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 ...@@ -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 { ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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 = &params.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[] = { ...@@ -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[] = { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 { ...@@ -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, ...@@ -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 */
......
This diff is collapsed.
...@@ -25,17 +25,22 @@ extern int find_probe_trace_events(int fd, struct perf_probe_event *pev, ...@@ -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 { ...@@ -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 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment