]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/frederic...
authorIngo Molnar <mingo@elte.hu>
Fri, 1 Jul 2011 09:51:58 +0000 (11:51 +0200)
committerIngo Molnar <mingo@elte.hu>
Fri, 1 Jul 2011 09:51:58 +0000 (11:51 +0200)
tools/perf/Documentation/perf-report.txt
tools/perf/builtin-report.c
tools/perf/util/callchain.h
tools/perf/util/hist.c
tools/perf/util/session.c
tools/perf/util/sort.c
tools/perf/util/sort.h

index 8ba03d6e5398d8387b11f9caf183bed81a0eb5a2..cfa8e513d0fb2fda8a123bba4451dc3d90e46754 100644 (file)
@@ -80,15 +80,24 @@ OPTIONS
 --dump-raw-trace::
         Dump raw trace in ASCII.
 
--g [type,min]::
+-g [type,min,order]::
 --call-graph::
-        Display call chains using type and min percent threshold.
+        Display call chains using type, min percent threshold and order.
        type can be either:
        - flat: single column, linear exposure of call chains.
        - graph: use a graph tree, displaying absolute overhead rates.
        - fractal: like graph, but displays relative rates. Each branch of
                 the tree is considered as a new profiled object. +
-       Default: fractal,0.5.
+
+       order can be either:
+       - callee: callee based call graph.
+       - caller: inverted caller based call graph.
+
+       Default: fractal,0.5,callee.
+
+-G::
+--inverted::
+        alias for inverted caller based call graph.
 
 --pretty=<key>::
         Pretty printing style.  key: normal, raw
index 287a173523a7fa4d39c0c6635cbbaf94a0ac181a..5d43d0181d632bc60711e82fecb392d8b3d4ac7a 100644 (file)
@@ -45,7 +45,8 @@ static struct perf_read_values        show_threads_values;
 static const char      default_pretty_printing_style[] = "normal";
 static const char      *pretty_printing_style = default_pretty_printing_style;
 
-static char            callchain_default_opt[] = "fractal,0.5";
+static char            callchain_default_opt[] = "fractal,0.5,callee";
+static bool            inverted_callchain;
 static symbol_filter_t annotate_init;
 
 static int perf_session__add_hist_entry(struct perf_session *session,
@@ -386,13 +387,29 @@ parse_callchain_opt(const struct option *opt __used, const char *arg,
        if (!tok)
                goto setup;
 
-       tok2 = strtok(NULL, ",");
        callchain_param.min_percent = strtod(tok, &endptr);
        if (tok == endptr)
                return -1;
 
-       if (tok2)
+       /* get the print limit */
+       tok2 = strtok(NULL, ",");
+       if (!tok2)
+               goto setup;
+
+       if (tok2[0] != 'c') {
                callchain_param.print_limit = strtod(tok2, &endptr);
+               tok2 = strtok(NULL, ",");
+               if (!tok2)
+                       goto setup;
+       }
+
+       /* get the call chain order */
+       if (!strcmp(tok2, "caller"))
+               callchain_param.order = ORDER_CALLER;
+       else if (!strcmp(tok2, "callee"))
+               callchain_param.order = ORDER_CALLEE;
+       else
+               return -1;
 setup:
        if (callchain_register_param(&callchain_param) < 0) {
                fprintf(stderr, "Can't register callchain params\n");
@@ -436,9 +453,10 @@ static const struct option options[] = {
                   "regex filter to identify parent, see: '--sort parent'"),
        OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
                    "Only display entries with parent-match"),
-       OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent",
-                    "Display callchains using output_type (graph, flat, fractal, or none) and min percent threshold. "
-                    "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt),
+       OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent, call_order",
+                    "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold and callchain order. "
+                    "Default: fractal,0.5,callee", &parse_callchain_opt, callchain_default_opt),
+       OPT_BOOLEAN('G', "inverted", &inverted_callchain, "alias for inverted call graph"),
        OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
                   "only consider symbols in these dsos"),
        OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
@@ -467,6 +485,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
        else if (use_tui)
                use_browser = 1;
 
+       if (inverted_callchain)
+               callchain_param.order = ORDER_CALLER;
+
        if (strcmp(input_name, "-") != 0)
                setup_browser(true);
        else
@@ -504,7 +525,14 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
        if (parent_pattern != default_parent_pattern) {
                if (sort_dimension__add("parent") < 0)
                        return -1;
-               sort_parent.elide = 1;
+
+               /*
+                * Only show the parent fields if we explicitly
+                * sort that way. If we only use parent machinery
+                * for filtering, we don't want it.
+                */
+               if (!strstr(sort_order, "parent"))
+                       sort_parent.elide = 1;
        } else
                symbol_conf.exclude_other = false;
 
index 1a79df9f739f8425ce2991dcc0cc7a2eeac982dc..9b4ff16cac96dd924ec37c7fb4ab3226ded1f377 100644 (file)
@@ -14,6 +14,11 @@ enum chain_mode {
        CHAIN_GRAPH_REL
 };
 
+enum chain_order {
+       ORDER_CALLER,
+       ORDER_CALLEE
+};
+
 struct callchain_node {
        struct callchain_node   *parent;
        struct list_head        siblings;
@@ -41,6 +46,7 @@ struct callchain_param {
        u32                     print_limit;
        double                  min_percent;
        sort_chain_func_t       sort;
+       enum chain_order        order;
 };
 
 struct callchain_list {
index 627a02e03c57ab381d97cf2be9756997851d752d..677e1da6bb3eae6d688cd7e1afc51070e55810ea 100644 (file)
@@ -14,7 +14,8 @@ enum hist_filter {
 
 struct callchain_param callchain_param = {
        .mode   = CHAIN_GRAPH_REL,
-       .min_percent = 0.5
+       .min_percent = 0.5,
+       .order  = ORDER_CALLEE
 };
 
 u16 hists__col_len(struct hists *self, enum hist_column col)
@@ -846,6 +847,9 @@ print_entries:
        for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 
+               if (h->filtered)
+                       continue;
+
                if (show_displacement) {
                        if (h->pair != NULL)
                                displacement = ((long)h->pair->position -
index b723f211881cdcc4e124afc26b29d07d20977d36..558bcf99694935a706fd2d0e4dde914880918c7f 100644 (file)
@@ -247,9 +247,14 @@ int perf_session__resolve_callchain(struct perf_session *self,
        callchain_cursor_reset(&self->callchain_cursor);
 
        for (i = 0; i < chain->nr; i++) {
-               u64 ip = chain->ips[i];
+               u64 ip;
                struct addr_location al;
 
+               if (callchain_param.order == ORDER_CALLEE)
+                       ip = chain->ips[i];
+               else
+                       ip = chain->ips[chain->nr - i - 1];
+
                if (ip >= PERF_CONTEXT_MAX) {
                        switch (ip) {
                        case PERF_CONTEXT_HV:
index f44fa541d56e67c6bb6c976e78123e99657ffbee..401e220566fdab96d87cb1ae24e5a66dfb92992b 100644 (file)
@@ -15,95 +15,6 @@ char * field_sep;
 
 LIST_HEAD(hist_entry__sort_list);
 
-static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
-                                      size_t size, unsigned int width);
-static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
-                                    size_t size, unsigned int width);
-static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
-                                   size_t size, unsigned int width);
-static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
-                                   size_t size, unsigned int width);
-static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
-                                      size_t size, unsigned int width);
-static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
-                                   size_t size, unsigned int width);
-
-struct sort_entry sort_thread = {
-       .se_header      = "Command:  Pid",
-       .se_cmp         = sort__thread_cmp,
-       .se_snprintf    = hist_entry__thread_snprintf,
-       .se_width_idx   = HISTC_THREAD,
-};
-
-struct sort_entry sort_comm = {
-       .se_header      = "Command",
-       .se_cmp         = sort__comm_cmp,
-       .se_collapse    = sort__comm_collapse,
-       .se_snprintf    = hist_entry__comm_snprintf,
-       .se_width_idx   = HISTC_COMM,
-};
-
-struct sort_entry sort_dso = {
-       .se_header      = "Shared Object",
-       .se_cmp         = sort__dso_cmp,
-       .se_snprintf    = hist_entry__dso_snprintf,
-       .se_width_idx   = HISTC_DSO,
-};
-
-struct sort_entry sort_sym = {
-       .se_header      = "Symbol",
-       .se_cmp         = sort__sym_cmp,
-       .se_snprintf    = hist_entry__sym_snprintf,
-       .se_width_idx   = HISTC_SYMBOL,
-};
-
-struct sort_entry sort_parent = {
-       .se_header      = "Parent symbol",
-       .se_cmp         = sort__parent_cmp,
-       .se_snprintf    = hist_entry__parent_snprintf,
-       .se_width_idx   = HISTC_PARENT,
-};
-struct sort_entry sort_cpu = {
-       .se_header      = "CPU",
-       .se_cmp         = sort__cpu_cmp,
-       .se_snprintf    = hist_entry__cpu_snprintf,
-       .se_width_idx   = HISTC_CPU,
-};
-
-struct sort_dimension {
-       const char              *name;
-       struct sort_entry       *entry;
-       int                     taken;
-};
-
-static struct sort_dimension sort_dimensions[] = {
-       { .name = "pid",        .entry = &sort_thread,  },
-       { .name = "comm",       .entry = &sort_comm,    },
-       { .name = "dso",        .entry = &sort_dso,     },
-       { .name = "symbol",     .entry = &sort_sym,     },
-       { .name = "parent",     .entry = &sort_parent,  },
-       { .name = "cpu",        .entry = &sort_cpu,     },
-};
-
-int64_t cmp_null(void *l, void *r)
-{
-       if (!l && !r)
-               return 0;
-       else if (!l)
-               return -1;
-       else
-               return 1;
-}
-
-/* --sort pid */
-
-int64_t
-sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
-{
-       return right->thread->pid - left->thread->pid;
-}
-
 static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
 {
        int n;
@@ -125,6 +36,24 @@ static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
        return n;
 }
 
+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
+sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+       return right->thread->pid - left->thread->pid;
+}
+
 static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
                                       size_t size, unsigned int width)
 {
@@ -132,15 +61,50 @@ static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
                              self->thread->comm ?: "", self->thread->pid);
 }
 
+struct sort_entry sort_thread = {
+       .se_header      = "Command:  Pid",
+       .se_cmp         = sort__thread_cmp,
+       .se_snprintf    = hist_entry__thread_snprintf,
+       .se_width_idx   = HISTC_THREAD,
+};
+
+/* --sort comm */
+
+static int64_t
+sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+       return right->thread->pid - left->thread->pid;
+}
+
+static int64_t
+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)
+               return cmp_null(comm_l, comm_r);
+
+       return strcmp(comm_l, comm_r);
+}
+
 static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
                                     size_t size, unsigned int width)
 {
        return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
 }
 
+struct sort_entry sort_comm = {
+       .se_header      = "Command",
+       .se_cmp         = sort__comm_cmp,
+       .se_collapse    = sort__comm_collapse,
+       .se_snprintf    = hist_entry__comm_snprintf,
+       .se_width_idx   = HISTC_COMM,
+};
+
 /* --sort dso */
 
-int64_t
+static int64_t
 sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
 {
        struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL;
@@ -173,9 +137,16 @@ static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
        return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
 }
 
+struct sort_entry sort_dso = {
+       .se_header      = "Shared Object",
+       .se_cmp         = sort__dso_cmp,
+       .se_snprintf    = hist_entry__dso_snprintf,
+       .se_width_idx   = HISTC_DSO,
+};
+
 /* --sort symbol */
 
-int64_t
+static int64_t
 sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
 {
        u64 ip_l, ip_r;
@@ -211,29 +182,16 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
        return ret;
 }
 
-/* --sort comm */
-
-int64_t
-sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
-{
-       return right->thread->pid - left->thread->pid;
-}
-
-int64_t
-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)
-               return cmp_null(comm_l, comm_r);
-
-       return strcmp(comm_l, comm_r);
-}
+struct sort_entry sort_sym = {
+       .se_header      = "Symbol",
+       .se_cmp         = sort__sym_cmp,
+       .se_snprintf    = hist_entry__sym_snprintf,
+       .se_width_idx   = HISTC_SYMBOL,
+};
 
 /* --sort parent */
 
-int64_t
+static int64_t
 sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
 {
        struct symbol *sym_l = left->parent;
@@ -252,9 +210,16 @@ static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
                              self->parent ? self->parent->name : "[other]");
 }
 
+struct sort_entry sort_parent = {
+       .se_header      = "Parent symbol",
+       .se_cmp         = sort__parent_cmp,
+       .se_snprintf    = hist_entry__parent_snprintf,
+       .se_width_idx   = HISTC_PARENT,
+};
+
 /* --sort cpu */
 
-int64_t
+static int64_t
 sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
 {
        return right->cpu - left->cpu;
@@ -266,6 +231,28 @@ static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
        return repsep_snprintf(bf, size, "%-*d", width, self->cpu);
 }
 
+struct sort_entry sort_cpu = {
+       .se_header      = "CPU",
+       .se_cmp         = sort__cpu_cmp,
+       .se_snprintf    = hist_entry__cpu_snprintf,
+       .se_width_idx   = HISTC_CPU,
+};
+
+struct sort_dimension {
+       const char              *name;
+       struct sort_entry       *entry;
+       int                     taken;
+};
+
+static struct sort_dimension sort_dimensions[] = {
+       { .name = "pid",        .entry = &sort_thread,  },
+       { .name = "comm",       .entry = &sort_comm,    },
+       { .name = "dso",        .entry = &sort_dso,     },
+       { .name = "symbol",     .entry = &sort_sym,     },
+       { .name = "parent",     .entry = &sort_parent,  },
+       { .name = "cpu",        .entry = &sort_cpu,     },
+};
+
 int sort_dimension__add(const char *tok)
 {
        unsigned int i;
@@ -273,15 +260,9 @@ int sort_dimension__add(const char *tok)
        for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
                struct sort_dimension *sd = &sort_dimensions[i];
 
-               if (sd->taken)
-                       continue;
-
                if (strncasecmp(tok, sd->name, strlen(tok)))
                        continue;
 
-               if (sd->entry->se_collapse)
-                       sort__need_collapse = 1;
-
                if (sd->entry == &sort_parent) {
                        int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
                        if (ret) {
@@ -294,6 +275,12 @@ int sort_dimension__add(const char *tok)
                        sort__has_parent = 1;
                }
 
+               if (sd->taken)
+                       return 0;
+
+               if (sd->entry->se_collapse)
+                       sort__need_collapse = 1;
+
                if (list_empty(&hist_entry__sort_list)) {
                        if (!strcmp(sd->name, "pid"))
                                sort__first_dimension = SORT_PID;
index 0b91053a7d11af888eea81a4c8de24fdd60ce6f8..77d0388ad415477d8be2faf57cbf6e3160f40079 100644 (file)
@@ -103,20 +103,6 @@ extern struct sort_entry sort_thread;
 extern struct list_head hist_entry__sort_list;
 
 void setup_sorting(const char * const usagestr[], const struct option *opts);
-
-extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int);
-extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int);
-extern size_t sort__dso_print(FILE *, struct hist_entry *, unsigned int);
-extern size_t sort__sym_print(FILE *, struct hist_entry *, unsigned int __used);
-extern int64_t cmp_null(void *, void *);
-extern int64_t sort__thread_cmp(struct hist_entry *, struct hist_entry *);
-extern int64_t sort__comm_cmp(struct hist_entry *, struct hist_entry *);
-extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *);
-extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *);
-extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *);
-extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *);
-int64_t sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right);
-extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int);
 extern int sort_dimension__add(const char *);
 void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
                             const char *list_name, FILE *fp);