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

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: default avatarMasami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent fb8c5a56
...@@ -16,9 +16,9 @@ or ...@@ -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 ...@@ -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 { ...@@ -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[] = { ...@@ -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[] = { ...@@ -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", &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,
...@@ -278,7 +281,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) ...@@ -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(&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;
...@@ -291,6 +294,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) ...@@ -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) ...@@ -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, ...@@ -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;
...@@ -421,7 +447,7 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev) ...@@ -1159,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;
...@@ -1588,14 +1622,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, ...@@ -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, ...@@ -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 { ...@@ -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, ...@@ -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); ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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 @@ int find_perf_probe_point(int fd, unsigned long addr, ...@@ -1491,7 +1597,8 @@ int find_perf_probe_point(int fd, unsigned long addr,
} }
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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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 {
......
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