diff options
Diffstat (limited to 'tools/perf/builtin-timechart.c')
-rw-r--r-- | tools/perf/builtin-timechart.c | 267 |
1 files changed, 132 insertions, 135 deletions
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index e8a510d935e5..cb58b6605fcc 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
@@ -29,14 +29,14 @@ | |||
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/data_map.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 | 39 | ||
38 | static unsigned long page_size; | ||
39 | static unsigned long mmap_window = 32; | ||
40 | static u64 sample_type; | 40 | static u64 sample_type; |
41 | 41 | ||
42 | static unsigned int numcpus; | 42 | static unsigned int numcpus; |
@@ -49,8 +49,6 @@ static u64 first_time, last_time; | |||
49 | static int power_only; | 49 | static int power_only; |
50 | 50 | ||
51 | 51 | ||
52 | static struct perf_header *header; | ||
53 | |||
54 | struct per_pid; | 52 | struct per_pid; |
55 | struct per_pidcomm; | 53 | struct per_pidcomm; |
56 | 54 | ||
@@ -153,6 +151,17 @@ static struct wake_event *wake_events; | |||
153 | 151 | ||
154 | struct sample_wrapper *all_samples; | 152 | struct sample_wrapper *all_samples; |
155 | 153 | ||
154 | |||
155 | struct process_filter; | ||
156 | struct process_filter { | ||
157 | char *name; | ||
158 | int pid; | ||
159 | struct process_filter *next; | ||
160 | }; | ||
161 | |||
162 | static struct process_filter *process_filter; | ||
163 | |||
164 | |||
156 | static struct per_pid *find_create_pid(int pid) | 165 | static struct per_pid *find_create_pid(int pid) |
157 | { | 166 | { |
158 | struct per_pid *cursor = all_data; | 167 | struct per_pid *cursor = all_data; |
@@ -763,11 +772,11 @@ static void draw_wakeups(void) | |||
763 | c = p->all; | 772 | c = p->all; |
764 | while (c) { | 773 | while (c) { |
765 | if (c->Y && c->start_time <= we->time && c->end_time >= we->time) { | 774 | if (c->Y && c->start_time <= we->time && c->end_time >= we->time) { |
766 | if (p->pid == we->waker) { | 775 | if (p->pid == we->waker && !from) { |
767 | from = c->Y; | 776 | from = c->Y; |
768 | task_from = strdup(c->comm); | 777 | task_from = strdup(c->comm); |
769 | } | 778 | } |
770 | if (p->pid == we->wakee) { | 779 | if (p->pid == we->wakee && !to) { |
771 | to = c->Y; | 780 | to = c->Y; |
772 | task_to = strdup(c->comm); | 781 | task_to = strdup(c->comm); |
773 | } | 782 | } |
@@ -882,12 +891,89 @@ static void draw_process_bars(void) | |||
882 | } | 891 | } |
883 | } | 892 | } |
884 | 893 | ||
894 | static void add_process_filter(const char *string) | ||
895 | { | ||
896 | struct process_filter *filt; | ||
897 | int pid; | ||
898 | |||
899 | pid = strtoull(string, NULL, 10); | ||
900 | filt = malloc(sizeof(struct process_filter)); | ||
901 | if (!filt) | ||
902 | return; | ||
903 | |||
904 | filt->name = strdup(string); | ||
905 | filt->pid = pid; | ||
906 | filt->next = process_filter; | ||
907 | |||
908 | process_filter = filt; | ||
909 | } | ||
910 | |||
911 | static int passes_filter(struct per_pid *p, struct per_pidcomm *c) | ||
912 | { | ||
913 | struct process_filter *filt; | ||
914 | if (!process_filter) | ||
915 | return 1; | ||
916 | |||
917 | filt = process_filter; | ||
918 | while (filt) { | ||
919 | if (filt->pid && p->pid == filt->pid) | ||
920 | return 1; | ||
921 | if (strcmp(filt->name, c->comm) == 0) | ||
922 | return 1; | ||
923 | filt = filt->next; | ||
924 | } | ||
925 | return 0; | ||
926 | } | ||
927 | |||
928 | static int determine_display_tasks_filtered(void) | ||
929 | { | ||
930 | struct per_pid *p; | ||
931 | struct per_pidcomm *c; | ||
932 | int count = 0; | ||
933 | |||
934 | p = all_data; | ||
935 | while (p) { | ||
936 | p->display = 0; | ||
937 | if (p->start_time == 1) | ||
938 | p->start_time = first_time; | ||
939 | |||
940 | /* no exit marker, task kept running to the end */ | ||
941 | if (p->end_time == 0) | ||
942 | p->end_time = last_time; | ||
943 | |||
944 | c = p->all; | ||
945 | |||
946 | while (c) { | ||
947 | c->display = 0; | ||
948 | |||
949 | if (c->start_time == 1) | ||
950 | c->start_time = first_time; | ||
951 | |||
952 | if (passes_filter(p, c)) { | ||
953 | c->display = 1; | ||
954 | p->display = 1; | ||
955 | count++; | ||
956 | } | ||
957 | |||
958 | if (c->end_time == 0) | ||
959 | c->end_time = last_time; | ||
960 | |||
961 | c = c->next; | ||
962 | } | ||
963 | p = p->next; | ||
964 | } | ||
965 | return count; | ||
966 | } | ||
967 | |||
885 | static int determine_display_tasks(u64 threshold) | 968 | static int determine_display_tasks(u64 threshold) |
886 | { | 969 | { |
887 | struct per_pid *p; | 970 | struct per_pid *p; |
888 | struct per_pidcomm *c; | 971 | struct per_pidcomm *c; |
889 | int count = 0; | 972 | int count = 0; |
890 | 973 | ||
974 | if (process_filter) | ||
975 | return determine_display_tasks_filtered(); | ||
976 | |||
891 | p = all_data; | 977 | p = all_data; |
892 | while (p) { | 978 | while (p) { |
893 | p->display = 0; | 979 | p->display = 0; |
@@ -957,36 +1043,6 @@ static void write_svg_file(const char *filename) | |||
957 | svg_close(); | 1043 | svg_close(); |
958 | } | 1044 | } |
959 | 1045 | ||
960 | static int | ||
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) | 1046 | static void process_samples(void) |
991 | { | 1047 | { |
992 | struct sample_wrapper *cursor; | 1048 | struct sample_wrapper *cursor; |
@@ -1002,107 +1058,38 @@ static void process_samples(void) | |||
1002 | } | 1058 | } |
1003 | } | 1059 | } |
1004 | 1060 | ||
1005 | 1061 | static int sample_type_check(u64 type) | |
1006 | static int __cmd_timechart(void) | ||
1007 | { | 1062 | { |
1008 | int ret, rc = EXIT_FAILURE; | 1063 | sample_type = type; |
1009 | unsigned long offset = 0; | ||
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 | |||
1037 | header = perf_header__read(input); | ||
1038 | head = header->data_offset; | ||
1039 | |||
1040 | sample_type = perf_header__sample_type(header); | ||
1041 | 1064 | ||
1042 | shift = page_size * (head / page_size); | 1065 | if (!(sample_type & PERF_SAMPLE_RAW)) { |
1043 | offset += shift; | 1066 | fprintf(stderr, "No trace samples found in the file.\n" |
1044 | head -= shift; | 1067 | "Have you used 'perf timechart record' to record it?\n"); |
1045 | 1068 | return -1; | |
1046 | remap: | ||
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 | } | 1069 | } |
1093 | 1070 | ||
1094 | head += size; | 1071 | return 0; |
1072 | } | ||
1095 | 1073 | ||
1096 | if (offset + head >= header->data_offset + header->data_size) | 1074 | static struct perf_file_handler file_handler = { |
1097 | goto done; | 1075 | .process_comm_event = process_comm_event, |
1076 | .process_fork_event = process_fork_event, | ||
1077 | .process_exit_event = process_exit_event, | ||
1078 | .process_sample_event = queue_sample_event, | ||
1079 | .sample_type_check = sample_type_check, | ||
1080 | }; | ||
1098 | 1081 | ||
1099 | if (offset + head < (unsigned long)statbuf.st_size) | 1082 | static int __cmd_timechart(void) |
1100 | goto more; | 1083 | { |
1084 | struct perf_header *header; | ||
1085 | int ret; | ||
1101 | 1086 | ||
1102 | done: | 1087 | register_perf_file_handler(&file_handler); |
1103 | rc = EXIT_SUCCESS; | ||
1104 | close(input); | ||
1105 | 1088 | ||
1089 | ret = mmap_dispatch_perf_file(&header, input_name, 0, 0, | ||
1090 | &event__cwdlen, &event__cwd); | ||
1091 | if (ret) | ||
1092 | return EXIT_FAILURE; | ||
1106 | 1093 | ||
1107 | process_samples(); | 1094 | process_samples(); |
1108 | 1095 | ||
@@ -1112,9 +1099,10 @@ done: | |||
1112 | 1099 | ||
1113 | write_svg_file(output_name); | 1100 | write_svg_file(output_name); |
1114 | 1101 | ||
1115 | printf("Written %2.1f seconds of trace to %s.\n", (last_time - first_time) / 1000000000.0, output_name); | 1102 | pr_info("Written %2.1f seconds of trace to %s.\n", |
1103 | (last_time - first_time) / 1000000000.0, output_name); | ||
1116 | 1104 | ||
1117 | return rc; | 1105 | return EXIT_SUCCESS; |
1118 | } | 1106 | } |
1119 | 1107 | ||
1120 | static const char * const timechart_usage[] = { | 1108 | static const char * const timechart_usage[] = { |
@@ -1153,6 +1141,14 @@ static int __cmd_record(int argc, const char **argv) | |||
1153 | return cmd_record(i, rec_argv, NULL); | 1141 | return cmd_record(i, rec_argv, NULL); |
1154 | } | 1142 | } |
1155 | 1143 | ||
1144 | static int | ||
1145 | parse_process(const struct option *opt __used, const char *arg, int __used unset) | ||
1146 | { | ||
1147 | if (arg) | ||
1148 | add_process_filter(arg); | ||
1149 | return 0; | ||
1150 | } | ||
1151 | |||
1156 | static const struct option options[] = { | 1152 | static const struct option options[] = { |
1157 | OPT_STRING('i', "input", &input_name, "file", | 1153 | OPT_STRING('i', "input", &input_name, "file", |
1158 | "input file name"), | 1154 | "input file name"), |
@@ -1160,17 +1156,18 @@ static const struct option options[] = { | |||
1160 | "output file name"), | 1156 | "output file name"), |
1161 | OPT_INTEGER('w', "width", &svg_page_width, | 1157 | OPT_INTEGER('w', "width", &svg_page_width, |
1162 | "page width"), | 1158 | "page width"), |
1163 | OPT_BOOLEAN('p', "power-only", &power_only, | 1159 | OPT_BOOLEAN('P', "power-only", &power_only, |
1164 | "output power data only"), | 1160 | "output power data only"), |
1161 | OPT_CALLBACK('p', "process", NULL, "process", | ||
1162 | "process selector. Pass a pid or process name.", | ||
1163 | parse_process), | ||
1165 | OPT_END() | 1164 | OPT_END() |
1166 | }; | 1165 | }; |
1167 | 1166 | ||
1168 | 1167 | ||
1169 | int cmd_timechart(int argc, const char **argv, const char *prefix __used) | 1168 | int cmd_timechart(int argc, const char **argv, const char *prefix __used) |
1170 | { | 1169 | { |
1171 | symbol__init(); | 1170 | symbol__init(0); |
1172 | |||
1173 | page_size = getpagesize(); | ||
1174 | 1171 | ||
1175 | argc = parse_options(argc, argv, options, timechart_usage, | 1172 | argc = parse_options(argc, argv, options, timechart_usage, |
1176 | PARSE_OPT_STOP_AT_NON_OPTION); | 1173 | PARSE_OPT_STOP_AT_NON_OPTION); |