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); |
