diff options
Diffstat (limited to 'tools/perf/builtin-script.c')
| -rw-r--r-- | tools/perf/builtin-script.c | 408 |
1 files changed, 335 insertions, 73 deletions
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index ac574ea23917..09024ec2ab2e 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
| @@ -10,10 +10,10 @@ | |||
| 10 | #include "util/symbol.h" | 10 | #include "util/symbol.h" |
| 11 | #include "util/thread.h" | 11 | #include "util/thread.h" |
| 12 | #include "util/trace-event.h" | 12 | #include "util/trace-event.h" |
| 13 | #include "util/parse-options.h" | ||
| 14 | #include "util/util.h" | 13 | #include "util/util.h" |
| 15 | #include "util/evlist.h" | 14 | #include "util/evlist.h" |
| 16 | #include "util/evsel.h" | 15 | #include "util/evsel.h" |
| 16 | #include <linux/bitmap.h> | ||
| 17 | 17 | ||
| 18 | static char const *script_name; | 18 | static char const *script_name; |
| 19 | static char const *generate_script_lang; | 19 | static char const *generate_script_lang; |
| @@ -22,6 +22,8 @@ static u64 last_timestamp; | |||
| 22 | static u64 nr_unordered; | 22 | static u64 nr_unordered; |
| 23 | extern const struct option record_options[]; | 23 | extern const struct option record_options[]; |
| 24 | static bool no_callchain; | 24 | static bool no_callchain; |
| 25 | static const char *cpu_list; | ||
| 26 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | ||
| 25 | 27 | ||
| 26 | enum perf_output_field { | 28 | enum perf_output_field { |
| 27 | PERF_OUTPUT_COMM = 1U << 0, | 29 | PERF_OUTPUT_COMM = 1U << 0, |
| @@ -31,7 +33,10 @@ enum perf_output_field { | |||
| 31 | PERF_OUTPUT_CPU = 1U << 4, | 33 | PERF_OUTPUT_CPU = 1U << 4, |
| 32 | PERF_OUTPUT_EVNAME = 1U << 5, | 34 | PERF_OUTPUT_EVNAME = 1U << 5, |
| 33 | PERF_OUTPUT_TRACE = 1U << 6, | 35 | PERF_OUTPUT_TRACE = 1U << 6, |
| 34 | PERF_OUTPUT_SYM = 1U << 7, | 36 | PERF_OUTPUT_IP = 1U << 7, |
| 37 | PERF_OUTPUT_SYM = 1U << 8, | ||
| 38 | PERF_OUTPUT_DSO = 1U << 9, | ||
| 39 | PERF_OUTPUT_ADDR = 1U << 10, | ||
| 35 | }; | 40 | }; |
| 36 | 41 | ||
| 37 | struct output_option { | 42 | struct output_option { |
| @@ -45,61 +50,197 @@ struct output_option { | |||
| 45 | {.str = "cpu", .field = PERF_OUTPUT_CPU}, | 50 | {.str = "cpu", .field = PERF_OUTPUT_CPU}, |
| 46 | {.str = "event", .field = PERF_OUTPUT_EVNAME}, | 51 | {.str = "event", .field = PERF_OUTPUT_EVNAME}, |
| 47 | {.str = "trace", .field = PERF_OUTPUT_TRACE}, | 52 | {.str = "trace", .field = PERF_OUTPUT_TRACE}, |
| 53 | {.str = "ip", .field = PERF_OUTPUT_IP}, | ||
| 48 | {.str = "sym", .field = PERF_OUTPUT_SYM}, | 54 | {.str = "sym", .field = PERF_OUTPUT_SYM}, |
| 55 | {.str = "dso", .field = PERF_OUTPUT_DSO}, | ||
| 56 | {.str = "addr", .field = PERF_OUTPUT_ADDR}, | ||
| 49 | }; | 57 | }; |
| 50 | 58 | ||
| 51 | /* default set to maintain compatibility with current format */ | 59 | /* default set to maintain compatibility with current format */ |
| 52 | static u64 output_fields[PERF_TYPE_MAX] = { | 60 | static struct { |
| 53 | [PERF_TYPE_HARDWARE] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \ | 61 | bool user_set; |
| 54 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \ | 62 | bool wildcard_set; |
| 55 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, | 63 | u64 fields; |
| 56 | 64 | u64 invalid_fields; | |
| 57 | [PERF_TYPE_SOFTWARE] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \ | 65 | } output[PERF_TYPE_MAX] = { |
| 58 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \ | 66 | |
| 59 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, | 67 | [PERF_TYPE_HARDWARE] = { |
| 60 | 68 | .user_set = false, | |
| 61 | [PERF_TYPE_TRACEPOINT] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \ | 69 | |
| 62 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \ | 70 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | |
| 63 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE, | 71 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
| 72 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | | ||
| 73 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO, | ||
| 74 | |||
| 75 | .invalid_fields = PERF_OUTPUT_TRACE, | ||
| 76 | }, | ||
| 77 | |||
| 78 | [PERF_TYPE_SOFTWARE] = { | ||
| 79 | .user_set = false, | ||
| 80 | |||
| 81 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | ||
| 82 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | ||
| 83 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | | ||
| 84 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO, | ||
| 85 | |||
| 86 | .invalid_fields = PERF_OUTPUT_TRACE, | ||
| 87 | }, | ||
| 88 | |||
| 89 | [PERF_TYPE_TRACEPOINT] = { | ||
| 90 | .user_set = false, | ||
| 91 | |||
| 92 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | ||
| 93 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | ||
| 94 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE, | ||
| 95 | }, | ||
| 96 | |||
| 97 | [PERF_TYPE_RAW] = { | ||
| 98 | .user_set = false, | ||
| 99 | |||
| 100 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | ||
| 101 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | ||
| 102 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | | ||
| 103 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO, | ||
| 104 | |||
| 105 | .invalid_fields = PERF_OUTPUT_TRACE, | ||
| 106 | }, | ||
| 64 | }; | 107 | }; |
| 65 | 108 | ||
| 66 | static bool output_set_by_user; | 109 | static bool output_set_by_user(void) |
| 110 | { | ||
| 111 | int j; | ||
| 112 | for (j = 0; j < PERF_TYPE_MAX; ++j) { | ||
| 113 | if (output[j].user_set) | ||
| 114 | return true; | ||
| 115 | } | ||
| 116 | return false; | ||
| 117 | } | ||
| 118 | |||
| 119 | static const char *output_field2str(enum perf_output_field field) | ||
| 120 | { | ||
| 121 | int i, imax = ARRAY_SIZE(all_output_options); | ||
| 122 | const char *str = ""; | ||
| 123 | |||
| 124 | for (i = 0; i < imax; ++i) { | ||
| 125 | if (all_output_options[i].field == field) { | ||
| 126 | str = all_output_options[i].str; | ||
| 127 | break; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | return str; | ||
| 131 | } | ||
| 132 | |||
| 133 | #define PRINT_FIELD(x) (output[attr->type].fields & PERF_OUTPUT_##x) | ||
| 134 | |||
| 135 | static int perf_event_attr__check_stype(struct perf_event_attr *attr, | ||
| 136 | u64 sample_type, const char *sample_msg, | ||
| 137 | enum perf_output_field field) | ||
| 138 | { | ||
| 139 | int type = attr->type; | ||
| 140 | const char *evname; | ||
| 141 | |||
| 142 | if (attr->sample_type & sample_type) | ||
| 143 | return 0; | ||
| 144 | |||
| 145 | if (output[type].user_set) { | ||
| 146 | evname = __event_name(attr->type, attr->config); | ||
| 147 | pr_err("Samples for '%s' event do not have %s attribute set. " | ||
| 148 | "Cannot print '%s' field.\n", | ||
| 149 | evname, sample_msg, output_field2str(field)); | ||
| 150 | return -1; | ||
| 151 | } | ||
| 152 | |||
| 153 | /* user did not ask for it explicitly so remove from the default list */ | ||
| 154 | output[type].fields &= ~field; | ||
| 155 | evname = __event_name(attr->type, attr->config); | ||
| 156 | pr_debug("Samples for '%s' event do not have %s attribute set. " | ||
| 157 | "Skipping '%s' field.\n", | ||
| 158 | evname, sample_msg, output_field2str(field)); | ||
| 67 | 159 | ||
| 68 | #define PRINT_FIELD(x) (output_fields[attr->type] & PERF_OUTPUT_##x) | 160 | return 0; |
| 161 | } | ||
| 69 | 162 | ||
| 70 | static int perf_session__check_attr(struct perf_session *session, | 163 | static int perf_evsel__check_attr(struct perf_evsel *evsel, |
| 71 | struct perf_event_attr *attr) | 164 | struct perf_session *session) |
| 72 | { | 165 | { |
| 166 | struct perf_event_attr *attr = &evsel->attr; | ||
| 167 | |||
| 73 | if (PRINT_FIELD(TRACE) && | 168 | if (PRINT_FIELD(TRACE) && |
| 74 | !perf_session__has_traces(session, "record -R")) | 169 | !perf_session__has_traces(session, "record -R")) |
| 75 | return -EINVAL; | 170 | return -EINVAL; |
| 76 | 171 | ||
| 77 | if (PRINT_FIELD(SYM)) { | 172 | if (PRINT_FIELD(IP)) { |
| 78 | if (!(session->sample_type & PERF_SAMPLE_IP)) { | 173 | if (perf_event_attr__check_stype(attr, PERF_SAMPLE_IP, "IP", |
| 79 | pr_err("Samples do not contain IP data.\n"); | 174 | PERF_OUTPUT_IP)) |
| 80 | return -EINVAL; | 175 | return -EINVAL; |
| 81 | } | 176 | |
| 82 | if (!no_callchain && | 177 | if (!no_callchain && |
| 83 | !(session->sample_type & PERF_SAMPLE_CALLCHAIN)) | 178 | !(attr->sample_type & PERF_SAMPLE_CALLCHAIN)) |
| 84 | symbol_conf.use_callchain = false; | 179 | symbol_conf.use_callchain = false; |
| 85 | } | 180 | } |
| 86 | 181 | ||
| 87 | if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) && | 182 | if (PRINT_FIELD(ADDR) && |
| 88 | !(session->sample_type & PERF_SAMPLE_TID)) { | 183 | perf_event_attr__check_stype(attr, PERF_SAMPLE_ADDR, "ADDR", |
| 89 | pr_err("Samples do not contain TID/PID data.\n"); | 184 | PERF_OUTPUT_ADDR)) |
| 185 | return -EINVAL; | ||
| 186 | |||
| 187 | if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { | ||
| 188 | pr_err("Display of symbols requested but neither sample IP nor " | ||
| 189 | "sample address\nis selected. Hence, no addresses to convert " | ||
| 190 | "to symbols.\n"); | ||
| 191 | return -EINVAL; | ||
| 192 | } | ||
| 193 | if (PRINT_FIELD(DSO) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { | ||
| 194 | pr_err("Display of DSO requested but neither sample IP nor " | ||
| 195 | "sample address\nis selected. Hence, no addresses to convert " | ||
| 196 | "to DSO.\n"); | ||
| 90 | return -EINVAL; | 197 | return -EINVAL; |
| 91 | } | 198 | } |
| 92 | 199 | ||
| 200 | if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) && | ||
| 201 | perf_event_attr__check_stype(attr, PERF_SAMPLE_TID, "TID", | ||
| 202 | PERF_OUTPUT_TID|PERF_OUTPUT_PID)) | ||
| 203 | return -EINVAL; | ||
| 204 | |||
| 93 | if (PRINT_FIELD(TIME) && | 205 | if (PRINT_FIELD(TIME) && |
| 94 | !(session->sample_type & PERF_SAMPLE_TIME)) { | 206 | perf_event_attr__check_stype(attr, PERF_SAMPLE_TIME, "TIME", |
| 95 | pr_err("Samples do not contain timestamps.\n"); | 207 | PERF_OUTPUT_TIME)) |
| 96 | return -EINVAL; | 208 | return -EINVAL; |
| 97 | } | ||
| 98 | 209 | ||
| 99 | if (PRINT_FIELD(CPU) && | 210 | if (PRINT_FIELD(CPU) && |
| 100 | !(session->sample_type & PERF_SAMPLE_CPU)) { | 211 | perf_event_attr__check_stype(attr, PERF_SAMPLE_CPU, "CPU", |
| 101 | pr_err("Samples do not contain cpu.\n"); | 212 | PERF_OUTPUT_CPU)) |
| 102 | return -EINVAL; | 213 | return -EINVAL; |
| 214 | |||
| 215 | return 0; | ||
| 216 | } | ||
| 217 | |||
| 218 | /* | ||
| 219 | * verify all user requested events exist and the samples | ||
| 220 | * have the expected data | ||
| 221 | */ | ||
| 222 | static int perf_session__check_output_opt(struct perf_session *session) | ||
| 223 | { | ||
| 224 | int j; | ||
| 225 | struct perf_evsel *evsel; | ||
| 226 | |||
| 227 | for (j = 0; j < PERF_TYPE_MAX; ++j) { | ||
| 228 | evsel = perf_session__find_first_evtype(session, j); | ||
| 229 | |||
| 230 | /* | ||
| 231 | * even if fields is set to 0 (ie., show nothing) event must | ||
| 232 | * exist if user explicitly includes it on the command line | ||
| 233 | */ | ||
| 234 | if (!evsel && output[j].user_set && !output[j].wildcard_set) { | ||
| 235 | pr_err("%s events do not exist. " | ||
| 236 | "Remove corresponding -f option to proceed.\n", | ||
| 237 | event_type(j)); | ||
| 238 | return -1; | ||
| 239 | } | ||
| 240 | |||
| 241 | if (evsel && output[j].fields && | ||
| 242 | perf_evsel__check_attr(evsel, session)) | ||
| 243 | return -1; | ||
| 103 | } | 244 | } |
| 104 | 245 | ||
| 105 | return 0; | 246 | return 0; |
| @@ -119,7 +260,7 @@ static void print_sample_start(struct perf_sample *sample, | |||
| 119 | if (PRINT_FIELD(COMM)) { | 260 | if (PRINT_FIELD(COMM)) { |
| 120 | if (latency_format) | 261 | if (latency_format) |
| 121 | printf("%8.8s ", thread->comm); | 262 | printf("%8.8s ", thread->comm); |
| 122 | else if (PRINT_FIELD(SYM) && symbol_conf.use_callchain) | 263 | else if (PRINT_FIELD(IP) && symbol_conf.use_callchain) |
| 123 | printf("%s ", thread->comm); | 264 | printf("%s ", thread->comm); |
| 124 | else | 265 | else |
| 125 | printf("%16s ", thread->comm); | 266 | printf("%16s ", thread->comm); |
| @@ -160,6 +301,63 @@ static void print_sample_start(struct perf_sample *sample, | |||
| 160 | } | 301 | } |
| 161 | } | 302 | } |
| 162 | 303 | ||
| 304 | static bool sample_addr_correlates_sym(struct perf_event_attr *attr) | ||
| 305 | { | ||
| 306 | if ((attr->type == PERF_TYPE_SOFTWARE) && | ||
| 307 | ((attr->config == PERF_COUNT_SW_PAGE_FAULTS) || | ||
| 308 | (attr->config == PERF_COUNT_SW_PAGE_FAULTS_MIN) || | ||
| 309 | (attr->config == PERF_COUNT_SW_PAGE_FAULTS_MAJ))) | ||
| 310 | return true; | ||
| 311 | |||
| 312 | return false; | ||
| 313 | } | ||
| 314 | |||
| 315 | static void print_sample_addr(union perf_event *event, | ||
| 316 | struct perf_sample *sample, | ||
| 317 | struct perf_session *session, | ||
| 318 | struct thread *thread, | ||
| 319 | struct perf_event_attr *attr) | ||
| 320 | { | ||
| 321 | struct addr_location al; | ||
| 322 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
| 323 | const char *symname, *dsoname; | ||
| 324 | |||
| 325 | printf("%16" PRIx64, sample->addr); | ||
| 326 | |||
| 327 | if (!sample_addr_correlates_sym(attr)) | ||
| 328 | return; | ||
| 329 | |||
| 330 | thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, | ||
| 331 | event->ip.pid, sample->addr, &al); | ||
| 332 | if (!al.map) | ||
| 333 | thread__find_addr_map(thread, session, cpumode, MAP__VARIABLE, | ||
| 334 | event->ip.pid, sample->addr, &al); | ||
| 335 | |||
| 336 | al.cpu = sample->cpu; | ||
| 337 | al.sym = NULL; | ||
| 338 | |||
| 339 | if (al.map) | ||
| 340 | al.sym = map__find_symbol(al.map, al.addr, NULL); | ||
| 341 | |||
| 342 | if (PRINT_FIELD(SYM)) { | ||
| 343 | if (al.sym && al.sym->name) | ||
| 344 | symname = al.sym->name; | ||
| 345 | else | ||
| 346 | symname = ""; | ||
| 347 | |||
| 348 | printf(" %16s", symname); | ||
| 349 | } | ||
| 350 | |||
| 351 | if (PRINT_FIELD(DSO)) { | ||
| 352 | if (al.map && al.map->dso && al.map->dso->name) | ||
| 353 | dsoname = al.map->dso->name; | ||
| 354 | else | ||
| 355 | dsoname = ""; | ||
| 356 | |||
| 357 | printf(" (%s)", dsoname); | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 163 | static void process_event(union perf_event *event __unused, | 361 | static void process_event(union perf_event *event __unused, |
| 164 | struct perf_sample *sample, | 362 | struct perf_sample *sample, |
| 165 | struct perf_evsel *evsel, | 363 | struct perf_evsel *evsel, |
| @@ -168,10 +366,7 @@ static void process_event(union perf_event *event __unused, | |||
| 168 | { | 366 | { |
| 169 | struct perf_event_attr *attr = &evsel->attr; | 367 | struct perf_event_attr *attr = &evsel->attr; |
| 170 | 368 | ||
| 171 | if (output_fields[attr->type] == 0) | 369 | if (output[attr->type].fields == 0) |
| 172 | return; | ||
| 173 | |||
| 174 | if (perf_session__check_attr(session, attr) < 0) | ||
| 175 | return; | 370 | return; |
| 176 | 371 | ||
| 177 | print_sample_start(sample, thread, attr); | 372 | print_sample_start(sample, thread, attr); |
| @@ -180,12 +375,16 @@ static void process_event(union perf_event *event __unused, | |||
| 180 | print_trace_event(sample->cpu, sample->raw_data, | 375 | print_trace_event(sample->cpu, sample->raw_data, |
| 181 | sample->raw_size); | 376 | sample->raw_size); |
| 182 | 377 | ||
| 183 | if (PRINT_FIELD(SYM)) { | 378 | if (PRINT_FIELD(ADDR)) |
| 379 | print_sample_addr(event, sample, session, thread, attr); | ||
| 380 | |||
| 381 | if (PRINT_FIELD(IP)) { | ||
| 184 | if (!symbol_conf.use_callchain) | 382 | if (!symbol_conf.use_callchain) |
| 185 | printf(" "); | 383 | printf(" "); |
| 186 | else | 384 | else |
| 187 | printf("\n"); | 385 | printf("\n"); |
| 188 | perf_session__print_symbols(event, sample, session); | 386 | perf_session__print_ip(event, sample, session, |
| 387 | PRINT_FIELD(SYM), PRINT_FIELD(DSO)); | ||
| 189 | } | 388 | } |
| 190 | 389 | ||
| 191 | printf("\n"); | 390 | printf("\n"); |
| @@ -257,6 +456,10 @@ static int process_sample_event(union perf_event *event, | |||
| 257 | last_timestamp = sample->time; | 456 | last_timestamp = sample->time; |
| 258 | return 0; | 457 | return 0; |
| 259 | } | 458 | } |
| 459 | |||
| 460 | if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) | ||
| 461 | return 0; | ||
| 462 | |||
| 260 | scripting_ops->process_event(event, sample, evsel, session, thread); | 463 | scripting_ops->process_event(event, sample, evsel, session, thread); |
| 261 | 464 | ||
| 262 | session->hists.stats.total_period += sample->period; | 465 | session->hists.stats.total_period += sample->period; |
| @@ -451,6 +654,7 @@ static int parse_output_fields(const struct option *opt __used, | |||
| 451 | { | 654 | { |
| 452 | char *tok; | 655 | char *tok; |
| 453 | int i, imax = sizeof(all_output_options) / sizeof(struct output_option); | 656 | int i, imax = sizeof(all_output_options) / sizeof(struct output_option); |
| 657 | int j; | ||
| 454 | int rc = 0; | 658 | int rc = 0; |
| 455 | char *str = strdup(arg); | 659 | char *str = strdup(arg); |
| 456 | int type = -1; | 660 | int type = -1; |
| @@ -458,52 +662,99 @@ static int parse_output_fields(const struct option *opt __used, | |||
| 458 | if (!str) | 662 | if (!str) |
| 459 | return -ENOMEM; | 663 | return -ENOMEM; |
| 460 | 664 | ||
| 461 | tok = strtok(str, ":"); | 665 | /* first word can state for which event type the user is specifying |
| 462 | if (!tok) { | 666 | * the fields. If no type exists, the specified fields apply to all |
| 463 | fprintf(stderr, | 667 | * 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 | */ | 668 | */ |
| 471 | if (!strcmp(tok, "hw")) | 669 | tok = strchr(str, ':'); |
| 472 | type = PERF_TYPE_HARDWARE; | 670 | if (tok) { |
| 473 | else if (!strcmp(tok, "sw")) | 671 | *tok = '\0'; |
| 474 | type = PERF_TYPE_SOFTWARE; | 672 | tok++; |
| 475 | else if (!strcmp(tok, "trace")) | 673 | if (!strcmp(str, "hw")) |
| 476 | type = PERF_TYPE_TRACEPOINT; | 674 | type = PERF_TYPE_HARDWARE; |
| 477 | else { | 675 | else if (!strcmp(str, "sw")) |
| 478 | fprintf(stderr, "Invalid event type in field string."); | 676 | type = PERF_TYPE_SOFTWARE; |
| 479 | return -EINVAL; | 677 | else if (!strcmp(str, "trace")) |
| 678 | type = PERF_TYPE_TRACEPOINT; | ||
| 679 | else if (!strcmp(str, "raw")) | ||
| 680 | type = PERF_TYPE_RAW; | ||
| 681 | else { | ||
| 682 | fprintf(stderr, "Invalid event type in field string.\n"); | ||
| 683 | return -EINVAL; | ||
| 684 | } | ||
| 685 | |||
| 686 | if (output[type].user_set) | ||
| 687 | pr_warning("Overriding previous field request for %s events.\n", | ||
| 688 | event_type(type)); | ||
| 689 | |||
| 690 | output[type].fields = 0; | ||
| 691 | output[type].user_set = true; | ||
| 692 | output[type].wildcard_set = false; | ||
| 693 | |||
| 694 | } else { | ||
| 695 | tok = str; | ||
| 696 | if (strlen(str) == 0) { | ||
| 697 | fprintf(stderr, | ||
| 698 | "Cannot set fields to 'none' for all event types.\n"); | ||
| 699 | rc = -EINVAL; | ||
| 700 | goto out; | ||
| 701 | } | ||
| 702 | |||
| 703 | if (output_set_by_user()) | ||
| 704 | pr_warning("Overriding previous field request for all events.\n"); | ||
| 705 | |||
| 706 | for (j = 0; j < PERF_TYPE_MAX; ++j) { | ||
| 707 | output[j].fields = 0; | ||
| 708 | output[j].user_set = true; | ||
| 709 | output[j].wildcard_set = true; | ||
| 710 | } | ||
| 480 | } | 711 | } |
| 481 | 712 | ||
| 482 | output_fields[type] = 0; | 713 | tok = strtok(tok, ","); |
| 483 | while (1) { | 714 | while (tok) { |
| 484 | tok = strtok(NULL, ","); | ||
| 485 | if (!tok) | ||
| 486 | break; | ||
| 487 | for (i = 0; i < imax; ++i) { | 715 | for (i = 0; i < imax; ++i) { |
| 488 | if (strcmp(tok, all_output_options[i].str) == 0) { | 716 | if (strcmp(tok, all_output_options[i].str) == 0) |
| 489 | output_fields[type] |= all_output_options[i].field; | ||
| 490 | break; | 717 | break; |
| 491 | } | ||
| 492 | } | 718 | } |
| 493 | if (i == imax) { | 719 | if (i == imax) { |
| 494 | fprintf(stderr, "Invalid field requested."); | 720 | fprintf(stderr, "Invalid field requested.\n"); |
| 495 | rc = -EINVAL; | 721 | rc = -EINVAL; |
| 496 | break; | 722 | goto out; |
| 497 | } | 723 | } |
| 498 | } | ||
| 499 | 724 | ||
| 500 | if (output_fields[type] == 0) { | 725 | if (type == -1) { |
| 501 | pr_debug("No fields requested for %s type. " | 726 | /* add user option to all events types for |
| 502 | "Events will not be displayed\n", event_type(type)); | 727 | * which it is valid |
| 728 | */ | ||
| 729 | for (j = 0; j < PERF_TYPE_MAX; ++j) { | ||
| 730 | if (output[j].invalid_fields & all_output_options[i].field) { | ||
| 731 | pr_warning("\'%s\' not valid for %s events. Ignoring.\n", | ||
| 732 | all_output_options[i].str, event_type(j)); | ||
| 733 | } else | ||
| 734 | output[j].fields |= all_output_options[i].field; | ||
| 735 | } | ||
| 736 | } else { | ||
| 737 | if (output[type].invalid_fields & all_output_options[i].field) { | ||
| 738 | fprintf(stderr, "\'%s\' not valid for %s events.\n", | ||
| 739 | all_output_options[i].str, event_type(type)); | ||
| 740 | |||
| 741 | rc = -EINVAL; | ||
| 742 | goto out; | ||
| 743 | } | ||
| 744 | output[type].fields |= all_output_options[i].field; | ||
| 745 | } | ||
| 746 | |||
| 747 | tok = strtok(NULL, ","); | ||
| 503 | } | 748 | } |
| 504 | 749 | ||
| 505 | output_set_by_user = true; | 750 | if (type >= 0) { |
| 751 | if (output[type].fields == 0) { | ||
| 752 | pr_debug("No fields requested for %s type. " | ||
| 753 | "Events will not be displayed.\n", event_type(type)); | ||
| 754 | } | ||
| 755 | } | ||
| 506 | 756 | ||
| 757 | out: | ||
| 507 | free(str); | 758 | free(str); |
| 508 | return rc; | 759 | return rc; |
| 509 | } | 760 | } |
| @@ -829,8 +1080,9 @@ static const struct option options[] = { | |||
| 829 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | 1080 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", |
| 830 | "Look for files with symbols relative to this directory"), | 1081 | "Look for files with symbols relative to this directory"), |
| 831 | OPT_CALLBACK('f', "fields", NULL, "str", | 1082 | 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", | 1083 | "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr", |
| 833 | parse_output_fields), | 1084 | parse_output_fields), |
| 1085 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | ||
| 834 | 1086 | ||
| 835 | OPT_END() | 1087 | OPT_END() |
| 836 | }; | 1088 | }; |
| @@ -1011,6 +1263,11 @@ int cmd_script(int argc, const char **argv, const char *prefix __used) | |||
| 1011 | if (session == NULL) | 1263 | if (session == NULL) |
| 1012 | return -ENOMEM; | 1264 | return -ENOMEM; |
| 1013 | 1265 | ||
| 1266 | if (cpu_list) { | ||
| 1267 | if (perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap)) | ||
| 1268 | return -1; | ||
| 1269 | } | ||
| 1270 | |||
| 1014 | if (!no_callchain) | 1271 | if (!no_callchain) |
| 1015 | symbol_conf.use_callchain = true; | 1272 | symbol_conf.use_callchain = true; |
| 1016 | else | 1273 | else |
| @@ -1020,7 +1277,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __used) | |||
| 1020 | struct stat perf_stat; | 1277 | struct stat perf_stat; |
| 1021 | int input; | 1278 | int input; |
| 1022 | 1279 | ||
| 1023 | if (output_set_by_user) { | 1280 | if (output_set_by_user()) { |
| 1024 | fprintf(stderr, | 1281 | fprintf(stderr, |
| 1025 | "custom fields not supported for generated scripts"); | 1282 | "custom fields not supported for generated scripts"); |
| 1026 | return -1; | 1283 | return -1; |
| @@ -1060,6 +1317,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); | 1317 | pr_debug("perf script started with script %s\n\n", script_name); |
| 1061 | } | 1318 | } |
| 1062 | 1319 | ||
| 1320 | |||
| 1321 | err = perf_session__check_output_opt(session); | ||
| 1322 | if (err < 0) | ||
| 1323 | goto out; | ||
| 1324 | |||
| 1063 | err = __cmd_script(session); | 1325 | err = __cmd_script(session); |
| 1064 | 1326 | ||
| 1065 | perf_session__delete(session); | 1327 | perf_session__delete(session); |
