diff options
author | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
commit | ada47b5fe13d89735805b566185f4885f5a3f750 (patch) | |
tree | 644b88f8a71896307d71438e9b3af49126ffb22b /tools/perf/builtin-timechart.c | |
parent | 43e98717ad40a4ae64545b5ba047c7b86aa44f4f (diff) | |
parent | 3280f21d43ee541f97f8cda5792150d2dbec20d5 (diff) |
Merge branch 'wip-2.6.34' into old-private-masterarchived-private-master
Diffstat (limited to 'tools/perf/builtin-timechart.c')
-rw-r--r-- | tools/perf/builtin-timechart.c | 356 |
1 files changed, 157 insertions, 199 deletions
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index e8a510d935e5..0d4d8ff7914b 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
@@ -29,16 +29,13 @@ | |||
29 | #include "util/header.h" | 29 | #include "util/header.h" |
30 | #include "util/parse-options.h" | 30 | #include "util/parse-options.h" |
31 | #include "util/parse-events.h" | 31 | #include "util/parse-events.h" |
32 | #include "util/event.h" | ||
33 | #include "util/session.h" | ||
32 | #include "util/svghelper.h" | 34 | #include "util/svghelper.h" |
33 | 35 | ||
34 | static char const *input_name = "perf.data"; | 36 | static char const *input_name = "perf.data"; |
35 | static char const *output_name = "output.svg"; | 37 | static char const *output_name = "output.svg"; |
36 | 38 | ||
37 | |||
38 | static unsigned long page_size; | ||
39 | static unsigned long mmap_window = 32; | ||
40 | static u64 sample_type; | ||
41 | |||
42 | static unsigned int numcpus; | 39 | static unsigned int numcpus; |
43 | static u64 min_freq; /* Lowest CPU frequency seen */ | 40 | static u64 min_freq; /* Lowest CPU frequency seen */ |
44 | static u64 max_freq; /* Highest CPU frequency seen */ | 41 | static u64 max_freq; /* Highest CPU frequency seen */ |
@@ -49,8 +46,6 @@ static u64 first_time, last_time; | |||
49 | static int power_only; | 46 | static int power_only; |
50 | 47 | ||
51 | 48 | ||
52 | static struct perf_header *header; | ||
53 | |||
54 | struct per_pid; | 49 | struct per_pid; |
55 | struct per_pidcomm; | 50 | struct per_pidcomm; |
56 | 51 | ||
@@ -153,6 +148,17 @@ static struct wake_event *wake_events; | |||
153 | 148 | ||
154 | struct sample_wrapper *all_samples; | 149 | struct sample_wrapper *all_samples; |
155 | 150 | ||
151 | |||
152 | struct process_filter; | ||
153 | struct process_filter { | ||
154 | char *name; | ||
155 | int pid; | ||
156 | struct process_filter *next; | ||
157 | }; | ||
158 | |||
159 | static struct process_filter *process_filter; | ||
160 | |||
161 | |||
156 | static struct per_pid *find_create_pid(int pid) | 162 | static struct per_pid *find_create_pid(int pid) |
157 | { | 163 | { |
158 | struct per_pid *cursor = all_data; | 164 | struct per_pid *cursor = all_data; |
@@ -272,33 +278,30 @@ static int cpus_cstate_state[MAX_CPUS]; | |||
272 | static u64 cpus_pstate_start_times[MAX_CPUS]; | 278 | static u64 cpus_pstate_start_times[MAX_CPUS]; |
273 | static u64 cpus_pstate_state[MAX_CPUS]; | 279 | static u64 cpus_pstate_state[MAX_CPUS]; |
274 | 280 | ||
275 | static int | 281 | static int process_comm_event(event_t *event, struct perf_session *session __used) |
276 | process_comm_event(event_t *event) | ||
277 | { | 282 | { |
278 | pid_set_comm(event->comm.pid, event->comm.comm); | 283 | pid_set_comm(event->comm.tid, event->comm.comm); |
279 | return 0; | 284 | return 0; |
280 | } | 285 | } |
281 | static int | 286 | |
282 | process_fork_event(event_t *event) | 287 | static int process_fork_event(event_t *event, struct perf_session *session __used) |
283 | { | 288 | { |
284 | pid_fork(event->fork.pid, event->fork.ppid, event->fork.time); | 289 | pid_fork(event->fork.pid, event->fork.ppid, event->fork.time); |
285 | return 0; | 290 | return 0; |
286 | } | 291 | } |
287 | 292 | ||
288 | static int | 293 | static int process_exit_event(event_t *event, struct perf_session *session __used) |
289 | process_exit_event(event_t *event) | ||
290 | { | 294 | { |
291 | pid_exit(event->fork.pid, event->fork.time); | 295 | pid_exit(event->fork.pid, event->fork.time); |
292 | return 0; | 296 | return 0; |
293 | } | 297 | } |
294 | 298 | ||
295 | struct trace_entry { | 299 | struct trace_entry { |
296 | u32 size; | ||
297 | unsigned short type; | 300 | unsigned short type; |
298 | unsigned char flags; | 301 | unsigned char flags; |
299 | unsigned char preempt_count; | 302 | unsigned char preempt_count; |
300 | int pid; | 303 | int pid; |
301 | int tgid; | 304 | int lock_depth; |
302 | }; | 305 | }; |
303 | 306 | ||
304 | struct power_entry { | 307 | struct power_entry { |
@@ -472,46 +475,24 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) | |||
472 | } | 475 | } |
473 | 476 | ||
474 | 477 | ||
475 | static int | 478 | static int process_sample_event(event_t *event, struct perf_session *session) |
476 | process_sample_event(event_t *event) | ||
477 | { | 479 | { |
478 | int cursor = 0; | 480 | struct sample_data data; |
479 | u64 addr = 0; | ||
480 | u64 stamp = 0; | ||
481 | u32 cpu = 0; | ||
482 | u32 pid = 0; | ||
483 | struct trace_entry *te; | 481 | struct trace_entry *te; |
484 | 482 | ||
485 | if (sample_type & PERF_SAMPLE_IP) | 483 | memset(&data, 0, sizeof(data)); |
486 | cursor++; | ||
487 | |||
488 | if (sample_type & PERF_SAMPLE_TID) { | ||
489 | pid = event->sample.array[cursor]>>32; | ||
490 | cursor++; | ||
491 | } | ||
492 | if (sample_type & PERF_SAMPLE_TIME) { | ||
493 | stamp = event->sample.array[cursor++]; | ||
494 | 484 | ||
495 | if (!first_time || first_time > stamp) | 485 | event__parse_sample(event, session->sample_type, &data); |
496 | first_time = stamp; | ||
497 | if (last_time < stamp) | ||
498 | last_time = stamp; | ||
499 | 486 | ||
487 | if (session->sample_type & PERF_SAMPLE_TIME) { | ||
488 | if (!first_time || first_time > data.time) | ||
489 | first_time = data.time; | ||
490 | if (last_time < data.time) | ||
491 | last_time = data.time; | ||
500 | } | 492 | } |
501 | if (sample_type & PERF_SAMPLE_ADDR) | ||
502 | addr = event->sample.array[cursor++]; | ||
503 | if (sample_type & PERF_SAMPLE_ID) | ||
504 | cursor++; | ||
505 | if (sample_type & PERF_SAMPLE_STREAM_ID) | ||
506 | cursor++; | ||
507 | if (sample_type & PERF_SAMPLE_CPU) | ||
508 | cpu = event->sample.array[cursor++] & 0xFFFFFFFF; | ||
509 | if (sample_type & PERF_SAMPLE_PERIOD) | ||
510 | cursor++; | ||
511 | |||
512 | te = (void *)&event->sample.array[cursor]; | ||
513 | 493 | ||
514 | if (sample_type & PERF_SAMPLE_RAW && te->size > 0) { | 494 | te = (void *)data.raw_data; |
495 | if (session->sample_type & PERF_SAMPLE_RAW && data.raw_size > 0) { | ||
515 | char *event_str; | 496 | char *event_str; |
516 | struct power_entry *pe; | 497 | struct power_entry *pe; |
517 | 498 | ||
@@ -523,19 +504,19 @@ process_sample_event(event_t *event) | |||
523 | return 0; | 504 | return 0; |
524 | 505 | ||
525 | if (strcmp(event_str, "power:power_start") == 0) | 506 | if (strcmp(event_str, "power:power_start") == 0) |
526 | c_state_start(cpu, stamp, pe->value); | 507 | c_state_start(data.cpu, data.time, pe->value); |
527 | 508 | ||
528 | if (strcmp(event_str, "power:power_end") == 0) | 509 | if (strcmp(event_str, "power:power_end") == 0) |
529 | c_state_end(cpu, stamp); | 510 | c_state_end(data.cpu, data.time); |
530 | 511 | ||
531 | if (strcmp(event_str, "power:power_frequency") == 0) | 512 | if (strcmp(event_str, "power:power_frequency") == 0) |
532 | p_state_change(cpu, stamp, pe->value); | 513 | p_state_change(data.cpu, data.time, pe->value); |
533 | 514 | ||
534 | if (strcmp(event_str, "sched:sched_wakeup") == 0) | 515 | if (strcmp(event_str, "sched:sched_wakeup") == 0) |
535 | sched_wakeup(cpu, stamp, pid, te); | 516 | sched_wakeup(data.cpu, data.time, data.pid, te); |
536 | 517 | ||
537 | if (strcmp(event_str, "sched:sched_switch") == 0) | 518 | if (strcmp(event_str, "sched:sched_switch") == 0) |
538 | sched_switch(cpu, stamp, te); | 519 | sched_switch(data.cpu, data.time, te); |
539 | } | 520 | } |
540 | return 0; | 521 | return 0; |
541 | } | 522 | } |
@@ -588,16 +569,16 @@ static void end_sample_processing(void) | |||
588 | } | 569 | } |
589 | } | 570 | } |
590 | 571 | ||
591 | static u64 sample_time(event_t *event) | 572 | static u64 sample_time(event_t *event, const struct perf_session *session) |
592 | { | 573 | { |
593 | int cursor; | 574 | int cursor; |
594 | 575 | ||
595 | cursor = 0; | 576 | cursor = 0; |
596 | if (sample_type & PERF_SAMPLE_IP) | 577 | if (session->sample_type & PERF_SAMPLE_IP) |
597 | cursor++; | 578 | cursor++; |
598 | if (sample_type & PERF_SAMPLE_TID) | 579 | if (session->sample_type & PERF_SAMPLE_TID) |
599 | cursor++; | 580 | cursor++; |
600 | if (sample_type & PERF_SAMPLE_TIME) | 581 | if (session->sample_type & PERF_SAMPLE_TIME) |
601 | return event->sample.array[cursor]; | 582 | return event->sample.array[cursor]; |
602 | return 0; | 583 | return 0; |
603 | } | 584 | } |
@@ -607,8 +588,7 @@ static u64 sample_time(event_t *event) | |||
607 | * We first queue all events, sorted backwards by insertion. | 588 | * We first queue all events, sorted backwards by insertion. |
608 | * The order will get flipped later. | 589 | * The order will get flipped later. |
609 | */ | 590 | */ |
610 | static int | 591 | static int queue_sample_event(event_t *event, struct perf_session *session) |
611 | queue_sample_event(event_t *event) | ||
612 | { | 592 | { |
613 | struct sample_wrapper *copy, *prev; | 593 | struct sample_wrapper *copy, *prev; |
614 | int size; | 594 | int size; |
@@ -622,7 +602,7 @@ queue_sample_event(event_t *event) | |||
622 | memset(copy, 0, size); | 602 | memset(copy, 0, size); |
623 | 603 | ||
624 | copy->next = NULL; | 604 | copy->next = NULL; |
625 | copy->timestamp = sample_time(event); | 605 | copy->timestamp = sample_time(event, session); |
626 | 606 | ||
627 | memcpy(©->data, event, event->sample.header.size); | 607 | memcpy(©->data, event, event->sample.header.size); |
628 | 608 | ||
@@ -763,11 +743,11 @@ static void draw_wakeups(void) | |||
763 | c = p->all; | 743 | c = p->all; |
764 | while (c) { | 744 | while (c) { |
765 | if (c->Y && c->start_time <= we->time && c->end_time >= we->time) { | 745 | if (c->Y && c->start_time <= we->time && c->end_time >= we->time) { |
766 | if (p->pid == we->waker) { | 746 | if (p->pid == we->waker && !from) { |
767 | from = c->Y; | 747 | from = c->Y; |
768 | task_from = strdup(c->comm); | 748 | task_from = strdup(c->comm); |
769 | } | 749 | } |
770 | if (p->pid == we->wakee) { | 750 | if (p->pid == we->wakee && !to) { |
771 | to = c->Y; | 751 | to = c->Y; |
772 | task_to = strdup(c->comm); | 752 | task_to = strdup(c->comm); |
773 | } | 753 | } |
@@ -882,12 +862,89 @@ static void draw_process_bars(void) | |||
882 | } | 862 | } |
883 | } | 863 | } |
884 | 864 | ||
865 | static void add_process_filter(const char *string) | ||
866 | { | ||
867 | struct process_filter *filt; | ||
868 | int pid; | ||
869 | |||
870 | pid = strtoull(string, NULL, 10); | ||
871 | filt = malloc(sizeof(struct process_filter)); | ||
872 | if (!filt) | ||
873 | return; | ||
874 | |||
875 | filt->name = strdup(string); | ||
876 | filt->pid = pid; | ||
877 | filt->next = process_filter; | ||
878 | |||
879 | process_filter = filt; | ||
880 | } | ||
881 | |||
882 | static int passes_filter(struct per_pid *p, struct per_pidcomm *c) | ||
883 | { | ||
884 | struct process_filter *filt; | ||
885 | if (!process_filter) | ||
886 | return 1; | ||
887 | |||
888 | filt = process_filter; | ||
889 | while (filt) { | ||
890 | if (filt->pid && p->pid == filt->pid) | ||
891 | return 1; | ||
892 | if (strcmp(filt->name, c->comm) == 0) | ||
893 | return 1; | ||
894 | filt = filt->next; | ||
895 | } | ||
896 | return 0; | ||
897 | } | ||
898 | |||
899 | static int determine_display_tasks_filtered(void) | ||
900 | { | ||
901 | struct per_pid *p; | ||
902 | struct per_pidcomm *c; | ||
903 | int count = 0; | ||
904 | |||
905 | p = all_data; | ||
906 | while (p) { | ||
907 | p->display = 0; | ||
908 | if (p->start_time == 1) | ||
909 | p->start_time = first_time; | ||
910 | |||
911 | /* no exit marker, task kept running to the end */ | ||
912 | if (p->end_time == 0) | ||
913 | p->end_time = last_time; | ||
914 | |||
915 | c = p->all; | ||
916 | |||
917 | while (c) { | ||
918 | c->display = 0; | ||
919 | |||
920 | if (c->start_time == 1) | ||
921 | c->start_time = first_time; | ||
922 | |||
923 | if (passes_filter(p, c)) { | ||
924 | c->display = 1; | ||
925 | p->display = 1; | ||
926 | count++; | ||
927 | } | ||
928 | |||
929 | if (c->end_time == 0) | ||
930 | c->end_time = last_time; | ||
931 | |||
932 | c = c->next; | ||
933 | } | ||
934 | p = p->next; | ||
935 | } | ||
936 | return count; | ||
937 | } | ||
938 | |||
885 | static int determine_display_tasks(u64 threshold) | 939 | static int determine_display_tasks(u64 threshold) |
886 | { | 940 | { |
887 | struct per_pid *p; | 941 | struct per_pid *p; |
888 | struct per_pidcomm *c; | 942 | struct per_pidcomm *c; |
889 | int count = 0; | 943 | int count = 0; |
890 | 944 | ||
945 | if (process_filter) | ||
946 | return determine_display_tasks_filtered(); | ||
947 | |||
891 | p = all_data; | 948 | p = all_data; |
892 | while (p) { | 949 | while (p) { |
893 | p->display = 0; | 950 | p->display = 0; |
@@ -957,37 +1014,7 @@ static void write_svg_file(const char *filename) | |||
957 | svg_close(); | 1014 | svg_close(); |
958 | } | 1015 | } |
959 | 1016 | ||
960 | static int | 1017 | static void process_samples(struct perf_session *session) |
961 | process_event(event_t *event) | ||
962 | { | ||
963 | |||
964 | switch (event->header.type) { | ||
965 | |||
966 | case PERF_RECORD_COMM: | ||
967 | return process_comm_event(event); | ||
968 | case PERF_RECORD_FORK: | ||
969 | return process_fork_event(event); | ||
970 | case PERF_RECORD_EXIT: | ||
971 | return process_exit_event(event); | ||
972 | case PERF_RECORD_SAMPLE: | ||
973 | return queue_sample_event(event); | ||
974 | |||
975 | /* | ||
976 | * We dont process them right now but they are fine: | ||
977 | */ | ||
978 | case PERF_RECORD_MMAP: | ||
979 | case PERF_RECORD_THROTTLE: | ||
980 | case PERF_RECORD_UNTHROTTLE: | ||
981 | return 0; | ||
982 | |||
983 | default: | ||
984 | return -1; | ||
985 | } | ||
986 | |||
987 | return 0; | ||
988 | } | ||
989 | |||
990 | static void process_samples(void) | ||
991 | { | 1018 | { |
992 | struct sample_wrapper *cursor; | 1019 | struct sample_wrapper *cursor; |
993 | event_t *event; | 1020 | event_t *event; |
@@ -998,113 +1025,33 @@ static void process_samples(void) | |||
998 | while (cursor) { | 1025 | while (cursor) { |
999 | event = (void *)&cursor->data; | 1026 | event = (void *)&cursor->data; |
1000 | cursor = cursor->next; | 1027 | cursor = cursor->next; |
1001 | process_sample_event(event); | 1028 | process_sample_event(event, session); |
1002 | } | 1029 | } |
1003 | } | 1030 | } |
1004 | 1031 | ||
1032 | static struct perf_event_ops event_ops = { | ||
1033 | .comm = process_comm_event, | ||
1034 | .fork = process_fork_event, | ||
1035 | .exit = process_exit_event, | ||
1036 | .sample = queue_sample_event, | ||
1037 | }; | ||
1005 | 1038 | ||
1006 | static int __cmd_timechart(void) | 1039 | static int __cmd_timechart(void) |
1007 | { | 1040 | { |
1008 | int ret, rc = EXIT_FAILURE; | 1041 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); |
1009 | unsigned long offset = 0; | 1042 | int ret = -EINVAL; |
1010 | unsigned long head, shift; | ||
1011 | struct stat statbuf; | ||
1012 | event_t *event; | ||
1013 | uint32_t size; | ||
1014 | char *buf; | ||
1015 | int input; | ||
1016 | |||
1017 | input = open(input_name, O_RDONLY); | ||
1018 | if (input < 0) { | ||
1019 | fprintf(stderr, " failed to open file: %s", input_name); | ||
1020 | if (!strcmp(input_name, "perf.data")) | ||
1021 | fprintf(stderr, " (try 'perf record' first)"); | ||
1022 | fprintf(stderr, "\n"); | ||
1023 | exit(-1); | ||
1024 | } | ||
1025 | |||
1026 | ret = fstat(input, &statbuf); | ||
1027 | if (ret < 0) { | ||
1028 | perror("failed to stat file"); | ||
1029 | exit(-1); | ||
1030 | } | ||
1031 | |||
1032 | if (!statbuf.st_size) { | ||
1033 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | ||
1034 | exit(0); | ||
1035 | } | ||
1036 | 1043 | ||
1037 | header = perf_header__read(input); | 1044 | if (session == NULL) |
1038 | head = header->data_offset; | 1045 | return -ENOMEM; |
1039 | 1046 | ||
1040 | sample_type = perf_header__sample_type(header); | 1047 | if (!perf_session__has_traces(session, "timechart record")) |
1048 | goto out_delete; | ||
1041 | 1049 | ||
1042 | shift = page_size * (head / page_size); | 1050 | ret = perf_session__process_events(session, &event_ops); |
1043 | offset += shift; | 1051 | if (ret) |
1044 | head -= shift; | 1052 | goto out_delete; |
1045 | 1053 | ||
1046 | remap: | 1054 | process_samples(session); |
1047 | buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, | ||
1048 | MAP_SHARED, input, offset); | ||
1049 | if (buf == MAP_FAILED) { | ||
1050 | perror("failed to mmap file"); | ||
1051 | exit(-1); | ||
1052 | } | ||
1053 | |||
1054 | more: | ||
1055 | event = (event_t *)(buf + head); | ||
1056 | |||
1057 | size = event->header.size; | ||
1058 | if (!size) | ||
1059 | size = 8; | ||
1060 | |||
1061 | if (head + event->header.size >= page_size * mmap_window) { | ||
1062 | int ret2; | ||
1063 | |||
1064 | shift = page_size * (head / page_size); | ||
1065 | |||
1066 | ret2 = munmap(buf, page_size * mmap_window); | ||
1067 | assert(ret2 == 0); | ||
1068 | |||
1069 | offset += shift; | ||
1070 | head -= shift; | ||
1071 | goto remap; | ||
1072 | } | ||
1073 | |||
1074 | size = event->header.size; | ||
1075 | |||
1076 | if (!size || process_event(event) < 0) { | ||
1077 | |||
1078 | printf("%p [%p]: skipping unknown header type: %d\n", | ||
1079 | (void *)(offset + head), | ||
1080 | (void *)(long)(event->header.size), | ||
1081 | event->header.type); | ||
1082 | |||
1083 | /* | ||
1084 | * assume we lost track of the stream, check alignment, and | ||
1085 | * increment a single u64 in the hope to catch on again 'soon'. | ||
1086 | */ | ||
1087 | |||
1088 | if (unlikely(head & 7)) | ||
1089 | head &= ~7ULL; | ||
1090 | |||
1091 | size = 8; | ||
1092 | } | ||
1093 | |||
1094 | head += size; | ||
1095 | |||
1096 | if (offset + head >= header->data_offset + header->data_size) | ||
1097 | goto done; | ||
1098 | |||
1099 | if (offset + head < (unsigned long)statbuf.st_size) | ||
1100 | goto more; | ||
1101 | |||
1102 | done: | ||
1103 | rc = EXIT_SUCCESS; | ||
1104 | close(input); | ||
1105 | |||
1106 | |||
1107 | process_samples(); | ||
1108 | 1055 | ||
1109 | end_sample_processing(); | 1056 | end_sample_processing(); |
1110 | 1057 | ||
@@ -1112,9 +1059,11 @@ done: | |||
1112 | 1059 | ||
1113 | write_svg_file(output_name); | 1060 | write_svg_file(output_name); |
1114 | 1061 | ||
1115 | printf("Written %2.1f seconds of trace to %s.\n", (last_time - first_time) / 1000000000.0, output_name); | 1062 | pr_info("Written %2.1f seconds of trace to %s.\n", |
1116 | 1063 | (last_time - first_time) / 1000000000.0, output_name); | |
1117 | return rc; | 1064 | out_delete: |
1065 | perf_session__delete(session); | ||
1066 | return ret; | ||
1118 | } | 1067 | } |
1119 | 1068 | ||
1120 | static const char * const timechart_usage[] = { | 1069 | static const char * const timechart_usage[] = { |
@@ -1153,6 +1102,14 @@ static int __cmd_record(int argc, const char **argv) | |||
1153 | return cmd_record(i, rec_argv, NULL); | 1102 | return cmd_record(i, rec_argv, NULL); |
1154 | } | 1103 | } |
1155 | 1104 | ||
1105 | static int | ||
1106 | parse_process(const struct option *opt __used, const char *arg, int __used unset) | ||
1107 | { | ||
1108 | if (arg) | ||
1109 | add_process_filter(arg); | ||
1110 | return 0; | ||
1111 | } | ||
1112 | |||
1156 | static const struct option options[] = { | 1113 | static const struct option options[] = { |
1157 | OPT_STRING('i', "input", &input_name, "file", | 1114 | OPT_STRING('i', "input", &input_name, "file", |
1158 | "input file name"), | 1115 | "input file name"), |
@@ -1160,21 +1117,22 @@ static const struct option options[] = { | |||
1160 | "output file name"), | 1117 | "output file name"), |
1161 | OPT_INTEGER('w', "width", &svg_page_width, | 1118 | OPT_INTEGER('w', "width", &svg_page_width, |
1162 | "page width"), | 1119 | "page width"), |
1163 | OPT_BOOLEAN('p', "power-only", &power_only, | 1120 | OPT_BOOLEAN('P', "power-only", &power_only, |
1164 | "output power data only"), | 1121 | "output power data only"), |
1122 | OPT_CALLBACK('p', "process", NULL, "process", | ||
1123 | "process selector. Pass a pid or process name.", | ||
1124 | parse_process), | ||
1165 | OPT_END() | 1125 | OPT_END() |
1166 | }; | 1126 | }; |
1167 | 1127 | ||
1168 | 1128 | ||
1169 | int cmd_timechart(int argc, const char **argv, const char *prefix __used) | 1129 | int cmd_timechart(int argc, const char **argv, const char *prefix __used) |
1170 | { | 1130 | { |
1171 | symbol__init(); | ||
1172 | |||
1173 | page_size = getpagesize(); | ||
1174 | |||
1175 | argc = parse_options(argc, argv, options, timechart_usage, | 1131 | argc = parse_options(argc, argv, options, timechart_usage, |
1176 | PARSE_OPT_STOP_AT_NON_OPTION); | 1132 | PARSE_OPT_STOP_AT_NON_OPTION); |
1177 | 1133 | ||
1134 | symbol__init(); | ||
1135 | |||
1178 | if (argc && !strncmp(argv[0], "rec", 3)) | 1136 | if (argc && !strncmp(argv[0], "rec", 3)) |
1179 | return __cmd_record(argc, argv); | 1137 | return __cmd_record(argc, argv); |
1180 | else if (argc) | 1138 | else if (argc) |