aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/builtin-script.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/builtin-script.c')
-rw-r--r--tools/perf/builtin-script.c136
1 files changed, 119 insertions, 17 deletions
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index e3ce2f34d3ad..971ff91b16cb 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -21,6 +21,7 @@
21#include "util/cpumap.h" 21#include "util/cpumap.h"
22#include "util/thread_map.h" 22#include "util/thread_map.h"
23#include "util/stat.h" 23#include "util/stat.h"
24#include "util/thread-stack.h"
24#include <linux/bitmap.h> 25#include <linux/bitmap.h>
25#include <linux/stringify.h> 26#include <linux/stringify.h>
26#include "asm/bug.h" 27#include "asm/bug.h"
@@ -63,6 +64,7 @@ enum perf_output_field {
63 PERF_OUTPUT_DATA_SRC = 1U << 17, 64 PERF_OUTPUT_DATA_SRC = 1U << 17,
64 PERF_OUTPUT_WEIGHT = 1U << 18, 65 PERF_OUTPUT_WEIGHT = 1U << 18,
65 PERF_OUTPUT_BPF_OUTPUT = 1U << 19, 66 PERF_OUTPUT_BPF_OUTPUT = 1U << 19,
67 PERF_OUTPUT_CALLINDENT = 1U << 20,
66}; 68};
67 69
68struct output_option { 70struct output_option {
@@ -89,6 +91,7 @@ struct output_option {
89 {.str = "data_src", .field = PERF_OUTPUT_DATA_SRC}, 91 {.str = "data_src", .field = PERF_OUTPUT_DATA_SRC},
90 {.str = "weight", .field = PERF_OUTPUT_WEIGHT}, 92 {.str = "weight", .field = PERF_OUTPUT_WEIGHT},
91 {.str = "bpf-output", .field = PERF_OUTPUT_BPF_OUTPUT}, 93 {.str = "bpf-output", .field = PERF_OUTPUT_BPF_OUTPUT},
94 {.str = "callindent", .field = PERF_OUTPUT_CALLINDENT},
92}; 95};
93 96
94/* default set to maintain compatibility with current format */ 97/* default set to maintain compatibility with current format */
@@ -339,7 +342,7 @@ static void set_print_ip_opts(struct perf_event_attr *attr)
339 */ 342 */
340static int perf_session__check_output_opt(struct perf_session *session) 343static int perf_session__check_output_opt(struct perf_session *session)
341{ 344{
342 int j; 345 unsigned int j;
343 struct perf_evsel *evsel; 346 struct perf_evsel *evsel;
344 347
345 for (j = 0; j < PERF_TYPE_MAX; ++j) { 348 for (j = 0; j < PERF_TYPE_MAX; ++j) {
@@ -369,7 +372,7 @@ static int perf_session__check_output_opt(struct perf_session *session)
369 if (!no_callchain) { 372 if (!no_callchain) {
370 bool use_callchain = false; 373 bool use_callchain = false;
371 374
372 evlist__for_each(session->evlist, evsel) { 375 evlist__for_each_entry(session->evlist, evsel) {
373 if (evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN) { 376 if (evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN) {
374 use_callchain = true; 377 use_callchain = true;
375 break; 378 break;
@@ -388,17 +391,20 @@ static int perf_session__check_output_opt(struct perf_session *session)
388 struct perf_event_attr *attr; 391 struct perf_event_attr *attr;
389 392
390 j = PERF_TYPE_TRACEPOINT; 393 j = PERF_TYPE_TRACEPOINT;
391 evsel = perf_session__find_first_evtype(session, j);
392 if (evsel == NULL)
393 goto out;
394 394
395 attr = &evsel->attr; 395 evlist__for_each_entry(session->evlist, evsel) {
396 if (evsel->attr.type != j)
397 continue;
398
399 attr = &evsel->attr;
396 400
397 if (attr->sample_type & PERF_SAMPLE_CALLCHAIN) { 401 if (attr->sample_type & PERF_SAMPLE_CALLCHAIN) {
398 output[j].fields |= PERF_OUTPUT_IP; 402 output[j].fields |= PERF_OUTPUT_IP;
399 output[j].fields |= PERF_OUTPUT_SYM; 403 output[j].fields |= PERF_OUTPUT_SYM;
400 output[j].fields |= PERF_OUTPUT_DSO; 404 output[j].fields |= PERF_OUTPUT_DSO;
401 set_print_ip_opts(attr); 405 set_print_ip_opts(attr);
406 goto out;
407 }
402 } 408 }
403 } 409 }
404 410
@@ -559,6 +565,62 @@ static void print_sample_addr(struct perf_sample *sample,
559 } 565 }
560} 566}
561 567
568static void print_sample_callindent(struct perf_sample *sample,
569 struct perf_evsel *evsel,
570 struct thread *thread,
571 struct addr_location *al)
572{
573 struct perf_event_attr *attr = &evsel->attr;
574 size_t depth = thread_stack__depth(thread);
575 struct addr_location addr_al;
576 const char *name = NULL;
577 static int spacing;
578 int len = 0;
579 u64 ip = 0;
580
581 /*
582 * The 'return' has already been popped off the stack so the depth has
583 * to be adjusted to match the 'call'.
584 */
585 if (thread->ts && sample->flags & PERF_IP_FLAG_RETURN)
586 depth += 1;
587
588 if (sample->flags & (PERF_IP_FLAG_CALL | PERF_IP_FLAG_TRACE_BEGIN)) {
589 if (sample_addr_correlates_sym(attr)) {
590 thread__resolve(thread, &addr_al, sample);
591 if (addr_al.sym)
592 name = addr_al.sym->name;
593 else
594 ip = sample->addr;
595 } else {
596 ip = sample->addr;
597 }
598 } else if (sample->flags & (PERF_IP_FLAG_RETURN | PERF_IP_FLAG_TRACE_END)) {
599 if (al->sym)
600 name = al->sym->name;
601 else
602 ip = sample->ip;
603 }
604
605 if (name)
606 len = printf("%*s%s", (int)depth * 4, "", name);
607 else if (ip)
608 len = printf("%*s%16" PRIx64, (int)depth * 4, "", ip);
609
610 if (len < 0)
611 return;
612
613 /*
614 * Try to keep the output length from changing frequently so that the
615 * output lines up more nicely.
616 */
617 if (len > spacing || (len && len < spacing - 52))
618 spacing = round_up(len + 4, 32);
619
620 if (len < spacing)
621 printf("%*s", spacing - len, "");
622}
623
562static void print_sample_bts(struct perf_sample *sample, 624static void print_sample_bts(struct perf_sample *sample,
563 struct perf_evsel *evsel, 625 struct perf_evsel *evsel,
564 struct thread *thread, 626 struct thread *thread,
@@ -567,6 +629,9 @@ static void print_sample_bts(struct perf_sample *sample,
567 struct perf_event_attr *attr = &evsel->attr; 629 struct perf_event_attr *attr = &evsel->attr;
568 bool print_srcline_last = false; 630 bool print_srcline_last = false;
569 631
632 if (PRINT_FIELD(CALLINDENT))
633 print_sample_callindent(sample, evsel, thread, al);
634
570 /* print branch_from information */ 635 /* print branch_from information */
571 if (PRINT_FIELD(IP)) { 636 if (PRINT_FIELD(IP)) {
572 unsigned int print_opts = output[attr->type].print_ip_opts; 637 unsigned int print_opts = output[attr->type].print_ip_opts;
@@ -603,13 +668,42 @@ static void print_sample_bts(struct perf_sample *sample,
603 printf("\n"); 668 printf("\n");
604} 669}
605 670
671static struct {
672 u32 flags;
673 const char *name;
674} sample_flags[] = {
675 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL, "call"},
676 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN, "return"},
677 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL, "jcc"},
678 {PERF_IP_FLAG_BRANCH, "jmp"},
679 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_INTERRUPT, "int"},
680 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT, "iret"},
681 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_SYSCALLRET, "syscall"},
682 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET, "sysret"},
683 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_ASYNC, "async"},
684 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_ASYNC | PERF_IP_FLAG_INTERRUPT, "hw int"},
685 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TX_ABORT, "tx abrt"},
686 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_BEGIN, "tr strt"},
687 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_END, "tr end"},
688 {0, NULL}
689};
690
606static void print_sample_flags(u32 flags) 691static void print_sample_flags(u32 flags)
607{ 692{
608 const char *chars = PERF_IP_FLAG_CHARS; 693 const char *chars = PERF_IP_FLAG_CHARS;
609 const int n = strlen(PERF_IP_FLAG_CHARS); 694 const int n = strlen(PERF_IP_FLAG_CHARS);
695 bool in_tx = flags & PERF_IP_FLAG_IN_TX;
696 const char *name = NULL;
610 char str[33]; 697 char str[33];
611 int i, pos = 0; 698 int i, pos = 0;
612 699
700 for (i = 0; sample_flags[i].name ; i++) {
701 if (sample_flags[i].flags == (flags & ~PERF_IP_FLAG_IN_TX)) {
702 name = sample_flags[i].name;
703 break;
704 }
705 }
706
613 for (i = 0; i < n; i++, flags >>= 1) { 707 for (i = 0; i < n; i++, flags >>= 1) {
614 if (flags & 1) 708 if (flags & 1)
615 str[pos++] = chars[i]; 709 str[pos++] = chars[i];
@@ -619,7 +713,11 @@ static void print_sample_flags(u32 flags)
619 str[pos++] = '?'; 713 str[pos++] = '?';
620 } 714 }
621 str[pos] = 0; 715 str[pos] = 0;
622 printf(" %-4s ", str); 716
717 if (name)
718 printf(" %-7s%4s ", name, in_tx ? "(x)" : "");
719 else
720 printf(" %-11s ", str);
623} 721}
624 722
625struct printer_data { 723struct printer_data {
@@ -717,7 +815,7 @@ static int perf_evlist__max_name_len(struct perf_evlist *evlist)
717 struct perf_evsel *evsel; 815 struct perf_evsel *evsel;
718 int max = 0; 816 int max = 0;
719 817
720 evlist__for_each(evlist, evsel) { 818 evlist__for_each_entry(evlist, evsel) {
721 int len = strlen(perf_evsel__name(evsel)); 819 int len = strlen(perf_evsel__name(evsel));
722 820
723 max = MAX(len, max); 821 max = MAX(len, max);
@@ -942,7 +1040,7 @@ static int process_attr(struct perf_tool *tool, union perf_event *event,
942 if (evsel->attr.type >= PERF_TYPE_MAX) 1040 if (evsel->attr.type >= PERF_TYPE_MAX)
943 return 0; 1041 return 0;
944 1042
945 evlist__for_each(evlist, pos) { 1043 evlist__for_each_entry(evlist, pos) {
946 if (pos->attr.type == evsel->attr.type && pos != evsel) 1044 if (pos->attr.type == evsel->attr.type && pos != evsel)
947 return 0; 1045 return 0;
948 } 1046 }
@@ -1668,7 +1766,7 @@ static int check_ev_match(char *dir_name, char *scriptname,
1668 snprintf(evname, len + 1, "%s", p); 1766 snprintf(evname, len + 1, "%s", p);
1669 1767
1670 match = 0; 1768 match = 0;
1671 evlist__for_each(session->evlist, pos) { 1769 evlist__for_each_entry(session->evlist, pos) {
1672 if (!strcmp(perf_evsel__name(pos), evname)) { 1770 if (!strcmp(perf_evsel__name(pos), evname)) {
1673 match = 1; 1771 match = 1;
1674 break; 1772 break;
@@ -1870,7 +1968,7 @@ static int process_stat_round_event(struct perf_tool *tool __maybe_unused,
1870 struct stat_round_event *round = &event->stat_round; 1968 struct stat_round_event *round = &event->stat_round;
1871 struct perf_evsel *counter; 1969 struct perf_evsel *counter;
1872 1970
1873 evlist__for_each(session->evlist, counter) { 1971 evlist__for_each_entry(session->evlist, counter) {
1874 perf_stat_process_counter(&stat_config, counter); 1972 perf_stat_process_counter(&stat_config, counter);
1875 process_stat(counter, round->time); 1973 process_stat(counter, round->time);
1876 } 1974 }
@@ -2017,7 +2115,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
2017 "comma separated output fields prepend with 'type:'. " 2115 "comma separated output fields prepend with 'type:'. "
2018 "Valid types: hw,sw,trace,raw. " 2116 "Valid types: hw,sw,trace,raw. "
2019 "Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso," 2117 "Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,"
2020 "addr,symoff,period,iregs,brstack,brstacksym,flags", parse_output_fields), 2118 "addr,symoff,period,iregs,brstack,brstacksym,flags,"
2119 "callindent", parse_output_fields),
2021 OPT_BOOLEAN('a', "all-cpus", &system_wide, 2120 OPT_BOOLEAN('a', "all-cpus", &system_wide,
2022 "system-wide collection from all CPUs"), 2121 "system-wide collection from all CPUs"),
2023 OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", 2122 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)
2256 script.session = session; 2355 script.session = session;
2257 script__setup_sample_type(&script); 2356 script__setup_sample_type(&script);
2258 2357
2358 if (output[PERF_TYPE_HARDWARE].fields & PERF_OUTPUT_CALLINDENT)
2359 itrace_synth_opts.thread_stack = true;
2360
2259 session->itrace_synth_opts = &itrace_synth_opts; 2361 session->itrace_synth_opts = &itrace_synth_opts;
2260 2362
2261 if (cpu_list) { 2363 if (cpu_list) {