perf probe: Add --funcs to show available functions in symtab
authorMasami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Thu, 13 Jan 2011 12:46:11 +0000 (21:46 +0900)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Mon, 24 Jan 2011 12:57:55 +0000 (10:57 -0200)
Add --funcs to show available functions in symtab.

Originally this feature came from Srikar's uprobes patches
( http://lkml.org/lkml/2010/8/27/244 )

e.g.
...
__ablkcipher_walk_complete
__absent_pages_in_range
__account_scheduler_latency
__add_pages
__alloc_pages_nodemask
__alloc_percpu
__alloc_reserved_percpu
__alloc_skb
__alloc_workqueue_key
__any_online_cpu
__ata_ehi_push_desc
...

This also supports symbols in module, e.g.

...
cleanup_module
cpuid_maxphyaddr
emulate_clts
emulate_instruction
emulate_int_real
emulate_invlpg
emulator_get_dr
emulator_set_dr
emulator_task_switch
emulator_write_emulated
emulator_write_phys
fx_init
...

Original-patch-from: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Franck Bui-Huu <fbuihuu@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <20110113124611.22426.10835.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
[ committer note: Add missing elf.h for STB_GLOBAL that broke a RHEL4 build ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/Documentation/perf-probe.txt
tools/perf/builtin-probe.c
tools/perf/util/probe-event.c
tools/perf/util/probe-event.h

index 86b797a35aa6acae540b652345937991257daa4d..fcc51fe0195c0b12008ced2899c5ca4c2a353a6e 100644 (file)
@@ -73,6 +73,10 @@ OPTIONS
        (Only for --vars) Show external defined variables in addition to local
        variables.
 
+-F::
+--funcs::
+       Show available functions in given module or kernel.
+
 -f::
 --force::
        Forcibly add events with existing name.
index add163c9f0e7d4db3fe01afe704a72c5a923597b..6cf708aba7c95325a6c07bc26dab22fd46dd4891 100644 (file)
@@ -52,6 +52,7 @@ static struct {
        bool show_lines;
        bool show_vars;
        bool show_ext_vars;
+       bool show_funcs;
        bool mod_events;
        int nevents;
        struct perf_probe_event events[MAX_PROBES];
@@ -221,6 +222,8 @@ static const struct option options[] = {
        OPT__DRY_RUN(&probe_event_dry_run),
        OPT_INTEGER('\0', "max-probes", &params.max_probe_points,
                 "Set how many probe points can be found for a probe."),
+       OPT_BOOLEAN('F', "funcs", &params.show_funcs,
+                   "Show potential probe-able functions."),
        OPT_END()
 };
 
@@ -246,7 +249,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
                params.max_probe_points = MAX_PROBES;
 
        if ((!params.nevents && !params.dellist && !params.list_events &&
-            !params.show_lines))
+            !params.show_lines && !params.show_funcs))
                usage_with_options(probe_usage, options);
 
        /*
@@ -267,12 +270,36 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
                        pr_err(" Error: Don't use --list with --vars.\n");
                        usage_with_options(probe_usage, options);
                }
+               if (params.show_funcs) {
+                       pr_err("  Error: Don't use --list with --funcs.\n");
+                       usage_with_options(probe_usage, options);
+               }
                ret = show_perf_probe_events();
                if (ret < 0)
                        pr_err("  Error: Failed to show event list. (%d)\n",
                               ret);
                return ret;
        }
+       if (params.show_funcs) {
+               if (params.nevents != 0 || params.dellist) {
+                       pr_err("  Error: Don't use --funcs with"
+                              " --add/--del.\n");
+                       usage_with_options(probe_usage, options);
+               }
+               if (params.show_lines) {
+                       pr_err("  Error: Don't use --funcs with --line.\n");
+                       usage_with_options(probe_usage, options);
+               }
+               if (params.show_vars) {
+                       pr_err("  Error: Don't use --funcs with --vars.\n");
+                       usage_with_options(probe_usage, options);
+               }
+               ret = show_available_funcs(params.target_module);
+               if (ret < 0)
+                       pr_err("  Error: Failed to show functions."
+                              " (%d)\n", ret);
+               return ret;
+       }
 
 #ifdef DWARF_SUPPORT
        if (params.show_lines) {
index 6e29d9c9dcccb50f63adac097bc512b56fb3e28c..859d377a3df3d7128f0bbedc6edcfcd00e42586b 100644 (file)
@@ -31,6 +31,7 @@
 #include <string.h>
 #include <stdarg.h>
 #include <limits.h>
+#include <elf.h>
 
 #undef _GNU_SOURCE
 #include "util.h"
@@ -111,7 +112,25 @@ static struct symbol *__find_kernel_function_by_name(const char *name,
                                                     NULL);
 }
 
-const char *kernel_get_module_path(const char *module)
+static struct map *kernel_get_module_map(const char *module)
+{
+       struct rb_node *nd;
+       struct map_groups *grp = &machine.kmaps;
+
+       if (!module)
+               module = "kernel";
+
+       for (nd = rb_first(&grp->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) {
+               struct map *pos = rb_entry(nd, struct map, rb_node);
+               if (strncmp(pos->dso->short_name + 1, module,
+                           pos->dso->short_name_len - 2) == 0) {
+                       return pos;
+               }
+       }
+       return NULL;
+}
+
+static struct dso *kernel_get_module_dso(const char *module)
 {
        struct dso *dso;
        struct map *map;
@@ -141,7 +160,13 @@ const char *kernel_get_module_path(const char *module)
                }
        }
 found:
-       return dso->long_name;
+       return dso;
+}
+
+const char *kernel_get_module_path(const char *module)
+{
+       struct dso *dso = kernel_get_module_dso(module);
+       return (dso) ? dso->long_name : NULL;
 }
 
 #ifdef DWARF_SUPPORT
@@ -1913,3 +1938,42 @@ int del_perf_probe_events(struct strlist *dellist)
        return ret;
 }
 
+/*
+ * If a symbol corresponds to a function with global binding return 0.
+ * For all others return 1.
+ */
+static int filter_non_global_functions(struct map *map __unused,
+                                       struct symbol *sym)
+{
+       if (sym->binding != STB_GLOBAL)
+               return 1;
+
+       return 0;
+}
+
+int show_available_funcs(const char *module)
+{
+       struct map *map;
+       int ret;
+
+       setup_pager();
+
+       ret = init_vmlinux();
+       if (ret < 0)
+               return ret;
+
+       map = kernel_get_module_map(module);
+       if (!map) {
+               pr_err("Failed to find %s map.\n", (module) ? : "kernel");
+               return -EINVAL;
+       }
+       if (map__load(map, filter_non_global_functions)) {
+               pr_err("Failed to load map.\n");
+               return -EINVAL;
+       }
+       if (!dso__sorted_by_name(map->dso, map->type))
+               dso__sort_by_name(map->dso, map->type);
+
+       dso__fprintf_symbols_by_name(map->dso, map->type, stdout);
+       return 0;
+}
index 5accbedfea372b6761727c0fc1c824e237f12c72..1fb4f18337d3c1f7b5ae51b2a4938483933a97a0 100644 (file)
@@ -127,6 +127,7 @@ extern int show_line_range(struct line_range *lr, const char *module);
 extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
                               int max_probe_points, const char *module,
                               bool externs);
+extern int show_available_funcs(const char *module);
 
 
 /* Maximum index number of event-name postfix */