perf probe: Add basic module support
Add basic module probe support on perf probe. This introduces "--module <MODNAME>" option to perf probe for putting probes and showing lines and variables in the given module. Currently, this supports only probing on running modules. Supporting off-line module probing is the next step. e.g.) [show lines] # ./perf probe --module drm -L drm_vblank_info <drm_vblank_info:0> 0 int drm_vblank_info(struct seq_file *m, void *data) 1 { struct drm_info_node *node = (struct drm_info_node *) m->private 3 struct drm_device *dev = node->minor->dev; ... [show vars] # ./perf probe --module drm -V drm_vblank_info:3 Available variables at drm_vblank_info:3 @<drm_vblank_info+20> (unknown_type) data struct drm_info_node* node struct seq_file* m [put a probe] # ./perf probe --module drm drm_vblank_info:3 node m Add new event: probe:drm_vblank_info (on drm_vblank_info:3 with node m) You can now use it on all perf tools, such as: perf record -e probe:drm_vblank_info -aR sleep 1 [list probes] # ./perf probe -l probe:drm_vblank_info (on drm_vblank_info:3@drivers/gpu/drm/drm_info.c with ... 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: <20101021101341.3542.71638.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
fb8c5a56c7
commit
469b9b8848
|
@ -16,9 +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
|
or
|
||||||
'perf probe' [--externs] --vars='PROBEPOINT'
|
'perf probe' [options] --vars='PROBEPOINT'
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
@ -33,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 +57,7 @@ static struct {
|
||||||
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;
|
||||||
|
|
||||||
|
@ -162,8 +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 [--externs] --vars 'PROBEPOINT'",
|
"perf probe [<options>] --vars 'PROBEPOINT'",
|
||||||
#endif
|
#endif
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
@ -214,6 +215,8 @@ static const struct option options[] = {
|
||||||
"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", ¶ms.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", ¶ms.max_probe_points,
|
OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points,
|
||||||
|
@ -278,7 +281,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = show_line_range(¶ms.line_range);
|
ret = show_line_range(¶ms.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;
|
||||||
|
@ -291,6 +294,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
|
||||||
}
|
}
|
||||||
ret = show_available_vars(params.events, params.nevents,
|
ret = show_available_vars(params.events, params.nevents,
|
||||||
params.max_probe_points,
|
params.max_probe_points,
|
||||||
|
params.target_module,
|
||||||
params.show_ext_vars);
|
params.show_ext_vars);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
pr_err(" Error: Failed to show vars. (%d)\n", ret);
|
pr_err(" Error: Failed to show vars. (%d)\n", ret);
|
||||||
|
@ -310,6 +314,7 @@ 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.max_probe_points,
|
params.max_probe_points,
|
||||||
|
params.target_module,
|
||||||
params.force_add);
|
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);
|
||||||
|
|
|
@ -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, ...)
|
||||||
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)
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DWARF_SUPPORT
|
static struct symbol *__find_kernel_function_by_name(const char *name,
|
||||||
static int open_vmlinux(void)
|
struct map **mapp)
|
||||||
{
|
{
|
||||||
if (map__load(machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) {
|
return machine__find_kernel_function_by_name(&machine, name, mapp,
|
||||||
pr_debug("Failed to load kernel map.\n");
|
NULL);
|
||||||
return -EINVAL;
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pr_debug("Try to open %s\n", machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name);
|
found:
|
||||||
return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY);
|
return dso->long_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DWARF_SUPPORT
|
||||||
|
static int open_vmlinux(const char *module)
|
||||||
|
{
|
||||||
|
const char *path = kernel_get_module_path(module);
|
||||||
|
if (!path) {
|
||||||
|
pr_err("Failed to find path of %s module", module ?: "kernel");
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
pr_debug("Try to open %s\n", path);
|
||||||
|
return open(path, O_RDONLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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,
|
||||||
/* 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 @@ error:
|
||||||
* 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)
|
||||||
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;
|
||||||
|
@ -421,7 +447,7 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev,
|
||||||
|
|
||||||
/* Show available variables on given probe point */
|
/* Show available variables on given probe point */
|
||||||
int show_available_vars(struct perf_probe_event *pevs, int npevs,
|
int show_available_vars(struct perf_probe_event *pevs, int npevs,
|
||||||
int max_vls, bool externs)
|
int max_vls, const char *module, bool externs)
|
||||||
{
|
{
|
||||||
int i, fd, ret = 0;
|
int i, fd, ret = 0;
|
||||||
|
|
||||||
|
@ -429,7 +455,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
|
||||||
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;
|
||||||
|
@ -447,8 +473,15 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
|
||||||
#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;
|
||||||
|
@ -460,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");
|
||||||
|
@ -469,14 +502,15 @@ 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 show_available_vars(struct perf_probe_event *pevs __unused,
|
||||||
int npevs __unused, int max_probe_points __unused)
|
int npevs __unused, int max_vls __unused,
|
||||||
|
const char *module __unused, bool externs __unused)
|
||||||
{
|
{
|
||||||
pr_warning("Debuginfo-analysis is not supported.\n");
|
pr_warning("Debuginfo-analysis is not supported.\n");
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
|
@ -1159,7 +1193,7 @@ error:
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
@ -1588,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;
|
||||||
|
|
||||||
|
@ -1644,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);
|
||||||
|
@ -1668,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,
|
||||||
int max_tevs, bool force_add)
|
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;
|
||||||
|
@ -1689,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;
|
||||||
|
|
|
@ -115,14 +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,
|
||||||
int max_probe_points, bool force_add);
|
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,
|
extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
|
||||||
int max_probe_points, bool externs);
|
int max_probe_points, const char *module,
|
||||||
|
bool externs);
|
||||||
|
|
||||||
|
|
||||||
/* Maximum index number of event-name postfix */
|
/* Maximum index number of event-name postfix */
|
||||||
|
|
|
@ -116,6 +116,101 @@ static void line_list__free(struct list_head *head)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Dwarf FL wrappers */
|
||||||
|
|
||||||
|
static int __linux_kernel_find_elf(Dwfl_Module *mod,
|
||||||
|
void **userdata,
|
||||||
|
const char *module_name,
|
||||||
|
Dwarf_Addr base,
|
||||||
|
char **file_name, Elf **elfp)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
const char *path = kernel_get_module_path(module_name);
|
||||||
|
|
||||||
|
if (path) {
|
||||||
|
fd = open(path, O_RDONLY);
|
||||||
|
if (fd >= 0) {
|
||||||
|
*file_name = strdup(path);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* If failed, try to call standard method */
|
||||||
|
return dwfl_linux_kernel_find_elf(mod, userdata, module_name, base,
|
||||||
|
file_name, elfp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *debuginfo_path; /* Currently dummy */
|
||||||
|
|
||||||
|
static const Dwfl_Callbacks offline_callbacks = {
|
||||||
|
.find_debuginfo = dwfl_standard_find_debuginfo,
|
||||||
|
.debuginfo_path = &debuginfo_path,
|
||||||
|
|
||||||
|
.section_address = dwfl_offline_section_address,
|
||||||
|
|
||||||
|
/* We use this table for core files too. */
|
||||||
|
.find_elf = dwfl_build_id_find_elf,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const Dwfl_Callbacks kernel_callbacks = {
|
||||||
|
.find_debuginfo = dwfl_standard_find_debuginfo,
|
||||||
|
.debuginfo_path = &debuginfo_path,
|
||||||
|
|
||||||
|
.find_elf = __linux_kernel_find_elf,
|
||||||
|
.section_address = dwfl_linux_kernel_module_section_address,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Get a Dwarf from offline image */
|
||||||
|
static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias)
|
||||||
|
{
|
||||||
|
Dwfl_Module *mod;
|
||||||
|
Dwarf *dbg = NULL;
|
||||||
|
|
||||||
|
if (!dwflp)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
*dwflp = dwfl_begin(&offline_callbacks);
|
||||||
|
if (!*dwflp)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
mod = dwfl_report_offline(*dwflp, "", "", fd);
|
||||||
|
if (!mod)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
dbg = dwfl_module_getdwarf(mod, bias);
|
||||||
|
if (!dbg) {
|
||||||
|
error:
|
||||||
|
dwfl_end(*dwflp);
|
||||||
|
*dwflp = NULL;
|
||||||
|
}
|
||||||
|
return dbg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get a Dwarf from live kernel image */
|
||||||
|
static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp,
|
||||||
|
Dwarf_Addr *bias)
|
||||||
|
{
|
||||||
|
Dwarf *dbg;
|
||||||
|
|
||||||
|
if (!dwflp)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
*dwflp = dwfl_begin(&kernel_callbacks);
|
||||||
|
if (!*dwflp)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Load the kernel dwarves: Don't care the result here */
|
||||||
|
dwfl_linux_kernel_report_kernel(*dwflp);
|
||||||
|
dwfl_linux_kernel_report_modules(*dwflp);
|
||||||
|
|
||||||
|
dbg = dwfl_addrdwarf(*dwflp, addr, bias);
|
||||||
|
/* Here, check whether we could get a real dwarf */
|
||||||
|
if (!dbg) {
|
||||||
|
dwfl_end(*dwflp);
|
||||||
|
*dwflp = NULL;
|
||||||
|
}
|
||||||
|
return dbg;
|
||||||
|
}
|
||||||
|
|
||||||
/* Dwarf wrappers */
|
/* Dwarf wrappers */
|
||||||
|
|
||||||
/* Find the realpath of the target file. */
|
/* Find the realpath of the target file. */
|
||||||
|
@ -1177,10 +1272,12 @@ static int find_probes(int fd, struct probe_finder *pf)
|
||||||
Dwarf_Off off, noff;
|
Dwarf_Off off, noff;
|
||||||
size_t cuhl;
|
size_t cuhl;
|
||||||
Dwarf_Die *diep;
|
Dwarf_Die *diep;
|
||||||
Dwarf *dbg;
|
Dwarf *dbg = NULL;
|
||||||
|
Dwfl *dwfl;
|
||||||
|
Dwarf_Addr bias; /* Currently ignored */
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
dbg = dwarf_begin(fd, DWARF_C_READ);
|
dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
|
||||||
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");
|
||||||
|
@ -1221,7 +1318,8 @@ static int find_probes(int fd, struct probe_finder *pf)
|
||||||
off = noff;
|
off = noff;
|
||||||
}
|
}
|
||||||
line_list__free(&pf->lcache);
|
line_list__free(&pf->lcache);
|
||||||
dwarf_end(dbg);
|
if (dwfl)
|
||||||
|
dwfl_end(dwfl);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1412,23 +1510,31 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reverse search */
|
/* Reverse search */
|
||||||
int find_perf_probe_point(int fd, unsigned long addr,
|
int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
|
||||||
struct perf_probe_point *ppt)
|
|
||||||
{
|
{
|
||||||
Dwarf_Die cudie, spdie, indie;
|
Dwarf_Die cudie, spdie, indie;
|
||||||
Dwarf *dbg;
|
Dwarf *dbg = NULL;
|
||||||
|
Dwfl *dwfl = NULL;
|
||||||
Dwarf_Line *line;
|
Dwarf_Line *line;
|
||||||
Dwarf_Addr laddr, eaddr;
|
Dwarf_Addr laddr, eaddr, bias = 0;
|
||||||
const char *tmp;
|
const char *tmp;
|
||||||
int lineno, ret = 0;
|
int lineno, ret = 0;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
dbg = dwarf_begin(fd, DWARF_C_READ);
|
/* Open the live linux kernel */
|
||||||
if (!dbg)
|
dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias);
|
||||||
return -EBADF;
|
if (!dbg) {
|
||||||
|
pr_warning("No dwarf info found in the vmlinux - "
|
||||||
|
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adjust address with bias */
|
||||||
|
addr += bias;
|
||||||
/* Find cu die */
|
/* Find cu die */
|
||||||
if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) {
|
if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) {
|
||||||
|
pr_warning("No CU DIE is found at %lx\n", addr);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
@ -1491,7 +1597,8 @@ found:
|
||||||
}
|
}
|
||||||
|
|
||||||
end:
|
end:
|
||||||
dwarf_end(dbg);
|
if (dwfl)
|
||||||
|
dwfl_end(dwfl);
|
||||||
if (ret >= 0)
|
if (ret >= 0)
|
||||||
ret = found ? 1 : 0;
|
ret = found ? 1 : 0;
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1624,6 +1731,8 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
|
||||||
struct line_finder *lf = param->data;
|
struct line_finder *lf = param->data;
|
||||||
struct line_range *lr = lf->lr;
|
struct line_range *lr = lf->lr;
|
||||||
|
|
||||||
|
pr_debug("find (%lx) %s\n", dwarf_dieoffset(sp_die),
|
||||||
|
dwarf_diename(sp_die));
|
||||||
if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
|
if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
|
||||||
die_compare_name(sp_die, lr->function)) {
|
die_compare_name(sp_die, lr->function)) {
|
||||||
lf->fname = dwarf_decl_file(sp_die);
|
lf->fname = dwarf_decl_file(sp_die);
|
||||||
|
@ -1667,10 +1776,12 @@ int find_line_range(int fd, struct line_range *lr)
|
||||||
Dwarf_Off off = 0, noff;
|
Dwarf_Off off = 0, noff;
|
||||||
size_t cuhl;
|
size_t cuhl;
|
||||||
Dwarf_Die *diep;
|
Dwarf_Die *diep;
|
||||||
Dwarf *dbg;
|
Dwarf *dbg = NULL;
|
||||||
|
Dwfl *dwfl;
|
||||||
|
Dwarf_Addr bias; /* Currently ignored */
|
||||||
const char *comp_dir;
|
const char *comp_dir;
|
||||||
|
|
||||||
dbg = dwarf_begin(fd, DWARF_C_READ);
|
dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
|
||||||
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");
|
||||||
|
@ -1716,8 +1827,7 @@ int find_line_range(int fd, struct line_range *lr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_debug("path: %s\n", lr->path);
|
pr_debug("path: %s\n", lr->path);
|
||||||
dwarf_end(dbg);
|
dwfl_end(dwfl);
|
||||||
|
|
||||||
return (ret < 0) ? ret : lf.found;
|
return (ret < 0) ? ret : lf.found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ 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 */
|
/* Find a line range */
|
||||||
|
@ -35,6 +35,7 @@ extern int find_available_vars_at(int fd, struct perf_probe_event *pev,
|
||||||
|
|
||||||
#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 {
|
||||||
|
|
Loading…
Reference in New Issue