Merge branch 'akpm' (patches from Andrew)
[deliverable/linux.git] / tools / perf / builtin-script.c
index e3ce2f34d3ad5276cc8f10d78b6590c41b56c179..971ff91b16cb3be52702cca780c3df818d52c51a 100644 (file)
@@ -21,6 +21,7 @@
 #include "util/cpumap.h"
 #include "util/thread_map.h"
 #include "util/stat.h"
+#include "util/thread-stack.h"
 #include <linux/bitmap.h>
 #include <linux/stringify.h>
 #include "asm/bug.h"
@@ -63,6 +64,7 @@ enum perf_output_field {
        PERF_OUTPUT_DATA_SRC        = 1U << 17,
        PERF_OUTPUT_WEIGHT          = 1U << 18,
        PERF_OUTPUT_BPF_OUTPUT      = 1U << 19,
+       PERF_OUTPUT_CALLINDENT      = 1U << 20,
 };
 
 struct output_option {
@@ -89,6 +91,7 @@ struct output_option {
        {.str = "data_src", .field = PERF_OUTPUT_DATA_SRC},
        {.str = "weight",   .field = PERF_OUTPUT_WEIGHT},
        {.str = "bpf-output",   .field = PERF_OUTPUT_BPF_OUTPUT},
+       {.str = "callindent", .field = PERF_OUTPUT_CALLINDENT},
 };
 
 /* default set to maintain compatibility with current format */
@@ -339,7 +342,7 @@ static void set_print_ip_opts(struct perf_event_attr *attr)
  */
 static int perf_session__check_output_opt(struct perf_session *session)
 {
-       int j;
+       unsigned int j;
        struct perf_evsel *evsel;
 
        for (j = 0; j < PERF_TYPE_MAX; ++j) {
@@ -369,7 +372,7 @@ static int perf_session__check_output_opt(struct perf_session *session)
        if (!no_callchain) {
                bool use_callchain = false;
 
-               evlist__for_each(session->evlist, evsel) {
+               evlist__for_each_entry(session->evlist, evsel) {
                        if (evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN) {
                                use_callchain = true;
                                break;
@@ -388,17 +391,20 @@ static int perf_session__check_output_opt(struct perf_session *session)
                struct perf_event_attr *attr;
 
                j = PERF_TYPE_TRACEPOINT;
-               evsel = perf_session__find_first_evtype(session, j);
-               if (evsel == NULL)
-                       goto out;
 
-               attr = &evsel->attr;
+               evlist__for_each_entry(session->evlist, evsel) {
+                       if (evsel->attr.type != j)
+                               continue;
+
+                       attr = &evsel->attr;
 
-               if (attr->sample_type & PERF_SAMPLE_CALLCHAIN) {
-                       output[j].fields |= PERF_OUTPUT_IP;
-                       output[j].fields |= PERF_OUTPUT_SYM;
-                       output[j].fields |= PERF_OUTPUT_DSO;
-                       set_print_ip_opts(attr);
+                       if (attr->sample_type & PERF_SAMPLE_CALLCHAIN) {
+                               output[j].fields |= PERF_OUTPUT_IP;
+                               output[j].fields |= PERF_OUTPUT_SYM;
+                               output[j].fields |= PERF_OUTPUT_DSO;
+                               set_print_ip_opts(attr);
+                               goto out;
+                       }
                }
        }
 
@@ -559,6 +565,62 @@ static void print_sample_addr(struct perf_sample *sample,
        }
 }
 
+static void print_sample_callindent(struct perf_sample *sample,
+                                   struct perf_evsel *evsel,
+                                   struct thread *thread,
+                                   struct addr_location *al)
+{
+       struct perf_event_attr *attr = &evsel->attr;
+       size_t depth = thread_stack__depth(thread);
+       struct addr_location addr_al;
+       const char *name = NULL;
+       static int spacing;
+       int len = 0;
+       u64 ip = 0;
+
+       /*
+        * The 'return' has already been popped off the stack so the depth has
+        * to be adjusted to match the 'call'.
+        */
+       if (thread->ts && sample->flags & PERF_IP_FLAG_RETURN)
+               depth += 1;
+
+       if (sample->flags & (PERF_IP_FLAG_CALL | PERF_IP_FLAG_TRACE_BEGIN)) {
+               if (sample_addr_correlates_sym(attr)) {
+                       thread__resolve(thread, &addr_al, sample);
+                       if (addr_al.sym)
+                               name = addr_al.sym->name;
+                       else
+                               ip = sample->addr;
+               } else {
+                       ip = sample->addr;
+               }
+       } else if (sample->flags & (PERF_IP_FLAG_RETURN | PERF_IP_FLAG_TRACE_END)) {
+               if (al->sym)
+                       name = al->sym->name;
+               else
+                       ip = sample->ip;
+       }
+
+       if (name)
+               len = printf("%*s%s", (int)depth * 4, "", name);
+       else if (ip)
+               len = printf("%*s%16" PRIx64, (int)depth * 4, "", ip);
+
+       if (len < 0)
+               return;
+
+       /*
+        * Try to keep the output length from changing frequently so that the
+        * output lines up more nicely.
+        */
+       if (len > spacing || (len && len < spacing - 52))
+               spacing = round_up(len + 4, 32);
+
+       if (len < spacing)
+               printf("%*s", spacing - len, "");
+}
+
 static void print_sample_bts(struct perf_sample *sample,
                             struct perf_evsel *evsel,
                             struct thread *thread,
@@ -567,6 +629,9 @@ static void print_sample_bts(struct perf_sample *sample,
        struct perf_event_attr *attr = &evsel->attr;
        bool print_srcline_last = false;
 
+       if (PRINT_FIELD(CALLINDENT))
+               print_sample_callindent(sample, evsel, thread, al);
+
        /* print branch_from information */
        if (PRINT_FIELD(IP)) {
                unsigned int print_opts = output[attr->type].print_ip_opts;
@@ -603,13 +668,42 @@ static void print_sample_bts(struct perf_sample *sample,
        printf("\n");
 }
 
+static struct {
+       u32 flags;
+       const char *name;
+} sample_flags[] = {
+       {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL, "call"},
+       {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN, "return"},
+       {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL, "jcc"},
+       {PERF_IP_FLAG_BRANCH, "jmp"},
+       {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_INTERRUPT, "int"},
+       {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT, "iret"},
+       {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_SYSCALLRET, "syscall"},
+       {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET, "sysret"},
+       {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_ASYNC, "async"},
+       {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_ASYNC | PERF_IP_FLAG_INTERRUPT, "hw int"},
+       {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TX_ABORT, "tx abrt"},
+       {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_BEGIN, "tr strt"},
+       {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_END, "tr end"},
+       {0, NULL}
+};
+
 static void print_sample_flags(u32 flags)
 {
        const char *chars = PERF_IP_FLAG_CHARS;
        const int n = strlen(PERF_IP_FLAG_CHARS);
+       bool in_tx = flags & PERF_IP_FLAG_IN_TX;
+       const char *name = NULL;
        char str[33];
        int i, pos = 0;
 
+       for (i = 0; sample_flags[i].name ; i++) {
+               if (sample_flags[i].flags == (flags & ~PERF_IP_FLAG_IN_TX)) {
+                       name = sample_flags[i].name;
+                       break;
+               }
+       }
+
        for (i = 0; i < n; i++, flags >>= 1) {
                if (flags & 1)
                        str[pos++] = chars[i];
@@ -619,7 +713,11 @@ static void print_sample_flags(u32 flags)
                        str[pos++] = '?';
        }
        str[pos] = 0;
-       printf("  %-4s ", str);
+
+       if (name)
+               printf("  %-7s%4s ", name, in_tx ? "(x)" : "");
+       else
+               printf("  %-11s ", str);
 }
 
 struct printer_data {
@@ -717,7 +815,7 @@ static int perf_evlist__max_name_len(struct perf_evlist *evlist)
        struct perf_evsel *evsel;
        int max = 0;
 
-       evlist__for_each(evlist, evsel) {
+       evlist__for_each_entry(evlist, evsel) {
                int len = strlen(perf_evsel__name(evsel));
 
                max = MAX(len, max);
@@ -942,7 +1040,7 @@ static int process_attr(struct perf_tool *tool, union perf_event *event,
        if (evsel->attr.type >= PERF_TYPE_MAX)
                return 0;
 
-       evlist__for_each(evlist, pos) {
+       evlist__for_each_entry(evlist, pos) {
                if (pos->attr.type == evsel->attr.type && pos != evsel)
                        return 0;
        }
@@ -1668,7 +1766,7 @@ static int check_ev_match(char *dir_name, char *scriptname,
                        snprintf(evname, len + 1, "%s", p);
 
                        match = 0;
-                       evlist__for_each(session->evlist, pos) {
+                       evlist__for_each_entry(session->evlist, pos) {
                                if (!strcmp(perf_evsel__name(pos), evname)) {
                                        match = 1;
                                        break;
@@ -1870,7 +1968,7 @@ static int process_stat_round_event(struct perf_tool *tool __maybe_unused,
        struct stat_round_event *round = &event->stat_round;
        struct perf_evsel *counter;
 
-       evlist__for_each(session->evlist, counter) {
+       evlist__for_each_entry(session->evlist, counter) {
                perf_stat_process_counter(&stat_config, counter);
                process_stat(counter, round->time);
        }
@@ -2017,7 +2115,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
                     "comma separated output fields prepend with 'type:'. "
                     "Valid types: hw,sw,trace,raw. "
                     "Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,"
-                    "addr,symoff,period,iregs,brstack,brstacksym,flags", parse_output_fields),
+                    "addr,symoff,period,iregs,brstack,brstacksym,flags,"
+                    "callindent", parse_output_fields),
        OPT_BOOLEAN('a', "all-cpus", &system_wide,
                    "system-wide collection from all CPUs"),
        OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
@@ -2256,6 +2355,9 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
        script.session = session;
        script__setup_sample_type(&script);
 
+       if (output[PERF_TYPE_HARDWARE].fields & PERF_OUTPUT_CALLINDENT)
+               itrace_synth_opts.thread_stack = true;
+
        session->itrace_synth_opts = &itrace_synth_opts;
 
        if (cpu_list) {
This page took 0.031037 seconds and 5 git commands to generate.