perf probe: Support adding probes on offline kernel modules
Masami Hiramatsu [Mon, 27 Jun 2011 07:27:51 +0000 (16:27 +0900)]
Support adding probes on offline kernel modules. This enables
perf-probe to trace kernel-module init functions via perf-probe.
If user gives the path of module with -m option, perf-probe
expects the module is offline.
This feature works with --add, --funcs, and --vars.

E.g)
 # perf probe -m /lib/modules/`uname -r`/kernel/fs/btrfs/btrfs.ko \
   -a "extent_io_init:5 extent_state_cache"
 Add new events:
   probe:extent_io_init (on extent_io_init:5 with extent_state_cache)
   probe:extent_io_init_1 (on extent_io_init:5 with extent_state_cache)

 You can now use it on all perf tools, such as:

         perf record -e probe:extent_io_init_1 -aR sleep 1

Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Link: http://lkml.kernel.org/r/20110627072751.6528.10230.stgit@fedora15
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>

tools/perf/Documentation/perf-probe.txt
tools/perf/builtin-probe.c
tools/perf/util/probe-event.c

index 02bafce..2780d9c 100644 (file)
@@ -34,9 +34,11 @@ OPTIONS
        Specify vmlinux path which has debuginfo (Dwarf binary).
 
 -m::
---module=MODNAME::
+--module=MODNAME|PATH::
        Specify module name in which perf-probe searches probe points
-       or lines.
+       or lines. If a path of module file is passed, perf-probe
+       treat it as an offline module (this means you can add a probe on
+        a module which has not been loaded yet).
 
 -s::
 --source=PATH::
index 2c0e64d..5f2a5c7 100644 (file)
@@ -242,7 +242,8 @@ static const struct option options[] = {
        OPT_STRING('s', "source", &symbol_conf.source_prefix,
                   "directory", "path to kernel source"),
        OPT_STRING('m', "module", &params.target_module,
-                  "modname", "target module name"),
+                  "modname|path",
+                  "target module name (for online) or path (for offline)"),
 #endif
        OPT__DRY_RUN(&probe_event_dry_run),
        OPT_INTEGER('\0', "max-probes", &params.max_probe_points,
index ee3f41e..b82d54f 100644 (file)
@@ -117,6 +117,10 @@ static struct map *kernel_get_module_map(const char *module)
        struct rb_node *nd;
        struct map_groups *grp = &machine.kmaps;
 
+       /* A file path -- this is an offline module */
+       if (module && strchr(module, '/'))
+               return machine__new_module(&machine, 0, module);
+
        if (!module)
                module = "kernel";
 
@@ -173,12 +177,19 @@ const char *kernel_get_module_path(const char *module)
 /* Open new debuginfo of given module */
 static struct debuginfo *open_debuginfo(const char *module)
 {
-       const char *path = kernel_get_module_path(module);
+       const char *path;
 
-       if (!path) {
-               pr_err("Failed to find path of %s module.\n",
-                      module ?: "kernel");
-               return NULL;
+       /* A file path -- this is an offline module */
+       if (module && strchr(module, '/'))
+               path = module;
+       else {
+               path = kernel_get_module_path(module);
+
+               if (!path) {
+                       pr_err("Failed to find path of %s module.\n",
+                              module ?: "kernel");
+                       return NULL;
+               }
        }
        return debuginfo__new(path);
 }
@@ -229,13 +240,36 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
 static int add_module_to_probe_trace_events(struct probe_trace_event *tevs,
                                            int ntevs, const char *module)
 {
-       int i;
+       int i, ret = 0;
+       char *tmp;
+
+       if (!module)
+               return 0;
+
+       tmp = strrchr(module, '/');
+       if (tmp) {
+               /* This is a module path -- get the module name */
+               module = strdup(tmp + 1);
+               if (!module)
+                       return -ENOMEM;
+               tmp = strchr(module, '.');
+               if (tmp)
+                       *tmp = '\0';
+               tmp = (char *)module;   /* For free() */
+       }
+
        for (i = 0; i < ntevs; i++) {
                tevs[i].point.module = strdup(module);
-               if (!tevs[i].point.module)
-                       return -ENOMEM;
+               if (!tevs[i].point.module) {
+                       ret = -ENOMEM;
+                       break;
+               }
        }
-       return 0;
+
+       if (tmp)
+               free(tmp);
+
+       return ret;
 }
 
 /* Try to find perf_probe_event with debuginfo */