]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - tools/perf/util/probe-event.c
Merge branch 'next-spi' of git://git.secretlab.ca/git/linux-2.6
[mv-sheeva.git] / tools / perf / util / probe-event.c
index cd7fbda5e2a57460a32ef56a673ddb3729d59b43..2ca62154f79b8c49c9973ea732449fd9893c29df 100644 (file)
@@ -47,6 +47,9 @@
 #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, ...)
+       __attribute__((format(printf, 3, 4)));
+
 static int e_snprintf(char *str, size_t size, const char *format, ...)
 {
        int ret;
@@ -66,10 +69,23 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
        char c, nc = 0;
        /*
         * <Syntax>
-        * perf probe SRC:LN
-        * perf probe FUNC[+OFFS|%return][@SRC]
+        * perf probe [EVENT=]SRC:LN
+        * perf probe [EVENT=]FUNC[+OFFS|%return][@SRC]
+        *
+        * TODO:Group name support
         */
 
+       ptr = strchr(arg, '=');
+       if (ptr) {      /* Event name */
+               *ptr = '\0';
+               tmp = ptr + 1;
+               ptr = strchr(arg, ':');
+               if (ptr)        /* Group name is not supported yet. */
+                       semantic_error("Group name is not supported yet.");
+               pp->event = strdup(arg);
+               arg = tmp;
+       }
+
        ptr = strpbrk(arg, ":+@%");
        if (ptr) {
                nc = *ptr;
@@ -147,10 +163,13 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
 }
 
 /* Parse perf-probe event definition */
-int parse_perf_probe_event(const char *str, struct probe_point *pp)
+void parse_perf_probe_event(const char *str, struct probe_point *pp,
+                           bool *need_dwarf)
 {
        char **argv;
-       int argc, i, need_dwarf = 0;
+       int argc, i;
+
+       *need_dwarf = false;
 
        argv = argv_split(str, &argc);
        if (!argv)
@@ -161,7 +180,7 @@ int parse_perf_probe_event(const char *str, struct probe_point *pp)
        /* Parse probe point */
        parse_perf_probe_probepoint(argv[0], pp);
        if (pp->file || pp->line)
-               need_dwarf = 1;
+               *need_dwarf = true;
 
        /* Copy arguments and ensure return probe has no C argument */
        pp->nr_args = argc - 1;
@@ -174,17 +193,15 @@ int parse_perf_probe_event(const char *str, struct probe_point *pp)
                        if (pp->retprobe)
                                semantic_error("You can't specify local"
                                                " variable for kretprobe");
-                       need_dwarf = 1;
+                       *need_dwarf = true;
                }
        }
 
        argv_free(argv);
-       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)
+void parse_trace_kprobe_event(const char *str, struct probe_point *pp)
 {
        char pr;
        char *p;
@@ -200,18 +217,17 @@ void parse_trace_kprobe_event(const char *str, char **group, char **event,
 
        /* Scan event and group name. */
        ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]",
-                    &pr, (float *)(void *)group, (float *)(void *)event);
+                    &pr, (float *)(void *)&pp->group,
+                    (float *)(void *)&pp->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;
+       pr_debug("Group:%s Event:%s probe:%c\n", pp->group, pp->event, pr);
 
        pp->retprobe = (pr == 'r');
 
        /* Scan function name and offset */
-       ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, &pp->offset);
+       ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function,
+                    &pp->offset);
        if (ret == 1)
                pp->offset = 0;
 
@@ -230,15 +246,15 @@ void parse_trace_kprobe_event(const char *str, char **group, char **event,
                        die("Failed to copy argument.");
        }
 
-end:
        argv_free(argv);
 }
 
-int synthesize_perf_probe_event(struct probe_point *pp)
+/* Synthesize only probe point (not argument) */
+int synthesize_perf_probe_point(struct probe_point *pp)
 {
        char *buf;
        char offs[64] = "", line[64] = "";
-       int i, len, ret;
+       int ret;
 
        pp->probes[0] = buf = zalloc(MAX_CMDLEN);
        if (!buf)
@@ -258,11 +274,25 @@ int synthesize_perf_probe_event(struct probe_point *pp)
                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;
+               ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line);
+       if (ret <= 0) {
+error:
+               free(pp->probes[0]);
+               pp->probes[0] = NULL;
+       }
+       return ret;
+}
 
+int synthesize_perf_probe_event(struct probe_point *pp)
+{
+       char *buf;
+       int i, len, ret;
+
+       len = synthesize_perf_probe_point(pp);
+       if (len < 0)
+               return 0;
+
+       buf = pp->probes[0];
        for (i = 0; i < pp->nr_args; i++) {
                ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s",
                                 pp->args[i]);
@@ -275,6 +305,7 @@ int synthesize_perf_probe_event(struct probe_point *pp)
        return pp->found;
 error:
        free(pp->probes[0]);
+       pp->probes[0] = NULL;
 
        return ret;
 }
@@ -304,6 +335,7 @@ int synthesize_trace_kprobe_event(struct probe_point *pp)
        return pp->found;
 error:
        free(pp->probes[0]);
+       pp->probes[0] = NULL;
 
        return ret;
 }
@@ -363,6 +395,10 @@ static void clear_probe_point(struct probe_point *pp)
 {
        int i;
 
+       if (pp->event)
+               free(pp->event);
+       if (pp->group)
+               free(pp->group);
        if (pp->function)
                free(pp->function);
        if (pp->file)
@@ -373,15 +409,33 @@ static void clear_probe_point(struct probe_point *pp)
                free(pp->args);
        for (i = 0; i < pp->found; i++)
                free(pp->probes[i]);
-       memset(pp, 0, sizeof(pp));
+       memset(pp, 0, sizeof(*pp));
+}
+
+/* Show an event */
+static void show_perf_probe_event(const char *event, const char *place,
+                                 struct probe_point *pp)
+{
+       int i, ret;
+       char buf[128];
+
+       ret = e_snprintf(buf, 128, "%s:%s", pp->group, event);
+       if (ret < 0)
+               die("Failed to copy event: %s", strerror(-ret));
+       printf("  %-40s (on %s", buf, place);
+
+       if (pp->nr_args > 0) {
+               printf(" with");
+               for (i = 0; i < pp->nr_args; i++)
+                       printf(" %s", pp->args[i]);
+       }
+       printf(")\n");
 }
 
 /* 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;
@@ -390,13 +444,12 @@ void show_perf_probe_events(void)
        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);
+       strlist__for_each(ent, rawlist) {
+               parse_trace_kprobe_event(ent->s, &pp);
+               /* Synthesize only event probe point */
+               synthesize_perf_probe_point(&pp);
+               /* Show an event */
+               show_perf_probe_event(pp.event, pp.probes[0], &pp);
                clear_probe_point(&pp);
        }
 
@@ -404,21 +457,27 @@ void show_perf_probe_events(void)
 }
 
 /* Get current perf-probe event names */
-static struct strlist *get_perf_event_names(int fd)
+static struct strlist *get_perf_event_names(int fd, bool include_group)
 {
-       unsigned int i;
-       char *group, *event;
+       char buf[128];
        struct strlist *sl, *rawlist;
        struct str_node *ent;
+       struct probe_point pp;
 
+       memset(&pp, 0, sizeof(pp));
        rawlist = get_trace_kprobe_event_rawlist(fd);
 
-       sl = strlist__new(false, NULL);
-       for (i = 0; i < strlist__nr_entries(rawlist); i++) {
-               ent = strlist__entry(rawlist, i);
-               parse_trace_kprobe_event(ent->s, &group, &event, NULL);
-               strlist__add(sl, event);
-               free(group);
+       sl = strlist__new(true, NULL);
+       strlist__for_each(ent, rawlist) {
+               parse_trace_kprobe_event(ent->s, &pp);
+               if (include_group) {
+                       if (e_snprintf(buf, 128, "%s:%s", pp.group,
+                                      pp.event) < 0)
+                               die("Failed to copy group:event name.");
+                       strlist__add(sl, buf);
+               } else
+                       strlist__add(sl, pp.event);
+               clear_probe_point(&pp);
        }
 
        strlist__delete(rawlist);
@@ -426,24 +485,36 @@ static struct strlist *get_perf_event_names(int fd)
        return sl;
 }
 
-static int write_trace_kprobe_event(int fd, const char *buf)
+static void write_trace_kprobe_event(int fd, const char *buf)
 {
        int ret;
 
+       pr_debug("Writing event: %s\n", buf);
        ret = write(fd, buf, strlen(buf));
        if (ret <= 0)
-               die("Failed to create event.");
-       else
-               printf("Added new event: %s\n", buf);
-
-       return ret;
+               die("Failed to write event: %s", strerror(errno));
 }
 
 static void get_new_event_name(char *buf, size_t len, const char *base,
-                              struct strlist *namelist)
+                              struct strlist *namelist, bool allow_suffix)
 {
        int i, ret;
-       for (i = 0; i < MAX_EVENT_INDEX; i++) {
+
+       /* Try no suffix */
+       ret = e_snprintf(buf, len, "%s", base);
+       if (ret < 0)
+               die("snprintf() failed: %s", strerror(-ret));
+       if (!strlist__has_entry(namelist, buf))
+               return;
+
+       if (!allow_suffix) {
+               pr_warning("Error: event \"%s\" already exists. "
+                          "(Use -f to force duplicates.)\n", base);
+               die("Can't add new event.");
+       }
+
+       /* Try to add suffix */
+       for (i = 1; i < MAX_EVENT_INDEX; i++) {
                ret = e_snprintf(buf, len, "%s_%d", base, i);
                if (ret < 0)
                        die("snprintf() failed: %s", strerror(-ret));
@@ -454,31 +525,138 @@ static void get_new_event_name(char *buf, size_t len, const char *base,
                die("Too many events are on the same function.");
 }
 
-void add_trace_kprobe_events(struct probe_point *probes, int nr_probes)
+void add_trace_kprobe_events(struct probe_point *probes, int nr_probes,
+                            bool force_add)
 {
        int i, j, fd;
        struct probe_point *pp;
        char buf[MAX_CMDLEN];
        char event[64];
        struct strlist *namelist;
+       bool allow_suffix;
 
        fd = open_kprobe_events(O_RDWR, O_APPEND);
        /* Get current event names */
-       namelist = get_perf_event_names(fd);
+       namelist = get_perf_event_names(fd, false);
 
        for (j = 0; j < nr_probes; j++) {
                pp = probes + j;
+               if (!pp->event)
+                       pp->event = strdup(pp->function);
+               if (!pp->group)
+                       pp->group = strdup(PERFPROBE_GROUP);
+               DIE_IF(!pp->event || !pp->group);
+               /* If force_add is true, suffix search is allowed */
+               allow_suffix = force_add;
                for (i = 0; i < pp->found; i++) {
                        /* Get an unused new event name */
-                       get_new_event_name(event, 64, pp->function, namelist);
+                       get_new_event_name(event, 64, pp->event, namelist,
+                                          allow_suffix);
                        snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n",
                                 pp->retprobe ? 'r' : 'p',
-                                PERFPROBE_GROUP, event,
+                                pp->group, event,
                                 pp->probes[i]);
                        write_trace_kprobe_event(fd, buf);
+                       printf("Added new event:\n");
+                       /* Get the first parameter (probe-point) */
+                       sscanf(pp->probes[i], "%s", buf);
+                       show_perf_probe_event(event, buf, pp);
                        /* Add added event name to namelist */
                        strlist__add(namelist, event);
+                       /*
+                        * Probes after the first probe which comes from same
+                        * user input are always allowed to add suffix, because
+                        * there might be several addresses corresponding to
+                        * one code line.
+                        */
+                       allow_suffix = true;
+               }
+       }
+       /* Show how to use the event. */
+       printf("\nYou can now use it on all perf tools, such as:\n\n");
+       printf("\tperf record -e %s:%s -a sleep 1\n\n", PERFPROBE_GROUP, event);
+
+       strlist__delete(namelist);
+       close(fd);
+}
+
+static void __del_trace_kprobe_event(int fd, struct str_node *ent)
+{
+       char *p;
+       char buf[128];
+
+       /* Convert from perf-probe event to trace-kprobe event */
+       if (e_snprintf(buf, 128, "-:%s", ent->s) < 0)
+               die("Failed to copy event.");
+       p = strchr(buf + 2, ':');
+       if (!p)
+               die("Internal error: %s should have ':' but not.", ent->s);
+       *p = '/';
+
+       write_trace_kprobe_event(fd, buf);
+       printf("Remove event: %s\n", ent->s);
+}
+
+static void del_trace_kprobe_event(int fd, const char *group,
+                                  const char *event, struct strlist *namelist)
+{
+       char buf[128];
+       struct str_node *ent, *n;
+       int found = 0;
+
+       if (e_snprintf(buf, 128, "%s:%s", group, event) < 0)
+               die("Failed to copy event.");
+
+       if (strpbrk(buf, "*?")) { /* Glob-exp */
+               strlist__for_each_safe(ent, n, namelist)
+                       if (strglobmatch(ent->s, buf)) {
+                               found++;
+                               __del_trace_kprobe_event(fd, ent);
+                               strlist__remove(namelist, ent);
+                       }
+       } else {
+               ent = strlist__find(namelist, buf);
+               if (ent) {
+                       found++;
+                       __del_trace_kprobe_event(fd, ent);
+                       strlist__remove(namelist, ent);
+               }
+       }
+       if (found == 0)
+               pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf);
+}
+
+void del_trace_kprobe_events(struct strlist *dellist)
+{
+       int fd;
+       const char *group, *event;
+       char *p, *str;
+       struct str_node *ent;
+       struct strlist *namelist;
+
+       fd = open_kprobe_events(O_RDWR, O_APPEND);
+       /* Get current event names */
+       namelist = get_perf_event_names(fd, true);
+
+       strlist__for_each(ent, dellist) {
+               str = strdup(ent->s);
+               if (!str)
+                       die("Failed to copy event.");
+               pr_debug("Parsing: %s\n", str);
+               p = strchr(str, ':');
+               if (p) {
+                       group = str;
+                       *p = '\0';
+                       event = p + 1;
+               } else {
+                       group = "*";
+                       event = str;
                }
+               pr_debug("Group: %s, Event: %s\n", group, event);
+               del_trace_kprobe_event(fd, group, event, namelist);
+               free(str);
        }
+       strlist__delete(namelist);
        close(fd);
 }
+