diff options
Diffstat (limited to 'tools/perf/builtin-timechart.c')
-rw-r--r-- | tools/perf/builtin-timechart.c | 620 |
1 files changed, 602 insertions, 18 deletions
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 74db2568b867..4079062d25b0 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
@@ -60,10 +60,14 @@ struct timechart { | |||
60 | tasks_only, | 60 | tasks_only, |
61 | with_backtrace, | 61 | with_backtrace, |
62 | topology; | 62 | topology; |
63 | /* IO related settings */ | ||
64 | u64 io_events; | ||
65 | bool io_only; | ||
63 | }; | 66 | }; |
64 | 67 | ||
65 | struct per_pidcomm; | 68 | struct per_pidcomm; |
66 | struct cpu_sample; | 69 | struct cpu_sample; |
70 | struct io_sample; | ||
67 | 71 | ||
68 | /* | 72 | /* |
69 | * Datastructure layout: | 73 | * Datastructure layout: |
@@ -84,6 +88,7 @@ struct per_pid { | |||
84 | u64 start_time; | 88 | u64 start_time; |
85 | u64 end_time; | 89 | u64 end_time; |
86 | u64 total_time; | 90 | u64 total_time; |
91 | u64 total_bytes; | ||
87 | int display; | 92 | int display; |
88 | 93 | ||
89 | struct per_pidcomm *all; | 94 | struct per_pidcomm *all; |
@@ -97,6 +102,8 @@ struct per_pidcomm { | |||
97 | u64 start_time; | 102 | u64 start_time; |
98 | u64 end_time; | 103 | u64 end_time; |
99 | u64 total_time; | 104 | u64 total_time; |
105 | u64 max_bytes; | ||
106 | u64 total_bytes; | ||
100 | 107 | ||
101 | int Y; | 108 | int Y; |
102 | int display; | 109 | int display; |
@@ -107,6 +114,7 @@ struct per_pidcomm { | |||
107 | char *comm; | 114 | char *comm; |
108 | 115 | ||
109 | struct cpu_sample *samples; | 116 | struct cpu_sample *samples; |
117 | struct io_sample *io_samples; | ||
110 | }; | 118 | }; |
111 | 119 | ||
112 | struct sample_wrapper { | 120 | struct sample_wrapper { |
@@ -131,6 +139,27 @@ struct cpu_sample { | |||
131 | const char *backtrace; | 139 | const char *backtrace; |
132 | }; | 140 | }; |
133 | 141 | ||
142 | enum { | ||
143 | IOTYPE_READ, | ||
144 | IOTYPE_WRITE, | ||
145 | IOTYPE_SYNC, | ||
146 | IOTYPE_TX, | ||
147 | IOTYPE_RX, | ||
148 | IOTYPE_POLL, | ||
149 | }; | ||
150 | |||
151 | struct io_sample { | ||
152 | struct io_sample *next; | ||
153 | |||
154 | u64 start_time; | ||
155 | u64 end_time; | ||
156 | u64 bytes; | ||
157 | int type; | ||
158 | int fd; | ||
159 | int err; | ||
160 | int merges; | ||
161 | }; | ||
162 | |||
134 | #define CSTATE 1 | 163 | #define CSTATE 1 |
135 | #define PSTATE 2 | 164 | #define PSTATE 2 |
136 | 165 | ||
@@ -682,6 +711,219 @@ static void end_sample_processing(struct timechart *tchart) | |||
682 | } | 711 | } |
683 | } | 712 | } |
684 | 713 | ||
714 | static int pid_begin_io_sample(struct timechart *tchart, int pid, int type, | ||
715 | u64 start, int fd) | ||
716 | { | ||
717 | struct per_pid *p = find_create_pid(tchart, pid); | ||
718 | struct per_pidcomm *c = p->current; | ||
719 | struct io_sample *sample; | ||
720 | struct io_sample *prev; | ||
721 | |||
722 | if (!c) { | ||
723 | c = zalloc(sizeof(*c)); | ||
724 | if (!c) | ||
725 | return -ENOMEM; | ||
726 | p->current = c; | ||
727 | c->next = p->all; | ||
728 | p->all = c; | ||
729 | } | ||
730 | |||
731 | prev = c->io_samples; | ||
732 | |||
733 | if (prev && prev->start_time && !prev->end_time) { | ||
734 | pr_warning("Skip invalid start event: " | ||
735 | "previous event already started!\n"); | ||
736 | |||
737 | /* remove previous event that has been started, | ||
738 | * we are not sure we will ever get an end for it */ | ||
739 | c->io_samples = prev->next; | ||
740 | free(prev); | ||
741 | return 0; | ||
742 | } | ||
743 | |||
744 | sample = zalloc(sizeof(*sample)); | ||
745 | if (!sample) | ||
746 | return -ENOMEM; | ||
747 | sample->start_time = start; | ||
748 | sample->type = type; | ||
749 | sample->fd = fd; | ||
750 | sample->next = c->io_samples; | ||
751 | c->io_samples = sample; | ||
752 | |||
753 | if (c->start_time == 0 || c->start_time > start) | ||
754 | c->start_time = start; | ||
755 | |||
756 | return 0; | ||
757 | } | ||
758 | |||
759 | static int pid_end_io_sample(struct timechart *tchart, int pid, int type, | ||
760 | u64 end, long ret) | ||
761 | { | ||
762 | struct per_pid *p = find_create_pid(tchart, pid); | ||
763 | struct per_pidcomm *c = p->current; | ||
764 | struct io_sample *sample; | ||
765 | |||
766 | if (!c) { | ||
767 | pr_warning("Invalid pidcomm!\n"); | ||
768 | return -1; | ||
769 | } | ||
770 | |||
771 | sample = c->io_samples; | ||
772 | |||
773 | if (!sample) /* skip partially captured events */ | ||
774 | return 0; | ||
775 | |||
776 | if (sample->end_time) { | ||
777 | pr_warning("Skip invalid end event: " | ||
778 | "previous event already ended!\n"); | ||
779 | return 0; | ||
780 | } | ||
781 | |||
782 | if (sample->type != type) { | ||
783 | pr_warning("Skip invalid end event: invalid event type!\n"); | ||
784 | return 0; | ||
785 | } | ||
786 | |||
787 | sample->end_time = end; | ||
788 | |||
789 | if (ret < 0) { | ||
790 | sample->err = ret; | ||
791 | } else if (type == IOTYPE_READ || type == IOTYPE_WRITE || | ||
792 | type == IOTYPE_TX || type == IOTYPE_RX) { | ||
793 | |||
794 | if ((u64)ret > c->max_bytes) | ||
795 | c->max_bytes = ret; | ||
796 | |||
797 | c->total_bytes += ret; | ||
798 | p->total_bytes += ret; | ||
799 | sample->bytes = ret; | ||
800 | } | ||
801 | |||
802 | tchart->io_events++; | ||
803 | |||
804 | return 0; | ||
805 | } | ||
806 | |||
807 | static int | ||
808 | process_enter_read(struct timechart *tchart, | ||
809 | struct perf_evsel *evsel, | ||
810 | struct perf_sample *sample) | ||
811 | { | ||
812 | long fd = perf_evsel__intval(evsel, sample, "fd"); | ||
813 | return pid_begin_io_sample(tchart, sample->tid, IOTYPE_READ, | ||
814 | sample->time, fd); | ||
815 | } | ||
816 | |||
817 | static int | ||
818 | process_exit_read(struct timechart *tchart, | ||
819 | struct perf_evsel *evsel, | ||
820 | struct perf_sample *sample) | ||
821 | { | ||
822 | long ret = perf_evsel__intval(evsel, sample, "ret"); | ||
823 | return pid_end_io_sample(tchart, sample->tid, IOTYPE_READ, | ||
824 | sample->time, ret); | ||
825 | } | ||
826 | |||
827 | static int | ||
828 | process_enter_write(struct timechart *tchart, | ||
829 | struct perf_evsel *evsel, | ||
830 | struct perf_sample *sample) | ||
831 | { | ||
832 | long fd = perf_evsel__intval(evsel, sample, "fd"); | ||
833 | return pid_begin_io_sample(tchart, sample->tid, IOTYPE_WRITE, | ||
834 | sample->time, fd); | ||
835 | } | ||
836 | |||
837 | static int | ||
838 | process_exit_write(struct timechart *tchart, | ||
839 | struct perf_evsel *evsel, | ||
840 | struct perf_sample *sample) | ||
841 | { | ||
842 | long ret = perf_evsel__intval(evsel, sample, "ret"); | ||
843 | return pid_end_io_sample(tchart, sample->tid, IOTYPE_WRITE, | ||
844 | sample->time, ret); | ||
845 | } | ||
846 | |||
847 | static int | ||
848 | process_enter_sync(struct timechart *tchart, | ||
849 | struct perf_evsel *evsel, | ||
850 | struct perf_sample *sample) | ||
851 | { | ||
852 | long fd = perf_evsel__intval(evsel, sample, "fd"); | ||
853 | return pid_begin_io_sample(tchart, sample->tid, IOTYPE_SYNC, | ||
854 | sample->time, fd); | ||
855 | } | ||
856 | |||
857 | static int | ||
858 | process_exit_sync(struct timechart *tchart, | ||
859 | struct perf_evsel *evsel, | ||
860 | struct perf_sample *sample) | ||
861 | { | ||
862 | long ret = perf_evsel__intval(evsel, sample, "ret"); | ||
863 | return pid_end_io_sample(tchart, sample->tid, IOTYPE_SYNC, | ||
864 | sample->time, ret); | ||
865 | } | ||
866 | |||
867 | static int | ||
868 | process_enter_tx(struct timechart *tchart, | ||
869 | struct perf_evsel *evsel, | ||
870 | struct perf_sample *sample) | ||
871 | { | ||
872 | long fd = perf_evsel__intval(evsel, sample, "fd"); | ||
873 | return pid_begin_io_sample(tchart, sample->tid, IOTYPE_TX, | ||
874 | sample->time, fd); | ||
875 | } | ||
876 | |||
877 | static int | ||
878 | process_exit_tx(struct timechart *tchart, | ||
879 | struct perf_evsel *evsel, | ||
880 | struct perf_sample *sample) | ||
881 | { | ||
882 | long ret = perf_evsel__intval(evsel, sample, "ret"); | ||
883 | return pid_end_io_sample(tchart, sample->tid, IOTYPE_TX, | ||
884 | sample->time, ret); | ||
885 | } | ||
886 | |||
887 | static int | ||
888 | process_enter_rx(struct timechart *tchart, | ||
889 | struct perf_evsel *evsel, | ||
890 | struct perf_sample *sample) | ||
891 | { | ||
892 | long fd = perf_evsel__intval(evsel, sample, "fd"); | ||
893 | return pid_begin_io_sample(tchart, sample->tid, IOTYPE_RX, | ||
894 | sample->time, fd); | ||
895 | } | ||
896 | |||
897 | static int | ||
898 | process_exit_rx(struct timechart *tchart, | ||
899 | struct perf_evsel *evsel, | ||
900 | struct perf_sample *sample) | ||
901 | { | ||
902 | long ret = perf_evsel__intval(evsel, sample, "ret"); | ||
903 | return pid_end_io_sample(tchart, sample->tid, IOTYPE_RX, | ||
904 | sample->time, ret); | ||
905 | } | ||
906 | |||
907 | static int | ||
908 | process_enter_poll(struct timechart *tchart, | ||
909 | struct perf_evsel *evsel, | ||
910 | struct perf_sample *sample) | ||
911 | { | ||
912 | long fd = perf_evsel__intval(evsel, sample, "fd"); | ||
913 | return pid_begin_io_sample(tchart, sample->tid, IOTYPE_POLL, | ||
914 | sample->time, fd); | ||
915 | } | ||
916 | |||
917 | static int | ||
918 | process_exit_poll(struct timechart *tchart, | ||
919 | struct perf_evsel *evsel, | ||
920 | struct perf_sample *sample) | ||
921 | { | ||
922 | long ret = perf_evsel__intval(evsel, sample, "ret"); | ||
923 | return pid_end_io_sample(tchart, sample->tid, IOTYPE_POLL, | ||
924 | sample->time, ret); | ||
925 | } | ||
926 | |||
685 | /* | 927 | /* |
686 | * Sort the pid datastructure | 928 | * Sort the pid datastructure |
687 | */ | 929 | */ |
@@ -852,6 +1094,117 @@ static void draw_cpu_usage(struct timechart *tchart) | |||
852 | } | 1094 | } |
853 | } | 1095 | } |
854 | 1096 | ||
1097 | static void draw_io_bars(struct timechart *tchart) | ||
1098 | { | ||
1099 | const char *suf; | ||
1100 | double bytes; | ||
1101 | char comm[256]; | ||
1102 | struct per_pid *p; | ||
1103 | struct per_pidcomm *c; | ||
1104 | struct io_sample *sample; | ||
1105 | int Y = 1; | ||
1106 | |||
1107 | p = tchart->all_data; | ||
1108 | while (p) { | ||
1109 | c = p->all; | ||
1110 | while (c) { | ||
1111 | if (!c->display) { | ||
1112 | c->Y = 0; | ||
1113 | c = c->next; | ||
1114 | continue; | ||
1115 | } | ||
1116 | |||
1117 | svg_box(Y, c->start_time, c->end_time, "process3"); | ||
1118 | sample = c->io_samples; | ||
1119 | for (sample = c->io_samples; sample; sample = sample->next) { | ||
1120 | double h = (double)sample->bytes / c->max_bytes; | ||
1121 | |||
1122 | if (sample->err) | ||
1123 | h = 1; | ||
1124 | |||
1125 | if (sample->type == IOTYPE_SYNC) | ||
1126 | svg_fbox(Y, | ||
1127 | sample->start_time, | ||
1128 | sample->end_time, | ||
1129 | 1, | ||
1130 | sample->err ? "error" : "sync", | ||
1131 | sample->fd, | ||
1132 | sample->err, | ||
1133 | sample->merges); | ||
1134 | else if (sample->type == IOTYPE_POLL) | ||
1135 | svg_fbox(Y, | ||
1136 | sample->start_time, | ||
1137 | sample->end_time, | ||
1138 | 1, | ||
1139 | sample->err ? "error" : "poll", | ||
1140 | sample->fd, | ||
1141 | sample->err, | ||
1142 | sample->merges); | ||
1143 | else if (sample->type == IOTYPE_READ) | ||
1144 | svg_ubox(Y, | ||
1145 | sample->start_time, | ||
1146 | sample->end_time, | ||
1147 | h, | ||
1148 | sample->err ? "error" : "disk", | ||
1149 | sample->fd, | ||
1150 | sample->err, | ||
1151 | sample->merges); | ||
1152 | else if (sample->type == IOTYPE_WRITE) | ||
1153 | svg_lbox(Y, | ||
1154 | sample->start_time, | ||
1155 | sample->end_time, | ||
1156 | h, | ||
1157 | sample->err ? "error" : "disk", | ||
1158 | sample->fd, | ||
1159 | sample->err, | ||
1160 | sample->merges); | ||
1161 | else if (sample->type == IOTYPE_RX) | ||
1162 | svg_ubox(Y, | ||
1163 | sample->start_time, | ||
1164 | sample->end_time, | ||
1165 | h, | ||
1166 | sample->err ? "error" : "net", | ||
1167 | sample->fd, | ||
1168 | sample->err, | ||
1169 | sample->merges); | ||
1170 | else if (sample->type == IOTYPE_TX) | ||
1171 | svg_lbox(Y, | ||
1172 | sample->start_time, | ||
1173 | sample->end_time, | ||
1174 | h, | ||
1175 | sample->err ? "error" : "net", | ||
1176 | sample->fd, | ||
1177 | sample->err, | ||
1178 | sample->merges); | ||
1179 | } | ||
1180 | |||
1181 | suf = ""; | ||
1182 | bytes = c->total_bytes; | ||
1183 | if (bytes > 1024) { | ||
1184 | bytes = bytes / 1024; | ||
1185 | suf = "K"; | ||
1186 | } | ||
1187 | if (bytes > 1024) { | ||
1188 | bytes = bytes / 1024; | ||
1189 | suf = "M"; | ||
1190 | } | ||
1191 | if (bytes > 1024) { | ||
1192 | bytes = bytes / 1024; | ||
1193 | suf = "G"; | ||
1194 | } | ||
1195 | |||
1196 | |||
1197 | sprintf(comm, "%s:%i (%3.1f %sbytes)", c->comm ?: "", p->pid, bytes, suf); | ||
1198 | svg_text(Y, c->start_time, comm); | ||
1199 | |||
1200 | c->Y = Y; | ||
1201 | Y++; | ||
1202 | c = c->next; | ||
1203 | } | ||
1204 | p = p->next; | ||
1205 | } | ||
1206 | } | ||
1207 | |||
855 | static void draw_process_bars(struct timechart *tchart) | 1208 | static void draw_process_bars(struct timechart *tchart) |
856 | { | 1209 | { |
857 | struct per_pid *p; | 1210 | struct per_pid *p; |
@@ -987,9 +1340,6 @@ static int determine_display_tasks(struct timechart *tchart, u64 threshold) | |||
987 | struct per_pidcomm *c; | 1340 | struct per_pidcomm *c; |
988 | int count = 0; | 1341 | int count = 0; |
989 | 1342 | ||
990 | if (process_filter) | ||
991 | return determine_display_tasks_filtered(tchart); | ||
992 | |||
993 | p = tchart->all_data; | 1343 | p = tchart->all_data; |
994 | while (p) { | 1344 | while (p) { |
995 | p->display = 0; | 1345 | p->display = 0; |
@@ -1025,15 +1375,46 @@ static int determine_display_tasks(struct timechart *tchart, u64 threshold) | |||
1025 | return count; | 1375 | return count; |
1026 | } | 1376 | } |
1027 | 1377 | ||
1378 | static int determine_display_io_tasks(struct timechart *timechart, u64 threshold) | ||
1379 | { | ||
1380 | struct per_pid *p; | ||
1381 | struct per_pidcomm *c; | ||
1382 | int count = 0; | ||
1383 | |||
1384 | p = timechart->all_data; | ||
1385 | while (p) { | ||
1386 | /* no exit marker, task kept running to the end */ | ||
1387 | if (p->end_time == 0) | ||
1388 | p->end_time = timechart->last_time; | ||
1389 | |||
1390 | c = p->all; | ||
1391 | |||
1392 | while (c) { | ||
1393 | c->display = 0; | ||
1394 | |||
1395 | if (c->total_bytes >= threshold) { | ||
1396 | c->display = 1; | ||
1397 | count++; | ||
1398 | } | ||
1028 | 1399 | ||
1400 | if (c->end_time == 0) | ||
1401 | c->end_time = timechart->last_time; | ||
1402 | |||
1403 | c = c->next; | ||
1404 | } | ||
1405 | p = p->next; | ||
1406 | } | ||
1407 | return count; | ||
1408 | } | ||
1029 | 1409 | ||
1410 | #define BYTES_THRESH (1 * 1024 * 1024) | ||
1030 | #define TIME_THRESH 10000000 | 1411 | #define TIME_THRESH 10000000 |
1031 | 1412 | ||
1032 | static void write_svg_file(struct timechart *tchart, const char *filename) | 1413 | static void write_svg_file(struct timechart *tchart, const char *filename) |
1033 | { | 1414 | { |
1034 | u64 i; | 1415 | u64 i; |
1035 | int count; | 1416 | int count; |
1036 | int thresh = TIME_THRESH; | 1417 | int thresh = tchart->io_events ? BYTES_THRESH : TIME_THRESH; |
1037 | 1418 | ||
1038 | if (tchart->power_only) | 1419 | if (tchart->power_only) |
1039 | tchart->proc_num = 0; | 1420 | tchart->proc_num = 0; |
@@ -1041,28 +1422,43 @@ static void write_svg_file(struct timechart *tchart, const char *filename) | |||
1041 | /* We'd like to show at least proc_num tasks; | 1422 | /* We'd like to show at least proc_num tasks; |
1042 | * be less picky if we have fewer */ | 1423 | * be less picky if we have fewer */ |
1043 | do { | 1424 | do { |
1044 | count = determine_display_tasks(tchart, thresh); | 1425 | if (process_filter) |
1426 | count = determine_display_tasks_filtered(tchart); | ||
1427 | else if (tchart->io_events) | ||
1428 | count = determine_display_io_tasks(tchart, thresh); | ||
1429 | else | ||
1430 | count = determine_display_tasks(tchart, thresh); | ||
1045 | thresh /= 10; | 1431 | thresh /= 10; |
1046 | } while (!process_filter && thresh && count < tchart->proc_num); | 1432 | } while (!process_filter && thresh && count < tchart->proc_num); |
1047 | 1433 | ||
1048 | if (!tchart->proc_num) | 1434 | if (!tchart->proc_num) |
1049 | count = 0; | 1435 | count = 0; |
1050 | 1436 | ||
1051 | open_svg(filename, tchart->numcpus, count, tchart->first_time, tchart->last_time); | 1437 | if (tchart->io_events) { |
1438 | open_svg(filename, 0, count, tchart->first_time, tchart->last_time); | ||
1052 | 1439 | ||
1053 | svg_time_grid(); | 1440 | svg_time_grid(0.5); |
1054 | svg_legenda(); | 1441 | svg_io_legenda(); |
1055 | 1442 | ||
1056 | for (i = 0; i < tchart->numcpus; i++) | 1443 | draw_io_bars(tchart); |
1057 | svg_cpu_box(i, tchart->max_freq, tchart->turbo_frequency); | 1444 | } else { |
1445 | open_svg(filename, tchart->numcpus, count, tchart->first_time, tchart->last_time); | ||
1058 | 1446 | ||
1059 | draw_cpu_usage(tchart); | 1447 | svg_time_grid(0); |
1060 | if (tchart->proc_num) | 1448 | |
1061 | draw_process_bars(tchart); | 1449 | svg_legenda(); |
1062 | if (!tchart->tasks_only) | 1450 | |
1063 | draw_c_p_states(tchart); | 1451 | for (i = 0; i < tchart->numcpus; i++) |
1064 | if (tchart->proc_num) | 1452 | svg_cpu_box(i, tchart->max_freq, tchart->turbo_frequency); |
1065 | draw_wakeups(tchart); | 1453 | |
1454 | draw_cpu_usage(tchart); | ||
1455 | if (tchart->proc_num) | ||
1456 | draw_process_bars(tchart); | ||
1457 | if (!tchart->tasks_only) | ||
1458 | draw_c_p_states(tchart); | ||
1459 | if (tchart->proc_num) | ||
1460 | draw_wakeups(tchart); | ||
1461 | } | ||
1066 | 1462 | ||
1067 | svg_close(); | 1463 | svg_close(); |
1068 | } | 1464 | } |
@@ -1110,6 +1506,56 @@ static int __cmd_timechart(struct timechart *tchart, const char *output_name) | |||
1110 | { "power:power_end", process_sample_power_end }, | 1506 | { "power:power_end", process_sample_power_end }, |
1111 | { "power:power_frequency", process_sample_power_frequency }, | 1507 | { "power:power_frequency", process_sample_power_frequency }, |
1112 | #endif | 1508 | #endif |
1509 | |||
1510 | { "syscalls:sys_enter_read", process_enter_read }, | ||
1511 | { "syscalls:sys_enter_pread64", process_enter_read }, | ||
1512 | { "syscalls:sys_enter_readv", process_enter_read }, | ||
1513 | { "syscalls:sys_enter_preadv", process_enter_read }, | ||
1514 | { "syscalls:sys_enter_write", process_enter_write }, | ||
1515 | { "syscalls:sys_enter_pwrite64", process_enter_write }, | ||
1516 | { "syscalls:sys_enter_writev", process_enter_write }, | ||
1517 | { "syscalls:sys_enter_pwritev", process_enter_write }, | ||
1518 | { "syscalls:sys_enter_sync", process_enter_sync }, | ||
1519 | { "syscalls:sys_enter_sync_file_range", process_enter_sync }, | ||
1520 | { "syscalls:sys_enter_fsync", process_enter_sync }, | ||
1521 | { "syscalls:sys_enter_msync", process_enter_sync }, | ||
1522 | { "syscalls:sys_enter_recvfrom", process_enter_rx }, | ||
1523 | { "syscalls:sys_enter_recvmmsg", process_enter_rx }, | ||
1524 | { "syscalls:sys_enter_recvmsg", process_enter_rx }, | ||
1525 | { "syscalls:sys_enter_sendto", process_enter_tx }, | ||
1526 | { "syscalls:sys_enter_sendmsg", process_enter_tx }, | ||
1527 | { "syscalls:sys_enter_sendmmsg", process_enter_tx }, | ||
1528 | { "syscalls:sys_enter_epoll_pwait", process_enter_poll }, | ||
1529 | { "syscalls:sys_enter_epoll_wait", process_enter_poll }, | ||
1530 | { "syscalls:sys_enter_poll", process_enter_poll }, | ||
1531 | { "syscalls:sys_enter_ppoll", process_enter_poll }, | ||
1532 | { "syscalls:sys_enter_pselect6", process_enter_poll }, | ||
1533 | { "syscalls:sys_enter_select", process_enter_poll }, | ||
1534 | |||
1535 | { "syscalls:sys_exit_read", process_exit_read }, | ||
1536 | { "syscalls:sys_exit_pread64", process_exit_read }, | ||
1537 | { "syscalls:sys_exit_readv", process_exit_read }, | ||
1538 | { "syscalls:sys_exit_preadv", process_exit_read }, | ||
1539 | { "syscalls:sys_exit_write", process_exit_write }, | ||
1540 | { "syscalls:sys_exit_pwrite64", process_exit_write }, | ||
1541 | { "syscalls:sys_exit_writev", process_exit_write }, | ||
1542 | { "syscalls:sys_exit_pwritev", process_exit_write }, | ||
1543 | { "syscalls:sys_exit_sync", process_exit_sync }, | ||
1544 | { "syscalls:sys_exit_sync_file_range", process_exit_sync }, | ||
1545 | { "syscalls:sys_exit_fsync", process_exit_sync }, | ||
1546 | { "syscalls:sys_exit_msync", process_exit_sync }, | ||
1547 | { "syscalls:sys_exit_recvfrom", process_exit_rx }, | ||
1548 | { "syscalls:sys_exit_recvmmsg", process_exit_rx }, | ||
1549 | { "syscalls:sys_exit_recvmsg", process_exit_rx }, | ||
1550 | { "syscalls:sys_exit_sendto", process_exit_tx }, | ||
1551 | { "syscalls:sys_exit_sendmsg", process_exit_tx }, | ||
1552 | { "syscalls:sys_exit_sendmmsg", process_exit_tx }, | ||
1553 | { "syscalls:sys_exit_epoll_pwait", process_exit_poll }, | ||
1554 | { "syscalls:sys_exit_epoll_wait", process_exit_poll }, | ||
1555 | { "syscalls:sys_exit_poll", process_exit_poll }, | ||
1556 | { "syscalls:sys_exit_ppoll", process_exit_poll }, | ||
1557 | { "syscalls:sys_exit_pselect6", process_exit_poll }, | ||
1558 | { "syscalls:sys_exit_select", process_exit_poll }, | ||
1113 | }; | 1559 | }; |
1114 | struct perf_data_file file = { | 1560 | struct perf_data_file file = { |
1115 | .path = input_name, | 1561 | .path = input_name, |
@@ -1154,6 +1600,139 @@ out_delete: | |||
1154 | return ret; | 1600 | return ret; |
1155 | } | 1601 | } |
1156 | 1602 | ||
1603 | static int timechart__io_record(int argc, const char **argv) | ||
1604 | { | ||
1605 | unsigned int rec_argc, i; | ||
1606 | const char **rec_argv; | ||
1607 | const char **p; | ||
1608 | char *filter = NULL; | ||
1609 | |||
1610 | const char * const common_args[] = { | ||
1611 | "record", "-a", "-R", "-c", "1", | ||
1612 | }; | ||
1613 | unsigned int common_args_nr = ARRAY_SIZE(common_args); | ||
1614 | |||
1615 | const char * const disk_events[] = { | ||
1616 | "syscalls:sys_enter_read", | ||
1617 | "syscalls:sys_enter_pread64", | ||
1618 | "syscalls:sys_enter_readv", | ||
1619 | "syscalls:sys_enter_preadv", | ||
1620 | "syscalls:sys_enter_write", | ||
1621 | "syscalls:sys_enter_pwrite64", | ||
1622 | "syscalls:sys_enter_writev", | ||
1623 | "syscalls:sys_enter_pwritev", | ||
1624 | "syscalls:sys_enter_sync", | ||
1625 | "syscalls:sys_enter_sync_file_range", | ||
1626 | "syscalls:sys_enter_fsync", | ||
1627 | "syscalls:sys_enter_msync", | ||
1628 | |||
1629 | "syscalls:sys_exit_read", | ||
1630 | "syscalls:sys_exit_pread64", | ||
1631 | "syscalls:sys_exit_readv", | ||
1632 | "syscalls:sys_exit_preadv", | ||
1633 | "syscalls:sys_exit_write", | ||
1634 | "syscalls:sys_exit_pwrite64", | ||
1635 | "syscalls:sys_exit_writev", | ||
1636 | "syscalls:sys_exit_pwritev", | ||
1637 | "syscalls:sys_exit_sync", | ||
1638 | "syscalls:sys_exit_sync_file_range", | ||
1639 | "syscalls:sys_exit_fsync", | ||
1640 | "syscalls:sys_exit_msync", | ||
1641 | }; | ||
1642 | unsigned int disk_events_nr = ARRAY_SIZE(disk_events); | ||
1643 | |||
1644 | const char * const net_events[] = { | ||
1645 | "syscalls:sys_enter_recvfrom", | ||
1646 | "syscalls:sys_enter_recvmmsg", | ||
1647 | "syscalls:sys_enter_recvmsg", | ||
1648 | "syscalls:sys_enter_sendto", | ||
1649 | "syscalls:sys_enter_sendmsg", | ||
1650 | "syscalls:sys_enter_sendmmsg", | ||
1651 | |||
1652 | "syscalls:sys_exit_recvfrom", | ||
1653 | "syscalls:sys_exit_recvmmsg", | ||
1654 | "syscalls:sys_exit_recvmsg", | ||
1655 | "syscalls:sys_exit_sendto", | ||
1656 | "syscalls:sys_exit_sendmsg", | ||
1657 | "syscalls:sys_exit_sendmmsg", | ||
1658 | }; | ||
1659 | unsigned int net_events_nr = ARRAY_SIZE(net_events); | ||
1660 | |||
1661 | const char * const poll_events[] = { | ||
1662 | "syscalls:sys_enter_epoll_pwait", | ||
1663 | "syscalls:sys_enter_epoll_wait", | ||
1664 | "syscalls:sys_enter_poll", | ||
1665 | "syscalls:sys_enter_ppoll", | ||
1666 | "syscalls:sys_enter_pselect6", | ||
1667 | "syscalls:sys_enter_select", | ||
1668 | |||
1669 | "syscalls:sys_exit_epoll_pwait", | ||
1670 | "syscalls:sys_exit_epoll_wait", | ||
1671 | "syscalls:sys_exit_poll", | ||
1672 | "syscalls:sys_exit_ppoll", | ||
1673 | "syscalls:sys_exit_pselect6", | ||
1674 | "syscalls:sys_exit_select", | ||
1675 | }; | ||
1676 | unsigned int poll_events_nr = ARRAY_SIZE(poll_events); | ||
1677 | |||
1678 | rec_argc = common_args_nr + | ||
1679 | disk_events_nr * 4 + | ||
1680 | net_events_nr * 4 + | ||
1681 | poll_events_nr * 4 + | ||
1682 | argc; | ||
1683 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | ||
1684 | |||
1685 | if (rec_argv == NULL) | ||
1686 | return -ENOMEM; | ||
1687 | |||
1688 | if (asprintf(&filter, "common_pid != %d", getpid()) < 0) | ||
1689 | return -ENOMEM; | ||
1690 | |||
1691 | p = rec_argv; | ||
1692 | for (i = 0; i < common_args_nr; i++) | ||
1693 | *p++ = strdup(common_args[i]); | ||
1694 | |||
1695 | for (i = 0; i < disk_events_nr; i++) { | ||
1696 | if (!is_valid_tracepoint(disk_events[i])) { | ||
1697 | rec_argc -= 4; | ||
1698 | continue; | ||
1699 | } | ||
1700 | |||
1701 | *p++ = "-e"; | ||
1702 | *p++ = strdup(disk_events[i]); | ||
1703 | *p++ = "--filter"; | ||
1704 | *p++ = filter; | ||
1705 | } | ||
1706 | for (i = 0; i < net_events_nr; i++) { | ||
1707 | if (!is_valid_tracepoint(net_events[i])) { | ||
1708 | rec_argc -= 4; | ||
1709 | continue; | ||
1710 | } | ||
1711 | |||
1712 | *p++ = "-e"; | ||
1713 | *p++ = strdup(net_events[i]); | ||
1714 | *p++ = "--filter"; | ||
1715 | *p++ = filter; | ||
1716 | } | ||
1717 | for (i = 0; i < poll_events_nr; i++) { | ||
1718 | if (!is_valid_tracepoint(poll_events[i])) { | ||
1719 | rec_argc -= 4; | ||
1720 | continue; | ||
1721 | } | ||
1722 | |||
1723 | *p++ = "-e"; | ||
1724 | *p++ = strdup(poll_events[i]); | ||
1725 | *p++ = "--filter"; | ||
1726 | *p++ = filter; | ||
1727 | } | ||
1728 | |||
1729 | for (i = 0; i < (unsigned int)argc; i++) | ||
1730 | *p++ = argv[i]; | ||
1731 | |||
1732 | return cmd_record(rec_argc, rec_argv, NULL); | ||
1733 | } | ||
1734 | |||
1735 | |||
1157 | static int timechart__record(struct timechart *tchart, int argc, const char **argv) | 1736 | static int timechart__record(struct timechart *tchart, int argc, const char **argv) |
1158 | { | 1737 | { |
1159 | unsigned int rec_argc, i, j; | 1738 | unsigned int rec_argc, i, j; |
@@ -1314,6 +1893,8 @@ int cmd_timechart(int argc, const char **argv, | |||
1314 | OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"), | 1893 | OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"), |
1315 | OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, | 1894 | OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, |
1316 | "output processes data only"), | 1895 | "output processes data only"), |
1896 | OPT_BOOLEAN('I', "io-only", &tchart.io_only, | ||
1897 | "record only IO data"), | ||
1317 | OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"), | 1898 | OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"), |
1318 | OPT_END() | 1899 | OPT_END() |
1319 | }; | 1900 | }; |
@@ -1340,7 +1921,10 @@ int cmd_timechart(int argc, const char **argv, | |||
1340 | return -1; | 1921 | return -1; |
1341 | } | 1922 | } |
1342 | 1923 | ||
1343 | return timechart__record(&tchart, argc, argv); | 1924 | if (tchart.io_only) |
1925 | return timechart__io_record(argc, argv); | ||
1926 | else | ||
1927 | return timechart__record(&tchart, argc, argv); | ||
1344 | } else if (argc) | 1928 | } else if (argc) |
1345 | usage_with_options(timechart_usage, timechart_options); | 1929 | usage_with_options(timechart_usage, timechart_options); |
1346 | 1930 | ||