perf probe: Fix module probe issue if no dwarf support
authorRavi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
Tue, 26 Apr 2016 14:25:40 +0000 (19:55 +0530)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Tue, 26 Apr 2016 16:15:01 +0000 (13:15 -0300)
Perf is not able to register probe in kernel module when dwarf supprt
is not there(and so it goes for symtab). Perf passes full path of
module where only module name is required which is causing the problem.
This patch fixes this issue.

Before applying patch:

  $ dpkg -s libdw-dev
  dpkg-query: package 'libdw-dev' is not installed and no information is...

  $ sudo ./perf probe -m /linux/samples/kprobes/kprobe_example.ko kprobe_init
  Added new event:
    probe:kprobe_init (on kprobe_init in /linux/samples/kprobes/kprobe_example.ko)

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

  perf record -e probe:kprobe_init -aR sleep 1

  $ sudo cat /sys/kernel/debug/tracing/kprobe_events
  p:probe/kprobe_init /linux/samples/kprobes/kprobe_example.ko:kprobe_init

  $ sudo ./perf record -a -e probe:kprobe_init
  [ perf record: Woken up 1 times to write data ]
  [ perf record: Captured and wrote 0.105 MB perf.data ]

  $ sudo ./perf script  # No output here

After applying patch:

  $ sudo ./perf probe -m /linux/samples/kprobes/kprobe_example.ko kprobe_init
  Added new event:
    probe:kprobe_init    (on kprobe_init in kprobe_example)

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

  perf record -e probe:kprobe_init -aR sleep 1

  $ sudo cat /sys/kernel/debug/tracing/kprobe_events
  p:probe/kprobe_init kprobe_example:kprobe_init

  $ sudo ./perf record -a -e probe:kprobe_init
  [ perf record: Woken up 1 times to write data ]
  [ perf record: Captured and wrote 0.105 MB perf.data (2 samples) ]

  $ sudo ./perf script
  insmod 13990 [002]  5961.216833: probe:kprobe_init: ...
  insmod 13995 [002]  5962.889384: probe:kprobe_init: ...

Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/1461680741-12517-1-git-send-email-ravi.bangoria@linux.vnet.ibm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/probe-event.c

index bc2eb7cda2d16c1f1738f237e186228d0b8615bb..a9774628c6f6a98109df78f0fd64ce2ea2bac5d1 100644 (file)
@@ -265,6 +265,65 @@ static bool kprobe_warn_out_range(const char *symbol, unsigned long address)
        return true;
 }
 
+/*
+ * NOTE:
+ * '.gnu.linkonce.this_module' section of kernel module elf directly
+ * maps to 'struct module' from linux/module.h. This section contains
+ * actual module name which will be used by kernel after loading it.
+ * But, we cannot use 'struct module' here since linux/module.h is not
+ * exposed to user-space. Offset of 'name' has remained same from long
+ * time, so hardcoding it here.
+ */
+#ifdef __LP64__
+#define MOD_NAME_OFFSET 24
+#else
+#define MOD_NAME_OFFSET 12
+#endif
+
+/*
+ * @module can be module name of module file path. In case of path,
+ * inspect elf and find out what is actual module name.
+ * Caller has to free mod_name after using it.
+ */
+static char *find_module_name(const char *module)
+{
+       int fd;
+       Elf *elf;
+       GElf_Ehdr ehdr;
+       GElf_Shdr shdr;
+       Elf_Data *data;
+       Elf_Scn *sec;
+       char *mod_name = NULL;
+
+       fd = open(module, O_RDONLY);
+       if (fd < 0)
+               return NULL;
+
+       elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+       if (elf == NULL)
+               goto elf_err;
+
+       if (gelf_getehdr(elf, &ehdr) == NULL)
+               goto ret_err;
+
+       sec = elf_section_by_name(elf, &ehdr, &shdr,
+                       ".gnu.linkonce.this_module", NULL);
+       if (!sec)
+               goto ret_err;
+
+       data = elf_getdata(sec, NULL);
+       if (!data || !data->d_buf)
+               goto ret_err;
+
+       mod_name = strdup((char *)data->d_buf + MOD_NAME_OFFSET);
+
+ret_err:
+       elf_end(elf);
+elf_err:
+       close(fd);
+       return mod_name;
+}
+
 #ifdef HAVE_DWARF_SUPPORT
 
 static int kernel_get_module_dso(const char *module, struct dso **pdso)
@@ -2512,6 +2571,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
        struct probe_trace_point *tp;
        int num_matched_functions;
        int ret, i, j, skipped = 0;
+       char *mod_name;
 
        map = get_target_map(pev->target, pev->uprobes);
        if (!map) {
@@ -2596,9 +2656,19 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
                tp->realname = strdup_or_goto(sym->name, nomem_out);
 
                tp->retprobe = pp->retprobe;
-               if (pev->target)
-                       tev->point.module = strdup_or_goto(pev->target,
-                                                          nomem_out);
+               if (pev->target) {
+                       if (pev->uprobes) {
+                               tev->point.module = strdup_or_goto(pev->target,
+                                                                  nomem_out);
+                       } else {
+                               mod_name = find_module_name(pev->target);
+                               tev->point.module =
+                                       strdup(mod_name ? mod_name : pev->target);
+                               free(mod_name);
+                               if (!tev->point.module)
+                                       goto nomem_out;
+                       }
+               }
                tev->uprobes = pev->uprobes;
                tev->nargs = pev->nargs;
                if (tev->nargs) {