perf probe: Add --list option for listing current probe events
Masami Hiramatsu [Tue, 1 Dec 2009 00:20:17 +0000 (19:20 -0500)]
Add --list option for listing currently defined probe events
in the kernel. This shows events in below format;

 [group:event] <perf-probe probe-definition>

for example:

 [probe:schedule_0] schedule+30 cpu

Note that source file/line information is not supported yet.
So even if you added a probe by line, it will be shown in
<symbol+offset>.

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: systemtap <systemtap@sources.redhat.com>
Cc: DLE <dle-develop@lists.sourceforge.net>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Jim Keniston <jkenisto@us.ibm.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Frank Ch. Eigler <fche@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jason Baron <jbaron@redhat.com>
Cc: K.Prasad <prasad@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <20091201002017.10235.76575.stgit@harusame>
Signed-off-by: Ingo Molnar <mingo@elte.hu>

tools/perf/builtin-probe.c
tools/perf/util/probe-event.c
tools/perf/util/probe-event.h

index bf20df2..b5d15cf 100644 (file)
@@ -62,6 +62,8 @@ static struct {
        struct probe_point probes[MAX_PROBES];
 } session;
 
+static bool listing;
+
 /* Parse an event definition. Note that any error must die. */
 static void parse_probe_event(const char *str)
 {
@@ -119,6 +121,7 @@ static int open_default_vmlinux(void)
 static const char * const probe_usage[] = {
        "perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]",
        "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
+       "perf probe --list",
        NULL
 };
 
@@ -129,6 +132,7 @@ static const struct option options[] = {
        OPT_STRING('k', "vmlinux", &session.vmlinux, "file",
                "vmlinux/module pathname"),
 #endif
+       OPT_BOOLEAN('l', "list", &listing, "list up current probes"),
        OPT_CALLBACK('a', "add", NULL,
 #ifdef NO_LIBDWARF
                "FUNC[+OFFS|%return] [ARG ...]",
@@ -164,9 +168,15 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
        for (i = 0; i < argc; i++)
                parse_probe_event(argv[i]);
 
-       if (session.nr_probe == 0)
+       if ((session.nr_probe == 0 && !listing) ||
+           (session.nr_probe != 0 && listing))
                usage_with_options(probe_usage, options);
 
+       if (listing) {
+               show_perf_probe_events();
+               return 0;
+       }
+
        if (session.need_dwarf)
 #ifdef NO_LIBDWARF
                die("Debuginfo-analysis is not supported");
index e3a683a..7f4f288 100644 (file)
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
+#include <stdarg.h>
+#include <limits.h>
 
 #undef _GNU_SOURCE
 #include "event.h"
 #include "string.h"
+#include "strlist.h"
 #include "debug.h"
 #include "parse-events.h"  /* For debugfs_path */
 #include "probe-event.h"
 
 #define semantic_error(msg ...) die("Semantic error :" msg)
 
+/* If there is no space to write, returns -E2BIG. */
+static int e_snprintf(char *str, size_t size, const char *format, ...)
+{
+       int ret;
+       va_list ap;
+       va_start(ap, format);
+       ret = vsnprintf(str, size, format, ap);
+       va_end(ap);
+       if (ret >= (int)size)
+               ret = -E2BIG;
+       return ret;
+}
+
 /* Parse probepoint definition. */
 static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
 {
@@ -166,6 +182,103 @@ int parse_perf_probe_event(const char *str, struct probe_point *pp)
        return need_dwarf;
 }
 
+/* Parse kprobe_events event into struct probe_point */
+void parse_trace_kprobe_event(const char *str, char **group, char **event,
+                             struct probe_point *pp)
+{
+       char pr;
+       char *p;
+       int ret, i, argc;
+       char **argv;
+
+       pr_debug("Parsing kprobe_events: %s\n", str);
+       argv = argv_split(str, &argc);
+       if (!argv)
+               die("argv_split failed.");
+       if (argc < 2)
+               semantic_error("Too less arguments.");
+
+       /* Scan event and group name. */
+       ret = sscanf(argv[0], "%c:%m[^/ \t]/%m[^ \t]",
+                    &pr, group, event);
+       if (ret != 3)
+               semantic_error("Failed to parse event name: %s", argv[0]);
+       pr_debug("Group:%s Event:%s probe:%c\n", *group, *event, pr);
+
+       if (!pp)
+               goto end;
+
+       pp->retprobe = (pr == 'r');
+
+       /* Scan function name and offset */
+       ret = sscanf(argv[1], "%m[^+]+%d", &pp->function, &pp->offset);
+       if (ret == 1)
+               pp->offset = 0;
+
+       /* kprobe_events doesn't have this information */
+       pp->line = 0;
+       pp->file = NULL;
+
+       pp->nr_args = argc - 2;
+       pp->args = zalloc(sizeof(char *) * pp->nr_args);
+       for (i = 0; i < pp->nr_args; i++) {
+               p = strchr(argv[i + 2], '=');
+               if (p)  /* We don't need which register is assigned. */
+                       *p = '\0';
+               pp->args[i] = strdup(argv[i + 2]);
+               if (!pp->args[i])
+                       die("Failed to copy argument.");
+       }
+
+end:
+       argv_free(argv);
+}
+
+int synthesize_perf_probe_event(struct probe_point *pp)
+{
+       char *buf;
+       char offs[64] = "", line[64] = "";
+       int i, len, ret;
+
+       pp->probes[0] = buf = zalloc(MAX_CMDLEN);
+       if (!buf)
+               die("Failed to allocate memory by zalloc.");
+       if (pp->offset) {
+               ret = e_snprintf(offs, 64, "+%d", pp->offset);
+               if (ret <= 0)
+                       goto error;
+       }
+       if (pp->line) {
+               ret = e_snprintf(line, 64, ":%d", pp->line);
+               if (ret <= 0)
+                       goto error;
+       }
+
+       if (pp->function)
+               ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function,
+                                offs, pp->retprobe ? "%return" : "", line);
+       else
+               ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->file, line);
+       if (ret <= 0)
+               goto error;
+       len = ret;
+
+       for (i = 0; i < pp->nr_args; i++) {
+               ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s",
+                                pp->args[i]);
+               if (ret <= 0)
+                       goto error;
+               len += ret;
+       }
+       pp->found = 1;
+
+       return pp->found;
+error:
+       free(pp->probes[0]);
+
+       return ret;
+}
+
 int synthesize_trace_kprobe_event(struct probe_point *pp)
 {
        char *buf;
@@ -174,15 +287,15 @@ int synthesize_trace_kprobe_event(struct probe_point *pp)
        pp->probes[0] = buf = zalloc(MAX_CMDLEN);
        if (!buf)
                die("Failed to allocate memory by zalloc.");
-       ret = snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset);
-       if (ret <= 0 || ret >= MAX_CMDLEN)
+       ret = e_snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset);
+       if (ret <= 0)
                goto error;
        len = ret;
 
        for (i = 0; i < pp->nr_args; i++) {
-               ret = snprintf(&buf[len], MAX_CMDLEN - len, " %s",
-                              pp->args[i]);
-               if (ret <= 0 || ret >= MAX_CMDLEN - len)
+               ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s",
+                                pp->args[i]);
+               if (ret <= 0)
                        goto error;
                len += ret;
        }
@@ -191,12 +304,105 @@ int synthesize_trace_kprobe_event(struct probe_point *pp)
        return pp->found;
 error:
        free(pp->probes[0]);
-       if (ret > 0)
-               ret = -E2BIG;
 
        return ret;
 }
 
+static int open_kprobe_events(int flags, int mode)
+{
+       char buf[PATH_MAX];
+       int ret;
+
+       ret = e_snprintf(buf, PATH_MAX, "%s/../kprobe_events", debugfs_path);
+       if (ret < 0)
+               die("Failed to make kprobe_events path.");
+
+       ret = open(buf, flags, mode);
+       if (ret < 0) {
+               if (errno == ENOENT)
+                       die("kprobe_events file does not exist -"
+                           " please rebuild with CONFIG_KPROBE_TRACER.");
+               else
+                       die("Could not open kprobe_events file: %s",
+                           strerror(errno));
+       }
+       return ret;
+}
+
+/* Get raw string list of current kprobe_events */
+static struct strlist *get_trace_kprobe_event_rawlist(int fd)
+{
+       int ret, idx;
+       FILE *fp;
+       char buf[MAX_CMDLEN];
+       char *p;
+       struct strlist *sl;
+
+       sl = strlist__new(true, NULL);
+
+       fp = fdopen(dup(fd), "r");
+       while (!feof(fp)) {
+               p = fgets(buf, MAX_CMDLEN, fp);
+               if (!p)
+                       break;
+
+               idx = strlen(p) - 1;
+               if (p[idx] == '\n')
+                       p[idx] = '\0';
+               ret = strlist__add(sl, buf);
+               if (ret < 0)
+                       die("strlist__add failed: %s", strerror(-ret));
+       }
+       fclose(fp);
+
+       return sl;
+}
+
+/* Free and zero clear probe_point */
+static void clear_probe_point(struct probe_point *pp)
+{
+       int i;
+
+       if (pp->function)
+               free(pp->function);
+       if (pp->file)
+               free(pp->file);
+       for (i = 0; i < pp->nr_args; i++)
+               free(pp->args[i]);
+       if (pp->args)
+               free(pp->args);
+       for (i = 0; i < pp->found; i++)
+               free(pp->probes[i]);
+       memset(pp, 0, sizeof(pp));
+}
+
+/* List up current perf-probe events */
+void show_perf_probe_events(void)
+{
+       unsigned int i;
+       int fd;
+       char *group, *event;
+       struct probe_point pp;
+       struct strlist *rawlist;
+       struct str_node *ent;
+
+       fd = open_kprobe_events(O_RDONLY, 0);
+       rawlist = get_trace_kprobe_event_rawlist(fd);
+       close(fd);
+
+       for (i = 0; i < strlist__nr_entries(rawlist); i++) {
+               ent = strlist__entry(rawlist, i);
+               parse_trace_kprobe_event(ent->s, &group, &event, &pp);
+               synthesize_perf_probe_event(&pp);
+               printf("[%s:%s]\t%s\n", group, event, pp.probes[0]);
+               free(group);
+               free(event);
+               clear_probe_point(&pp);
+       }
+
+       strlist__delete(rawlist);
+}
+
 static int write_trace_kprobe_event(int fd, const char *buf)
 {
        int ret;
@@ -216,16 +422,7 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes)
        struct probe_point *pp;
        char buf[MAX_CMDLEN];
 
-       snprintf(buf, MAX_CMDLEN, "%s/../kprobe_events", debugfs_path);
-       fd = open(buf, O_WRONLY, O_APPEND);
-       if (fd < 0) {
-               if (errno == ENOENT)
-                       die("kprobe_events file does not exist -"
-                           " please rebuild with CONFIG_KPROBE_TRACER.");
-               else
-                       die("Could not open kprobe_events file: %s",
-                           strerror(errno));
-       }
+       fd = open_kprobe_events(O_WRONLY, O_APPEND);
 
        for (j = 0; j < nr_probes; j++) {
                pp = probes + j;
index 0089c45..88db7d1 100644 (file)
@@ -2,9 +2,14 @@
 #define _PROBE_EVENT_H
 
 #include "probe-finder.h"
+#include "strlist.h"
 
 extern int parse_perf_probe_event(const char *str, struct probe_point *pp);
+extern int synthesize_perf_probe_event(struct probe_point *pp);
+extern void parse_trace_kprobe_event(const char *str, char **group,
+                                    char **event, struct probe_point *pp);
 extern int synthesize_trace_kprobe_event(struct probe_point *pp);
 extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes);
+extern void show_perf_probe_events(void);
 
 #endif /*_PROBE_EVENT_H */