diff options
Diffstat (limited to 'tools/perf/builtin-script.c')
-rw-r--r-- | tools/perf/builtin-script.c | 136 |
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 | ||
68 | struct output_option { | 70 | struct 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 | */ |
340 | static int perf_session__check_output_opt(struct perf_session *session) | 343 | static 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 | ||
568 | static 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 | |||
562 | static void print_sample_bts(struct perf_sample *sample, | 624 | static 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 | ||
671 | static 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 | |||
606 | static void print_sample_flags(u32 flags) | 691 | static 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 | ||
625 | struct printer_data { | 723 | struct 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) { |