diff options
| -rw-r--r-- | tools/perf/Documentation/perf-script.txt | 4 | ||||
| -rw-r--r-- | tools/perf/builtin-script.c | 96 | ||||
| -rw-r--r-- | tools/perf/util/symbol.h | 3 | ||||
| -rw-r--r-- | tools/perf/util/thread.h | 2 |
4 files changed, 86 insertions, 19 deletions
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 805baabd238e..a2b37ce48094 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
| @@ -397,6 +397,10 @@ include::itrace.txt[] | |||
| 397 | --call-ret-trace:: | 397 | --call-ret-trace:: |
| 398 | Show call and return stream for intel_pt traces. | 398 | Show call and return stream for intel_pt traces. |
| 399 | 399 | ||
| 400 | --graph-function:: | ||
| 401 | For itrace only show specified functions and their callees for | ||
| 402 | itrace. Multiple functions can be separated by comma. | ||
| 403 | |||
| 400 | SEE ALSO | 404 | SEE ALSO |
| 401 | -------- | 405 | -------- |
| 402 | linkperf:perf-record[1], linkperf:perf-script-perl[1], | 406 | linkperf:perf-record[1], linkperf:perf-script-perl[1], |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 566e1450898a..9d2249ea75e3 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
| @@ -1105,6 +1105,35 @@ out: | |||
| 1105 | return printed; | 1105 | return printed; |
| 1106 | } | 1106 | } |
| 1107 | 1107 | ||
| 1108 | static const char *resolve_branch_sym(struct perf_sample *sample, | ||
| 1109 | struct perf_evsel *evsel, | ||
| 1110 | struct thread *thread, | ||
| 1111 | struct addr_location *al, | ||
| 1112 | u64 *ip) | ||
| 1113 | { | ||
| 1114 | struct addr_location addr_al; | ||
| 1115 | struct perf_event_attr *attr = &evsel->attr; | ||
| 1116 | const char *name = NULL; | ||
| 1117 | |||
| 1118 | if (sample->flags & (PERF_IP_FLAG_CALL | PERF_IP_FLAG_TRACE_BEGIN)) { | ||
| 1119 | if (sample_addr_correlates_sym(attr)) { | ||
| 1120 | thread__resolve(thread, &addr_al, sample); | ||
| 1121 | if (addr_al.sym) | ||
| 1122 | name = addr_al.sym->name; | ||
| 1123 | else | ||
| 1124 | *ip = sample->addr; | ||
| 1125 | } else { | ||
| 1126 | *ip = sample->addr; | ||
| 1127 | } | ||
| 1128 | } else if (sample->flags & (PERF_IP_FLAG_RETURN | PERF_IP_FLAG_TRACE_END)) { | ||
| 1129 | if (al->sym) | ||
| 1130 | name = al->sym->name; | ||
| 1131 | else | ||
| 1132 | *ip = sample->ip; | ||
| 1133 | } | ||
| 1134 | return name; | ||
| 1135 | } | ||
| 1136 | |||
| 1108 | static int perf_sample__fprintf_callindent(struct perf_sample *sample, | 1137 | static int perf_sample__fprintf_callindent(struct perf_sample *sample, |
| 1109 | struct perf_evsel *evsel, | 1138 | struct perf_evsel *evsel, |
| 1110 | struct thread *thread, | 1139 | struct thread *thread, |
| @@ -1112,7 +1141,6 @@ static int perf_sample__fprintf_callindent(struct perf_sample *sample, | |||
| 1112 | { | 1141 | { |
| 1113 | struct perf_event_attr *attr = &evsel->attr; | 1142 | struct perf_event_attr *attr = &evsel->attr; |
| 1114 | size_t depth = thread_stack__depth(thread); | 1143 | size_t depth = thread_stack__depth(thread); |
| 1115 | struct addr_location addr_al; | ||
| 1116 | const char *name = NULL; | 1144 | const char *name = NULL; |
| 1117 | static int spacing; | 1145 | static int spacing; |
| 1118 | int len = 0; | 1146 | int len = 0; |
| @@ -1126,22 +1154,7 @@ static int perf_sample__fprintf_callindent(struct perf_sample *sample, | |||
| 1126 | if (thread->ts && sample->flags & PERF_IP_FLAG_RETURN) | 1154 | if (thread->ts && sample->flags & PERF_IP_FLAG_RETURN) |
| 1127 | depth += 1; | 1155 | depth += 1; |
| 1128 | 1156 | ||
| 1129 | if (sample->flags & (PERF_IP_FLAG_CALL | PERF_IP_FLAG_TRACE_BEGIN)) { | 1157 | name = resolve_branch_sym(sample, evsel, thread, al, &ip); |
| 1130 | if (sample_addr_correlates_sym(attr)) { | ||
| 1131 | thread__resolve(thread, &addr_al, sample); | ||
| 1132 | if (addr_al.sym) | ||
| 1133 | name = addr_al.sym->name; | ||
| 1134 | else | ||
| 1135 | ip = sample->addr; | ||
| 1136 | } else { | ||
| 1137 | ip = sample->addr; | ||
| 1138 | } | ||
| 1139 | } else if (sample->flags & (PERF_IP_FLAG_RETURN | PERF_IP_FLAG_TRACE_END)) { | ||
| 1140 | if (al->sym) | ||
| 1141 | name = al->sym->name; | ||
| 1142 | else | ||
| 1143 | ip = sample->ip; | ||
| 1144 | } | ||
| 1145 | 1158 | ||
| 1146 | if (PRINT_FIELD(DSO) && !(PRINT_FIELD(IP) || PRINT_FIELD(ADDR))) { | 1159 | if (PRINT_FIELD(DSO) && !(PRINT_FIELD(IP) || PRINT_FIELD(ADDR))) { |
| 1147 | dlen += fprintf(fp, "("); | 1160 | dlen += fprintf(fp, "("); |
| @@ -1647,6 +1660,47 @@ static void perf_sample__fprint_metric(struct perf_script *script, | |||
| 1647 | } | 1660 | } |
| 1648 | } | 1661 | } |
| 1649 | 1662 | ||
| 1663 | static bool show_event(struct perf_sample *sample, | ||
| 1664 | struct perf_evsel *evsel, | ||
| 1665 | struct thread *thread, | ||
| 1666 | struct addr_location *al) | ||
| 1667 | { | ||
| 1668 | int depth = thread_stack__depth(thread); | ||
| 1669 | |||
| 1670 | if (!symbol_conf.graph_function) | ||
| 1671 | return true; | ||
| 1672 | |||
| 1673 | if (thread->filter) { | ||
| 1674 | if (depth <= thread->filter_entry_depth) { | ||
| 1675 | thread->filter = false; | ||
| 1676 | return false; | ||
| 1677 | } | ||
| 1678 | return true; | ||
| 1679 | } else { | ||
| 1680 | const char *s = symbol_conf.graph_function; | ||
| 1681 | u64 ip; | ||
| 1682 | const char *name = resolve_branch_sym(sample, evsel, thread, al, | ||
| 1683 | &ip); | ||
| 1684 | unsigned nlen; | ||
| 1685 | |||
| 1686 | if (!name) | ||
| 1687 | return false; | ||
| 1688 | nlen = strlen(name); | ||
| 1689 | while (*s) { | ||
| 1690 | unsigned len = strcspn(s, ","); | ||
| 1691 | if (nlen == len && !strncmp(name, s, len)) { | ||
| 1692 | thread->filter = true; | ||
| 1693 | thread->filter_entry_depth = depth; | ||
| 1694 | return true; | ||
| 1695 | } | ||
| 1696 | s += len; | ||
| 1697 | if (*s == ',') | ||
| 1698 | s++; | ||
| 1699 | } | ||
| 1700 | return false; | ||
| 1701 | } | ||
| 1702 | } | ||
| 1703 | |||
| 1650 | static void process_event(struct perf_script *script, | 1704 | static void process_event(struct perf_script *script, |
| 1651 | struct perf_sample *sample, struct perf_evsel *evsel, | 1705 | struct perf_sample *sample, struct perf_evsel *evsel, |
| 1652 | struct addr_location *al, | 1706 | struct addr_location *al, |
| @@ -1661,6 +1715,9 @@ static void process_event(struct perf_script *script, | |||
| 1661 | if (output[type].fields == 0) | 1715 | if (output[type].fields == 0) |
| 1662 | return; | 1716 | return; |
| 1663 | 1717 | ||
| 1718 | if (!show_event(sample, evsel, thread, al)) | ||
| 1719 | return; | ||
| 1720 | |||
| 1664 | ++es->samples; | 1721 | ++es->samples; |
| 1665 | 1722 | ||
| 1666 | perf_sample__fprintf_start(sample, thread, evsel, | 1723 | perf_sample__fprintf_start(sample, thread, evsel, |
| @@ -3237,6 +3294,8 @@ int cmd_script(int argc, const char **argv) | |||
| 3237 | "Decode calls from from itrace", parse_call_trace), | 3294 | "Decode calls from from itrace", parse_call_trace), |
| 3238 | OPT_CALLBACK_OPTARG(0, "call-ret-trace", &itrace_synth_opts, NULL, NULL, | 3295 | OPT_CALLBACK_OPTARG(0, "call-ret-trace", &itrace_synth_opts, NULL, NULL, |
| 3239 | "Decode calls and returns from itrace", parse_callret_trace), | 3296 | "Decode calls and returns from itrace", parse_callret_trace), |
| 3297 | OPT_STRING(0, "graph-function", &symbol_conf.graph_function, "symbol[,symbol...]", | ||
| 3298 | "Only print symbols and callees with --call-trace/--call-ret-trace"), | ||
| 3240 | OPT_STRING(0, "stop-bt", &symbol_conf.bt_stop_list_str, "symbol[,symbol...]", | 3299 | OPT_STRING(0, "stop-bt", &symbol_conf.bt_stop_list_str, "symbol[,symbol...]", |
| 3241 | "Stop display of callgraph at these symbols"), | 3300 | "Stop display of callgraph at these symbols"), |
| 3242 | OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | 3301 | OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"), |
| @@ -3494,7 +3553,8 @@ int cmd_script(int argc, const char **argv) | |||
| 3494 | script.session = session; | 3553 | script.session = session; |
| 3495 | script__setup_sample_type(&script); | 3554 | script__setup_sample_type(&script); |
| 3496 | 3555 | ||
| 3497 | if (output[PERF_TYPE_HARDWARE].fields & PERF_OUTPUT_CALLINDENT) | 3556 | if ((output[PERF_TYPE_HARDWARE].fields & PERF_OUTPUT_CALLINDENT) || |
| 3557 | symbol_conf.graph_function) | ||
| 3498 | itrace_synth_opts.thread_stack = true; | 3558 | itrace_synth_opts.thread_stack = true; |
| 3499 | 3559 | ||
| 3500 | session->itrace_synth_opts = &itrace_synth_opts; | 3560 | session->itrace_synth_opts = &itrace_synth_opts; |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index f25fae4b5743..d726a8a7bb1b 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
| @@ -123,7 +123,8 @@ struct symbol_conf { | |||
| 123 | const char *vmlinux_name, | 123 | const char *vmlinux_name, |
| 124 | *kallsyms_name, | 124 | *kallsyms_name, |
| 125 | *source_prefix, | 125 | *source_prefix, |
| 126 | *field_sep; | 126 | *field_sep, |
| 127 | *graph_function; | ||
| 127 | const char *default_guest_vmlinux_name, | 128 | const char *default_guest_vmlinux_name, |
| 128 | *default_guest_kallsyms, | 129 | *default_guest_kallsyms, |
| 129 | *default_guest_modules; | 130 | *default_guest_modules; |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 07606aa6998d..36c09a9904e6 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
| @@ -42,6 +42,8 @@ struct thread { | |||
| 42 | void *addr_space; | 42 | void *addr_space; |
| 43 | struct unwind_libunwind_ops *unwind_libunwind_ops; | 43 | struct unwind_libunwind_ops *unwind_libunwind_ops; |
| 44 | #endif | 44 | #endif |
| 45 | bool filter; | ||
| 46 | int filter_entry_depth; | ||
| 45 | }; | 47 | }; |
| 46 | 48 | ||
| 47 | struct machine; | 49 | struct machine; |
