diff options
| -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) |
