diff options
Diffstat (limited to 'tools/perf/builtin-script.c')
-rw-r--r-- | tools/perf/builtin-script.c | 296 |
1 files changed, 229 insertions, 67 deletions
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index ac574ea23917..974f6d3f4e53 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -49,57 +49,169 @@ struct output_option { | |||
49 | }; | 49 | }; |
50 | 50 | ||
51 | /* default set to maintain compatibility with current format */ | 51 | /* default set to maintain compatibility with current format */ |
52 | static u64 output_fields[PERF_TYPE_MAX] = { | 52 | static struct { |
53 | [PERF_TYPE_HARDWARE] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \ | 53 | bool user_set; |
54 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \ | 54 | bool wildcard_set; |
55 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, | 55 | u64 fields; |
56 | 56 | u64 invalid_fields; | |
57 | [PERF_TYPE_SOFTWARE] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \ | 57 | } output[PERF_TYPE_MAX] = { |
58 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \ | 58 | |
59 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, | 59 | [PERF_TYPE_HARDWARE] = { |
60 | 60 | .user_set = false, | |
61 | [PERF_TYPE_TRACEPOINT] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \ | 61 | |
62 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \ | 62 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | |
63 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE, | 63 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
64 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, | ||
65 | |||
66 | .invalid_fields = PERF_OUTPUT_TRACE, | ||
67 | }, | ||
68 | |||
69 | [PERF_TYPE_SOFTWARE] = { | ||
70 | .user_set = false, | ||
71 | |||
72 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | ||
73 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | ||
74 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, | ||
75 | |||
76 | .invalid_fields = PERF_OUTPUT_TRACE, | ||
77 | }, | ||
78 | |||
79 | [PERF_TYPE_TRACEPOINT] = { | ||
80 | .user_set = false, | ||
81 | |||
82 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | ||
83 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | ||
84 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE, | ||
85 | }, | ||
86 | |||
87 | [PERF_TYPE_RAW] = { | ||
88 | .user_set = false, | ||
89 | |||
90 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | ||
91 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | ||
92 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, | ||
93 | |||
94 | .invalid_fields = PERF_OUTPUT_TRACE, | ||
95 | }, | ||
64 | }; | 96 | }; |
65 | 97 | ||
66 | static bool output_set_by_user; | 98 | static bool output_set_by_user(void) |
99 | { | ||
100 | int j; | ||
101 | for (j = 0; j < PERF_TYPE_MAX; ++j) { | ||
102 | if (output[j].user_set) | ||
103 | return true; | ||
104 | } | ||
105 | return false; | ||
106 | } | ||
107 | |||
108 | static const char *output_field2str(enum perf_output_field field) | ||
109 | { | ||
110 | int i, imax = ARRAY_SIZE(all_output_options); | ||
111 | const char *str = ""; | ||
112 | |||
113 | for (i = 0; i < imax; ++i) { | ||
114 | if (all_output_options[i].field == field) { | ||
115 | str = all_output_options[i].str; | ||
116 | break; | ||
117 | } | ||
118 | } | ||
119 | return str; | ||
120 | } | ||
67 | 121 | ||
68 | #define PRINT_FIELD(x) (output_fields[attr->type] & PERF_OUTPUT_##x) | 122 | #define PRINT_FIELD(x) (output[attr->type].fields & PERF_OUTPUT_##x) |
69 | 123 | ||
70 | static int perf_session__check_attr(struct perf_session *session, | 124 | static int perf_event_attr__check_stype(struct perf_event_attr *attr, |
71 | struct perf_event_attr *attr) | 125 | u64 sample_type, const char *sample_msg, |
126 | enum perf_output_field field) | ||
72 | { | 127 | { |
128 | int type = attr->type; | ||
129 | const char *evname; | ||
130 | |||
131 | if (attr->sample_type & sample_type) | ||
132 | return 0; | ||
133 | |||
134 | if (output[type].user_set) { | ||
135 | evname = __event_name(attr->type, attr->config); | ||
136 | pr_err("Samples for '%s' event do not have %s attribute set. " | ||
137 | "Cannot print '%s' field.\n", | ||
138 | evname, sample_msg, output_field2str(field)); | ||
139 | return -1; | ||
140 | } | ||
141 | |||
142 | /* user did not ask for it explicitly so remove from the default list */ | ||
143 | output[type].fields &= ~field; | ||
144 | evname = __event_name(attr->type, attr->config); | ||
145 | pr_debug("Samples for '%s' event do not have %s attribute set. " | ||
146 | "Skipping '%s' field.\n", | ||
147 | evname, sample_msg, output_field2str(field)); | ||
148 | |||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | static int perf_evsel__check_attr(struct perf_evsel *evsel, | ||
153 | struct perf_session *session) | ||
154 | { | ||
155 | struct perf_event_attr *attr = &evsel->attr; | ||
156 | |||
73 | if (PRINT_FIELD(TRACE) && | 157 | if (PRINT_FIELD(TRACE) && |
74 | !perf_session__has_traces(session, "record -R")) | 158 | !perf_session__has_traces(session, "record -R")) |
75 | return -EINVAL; | 159 | return -EINVAL; |
76 | 160 | ||
77 | if (PRINT_FIELD(SYM)) { | 161 | if (PRINT_FIELD(SYM)) { |
78 | if (!(session->sample_type & PERF_SAMPLE_IP)) { | 162 | if (perf_event_attr__check_stype(attr, PERF_SAMPLE_IP, "IP", |
79 | pr_err("Samples do not contain IP data.\n"); | 163 | PERF_OUTPUT_SYM)) |
80 | return -EINVAL; | 164 | return -EINVAL; |
81 | } | 165 | |
82 | if (!no_callchain && | 166 | if (!no_callchain && |
83 | !(session->sample_type & PERF_SAMPLE_CALLCHAIN)) | 167 | !(attr->sample_type & PERF_SAMPLE_CALLCHAIN)) |
84 | symbol_conf.use_callchain = false; | 168 | symbol_conf.use_callchain = false; |
85 | } | 169 | } |
86 | 170 | ||
87 | if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) && | 171 | if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) && |
88 | !(session->sample_type & PERF_SAMPLE_TID)) { | 172 | perf_event_attr__check_stype(attr, PERF_SAMPLE_TID, "TID", |
89 | pr_err("Samples do not contain TID/PID data.\n"); | 173 | PERF_OUTPUT_TID|PERF_OUTPUT_PID)) |
90 | return -EINVAL; | 174 | return -EINVAL; |
91 | } | ||
92 | 175 | ||
93 | if (PRINT_FIELD(TIME) && | 176 | if (PRINT_FIELD(TIME) && |
94 | !(session->sample_type & PERF_SAMPLE_TIME)) { | 177 | perf_event_attr__check_stype(attr, PERF_SAMPLE_TIME, "TIME", |
95 | pr_err("Samples do not contain timestamps.\n"); | 178 | PERF_OUTPUT_TIME)) |
96 | return -EINVAL; | 179 | return -EINVAL; |
97 | } | ||
98 | 180 | ||
99 | if (PRINT_FIELD(CPU) && | 181 | if (PRINT_FIELD(CPU) && |
100 | !(session->sample_type & PERF_SAMPLE_CPU)) { | 182 | perf_event_attr__check_stype(attr, PERF_SAMPLE_CPU, "CPU", |
101 | pr_err("Samples do not contain cpu.\n"); | 183 | PERF_OUTPUT_CPU)) |
102 | return -EINVAL; | 184 | return -EINVAL; |
185 | |||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | /* | ||
190 | * verify all user requested events exist and the samples | ||
191 | * have the expected data | ||
192 | */ | ||
193 | static int perf_session__check_output_opt(struct perf_session *session) | ||
194 | { | ||
195 | int j; | ||
196 | struct perf_evsel *evsel; | ||
197 | |||
198 | for (j = 0; j < PERF_TYPE_MAX; ++j) { | ||
199 | evsel = perf_session__find_first_evtype(session, j); | ||
200 | |||
201 | /* | ||
202 | * even if fields is set to 0 (ie., show nothing) event must | ||
203 | * exist if user explicitly includes it on the command line | ||
204 | */ | ||
205 | if (!evsel && output[j].user_set && !output[j].wildcard_set) { | ||
206 | pr_err("%s events do not exist. " | ||
207 | "Remove corresponding -f option to proceed.\n", | ||
208 | event_type(j)); | ||
209 | return -1; | ||
210 | } | ||
211 | |||
212 | if (evsel && output[j].fields && | ||
213 | perf_evsel__check_attr(evsel, session)) | ||
214 | return -1; | ||
103 | } | 215 | } |
104 | 216 | ||
105 | return 0; | 217 | return 0; |
@@ -168,10 +280,7 @@ static void process_event(union perf_event *event __unused, | |||
168 | { | 280 | { |
169 | struct perf_event_attr *attr = &evsel->attr; | 281 | struct perf_event_attr *attr = &evsel->attr; |
170 | 282 | ||
171 | if (output_fields[attr->type] == 0) | 283 | if (output[attr->type].fields == 0) |
172 | return; | ||
173 | |||
174 | if (perf_session__check_attr(session, attr) < 0) | ||
175 | return; | 284 | return; |
176 | 285 | ||
177 | print_sample_start(sample, thread, attr); | 286 | print_sample_start(sample, thread, attr); |
@@ -451,6 +560,7 @@ static int parse_output_fields(const struct option *opt __used, | |||
451 | { | 560 | { |
452 | char *tok; | 561 | char *tok; |
453 | int i, imax = sizeof(all_output_options) / sizeof(struct output_option); | 562 | int i, imax = sizeof(all_output_options) / sizeof(struct output_option); |
563 | int j; | ||
454 | int rc = 0; | 564 | int rc = 0; |
455 | char *str = strdup(arg); | 565 | char *str = strdup(arg); |
456 | int type = -1; | 566 | int type = -1; |
@@ -458,52 +568,99 @@ static int parse_output_fields(const struct option *opt __used, | |||
458 | if (!str) | 568 | if (!str) |
459 | return -ENOMEM; | 569 | return -ENOMEM; |
460 | 570 | ||
461 | tok = strtok(str, ":"); | 571 | /* first word can state for which event type the user is specifying |
462 | if (!tok) { | 572 | * the fields. If no type exists, the specified fields apply to all |
463 | fprintf(stderr, | 573 | * event types found in the file minus the invalid fields for a type. |
464 | "Invalid field string - not prepended with type."); | ||
465 | return -EINVAL; | ||
466 | } | ||
467 | |||
468 | /* first word should state which event type user | ||
469 | * is specifying the fields | ||
470 | */ | 574 | */ |
471 | if (!strcmp(tok, "hw")) | 575 | tok = strchr(str, ':'); |
472 | type = PERF_TYPE_HARDWARE; | 576 | if (tok) { |
473 | else if (!strcmp(tok, "sw")) | 577 | *tok = '\0'; |
474 | type = PERF_TYPE_SOFTWARE; | 578 | tok++; |
475 | else if (!strcmp(tok, "trace")) | 579 | if (!strcmp(str, "hw")) |
476 | type = PERF_TYPE_TRACEPOINT; | 580 | type = PERF_TYPE_HARDWARE; |
477 | else { | 581 | else if (!strcmp(str, "sw")) |
478 | fprintf(stderr, "Invalid event type in field string."); | 582 | type = PERF_TYPE_SOFTWARE; |
479 | return -EINVAL; | 583 | else if (!strcmp(str, "trace")) |
584 | type = PERF_TYPE_TRACEPOINT; | ||
585 | else if (!strcmp(str, "raw")) | ||
586 | type = PERF_TYPE_RAW; | ||
587 | else { | ||
588 | fprintf(stderr, "Invalid event type in field string.\n"); | ||
589 | return -EINVAL; | ||
590 | } | ||
591 | |||
592 | if (output[type].user_set) | ||
593 | pr_warning("Overriding previous field request for %s events.\n", | ||
594 | event_type(type)); | ||
595 | |||
596 | output[type].fields = 0; | ||
597 | output[type].user_set = true; | ||
598 | output[type].wildcard_set = false; | ||
599 | |||
600 | } else { | ||
601 | tok = str; | ||
602 | if (strlen(str) == 0) { | ||
603 | fprintf(stderr, | ||
604 | "Cannot set fields to 'none' for all event types.\n"); | ||
605 | rc = -EINVAL; | ||
606 | goto out; | ||
607 | } | ||
608 | |||
609 | if (output_set_by_user()) | ||
610 | pr_warning("Overriding previous field request for all events.\n"); | ||
611 | |||
612 | for (j = 0; j < PERF_TYPE_MAX; ++j) { | ||
613 | output[j].fields = 0; | ||
614 | output[j].user_set = true; | ||
615 | output[j].wildcard_set = true; | ||
616 | } | ||
480 | } | 617 | } |
481 | 618 | ||
482 | output_fields[type] = 0; | 619 | tok = strtok(tok, ","); |
483 | while (1) { | 620 | while (tok) { |
484 | tok = strtok(NULL, ","); | ||
485 | if (!tok) | ||
486 | break; | ||
487 | for (i = 0; i < imax; ++i) { | 621 | for (i = 0; i < imax; ++i) { |
488 | if (strcmp(tok, all_output_options[i].str) == 0) { | 622 | if (strcmp(tok, all_output_options[i].str) == 0) |
489 | output_fields[type] |= all_output_options[i].field; | ||
490 | break; | 623 | break; |
491 | } | ||
492 | } | 624 | } |
493 | if (i == imax) { | 625 | if (i == imax) { |
494 | fprintf(stderr, "Invalid field requested."); | 626 | fprintf(stderr, "Invalid field requested.\n"); |
495 | rc = -EINVAL; | 627 | rc = -EINVAL; |
496 | break; | 628 | goto out; |
497 | } | 629 | } |
498 | } | ||
499 | 630 | ||
500 | if (output_fields[type] == 0) { | 631 | if (type == -1) { |
501 | pr_debug("No fields requested for %s type. " | 632 | /* add user option to all events types for |
502 | "Events will not be displayed\n", event_type(type)); | 633 | * which it is valid |
634 | */ | ||
635 | for (j = 0; j < PERF_TYPE_MAX; ++j) { | ||
636 | if (output[j].invalid_fields & all_output_options[i].field) { | ||
637 | pr_warning("\'%s\' not valid for %s events. Ignoring.\n", | ||
638 | all_output_options[i].str, event_type(j)); | ||
639 | } else | ||
640 | output[j].fields |= all_output_options[i].field; | ||
641 | } | ||
642 | } else { | ||
643 | if (output[type].invalid_fields & all_output_options[i].field) { | ||
644 | fprintf(stderr, "\'%s\' not valid for %s events.\n", | ||
645 | all_output_options[i].str, event_type(type)); | ||
646 | |||
647 | rc = -EINVAL; | ||
648 | goto out; | ||
649 | } | ||
650 | output[type].fields |= all_output_options[i].field; | ||
651 | } | ||
652 | |||
653 | tok = strtok(NULL, ","); | ||
503 | } | 654 | } |
504 | 655 | ||
505 | output_set_by_user = true; | 656 | if (type >= 0) { |
657 | if (output[type].fields == 0) { | ||
658 | pr_debug("No fields requested for %s type. " | ||
659 | "Events will not be displayed.\n", event_type(type)); | ||
660 | } | ||
661 | } | ||
506 | 662 | ||
663 | out: | ||
507 | free(str); | 664 | free(str); |
508 | return rc; | 665 | return rc; |
509 | } | 666 | } |
@@ -829,7 +986,7 @@ static const struct option options[] = { | |||
829 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | 986 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", |
830 | "Look for files with symbols relative to this directory"), | 987 | "Look for files with symbols relative to this directory"), |
831 | OPT_CALLBACK('f', "fields", NULL, "str", | 988 | OPT_CALLBACK('f', "fields", NULL, "str", |
832 | "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace. Fields: comm,tid,pid,time,cpu,event,trace,sym", | 989 | "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,sym", |
833 | parse_output_fields), | 990 | parse_output_fields), |
834 | 991 | ||
835 | OPT_END() | 992 | OPT_END() |
@@ -1020,7 +1177,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __used) | |||
1020 | struct stat perf_stat; | 1177 | struct stat perf_stat; |
1021 | int input; | 1178 | int input; |
1022 | 1179 | ||
1023 | if (output_set_by_user) { | 1180 | if (output_set_by_user()) { |
1024 | fprintf(stderr, | 1181 | fprintf(stderr, |
1025 | "custom fields not supported for generated scripts"); | 1182 | "custom fields not supported for generated scripts"); |
1026 | return -1; | 1183 | return -1; |
@@ -1060,6 +1217,11 @@ int cmd_script(int argc, const char **argv, const char *prefix __used) | |||
1060 | pr_debug("perf script started with script %s\n\n", script_name); | 1217 | pr_debug("perf script started with script %s\n\n", script_name); |
1061 | } | 1218 | } |
1062 | 1219 | ||
1220 | |||
1221 | err = perf_session__check_output_opt(session); | ||
1222 | if (err < 0) | ||
1223 | goto out; | ||
1224 | |||
1063 | err = __cmd_script(session); | 1225 | err = __cmd_script(session); |
1064 | 1226 | ||
1065 | perf_session__delete(session); | 1227 | perf_session__delete(session); |