diff options
Diffstat (limited to 'tools/perf/builtin-timechart.c')
-rw-r--r-- | tools/perf/builtin-timechart.c | 694 |
1 files changed, 675 insertions, 19 deletions
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 74db2568b867..2f1a5220c090 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include "util/svghelper.h" | 37 | #include "util/svghelper.h" |
38 | #include "util/tool.h" | 38 | #include "util/tool.h" |
39 | #include "util/data.h" | 39 | #include "util/data.h" |
40 | #include "util/debug.h" | ||
40 | 41 | ||
41 | #define SUPPORT_OLD_POWER_EVENTS 1 | 42 | #define SUPPORT_OLD_POWER_EVENTS 1 |
42 | #define PWR_EVENT_EXIT -1 | 43 | #define PWR_EVENT_EXIT -1 |
@@ -60,10 +61,17 @@ struct timechart { | |||
60 | tasks_only, | 61 | tasks_only, |
61 | with_backtrace, | 62 | with_backtrace, |
62 | topology; | 63 | topology; |
64 | /* IO related settings */ | ||
65 | u64 io_events; | ||
66 | bool io_only, | ||
67 | skip_eagain; | ||
68 | u64 min_time, | ||
69 | merge_dist; | ||
63 | }; | 70 | }; |
64 | 71 | ||
65 | struct per_pidcomm; | 72 | struct per_pidcomm; |
66 | struct cpu_sample; | 73 | struct cpu_sample; |
74 | struct io_sample; | ||
67 | 75 | ||
68 | /* | 76 | /* |
69 | * Datastructure layout: | 77 | * Datastructure layout: |
@@ -84,6 +92,7 @@ struct per_pid { | |||
84 | u64 start_time; | 92 | u64 start_time; |
85 | u64 end_time; | 93 | u64 end_time; |
86 | u64 total_time; | 94 | u64 total_time; |
95 | u64 total_bytes; | ||
87 | int display; | 96 | int display; |
88 | 97 | ||
89 | struct per_pidcomm *all; | 98 | struct per_pidcomm *all; |
@@ -97,6 +106,8 @@ struct per_pidcomm { | |||
97 | u64 start_time; | 106 | u64 start_time; |
98 | u64 end_time; | 107 | u64 end_time; |
99 | u64 total_time; | 108 | u64 total_time; |
109 | u64 max_bytes; | ||
110 | u64 total_bytes; | ||
100 | 111 | ||
101 | int Y; | 112 | int Y; |
102 | int display; | 113 | int display; |
@@ -107,6 +118,7 @@ struct per_pidcomm { | |||
107 | char *comm; | 118 | char *comm; |
108 | 119 | ||
109 | struct cpu_sample *samples; | 120 | struct cpu_sample *samples; |
121 | struct io_sample *io_samples; | ||
110 | }; | 122 | }; |
111 | 123 | ||
112 | struct sample_wrapper { | 124 | struct sample_wrapper { |
@@ -131,6 +143,27 @@ struct cpu_sample { | |||
131 | const char *backtrace; | 143 | const char *backtrace; |
132 | }; | 144 | }; |
133 | 145 | ||
146 | enum { | ||
147 | IOTYPE_READ, | ||
148 | IOTYPE_WRITE, | ||
149 | IOTYPE_SYNC, | ||
150 | IOTYPE_TX, | ||
151 | IOTYPE_RX, | ||
152 | IOTYPE_POLL, | ||
153 | }; | ||
154 | |||
155 | struct io_sample { | ||
156 | struct io_sample *next; | ||
157 | |||
158 | u64 start_time; | ||
159 | u64 end_time; | ||
160 | u64 bytes; | ||
161 | int type; | ||
162 | int fd; | ||
163 | int err; | ||
164 | int merges; | ||
165 | }; | ||
166 | |||
134 | #define CSTATE 1 | 167 | #define CSTATE 1 |
135 | #define PSTATE 2 | 168 | #define PSTATE 2 |
136 | 169 | ||
@@ -213,7 +246,7 @@ static void pid_fork(struct timechart *tchart, int pid, int ppid, u64 timestamp) | |||
213 | pid_set_comm(tchart, pid, pp->current->comm); | 246 | pid_set_comm(tchart, pid, pp->current->comm); |
214 | 247 | ||
215 | p->start_time = timestamp; | 248 | p->start_time = timestamp; |
216 | if (p->current) { | 249 | if (p->current && !p->current->start_time) { |
217 | p->current->start_time = timestamp; | 250 | p->current->start_time = timestamp; |
218 | p->current->state_since = timestamp; | 251 | p->current->state_since = timestamp; |
219 | } | 252 | } |
@@ -682,6 +715,249 @@ static void end_sample_processing(struct timechart *tchart) | |||
682 | } | 715 | } |
683 | } | 716 | } |
684 | 717 | ||
718 | static int pid_begin_io_sample(struct timechart *tchart, int pid, int type, | ||
719 | u64 start, int fd) | ||
720 | { | ||
721 | struct per_pid *p = find_create_pid(tchart, pid); | ||
722 | struct per_pidcomm *c = p->current; | ||
723 | struct io_sample *sample; | ||
724 | struct io_sample *prev; | ||
725 | |||
726 | if (!c) { | ||
727 | c = zalloc(sizeof(*c)); | ||
728 | if (!c) | ||
729 | return -ENOMEM; | ||
730 | p->current = c; | ||
731 | c->next = p->all; | ||
732 | p->all = c; | ||
733 | } | ||
734 | |||
735 | prev = c->io_samples; | ||
736 | |||
737 | if (prev && prev->start_time && !prev->end_time) { | ||
738 | pr_warning("Skip invalid start event: " | ||
739 | "previous event already started!\n"); | ||
740 | |||
741 | /* remove previous event that has been started, | ||
742 | * we are not sure we will ever get an end for it */ | ||
743 | c->io_samples = prev->next; | ||
744 | free(prev); | ||
745 | return 0; | ||
746 | } | ||
747 | |||
748 | sample = zalloc(sizeof(*sample)); | ||
749 | if (!sample) | ||
750 | return -ENOMEM; | ||
751 | sample->start_time = start; | ||
752 | sample->type = type; | ||
753 | sample->fd = fd; | ||
754 | sample->next = c->io_samples; | ||
755 | c->io_samples = sample; | ||
756 | |||
757 | if (c->start_time == 0 || c->start_time > start) | ||
758 | c->start_time = start; | ||
759 | |||
760 | return 0; | ||
761 | } | ||
762 | |||
763 | static int pid_end_io_sample(struct timechart *tchart, int pid, int type, | ||
764 | u64 end, long ret) | ||
765 | { | ||
766 | struct per_pid *p = find_create_pid(tchart, pid); | ||
767 | struct per_pidcomm *c = p->current; | ||
768 | struct io_sample *sample, *prev; | ||
769 | |||
770 | if (!c) { | ||
771 | pr_warning("Invalid pidcomm!\n"); | ||
772 | return -1; | ||
773 | } | ||
774 | |||
775 | sample = c->io_samples; | ||
776 | |||
777 | if (!sample) /* skip partially captured events */ | ||
778 | return 0; | ||
779 | |||
780 | if (sample->end_time) { | ||
781 | pr_warning("Skip invalid end event: " | ||
782 | "previous event already ended!\n"); | ||
783 | return 0; | ||
784 | } | ||
785 | |||
786 | if (sample->type != type) { | ||
787 | pr_warning("Skip invalid end event: invalid event type!\n"); | ||
788 | return 0; | ||
789 | } | ||
790 | |||
791 | sample->end_time = end; | ||
792 | prev = sample->next; | ||
793 | |||
794 | /* we want to be able to see small and fast transfers, so make them | ||
795 | * at least min_time long, but don't overlap them */ | ||
796 | if (sample->end_time - sample->start_time < tchart->min_time) | ||
797 | sample->end_time = sample->start_time + tchart->min_time; | ||
798 | if (prev && sample->start_time < prev->end_time) { | ||
799 | if (prev->err) /* try to make errors more visible */ | ||
800 | sample->start_time = prev->end_time; | ||
801 | else | ||
802 | prev->end_time = sample->start_time; | ||
803 | } | ||
804 | |||
805 | if (ret < 0) { | ||
806 | sample->err = ret; | ||
807 | } else if (type == IOTYPE_READ || type == IOTYPE_WRITE || | ||
808 | type == IOTYPE_TX || type == IOTYPE_RX) { | ||
809 | |||
810 | if ((u64)ret > c->max_bytes) | ||
811 | c->max_bytes = ret; | ||
812 | |||
813 | c->total_bytes += ret; | ||
814 | p->total_bytes += ret; | ||
815 | sample->bytes = ret; | ||
816 | } | ||
817 | |||
818 | /* merge two requests to make svg smaller and render-friendly */ | ||
819 | if (prev && | ||
820 | prev->type == sample->type && | ||
821 | prev->err == sample->err && | ||
822 | prev->fd == sample->fd && | ||
823 | prev->end_time + tchart->merge_dist >= sample->start_time) { | ||
824 | |||
825 | sample->bytes += prev->bytes; | ||
826 | sample->merges += prev->merges + 1; | ||
827 | |||
828 | sample->start_time = prev->start_time; | ||
829 | sample->next = prev->next; | ||
830 | free(prev); | ||
831 | |||
832 | if (!sample->err && sample->bytes > c->max_bytes) | ||
833 | c->max_bytes = sample->bytes; | ||
834 | } | ||
835 | |||
836 | tchart->io_events++; | ||
837 | |||
838 | return 0; | ||
839 | } | ||
840 | |||
841 | static int | ||
842 | process_enter_read(struct timechart *tchart, | ||
843 | struct perf_evsel *evsel, | ||
844 | struct perf_sample *sample) | ||
845 | { | ||
846 | long fd = perf_evsel__intval(evsel, sample, "fd"); | ||
847 | return pid_begin_io_sample(tchart, sample->tid, IOTYPE_READ, | ||
848 | sample->time, fd); | ||
849 | } | ||
850 | |||
851 | static int | ||
852 | process_exit_read(struct timechart *tchart, | ||
853 | struct perf_evsel *evsel, | ||
854 | struct perf_sample *sample) | ||
855 | { | ||
856 | long ret = perf_evsel__intval(evsel, sample, "ret"); | ||
857 | return pid_end_io_sample(tchart, sample->tid, IOTYPE_READ, | ||
858 | sample->time, ret); | ||
859 | } | ||
860 | |||
861 | static int | ||
862 | process_enter_write(struct timechart *tchart, | ||
863 | struct perf_evsel *evsel, | ||
864 | struct perf_sample *sample) | ||
865 | { | ||
866 | long fd = perf_evsel__intval(evsel, sample, "fd"); | ||
867 | return pid_begin_io_sample(tchart, sample->tid, IOTYPE_WRITE, | ||
868 | sample->time, fd); | ||
869 | } | ||
870 | |||
871 | static int | ||
872 | process_exit_write(struct timechart *tchart, | ||
873 | struct perf_evsel *evsel, | ||
874 | struct perf_sample *sample) | ||
875 | { | ||
876 | long ret = perf_evsel__intval(evsel, sample, "ret"); | ||
877 | return pid_end_io_sample(tchart, sample->tid, IOTYPE_WRITE, | ||
878 | sample->time, ret); | ||
879 | } | ||
880 | |||
881 | static int | ||
882 | process_enter_sync(struct timechart *tchart, | ||
883 | struct perf_evsel *evsel, | ||
884 | struct perf_sample *sample) | ||
885 | { | ||
886 | long fd = perf_evsel__intval(evsel, sample, "fd"); | ||
887 | return pid_begin_io_sample(tchart, sample->tid, IOTYPE_SYNC, | ||
888 | sample->time, fd); | ||
889 | } | ||
890 | |||
891 | static int | ||
892 | process_exit_sync(struct timechart *tchart, | ||
893 | struct perf_evsel *evsel, | ||
894 | struct perf_sample *sample) | ||
895 | { | ||
896 | long ret = perf_evsel__intval(evsel, sample, "ret"); | ||
897 | return pid_end_io_sample(tchart, sample->tid, IOTYPE_SYNC, | ||
898 | sample->time, ret); | ||
899 | } | ||
900 | |||
901 | static int | ||
902 | process_enter_tx(struct timechart *tchart, | ||
903 | struct perf_evsel *evsel, | ||
904 | struct perf_sample *sample) | ||
905 | { | ||
906 | long fd = perf_evsel__intval(evsel, sample, "fd"); | ||
907 | return pid_begin_io_sample(tchart, sample->tid, IOTYPE_TX, | ||
908 | sample->time, fd); | ||
909 | } | ||
910 | |||
911 | static int | ||
912 | process_exit_tx(struct timechart *tchart, | ||
913 | struct perf_evsel *evsel, | ||
914 | struct perf_sample *sample) | ||
915 | { | ||
916 | long ret = perf_evsel__intval(evsel, sample, "ret"); | ||
917 | return pid_end_io_sample(tchart, sample->tid, IOTYPE_TX, | ||
918 | sample->time, ret); | ||
919 | } | ||
920 | |||
921 | static int | ||
922 | process_enter_rx(struct timechart *tchart, | ||
923 | struct perf_evsel *evsel, | ||
924 | struct perf_sample *sample) | ||
925 | { | ||
926 | long fd = perf_evsel__intval(evsel, sample, "fd"); | ||
927 | return pid_begin_io_sample(tchart, sample->tid, IOTYPE_RX, | ||
928 | sample->time, fd); | ||
929 | } | ||
930 | |||
931 | static int | ||
932 | process_exit_rx(struct timechart *tchart, | ||
933 | struct perf_evsel *evsel, | ||
934 | struct perf_sample *sample) | ||
935 | { | ||
936 | long ret = perf_evsel__intval(evsel, sample, "ret"); | ||
937 | return pid_end_io_sample(tchart, sample->tid, IOTYPE_RX, | ||
938 | sample->time, ret); | ||
939 | } | ||
940 | |||
941 | static int | ||
942 | process_enter_poll(struct timechart *tchart, | ||
943 | struct perf_evsel *evsel, | ||
944 | struct perf_sample *sample) | ||
945 | { | ||
946 | long fd = perf_evsel__intval(evsel, sample, "fd"); | ||
947 | return pid_begin_io_sample(tchart, sample->tid, IOTYPE_POLL, | ||
948 | sample->time, fd); | ||
949 | } | ||
950 | |||
951 | static int | ||
952 | process_exit_poll(struct timechart *tchart, | ||
953 | struct perf_evsel *evsel, | ||
954 | struct perf_sample *sample) | ||
955 | { | ||
956 | long ret = perf_evsel__intval(evsel, sample, "ret"); | ||
957 | return pid_end_io_sample(tchart, sample->tid, IOTYPE_POLL, | ||
958 | sample->time, ret); | ||
959 | } | ||
960 | |||
685 | /* | 961 | /* |
686 | * Sort the pid datastructure | 962 | * Sort the pid datastructure |
687 | */ | 963 | */ |
@@ -852,6 +1128,121 @@ static void draw_cpu_usage(struct timechart *tchart) | |||
852 | } | 1128 | } |
853 | } | 1129 | } |
854 | 1130 | ||
1131 | static void draw_io_bars(struct timechart *tchart) | ||
1132 | { | ||
1133 | const char *suf; | ||
1134 | double bytes; | ||
1135 | char comm[256]; | ||
1136 | struct per_pid *p; | ||
1137 | struct per_pidcomm *c; | ||
1138 | struct io_sample *sample; | ||
1139 | int Y = 1; | ||
1140 | |||
1141 | p = tchart->all_data; | ||
1142 | while (p) { | ||
1143 | c = p->all; | ||
1144 | while (c) { | ||
1145 | if (!c->display) { | ||
1146 | c->Y = 0; | ||
1147 | c = c->next; | ||
1148 | continue; | ||
1149 | } | ||
1150 | |||
1151 | svg_box(Y, c->start_time, c->end_time, "process3"); | ||
1152 | sample = c->io_samples; | ||
1153 | for (sample = c->io_samples; sample; sample = sample->next) { | ||
1154 | double h = (double)sample->bytes / c->max_bytes; | ||
1155 | |||
1156 | if (tchart->skip_eagain && | ||
1157 | sample->err == -EAGAIN) | ||
1158 | continue; | ||
1159 | |||
1160 | if (sample->err) | ||
1161 | h = 1; | ||
1162 | |||
1163 | if (sample->type == IOTYPE_SYNC) | ||
1164 | svg_fbox(Y, | ||
1165 | sample->start_time, | ||
1166 | sample->end_time, | ||
1167 | 1, | ||
1168 | sample->err ? "error" : "sync", | ||
1169 | sample->fd, | ||
1170 | sample->err, | ||
1171 | sample->merges); | ||
1172 | else if (sample->type == IOTYPE_POLL) | ||
1173 | svg_fbox(Y, | ||
1174 | sample->start_time, | ||
1175 | sample->end_time, | ||
1176 | 1, | ||
1177 | sample->err ? "error" : "poll", | ||
1178 | sample->fd, | ||
1179 | sample->err, | ||
1180 | sample->merges); | ||
1181 | else if (sample->type == IOTYPE_READ) | ||
1182 | svg_ubox(Y, | ||
1183 | sample->start_time, | ||
1184 | sample->end_time, | ||
1185 | h, | ||
1186 | sample->err ? "error" : "disk", | ||
1187 | sample->fd, | ||
1188 | sample->err, | ||
1189 | sample->merges); | ||
1190 | else if (sample->type == IOTYPE_WRITE) | ||
1191 | svg_lbox(Y, | ||
1192 | sample->start_time, | ||
1193 | sample->end_time, | ||
1194 | h, | ||
1195 | sample->err ? "error" : "disk", | ||
1196 | sample->fd, | ||
1197 | sample->err, | ||
1198 | sample->merges); | ||
1199 | else if (sample->type == IOTYPE_RX) | ||
1200 | svg_ubox(Y, | ||
1201 | sample->start_time, | ||
1202 | sample->end_time, | ||
1203 | h, | ||
1204 | sample->err ? "error" : "net", | ||
1205 | sample->fd, | ||
1206 | sample->err, | ||
1207 | sample->merges); | ||
1208 | else if (sample->type == IOTYPE_TX) | ||
1209 | svg_lbox(Y, | ||
1210 | sample->start_time, | ||
1211 | sample->end_time, | ||
1212 | h, | ||
1213 | sample->err ? "error" : "net", | ||
1214 | sample->fd, | ||
1215 | sample->err, | ||
1216 | sample->merges); | ||
1217 | } | ||
1218 | |||
1219 | suf = ""; | ||
1220 | bytes = c->total_bytes; | ||
1221 | if (bytes > 1024) { | ||
1222 | bytes = bytes / 1024; | ||
1223 | suf = "K"; | ||
1224 | } | ||
1225 | if (bytes > 1024) { | ||
1226 | bytes = bytes / 1024; | ||
1227 | suf = "M"; | ||
1228 | } | ||
1229 | if (bytes > 1024) { | ||
1230 | bytes = bytes / 1024; | ||
1231 | suf = "G"; | ||
1232 | } | ||
1233 | |||
1234 | |||
1235 | sprintf(comm, "%s:%i (%3.1f %sbytes)", c->comm ?: "", p->pid, bytes, suf); | ||
1236 | svg_text(Y, c->start_time, comm); | ||
1237 | |||
1238 | c->Y = Y; | ||
1239 | Y++; | ||
1240 | c = c->next; | ||
1241 | } | ||
1242 | p = p->next; | ||
1243 | } | ||
1244 | } | ||
1245 | |||
855 | static void draw_process_bars(struct timechart *tchart) | 1246 | static void draw_process_bars(struct timechart *tchart) |
856 | { | 1247 | { |
857 | struct per_pid *p; | 1248 | struct per_pid *p; |
@@ -987,9 +1378,6 @@ static int determine_display_tasks(struct timechart *tchart, u64 threshold) | |||
987 | struct per_pidcomm *c; | 1378 | struct per_pidcomm *c; |
988 | int count = 0; | 1379 | int count = 0; |
989 | 1380 | ||
990 | if (process_filter) | ||
991 | return determine_display_tasks_filtered(tchart); | ||
992 | |||
993 | p = tchart->all_data; | 1381 | p = tchart->all_data; |
994 | while (p) { | 1382 | while (p) { |
995 | p->display = 0; | 1383 | p->display = 0; |
@@ -1025,15 +1413,46 @@ static int determine_display_tasks(struct timechart *tchart, u64 threshold) | |||
1025 | return count; | 1413 | return count; |
1026 | } | 1414 | } |
1027 | 1415 | ||
1416 | static int determine_display_io_tasks(struct timechart *timechart, u64 threshold) | ||
1417 | { | ||
1418 | struct per_pid *p; | ||
1419 | struct per_pidcomm *c; | ||
1420 | int count = 0; | ||
1421 | |||
1422 | p = timechart->all_data; | ||
1423 | while (p) { | ||
1424 | /* no exit marker, task kept running to the end */ | ||
1425 | if (p->end_time == 0) | ||
1426 | p->end_time = timechart->last_time; | ||
1028 | 1427 | ||
1428 | c = p->all; | ||
1029 | 1429 | ||
1430 | while (c) { | ||
1431 | c->display = 0; | ||
1432 | |||
1433 | if (c->total_bytes >= threshold) { | ||
1434 | c->display = 1; | ||
1435 | count++; | ||
1436 | } | ||
1437 | |||
1438 | if (c->end_time == 0) | ||
1439 | c->end_time = timechart->last_time; | ||
1440 | |||
1441 | c = c->next; | ||
1442 | } | ||
1443 | p = p->next; | ||
1444 | } | ||
1445 | return count; | ||
1446 | } | ||
1447 | |||
1448 | #define BYTES_THRESH (1 * 1024 * 1024) | ||
1030 | #define TIME_THRESH 10000000 | 1449 | #define TIME_THRESH 10000000 |
1031 | 1450 | ||
1032 | static void write_svg_file(struct timechart *tchart, const char *filename) | 1451 | static void write_svg_file(struct timechart *tchart, const char *filename) |
1033 | { | 1452 | { |
1034 | u64 i; | 1453 | u64 i; |
1035 | int count; | 1454 | int count; |
1036 | int thresh = TIME_THRESH; | 1455 | int thresh = tchart->io_events ? BYTES_THRESH : TIME_THRESH; |
1037 | 1456 | ||
1038 | if (tchart->power_only) | 1457 | if (tchart->power_only) |
1039 | tchart->proc_num = 0; | 1458 | tchart->proc_num = 0; |
@@ -1041,28 +1460,43 @@ static void write_svg_file(struct timechart *tchart, const char *filename) | |||
1041 | /* We'd like to show at least proc_num tasks; | 1460 | /* We'd like to show at least proc_num tasks; |
1042 | * be less picky if we have fewer */ | 1461 | * be less picky if we have fewer */ |
1043 | do { | 1462 | do { |
1044 | count = determine_display_tasks(tchart, thresh); | 1463 | if (process_filter) |
1464 | count = determine_display_tasks_filtered(tchart); | ||
1465 | else if (tchart->io_events) | ||
1466 | count = determine_display_io_tasks(tchart, thresh); | ||
1467 | else | ||
1468 | count = determine_display_tasks(tchart, thresh); | ||
1045 | thresh /= 10; | 1469 | thresh /= 10; |
1046 | } while (!process_filter && thresh && count < tchart->proc_num); | 1470 | } while (!process_filter && thresh && count < tchart->proc_num); |
1047 | 1471 | ||
1048 | if (!tchart->proc_num) | 1472 | if (!tchart->proc_num) |
1049 | count = 0; | 1473 | count = 0; |
1050 | 1474 | ||
1051 | open_svg(filename, tchart->numcpus, count, tchart->first_time, tchart->last_time); | 1475 | if (tchart->io_events) { |
1476 | open_svg(filename, 0, count, tchart->first_time, tchart->last_time); | ||
1052 | 1477 | ||
1053 | svg_time_grid(); | 1478 | svg_time_grid(0.5); |
1054 | svg_legenda(); | 1479 | svg_io_legenda(); |
1480 | |||
1481 | draw_io_bars(tchart); | ||
1482 | } else { | ||
1483 | open_svg(filename, tchart->numcpus, count, tchart->first_time, tchart->last_time); | ||
1055 | 1484 | ||
1056 | for (i = 0; i < tchart->numcpus; i++) | 1485 | svg_time_grid(0); |
1057 | svg_cpu_box(i, tchart->max_freq, tchart->turbo_frequency); | ||
1058 | 1486 | ||
1059 | draw_cpu_usage(tchart); | 1487 | svg_legenda(); |
1060 | if (tchart->proc_num) | 1488 | |
1061 | draw_process_bars(tchart); | 1489 | for (i = 0; i < tchart->numcpus; i++) |
1062 | if (!tchart->tasks_only) | 1490 | svg_cpu_box(i, tchart->max_freq, tchart->turbo_frequency); |
1063 | draw_c_p_states(tchart); | 1491 | |
1064 | if (tchart->proc_num) | 1492 | draw_cpu_usage(tchart); |
1065 | draw_wakeups(tchart); | 1493 | if (tchart->proc_num) |
1494 | draw_process_bars(tchart); | ||
1495 | if (!tchart->tasks_only) | ||
1496 | draw_c_p_states(tchart); | ||
1497 | if (tchart->proc_num) | ||
1498 | draw_wakeups(tchart); | ||
1499 | } | ||
1066 | 1500 | ||
1067 | svg_close(); | 1501 | svg_close(); |
1068 | } | 1502 | } |
@@ -1110,6 +1544,56 @@ static int __cmd_timechart(struct timechart *tchart, const char *output_name) | |||
1110 | { "power:power_end", process_sample_power_end }, | 1544 | { "power:power_end", process_sample_power_end }, |
1111 | { "power:power_frequency", process_sample_power_frequency }, | 1545 | { "power:power_frequency", process_sample_power_frequency }, |
1112 | #endif | 1546 | #endif |
1547 | |||
1548 | { "syscalls:sys_enter_read", process_enter_read }, | ||
1549 | { "syscalls:sys_enter_pread64", process_enter_read }, | ||
1550 | { "syscalls:sys_enter_readv", process_enter_read }, | ||
1551 | { "syscalls:sys_enter_preadv", process_enter_read }, | ||
1552 | { "syscalls:sys_enter_write", process_enter_write }, | ||
1553 | { "syscalls:sys_enter_pwrite64", process_enter_write }, | ||
1554 | { "syscalls:sys_enter_writev", process_enter_write }, | ||
1555 | { "syscalls:sys_enter_pwritev", process_enter_write }, | ||
1556 | { "syscalls:sys_enter_sync", process_enter_sync }, | ||
1557 | { "syscalls:sys_enter_sync_file_range", process_enter_sync }, | ||
1558 | { "syscalls:sys_enter_fsync", process_enter_sync }, | ||
1559 | { "syscalls:sys_enter_msync", process_enter_sync }, | ||
1560 | { "syscalls:sys_enter_recvfrom", process_enter_rx }, | ||
1561 | { "syscalls:sys_enter_recvmmsg", process_enter_rx }, | ||
1562 | { "syscalls:sys_enter_recvmsg", process_enter_rx }, | ||
1563 | { "syscalls:sys_enter_sendto", process_enter_tx }, | ||
1564 | { "syscalls:sys_enter_sendmsg", process_enter_tx }, | ||
1565 | { "syscalls:sys_enter_sendmmsg", process_enter_tx }, | ||
1566 | { "syscalls:sys_enter_epoll_pwait", process_enter_poll }, | ||
1567 | { "syscalls:sys_enter_epoll_wait", process_enter_poll }, | ||
1568 | { "syscalls:sys_enter_poll", process_enter_poll }, | ||
1569 | { "syscalls:sys_enter_ppoll", process_enter_poll }, | ||
1570 | { "syscalls:sys_enter_pselect6", process_enter_poll }, | ||
1571 | { "syscalls:sys_enter_select", process_enter_poll }, | ||
1572 | |||
1573 | { "syscalls:sys_exit_read", process_exit_read }, | ||
1574 | { "syscalls:sys_exit_pread64", process_exit_read }, | ||
1575 | { "syscalls:sys_exit_readv", process_exit_read }, | ||
1576 | { "syscalls:sys_exit_preadv", process_exit_read }, | ||
1577 | { "syscalls:sys_exit_write", process_exit_write }, | ||
1578 | { "syscalls:sys_exit_pwrite64", process_exit_write }, | ||
1579 | { "syscalls:sys_exit_writev", process_exit_write }, | ||
1580 | { "syscalls:sys_exit_pwritev", process_exit_write }, | ||
1581 | { "syscalls:sys_exit_sync", process_exit_sync }, | ||
1582 | { "syscalls:sys_exit_sync_file_range", process_exit_sync }, | ||
1583 | { "syscalls:sys_exit_fsync", process_exit_sync }, | ||
1584 | { "syscalls:sys_exit_msync", process_exit_sync }, | ||
1585 | { "syscalls:sys_exit_recvfrom", process_exit_rx }, | ||
1586 | { "syscalls:sys_exit_recvmmsg", process_exit_rx }, | ||
1587 | { "syscalls:sys_exit_recvmsg", process_exit_rx }, | ||
1588 | { "syscalls:sys_exit_sendto", process_exit_tx }, | ||
1589 | { "syscalls:sys_exit_sendmsg", process_exit_tx }, | ||
1590 | { "syscalls:sys_exit_sendmmsg", process_exit_tx }, | ||
1591 | { "syscalls:sys_exit_epoll_pwait", process_exit_poll }, | ||
1592 | { "syscalls:sys_exit_epoll_wait", process_exit_poll }, | ||
1593 | { "syscalls:sys_exit_poll", process_exit_poll }, | ||
1594 | { "syscalls:sys_exit_ppoll", process_exit_poll }, | ||
1595 | { "syscalls:sys_exit_pselect6", process_exit_poll }, | ||
1596 | { "syscalls:sys_exit_select", process_exit_poll }, | ||
1113 | }; | 1597 | }; |
1114 | struct perf_data_file file = { | 1598 | struct perf_data_file file = { |
1115 | .path = input_name, | 1599 | .path = input_name, |
@@ -1154,6 +1638,139 @@ out_delete: | |||
1154 | return ret; | 1638 | return ret; |
1155 | } | 1639 | } |
1156 | 1640 | ||
1641 | static int timechart__io_record(int argc, const char **argv) | ||
1642 | { | ||
1643 | unsigned int rec_argc, i; | ||
1644 | const char **rec_argv; | ||
1645 | const char **p; | ||
1646 | char *filter = NULL; | ||
1647 | |||
1648 | const char * const common_args[] = { | ||
1649 | "record", "-a", "-R", "-c", "1", | ||
1650 | }; | ||
1651 | unsigned int common_args_nr = ARRAY_SIZE(common_args); | ||
1652 | |||
1653 | const char * const disk_events[] = { | ||
1654 | "syscalls:sys_enter_read", | ||
1655 | "syscalls:sys_enter_pread64", | ||
1656 | "syscalls:sys_enter_readv", | ||
1657 | "syscalls:sys_enter_preadv", | ||
1658 | "syscalls:sys_enter_write", | ||
1659 | "syscalls:sys_enter_pwrite64", | ||
1660 | "syscalls:sys_enter_writev", | ||
1661 | "syscalls:sys_enter_pwritev", | ||
1662 | "syscalls:sys_enter_sync", | ||
1663 | "syscalls:sys_enter_sync_file_range", | ||
1664 | "syscalls:sys_enter_fsync", | ||
1665 | "syscalls:sys_enter_msync", | ||
1666 | |||
1667 | "syscalls:sys_exit_read", | ||
1668 | "syscalls:sys_exit_pread64", | ||
1669 | "syscalls:sys_exit_readv", | ||
1670 | "syscalls:sys_exit_preadv", | ||
1671 | "syscalls:sys_exit_write", | ||
1672 | "syscalls:sys_exit_pwrite64", | ||
1673 | "syscalls:sys_exit_writev", | ||
1674 | "syscalls:sys_exit_pwritev", | ||
1675 | "syscalls:sys_exit_sync", | ||
1676 | "syscalls:sys_exit_sync_file_range", | ||
1677 | "syscalls:sys_exit_fsync", | ||
1678 | "syscalls:sys_exit_msync", | ||
1679 | }; | ||
1680 | unsigned int disk_events_nr = ARRAY_SIZE(disk_events); | ||
1681 | |||
1682 | const char * const net_events[] = { | ||
1683 | "syscalls:sys_enter_recvfrom", | ||
1684 | "syscalls:sys_enter_recvmmsg", | ||
1685 | "syscalls:sys_enter_recvmsg", | ||
1686 | "syscalls:sys_enter_sendto", | ||
1687 | "syscalls:sys_enter_sendmsg", | ||
1688 | "syscalls:sys_enter_sendmmsg", | ||
1689 | |||
1690 | "syscalls:sys_exit_recvfrom", | ||
1691 | "syscalls:sys_exit_recvmmsg", | ||
1692 | "syscalls:sys_exit_recvmsg", | ||
1693 | "syscalls:sys_exit_sendto", | ||
1694 | "syscalls:sys_exit_sendmsg", | ||
1695 | "syscalls:sys_exit_sendmmsg", | ||
1696 | }; | ||
1697 | unsigned int net_events_nr = ARRAY_SIZE(net_events); | ||
1698 | |||
1699 | const char * const poll_events[] = { | ||
1700 | "syscalls:sys_enter_epoll_pwait", | ||
1701 | "syscalls:sys_enter_epoll_wait", | ||
1702 | "syscalls:sys_enter_poll", | ||
1703 | "syscalls:sys_enter_ppoll", | ||
1704 | "syscalls:sys_enter_pselect6", | ||
1705 | "syscalls:sys_enter_select", | ||
1706 | |||
1707 | "syscalls:sys_exit_epoll_pwait", | ||
1708 | "syscalls:sys_exit_epoll_wait", | ||
1709 | "syscalls:sys_exit_poll", | ||
1710 | "syscalls:sys_exit_ppoll", | ||
1711 | "syscalls:sys_exit_pselect6", | ||
1712 | "syscalls:sys_exit_select", | ||
1713 | }; | ||
1714 | unsigned int poll_events_nr = ARRAY_SIZE(poll_events); | ||
1715 | |||
1716 | rec_argc = common_args_nr + | ||
1717 | disk_events_nr * 4 + | ||
1718 | net_events_nr * 4 + | ||
1719 | poll_events_nr * 4 + | ||
1720 | argc; | ||
1721 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | ||
1722 | |||
1723 | if (rec_argv == NULL) | ||
1724 | return -ENOMEM; | ||
1725 | |||
1726 | if (asprintf(&filter, "common_pid != %d", getpid()) < 0) | ||
1727 | return -ENOMEM; | ||
1728 | |||
1729 | p = rec_argv; | ||
1730 | for (i = 0; i < common_args_nr; i++) | ||
1731 | *p++ = strdup(common_args[i]); | ||
1732 | |||
1733 | for (i = 0; i < disk_events_nr; i++) { | ||
1734 | if (!is_valid_tracepoint(disk_events[i])) { | ||
1735 | rec_argc -= 4; | ||
1736 | continue; | ||
1737 | } | ||
1738 | |||
1739 | *p++ = "-e"; | ||
1740 | *p++ = strdup(disk_events[i]); | ||
1741 | *p++ = "--filter"; | ||
1742 | *p++ = filter; | ||
1743 | } | ||
1744 | for (i = 0; i < net_events_nr; i++) { | ||
1745 | if (!is_valid_tracepoint(net_events[i])) { | ||
1746 | rec_argc -= 4; | ||
1747 | continue; | ||
1748 | } | ||
1749 | |||
1750 | *p++ = "-e"; | ||
1751 | *p++ = strdup(net_events[i]); | ||
1752 | *p++ = "--filter"; | ||
1753 | *p++ = filter; | ||
1754 | } | ||
1755 | for (i = 0; i < poll_events_nr; i++) { | ||
1756 | if (!is_valid_tracepoint(poll_events[i])) { | ||
1757 | rec_argc -= 4; | ||
1758 | continue; | ||
1759 | } | ||
1760 | |||
1761 | *p++ = "-e"; | ||
1762 | *p++ = strdup(poll_events[i]); | ||
1763 | *p++ = "--filter"; | ||
1764 | *p++ = filter; | ||
1765 | } | ||
1766 | |||
1767 | for (i = 0; i < (unsigned int)argc; i++) | ||
1768 | *p++ = argv[i]; | ||
1769 | |||
1770 | return cmd_record(rec_argc, rec_argv, NULL); | ||
1771 | } | ||
1772 | |||
1773 | |||
1157 | static int timechart__record(struct timechart *tchart, int argc, const char **argv) | 1774 | static int timechart__record(struct timechart *tchart, int argc, const char **argv) |
1158 | { | 1775 | { |
1159 | unsigned int rec_argc, i, j; | 1776 | unsigned int rec_argc, i, j; |
@@ -1270,6 +1887,30 @@ parse_highlight(const struct option *opt __maybe_unused, const char *arg, | |||
1270 | return 0; | 1887 | return 0; |
1271 | } | 1888 | } |
1272 | 1889 | ||
1890 | static int | ||
1891 | parse_time(const struct option *opt, const char *arg, int __maybe_unused unset) | ||
1892 | { | ||
1893 | char unit = 'n'; | ||
1894 | u64 *value = opt->value; | ||
1895 | |||
1896 | if (sscanf(arg, "%" PRIu64 "%cs", value, &unit) > 0) { | ||
1897 | switch (unit) { | ||
1898 | case 'm': | ||
1899 | *value *= 1000000; | ||
1900 | break; | ||
1901 | case 'u': | ||
1902 | *value *= 1000; | ||
1903 | break; | ||
1904 | case 'n': | ||
1905 | break; | ||
1906 | default: | ||
1907 | return -1; | ||
1908 | } | ||
1909 | } | ||
1910 | |||
1911 | return 0; | ||
1912 | } | ||
1913 | |||
1273 | int cmd_timechart(int argc, const char **argv, | 1914 | int cmd_timechart(int argc, const char **argv, |
1274 | const char *prefix __maybe_unused) | 1915 | const char *prefix __maybe_unused) |
1275 | { | 1916 | { |
@@ -1282,6 +1923,8 @@ int cmd_timechart(int argc, const char **argv, | |||
1282 | .ordered_samples = true, | 1923 | .ordered_samples = true, |
1283 | }, | 1924 | }, |
1284 | .proc_num = 15, | 1925 | .proc_num = 15, |
1926 | .min_time = 1000000, | ||
1927 | .merge_dist = 1000, | ||
1285 | }; | 1928 | }; |
1286 | const char *output_name = "output.svg"; | 1929 | const char *output_name = "output.svg"; |
1287 | const struct option timechart_options[] = { | 1930 | const struct option timechart_options[] = { |
@@ -1303,6 +1946,14 @@ int cmd_timechart(int argc, const char **argv, | |||
1303 | "min. number of tasks to print"), | 1946 | "min. number of tasks to print"), |
1304 | OPT_BOOLEAN('t', "topology", &tchart.topology, | 1947 | OPT_BOOLEAN('t', "topology", &tchart.topology, |
1305 | "sort CPUs according to topology"), | 1948 | "sort CPUs according to topology"), |
1949 | OPT_BOOLEAN(0, "io-skip-eagain", &tchart.skip_eagain, | ||
1950 | "skip EAGAIN errors"), | ||
1951 | OPT_CALLBACK(0, "io-min-time", &tchart.min_time, "time", | ||
1952 | "all IO faster than min-time will visually appear longer", | ||
1953 | parse_time), | ||
1954 | OPT_CALLBACK(0, "io-merge-dist", &tchart.merge_dist, "time", | ||
1955 | "merge events that are merge-dist us apart", | ||
1956 | parse_time), | ||
1306 | OPT_END() | 1957 | OPT_END() |
1307 | }; | 1958 | }; |
1308 | const char * const timechart_usage[] = { | 1959 | const char * const timechart_usage[] = { |
@@ -1314,6 +1965,8 @@ int cmd_timechart(int argc, const char **argv, | |||
1314 | OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"), | 1965 | OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"), |
1315 | OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, | 1966 | OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, |
1316 | "output processes data only"), | 1967 | "output processes data only"), |
1968 | OPT_BOOLEAN('I', "io-only", &tchart.io_only, | ||
1969 | "record only IO data"), | ||
1317 | OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"), | 1970 | OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"), |
1318 | OPT_END() | 1971 | OPT_END() |
1319 | }; | 1972 | }; |
@@ -1340,7 +1993,10 @@ int cmd_timechart(int argc, const char **argv, | |||
1340 | return -1; | 1993 | return -1; |
1341 | } | 1994 | } |
1342 | 1995 | ||
1343 | return timechart__record(&tchart, argc, argv); | 1996 | if (tchart.io_only) |
1997 | return timechart__io_record(argc, argv); | ||
1998 | else | ||
1999 | return timechart__record(&tchart, argc, argv); | ||
1344 | } else if (argc) | 2000 | } else if (argc) |
1345 | usage_with_options(timechart_usage, timechart_options); | 2001 | usage_with_options(timechart_usage, timechart_options); |
1346 | 2002 | ||