diff options
author | Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | 2010-10-21 19:13:41 +0900 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2010-10-21 16:11:44 -0200 |
commit | 469b9b88488e89114bb3e9ac5ee7906b7b96123f (patch) | |
tree | 9f002195a8b1167eddbe560ac834e6ba2dedb19f /tools/perf/util/probe-finder.c | |
parent | fb8c5a56c7ddbc2b0d2ee7a8da60fe1355f75141 (diff) |
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>
Diffstat (limited to 'tools/perf/util/probe-finder.c')
-rw-r--r-- | tools/perf/util/probe-finder.c | 142 |
1 files changed, 126 insertions, 16 deletions
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index a274fd0c143e..c20bd52833aa 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -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 */ /* 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; size_t cuhl; Dwarf_Die *diep; - Dwarf *dbg; + Dwarf *dbg = NULL; + Dwfl *dwfl; + Dwarf_Addr bias; /* Currently ignored */ int ret = 0; - dbg = dwarf_begin(fd, DWARF_C_READ); + dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias); if (!dbg) { pr_warning("No dwarf info found in the vmlinux - " "please rebuild with CONFIG_DEBUG_INFO=y.\n"); @@ -1221,7 +1318,8 @@ static int find_probes(int fd, struct probe_finder *pf) off = noff; } line_list__free(&pf->lcache); - dwarf_end(dbg); + if (dwfl) + dwfl_end(dwfl); return ret; } @@ -1412,23 +1510,31 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev, } /* Reverse search */ -int find_perf_probe_point(int fd, unsigned long addr, - struct perf_probe_point *ppt) +int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt) { Dwarf_Die cudie, spdie, indie; - Dwarf *dbg; + Dwarf *dbg = NULL; + Dwfl *dwfl = NULL; Dwarf_Line *line; - Dwarf_Addr laddr, eaddr; + Dwarf_Addr laddr, eaddr, bias = 0; const char *tmp; int lineno, ret = 0; bool found = false; - dbg = dwarf_begin(fd, DWARF_C_READ); - if (!dbg) - return -EBADF; + /* Open the live linux kernel */ + dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias); + 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 */ - 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; goto end; } @@ -1491,7 +1597,8 @@ found: } end: - dwarf_end(dbg); + if (dwfl) + dwfl_end(dwfl); if (ret >= 0) ret = found ? 1 : 0; 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_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 && die_compare_name(sp_die, lr->function)) { 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; size_t cuhl; Dwarf_Die *diep; - Dwarf *dbg; + Dwarf *dbg = NULL; + Dwfl *dwfl; + Dwarf_Addr bias; /* Currently ignored */ const char *comp_dir; - dbg = dwarf_begin(fd, DWARF_C_READ); + dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias); if (!dbg) { pr_warning("No dwarf info found in the vmlinux - " "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); - dwarf_end(dbg); - + dwfl_end(dwfl); return (ret < 0) ? ret : lf.found; } |