Commit b8ecad8b authored by Ingo Molnar's avatar Ingo Molnar

Merge branch 'perf/core' of...

Merge branch 'perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux-2.6 into perf/urgent
parents eea4a0b1 8bfb5e7d
...@@ -410,7 +410,7 @@ extern void open_softirq(int nr, void (*action)(struct softirq_action *)); ...@@ -410,7 +410,7 @@ extern void open_softirq(int nr, void (*action)(struct softirq_action *));
extern void softirq_init(void); extern void softirq_init(void);
static inline void __raise_softirq_irqoff(unsigned int nr) static inline void __raise_softirq_irqoff(unsigned int nr)
{ {
trace_softirq_raise((struct softirq_action *)(unsigned long)nr, NULL); trace_softirq_raise(nr);
or_softirq_pending(1UL << nr); or_softirq_pending(1UL << nr);
} }
......
...@@ -86,76 +86,62 @@ TRACE_EVENT(irq_handler_exit, ...@@ -86,76 +86,62 @@ TRACE_EVENT(irq_handler_exit,
DECLARE_EVENT_CLASS(softirq, DECLARE_EVENT_CLASS(softirq,
TP_PROTO(struct softirq_action *h, struct softirq_action *vec), TP_PROTO(unsigned int vec_nr),
TP_ARGS(h, vec), TP_ARGS(vec_nr),
TP_STRUCT__entry( TP_STRUCT__entry(
__field( int, vec ) __field( unsigned int, vec )
), ),
TP_fast_assign( TP_fast_assign(
if (vec) __entry->vec = vec_nr;
__entry->vec = (int)(h - vec);
else
__entry->vec = (int)(long)h;
), ),
TP_printk("vec=%d [action=%s]", __entry->vec, TP_printk("vec=%u [action=%s]", __entry->vec,
show_softirq_name(__entry->vec)) show_softirq_name(__entry->vec))
); );
/** /**
* softirq_entry - called immediately before the softirq handler * softirq_entry - called immediately before the softirq handler
* @h: pointer to struct softirq_action * @vec_nr: softirq vector number
* @vec: pointer to first struct softirq_action in softirq_vec array
* *
* The @h parameter, contains a pointer to the struct softirq_action * When used in combination with the softirq_exit tracepoint
* which has a pointer to the action handler that is called. By subtracting * we can determine the softirq handler runtine.
* the @vec pointer from the @h pointer, we can determine the softirq
* number. Also, when used in combination with the softirq_exit tracepoint
* we can determine the softirq latency.
*/ */
DEFINE_EVENT(softirq, softirq_entry, DEFINE_EVENT(softirq, softirq_entry,
TP_PROTO(struct softirq_action *h, struct softirq_action *vec), TP_PROTO(unsigned int vec_nr),
TP_ARGS(h, vec) TP_ARGS(vec_nr)
); );
/** /**
* softirq_exit - called immediately after the softirq handler returns * softirq_exit - called immediately after the softirq handler returns
* @h: pointer to struct softirq_action * @vec_nr: softirq vector number
* @vec: pointer to first struct softirq_action in softirq_vec array
* *
* The @h parameter contains a pointer to the struct softirq_action * When used in combination with the softirq_entry tracepoint
* that has handled the softirq. By subtracting the @vec pointer from * we can determine the softirq handler runtine.
* the @h pointer, we can determine the softirq number. Also, when used in
* combination with the softirq_entry tracepoint we can determine the softirq
* latency.
*/ */
DEFINE_EVENT(softirq, softirq_exit, DEFINE_EVENT(softirq, softirq_exit,
TP_PROTO(struct softirq_action *h, struct softirq_action *vec), TP_PROTO(unsigned int vec_nr),
TP_ARGS(h, vec) TP_ARGS(vec_nr)
); );
/** /**
* softirq_raise - called immediately when a softirq is raised * softirq_raise - called immediately when a softirq is raised
* @h: pointer to struct softirq_action * @vec_nr: softirq vector number
* @vec: pointer to first struct softirq_action in softirq_vec array
* *
* The @h parameter contains a pointer to the softirq vector number which is * When used in combination with the softirq_entry tracepoint
* raised. @vec is NULL and it means @h includes vector number not * we can determine the softirq raise to run latency.
* softirq_action. When used in combination with the softirq_entry tracepoint
* we can determine the softirq raise latency.
*/ */
DEFINE_EVENT(softirq, softirq_raise, DEFINE_EVENT(softirq, softirq_raise,
TP_PROTO(struct softirq_action *h, struct softirq_action *vec), TP_PROTO(unsigned int vec_nr),
TP_ARGS(h, vec) TP_ARGS(vec_nr)
); );
#endif /* _TRACE_IRQ_H */ #endif /* _TRACE_IRQ_H */
......
...@@ -229,18 +229,20 @@ asmlinkage void __do_softirq(void) ...@@ -229,18 +229,20 @@ asmlinkage void __do_softirq(void)
do { do {
if (pending & 1) { if (pending & 1) {
unsigned int vec_nr = h - softirq_vec;
int prev_count = preempt_count(); int prev_count = preempt_count();
kstat_incr_softirqs_this_cpu(h - softirq_vec);
trace_softirq_entry(h, softirq_vec); kstat_incr_softirqs_this_cpu(vec_nr);
trace_softirq_entry(vec_nr);
h->action(h); h->action(h);
trace_softirq_exit(h, softirq_vec); trace_softirq_exit(vec_nr);
if (unlikely(prev_count != preempt_count())) { if (unlikely(prev_count != preempt_count())) {
printk(KERN_ERR "huh, entered softirq %td %s %p" printk(KERN_ERR "huh, entered softirq %u %s %p"
"with preempt_count %08x," "with preempt_count %08x,"
" exited with %08x?\n", h - softirq_vec, " exited with %08x?\n", vec_nr,
softirq_to_name[h - softirq_vec], softirq_to_name[vec_nr], h->action,
h->action, prev_count, preempt_count()); prev_count, preempt_count());
preempt_count() = prev_count; preempt_count() = prev_count;
} }
......
...@@ -16,7 +16,9 @@ or ...@@ -16,7 +16,9 @@ or
or or
'perf probe' --list 'perf probe' --list
or or
'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]' 'perf probe' [options] --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
or
'perf probe' [options] --vars='PROBEPOINT'
DESCRIPTION DESCRIPTION
----------- -----------
...@@ -31,6 +33,11 @@ OPTIONS ...@@ -31,6 +33,11 @@ OPTIONS
--vmlinux=PATH:: --vmlinux=PATH::
Specify vmlinux path which has debuginfo (Dwarf binary). Specify vmlinux path which has debuginfo (Dwarf binary).
-m::
--module=MODNAME::
Specify module name in which perf-probe searches probe points
or lines.
-s:: -s::
--source=PATH:: --source=PATH::
Specify path to kernel source. Specify path to kernel source.
...@@ -57,6 +64,15 @@ OPTIONS ...@@ -57,6 +64,15 @@ 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.
--externs::
(Only for --vars) Show external defined variables in addition to local
variables.
-f:: -f::
--force:: --force::
Forcibly add events with existing name. Forcibly add events with existing name.
......
...@@ -50,14 +50,17 @@ static struct { ...@@ -50,14 +50,17 @@ static struct {
bool list_events; bool list_events;
bool force_add; bool force_add;
bool show_lines; bool show_lines;
bool show_vars;
bool show_ext_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;
struct line_range line_range; struct line_range line_range;
const char *target_module;
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 +95,7 @@ static int parse_probe_event_argv(int argc, const char **argv) ...@@ -92,6 +95,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 +104,10 @@ static int parse_probe_event_argv(int argc, const char **argv) ...@@ -100,9 +104,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 +115,7 @@ static int opt_del_probe_event(const struct option *opt __used, ...@@ -110,6 +115,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 +136,25 @@ static int opt_show_lines(const struct option *opt __used, ...@@ -130,6 +136,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[] = {
...@@ -138,7 +163,8 @@ static const char * const probe_usage[] = { ...@@ -138,7 +163,8 @@ static const char * const probe_usage[] = {
"perf probe [<options>] --del '[GROUP:]EVENT' ...", "perf probe [<options>] --del '[GROUP:]EVENT' ...",
"perf probe --list", "perf probe --list",
#ifdef DWARF_SUPPORT #ifdef DWARF_SUPPORT
"perf probe --line 'LINEDESC'", "perf probe [<options>] --line 'LINEDESC'",
"perf probe [<options>] --vars 'PROBEPOINT'",
#endif #endif
NULL NULL
}; };
...@@ -180,10 +206,17 @@ static const struct option options[] = { ...@@ -180,10 +206,17 @@ 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_BOOLEAN('\0', "externs", &params.show_ext_vars,
"Show external variables too (with --vars only)"),
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,
"directory", "path to kernel source"), "directory", "path to kernel source"),
OPT_STRING('m', "module", &params.target_module,
"modname", "target module name"),
#endif #endif
OPT__DRY_RUN(&probe_event_dry_run), OPT__DRY_RUN(&probe_event_dry_run),
OPT_INTEGER('\0', "max-probes", &params.max_probe_points, OPT_INTEGER('\0', "max-probes", &params.max_probe_points,
...@@ -217,7 +250,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) ...@@ -217,7 +250,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 +258,10 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) ...@@ -225,6 +258,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,17 +271,35 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) ...@@ -234,17 +271,35 @@ 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);
} }
ret = show_line_range(&params.line_range); ret = show_line_range(&params.line_range, params.target_module);
if (ret < 0) if (ret < 0)
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,
params.target_module,
params.show_ext_vars);
if (ret < 0)
pr_err(" Error: Failed to show vars. (%d)\n", ret);
return ret;
}
#endif #endif
if (params.dellist) { if (params.dellist) {
...@@ -258,8 +313,9 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) ...@@ -258,8 +313,9 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
if (params.nevents) { if (params.nevents) {
ret = add_perf_probe_events(params.events, params.nevents, ret = add_perf_probe_events(params.events, params.nevents,
params.force_add, params.max_probe_points,
params.max_probe_points); params.target_module,
params.force_add);
if (ret < 0) { if (ret < 0) {
pr_err(" Error: Failed to add events. (%d)\n", ret); pr_err(" Error: Failed to add events. (%d)\n", ret);
return ret; return ret;
......
...@@ -215,6 +215,16 @@ struct symbol *map_groups__find_function_by_name(struct map_groups *self, ...@@ -215,6 +215,16 @@ struct symbol *map_groups__find_function_by_name(struct map_groups *self,
return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter); return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter);
} }
static inline
struct symbol *machine__find_kernel_function_by_name(struct machine *self,
const char *name,
struct map **mapp,
symbol_filter_t filter)
{
return map_groups__find_function_by_name(&self->kmaps, name, mapp,
filter);
}
int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
int verbose, FILE *fp); int verbose, FILE *fp);
......
...@@ -74,10 +74,9 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) ...@@ -74,10 +74,9 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
static char *synthesize_perf_probe_point(struct perf_probe_point *pp); static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
static struct machine machine; static struct machine machine;
/* Initialize symbol maps and path of vmlinux */ /* Initialize symbol maps and path of vmlinux/modules */
static int init_vmlinux(void) static int init_vmlinux(void)
{ {
struct dso *kernel;
int ret; int ret;
symbol_conf.sort_by_name = true; symbol_conf.sort_by_name = true;
...@@ -91,33 +90,61 @@ static int init_vmlinux(void) ...@@ -91,33 +90,61 @@ static int init_vmlinux(void)
goto out; goto out;
} }
ret = machine__init(&machine, "/", 0); ret = machine__init(&machine, "", HOST_KERNEL_ID);
if (ret < 0) if (ret < 0)
goto out; goto out;
kernel = dso__new_kernel(symbol_conf.vmlinux_name); if (machine__create_kernel_maps(&machine) < 0) {
if (kernel == NULL) pr_debug("machine__create_kernel_maps ");
die("Failed to create kernel dso."); goto out;
}
ret = __machine__create_kernel_maps(&machine, kernel);
if (ret < 0)
pr_debug("Failed to create kernel maps.\n");
out: out:
if (ret < 0) if (ret < 0)
pr_warning("Failed to init vmlinux path.\n"); pr_warning("Failed to init vmlinux path.\n");
return ret; return ret;
} }
static struct symbol *__find_kernel_function_by_name(const char *name,
struct map **mapp)
{
return machine__find_kernel_function_by_name(&machine, name, mapp,
NULL);
}
const char *kernel_get_module_path(const char *module)
{
struct dso *dso;
if (module) {
list_for_each_entry(dso, &machine.kernel_dsos, node) {
if (strncmp(dso->short_name + 1, module,
dso->short_name_len - 2) == 0)
goto found;
}
pr_debug("Failed to find module %s.\n", module);
return NULL;
} else {
dso = machine.vmlinux_maps[MAP__FUNCTION]->dso;
if (dso__load_vmlinux_path(dso,
machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) {
pr_debug("Failed to load kernel map.\n");
return NULL;
}
}
found:
return dso->long_name;
}
#ifdef DWARF_SUPPORT #ifdef DWARF_SUPPORT
static int open_vmlinux(void) static int open_vmlinux(const char *module)
{ {
if (map__load(machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) { const char *path = kernel_get_module_path(module);
pr_debug("Failed to load kernel map.\n"); if (!path) {
return -EINVAL; pr_err("Failed to find path of %s module", module ?: "kernel");
return -ENOENT;
} }
pr_debug("Try to open %s\n", machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name); pr_debug("Try to open %s\n", path);
return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY); return open(path, O_RDONLY);
} }
/* /*
...@@ -125,20 +152,19 @@ static int open_vmlinux(void) ...@@ -125,20 +152,19 @@ static int open_vmlinux(void)
* Currently only handles kprobes. * Currently only handles kprobes.
*/ */
static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
struct perf_probe_point *pp) struct perf_probe_point *pp)
{ {
struct symbol *sym; struct symbol *sym;
int fd, ret = -ENOENT; struct map *map;
u64 addr;
int ret = -ENOENT;
sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], sym = __find_kernel_function_by_name(tp->symbol, &map);
tp->symbol, NULL);
if (sym) { if (sym) {
fd = open_vmlinux(); addr = map->unmap_ip(map, sym->start + tp->offset);
if (fd >= 0) { pr_debug("try to find %s+%ld@%llx\n", tp->symbol,
ret = find_perf_probe_point(fd, tp->offset, addr);
sym->start + tp->offset, pp); ret = find_perf_probe_point((unsigned long)addr, pp);
close(fd);
}
} }
if (ret <= 0) { if (ret <= 0) {
pr_debug("Failed to find corresponding probes from " pr_debug("Failed to find corresponding probes from "
...@@ -156,12 +182,12 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, ...@@ -156,12 +182,12 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
/* Try to find perf_probe_event with debuginfo */ /* Try to find perf_probe_event with debuginfo */
static int try_to_find_probe_trace_events(struct perf_probe_event *pev, static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
struct probe_trace_event **tevs, struct probe_trace_event **tevs,
int max_tevs) int max_tevs, const char *module)
{ {
bool need_dwarf = perf_probe_event_need_dwarf(pev); bool need_dwarf = perf_probe_event_need_dwarf(pev);
int fd, ntevs; int fd, ntevs;
fd = open_vmlinux(); fd = open_vmlinux(module);
if (fd < 0) { if (fd < 0) {
if (need_dwarf) { if (need_dwarf) {
pr_warning("Failed to open debuginfo file.\n"); pr_warning("Failed to open debuginfo file.\n");
...@@ -300,7 +326,7 @@ static int show_one_line(FILE *fp, int l, bool skip, bool show_num) ...@@ -300,7 +326,7 @@ static int show_one_line(FILE *fp, int l, bool skip, bool show_num)
* Show line-range always requires debuginfo to find source file and * Show line-range always requires debuginfo to find source file and
* line number. * line number.
*/ */
int show_line_range(struct line_range *lr) int show_line_range(struct line_range *lr, const char *module)
{ {
int l = 1; int l = 1;
struct line_node *ln; struct line_node *ln;
...@@ -313,7 +339,7 @@ int show_line_range(struct line_range *lr) ...@@ -313,7 +339,7 @@ int show_line_range(struct line_range *lr)
if (ret < 0) if (ret < 0)
return ret; return ret;
fd = open_vmlinux(); fd = open_vmlinux(module);
if (fd < 0) { if (fd < 0) {
pr_warning("Failed to open debuginfo file.\n"); pr_warning("Failed to open debuginfo file.\n");
return fd; return fd;
...@@ -378,11 +404,84 @@ int show_line_range(struct line_range *lr) ...@@ -378,11 +404,84 @@ 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, bool externs)
{
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, externs);
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, const char *module, bool externs)
{
int i, fd, ret = 0;
ret = init_vmlinux();
if (ret < 0)
return ret;
fd = open_vmlinux(module);
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, externs);
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,
struct perf_probe_point *pp) struct perf_probe_point *pp)
{ {
struct symbol *sym;
sym = __find_kernel_function_by_name(tp->symbol, NULL);
if (!sym) {
pr_err("Failed to find symbol %s in kernel.\n", tp->symbol);
return -ENOENT;
}
pp->function = strdup(tp->symbol); pp->function = strdup(tp->symbol);
if (pp->function == NULL) if (pp->function == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -394,7 +493,7 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, ...@@ -394,7 +493,7 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
static int try_to_find_probe_trace_events(struct perf_probe_event *pev, static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
struct probe_trace_event **tevs __unused, struct probe_trace_event **tevs __unused,
int max_tevs __unused) int max_tevs __unused, const char *mod __unused)
{ {
if (perf_probe_event_need_dwarf(pev)) { if (perf_probe_event_need_dwarf(pev)) {
pr_warning("Debuginfo-analysis is not supported.\n"); pr_warning("Debuginfo-analysis is not supported.\n");
...@@ -403,12 +502,19 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, ...@@ -403,12 +502,19 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
return 0; return 0;
} }
int show_line_range(struct line_range *lr __unused) int show_line_range(struct line_range *lr __unused, const char *module __unused)
{ {
pr_warning("Debuginfo-analysis is not supported.\n"); pr_warning("Debuginfo-analysis is not supported.\n");
return -ENOSYS; return -ENOSYS;
} }
int show_available_vars(struct perf_probe_event *pevs __unused,
int npevs __unused, int max_vls __unused,
const char *module __unused, bool externs __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)
...@@ -1087,7 +1193,7 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev) ...@@ -1087,7 +1193,7 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)
} }
static int convert_to_perf_probe_event(struct probe_trace_event *tev, static int convert_to_perf_probe_event(struct probe_trace_event *tev,
struct perf_probe_event *pev) struct perf_probe_event *pev)
{ {
char buf[64] = ""; char buf[64] = "";
int i, ret; int i, ret;
...@@ -1516,14 +1622,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, ...@@ -1516,14 +1622,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
static int convert_to_probe_trace_events(struct perf_probe_event *pev, static int convert_to_probe_trace_events(struct perf_probe_event *pev,
struct probe_trace_event **tevs, struct probe_trace_event **tevs,
int max_tevs) int max_tevs, const char *module)
{ {
struct symbol *sym; struct symbol *sym;
int ret = 0, i; int ret = 0, i;
struct probe_trace_event *tev; struct probe_trace_event *tev;
/* Convert perf_probe_event with debuginfo */ /* Convert perf_probe_event with debuginfo */
ret = try_to_find_probe_trace_events(pev, tevs, max_tevs); ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module);
if (ret != 0) if (ret != 0)
return ret; return ret;
...@@ -1572,8 +1678,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, ...@@ -1572,8 +1678,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
} }
/* Currently just checking function name from symbol map */ /* Currently just checking function name from symbol map */
sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], sym = __find_kernel_function_by_name(tev->point.symbol, NULL);
tev->point.symbol, NULL);
if (!sym) { if (!sym) {
pr_warning("Kernel symbol \'%s\' not found.\n", pr_warning("Kernel symbol \'%s\' not found.\n",
tev->point.symbol); tev->point.symbol);
...@@ -1596,7 +1701,7 @@ struct __event_package { ...@@ -1596,7 +1701,7 @@ struct __event_package {
}; };
int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
bool force_add, int max_tevs) int max_tevs, const char *module, bool force_add)
{ {
int i, j, ret; int i, j, ret;
struct __event_package *pkgs; struct __event_package *pkgs;
...@@ -1617,7 +1722,9 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, ...@@ -1617,7 +1722,9 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
pkgs[i].pev = &pevs[i]; pkgs[i].pev = &pevs[i];
/* Convert with or without debuginfo */ /* Convert with or without debuginfo */
ret = convert_to_probe_trace_events(pkgs[i].pev, ret = convert_to_probe_trace_events(pkgs[i].pev,
&pkgs[i].tevs, max_tevs); &pkgs[i].tevs,
max_tevs,
module);
if (ret < 0) if (ret < 0)
goto end; goto end;
pkgs[i].ntevs = ret; pkgs[i].ntevs = ret;
......
...@@ -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);
...@@ -109,12 +115,18 @@ extern void clear_perf_probe_event(struct perf_probe_event *pev); ...@@ -109,12 +115,18 @@ extern void clear_perf_probe_event(struct perf_probe_event *pev);
/* Command string to line-range */ /* Command string to line-range */
extern int parse_line_range_desc(const char *cmd, struct line_range *lr); extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
/* Internal use: Return kernel/module path */
extern const char *kernel_get_module_path(const char *module);
extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
bool force_add, int max_probe_points); int max_probe_points, const char *module,
bool force_add);
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, const char *module);
extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
int max_probe_points, const char *module,
bool externs);
/* Maximum index number of event-name postfix */ /* Maximum index number of event-name postfix */
......
This diff is collapsed.
...@@ -22,20 +22,27 @@ extern int find_probe_trace_events(int fd, struct perf_probe_event *pev, ...@@ -22,20 +22,27 @@ extern int find_probe_trace_events(int fd, struct perf_probe_event *pev,
int max_tevs); int max_tevs);
/* Find a perf_probe_point from debuginfo */ /* Find a perf_probe_point from debuginfo */
extern int find_perf_probe_point(int fd, unsigned long addr, extern int find_perf_probe_point(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,
bool externs);
#include <dwarf.h> #include <dwarf.h>
#include <libdw.h> #include <libdw.h>
#include <libdwfl.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 +60,22 @@ struct probe_finder { ...@@ -53,6 +60,22 @@ 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 */
bool externs; /* Find external vars too */
bool child; /* Search child scopes */
};
struct line_finder { struct line_finder {
struct line_range *lr; /* Target line range */ struct line_range *lr; /* Target line range */
......
#include <slang.h>
#include "libslang.h" #include "libslang.h"
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/list.h> #include <linux/list.h>
......
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