diff options
Diffstat (limited to 'tools/perf/builtin-sched.c')
-rw-r--r-- | tools/perf/builtin-sched.c | 206 |
1 files changed, 196 insertions, 10 deletions
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 67a0ba88aecf..10fcd49e298b 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -33,6 +33,9 @@ static u64 sample_type; | |||
33 | static int replay_mode; | 33 | static int replay_mode; |
34 | static int lat_mode; | 34 | static int lat_mode; |
35 | 35 | ||
36 | static char default_sort_order[] = "avg, max, switch, runtime"; | ||
37 | static char *sort_order = default_sort_order; | ||
38 | |||
36 | 39 | ||
37 | /* | 40 | /* |
38 | * Scheduler benchmarks | 41 | * Scheduler benchmarks |
@@ -890,7 +893,17 @@ struct task_atoms { | |||
890 | u64 total_runtime; | 893 | u64 total_runtime; |
891 | }; | 894 | }; |
892 | 895 | ||
893 | static struct rb_root lat_snapshot_root; | 896 | typedef int (*sort_thread_lat)(struct task_atoms *, struct task_atoms *); |
897 | |||
898 | struct sort_dimension { | ||
899 | const char *name; | ||
900 | sort_thread_lat cmp; | ||
901 | struct list_head list; | ||
902 | }; | ||
903 | |||
904 | static LIST_HEAD(cmp_pid); | ||
905 | |||
906 | static struct rb_root lat_snapshot_root, sorted_lat_snapshot_root; | ||
894 | 907 | ||
895 | static struct task_atoms * | 908 | static struct task_atoms * |
896 | thread_atom_list_search(struct rb_root *root, struct thread *thread) | 909 | thread_atom_list_search(struct rb_root *root, struct thread *thread) |
@@ -901,9 +914,9 @@ thread_atom_list_search(struct rb_root *root, struct thread *thread) | |||
901 | struct task_atoms *atoms; | 914 | struct task_atoms *atoms; |
902 | 915 | ||
903 | atoms = container_of(node, struct task_atoms, node); | 916 | atoms = container_of(node, struct task_atoms, node); |
904 | if (thread->pid < atoms->thread->pid) | 917 | if (thread->pid > atoms->thread->pid) |
905 | node = node->rb_left; | 918 | node = node->rb_left; |
906 | else if (thread->pid > atoms->thread->pid) | 919 | else if (thread->pid < atoms->thread->pid) |
907 | node = node->rb_right; | 920 | node = node->rb_right; |
908 | else { | 921 | else { |
909 | return atoms; | 922 | return atoms; |
@@ -912,22 +925,41 @@ thread_atom_list_search(struct rb_root *root, struct thread *thread) | |||
912 | return NULL; | 925 | return NULL; |
913 | } | 926 | } |
914 | 927 | ||
928 | static int | ||
929 | thread_lat_cmp(struct list_head *list, struct task_atoms *l, | ||
930 | struct task_atoms *r) | ||
931 | { | ||
932 | struct sort_dimension *sort; | ||
933 | int ret = 0; | ||
934 | |||
935 | list_for_each_entry(sort, list, list) { | ||
936 | ret = sort->cmp(l, r); | ||
937 | if (ret) | ||
938 | return ret; | ||
939 | } | ||
940 | |||
941 | return ret; | ||
942 | } | ||
943 | |||
915 | static void | 944 | static void |
916 | __thread_latency_insert(struct rb_root *root, struct task_atoms *data) | 945 | __thread_latency_insert(struct rb_root *root, struct task_atoms *data, |
946 | struct list_head *sort_list) | ||
917 | { | 947 | { |
918 | struct rb_node **new = &(root->rb_node), *parent = NULL; | 948 | struct rb_node **new = &(root->rb_node), *parent = NULL; |
919 | 949 | ||
920 | while (*new) { | 950 | while (*new) { |
921 | struct task_atoms *this; | 951 | struct task_atoms *this; |
952 | int cmp; | ||
922 | 953 | ||
923 | this = container_of(*new, struct task_atoms, node); | 954 | this = container_of(*new, struct task_atoms, node); |
924 | parent = *new; | 955 | parent = *new; |
925 | if (data->thread->pid < this->thread->pid) | 956 | |
957 | cmp = thread_lat_cmp(sort_list, data, this); | ||
958 | |||
959 | if (cmp > 0) | ||
926 | new = &((*new)->rb_left); | 960 | new = &((*new)->rb_left); |
927 | else if (data->thread->pid > this->thread->pid) | ||
928 | new = &((*new)->rb_right); | ||
929 | else | 961 | else |
930 | die("Double thread insertion\n"); | 962 | new = &((*new)->rb_right); |
931 | } | 963 | } |
932 | 964 | ||
933 | rb_link_node(&data->node, parent, new); | 965 | rb_link_node(&data->node, parent, new); |
@@ -943,7 +975,7 @@ static void thread_atom_list_insert(struct thread *thread) | |||
943 | 975 | ||
944 | atoms->thread = thread; | 976 | atoms->thread = thread; |
945 | INIT_LIST_HEAD(&atoms->snapshot_list); | 977 | INIT_LIST_HEAD(&atoms->snapshot_list); |
946 | __thread_latency_insert(&lat_snapshot_root, atoms); | 978 | __thread_latency_insert(&lat_snapshot_root, atoms, &cmp_pid); |
947 | } | 979 | } |
948 | 980 | ||
949 | static void | 981 | static void |
@@ -1134,18 +1166,151 @@ static void output_lat_thread(struct task_atoms *atom_list) | |||
1134 | (double)atom_list->max_lat / 1e6); | 1166 | (double)atom_list->max_lat / 1e6); |
1135 | } | 1167 | } |
1136 | 1168 | ||
1169 | static int pid_cmp(struct task_atoms *l, struct task_atoms *r) | ||
1170 | { | ||
1171 | |||
1172 | if (l->thread->pid < r->thread->pid) | ||
1173 | return -1; | ||
1174 | if (l->thread->pid > r->thread->pid) | ||
1175 | return 1; | ||
1176 | |||
1177 | return 0; | ||
1178 | } | ||
1179 | |||
1180 | static struct sort_dimension pid_sort_dimension = { | ||
1181 | .name = "pid", | ||
1182 | .cmp = pid_cmp, | ||
1183 | }; | ||
1184 | |||
1185 | static int avg_cmp(struct task_atoms *l, struct task_atoms *r) | ||
1186 | { | ||
1187 | u64 avgl, avgr; | ||
1188 | |||
1189 | if (!l->nb_atoms) | ||
1190 | return -1; | ||
1191 | |||
1192 | if (!r->nb_atoms) | ||
1193 | return 1; | ||
1194 | |||
1195 | avgl = l->total_lat / l->nb_atoms; | ||
1196 | avgr = r->total_lat / r->nb_atoms; | ||
1197 | |||
1198 | if (avgl < avgr) | ||
1199 | return -1; | ||
1200 | if (avgl > avgr) | ||
1201 | return 1; | ||
1202 | |||
1203 | return 0; | ||
1204 | } | ||
1205 | |||
1206 | static struct sort_dimension avg_sort_dimension = { | ||
1207 | .name = "avg", | ||
1208 | .cmp = avg_cmp, | ||
1209 | }; | ||
1210 | |||
1211 | static int max_cmp(struct task_atoms *l, struct task_atoms *r) | ||
1212 | { | ||
1213 | if (l->max_lat < r->max_lat) | ||
1214 | return -1; | ||
1215 | if (l->max_lat > r->max_lat) | ||
1216 | return 1; | ||
1217 | |||
1218 | return 0; | ||
1219 | } | ||
1220 | |||
1221 | static struct sort_dimension max_sort_dimension = { | ||
1222 | .name = "max", | ||
1223 | .cmp = max_cmp, | ||
1224 | }; | ||
1225 | |||
1226 | static int switch_cmp(struct task_atoms *l, struct task_atoms *r) | ||
1227 | { | ||
1228 | if (l->nb_atoms < r->nb_atoms) | ||
1229 | return -1; | ||
1230 | if (l->nb_atoms > r->nb_atoms) | ||
1231 | return 1; | ||
1232 | |||
1233 | return 0; | ||
1234 | } | ||
1235 | |||
1236 | static struct sort_dimension switch_sort_dimension = { | ||
1237 | .name = "switch", | ||
1238 | .cmp = switch_cmp, | ||
1239 | }; | ||
1240 | |||
1241 | static int runtime_cmp(struct task_atoms *l, struct task_atoms *r) | ||
1242 | { | ||
1243 | if (l->total_runtime < r->total_runtime) | ||
1244 | return -1; | ||
1245 | if (l->total_runtime > r->total_runtime) | ||
1246 | return 1; | ||
1247 | |||
1248 | return 0; | ||
1249 | } | ||
1250 | |||
1251 | static struct sort_dimension runtime_sort_dimension = { | ||
1252 | .name = "runtime", | ||
1253 | .cmp = runtime_cmp, | ||
1254 | }; | ||
1255 | |||
1256 | static struct sort_dimension *available_sorts[] = { | ||
1257 | &pid_sort_dimension, | ||
1258 | &avg_sort_dimension, | ||
1259 | &max_sort_dimension, | ||
1260 | &switch_sort_dimension, | ||
1261 | &runtime_sort_dimension, | ||
1262 | }; | ||
1263 | |||
1264 | #define NB_AVAILABLE_SORTS (int)(sizeof(available_sorts) / sizeof(struct sort_dimension *)) | ||
1265 | |||
1266 | static LIST_HEAD(sort_list); | ||
1267 | |||
1268 | static int sort_dimension__add(char *tok, struct list_head *list) | ||
1269 | { | ||
1270 | int i; | ||
1271 | |||
1272 | for (i = 0; i < NB_AVAILABLE_SORTS; i++) { | ||
1273 | if (!strcmp(available_sorts[i]->name, tok)) { | ||
1274 | list_add_tail(&available_sorts[i]->list, list); | ||
1275 | |||
1276 | return 0; | ||
1277 | } | ||
1278 | } | ||
1279 | |||
1280 | return -1; | ||
1281 | } | ||
1282 | |||
1283 | static void setup_sorting(void); | ||
1284 | |||
1285 | static void sort_lat(void) | ||
1286 | { | ||
1287 | struct rb_node *node; | ||
1288 | |||
1289 | for (;;) { | ||
1290 | struct task_atoms *data; | ||
1291 | node = rb_first(&lat_snapshot_root); | ||
1292 | if (!node) | ||
1293 | break; | ||
1294 | |||
1295 | rb_erase(node, &lat_snapshot_root); | ||
1296 | data = rb_entry(node, struct task_atoms, node); | ||
1297 | __thread_latency_insert(&sorted_lat_snapshot_root, data, &sort_list); | ||
1298 | } | ||
1299 | } | ||
1300 | |||
1137 | static void __cmd_lat(void) | 1301 | static void __cmd_lat(void) |
1138 | { | 1302 | { |
1139 | struct rb_node *next; | 1303 | struct rb_node *next; |
1140 | 1304 | ||
1141 | setup_pager(); | 1305 | setup_pager(); |
1142 | read_events(); | 1306 | read_events(); |
1307 | sort_lat(); | ||
1143 | 1308 | ||
1144 | printf("-----------------------------------------------------------------------------------\n"); | 1309 | printf("-----------------------------------------------------------------------------------\n"); |
1145 | printf(" Task | Runtime ms | Switches | Average delay ms | Maximum delay ms |\n"); | 1310 | printf(" Task | Runtime ms | Switches | Average delay ms | Maximum delay ms |\n"); |
1146 | printf("-----------------------------------------------------------------------------------\n"); | 1311 | printf("-----------------------------------------------------------------------------------\n"); |
1147 | 1312 | ||
1148 | next = rb_first(&lat_snapshot_root); | 1313 | next = rb_first(&sorted_lat_snapshot_root); |
1149 | 1314 | ||
1150 | while (next) { | 1315 | while (next) { |
1151 | struct task_atoms *atom_list; | 1316 | struct task_atoms *atom_list; |
@@ -1469,11 +1634,30 @@ static const struct option options[] = { | |||
1469 | "replay sched behaviour from traces"), | 1634 | "replay sched behaviour from traces"), |
1470 | OPT_BOOLEAN('l', "latency", &lat_mode, | 1635 | OPT_BOOLEAN('l', "latency", &lat_mode, |
1471 | "measure various latencies"), | 1636 | "measure various latencies"), |
1637 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | ||
1638 | "sort by key(s): runtime, switch, avg, max"), | ||
1472 | OPT_BOOLEAN('v', "verbose", &verbose, | 1639 | OPT_BOOLEAN('v', "verbose", &verbose, |
1473 | "be more verbose (show symbol address, etc)"), | 1640 | "be more verbose (show symbol address, etc)"), |
1474 | OPT_END() | 1641 | OPT_END() |
1475 | }; | 1642 | }; |
1476 | 1643 | ||
1644 | static void setup_sorting(void) | ||
1645 | { | ||
1646 | char *tmp, *tok, *str = strdup(sort_order); | ||
1647 | |||
1648 | for (tok = strtok_r(str, ", ", &tmp); | ||
1649 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | ||
1650 | if (sort_dimension__add(tok, &sort_list) < 0) { | ||
1651 | error("Unknown --sort key: `%s'", tok); | ||
1652 | usage_with_options(sched_usage, options); | ||
1653 | } | ||
1654 | } | ||
1655 | |||
1656 | free(str); | ||
1657 | |||
1658 | sort_dimension__add((char *)"pid", &cmp_pid); | ||
1659 | } | ||
1660 | |||
1477 | int cmd_sched(int argc, const char **argv, const char *prefix __used) | 1661 | int cmd_sched(int argc, const char **argv, const char *prefix __used) |
1478 | { | 1662 | { |
1479 | symbol__init(); | 1663 | symbol__init(); |
@@ -1496,6 +1680,8 @@ int cmd_sched(int argc, const char **argv, const char *prefix __used) | |||
1496 | else | 1680 | else |
1497 | usage_with_options(sched_usage, options); | 1681 | usage_with_options(sched_usage, options); |
1498 | 1682 | ||
1683 | setup_sorting(); | ||
1684 | |||
1499 | if (replay_mode) | 1685 | if (replay_mode) |
1500 | __cmd_replay(); | 1686 | __cmd_replay(); |
1501 | else if (lat_mode) | 1687 | else if (lat_mode) |