perf report: Add --sort <call> --call <$regex>
authorPeter Zijlstra <a.p.zijlstra@chello.nl>
Wed, 17 Jun 2009 13:51:44 +0000 (15:51 +0200)
committerIngo Molnar <mingo@elte.hu>
Wed, 17 Jun 2009 17:23:52 +0000 (19:23 +0200)
Implement sorting by callchain symbols, --sort <call>.

It will create a new column which will show a match to
--call $regex or "[unmatched]".

Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
tools/perf/builtin-report.c

index f86bb07..cd74b2e 100644 (file)
@@ -40,11 +40,13 @@ static int          dump_trace = 0;
 
 static int             verbose;
 static int             full_paths;
-static int             collapse_syscalls;
 
 static unsigned long   page_size;
 static unsigned long   mmap_window = 32;
 
+static char            *call = "^sys_";
+static regex_t         call_regex;
+
 struct ip_chain_event {
        __u16 nr;
        __u16 hv;
@@ -463,6 +465,7 @@ struct hist_entry {
        struct map       *map;
        struct dso       *dso;
        struct symbol    *sym;
+       struct symbol    *call;
        __u64            ip;
        char             level;
 
@@ -483,6 +486,16 @@ struct sort_entry {
        size_t  (*print)(FILE *fp, struct hist_entry *);
 };
 
+static int64_t cmp_null(void *l, void *r)
+{
+       if (!l && !r)
+               return 0;
+       else if (!l)
+               return -1;
+       else
+               return 1;
+}
+
 /* --sort pid */
 
 static int64_t
@@ -517,14 +530,8 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
        char *comm_l = left->thread->comm;
        char *comm_r = right->thread->comm;
 
-       if (!comm_l || !comm_r) {
-               if (!comm_l && !comm_r)
-                       return 0;
-               else if (!comm_l)
-                       return -1;
-               else
-                       return 1;
-       }
+       if (!comm_l || !comm_r)
+               return cmp_null(comm_l, comm_r);
 
        return strcmp(comm_l, comm_r);
 }
@@ -550,14 +557,8 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
        struct dso *dso_l = left->dso;
        struct dso *dso_r = right->dso;
 
-       if (!dso_l || !dso_r) {
-               if (!dso_l && !dso_r)
-                       return 0;
-               else if (!dso_l)
-                       return -1;
-               else
-                       return 1;
-       }
+       if (!dso_l || !dso_r)
+               return cmp_null(dso_l, dso_r);
 
        return strcmp(dso_l->name, dso_r->name);
 }
@@ -617,7 +618,38 @@ static struct sort_entry sort_sym = {
        .print  = sort__sym_print,
 };
 
+/* --sort call */
+
+static int64_t
+sort__call_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+       struct symbol *sym_l = left->call;
+       struct symbol *sym_r = right->call;
+
+       if (!sym_l || !sym_r)
+               return cmp_null(sym_l, sym_r);
+
+       return strcmp(sym_l->name, sym_r->name);
+}
+
+static size_t
+sort__call_print(FILE *fp, struct hist_entry *self)
+{
+       size_t ret = 0;
+
+       ret += fprintf(fp, "%-20s", self->call ? self->call->name : "[unmatched]");
+
+       return ret;
+}
+
+static struct sort_entry sort_call = {
+       .header = "Callchain symbol    ",
+       .cmp    = sort__call_cmp,
+       .print  = sort__call_print,
+};
+
 static int sort__need_collapse = 0;
+static int sort__has_call = 0;
 
 struct sort_dimension {
        char                    *name;
@@ -630,6 +662,7 @@ static struct sort_dimension sort_dimensions[] = {
        { .name = "comm",       .entry = &sort_comm,    },
        { .name = "dso",        .entry = &sort_dso,     },
        { .name = "symbol",     .entry = &sort_sym,     },
+       { .name = "call",       .entry = &sort_call,    },
 };
 
 static LIST_HEAD(hist_entry__sort_list);
@@ -650,6 +683,18 @@ static int sort_dimension__add(char *tok)
                if (sd->entry->collapse)
                        sort__need_collapse = 1;
 
+               if (sd->entry == &sort_call) {
+                       int ret = regcomp(&call_regex, call, REG_EXTENDED);
+                       if (ret) {
+                               char err[BUFSIZ];
+
+                               regerror(ret, &call_regex, err, sizeof(err));
+                               fprintf(stderr, "Invalid regex: %s\n%s", call, err);
+                               exit(-1);
+                       }
+                       sort__has_call = 1;
+               }
+
                list_add_tail(&sd->entry->list, &hist_entry__sort_list);
                sd->taken = 1;
 
@@ -730,13 +775,76 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, __u64 total_samples)
        return ret;
 }
 
+/*
+ *
+ */
+
+static struct symbol *
+resolve_symbol(struct thread *thread, struct map **mapp,
+              struct dso **dsop, __u64 *ipp)
+{
+       struct dso *dso = dsop ? *dsop : NULL;
+       struct map *map = mapp ? *mapp : NULL;
+       uint64_t ip = *ipp;
+
+       if (!thread)
+               return NULL;
+
+       if (dso)
+               goto got_dso;
+
+       if (map)
+               goto got_map;
+
+       map = thread__find_map(thread, ip);
+       if (map != NULL) {
+               if (mapp)
+                       *mapp = map;
+got_map:
+               ip = map->map_ip(map, ip);
+               *ipp  = ip;
+
+               dso = map->dso;
+       } else {
+               /*
+                * If this is outside of all known maps,
+                * and is a negative address, try to look it
+                * up in the kernel dso, as it might be a
+                * vsyscall (which executes in user-mode):
+                */
+               if ((long long)ip < 0)
+               dso = kernel_dso;
+       }
+       dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
+
+       if (dsop)
+               *dsop = dso;
+
+       if (!dso)
+               return NULL;
+got_dso:
+       return dso->find_symbol(dso, ip);
+}
+
+static struct symbol *call__match(struct symbol *sym)
+{
+       if (!sym)
+               return NULL;
+
+       if (sym->name && !regexec(&call_regex, sym->name, 0, NULL, 0))
+               return sym;
+
+       return NULL;
+}
+
 /*
  * collect histogram counts
  */
 
 static int
 hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
-               struct symbol *sym, __u64 ip, char level, __u64 count)
+               struct symbol *sym, __u64 ip, struct ip_chain_event *chain,
+               char level, __u64 count)
 {
        struct rb_node **p = &hist.rb_node;
        struct rb_node *parent = NULL;
@@ -752,6 +860,33 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
        };
        int cmp;
 
+       if (sort__has_call && chain) {
+               int i, nr = chain->hv;
+               struct symbol *sym;
+               struct dso *dso;
+               __u64 ip;
+
+               for (i = 0; i < chain->kernel; i++) {
+                       ip = chain->ips[nr + i];
+                       dso = kernel_dso;
+                       sym = resolve_symbol(thread, NULL, &dso, &ip);
+                       entry.call = call__match(sym);
+                       if (entry.call)
+                               goto got_call;
+               }
+               nr += i;
+
+               for (i = 0; i < chain->user; i++) {
+                       ip = chain->ips[nr + i];
+                       sym = resolve_symbol(thread, NULL, NULL, &ip);
+                       entry.call = call__match(sym);
+                       if (entry.call)
+                               goto got_call;
+               }
+               nr += i;
+       }
+got_call:
+
        while (*p != NULL) {
                parent = *p;
                he = rb_entry(parent, struct hist_entry, rb_node);
@@ -955,7 +1090,7 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
        __u64 period = 1;
        struct map *map = NULL;
        void *more_data = event->ip.__more_data;
-       struct ip_chain_event *chain;
+       struct ip_chain_event *chain = NULL;
 
        if (event->header.type & PERF_SAMPLE_PERIOD) {
                period = *(__u64 *)more_data;
@@ -984,15 +1119,6 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
                        for (i = 0; i < chain->nr; i++)
                                dprintf("..... %2d: %016Lx\n", i, chain->ips[i]);
                }
-               if (collapse_syscalls) {
-                       /*
-                        * Find the all-but-last kernel entry
-                        * amongst the call-chains - to get
-                        * to the level of system calls:
-                        */
-                       if (chain->kernel >= 2)
-                               ip = chain->ips[chain->kernel-2];
-               }
        }
 
        dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid);
@@ -1016,22 +1142,6 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
                show = SHOW_USER;
                level = '.';
 
-               map = thread__find_map(thread, ip);
-               if (map != NULL) {
-                       ip = map->map_ip(map, ip);
-                       dso = map->dso;
-               } else {
-                       /*
-                        * If this is outside of all known maps,
-                        * and is a negative address, try to look it
-                        * up in the kernel dso, as it might be a
-                        * vsyscall (which executes in user-mode):
-                        */
-                       if ((long long)ip < 0)
-                               dso = kernel_dso;
-               }
-               dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
-
        } else {
                show = SHOW_HV;
                level = 'H';
@@ -1039,12 +1149,9 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
        }
 
        if (show & show_mask) {
-               struct symbol *sym = NULL;
-
-               if (dso)
-                       sym = dso->find_symbol(dso, ip);
+               struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip);
 
-               if (hist_entry__add(thread, map, dso, sym, ip, level, period)) {
+               if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) {
                        fprintf(stderr,
                "problem incrementing symbol count, skipping event\n");
                        return -1;
@@ -1353,8 +1460,8 @@ static const struct option options[] = {
                   "sort by key(s): pid, comm, dso, symbol. Default: pid,symbol"),
        OPT_BOOLEAN('P', "full-paths", &full_paths,
                    "Don't shorten the pathnames taking into account the cwd"),
-       OPT_BOOLEAN('S', "syscalls", &collapse_syscalls,
-                   "show per syscall summary overhead, using call graph"),
+       OPT_STRING('c', "call", &call, "regex",
+                  "regex to use for --sort call"),
        OPT_END()
 };