diff options
Diffstat (limited to 'tools/perf/builtin-timechart.c')
-rw-r--r-- | tools/perf/builtin-timechart.c | 152 |
1 files changed, 115 insertions, 37 deletions
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 9bcc38f0b706..aa26f4d66d10 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
@@ -32,6 +32,10 @@ | |||
32 | #include "util/session.h" | 32 | #include "util/session.h" |
33 | #include "util/svghelper.h" | 33 | #include "util/svghelper.h" |
34 | 34 | ||
35 | #define SUPPORT_OLD_POWER_EVENTS 1 | ||
36 | #define PWR_EVENT_EXIT -1 | ||
37 | |||
38 | |||
35 | static char const *input_name = "perf.data"; | 39 | static char const *input_name = "perf.data"; |
36 | static char const *output_name = "output.svg"; | 40 | static char const *output_name = "output.svg"; |
37 | 41 | ||
@@ -260,9 +264,6 @@ pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end) | |||
260 | c->start_time = start; | 264 | c->start_time = start; |
261 | if (p->start_time == 0 || p->start_time > start) | 265 | if (p->start_time == 0 || p->start_time > start) |
262 | p->start_time = start; | 266 | p->start_time = start; |
263 | |||
264 | if (cpu > numcpus) | ||
265 | numcpus = cpu; | ||
266 | } | 267 | } |
267 | 268 | ||
268 | #define MAX_CPUS 4096 | 269 | #define MAX_CPUS 4096 |
@@ -272,19 +273,25 @@ static int cpus_cstate_state[MAX_CPUS]; | |||
272 | static u64 cpus_pstate_start_times[MAX_CPUS]; | 273 | static u64 cpus_pstate_start_times[MAX_CPUS]; |
273 | static u64 cpus_pstate_state[MAX_CPUS]; | 274 | static u64 cpus_pstate_state[MAX_CPUS]; |
274 | 275 | ||
275 | static int process_comm_event(event_t *event, struct perf_session *session __used) | 276 | static int process_comm_event(union perf_event *event, |
277 | struct perf_sample *sample __used, | ||
278 | struct perf_session *session __used) | ||
276 | { | 279 | { |
277 | pid_set_comm(event->comm.tid, event->comm.comm); | 280 | pid_set_comm(event->comm.tid, event->comm.comm); |
278 | return 0; | 281 | return 0; |
279 | } | 282 | } |
280 | 283 | ||
281 | static int process_fork_event(event_t *event, struct perf_session *session __used) | 284 | static int process_fork_event(union perf_event *event, |
285 | struct perf_sample *sample __used, | ||
286 | struct perf_session *session __used) | ||
282 | { | 287 | { |
283 | pid_fork(event->fork.pid, event->fork.ppid, event->fork.time); | 288 | pid_fork(event->fork.pid, event->fork.ppid, event->fork.time); |
284 | return 0; | 289 | return 0; |
285 | } | 290 | } |
286 | 291 | ||
287 | static int process_exit_event(event_t *event, struct perf_session *session __used) | 292 | static int process_exit_event(union perf_event *event, |
293 | struct perf_sample *sample __used, | ||
294 | struct perf_session *session __used) | ||
288 | { | 295 | { |
289 | pid_exit(event->fork.pid, event->fork.time); | 296 | pid_exit(event->fork.pid, event->fork.time); |
290 | return 0; | 297 | return 0; |
@@ -298,12 +305,21 @@ struct trace_entry { | |||
298 | int lock_depth; | 305 | int lock_depth; |
299 | }; | 306 | }; |
300 | 307 | ||
301 | struct power_entry { | 308 | #ifdef SUPPORT_OLD_POWER_EVENTS |
309 | static int use_old_power_events; | ||
310 | struct power_entry_old { | ||
302 | struct trace_entry te; | 311 | struct trace_entry te; |
303 | u64 type; | 312 | u64 type; |
304 | u64 value; | 313 | u64 value; |
305 | u64 cpu_id; | 314 | u64 cpu_id; |
306 | }; | 315 | }; |
316 | #endif | ||
317 | |||
318 | struct power_processor_entry { | ||
319 | struct trace_entry te; | ||
320 | u32 state; | ||
321 | u32 cpu_id; | ||
322 | }; | ||
307 | 323 | ||
308 | #define TASK_COMM_LEN 16 | 324 | #define TASK_COMM_LEN 16 |
309 | struct wakeup_entry { | 325 | struct wakeup_entry { |
@@ -470,48 +486,79 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) | |||
470 | } | 486 | } |
471 | 487 | ||
472 | 488 | ||
473 | static int process_sample_event(event_t *event, struct perf_session *session) | 489 | static int process_sample_event(union perf_event *event __used, |
490 | struct perf_sample *sample, | ||
491 | struct perf_evsel *evsel __used, | ||
492 | struct perf_session *session) | ||
474 | { | 493 | { |
475 | struct sample_data data; | ||
476 | struct trace_entry *te; | 494 | struct trace_entry *te; |
477 | 495 | ||
478 | memset(&data, 0, sizeof(data)); | ||
479 | |||
480 | event__parse_sample(event, session->sample_type, &data); | ||
481 | |||
482 | if (session->sample_type & PERF_SAMPLE_TIME) { | 496 | if (session->sample_type & PERF_SAMPLE_TIME) { |
483 | if (!first_time || first_time > data.time) | 497 | if (!first_time || first_time > sample->time) |
484 | first_time = data.time; | 498 | first_time = sample->time; |
485 | if (last_time < data.time) | 499 | if (last_time < sample->time) |
486 | last_time = data.time; | 500 | last_time = sample->time; |
487 | } | 501 | } |
488 | 502 | ||
489 | te = (void *)data.raw_data; | 503 | te = (void *)sample->raw_data; |
490 | if (session->sample_type & PERF_SAMPLE_RAW && data.raw_size > 0) { | 504 | if (session->sample_type & PERF_SAMPLE_RAW && sample->raw_size > 0) { |
491 | char *event_str; | 505 | char *event_str; |
492 | struct power_entry *pe; | 506 | #ifdef SUPPORT_OLD_POWER_EVENTS |
493 | 507 | struct power_entry_old *peo; | |
494 | pe = (void *)te; | 508 | peo = (void *)te; |
495 | 509 | #endif | |
510 | /* | ||
511 | * FIXME: use evsel, its already mapped from id to perf_evsel, | ||
512 | * remove perf_header__find_event infrastructure bits. | ||
513 | * Mapping all these "power:cpu_idle" strings to the tracepoint | ||
514 | * ID and then just comparing against evsel->attr.config. | ||
515 | * | ||
516 | * e.g.: | ||
517 | * | ||
518 | * if (evsel->attr.config == power_cpu_idle_id) | ||
519 | */ | ||
496 | event_str = perf_header__find_event(te->type); | 520 | event_str = perf_header__find_event(te->type); |
497 | 521 | ||
498 | if (!event_str) | 522 | if (!event_str) |
499 | return 0; | 523 | return 0; |
500 | 524 | ||
501 | if (strcmp(event_str, "power:power_start") == 0) | 525 | if (sample->cpu > numcpus) |
502 | c_state_start(pe->cpu_id, data.time, pe->value); | 526 | numcpus = sample->cpu; |
527 | |||
528 | if (strcmp(event_str, "power:cpu_idle") == 0) { | ||
529 | struct power_processor_entry *ppe = (void *)te; | ||
530 | if (ppe->state == (u32)PWR_EVENT_EXIT) | ||
531 | c_state_end(ppe->cpu_id, sample->time); | ||
532 | else | ||
533 | c_state_start(ppe->cpu_id, sample->time, | ||
534 | ppe->state); | ||
535 | } | ||
536 | else if (strcmp(event_str, "power:cpu_frequency") == 0) { | ||
537 | struct power_processor_entry *ppe = (void *)te; | ||
538 | p_state_change(ppe->cpu_id, sample->time, ppe->state); | ||
539 | } | ||
540 | |||
541 | else if (strcmp(event_str, "sched:sched_wakeup") == 0) | ||
542 | sched_wakeup(sample->cpu, sample->time, sample->pid, te); | ||
503 | 543 | ||
504 | if (strcmp(event_str, "power:power_end") == 0) | 544 | else if (strcmp(event_str, "sched:sched_switch") == 0) |
505 | c_state_end(pe->cpu_id, data.time); | 545 | sched_switch(sample->cpu, sample->time, te); |
506 | 546 | ||
507 | if (strcmp(event_str, "power:power_frequency") == 0) | 547 | #ifdef SUPPORT_OLD_POWER_EVENTS |
508 | p_state_change(pe->cpu_id, data.time, pe->value); | 548 | if (use_old_power_events) { |
549 | if (strcmp(event_str, "power:power_start") == 0) | ||
550 | c_state_start(peo->cpu_id, sample->time, | ||
551 | peo->value); | ||
509 | 552 | ||
510 | if (strcmp(event_str, "sched:sched_wakeup") == 0) | 553 | else if (strcmp(event_str, "power:power_end") == 0) |
511 | sched_wakeup(data.cpu, data.time, data.pid, te); | 554 | c_state_end(sample->cpu, sample->time); |
512 | 555 | ||
513 | if (strcmp(event_str, "sched:sched_switch") == 0) | 556 | else if (strcmp(event_str, |
514 | sched_switch(data.cpu, data.time, te); | 557 | "power:power_frequency") == 0) |
558 | p_state_change(peo->cpu_id, sample->time, | ||
559 | peo->value); | ||
560 | } | ||
561 | #endif | ||
515 | } | 562 | } |
516 | return 0; | 563 | return 0; |
517 | } | 564 | } |
@@ -937,7 +984,8 @@ static struct perf_event_ops event_ops = { | |||
937 | 984 | ||
938 | static int __cmd_timechart(void) | 985 | static int __cmd_timechart(void) |
939 | { | 986 | { |
940 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false); | 987 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, |
988 | 0, false, &event_ops); | ||
941 | int ret = -EINVAL; | 989 | int ret = -EINVAL; |
942 | 990 | ||
943 | if (session == NULL) | 991 | if (session == NULL) |
@@ -968,7 +1016,8 @@ static const char * const timechart_usage[] = { | |||
968 | NULL | 1016 | NULL |
969 | }; | 1017 | }; |
970 | 1018 | ||
971 | static const char *record_args[] = { | 1019 | #ifdef SUPPORT_OLD_POWER_EVENTS |
1020 | static const char * const record_old_args[] = { | ||
972 | "record", | 1021 | "record", |
973 | "-a", | 1022 | "-a", |
974 | "-R", | 1023 | "-R", |
@@ -980,16 +1029,43 @@ static const char *record_args[] = { | |||
980 | "-e", "sched:sched_wakeup", | 1029 | "-e", "sched:sched_wakeup", |
981 | "-e", "sched:sched_switch", | 1030 | "-e", "sched:sched_switch", |
982 | }; | 1031 | }; |
1032 | #endif | ||
1033 | |||
1034 | static const char * const record_new_args[] = { | ||
1035 | "record", | ||
1036 | "-a", | ||
1037 | "-R", | ||
1038 | "-f", | ||
1039 | "-c", "1", | ||
1040 | "-e", "power:cpu_frequency", | ||
1041 | "-e", "power:cpu_idle", | ||
1042 | "-e", "sched:sched_wakeup", | ||
1043 | "-e", "sched:sched_switch", | ||
1044 | }; | ||
983 | 1045 | ||
984 | static int __cmd_record(int argc, const char **argv) | 1046 | static int __cmd_record(int argc, const char **argv) |
985 | { | 1047 | { |
986 | unsigned int rec_argc, i, j; | 1048 | unsigned int rec_argc, i, j; |
987 | const char **rec_argv; | 1049 | const char **rec_argv; |
1050 | const char * const *record_args = record_new_args; | ||
1051 | unsigned int record_elems = ARRAY_SIZE(record_new_args); | ||
1052 | |||
1053 | #ifdef SUPPORT_OLD_POWER_EVENTS | ||
1054 | if (!is_valid_tracepoint("power:cpu_idle") && | ||
1055 | is_valid_tracepoint("power:power_start")) { | ||
1056 | use_old_power_events = 1; | ||
1057 | record_args = record_old_args; | ||
1058 | record_elems = ARRAY_SIZE(record_old_args); | ||
1059 | } | ||
1060 | #endif | ||
988 | 1061 | ||
989 | rec_argc = ARRAY_SIZE(record_args) + argc - 1; | 1062 | rec_argc = record_elems + argc - 1; |
990 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | 1063 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
991 | 1064 | ||
992 | for (i = 0; i < ARRAY_SIZE(record_args); i++) | 1065 | if (rec_argv == NULL) |
1066 | return -ENOMEM; | ||
1067 | |||
1068 | for (i = 0; i < record_elems; i++) | ||
993 | rec_argv[i] = strdup(record_args[i]); | 1069 | rec_argv[i] = strdup(record_args[i]); |
994 | 1070 | ||
995 | for (j = 1; j < (unsigned int)argc; j++, i++) | 1071 | for (j = 1; j < (unsigned int)argc; j++, i++) |
@@ -1018,6 +1094,8 @@ static const struct option options[] = { | |||
1018 | OPT_CALLBACK('p', "process", NULL, "process", | 1094 | OPT_CALLBACK('p', "process", NULL, "process", |
1019 | "process selector. Pass a pid or process name.", | 1095 | "process selector. Pass a pid or process name.", |
1020 | parse_process), | 1096 | parse_process), |
1097 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | ||
1098 | "Look for files with symbols relative to this directory"), | ||
1021 | OPT_END() | 1099 | OPT_END() |
1022 | }; | 1100 | }; |
1023 | 1101 | ||