diff options
Diffstat (limited to 'tools/perf/builtin-script.c')
-rw-r--r-- | tools/perf/builtin-script.c | 166 |
1 files changed, 142 insertions, 24 deletions
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 4da5e32b9e03..b5bc85bd0bbe 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -44,6 +44,7 @@ | |||
44 | #include <sys/stat.h> | 44 | #include <sys/stat.h> |
45 | #include <fcntl.h> | 45 | #include <fcntl.h> |
46 | #include <unistd.h> | 46 | #include <unistd.h> |
47 | #include <subcmd/pager.h> | ||
47 | 48 | ||
48 | #include "sane_ctype.h" | 49 | #include "sane_ctype.h" |
49 | 50 | ||
@@ -912,7 +913,7 @@ static int grab_bb(u8 *buffer, u64 start, u64 end, | |||
912 | 913 | ||
913 | static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en, | 914 | static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en, |
914 | struct perf_insn *x, u8 *inbuf, int len, | 915 | struct perf_insn *x, u8 *inbuf, int len, |
915 | int insn, FILE *fp) | 916 | int insn, FILE *fp, int *total_cycles) |
916 | { | 917 | { |
917 | int printed = fprintf(fp, "\t%016" PRIx64 "\t%-30s\t#%s%s%s%s", ip, | 918 | int printed = fprintf(fp, "\t%016" PRIx64 "\t%-30s\t#%s%s%s%s", ip, |
918 | dump_insn(x, ip, inbuf, len, NULL), | 919 | dump_insn(x, ip, inbuf, len, NULL), |
@@ -921,7 +922,8 @@ static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en, | |||
921 | en->flags.in_tx ? " INTX" : "", | 922 | en->flags.in_tx ? " INTX" : "", |
922 | en->flags.abort ? " ABORT" : ""); | 923 | en->flags.abort ? " ABORT" : ""); |
923 | if (en->flags.cycles) { | 924 | if (en->flags.cycles) { |
924 | printed += fprintf(fp, " %d cycles", en->flags.cycles); | 925 | *total_cycles += en->flags.cycles; |
926 | printed += fprintf(fp, " %d cycles [%d]", en->flags.cycles, *total_cycles); | ||
925 | if (insn) | 927 | if (insn) |
926 | printed += fprintf(fp, " %.2f IPC", (float)insn / en->flags.cycles); | 928 | printed += fprintf(fp, " %.2f IPC", (float)insn / en->flags.cycles); |
927 | } | 929 | } |
@@ -978,6 +980,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample, | |||
978 | u8 buffer[MAXBB]; | 980 | u8 buffer[MAXBB]; |
979 | unsigned off; | 981 | unsigned off; |
980 | struct symbol *lastsym = NULL; | 982 | struct symbol *lastsym = NULL; |
983 | int total_cycles = 0; | ||
981 | 984 | ||
982 | if (!(br && br->nr)) | 985 | if (!(br && br->nr)) |
983 | return 0; | 986 | return 0; |
@@ -998,7 +1001,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample, | |||
998 | printed += ip__fprintf_sym(br->entries[nr - 1].from, thread, | 1001 | printed += ip__fprintf_sym(br->entries[nr - 1].from, thread, |
999 | x.cpumode, x.cpu, &lastsym, attr, fp); | 1002 | x.cpumode, x.cpu, &lastsym, attr, fp); |
1000 | printed += ip__fprintf_jump(br->entries[nr - 1].from, &br->entries[nr - 1], | 1003 | printed += ip__fprintf_jump(br->entries[nr - 1].from, &br->entries[nr - 1], |
1001 | &x, buffer, len, 0, fp); | 1004 | &x, buffer, len, 0, fp, &total_cycles); |
1002 | } | 1005 | } |
1003 | 1006 | ||
1004 | /* Print all blocks */ | 1007 | /* Print all blocks */ |
@@ -1026,7 +1029,8 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample, | |||
1026 | 1029 | ||
1027 | printed += ip__fprintf_sym(ip, thread, x.cpumode, x.cpu, &lastsym, attr, fp); | 1030 | printed += ip__fprintf_sym(ip, thread, x.cpumode, x.cpu, &lastsym, attr, fp); |
1028 | if (ip == end) { | 1031 | if (ip == end) { |
1029 | printed += ip__fprintf_jump(ip, &br->entries[i], &x, buffer + off, len - off, insn, fp); | 1032 | printed += ip__fprintf_jump(ip, &br->entries[i], &x, buffer + off, len - off, insn, fp, |
1033 | &total_cycles); | ||
1030 | break; | 1034 | break; |
1031 | } else { | 1035 | } else { |
1032 | printed += fprintf(fp, "\t%016" PRIx64 "\t%s\n", ip, | 1036 | printed += fprintf(fp, "\t%016" PRIx64 "\t%s\n", ip, |
@@ -1104,6 +1108,35 @@ out: | |||
1104 | return printed; | 1108 | return printed; |
1105 | } | 1109 | } |
1106 | 1110 | ||
1111 | static const char *resolve_branch_sym(struct perf_sample *sample, | ||
1112 | struct perf_evsel *evsel, | ||
1113 | struct thread *thread, | ||
1114 | struct addr_location *al, | ||
1115 | u64 *ip) | ||
1116 | { | ||
1117 | struct addr_location addr_al; | ||
1118 | struct perf_event_attr *attr = &evsel->attr; | ||
1119 | const char *name = NULL; | ||
1120 | |||
1121 | if (sample->flags & (PERF_IP_FLAG_CALL | PERF_IP_FLAG_TRACE_BEGIN)) { | ||
1122 | if (sample_addr_correlates_sym(attr)) { | ||
1123 | thread__resolve(thread, &addr_al, sample); | ||
1124 | if (addr_al.sym) | ||
1125 | name = addr_al.sym->name; | ||
1126 | else | ||
1127 | *ip = sample->addr; | ||
1128 | } else { | ||
1129 | *ip = sample->addr; | ||
1130 | } | ||
1131 | } else if (sample->flags & (PERF_IP_FLAG_RETURN | PERF_IP_FLAG_TRACE_END)) { | ||
1132 | if (al->sym) | ||
1133 | name = al->sym->name; | ||
1134 | else | ||
1135 | *ip = sample->ip; | ||
1136 | } | ||
1137 | return name; | ||
1138 | } | ||
1139 | |||
1107 | static int perf_sample__fprintf_callindent(struct perf_sample *sample, | 1140 | static int perf_sample__fprintf_callindent(struct perf_sample *sample, |
1108 | struct perf_evsel *evsel, | 1141 | struct perf_evsel *evsel, |
1109 | struct thread *thread, | 1142 | struct thread *thread, |
@@ -1111,7 +1144,6 @@ static int perf_sample__fprintf_callindent(struct perf_sample *sample, | |||
1111 | { | 1144 | { |
1112 | struct perf_event_attr *attr = &evsel->attr; | 1145 | struct perf_event_attr *attr = &evsel->attr; |
1113 | size_t depth = thread_stack__depth(thread); | 1146 | size_t depth = thread_stack__depth(thread); |
1114 | struct addr_location addr_al; | ||
1115 | const char *name = NULL; | 1147 | const char *name = NULL; |
1116 | static int spacing; | 1148 | static int spacing; |
1117 | int len = 0; | 1149 | int len = 0; |
@@ -1125,22 +1157,7 @@ static int perf_sample__fprintf_callindent(struct perf_sample *sample, | |||
1125 | if (thread->ts && sample->flags & PERF_IP_FLAG_RETURN) | 1157 | if (thread->ts && sample->flags & PERF_IP_FLAG_RETURN) |
1126 | depth += 1; | 1158 | depth += 1; |
1127 | 1159 | ||
1128 | if (sample->flags & (PERF_IP_FLAG_CALL | PERF_IP_FLAG_TRACE_BEGIN)) { | 1160 | name = resolve_branch_sym(sample, evsel, thread, al, &ip); |
1129 | if (sample_addr_correlates_sym(attr)) { | ||
1130 | thread__resolve(thread, &addr_al, sample); | ||
1131 | if (addr_al.sym) | ||
1132 | name = addr_al.sym->name; | ||
1133 | else | ||
1134 | ip = sample->addr; | ||
1135 | } else { | ||
1136 | ip = sample->addr; | ||
1137 | } | ||
1138 | } else if (sample->flags & (PERF_IP_FLAG_RETURN | PERF_IP_FLAG_TRACE_END)) { | ||
1139 | if (al->sym) | ||
1140 | name = al->sym->name; | ||
1141 | else | ||
1142 | ip = sample->ip; | ||
1143 | } | ||
1144 | 1161 | ||
1145 | if (PRINT_FIELD(DSO) && !(PRINT_FIELD(IP) || PRINT_FIELD(ADDR))) { | 1162 | if (PRINT_FIELD(DSO) && !(PRINT_FIELD(IP) || PRINT_FIELD(ADDR))) { |
1146 | dlen += fprintf(fp, "("); | 1163 | dlen += fprintf(fp, "("); |
@@ -1646,6 +1663,47 @@ static void perf_sample__fprint_metric(struct perf_script *script, | |||
1646 | } | 1663 | } |
1647 | } | 1664 | } |
1648 | 1665 | ||
1666 | static bool show_event(struct perf_sample *sample, | ||
1667 | struct perf_evsel *evsel, | ||
1668 | struct thread *thread, | ||
1669 | struct addr_location *al) | ||
1670 | { | ||
1671 | int depth = thread_stack__depth(thread); | ||
1672 | |||
1673 | if (!symbol_conf.graph_function) | ||
1674 | return true; | ||
1675 | |||
1676 | if (thread->filter) { | ||
1677 | if (depth <= thread->filter_entry_depth) { | ||
1678 | thread->filter = false; | ||
1679 | return false; | ||
1680 | } | ||
1681 | return true; | ||
1682 | } else { | ||
1683 | const char *s = symbol_conf.graph_function; | ||
1684 | u64 ip; | ||
1685 | const char *name = resolve_branch_sym(sample, evsel, thread, al, | ||
1686 | &ip); | ||
1687 | unsigned nlen; | ||
1688 | |||
1689 | if (!name) | ||
1690 | return false; | ||
1691 | nlen = strlen(name); | ||
1692 | while (*s) { | ||
1693 | unsigned len = strcspn(s, ","); | ||
1694 | if (nlen == len && !strncmp(name, s, len)) { | ||
1695 | thread->filter = true; | ||
1696 | thread->filter_entry_depth = depth; | ||
1697 | return true; | ||
1698 | } | ||
1699 | s += len; | ||
1700 | if (*s == ',') | ||
1701 | s++; | ||
1702 | } | ||
1703 | return false; | ||
1704 | } | ||
1705 | } | ||
1706 | |||
1649 | static void process_event(struct perf_script *script, | 1707 | static void process_event(struct perf_script *script, |
1650 | struct perf_sample *sample, struct perf_evsel *evsel, | 1708 | struct perf_sample *sample, struct perf_evsel *evsel, |
1651 | struct addr_location *al, | 1709 | struct addr_location *al, |
@@ -1660,6 +1718,9 @@ static void process_event(struct perf_script *script, | |||
1660 | if (output[type].fields == 0) | 1718 | if (output[type].fields == 0) |
1661 | return; | 1719 | return; |
1662 | 1720 | ||
1721 | if (!show_event(sample, evsel, thread, al)) | ||
1722 | return; | ||
1723 | |||
1663 | ++es->samples; | 1724 | ++es->samples; |
1664 | 1725 | ||
1665 | perf_sample__fprintf_start(sample, thread, evsel, | 1726 | perf_sample__fprintf_start(sample, thread, evsel, |
@@ -1737,6 +1798,9 @@ static void process_event(struct perf_script *script, | |||
1737 | 1798 | ||
1738 | if (PRINT_FIELD(METRIC)) | 1799 | if (PRINT_FIELD(METRIC)) |
1739 | perf_sample__fprint_metric(script, thread, evsel, sample, fp); | 1800 | perf_sample__fprint_metric(script, thread, evsel, sample, fp); |
1801 | |||
1802 | if (verbose) | ||
1803 | fflush(fp); | ||
1740 | } | 1804 | } |
1741 | 1805 | ||
1742 | static struct scripting_ops *scripting_ops; | 1806 | static struct scripting_ops *scripting_ops; |
@@ -3100,6 +3164,44 @@ static int perf_script__process_auxtrace_info(struct perf_session *session, | |||
3100 | #define perf_script__process_auxtrace_info 0 | 3164 | #define perf_script__process_auxtrace_info 0 |
3101 | #endif | 3165 | #endif |
3102 | 3166 | ||
3167 | static int parse_insn_trace(const struct option *opt __maybe_unused, | ||
3168 | const char *str __maybe_unused, | ||
3169 | int unset __maybe_unused) | ||
3170 | { | ||
3171 | parse_output_fields(NULL, "+insn,-event,-period", 0); | ||
3172 | itrace_parse_synth_opts(opt, "i0ns", 0); | ||
3173 | nanosecs = true; | ||
3174 | return 0; | ||
3175 | } | ||
3176 | |||
3177 | static int parse_xed(const struct option *opt __maybe_unused, | ||
3178 | const char *str __maybe_unused, | ||
3179 | int unset __maybe_unused) | ||
3180 | { | ||
3181 | force_pager("xed -F insn: -A -64 | less"); | ||
3182 | return 0; | ||
3183 | } | ||
3184 | |||
3185 | static int parse_call_trace(const struct option *opt __maybe_unused, | ||
3186 | const char *str __maybe_unused, | ||
3187 | int unset __maybe_unused) | ||
3188 | { | ||
3189 | parse_output_fields(NULL, "-ip,-addr,-event,-period,+callindent", 0); | ||
3190 | itrace_parse_synth_opts(opt, "cewp", 0); | ||
3191 | nanosecs = true; | ||
3192 | return 0; | ||
3193 | } | ||
3194 | |||
3195 | static int parse_callret_trace(const struct option *opt __maybe_unused, | ||
3196 | const char *str __maybe_unused, | ||
3197 | int unset __maybe_unused) | ||
3198 | { | ||
3199 | parse_output_fields(NULL, "-ip,-addr,-event,-period,+callindent,+flags", 0); | ||
3200 | itrace_parse_synth_opts(opt, "crewp", 0); | ||
3201 | nanosecs = true; | ||
3202 | return 0; | ||
3203 | } | ||
3204 | |||
3103 | int cmd_script(int argc, const char **argv) | 3205 | int cmd_script(int argc, const char **argv) |
3104 | { | 3206 | { |
3105 | bool show_full_info = false; | 3207 | bool show_full_info = false; |
@@ -3109,7 +3211,10 @@ int cmd_script(int argc, const char **argv) | |||
3109 | char *rec_script_path = NULL; | 3211 | char *rec_script_path = NULL; |
3110 | char *rep_script_path = NULL; | 3212 | char *rep_script_path = NULL; |
3111 | struct perf_session *session; | 3213 | struct perf_session *session; |
3112 | struct itrace_synth_opts itrace_synth_opts = { .set = false, }; | 3214 | struct itrace_synth_opts itrace_synth_opts = { |
3215 | .set = false, | ||
3216 | .default_no_sample = true, | ||
3217 | }; | ||
3113 | char *script_path = NULL; | 3218 | char *script_path = NULL; |
3114 | const char **__argv; | 3219 | const char **__argv; |
3115 | int i, j, err = 0; | 3220 | int i, j, err = 0; |
@@ -3184,6 +3289,16 @@ int cmd_script(int argc, const char **argv) | |||
3184 | "system-wide collection from all CPUs"), | 3289 | "system-wide collection from all CPUs"), |
3185 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", | 3290 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", |
3186 | "only consider these symbols"), | 3291 | "only consider these symbols"), |
3292 | OPT_CALLBACK_OPTARG(0, "insn-trace", &itrace_synth_opts, NULL, NULL, | ||
3293 | "Decode instructions from itrace", parse_insn_trace), | ||
3294 | OPT_CALLBACK_OPTARG(0, "xed", NULL, NULL, NULL, | ||
3295 | "Run xed disassembler on output", parse_xed), | ||
3296 | OPT_CALLBACK_OPTARG(0, "call-trace", &itrace_synth_opts, NULL, NULL, | ||
3297 | "Decode calls from from itrace", parse_call_trace), | ||
3298 | OPT_CALLBACK_OPTARG(0, "call-ret-trace", &itrace_synth_opts, NULL, NULL, | ||
3299 | "Decode calls and returns from itrace", parse_callret_trace), | ||
3300 | OPT_STRING(0, "graph-function", &symbol_conf.graph_function, "symbol[,symbol...]", | ||
3301 | "Only print symbols and callees with --call-trace/--call-ret-trace"), | ||
3187 | OPT_STRING(0, "stop-bt", &symbol_conf.bt_stop_list_str, "symbol[,symbol...]", | 3302 | OPT_STRING(0, "stop-bt", &symbol_conf.bt_stop_list_str, "symbol[,symbol...]", |
3188 | "Stop display of callgraph at these symbols"), | 3303 | "Stop display of callgraph at these symbols"), |
3189 | OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | 3304 | OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"), |
@@ -3417,8 +3532,10 @@ int cmd_script(int argc, const char **argv) | |||
3417 | exit(-1); | 3532 | exit(-1); |
3418 | } | 3533 | } |
3419 | 3534 | ||
3420 | if (!script_name) | 3535 | if (!script_name) { |
3421 | setup_pager(); | 3536 | setup_pager(); |
3537 | use_browser = 0; | ||
3538 | } | ||
3422 | 3539 | ||
3423 | session = perf_session__new(&data, false, &script.tool); | 3540 | session = perf_session__new(&data, false, &script.tool); |
3424 | if (session == NULL) | 3541 | if (session == NULL) |
@@ -3439,7 +3556,8 @@ int cmd_script(int argc, const char **argv) | |||
3439 | script.session = session; | 3556 | script.session = session; |
3440 | script__setup_sample_type(&script); | 3557 | script__setup_sample_type(&script); |
3441 | 3558 | ||
3442 | if (output[PERF_TYPE_HARDWARE].fields & PERF_OUTPUT_CALLINDENT) | 3559 | if ((output[PERF_TYPE_HARDWARE].fields & PERF_OUTPUT_CALLINDENT) || |
3560 | symbol_conf.graph_function) | ||
3443 | itrace_synth_opts.thread_stack = true; | 3561 | itrace_synth_opts.thread_stack = true; |
3444 | 3562 | ||
3445 | session->itrace_synth_opts = &itrace_synth_opts; | 3563 | session->itrace_synth_opts = &itrace_synth_opts; |