diff options
Diffstat (limited to 'tools/perf/builtin-sched.c')
| -rw-r--r-- | tools/perf/builtin-sched.c | 1522 |
1 files changed, 683 insertions, 839 deletions
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 7a9ad2b1ee76..9b9e32eaa805 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
| @@ -23,31 +23,12 @@ | |||
| 23 | #include <pthread.h> | 23 | #include <pthread.h> |
| 24 | #include <math.h> | 24 | #include <math.h> |
| 25 | 25 | ||
| 26 | static const char *input_name; | ||
| 27 | |||
| 28 | static char default_sort_order[] = "avg, max, switch, runtime"; | ||
| 29 | static const char *sort_order = default_sort_order; | ||
| 30 | |||
| 31 | static int profile_cpu = -1; | ||
| 32 | |||
| 33 | #define PR_SET_NAME 15 /* Set process name */ | 26 | #define PR_SET_NAME 15 /* Set process name */ |
| 34 | #define MAX_CPUS 4096 | 27 | #define MAX_CPUS 4096 |
| 35 | |||
| 36 | static u64 run_measurement_overhead; | ||
| 37 | static u64 sleep_measurement_overhead; | ||
| 38 | |||
| 39 | #define COMM_LEN 20 | 28 | #define COMM_LEN 20 |
| 40 | #define SYM_LEN 129 | 29 | #define SYM_LEN 129 |
| 41 | |||
| 42 | #define MAX_PID 65536 | 30 | #define MAX_PID 65536 |
| 43 | 31 | ||
| 44 | static unsigned long nr_tasks; | ||
| 45 | |||
| 46 | struct perf_sched { | ||
| 47 | struct perf_tool tool; | ||
| 48 | struct perf_session *session; | ||
| 49 | }; | ||
| 50 | |||
| 51 | struct sched_atom; | 32 | struct sched_atom; |
| 52 | 33 | ||
| 53 | struct task_desc { | 34 | struct task_desc { |
| @@ -85,44 +66,6 @@ struct sched_atom { | |||
| 85 | struct task_desc *wakee; | 66 | struct task_desc *wakee; |
| 86 | }; | 67 | }; |
| 87 | 68 | ||
| 88 | static struct task_desc *pid_to_task[MAX_PID]; | ||
| 89 | |||
| 90 | static struct task_desc **tasks; | ||
| 91 | |||
| 92 | static pthread_mutex_t start_work_mutex = PTHREAD_MUTEX_INITIALIZER; | ||
| 93 | static u64 start_time; | ||
| 94 | |||
| 95 | static pthread_mutex_t work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER; | ||
| 96 | |||
| 97 | static unsigned long nr_run_events; | ||
| 98 | static unsigned long nr_sleep_events; | ||
| 99 | static unsigned long nr_wakeup_events; | ||
| 100 | |||
| 101 | static unsigned long nr_sleep_corrections; | ||
| 102 | static unsigned long nr_run_events_optimized; | ||
| 103 | |||
| 104 | static unsigned long targetless_wakeups; | ||
| 105 | static unsigned long multitarget_wakeups; | ||
| 106 | |||
| 107 | static u64 cpu_usage; | ||
| 108 | static u64 runavg_cpu_usage; | ||
| 109 | static u64 parent_cpu_usage; | ||
| 110 | static u64 runavg_parent_cpu_usage; | ||
| 111 | |||
| 112 | static unsigned long nr_runs; | ||
| 113 | static u64 sum_runtime; | ||
| 114 | static u64 sum_fluct; | ||
| 115 | static u64 run_avg; | ||
| 116 | |||
| 117 | static unsigned int replay_repeat = 10; | ||
| 118 | static unsigned long nr_timestamps; | ||
| 119 | static unsigned long nr_unordered_timestamps; | ||
| 120 | static unsigned long nr_state_machine_bugs; | ||
| 121 | static unsigned long nr_context_switch_bugs; | ||
| 122 | static unsigned long nr_events; | ||
| 123 | static unsigned long nr_lost_chunks; | ||
| 124 | static unsigned long nr_lost_events; | ||
| 125 | |||
| 126 | #define TASK_STATE_TO_CHAR_STR "RSDTtZX" | 69 | #define TASK_STATE_TO_CHAR_STR "RSDTtZX" |
| 127 | 70 | ||
| 128 | enum thread_state { | 71 | enum thread_state { |
| @@ -154,11 +97,79 @@ struct work_atoms { | |||
| 154 | 97 | ||
| 155 | typedef int (*sort_fn_t)(struct work_atoms *, struct work_atoms *); | 98 | typedef int (*sort_fn_t)(struct work_atoms *, struct work_atoms *); |
| 156 | 99 | ||
| 157 | static struct rb_root atom_root, sorted_atom_root; | 100 | struct perf_sched; |
| 101 | |||
| 102 | struct trace_sched_handler { | ||
| 103 | int (*switch_event)(struct perf_sched *sched, struct perf_evsel *evsel, | ||
| 104 | struct perf_sample *sample, struct machine *machine); | ||
| 158 | 105 | ||
| 159 | static u64 all_runtime; | 106 | int (*runtime_event)(struct perf_sched *sched, struct perf_evsel *evsel, |
| 160 | static u64 all_count; | 107 | struct perf_sample *sample, struct machine *machine); |
| 161 | 108 | ||
| 109 | int (*wakeup_event)(struct perf_sched *sched, struct perf_evsel *evsel, | ||
| 110 | struct perf_sample *sample, struct machine *machine); | ||
| 111 | |||
| 112 | int (*fork_event)(struct perf_sched *sched, struct perf_evsel *evsel, | ||
| 113 | struct perf_sample *sample); | ||
| 114 | |||
| 115 | int (*migrate_task_event)(struct perf_sched *sched, | ||
| 116 | struct perf_evsel *evsel, | ||
| 117 | struct perf_sample *sample, | ||
| 118 | struct machine *machine); | ||
| 119 | }; | ||
| 120 | |||
| 121 | struct perf_sched { | ||
| 122 | struct perf_tool tool; | ||
| 123 | const char *input_name; | ||
| 124 | const char *sort_order; | ||
| 125 | unsigned long nr_tasks; | ||
| 126 | struct task_desc *pid_to_task[MAX_PID]; | ||
| 127 | struct task_desc **tasks; | ||
| 128 | const struct trace_sched_handler *tp_handler; | ||
| 129 | pthread_mutex_t start_work_mutex; | ||
| 130 | pthread_mutex_t work_done_wait_mutex; | ||
| 131 | int profile_cpu; | ||
| 132 | /* | ||
| 133 | * Track the current task - that way we can know whether there's any | ||
| 134 | * weird events, such as a task being switched away that is not current. | ||
| 135 | */ | ||
| 136 | int max_cpu; | ||
| 137 | u32 curr_pid[MAX_CPUS]; | ||
| 138 | struct thread *curr_thread[MAX_CPUS]; | ||
| 139 | char next_shortname1; | ||
| 140 | char next_shortname2; | ||
| 141 | unsigned int replay_repeat; | ||
| 142 | unsigned long nr_run_events; | ||
| 143 | unsigned long nr_sleep_events; | ||
| 144 | unsigned long nr_wakeup_events; | ||
| 145 | unsigned long nr_sleep_corrections; | ||
| 146 | unsigned long nr_run_events_optimized; | ||
| 147 | unsigned long targetless_wakeups; | ||
| 148 | unsigned long multitarget_wakeups; | ||
| 149 | unsigned long nr_runs; | ||
| 150 | unsigned long nr_timestamps; | ||
| 151 | unsigned long nr_unordered_timestamps; | ||
| 152 | unsigned long nr_state_machine_bugs; | ||
| 153 | unsigned long nr_context_switch_bugs; | ||
| 154 | unsigned long nr_events; | ||
| 155 | unsigned long nr_lost_chunks; | ||
| 156 | unsigned long nr_lost_events; | ||
| 157 | u64 run_measurement_overhead; | ||
| 158 | u64 sleep_measurement_overhead; | ||
| 159 | u64 start_time; | ||
| 160 | u64 cpu_usage; | ||
| 161 | u64 runavg_cpu_usage; | ||
| 162 | u64 parent_cpu_usage; | ||
| 163 | u64 runavg_parent_cpu_usage; | ||
| 164 | u64 sum_runtime; | ||
| 165 | u64 sum_fluct; | ||
| 166 | u64 run_avg; | ||
| 167 | u64 all_runtime; | ||
| 168 | u64 all_count; | ||
| 169 | u64 cpu_last_switched[MAX_CPUS]; | ||
| 170 | struct rb_root atom_root, sorted_atom_root; | ||
| 171 | struct list_head sort_list, cmp_pid; | ||
| 172 | }; | ||
| 162 | 173 | ||
| 163 | static u64 get_nsecs(void) | 174 | static u64 get_nsecs(void) |
| 164 | { | 175 | { |
| @@ -169,13 +180,13 @@ static u64 get_nsecs(void) | |||
| 169 | return ts.tv_sec * 1000000000ULL + ts.tv_nsec; | 180 | return ts.tv_sec * 1000000000ULL + ts.tv_nsec; |
| 170 | } | 181 | } |
| 171 | 182 | ||
| 172 | static void burn_nsecs(u64 nsecs) | 183 | static void burn_nsecs(struct perf_sched *sched, u64 nsecs) |
| 173 | { | 184 | { |
| 174 | u64 T0 = get_nsecs(), T1; | 185 | u64 T0 = get_nsecs(), T1; |
| 175 | 186 | ||
| 176 | do { | 187 | do { |
| 177 | T1 = get_nsecs(); | 188 | T1 = get_nsecs(); |
| 178 | } while (T1 + run_measurement_overhead < T0 + nsecs); | 189 | } while (T1 + sched->run_measurement_overhead < T0 + nsecs); |
| 179 | } | 190 | } |
| 180 | 191 | ||
| 181 | static void sleep_nsecs(u64 nsecs) | 192 | static void sleep_nsecs(u64 nsecs) |
| @@ -188,24 +199,24 @@ static void sleep_nsecs(u64 nsecs) | |||
| 188 | nanosleep(&ts, NULL); | 199 | nanosleep(&ts, NULL); |
| 189 | } | 200 | } |
| 190 | 201 | ||
| 191 | static void calibrate_run_measurement_overhead(void) | 202 | static void calibrate_run_measurement_overhead(struct perf_sched *sched) |
| 192 | { | 203 | { |
| 193 | u64 T0, T1, delta, min_delta = 1000000000ULL; | 204 | u64 T0, T1, delta, min_delta = 1000000000ULL; |
| 194 | int i; | 205 | int i; |
| 195 | 206 | ||
| 196 | for (i = 0; i < 10; i++) { | 207 | for (i = 0; i < 10; i++) { |
| 197 | T0 = get_nsecs(); | 208 | T0 = get_nsecs(); |
| 198 | burn_nsecs(0); | 209 | burn_nsecs(sched, 0); |
| 199 | T1 = get_nsecs(); | 210 | T1 = get_nsecs(); |
| 200 | delta = T1-T0; | 211 | delta = T1-T0; |
| 201 | min_delta = min(min_delta, delta); | 212 | min_delta = min(min_delta, delta); |
| 202 | } | 213 | } |
| 203 | run_measurement_overhead = min_delta; | 214 | sched->run_measurement_overhead = min_delta; |
| 204 | 215 | ||
| 205 | printf("run measurement overhead: %" PRIu64 " nsecs\n", min_delta); | 216 | printf("run measurement overhead: %" PRIu64 " nsecs\n", min_delta); |
| 206 | } | 217 | } |
| 207 | 218 | ||
| 208 | static void calibrate_sleep_measurement_overhead(void) | 219 | static void calibrate_sleep_measurement_overhead(struct perf_sched *sched) |
| 209 | { | 220 | { |
| 210 | u64 T0, T1, delta, min_delta = 1000000000ULL; | 221 | u64 T0, T1, delta, min_delta = 1000000000ULL; |
| 211 | int i; | 222 | int i; |
| @@ -218,7 +229,7 @@ static void calibrate_sleep_measurement_overhead(void) | |||
| 218 | min_delta = min(min_delta, delta); | 229 | min_delta = min(min_delta, delta); |
| 219 | } | 230 | } |
| 220 | min_delta -= 10000; | 231 | min_delta -= 10000; |
| 221 | sleep_measurement_overhead = min_delta; | 232 | sched->sleep_measurement_overhead = min_delta; |
| 222 | 233 | ||
| 223 | printf("sleep measurement overhead: %" PRIu64 " nsecs\n", min_delta); | 234 | printf("sleep measurement overhead: %" PRIu64 " nsecs\n", min_delta); |
| 224 | } | 235 | } |
| @@ -251,8 +262,8 @@ static struct sched_atom *last_event(struct task_desc *task) | |||
| 251 | return task->atoms[task->nr_events - 1]; | 262 | return task->atoms[task->nr_events - 1]; |
| 252 | } | 263 | } |
| 253 | 264 | ||
| 254 | static void | 265 | static void add_sched_event_run(struct perf_sched *sched, struct task_desc *task, |
| 255 | add_sched_event_run(struct task_desc *task, u64 timestamp, u64 duration) | 266 | u64 timestamp, u64 duration) |
| 256 | { | 267 | { |
| 257 | struct sched_atom *event, *curr_event = last_event(task); | 268 | struct sched_atom *event, *curr_event = last_event(task); |
| 258 | 269 | ||
| @@ -261,7 +272,7 @@ add_sched_event_run(struct task_desc *task, u64 timestamp, u64 duration) | |||
| 261 | * to it: | 272 | * to it: |
| 262 | */ | 273 | */ |
| 263 | if (curr_event && curr_event->type == SCHED_EVENT_RUN) { | 274 | if (curr_event && curr_event->type == SCHED_EVENT_RUN) { |
| 264 | nr_run_events_optimized++; | 275 | sched->nr_run_events_optimized++; |
| 265 | curr_event->duration += duration; | 276 | curr_event->duration += duration; |
| 266 | return; | 277 | return; |
| 267 | } | 278 | } |
| @@ -271,12 +282,11 @@ add_sched_event_run(struct task_desc *task, u64 timestamp, u64 duration) | |||
| 271 | event->type = SCHED_EVENT_RUN; | 282 | event->type = SCHED_EVENT_RUN; |
| 272 | event->duration = duration; | 283 | event->duration = duration; |
| 273 | 284 | ||
| 274 | nr_run_events++; | 285 | sched->nr_run_events++; |
| 275 | } | 286 | } |
| 276 | 287 | ||
| 277 | static void | 288 | static void add_sched_event_wakeup(struct perf_sched *sched, struct task_desc *task, |
| 278 | add_sched_event_wakeup(struct task_desc *task, u64 timestamp, | 289 | u64 timestamp, struct task_desc *wakee) |
| 279 | struct task_desc *wakee) | ||
| 280 | { | 290 | { |
| 281 | struct sched_atom *event, *wakee_event; | 291 | struct sched_atom *event, *wakee_event; |
| 282 | 292 | ||
| @@ -286,11 +296,11 @@ add_sched_event_wakeup(struct task_desc *task, u64 timestamp, | |||
| 286 | 296 | ||
| 287 | wakee_event = last_event(wakee); | 297 | wakee_event = last_event(wakee); |
| 288 | if (!wakee_event || wakee_event->type != SCHED_EVENT_SLEEP) { | 298 | if (!wakee_event || wakee_event->type != SCHED_EVENT_SLEEP) { |
| 289 | targetless_wakeups++; | 299 | sched->targetless_wakeups++; |
| 290 | return; | 300 | return; |
| 291 | } | 301 | } |
| 292 | if (wakee_event->wait_sem) { | 302 | if (wakee_event->wait_sem) { |
| 293 | multitarget_wakeups++; | 303 | sched->multitarget_wakeups++; |
| 294 | return; | 304 | return; |
| 295 | } | 305 | } |
| 296 | 306 | ||
| @@ -299,89 +309,89 @@ add_sched_event_wakeup(struct task_desc *task, u64 timestamp, | |||
| 299 | wakee_event->specific_wait = 1; | 309 | wakee_event->specific_wait = 1; |
| 300 | event->wait_sem = wakee_event->wait_sem; | 310 | event->wait_sem = wakee_event->wait_sem; |
| 301 | 311 | ||
| 302 | nr_wakeup_events++; | 312 | sched->nr_wakeup_events++; |
| 303 | } | 313 | } |
| 304 | 314 | ||
| 305 | static void | 315 | static void add_sched_event_sleep(struct perf_sched *sched, struct task_desc *task, |
| 306 | add_sched_event_sleep(struct task_desc *task, u64 timestamp, | 316 | u64 timestamp, u64 task_state __maybe_unused) |
| 307 | u64 task_state __used) | ||
| 308 | { | 317 | { |
| 309 | struct sched_atom *event = get_new_event(task, timestamp); | 318 | struct sched_atom *event = get_new_event(task, timestamp); |
| 310 | 319 | ||
| 311 | event->type = SCHED_EVENT_SLEEP; | 320 | event->type = SCHED_EVENT_SLEEP; |
| 312 | 321 | ||
| 313 | nr_sleep_events++; | 322 | sched->nr_sleep_events++; |
| 314 | } | 323 | } |
| 315 | 324 | ||
| 316 | static struct task_desc *register_pid(unsigned long pid, const char *comm) | 325 | static struct task_desc *register_pid(struct perf_sched *sched, |
| 326 | unsigned long pid, const char *comm) | ||
| 317 | { | 327 | { |
| 318 | struct task_desc *task; | 328 | struct task_desc *task; |
| 319 | 329 | ||
| 320 | BUG_ON(pid >= MAX_PID); | 330 | BUG_ON(pid >= MAX_PID); |
| 321 | 331 | ||
| 322 | task = pid_to_task[pid]; | 332 | task = sched->pid_to_task[pid]; |
| 323 | 333 | ||
| 324 | if (task) | 334 | if (task) |
| 325 | return task; | 335 | return task; |
| 326 | 336 | ||
| 327 | task = zalloc(sizeof(*task)); | 337 | task = zalloc(sizeof(*task)); |
| 328 | task->pid = pid; | 338 | task->pid = pid; |
| 329 | task->nr = nr_tasks; | 339 | task->nr = sched->nr_tasks; |
| 330 | strcpy(task->comm, comm); | 340 | strcpy(task->comm, comm); |
| 331 | /* | 341 | /* |
| 332 | * every task starts in sleeping state - this gets ignored | 342 | * every task starts in sleeping state - this gets ignored |
| 333 | * if there's no wakeup pointing to this sleep state: | 343 | * if there's no wakeup pointing to this sleep state: |
| 334 | */ | 344 | */ |
| 335 | add_sched_event_sleep(task, 0, 0); | 345 | add_sched_event_sleep(sched, task, 0, 0); |
| 336 | 346 | ||
| 337 | pid_to_task[pid] = task; | 347 | sched->pid_to_task[pid] = task; |
| 338 | nr_tasks++; | 348 | sched->nr_tasks++; |
| 339 | tasks = realloc(tasks, nr_tasks*sizeof(struct task_task *)); | 349 | sched->tasks = realloc(sched->tasks, sched->nr_tasks * sizeof(struct task_task *)); |
| 340 | BUG_ON(!tasks); | 350 | BUG_ON(!sched->tasks); |
| 341 | tasks[task->nr] = task; | 351 | sched->tasks[task->nr] = task; |
| 342 | 352 | ||
| 343 | if (verbose) | 353 | if (verbose) |
| 344 | printf("registered task #%ld, PID %ld (%s)\n", nr_tasks, pid, comm); | 354 | printf("registered task #%ld, PID %ld (%s)\n", sched->nr_tasks, pid, comm); |
| 345 | 355 | ||
| 346 | return task; | 356 | return task; |
| 347 | } | 357 | } |
| 348 | 358 | ||
| 349 | 359 | ||
| 350 | static void print_task_traces(void) | 360 | static void print_task_traces(struct perf_sched *sched) |
| 351 | { | 361 | { |
| 352 | struct task_desc *task; | 362 | struct task_desc *task; |
| 353 | unsigned long i; | 363 | unsigned long i; |
| 354 | 364 | ||
| 355 | for (i = 0; i < nr_tasks; i++) { | 365 | for (i = 0; i < sched->nr_tasks; i++) { |
| 356 | task = tasks[i]; | 366 | task = sched->tasks[i]; |
| 357 | printf("task %6ld (%20s:%10ld), nr_events: %ld\n", | 367 | printf("task %6ld (%20s:%10ld), nr_events: %ld\n", |
| 358 | task->nr, task->comm, task->pid, task->nr_events); | 368 | task->nr, task->comm, task->pid, task->nr_events); |
| 359 | } | 369 | } |
| 360 | } | 370 | } |
| 361 | 371 | ||
| 362 | static void add_cross_task_wakeups(void) | 372 | static void add_cross_task_wakeups(struct perf_sched *sched) |
| 363 | { | 373 | { |
| 364 | struct task_desc *task1, *task2; | 374 | struct task_desc *task1, *task2; |
| 365 | unsigned long i, j; | 375 | unsigned long i, j; |
| 366 | 376 | ||
| 367 | for (i = 0; i < nr_tasks; i++) { | 377 | for (i = 0; i < sched->nr_tasks; i++) { |
| 368 | task1 = tasks[i]; | 378 | task1 = sched->tasks[i]; |
| 369 | j = i + 1; | 379 | j = i + 1; |
| 370 | if (j == nr_tasks) | 380 | if (j == sched->nr_tasks) |
| 371 | j = 0; | 381 | j = 0; |
| 372 | task2 = tasks[j]; | 382 | task2 = sched->tasks[j]; |
| 373 | add_sched_event_wakeup(task1, 0, task2); | 383 | add_sched_event_wakeup(sched, task1, 0, task2); |
| 374 | } | 384 | } |
| 375 | } | 385 | } |
| 376 | 386 | ||
| 377 | static void | 387 | static void perf_sched__process_event(struct perf_sched *sched, |
| 378 | process_sched_event(struct task_desc *this_task __used, struct sched_atom *atom) | 388 | struct sched_atom *atom) |
| 379 | { | 389 | { |
| 380 | int ret = 0; | 390 | int ret = 0; |
| 381 | 391 | ||
| 382 | switch (atom->type) { | 392 | switch (atom->type) { |
| 383 | case SCHED_EVENT_RUN: | 393 | case SCHED_EVENT_RUN: |
| 384 | burn_nsecs(atom->duration); | 394 | burn_nsecs(sched, atom->duration); |
| 385 | break; | 395 | break; |
| 386 | case SCHED_EVENT_SLEEP: | 396 | case SCHED_EVENT_SLEEP: |
| 387 | if (atom->wait_sem) | 397 | if (atom->wait_sem) |
| @@ -428,8 +438,8 @@ static int self_open_counters(void) | |||
| 428 | fd = sys_perf_event_open(&attr, 0, -1, -1, 0); | 438 | fd = sys_perf_event_open(&attr, 0, -1, -1, 0); |
| 429 | 439 | ||
| 430 | if (fd < 0) | 440 | if (fd < 0) |
| 431 | die("Error: sys_perf_event_open() syscall returned" | 441 | pr_err("Error: sys_perf_event_open() syscall returned " |
| 432 | "with %d (%s)\n", fd, strerror(errno)); | 442 | "with %d (%s)\n", fd, strerror(errno)); |
| 433 | return fd; | 443 | return fd; |
| 434 | } | 444 | } |
| 435 | 445 | ||
| @@ -444,31 +454,41 @@ static u64 get_cpu_usage_nsec_self(int fd) | |||
| 444 | return runtime; | 454 | return runtime; |
| 445 | } | 455 | } |
| 446 | 456 | ||
| 457 | struct sched_thread_parms { | ||
| 458 | struct task_desc *task; | ||
| 459 | struct perf_sched *sched; | ||
| 460 | }; | ||
| 461 | |||
| 447 | static void *thread_func(void *ctx) | 462 | static void *thread_func(void *ctx) |
| 448 | { | 463 | { |
| 449 | struct task_desc *this_task = ctx; | 464 | struct sched_thread_parms *parms = ctx; |
| 465 | struct task_desc *this_task = parms->task; | ||
| 466 | struct perf_sched *sched = parms->sched; | ||
| 450 | u64 cpu_usage_0, cpu_usage_1; | 467 | u64 cpu_usage_0, cpu_usage_1; |
| 451 | unsigned long i, ret; | 468 | unsigned long i, ret; |
| 452 | char comm2[22]; | 469 | char comm2[22]; |
| 453 | int fd; | 470 | int fd; |
| 454 | 471 | ||
| 472 | free(parms); | ||
| 473 | |||
| 455 | sprintf(comm2, ":%s", this_task->comm); | 474 | sprintf(comm2, ":%s", this_task->comm); |
| 456 | prctl(PR_SET_NAME, comm2); | 475 | prctl(PR_SET_NAME, comm2); |
| 457 | fd = self_open_counters(); | 476 | fd = self_open_counters(); |
| 458 | 477 | if (fd < 0) | |
| 478 | return NULL; | ||
| 459 | again: | 479 | again: |
| 460 | ret = sem_post(&this_task->ready_for_work); | 480 | ret = sem_post(&this_task->ready_for_work); |
| 461 | BUG_ON(ret); | 481 | BUG_ON(ret); |
| 462 | ret = pthread_mutex_lock(&start_work_mutex); | 482 | ret = pthread_mutex_lock(&sched->start_work_mutex); |
| 463 | BUG_ON(ret); | 483 | BUG_ON(ret); |
| 464 | ret = pthread_mutex_unlock(&start_work_mutex); | 484 | ret = pthread_mutex_unlock(&sched->start_work_mutex); |
| 465 | BUG_ON(ret); | 485 | BUG_ON(ret); |
| 466 | 486 | ||
| 467 | cpu_usage_0 = get_cpu_usage_nsec_self(fd); | 487 | cpu_usage_0 = get_cpu_usage_nsec_self(fd); |
| 468 | 488 | ||
| 469 | for (i = 0; i < this_task->nr_events; i++) { | 489 | for (i = 0; i < this_task->nr_events; i++) { |
| 470 | this_task->curr_event = i; | 490 | this_task->curr_event = i; |
| 471 | process_sched_event(this_task, this_task->atoms[i]); | 491 | perf_sched__process_event(sched, this_task->atoms[i]); |
| 472 | } | 492 | } |
| 473 | 493 | ||
| 474 | cpu_usage_1 = get_cpu_usage_nsec_self(fd); | 494 | cpu_usage_1 = get_cpu_usage_nsec_self(fd); |
| @@ -476,15 +496,15 @@ again: | |||
| 476 | ret = sem_post(&this_task->work_done_sem); | 496 | ret = sem_post(&this_task->work_done_sem); |
| 477 | BUG_ON(ret); | 497 | BUG_ON(ret); |
| 478 | 498 | ||
| 479 | ret = pthread_mutex_lock(&work_done_wait_mutex); | 499 | ret = pthread_mutex_lock(&sched->work_done_wait_mutex); |
| 480 | BUG_ON(ret); | 500 | BUG_ON(ret); |
| 481 | ret = pthread_mutex_unlock(&work_done_wait_mutex); | 501 | ret = pthread_mutex_unlock(&sched->work_done_wait_mutex); |
| 482 | BUG_ON(ret); | 502 | BUG_ON(ret); |
| 483 | 503 | ||
| 484 | goto again; | 504 | goto again; |
| 485 | } | 505 | } |
| 486 | 506 | ||
| 487 | static void create_tasks(void) | 507 | static void create_tasks(struct perf_sched *sched) |
| 488 | { | 508 | { |
| 489 | struct task_desc *task; | 509 | struct task_desc *task; |
| 490 | pthread_attr_t attr; | 510 | pthread_attr_t attr; |
| @@ -496,128 +516,129 @@ static void create_tasks(void) | |||
| 496 | err = pthread_attr_setstacksize(&attr, | 516 | err = pthread_attr_setstacksize(&attr, |
| 497 | (size_t) max(16 * 1024, PTHREAD_STACK_MIN)); | 517 | (size_t) max(16 * 1024, PTHREAD_STACK_MIN)); |
| 498 | BUG_ON(err); | 518 | BUG_ON(err); |
| 499 | err = pthread_mutex_lock(&start_work_mutex); | 519 | err = pthread_mutex_lock(&sched->start_work_mutex); |
| 500 | BUG_ON(err); | 520 | BUG_ON(err); |
| 501 | err = pthread_mutex_lock(&work_done_wait_mutex); | 521 | err = pthread_mutex_lock(&sched->work_done_wait_mutex); |
| 502 | BUG_ON(err); | 522 | BUG_ON(err); |
| 503 | for (i = 0; i < nr_tasks; i++) { | 523 | for (i = 0; i < sched->nr_tasks; i++) { |
| 504 | task = tasks[i]; | 524 | struct sched_thread_parms *parms = malloc(sizeof(*parms)); |
| 525 | BUG_ON(parms == NULL); | ||
| 526 | parms->task = task = sched->tasks[i]; | ||
| 527 | parms->sched = sched; | ||
| 505 | sem_init(&task->sleep_sem, 0, 0); | 528 | sem_init(&task->sleep_sem, 0, 0); |
| 506 | sem_init(&task->ready_for_work, 0, 0); | 529 | sem_init(&task->ready_for_work, 0, 0); |
| 507 | sem_init(&task->work_done_sem, 0, 0); | 530 | sem_init(&task->work_done_sem, 0, 0); |
| 508 | task->curr_event = 0; | 531 | task->curr_event = 0; |
| 509 | err = pthread_create(&task->thread, &attr, thread_func, task); | 532 | err = pthread_create(&task->thread, &attr, thread_func, parms); |
| 510 | BUG_ON(err); | 533 | BUG_ON(err); |
| 511 | } | 534 | } |
| 512 | } | 535 | } |
| 513 | 536 | ||
| 514 | static void wait_for_tasks(void) | 537 | static void wait_for_tasks(struct perf_sched *sched) |
| 515 | { | 538 | { |
| 516 | u64 cpu_usage_0, cpu_usage_1; | 539 | u64 cpu_usage_0, cpu_usage_1; |
| 517 | struct task_desc *task; | 540 | struct task_desc *task; |
| 518 | unsigned long i, ret; | 541 | unsigned long i, ret; |
| 519 | 542 | ||
| 520 | start_time = get_nsecs(); | 543 | sched->start_time = get_nsecs(); |
| 521 | cpu_usage = 0; | 544 | sched->cpu_usage = 0; |
| 522 | pthread_mutex_unlock(&work_done_wait_mutex); | 545 | pthread_mutex_unlock(&sched->work_done_wait_mutex); |
| 523 | 546 | ||
| 524 | for (i = 0; i < nr_tasks; i++) { | 547 | for (i = 0; i < sched->nr_tasks; i++) { |
| 525 | task = tasks[i]; | 548 | task = sched->tasks[i]; |
| 526 | ret = sem_wait(&task->ready_for_work); | 549 | ret = sem_wait(&task->ready_for_work); |
| 527 | BUG_ON(ret); | 550 | BUG_ON(ret); |
| 528 | sem_init(&task->ready_for_work, 0, 0); | 551 | sem_init(&task->ready_for_work, 0, 0); |
| 529 | } | 552 | } |
| 530 | ret = pthread_mutex_lock(&work_done_wait_mutex); | 553 | ret = pthread_mutex_lock(&sched->work_done_wait_mutex); |
| 531 | BUG_ON(ret); | 554 | BUG_ON(ret); |
| 532 | 555 | ||
| 533 | cpu_usage_0 = get_cpu_usage_nsec_parent(); | 556 | cpu_usage_0 = get_cpu_usage_nsec_parent(); |
| 534 | 557 | ||
| 535 | pthread_mutex_unlock(&start_work_mutex); | 558 | pthread_mutex_unlock(&sched->start_work_mutex); |
| 536 | 559 | ||
| 537 | for (i = 0; i < nr_tasks; i++) { | 560 | for (i = 0; i < sched->nr_tasks; i++) { |
| 538 | task = tasks[i]; | 561 | task = sched->tasks[i]; |
| 539 | ret = sem_wait(&task->work_done_sem); | 562 | ret = sem_wait(&task->work_done_sem); |
| 540 | BUG_ON(ret); | 563 | BUG_ON(ret); |
| 541 | sem_init(&task->work_done_sem, 0, 0); | 564 | sem_init(&task->work_done_sem, 0, 0); |
| 542 | cpu_usage += task->cpu_usage; | 565 | sched->cpu_usage += task->cpu_usage; |
| 543 | task->cpu_usage = 0; | 566 | task->cpu_usage = 0; |
| 544 | } | 567 | } |
| 545 | 568 | ||
| 546 | cpu_usage_1 = get_cpu_usage_nsec_parent(); | 569 | cpu_usage_1 = get_cpu_usage_nsec_parent(); |
| 547 | if (!runavg_cpu_usage) | 570 | if (!sched->runavg_cpu_usage) |
| 548 | runavg_cpu_usage = cpu_usage; | 571 | sched->runavg_cpu_usage = sched->cpu_usage; |
| 549 | runavg_cpu_usage = (runavg_cpu_usage*9 + cpu_usage)/10; | 572 | sched->runavg_cpu_usage = (sched->runavg_cpu_usage * 9 + sched->cpu_usage) / 10; |
| 550 | 573 | ||
| 551 | parent_cpu_usage = cpu_usage_1 - cpu_usage_0; | 574 | sched->parent_cpu_usage = cpu_usage_1 - cpu_usage_0; |
| 552 | if (!runavg_parent_cpu_usage) | 575 | if (!sched->runavg_parent_cpu_usage) |
| 553 | runavg_parent_cpu_usage = parent_cpu_usage; | 576 | sched->runavg_parent_cpu_usage = sched->parent_cpu_usage; |
| 554 | runavg_parent_cpu_usage = (runavg_parent_cpu_usage*9 + | 577 | sched->runavg_parent_cpu_usage = (sched->runavg_parent_cpu_usage * 9 + |
| 555 | parent_cpu_usage)/10; | 578 | sched->parent_cpu_usage)/10; |
| 556 | 579 | ||
| 557 | ret = pthread_mutex_lock(&start_work_mutex); | 580 | ret = pthread_mutex_lock(&sched->start_work_mutex); |
| 558 | BUG_ON(ret); | 581 | BUG_ON(ret); |
| 559 | 582 | ||
| 560 | for (i = 0; i < nr_tasks; i++) { | 583 | for (i = 0; i < sched->nr_tasks; i++) { |
| 561 | task = tasks[i]; | 584 | task = sched->tasks[i]; |
| 562 | sem_init(&task->sleep_sem, 0, 0); | 585 | sem_init(&task->sleep_sem, 0, 0); |
| 563 | task->curr_event = 0; | 586 | task->curr_event = 0; |
| 564 | } | 587 | } |
| 565 | } | 588 | } |
| 566 | 589 | ||
| 567 | static void run_one_test(void) | 590 | static void run_one_test(struct perf_sched *sched) |
| 568 | { | 591 | { |
| 569 | u64 T0, T1, delta, avg_delta, fluct; | 592 | u64 T0, T1, delta, avg_delta, fluct; |
| 570 | 593 | ||
| 571 | T0 = get_nsecs(); | 594 | T0 = get_nsecs(); |
| 572 | wait_for_tasks(); | 595 | wait_for_tasks(sched); |
| 573 | T1 = get_nsecs(); | 596 | T1 = get_nsecs(); |
| 574 | 597 | ||
| 575 | delta = T1 - T0; | 598 | delta = T1 - T0; |
| 576 | sum_runtime += delta; | 599 | sched->sum_runtime += delta; |
| 577 | nr_runs++; | 600 | sched->nr_runs++; |
| 578 | 601 | ||
| 579 | avg_delta = sum_runtime / nr_runs; | 602 | avg_delta = sched->sum_runtime / sched->nr_runs; |
| 580 | if (delta < avg_delta) | 603 | if (delta < avg_delta) |
| 581 | fluct = avg_delta - delta; | 604 | fluct = avg_delta - delta; |
| 582 | else | 605 | else |
| 583 | fluct = delta - avg_delta; | 606 | fluct = delta - avg_delta; |
| 584 | sum_fluct += fluct; | 607 | sched->sum_fluct += fluct; |
| 585 | if (!run_avg) | 608 | if (!sched->run_avg) |
| 586 | run_avg = delta; | 609 | sched->run_avg = delta; |
| 587 | run_avg = (run_avg*9 + delta)/10; | 610 | sched->run_avg = (sched->run_avg * 9 + delta) / 10; |
| 588 | 611 | ||
| 589 | printf("#%-3ld: %0.3f, ", | 612 | printf("#%-3ld: %0.3f, ", sched->nr_runs, (double)delta / 1000000.0); |
| 590 | nr_runs, (double)delta/1000000.0); | ||
| 591 | 613 | ||
| 592 | printf("ravg: %0.2f, ", | 614 | printf("ravg: %0.2f, ", (double)sched->run_avg / 1e6); |
| 593 | (double)run_avg/1e6); | ||
| 594 | 615 | ||
| 595 | printf("cpu: %0.2f / %0.2f", | 616 | printf("cpu: %0.2f / %0.2f", |
| 596 | (double)cpu_usage/1e6, (double)runavg_cpu_usage/1e6); | 617 | (double)sched->cpu_usage / 1e6, (double)sched->runavg_cpu_usage / 1e6); |
| 597 | 618 | ||
| 598 | #if 0 | 619 | #if 0 |
| 599 | /* | 620 | /* |
| 600 | * rusage statistics done by the parent, these are less | 621 | * rusage statistics done by the parent, these are less |
| 601 | * accurate than the sum_exec_runtime based statistics: | 622 | * accurate than the sched->sum_exec_runtime based statistics: |
| 602 | */ | 623 | */ |
| 603 | printf(" [%0.2f / %0.2f]", | 624 | printf(" [%0.2f / %0.2f]", |
| 604 | (double)parent_cpu_usage/1e6, | 625 | (double)sched->parent_cpu_usage/1e6, |
| 605 | (double)runavg_parent_cpu_usage/1e6); | 626 | (double)sched->runavg_parent_cpu_usage/1e6); |
| 606 | #endif | 627 | #endif |
| 607 | 628 | ||
| 608 | printf("\n"); | 629 | printf("\n"); |
| 609 | 630 | ||
| 610 | if (nr_sleep_corrections) | 631 | if (sched->nr_sleep_corrections) |
| 611 | printf(" (%ld sleep corrections)\n", nr_sleep_corrections); | 632 | printf(" (%ld sleep corrections)\n", sched->nr_sleep_corrections); |
| 612 | nr_sleep_corrections = 0; | 633 | sched->nr_sleep_corrections = 0; |
| 613 | } | 634 | } |
| 614 | 635 | ||
| 615 | static void test_calibrations(void) | 636 | static void test_calibrations(struct perf_sched *sched) |
| 616 | { | 637 | { |
| 617 | u64 T0, T1; | 638 | u64 T0, T1; |
| 618 | 639 | ||
| 619 | T0 = get_nsecs(); | 640 | T0 = get_nsecs(); |
| 620 | burn_nsecs(1e6); | 641 | burn_nsecs(sched, 1e6); |
| 621 | T1 = get_nsecs(); | 642 | T1 = get_nsecs(); |
| 622 | 643 | ||
| 623 | printf("the run test took %" PRIu64 " nsecs\n", T1 - T0); | 644 | printf("the run test took %" PRIu64 " nsecs\n", T1 - T0); |
| @@ -629,236 +650,92 @@ static void test_calibrations(void) | |||
| 629 | printf("the sleep test took %" PRIu64 " nsecs\n", T1 - T0); | 650 | printf("the sleep test took %" PRIu64 " nsecs\n", T1 - T0); |
| 630 | } | 651 | } |
| 631 | 652 | ||
| 632 | #define FILL_FIELD(ptr, field, event, data) \ | 653 | static int |
| 633 | ptr.field = (typeof(ptr.field)) raw_field_value(event, #field, data) | 654 | replay_wakeup_event(struct perf_sched *sched, |
| 634 | 655 | struct perf_evsel *evsel, struct perf_sample *sample, | |
| 635 | #define FILL_ARRAY(ptr, array, event, data) \ | 656 | struct machine *machine __maybe_unused) |
| 636 | do { \ | ||
| 637 | void *__array = raw_field_ptr(event, #array, data); \ | ||
| 638 | memcpy(ptr.array, __array, sizeof(ptr.array)); \ | ||
| 639 | } while(0) | ||
| 640 | |||
| 641 | #define FILL_COMMON_FIELDS(ptr, event, data) \ | ||
| 642 | do { \ | ||
| 643 | FILL_FIELD(ptr, common_type, event, data); \ | ||
| 644 | FILL_FIELD(ptr, common_flags, event, data); \ | ||
| 645 | FILL_FIELD(ptr, common_preempt_count, event, data); \ | ||
| 646 | FILL_FIELD(ptr, common_pid, event, data); \ | ||
| 647 | FILL_FIELD(ptr, common_tgid, event, data); \ | ||
| 648 | } while (0) | ||
| 649 | |||
| 650 | |||
| 651 | |||
| 652 | struct trace_switch_event { | ||
| 653 | u32 size; | ||
| 654 | |||
| 655 | u16 common_type; | ||
| 656 | u8 common_flags; | ||
| 657 | u8 common_preempt_count; | ||
| 658 | u32 common_pid; | ||
| 659 | u32 common_tgid; | ||
| 660 | |||
| 661 | char prev_comm[16]; | ||
| 662 | u32 prev_pid; | ||
| 663 | u32 prev_prio; | ||
| 664 | u64 prev_state; | ||
| 665 | char next_comm[16]; | ||
| 666 | u32 next_pid; | ||
| 667 | u32 next_prio; | ||
| 668 | }; | ||
| 669 | |||
| 670 | struct trace_runtime_event { | ||
| 671 | u32 size; | ||
| 672 | |||
| 673 | u16 common_type; | ||
| 674 | u8 common_flags; | ||
| 675 | u8 common_preempt_count; | ||
| 676 | u32 common_pid; | ||
| 677 | u32 common_tgid; | ||
| 678 | |||
| 679 | char comm[16]; | ||
| 680 | u32 pid; | ||
| 681 | u64 runtime; | ||
| 682 | u64 vruntime; | ||
| 683 | }; | ||
| 684 | |||
| 685 | struct trace_wakeup_event { | ||
| 686 | u32 size; | ||
| 687 | |||
| 688 | u16 common_type; | ||
| 689 | u8 common_flags; | ||
| 690 | u8 common_preempt_count; | ||
| 691 | u32 common_pid; | ||
| 692 | u32 common_tgid; | ||
| 693 | |||
| 694 | char comm[16]; | ||
| 695 | u32 pid; | ||
| 696 | |||
| 697 | u32 prio; | ||
| 698 | u32 success; | ||
| 699 | u32 cpu; | ||
| 700 | }; | ||
| 701 | |||
| 702 | struct trace_fork_event { | ||
| 703 | u32 size; | ||
| 704 | |||
| 705 | u16 common_type; | ||
| 706 | u8 common_flags; | ||
| 707 | u8 common_preempt_count; | ||
| 708 | u32 common_pid; | ||
| 709 | u32 common_tgid; | ||
| 710 | |||
| 711 | char parent_comm[16]; | ||
| 712 | u32 parent_pid; | ||
| 713 | char child_comm[16]; | ||
| 714 | u32 child_pid; | ||
| 715 | }; | ||
| 716 | |||
| 717 | struct trace_migrate_task_event { | ||
| 718 | u32 size; | ||
| 719 | |||
| 720 | u16 common_type; | ||
| 721 | u8 common_flags; | ||
| 722 | u8 common_preempt_count; | ||
| 723 | u32 common_pid; | ||
| 724 | u32 common_tgid; | ||
| 725 | |||
| 726 | char comm[16]; | ||
| 727 | u32 pid; | ||
| 728 | |||
| 729 | u32 prio; | ||
| 730 | u32 cpu; | ||
| 731 | }; | ||
| 732 | |||
| 733 | struct trace_sched_handler { | ||
| 734 | void (*switch_event)(struct trace_switch_event *, | ||
| 735 | struct machine *, | ||
| 736 | struct event_format *, | ||
| 737 | int cpu, | ||
| 738 | u64 timestamp, | ||
| 739 | struct thread *thread); | ||
| 740 | |||
| 741 | void (*runtime_event)(struct trace_runtime_event *, | ||
| 742 | struct machine *, | ||
| 743 | struct event_format *, | ||
| 744 | int cpu, | ||
| 745 | u64 timestamp, | ||
| 746 | struct thread *thread); | ||
| 747 | |||
| 748 | void (*wakeup_event)(struct trace_wakeup_event *, | ||
| 749 | struct machine *, | ||
| 750 | struct event_format *, | ||
| 751 | int cpu, | ||
| 752 | u64 timestamp, | ||
| 753 | struct thread *thread); | ||
| 754 | |||
| 755 | void (*fork_event)(struct trace_fork_event *, | ||
| 756 | struct event_format *, | ||
| 757 | int cpu, | ||
| 758 | u64 timestamp, | ||
| 759 | struct thread *thread); | ||
| 760 | |||
| 761 | void (*migrate_task_event)(struct trace_migrate_task_event *, | ||
| 762 | struct machine *machine, | ||
| 763 | struct event_format *, | ||
| 764 | int cpu, | ||
| 765 | u64 timestamp, | ||
| 766 | struct thread *thread); | ||
| 767 | }; | ||
| 768 | |||
| 769 | |||
| 770 | static void | ||
| 771 | replay_wakeup_event(struct trace_wakeup_event *wakeup_event, | ||
| 772 | struct machine *machine __used, | ||
| 773 | struct event_format *event, | ||
| 774 | int cpu __used, | ||
| 775 | u64 timestamp __used, | ||
| 776 | struct thread *thread __used) | ||
| 777 | { | 657 | { |
| 658 | const char *comm = perf_evsel__strval(evsel, sample, "comm"); | ||
| 659 | const u32 pid = perf_evsel__intval(evsel, sample, "pid"); | ||
| 778 | struct task_desc *waker, *wakee; | 660 | struct task_desc *waker, *wakee; |
| 779 | 661 | ||
| 780 | if (verbose) { | 662 | if (verbose) { |
| 781 | printf("sched_wakeup event %p\n", event); | 663 | printf("sched_wakeup event %p\n", evsel); |
| 782 | 664 | ||
| 783 | printf(" ... pid %d woke up %s/%d\n", | 665 | printf(" ... pid %d woke up %s/%d\n", sample->tid, comm, pid); |
| 784 | wakeup_event->common_pid, | ||
| 785 | wakeup_event->comm, | ||
| 786 | wakeup_event->pid); | ||
| 787 | } | 666 | } |
| 788 | 667 | ||
| 789 | waker = register_pid(wakeup_event->common_pid, "<unknown>"); | 668 | waker = register_pid(sched, sample->tid, "<unknown>"); |
| 790 | wakee = register_pid(wakeup_event->pid, wakeup_event->comm); | 669 | wakee = register_pid(sched, pid, comm); |
| 791 | 670 | ||
| 792 | add_sched_event_wakeup(waker, timestamp, wakee); | 671 | add_sched_event_wakeup(sched, waker, sample->time, wakee); |
| 672 | return 0; | ||
| 793 | } | 673 | } |
| 794 | 674 | ||
| 795 | static u64 cpu_last_switched[MAX_CPUS]; | 675 | static int replay_switch_event(struct perf_sched *sched, |
| 796 | 676 | struct perf_evsel *evsel, | |
| 797 | static void | 677 | struct perf_sample *sample, |
| 798 | replay_switch_event(struct trace_switch_event *switch_event, | 678 | struct machine *machine __maybe_unused) |
| 799 | struct machine *machine __used, | ||
| 800 | struct event_format *event, | ||
| 801 | int cpu, | ||
| 802 | u64 timestamp, | ||
| 803 | struct thread *thread __used) | ||
| 804 | { | 679 | { |
| 805 | struct task_desc *prev, __used *next; | 680 | const char *prev_comm = perf_evsel__strval(evsel, sample, "prev_comm"), |
| 806 | u64 timestamp0; | 681 | *next_comm = perf_evsel__strval(evsel, sample, "next_comm"); |
| 682 | const u32 prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"), | ||
| 683 | next_pid = perf_evsel__intval(evsel, sample, "next_pid"); | ||
| 684 | const u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state"); | ||
| 685 | struct task_desc *prev, __maybe_unused *next; | ||
| 686 | u64 timestamp0, timestamp = sample->time; | ||
| 687 | int cpu = sample->cpu; | ||
| 807 | s64 delta; | 688 | s64 delta; |
| 808 | 689 | ||
| 809 | if (verbose) | 690 | if (verbose) |
| 810 | printf("sched_switch event %p\n", event); | 691 | printf("sched_switch event %p\n", evsel); |
| 811 | 692 | ||
| 812 | if (cpu >= MAX_CPUS || cpu < 0) | 693 | if (cpu >= MAX_CPUS || cpu < 0) |
| 813 | return; | 694 | return 0; |
| 814 | 695 | ||
| 815 | timestamp0 = cpu_last_switched[cpu]; | 696 | timestamp0 = sched->cpu_last_switched[cpu]; |
| 816 | if (timestamp0) | 697 | if (timestamp0) |
| 817 | delta = timestamp - timestamp0; | 698 | delta = timestamp - timestamp0; |
| 818 | else | 699 | else |
| 819 | delta = 0; | 700 | delta = 0; |
| 820 | 701 | ||
| 821 | if (delta < 0) | 702 | if (delta < 0) { |
| 822 | die("hm, delta: %" PRIu64 " < 0 ?\n", delta); | 703 | pr_err("hm, delta: %" PRIu64 " < 0 ?\n", delta); |
| 823 | 704 | return -1; | |
| 824 | if (verbose) { | ||
| 825 | printf(" ... switch from %s/%d to %s/%d [ran %" PRIu64 " nsecs]\n", | ||
| 826 | switch_event->prev_comm, switch_event->prev_pid, | ||
| 827 | switch_event->next_comm, switch_event->next_pid, | ||
| 828 | delta); | ||
| 829 | } | 705 | } |
| 830 | 706 | ||
| 831 | prev = register_pid(switch_event->prev_pid, switch_event->prev_comm); | 707 | pr_debug(" ... switch from %s/%d to %s/%d [ran %" PRIu64 " nsecs]\n", |
| 832 | next = register_pid(switch_event->next_pid, switch_event->next_comm); | 708 | prev_comm, prev_pid, next_comm, next_pid, delta); |
| 833 | 709 | ||
| 834 | cpu_last_switched[cpu] = timestamp; | 710 | prev = register_pid(sched, prev_pid, prev_comm); |
| 711 | next = register_pid(sched, next_pid, next_comm); | ||
| 835 | 712 | ||
| 836 | add_sched_event_run(prev, timestamp, delta); | 713 | sched->cpu_last_switched[cpu] = timestamp; |
| 837 | add_sched_event_sleep(prev, timestamp, switch_event->prev_state); | ||
| 838 | } | ||
| 839 | 714 | ||
| 715 | add_sched_event_run(sched, prev, timestamp, delta); | ||
| 716 | add_sched_event_sleep(sched, prev, timestamp, prev_state); | ||
| 840 | 717 | ||
| 841 | static void | 718 | return 0; |
| 842 | replay_fork_event(struct trace_fork_event *fork_event, | 719 | } |
| 843 | struct event_format *event, | 720 | |
| 844 | int cpu __used, | 721 | static int replay_fork_event(struct perf_sched *sched, struct perf_evsel *evsel, |
| 845 | u64 timestamp __used, | 722 | struct perf_sample *sample) |
| 846 | struct thread *thread __used) | ||
| 847 | { | 723 | { |
| 724 | const char *parent_comm = perf_evsel__strval(evsel, sample, "parent_comm"), | ||
| 725 | *child_comm = perf_evsel__strval(evsel, sample, "child_comm"); | ||
| 726 | const u32 parent_pid = perf_evsel__intval(evsel, sample, "parent_pid"), | ||
| 727 | child_pid = perf_evsel__intval(evsel, sample, "child_pid"); | ||
| 728 | |||
| 848 | if (verbose) { | 729 | if (verbose) { |
| 849 | printf("sched_fork event %p\n", event); | 730 | printf("sched_fork event %p\n", evsel); |
| 850 | printf("... parent: %s/%d\n", fork_event->parent_comm, fork_event->parent_pid); | 731 | printf("... parent: %s/%d\n", parent_comm, parent_pid); |
| 851 | printf("... child: %s/%d\n", fork_event->child_comm, fork_event->child_pid); | 732 | printf("... child: %s/%d\n", child_comm, child_pid); |
| 852 | } | 733 | } |
| 853 | register_pid(fork_event->parent_pid, fork_event->parent_comm); | ||
| 854 | register_pid(fork_event->child_pid, fork_event->child_comm); | ||
| 855 | } | ||
| 856 | 734 | ||
| 857 | static struct trace_sched_handler replay_ops = { | 735 | register_pid(sched, parent_pid, parent_comm); |
| 858 | .wakeup_event = replay_wakeup_event, | 736 | register_pid(sched, child_pid, child_comm); |
| 859 | .switch_event = replay_switch_event, | 737 | return 0; |
| 860 | .fork_event = replay_fork_event, | 738 | } |
| 861 | }; | ||
| 862 | 739 | ||
| 863 | struct sort_dimension { | 740 | struct sort_dimension { |
| 864 | const char *name; | 741 | const char *name; |
| @@ -866,8 +743,6 @@ struct sort_dimension { | |||
| 866 | struct list_head list; | 743 | struct list_head list; |
| 867 | }; | 744 | }; |
| 868 | 745 | ||
| 869 | static LIST_HEAD(cmp_pid); | ||
| 870 | |||
| 871 | static int | 746 | static int |
| 872 | thread_lat_cmp(struct list_head *list, struct work_atoms *l, struct work_atoms *r) | 747 | thread_lat_cmp(struct list_head *list, struct work_atoms *l, struct work_atoms *r) |
| 873 | { | 748 | { |
| @@ -936,43 +811,45 @@ __thread_latency_insert(struct rb_root *root, struct work_atoms *data, | |||
| 936 | rb_insert_color(&data->node, root); | 811 | rb_insert_color(&data->node, root); |
| 937 | } | 812 | } |
| 938 | 813 | ||
| 939 | static void thread_atoms_insert(struct thread *thread) | 814 | static int thread_atoms_insert(struct perf_sched *sched, struct thread *thread) |
| 940 | { | 815 | { |
| 941 | struct work_atoms *atoms = zalloc(sizeof(*atoms)); | 816 | struct work_atoms *atoms = zalloc(sizeof(*atoms)); |
| 942 | if (!atoms) | 817 | if (!atoms) { |
| 943 | die("No memory"); | 818 | pr_err("No memory at %s\n", __func__); |
| 819 | return -1; | ||
| 820 | } | ||
| 944 | 821 | ||
| 945 | atoms->thread = thread; | 822 | atoms->thread = thread; |
| 946 | INIT_LIST_HEAD(&atoms->work_list); | 823 | INIT_LIST_HEAD(&atoms->work_list); |
| 947 | __thread_latency_insert(&atom_root, atoms, &cmp_pid); | 824 | __thread_latency_insert(&sched->atom_root, atoms, &sched->cmp_pid); |
| 825 | return 0; | ||
| 948 | } | 826 | } |
| 949 | 827 | ||
| 950 | static void | 828 | static int latency_fork_event(struct perf_sched *sched __maybe_unused, |
| 951 | latency_fork_event(struct trace_fork_event *fork_event __used, | 829 | struct perf_evsel *evsel __maybe_unused, |
| 952 | struct event_format *event __used, | 830 | struct perf_sample *sample __maybe_unused) |
| 953 | int cpu __used, | ||
| 954 | u64 timestamp __used, | ||
| 955 | struct thread *thread __used) | ||
| 956 | { | 831 | { |
| 957 | /* should insert the newcomer */ | 832 | /* should insert the newcomer */ |
| 833 | return 0; | ||
| 958 | } | 834 | } |
| 959 | 835 | ||
| 960 | __used | 836 | static char sched_out_state(u64 prev_state) |
| 961 | static char sched_out_state(struct trace_switch_event *switch_event) | ||
| 962 | { | 837 | { |
| 963 | const char *str = TASK_STATE_TO_CHAR_STR; | 838 | const char *str = TASK_STATE_TO_CHAR_STR; |
| 964 | 839 | ||
| 965 | return str[switch_event->prev_state]; | 840 | return str[prev_state]; |
| 966 | } | 841 | } |
| 967 | 842 | ||
| 968 | static void | 843 | static int |
| 969 | add_sched_out_event(struct work_atoms *atoms, | 844 | add_sched_out_event(struct work_atoms *atoms, |
| 970 | char run_state, | 845 | char run_state, |
| 971 | u64 timestamp) | 846 | u64 timestamp) |
| 972 | { | 847 | { |
| 973 | struct work_atom *atom = zalloc(sizeof(*atom)); | 848 | struct work_atom *atom = zalloc(sizeof(*atom)); |
| 974 | if (!atom) | 849 | if (!atom) { |
| 975 | die("Non memory"); | 850 | pr_err("Non memory at %s", __func__); |
| 851 | return -1; | ||
| 852 | } | ||
| 976 | 853 | ||
| 977 | atom->sched_out_time = timestamp; | 854 | atom->sched_out_time = timestamp; |
| 978 | 855 | ||
| @@ -982,10 +859,12 @@ add_sched_out_event(struct work_atoms *atoms, | |||
| 982 | } | 859 | } |
| 983 | 860 | ||
| 984 | list_add_tail(&atom->list, &atoms->work_list); | 861 | list_add_tail(&atom->list, &atoms->work_list); |
| 862 | return 0; | ||
| 985 | } | 863 | } |
| 986 | 864 | ||
| 987 | static void | 865 | static void |
| 988 | add_runtime_event(struct work_atoms *atoms, u64 delta, u64 timestamp __used) | 866 | add_runtime_event(struct work_atoms *atoms, u64 delta, |
| 867 | u64 timestamp __maybe_unused) | ||
| 989 | { | 868 | { |
| 990 | struct work_atom *atom; | 869 | struct work_atom *atom; |
| 991 | 870 | ||
| @@ -1028,106 +907,128 @@ add_sched_in_event(struct work_atoms *atoms, u64 timestamp) | |||
| 1028 | atoms->nb_atoms++; | 907 | atoms->nb_atoms++; |
| 1029 | } | 908 | } |
| 1030 | 909 | ||
| 1031 | static void | 910 | static int latency_switch_event(struct perf_sched *sched, |
| 1032 | latency_switch_event(struct trace_switch_event *switch_event, | 911 | struct perf_evsel *evsel, |
| 1033 | struct machine *machine, | 912 | struct perf_sample *sample, |
| 1034 | struct event_format *event __used, | 913 | struct machine *machine) |
| 1035 | int cpu, | ||
| 1036 | u64 timestamp, | ||
| 1037 | struct thread *thread __used) | ||
| 1038 | { | 914 | { |
| 915 | const u32 prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"), | ||
| 916 | next_pid = perf_evsel__intval(evsel, sample, "next_pid"); | ||
| 917 | const u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state"); | ||
| 1039 | struct work_atoms *out_events, *in_events; | 918 | struct work_atoms *out_events, *in_events; |
| 1040 | struct thread *sched_out, *sched_in; | 919 | struct thread *sched_out, *sched_in; |
| 1041 | u64 timestamp0; | 920 | u64 timestamp0, timestamp = sample->time; |
| 921 | int cpu = sample->cpu; | ||
| 1042 | s64 delta; | 922 | s64 delta; |
| 1043 | 923 | ||
| 1044 | BUG_ON(cpu >= MAX_CPUS || cpu < 0); | 924 | BUG_ON(cpu >= MAX_CPUS || cpu < 0); |
| 1045 | 925 | ||
| 1046 | timestamp0 = cpu_last_switched[cpu]; | 926 | timestamp0 = sched->cpu_last_switched[cpu]; |
| 1047 | cpu_last_switched[cpu] = timestamp; | 927 | sched->cpu_last_switched[cpu] = timestamp; |
| 1048 | if (timestamp0) | 928 | if (timestamp0) |
| 1049 | delta = timestamp - timestamp0; | 929 | delta = timestamp - timestamp0; |
| 1050 | else | 930 | else |
| 1051 | delta = 0; | 931 | delta = 0; |
| 1052 | 932 | ||
| 1053 | if (delta < 0) | 933 | if (delta < 0) { |
| 1054 | die("hm, delta: %" PRIu64 " < 0 ?\n", delta); | 934 | pr_err("hm, delta: %" PRIu64 " < 0 ?\n", delta); |
| 1055 | 935 | return -1; | |
| 936 | } | ||
| 1056 | 937 | ||
| 1057 | sched_out = machine__findnew_thread(machine, switch_event->prev_pid); | 938 | sched_out = machine__findnew_thread(machine, prev_pid); |
| 1058 | sched_in = machine__findnew_thread(machine, switch_event->next_pid); | 939 | sched_in = machine__findnew_thread(machine, next_pid); |
| 1059 | 940 | ||
| 1060 | out_events = thread_atoms_search(&atom_root, sched_out, &cmp_pid); | 941 | out_events = thread_atoms_search(&sched->atom_root, sched_out, &sched->cmp_pid); |
| 1061 | if (!out_events) { | 942 | if (!out_events) { |
| 1062 | thread_atoms_insert(sched_out); | 943 | if (thread_atoms_insert(sched, sched_out)) |
| 1063 | out_events = thread_atoms_search(&atom_root, sched_out, &cmp_pid); | 944 | return -1; |
| 1064 | if (!out_events) | 945 | out_events = thread_atoms_search(&sched->atom_root, sched_out, &sched->cmp_pid); |
| 1065 | die("out-event: Internal tree error"); | 946 | if (!out_events) { |
| 947 | pr_err("out-event: Internal tree error"); | ||
| 948 | return -1; | ||
| 949 | } | ||
| 1066 | } | 950 | } |
| 1067 | add_sched_out_event(out_events, sched_out_state(switch_event), timestamp); | 951 | if (add_sched_out_event(out_events, sched_out_state(prev_state), timestamp)) |
| 952 | return -1; | ||
| 1068 | 953 | ||
| 1069 | in_events = thread_atoms_search(&atom_root, sched_in, &cmp_pid); | 954 | in_events = thread_atoms_search(&sched->atom_root, sched_in, &sched->cmp_pid); |
| 1070 | if (!in_events) { | 955 | if (!in_events) { |
| 1071 | thread_atoms_insert(sched_in); | 956 | if (thread_atoms_insert(sched, sched_in)) |
| 1072 | in_events = thread_atoms_search(&atom_root, sched_in, &cmp_pid); | 957 | return -1; |
| 1073 | if (!in_events) | 958 | in_events = thread_atoms_search(&sched->atom_root, sched_in, &sched->cmp_pid); |
| 1074 | die("in-event: Internal tree error"); | 959 | if (!in_events) { |
| 960 | pr_err("in-event: Internal tree error"); | ||
| 961 | return -1; | ||
| 962 | } | ||
| 1075 | /* | 963 | /* |
| 1076 | * Take came in we have not heard about yet, | 964 | * Take came in we have not heard about yet, |
| 1077 | * add in an initial atom in runnable state: | 965 | * add in an initial atom in runnable state: |
| 1078 | */ | 966 | */ |
| 1079 | add_sched_out_event(in_events, 'R', timestamp); | 967 | if (add_sched_out_event(in_events, 'R', timestamp)) |
| 968 | return -1; | ||
| 1080 | } | 969 | } |
| 1081 | add_sched_in_event(in_events, timestamp); | 970 | add_sched_in_event(in_events, timestamp); |
| 971 | |||
| 972 | return 0; | ||
| 1082 | } | 973 | } |
| 1083 | 974 | ||
| 1084 | static void | 975 | static int latency_runtime_event(struct perf_sched *sched, |
| 1085 | latency_runtime_event(struct trace_runtime_event *runtime_event, | 976 | struct perf_evsel *evsel, |
| 1086 | struct machine *machine, | 977 | struct perf_sample *sample, |
| 1087 | struct event_format *event __used, | 978 | struct machine *machine) |
| 1088 | int cpu, | ||
| 1089 | u64 timestamp, | ||
| 1090 | struct thread *this_thread __used) | ||
| 1091 | { | 979 | { |
| 1092 | struct thread *thread = machine__findnew_thread(machine, runtime_event->pid); | 980 | const u32 pid = perf_evsel__intval(evsel, sample, "pid"); |
| 1093 | struct work_atoms *atoms = thread_atoms_search(&atom_root, thread, &cmp_pid); | 981 | const u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); |
| 982 | struct thread *thread = machine__findnew_thread(machine, pid); | ||
| 983 | struct work_atoms *atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid); | ||
| 984 | u64 timestamp = sample->time; | ||
| 985 | int cpu = sample->cpu; | ||
| 1094 | 986 | ||
| 1095 | BUG_ON(cpu >= MAX_CPUS || cpu < 0); | 987 | BUG_ON(cpu >= MAX_CPUS || cpu < 0); |
| 1096 | if (!atoms) { | 988 | if (!atoms) { |
| 1097 | thread_atoms_insert(thread); | 989 | if (thread_atoms_insert(sched, thread)) |
| 1098 | atoms = thread_atoms_search(&atom_root, thread, &cmp_pid); | 990 | return -1; |
| 1099 | if (!atoms) | 991 | atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid); |
| 1100 | die("in-event: Internal tree error"); | 992 | if (!atoms) { |
| 1101 | add_sched_out_event(atoms, 'R', timestamp); | 993 | pr_err("in-event: Internal tree error"); |
| 994 | return -1; | ||
| 995 | } | ||
| 996 | if (add_sched_out_event(atoms, 'R', timestamp)) | ||
| 997 | return -1; | ||
| 1102 | } | 998 | } |
| 1103 | 999 | ||
| 1104 | add_runtime_event(atoms, runtime_event->runtime, timestamp); | 1000 | add_runtime_event(atoms, runtime, timestamp); |
| 1001 | return 0; | ||
| 1105 | } | 1002 | } |
| 1106 | 1003 | ||
| 1107 | static void | 1004 | static int latency_wakeup_event(struct perf_sched *sched, |
| 1108 | latency_wakeup_event(struct trace_wakeup_event *wakeup_event, | 1005 | struct perf_evsel *evsel, |
| 1109 | struct machine *machine, | 1006 | struct perf_sample *sample, |
| 1110 | struct event_format *__event __used, | 1007 | struct machine *machine) |
| 1111 | int cpu __used, | ||
| 1112 | u64 timestamp, | ||
| 1113 | struct thread *thread __used) | ||
| 1114 | { | 1008 | { |
| 1009 | const u32 pid = perf_evsel__intval(evsel, sample, "pid"), | ||
| 1010 | success = perf_evsel__intval(evsel, sample, "success"); | ||
| 1115 | struct work_atoms *atoms; | 1011 | struct work_atoms *atoms; |
| 1116 | struct work_atom *atom; | 1012 | struct work_atom *atom; |
| 1117 | struct thread *wakee; | 1013 | struct thread *wakee; |
| 1014 | u64 timestamp = sample->time; | ||
| 1118 | 1015 | ||
| 1119 | /* Note for later, it may be interesting to observe the failing cases */ | 1016 | /* Note for later, it may be interesting to observe the failing cases */ |
| 1120 | if (!wakeup_event->success) | 1017 | if (!success) |
| 1121 | return; | 1018 | return 0; |
| 1122 | 1019 | ||
| 1123 | wakee = machine__findnew_thread(machine, wakeup_event->pid); | 1020 | wakee = machine__findnew_thread(machine, pid); |
| 1124 | atoms = thread_atoms_search(&atom_root, wakee, &cmp_pid); | 1021 | atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid); |
| 1125 | if (!atoms) { | 1022 | if (!atoms) { |
| 1126 | thread_atoms_insert(wakee); | 1023 | if (thread_atoms_insert(sched, wakee)) |
| 1127 | atoms = thread_atoms_search(&atom_root, wakee, &cmp_pid); | 1024 | return -1; |
| 1128 | if (!atoms) | 1025 | atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid); |
| 1129 | die("wakeup-event: Internal tree error"); | 1026 | if (!atoms) { |
| 1130 | add_sched_out_event(atoms, 'S', timestamp); | 1027 | pr_err("wakeup-event: Internal tree error"); |
| 1028 | return -1; | ||
| 1029 | } | ||
| 1030 | if (add_sched_out_event(atoms, 'S', timestamp)) | ||
| 1031 | return -1; | ||
| 1131 | } | 1032 | } |
| 1132 | 1033 | ||
| 1133 | BUG_ON(list_empty(&atoms->work_list)); | 1034 | BUG_ON(list_empty(&atoms->work_list)); |
| @@ -1139,27 +1040,27 @@ latency_wakeup_event(struct trace_wakeup_event *wakeup_event, | |||
| 1139 | * one CPU, or are only looking at only one, so don't | 1040 | * one CPU, or are only looking at only one, so don't |
| 1140 | * make useless noise. | 1041 | * make useless noise. |
| 1141 | */ | 1042 | */ |
| 1142 | if (profile_cpu == -1 && atom->state != THREAD_SLEEPING) | 1043 | if (sched->profile_cpu == -1 && atom->state != THREAD_SLEEPING) |
| 1143 | nr_state_machine_bugs++; | 1044 | sched->nr_state_machine_bugs++; |
| 1144 | 1045 | ||
| 1145 | nr_timestamps++; | 1046 | sched->nr_timestamps++; |
| 1146 | if (atom->sched_out_time > timestamp) { | 1047 | if (atom->sched_out_time > timestamp) { |
| 1147 | nr_unordered_timestamps++; | 1048 | sched->nr_unordered_timestamps++; |
| 1148 | return; | 1049 | return 0; |
| 1149 | } | 1050 | } |
| 1150 | 1051 | ||
| 1151 | atom->state = THREAD_WAIT_CPU; | 1052 | atom->state = THREAD_WAIT_CPU; |
| 1152 | atom->wake_up_time = timestamp; | 1053 | atom->wake_up_time = timestamp; |
| 1054 | return 0; | ||
| 1153 | } | 1055 | } |
| 1154 | 1056 | ||
| 1155 | static void | 1057 | static int latency_migrate_task_event(struct perf_sched *sched, |
| 1156 | latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event, | 1058 | struct perf_evsel *evsel, |
| 1157 | struct machine *machine, | 1059 | struct perf_sample *sample, |
| 1158 | struct event_format *__event __used, | 1060 | struct machine *machine) |
| 1159 | int cpu __used, | ||
| 1160 | u64 timestamp, | ||
| 1161 | struct thread *thread __used) | ||
| 1162 | { | 1061 | { |
| 1062 | const u32 pid = perf_evsel__intval(evsel, sample, "pid"); | ||
| 1063 | u64 timestamp = sample->time; | ||
| 1163 | struct work_atoms *atoms; | 1064 | struct work_atoms *atoms; |
| 1164 | struct work_atom *atom; | 1065 | struct work_atom *atom; |
| 1165 | struct thread *migrant; | 1066 | struct thread *migrant; |
| @@ -1167,18 +1068,22 @@ latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event, | |||
| 1167 | /* | 1068 | /* |
| 1168 | * Only need to worry about migration when profiling one CPU. | 1069 | * Only need to worry about migration when profiling one CPU. |
| 1169 | */ | 1070 | */ |
| 1170 | if (profile_cpu == -1) | 1071 | if (sched->profile_cpu == -1) |
| 1171 | return; | 1072 | return 0; |
| 1172 | 1073 | ||
| 1173 | migrant = machine__findnew_thread(machine, migrate_task_event->pid); | 1074 | migrant = machine__findnew_thread(machine, pid); |
| 1174 | atoms = thread_atoms_search(&atom_root, migrant, &cmp_pid); | 1075 | atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid); |
| 1175 | if (!atoms) { | 1076 | if (!atoms) { |
| 1176 | thread_atoms_insert(migrant); | 1077 | if (thread_atoms_insert(sched, migrant)) |
| 1177 | register_pid(migrant->pid, migrant->comm); | 1078 | return -1; |
| 1178 | atoms = thread_atoms_search(&atom_root, migrant, &cmp_pid); | 1079 | register_pid(sched, migrant->pid, migrant->comm); |
| 1179 | if (!atoms) | 1080 | atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid); |
| 1180 | die("migration-event: Internal tree error"); | 1081 | if (!atoms) { |
| 1181 | add_sched_out_event(atoms, 'R', timestamp); | 1082 | pr_err("migration-event: Internal tree error"); |
| 1083 | return -1; | ||
| 1084 | } | ||
| 1085 | if (add_sched_out_event(atoms, 'R', timestamp)) | ||
| 1086 | return -1; | ||
| 1182 | } | 1087 | } |
| 1183 | 1088 | ||
| 1184 | BUG_ON(list_empty(&atoms->work_list)); | 1089 | BUG_ON(list_empty(&atoms->work_list)); |
| @@ -1186,21 +1091,15 @@ latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event, | |||
| 1186 | atom = list_entry(atoms->work_list.prev, struct work_atom, list); | 1091 | atom = list_entry(atoms->work_list.prev, struct work_atom, list); |
| 1187 | atom->sched_in_time = atom->sched_out_time = atom->wake_up_time = timestamp; | 1092 | atom->sched_in_time = atom->sched_out_time = atom->wake_up_time = timestamp; |
| 1188 | 1093 | ||
| 1189 | nr_timestamps++; | 1094 | sched->nr_timestamps++; |
| 1190 | 1095 | ||
| 1191 | if (atom->sched_out_time > timestamp) | 1096 | if (atom->sched_out_time > timestamp) |
| 1192 | nr_unordered_timestamps++; | 1097 | sched->nr_unordered_timestamps++; |
| 1193 | } | ||
| 1194 | 1098 | ||
| 1195 | static struct trace_sched_handler lat_ops = { | 1099 | return 0; |
| 1196 | .wakeup_event = latency_wakeup_event, | 1100 | } |
| 1197 | .switch_event = latency_switch_event, | ||
| 1198 | .runtime_event = latency_runtime_event, | ||
| 1199 | .fork_event = latency_fork_event, | ||
| 1200 | .migrate_task_event = latency_migrate_task_event, | ||
| 1201 | }; | ||
| 1202 | 1101 | ||
| 1203 | static void output_lat_thread(struct work_atoms *work_list) | 1102 | static void output_lat_thread(struct perf_sched *sched, struct work_atoms *work_list) |
| 1204 | { | 1103 | { |
| 1205 | int i; | 1104 | int i; |
| 1206 | int ret; | 1105 | int ret; |
| @@ -1214,8 +1113,8 @@ static void output_lat_thread(struct work_atoms *work_list) | |||
| 1214 | if (!strcmp(work_list->thread->comm, "swapper")) | 1113 | if (!strcmp(work_list->thread->comm, "swapper")) |
| 1215 | return; | 1114 | return; |
| 1216 | 1115 | ||
| 1217 | all_runtime += work_list->total_runtime; | 1116 | sched->all_runtime += work_list->total_runtime; |
| 1218 | all_count += work_list->nb_atoms; | 1117 | sched->all_count += work_list->nb_atoms; |
| 1219 | 1118 | ||
| 1220 | ret = printf(" %s:%d ", work_list->thread->comm, work_list->thread->pid); | 1119 | ret = printf(" %s:%d ", work_list->thread->comm, work_list->thread->pid); |
| 1221 | 1120 | ||
| @@ -1241,11 +1140,6 @@ static int pid_cmp(struct work_atoms *l, struct work_atoms *r) | |||
| 1241 | return 0; | 1140 | return 0; |
| 1242 | } | 1141 | } |
| 1243 | 1142 | ||
| 1244 | static struct sort_dimension pid_sort_dimension = { | ||
| 1245 | .name = "pid", | ||
| 1246 | .cmp = pid_cmp, | ||
| 1247 | }; | ||
| 1248 | |||
| 1249 | static int avg_cmp(struct work_atoms *l, struct work_atoms *r) | 1143 | static int avg_cmp(struct work_atoms *l, struct work_atoms *r) |
| 1250 | { | 1144 | { |
| 1251 | u64 avgl, avgr; | 1145 | u64 avgl, avgr; |
| @@ -1267,11 +1161,6 @@ static int avg_cmp(struct work_atoms *l, struct work_atoms *r) | |||
| 1267 | return 0; | 1161 | return 0; |
| 1268 | } | 1162 | } |
| 1269 | 1163 | ||
| 1270 | static struct sort_dimension avg_sort_dimension = { | ||
| 1271 | .name = "avg", | ||
| 1272 | .cmp = avg_cmp, | ||
| 1273 | }; | ||
| 1274 | |||
| 1275 | static int max_cmp(struct work_atoms *l, struct work_atoms *r) | 1164 | static int max_cmp(struct work_atoms *l, struct work_atoms *r) |
| 1276 | { | 1165 | { |
| 1277 | if (l->max_lat < r->max_lat) | 1166 | if (l->max_lat < r->max_lat) |
| @@ -1282,11 +1171,6 @@ static int max_cmp(struct work_atoms *l, struct work_atoms *r) | |||
| 1282 | return 0; | 1171 | return 0; |
| 1283 | } | 1172 | } |
| 1284 | 1173 | ||
| 1285 | static struct sort_dimension max_sort_dimension = { | ||
| 1286 | .name = "max", | ||
| 1287 | .cmp = max_cmp, | ||
| 1288 | }; | ||
| 1289 | |||
| 1290 | static int switch_cmp(struct work_atoms *l, struct work_atoms *r) | 1174 | static int switch_cmp(struct work_atoms *l, struct work_atoms *r) |
| 1291 | { | 1175 | { |
| 1292 | if (l->nb_atoms < r->nb_atoms) | 1176 | if (l->nb_atoms < r->nb_atoms) |
| @@ -1297,11 +1181,6 @@ static int switch_cmp(struct work_atoms *l, struct work_atoms *r) | |||
| 1297 | return 0; | 1181 | return 0; |
| 1298 | } | 1182 | } |
| 1299 | 1183 | ||
| 1300 | static struct sort_dimension switch_sort_dimension = { | ||
| 1301 | .name = "switch", | ||
| 1302 | .cmp = switch_cmp, | ||
| 1303 | }; | ||
| 1304 | |||
| 1305 | static int runtime_cmp(struct work_atoms *l, struct work_atoms *r) | 1184 | static int runtime_cmp(struct work_atoms *l, struct work_atoms *r) |
| 1306 | { | 1185 | { |
| 1307 | if (l->total_runtime < r->total_runtime) | 1186 | if (l->total_runtime < r->total_runtime) |
| @@ -1312,28 +1191,38 @@ static int runtime_cmp(struct work_atoms *l, struct work_atoms *r) | |||
| 1312 | return 0; | 1191 | return 0; |
| 1313 | } | 1192 | } |
| 1314 | 1193 | ||
| 1315 | static struct sort_dimension runtime_sort_dimension = { | ||
| 1316 | .name = "runtime", | ||
| 1317 | .cmp = runtime_cmp, | ||
| 1318 | }; | ||
| 1319 | |||
| 1320 | static struct sort_dimension *available_sorts[] = { | ||
| 1321 | &pid_sort_dimension, | ||
| 1322 | &avg_sort_dimension, | ||
| 1323 | &max_sort_dimension, | ||
| 1324 | &switch_sort_dimension, | ||
| 1325 | &runtime_sort_dimension, | ||
| 1326 | }; | ||
| 1327 | |||
| 1328 | #define NB_AVAILABLE_SORTS (int)(sizeof(available_sorts) / sizeof(struct sort_dimension *)) | ||
| 1329 | |||
| 1330 | static LIST_HEAD(sort_list); | ||
| 1331 | |||
| 1332 | static int sort_dimension__add(const char *tok, struct list_head *list) | 1194 | static int sort_dimension__add(const char *tok, struct list_head *list) |
| 1333 | { | 1195 | { |
| 1334 | int i; | 1196 | size_t i; |
| 1197 | static struct sort_dimension avg_sort_dimension = { | ||
| 1198 | .name = "avg", | ||
| 1199 | .cmp = avg_cmp, | ||
| 1200 | }; | ||
| 1201 | static struct sort_dimension max_sort_dimension = { | ||
| 1202 | .name = "max", | ||
| 1203 | .cmp = max_cmp, | ||
| 1204 | }; | ||
| 1205 | static struct sort_dimension pid_sort_dimension = { | ||
| 1206 | .name = "pid", | ||
| 1207 | .cmp = pid_cmp, | ||
| 1208 | }; | ||
| 1209 | static struct sort_dimension runtime_sort_dimension = { | ||
| 1210 | .name = "runtime", | ||
| 1211 | .cmp = runtime_cmp, | ||
| 1212 | }; | ||
| 1213 | static struct sort_dimension switch_sort_dimension = { | ||
| 1214 | .name = "switch", | ||
| 1215 | .cmp = switch_cmp, | ||
| 1216 | }; | ||
| 1217 | struct sort_dimension *available_sorts[] = { | ||
| 1218 | &pid_sort_dimension, | ||
| 1219 | &avg_sort_dimension, | ||
| 1220 | &max_sort_dimension, | ||
| 1221 | &switch_sort_dimension, | ||
| 1222 | &runtime_sort_dimension, | ||
| 1223 | }; | ||
| 1335 | 1224 | ||
| 1336 | for (i = 0; i < NB_AVAILABLE_SORTS; i++) { | 1225 | for (i = 0; i < ARRAY_SIZE(available_sorts); i++) { |
| 1337 | if (!strcmp(available_sorts[i]->name, tok)) { | 1226 | if (!strcmp(available_sorts[i]->name, tok)) { |
| 1338 | list_add_tail(&available_sorts[i]->list, list); | 1227 | list_add_tail(&available_sorts[i]->list, list); |
| 1339 | 1228 | ||
| @@ -1344,126 +1233,97 @@ static int sort_dimension__add(const char *tok, struct list_head *list) | |||
| 1344 | return -1; | 1233 | return -1; |
| 1345 | } | 1234 | } |
| 1346 | 1235 | ||
| 1347 | static void setup_sorting(void); | 1236 | static void perf_sched__sort_lat(struct perf_sched *sched) |
| 1348 | |||
| 1349 | static void sort_lat(void) | ||
| 1350 | { | 1237 | { |
| 1351 | struct rb_node *node; | 1238 | struct rb_node *node; |
| 1352 | 1239 | ||
| 1353 | for (;;) { | 1240 | for (;;) { |
| 1354 | struct work_atoms *data; | 1241 | struct work_atoms *data; |
| 1355 | node = rb_first(&atom_root); | 1242 | node = rb_first(&sched->atom_root); |
| 1356 | if (!node) | 1243 | if (!node) |
| 1357 | break; | 1244 | break; |
| 1358 | 1245 | ||
| 1359 | rb_erase(node, &atom_root); | 1246 | rb_erase(node, &sched->atom_root); |
| 1360 | data = rb_entry(node, struct work_atoms, node); | 1247 | data = rb_entry(node, struct work_atoms, node); |
| 1361 | __thread_latency_insert(&sorted_atom_root, data, &sort_list); | 1248 | __thread_latency_insert(&sched->sorted_atom_root, data, &sched->sort_list); |
| 1362 | } | 1249 | } |
| 1363 | } | 1250 | } |
| 1364 | 1251 | ||
| 1365 | static struct trace_sched_handler *trace_handler; | 1252 | static int process_sched_wakeup_event(struct perf_tool *tool, |
| 1366 | 1253 | struct perf_evsel *evsel, | |
| 1367 | static void | 1254 | struct perf_sample *sample, |
| 1368 | process_sched_wakeup_event(struct perf_tool *tool __used, | 1255 | struct machine *machine) |
| 1369 | struct event_format *event, | ||
| 1370 | struct perf_sample *sample, | ||
| 1371 | struct machine *machine, | ||
| 1372 | struct thread *thread) | ||
| 1373 | { | 1256 | { |
| 1374 | void *data = sample->raw_data; | 1257 | struct perf_sched *sched = container_of(tool, struct perf_sched, tool); |
| 1375 | struct trace_wakeup_event wakeup_event; | ||
| 1376 | |||
| 1377 | FILL_COMMON_FIELDS(wakeup_event, event, data); | ||
| 1378 | 1258 | ||
| 1379 | FILL_ARRAY(wakeup_event, comm, event, data); | 1259 | if (sched->tp_handler->wakeup_event) |
| 1380 | FILL_FIELD(wakeup_event, pid, event, data); | 1260 | return sched->tp_handler->wakeup_event(sched, evsel, sample, machine); |
| 1381 | FILL_FIELD(wakeup_event, prio, event, data); | ||
| 1382 | FILL_FIELD(wakeup_event, success, event, data); | ||
| 1383 | FILL_FIELD(wakeup_event, cpu, event, data); | ||
| 1384 | 1261 | ||
| 1385 | if (trace_handler->wakeup_event) | 1262 | return 0; |
| 1386 | trace_handler->wakeup_event(&wakeup_event, machine, event, | ||
| 1387 | sample->cpu, sample->time, thread); | ||
| 1388 | } | 1263 | } |
| 1389 | 1264 | ||
| 1390 | /* | 1265 | static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel, |
| 1391 | * Track the current task - that way we can know whether there's any | 1266 | struct perf_sample *sample, struct machine *machine) |
| 1392 | * weird events, such as a task being switched away that is not current. | ||
| 1393 | */ | ||
| 1394 | static int max_cpu; | ||
| 1395 | |||
| 1396 | static u32 curr_pid[MAX_CPUS] = { [0 ... MAX_CPUS-1] = -1 }; | ||
| 1397 | |||
| 1398 | static struct thread *curr_thread[MAX_CPUS]; | ||
| 1399 | |||
| 1400 | static char next_shortname1 = 'A'; | ||
| 1401 | static char next_shortname2 = '0'; | ||
| 1402 | |||
| 1403 | static void | ||
| 1404 | map_switch_event(struct trace_switch_event *switch_event, | ||
| 1405 | struct machine *machine, | ||
| 1406 | struct event_format *event __used, | ||
| 1407 | int this_cpu, | ||
| 1408 | u64 timestamp, | ||
| 1409 | struct thread *thread __used) | ||
| 1410 | { | 1267 | { |
| 1411 | struct thread *sched_out __used, *sched_in; | 1268 | const u32 prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"), |
| 1269 | next_pid = perf_evsel__intval(evsel, sample, "next_pid"); | ||
| 1270 | struct thread *sched_out __maybe_unused, *sched_in; | ||
| 1412 | int new_shortname; | 1271 | int new_shortname; |
| 1413 | u64 timestamp0; | 1272 | u64 timestamp0, timestamp = sample->time; |
| 1414 | s64 delta; | 1273 | s64 delta; |
| 1415 | int cpu; | 1274 | int cpu, this_cpu = sample->cpu; |
| 1416 | 1275 | ||
| 1417 | BUG_ON(this_cpu >= MAX_CPUS || this_cpu < 0); | 1276 | BUG_ON(this_cpu >= MAX_CPUS || this_cpu < 0); |
| 1418 | 1277 | ||
| 1419 | if (this_cpu > max_cpu) | 1278 | if (this_cpu > sched->max_cpu) |
| 1420 | max_cpu = this_cpu; | 1279 | sched->max_cpu = this_cpu; |
| 1421 | 1280 | ||
| 1422 | timestamp0 = cpu_last_switched[this_cpu]; | 1281 | timestamp0 = sched->cpu_last_switched[this_cpu]; |
| 1423 | cpu_last_switched[this_cpu] = timestamp; | 1282 | sched->cpu_last_switched[this_cpu] = timestamp; |
| 1424 | if (timestamp0) | 1283 | if (timestamp0) |
| 1425 | delta = timestamp - timestamp0; | 1284 | delta = timestamp - timestamp0; |
| 1426 | else | 1285 | else |
| 1427 | delta = 0; | 1286 | delta = 0; |
| 1428 | 1287 | ||
| 1429 | if (delta < 0) | 1288 | if (delta < 0) { |
| 1430 | die("hm, delta: %" PRIu64 " < 0 ?\n", delta); | 1289 | pr_err("hm, delta: %" PRIu64 " < 0 ?\n", delta); |
| 1431 | 1290 | return -1; | |
| 1291 | } | ||
| 1432 | 1292 | ||
| 1433 | sched_out = machine__findnew_thread(machine, switch_event->prev_pid); | 1293 | sched_out = machine__findnew_thread(machine, prev_pid); |
| 1434 | sched_in = machine__findnew_thread(machine, switch_event->next_pid); | 1294 | sched_in = machine__findnew_thread(machine, next_pid); |
| 1435 | 1295 | ||
| 1436 | curr_thread[this_cpu] = sched_in; | 1296 | sched->curr_thread[this_cpu] = sched_in; |
| 1437 | 1297 | ||
| 1438 | printf(" "); | 1298 | printf(" "); |
| 1439 | 1299 | ||
| 1440 | new_shortname = 0; | 1300 | new_shortname = 0; |
| 1441 | if (!sched_in->shortname[0]) { | 1301 | if (!sched_in->shortname[0]) { |
| 1442 | sched_in->shortname[0] = next_shortname1; | 1302 | sched_in->shortname[0] = sched->next_shortname1; |
| 1443 | sched_in->shortname[1] = next_shortname2; | 1303 | sched_in->shortname[1] = sched->next_shortname2; |
| 1444 | 1304 | ||
| 1445 | if (next_shortname1 < 'Z') { | 1305 | if (sched->next_shortname1 < 'Z') { |
| 1446 | next_shortname1++; | 1306 | sched->next_shortname1++; |
| 1447 | } else { | 1307 | } else { |
| 1448 | next_shortname1='A'; | 1308 | sched->next_shortname1='A'; |
| 1449 | if (next_shortname2 < '9') { | 1309 | if (sched->next_shortname2 < '9') { |
| 1450 | next_shortname2++; | 1310 | sched->next_shortname2++; |
| 1451 | } else { | 1311 | } else { |
| 1452 | next_shortname2='0'; | 1312 | sched->next_shortname2='0'; |
| 1453 | } | 1313 | } |
| 1454 | } | 1314 | } |
| 1455 | new_shortname = 1; | 1315 | new_shortname = 1; |
| 1456 | } | 1316 | } |
| 1457 | 1317 | ||
| 1458 | for (cpu = 0; cpu <= max_cpu; cpu++) { | 1318 | for (cpu = 0; cpu <= sched->max_cpu; cpu++) { |
| 1459 | if (cpu != this_cpu) | 1319 | if (cpu != this_cpu) |
| 1460 | printf(" "); | 1320 | printf(" "); |
| 1461 | else | 1321 | else |
| 1462 | printf("*"); | 1322 | printf("*"); |
| 1463 | 1323 | ||
| 1464 | if (curr_thread[cpu]) { | 1324 | if (sched->curr_thread[cpu]) { |
| 1465 | if (curr_thread[cpu]->pid) | 1325 | if (sched->curr_thread[cpu]->pid) |
| 1466 | printf("%2s ", curr_thread[cpu]->shortname); | 1326 | printf("%2s ", sched->curr_thread[cpu]->shortname); |
| 1467 | else | 1327 | else |
| 1468 | printf(". "); | 1328 | printf(". "); |
| 1469 | } else | 1329 | } else |
| @@ -1477,134 +1337,97 @@ map_switch_event(struct trace_switch_event *switch_event, | |||
| 1477 | } else { | 1337 | } else { |
| 1478 | printf("\n"); | 1338 | printf("\n"); |
| 1479 | } | 1339 | } |
| 1340 | |||
| 1341 | return 0; | ||
| 1480 | } | 1342 | } |
| 1481 | 1343 | ||
| 1482 | static void | 1344 | static int process_sched_switch_event(struct perf_tool *tool, |
| 1483 | process_sched_switch_event(struct perf_tool *tool __used, | 1345 | struct perf_evsel *evsel, |
| 1484 | struct event_format *event, | 1346 | struct perf_sample *sample, |
| 1485 | struct perf_sample *sample, | 1347 | struct machine *machine) |
| 1486 | struct machine *machine, | ||
| 1487 | struct thread *thread) | ||
| 1488 | { | 1348 | { |
| 1489 | int this_cpu = sample->cpu; | 1349 | struct perf_sched *sched = container_of(tool, struct perf_sched, tool); |
| 1490 | void *data = sample->raw_data; | 1350 | int this_cpu = sample->cpu, err = 0; |
| 1491 | struct trace_switch_event switch_event; | 1351 | u32 prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"), |
| 1492 | 1352 | next_pid = perf_evsel__intval(evsel, sample, "next_pid"); | |
| 1493 | FILL_COMMON_FIELDS(switch_event, event, data); | ||
| 1494 | |||
| 1495 | FILL_ARRAY(switch_event, prev_comm, event, data); | ||
| 1496 | FILL_FIELD(switch_event, prev_pid, event, data); | ||
| 1497 | FILL_FIELD(switch_event, prev_prio, event, data); | ||
| 1498 | FILL_FIELD(switch_event, prev_state, event, data); | ||
| 1499 | FILL_ARRAY(switch_event, next_comm, event, data); | ||
| 1500 | FILL_FIELD(switch_event, next_pid, event, data); | ||
| 1501 | FILL_FIELD(switch_event, next_prio, event, data); | ||
| 1502 | 1353 | ||
| 1503 | if (curr_pid[this_cpu] != (u32)-1) { | 1354 | if (sched->curr_pid[this_cpu] != (u32)-1) { |
| 1504 | /* | 1355 | /* |
| 1505 | * Are we trying to switch away a PID that is | 1356 | * Are we trying to switch away a PID that is |
| 1506 | * not current? | 1357 | * not current? |
| 1507 | */ | 1358 | */ |
| 1508 | if (curr_pid[this_cpu] != switch_event.prev_pid) | 1359 | if (sched->curr_pid[this_cpu] != prev_pid) |
| 1509 | nr_context_switch_bugs++; | 1360 | sched->nr_context_switch_bugs++; |
| 1510 | } | 1361 | } |
| 1511 | if (trace_handler->switch_event) | ||
| 1512 | trace_handler->switch_event(&switch_event, machine, event, | ||
| 1513 | this_cpu, sample->time, thread); | ||
| 1514 | 1362 | ||
| 1515 | curr_pid[this_cpu] = switch_event.next_pid; | 1363 | if (sched->tp_handler->switch_event) |
| 1364 | err = sched->tp_handler->switch_event(sched, evsel, sample, machine); | ||
| 1365 | |||
| 1366 | sched->curr_pid[this_cpu] = next_pid; | ||
| 1367 | return err; | ||
| 1516 | } | 1368 | } |
| 1517 | 1369 | ||
| 1518 | static void | 1370 | static int process_sched_runtime_event(struct perf_tool *tool, |
| 1519 | process_sched_runtime_event(struct perf_tool *tool __used, | 1371 | struct perf_evsel *evsel, |
| 1520 | struct event_format *event, | 1372 | struct perf_sample *sample, |
| 1521 | struct perf_sample *sample, | 1373 | struct machine *machine) |
| 1522 | struct machine *machine, | ||
| 1523 | struct thread *thread) | ||
| 1524 | { | 1374 | { |
| 1525 | void *data = sample->raw_data; | 1375 | struct perf_sched *sched = container_of(tool, struct perf_sched, tool); |
| 1526 | struct trace_runtime_event runtime_event; | ||
| 1527 | 1376 | ||
| 1528 | FILL_ARRAY(runtime_event, comm, event, data); | 1377 | if (sched->tp_handler->runtime_event) |
| 1529 | FILL_FIELD(runtime_event, pid, event, data); | 1378 | return sched->tp_handler->runtime_event(sched, evsel, sample, machine); |
| 1530 | FILL_FIELD(runtime_event, runtime, event, data); | ||
| 1531 | FILL_FIELD(runtime_event, vruntime, event, data); | ||
| 1532 | 1379 | ||
| 1533 | if (trace_handler->runtime_event) | 1380 | return 0; |
| 1534 | trace_handler->runtime_event(&runtime_event, machine, event, | ||
| 1535 | sample->cpu, sample->time, thread); | ||
| 1536 | } | 1381 | } |
| 1537 | 1382 | ||
| 1538 | static void | 1383 | static int process_sched_fork_event(struct perf_tool *tool, |
| 1539 | process_sched_fork_event(struct perf_tool *tool __used, | 1384 | struct perf_evsel *evsel, |
| 1540 | struct event_format *event, | 1385 | struct perf_sample *sample, |
| 1541 | struct perf_sample *sample, | 1386 | struct machine *machine __maybe_unused) |
| 1542 | struct machine *machine __used, | ||
| 1543 | struct thread *thread) | ||
| 1544 | { | 1387 | { |
| 1545 | void *data = sample->raw_data; | 1388 | struct perf_sched *sched = container_of(tool, struct perf_sched, tool); |
| 1546 | struct trace_fork_event fork_event; | ||
| 1547 | |||
| 1548 | FILL_COMMON_FIELDS(fork_event, event, data); | ||
| 1549 | 1389 | ||
| 1550 | FILL_ARRAY(fork_event, parent_comm, event, data); | 1390 | if (sched->tp_handler->fork_event) |
| 1551 | FILL_FIELD(fork_event, parent_pid, event, data); | 1391 | return sched->tp_handler->fork_event(sched, evsel, sample); |
| 1552 | FILL_ARRAY(fork_event, child_comm, event, data); | ||
| 1553 | FILL_FIELD(fork_event, child_pid, event, data); | ||
| 1554 | 1392 | ||
| 1555 | if (trace_handler->fork_event) | 1393 | return 0; |
| 1556 | trace_handler->fork_event(&fork_event, event, | ||
| 1557 | sample->cpu, sample->time, thread); | ||
| 1558 | } | 1394 | } |
| 1559 | 1395 | ||
| 1560 | static void | 1396 | static int process_sched_exit_event(struct perf_tool *tool __maybe_unused, |
| 1561 | process_sched_exit_event(struct perf_tool *tool __used, | 1397 | struct perf_evsel *evsel, |
| 1562 | struct event_format *event, | 1398 | struct perf_sample *sample __maybe_unused, |
| 1563 | struct perf_sample *sample __used, | 1399 | struct machine *machine __maybe_unused) |
| 1564 | struct machine *machine __used, | ||
| 1565 | struct thread *thread __used) | ||
| 1566 | { | 1400 | { |
| 1567 | if (verbose) | 1401 | pr_debug("sched_exit event %p\n", evsel); |
| 1568 | printf("sched_exit event %p\n", event); | 1402 | return 0; |
| 1569 | } | 1403 | } |
| 1570 | 1404 | ||
| 1571 | static void | 1405 | static int process_sched_migrate_task_event(struct perf_tool *tool, |
| 1572 | process_sched_migrate_task_event(struct perf_tool *tool __used, | 1406 | struct perf_evsel *evsel, |
| 1573 | struct event_format *event, | 1407 | struct perf_sample *sample, |
| 1574 | struct perf_sample *sample, | 1408 | struct machine *machine) |
| 1575 | struct machine *machine, | ||
| 1576 | struct thread *thread) | ||
| 1577 | { | 1409 | { |
| 1578 | void *data = sample->raw_data; | 1410 | struct perf_sched *sched = container_of(tool, struct perf_sched, tool); |
| 1579 | struct trace_migrate_task_event migrate_task_event; | ||
| 1580 | |||
| 1581 | FILL_COMMON_FIELDS(migrate_task_event, event, data); | ||
| 1582 | 1411 | ||
| 1583 | FILL_ARRAY(migrate_task_event, comm, event, data); | 1412 | if (sched->tp_handler->migrate_task_event) |
| 1584 | FILL_FIELD(migrate_task_event, pid, event, data); | 1413 | return sched->tp_handler->migrate_task_event(sched, evsel, sample, machine); |
| 1585 | FILL_FIELD(migrate_task_event, prio, event, data); | ||
| 1586 | FILL_FIELD(migrate_task_event, cpu, event, data); | ||
| 1587 | 1414 | ||
| 1588 | if (trace_handler->migrate_task_event) | 1415 | return 0; |
| 1589 | trace_handler->migrate_task_event(&migrate_task_event, machine, | ||
| 1590 | event, sample->cpu, | ||
| 1591 | sample->time, thread); | ||
| 1592 | } | 1416 | } |
| 1593 | 1417 | ||
| 1594 | typedef void (*tracepoint_handler)(struct perf_tool *tool, struct event_format *event, | 1418 | typedef int (*tracepoint_handler)(struct perf_tool *tool, |
| 1595 | struct perf_sample *sample, | 1419 | struct perf_evsel *evsel, |
| 1596 | struct machine *machine, | 1420 | struct perf_sample *sample, |
| 1597 | struct thread *thread); | 1421 | struct machine *machine); |
| 1598 | 1422 | ||
| 1599 | static int perf_sched__process_tracepoint_sample(struct perf_tool *tool, | 1423 | static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_unused, |
| 1600 | union perf_event *event __used, | 1424 | union perf_event *event __maybe_unused, |
| 1601 | struct perf_sample *sample, | 1425 | struct perf_sample *sample, |
| 1602 | struct perf_evsel *evsel, | 1426 | struct perf_evsel *evsel, |
| 1603 | struct machine *machine) | 1427 | struct machine *machine) |
| 1604 | { | 1428 | { |
| 1605 | struct perf_sched *sched = container_of(tool, struct perf_sched, tool); | ||
| 1606 | struct pevent *pevent = sched->session->pevent; | ||
| 1607 | struct thread *thread = machine__findnew_thread(machine, sample->pid); | 1429 | struct thread *thread = machine__findnew_thread(machine, sample->pid); |
| 1430 | int err = 0; | ||
| 1608 | 1431 | ||
| 1609 | if (thread == NULL) { | 1432 | if (thread == NULL) { |
| 1610 | pr_debug("problem processing %s event, skipping it.\n", | 1433 | pr_debug("problem processing %s event, skipping it.\n", |
| @@ -1617,30 +1440,15 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool, | |||
| 1617 | 1440 | ||
| 1618 | if (evsel->handler.func != NULL) { | 1441 | if (evsel->handler.func != NULL) { |
| 1619 | tracepoint_handler f = evsel->handler.func; | 1442 | tracepoint_handler f = evsel->handler.func; |
| 1620 | 1443 | err = f(tool, evsel, sample, machine); | |
| 1621 | if (evsel->handler.data == NULL) | ||
| 1622 | evsel->handler.data = pevent_find_event(pevent, | ||
| 1623 | evsel->attr.config); | ||
| 1624 | |||
| 1625 | f(tool, evsel->handler.data, sample, machine, thread); | ||
| 1626 | } | 1444 | } |
| 1627 | 1445 | ||
| 1628 | return 0; | 1446 | return err; |
| 1629 | } | 1447 | } |
| 1630 | 1448 | ||
| 1631 | static struct perf_sched perf_sched = { | 1449 | static int perf_sched__read_events(struct perf_sched *sched, bool destroy, |
| 1632 | .tool = { | 1450 | struct perf_session **psession) |
| 1633 | .sample = perf_sched__process_tracepoint_sample, | ||
| 1634 | .comm = perf_event__process_comm, | ||
| 1635 | .lost = perf_event__process_lost, | ||
| 1636 | .fork = perf_event__process_task, | ||
| 1637 | .ordered_samples = true, | ||
| 1638 | }, | ||
| 1639 | }; | ||
| 1640 | |||
| 1641 | static void read_events(bool destroy, struct perf_session **psession) | ||
| 1642 | { | 1451 | { |
| 1643 | int err = -EINVAL; | ||
| 1644 | const struct perf_evsel_str_handler handlers[] = { | 1452 | const struct perf_evsel_str_handler handlers[] = { |
| 1645 | { "sched:sched_switch", process_sched_switch_event, }, | 1453 | { "sched:sched_switch", process_sched_switch_event, }, |
| 1646 | { "sched:sched_stat_runtime", process_sched_runtime_event, }, | 1454 | { "sched:sched_stat_runtime", process_sched_runtime_event, }, |
| @@ -1652,24 +1460,25 @@ static void read_events(bool destroy, struct perf_session **psession) | |||
| 1652 | }; | 1460 | }; |
| 1653 | struct perf_session *session; | 1461 | struct perf_session *session; |
| 1654 | 1462 | ||
| 1655 | session = perf_session__new(input_name, O_RDONLY, 0, false, | 1463 | session = perf_session__new(sched->input_name, O_RDONLY, 0, false, &sched->tool); |
| 1656 | &perf_sched.tool); | 1464 | if (session == NULL) { |
| 1657 | if (session == NULL) | 1465 | pr_debug("No Memory for session\n"); |
| 1658 | die("No Memory"); | 1466 | return -1; |
| 1659 | 1467 | } | |
| 1660 | perf_sched.session = session; | ||
| 1661 | 1468 | ||
| 1662 | err = perf_session__set_tracepoints_handlers(session, handlers); | 1469 | if (perf_session__set_tracepoints_handlers(session, handlers)) |
| 1663 | assert(err == 0); | 1470 | goto out_delete; |
| 1664 | 1471 | ||
| 1665 | if (perf_session__has_traces(session, "record -R")) { | 1472 | if (perf_session__has_traces(session, "record -R")) { |
| 1666 | err = perf_session__process_events(session, &perf_sched.tool); | 1473 | int err = perf_session__process_events(session, &sched->tool); |
| 1667 | if (err) | 1474 | if (err) { |
| 1668 | die("Failed to process events, error %d", err); | 1475 | pr_err("Failed to process events, error %d", err); |
| 1476 | goto out_delete; | ||
| 1477 | } | ||
| 1669 | 1478 | ||
| 1670 | nr_events = session->hists.stats.nr_events[0]; | 1479 | sched->nr_events = session->hists.stats.nr_events[0]; |
| 1671 | nr_lost_events = session->hists.stats.total_lost; | 1480 | sched->nr_lost_events = session->hists.stats.total_lost; |
| 1672 | nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST]; | 1481 | sched->nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST]; |
| 1673 | } | 1482 | } |
| 1674 | 1483 | ||
| 1675 | if (destroy) | 1484 | if (destroy) |
| @@ -1677,208 +1486,166 @@ static void read_events(bool destroy, struct perf_session **psession) | |||
| 1677 | 1486 | ||
| 1678 | if (psession) | 1487 | if (psession) |
| 1679 | *psession = session; | 1488 | *psession = session; |
| 1489 | |||
| 1490 | return 0; | ||
| 1491 | |||
| 1492 | out_delete: | ||
| 1493 | perf_session__delete(session); | ||
| 1494 | return -1; | ||
| 1680 | } | 1495 | } |
| 1681 | 1496 | ||
| 1682 | static void print_bad_events(void) | 1497 | static void print_bad_events(struct perf_sched *sched) |
| 1683 | { | 1498 | { |
| 1684 | if (nr_unordered_timestamps && nr_timestamps) { | 1499 | if (sched->nr_unordered_timestamps && sched->nr_timestamps) { |
| 1685 | printf(" INFO: %.3f%% unordered timestamps (%ld out of %ld)\n", | 1500 | printf(" INFO: %.3f%% unordered timestamps (%ld out of %ld)\n", |
| 1686 | (double)nr_unordered_timestamps/(double)nr_timestamps*100.0, | 1501 | (double)sched->nr_unordered_timestamps/(double)sched->nr_timestamps*100.0, |
| 1687 | nr_unordered_timestamps, nr_timestamps); | 1502 | sched->nr_unordered_timestamps, sched->nr_timestamps); |
| 1688 | } | 1503 | } |
| 1689 | if (nr_lost_events && nr_events) { | 1504 | if (sched->nr_lost_events && sched->nr_events) { |
| 1690 | printf(" INFO: %.3f%% lost events (%ld out of %ld, in %ld chunks)\n", | 1505 | printf(" INFO: %.3f%% lost events (%ld out of %ld, in %ld chunks)\n", |
| 1691 | (double)nr_lost_events/(double)nr_events*100.0, | 1506 | (double)sched->nr_lost_events/(double)sched->nr_events * 100.0, |
| 1692 | nr_lost_events, nr_events, nr_lost_chunks); | 1507 | sched->nr_lost_events, sched->nr_events, sched->nr_lost_chunks); |
| 1693 | } | 1508 | } |
| 1694 | if (nr_state_machine_bugs && nr_timestamps) { | 1509 | if (sched->nr_state_machine_bugs && sched->nr_timestamps) { |
| 1695 | printf(" INFO: %.3f%% state machine bugs (%ld out of %ld)", | 1510 | printf(" INFO: %.3f%% state machine bugs (%ld out of %ld)", |
| 1696 | (double)nr_state_machine_bugs/(double)nr_timestamps*100.0, | 1511 | (double)sched->nr_state_machine_bugs/(double)sched->nr_timestamps*100.0, |
| 1697 | nr_state_machine_bugs, nr_timestamps); | 1512 | sched->nr_state_machine_bugs, sched->nr_timestamps); |
| 1698 | if (nr_lost_events) | 1513 | if (sched->nr_lost_events) |
| 1699 | printf(" (due to lost events?)"); | 1514 | printf(" (due to lost events?)"); |
| 1700 | printf("\n"); | 1515 | printf("\n"); |
| 1701 | } | 1516 | } |
| 1702 | if (nr_context_switch_bugs && nr_timestamps) { | 1517 | if (sched->nr_context_switch_bugs && sched->nr_timestamps) { |
| 1703 | printf(" INFO: %.3f%% context switch bugs (%ld out of %ld)", | 1518 | printf(" INFO: %.3f%% context switch bugs (%ld out of %ld)", |
| 1704 | (double)nr_context_switch_bugs/(double)nr_timestamps*100.0, | 1519 | (double)sched->nr_context_switch_bugs/(double)sched->nr_timestamps*100.0, |
| 1705 | nr_context_switch_bugs, nr_timestamps); | 1520 | sched->nr_context_switch_bugs, sched->nr_timestamps); |
| 1706 | if (nr_lost_events) | 1521 | if (sched->nr_lost_events) |
| 1707 | printf(" (due to lost events?)"); | 1522 | printf(" (due to lost events?)"); |
| 1708 | printf("\n"); | 1523 | printf("\n"); |
| 1709 | } | 1524 | } |
| 1710 | } | 1525 | } |
| 1711 | 1526 | ||
| 1712 | static void __cmd_lat(void) | 1527 | static int perf_sched__lat(struct perf_sched *sched) |
| 1713 | { | 1528 | { |
| 1714 | struct rb_node *next; | 1529 | struct rb_node *next; |
| 1715 | struct perf_session *session; | 1530 | struct perf_session *session; |
| 1716 | 1531 | ||
| 1717 | setup_pager(); | 1532 | setup_pager(); |
| 1718 | read_events(false, &session); | 1533 | if (perf_sched__read_events(sched, false, &session)) |
| 1719 | sort_lat(); | 1534 | return -1; |
| 1535 | perf_sched__sort_lat(sched); | ||
| 1720 | 1536 | ||
| 1721 | printf("\n ---------------------------------------------------------------------------------------------------------------\n"); | 1537 | printf("\n ---------------------------------------------------------------------------------------------------------------\n"); |
| 1722 | printf(" Task | Runtime ms | Switches | Average delay ms | Maximum delay ms | Maximum delay at |\n"); | 1538 | printf(" Task | Runtime ms | Switches | Average delay ms | Maximum delay ms | Maximum delay at |\n"); |
| 1723 | printf(" ---------------------------------------------------------------------------------------------------------------\n"); | 1539 | printf(" ---------------------------------------------------------------------------------------------------------------\n"); |
| 1724 | 1540 | ||
| 1725 | next = rb_first(&sorted_atom_root); | 1541 | next = rb_first(&sched->sorted_atom_root); |
| 1726 | 1542 | ||
| 1727 | while (next) { | 1543 | while (next) { |
| 1728 | struct work_atoms *work_list; | 1544 | struct work_atoms *work_list; |
| 1729 | 1545 | ||
| 1730 | work_list = rb_entry(next, struct work_atoms, node); | 1546 | work_list = rb_entry(next, struct work_atoms, node); |
| 1731 | output_lat_thread(work_list); | 1547 | output_lat_thread(sched, work_list); |
| 1732 | next = rb_next(next); | 1548 | next = rb_next(next); |
| 1733 | } | 1549 | } |
| 1734 | 1550 | ||
| 1735 | printf(" -----------------------------------------------------------------------------------------\n"); | 1551 | printf(" -----------------------------------------------------------------------------------------\n"); |
| 1736 | printf(" TOTAL: |%11.3f ms |%9" PRIu64 " |\n", | 1552 | printf(" TOTAL: |%11.3f ms |%9" PRIu64 " |\n", |
| 1737 | (double)all_runtime/1e6, all_count); | 1553 | (double)sched->all_runtime / 1e6, sched->all_count); |
| 1738 | 1554 | ||
| 1739 | printf(" ---------------------------------------------------\n"); | 1555 | printf(" ---------------------------------------------------\n"); |
| 1740 | 1556 | ||
| 1741 | print_bad_events(); | 1557 | print_bad_events(sched); |
| 1742 | printf("\n"); | 1558 | printf("\n"); |
| 1743 | 1559 | ||
| 1744 | perf_session__delete(session); | 1560 | perf_session__delete(session); |
| 1561 | return 0; | ||
| 1745 | } | 1562 | } |
| 1746 | 1563 | ||
| 1747 | static struct trace_sched_handler map_ops = { | 1564 | static int perf_sched__map(struct perf_sched *sched) |
| 1748 | .wakeup_event = NULL, | ||
| 1749 | .switch_event = map_switch_event, | ||
| 1750 | .runtime_event = NULL, | ||
| 1751 | .fork_event = NULL, | ||
| 1752 | }; | ||
| 1753 | |||
| 1754 | static void __cmd_map(void) | ||
| 1755 | { | 1565 | { |
| 1756 | max_cpu = sysconf(_SC_NPROCESSORS_CONF); | 1566 | sched->max_cpu = sysconf(_SC_NPROCESSORS_CONF); |
| 1757 | 1567 | ||
| 1758 | setup_pager(); | 1568 | setup_pager(); |
| 1759 | read_events(true, NULL); | 1569 | if (perf_sched__read_events(sched, true, NULL)) |
| 1760 | print_bad_events(); | 1570 | return -1; |
| 1571 | print_bad_events(sched); | ||
| 1572 | return 0; | ||
| 1761 | } | 1573 | } |
| 1762 | 1574 | ||
| 1763 | static void __cmd_replay(void) | 1575 | static int perf_sched__replay(struct perf_sched *sched) |
| 1764 | { | 1576 | { |
| 1765 | unsigned long i; | 1577 | unsigned long i; |
| 1766 | 1578 | ||
| 1767 | calibrate_run_measurement_overhead(); | 1579 | calibrate_run_measurement_overhead(sched); |
| 1768 | calibrate_sleep_measurement_overhead(); | 1580 | calibrate_sleep_measurement_overhead(sched); |
| 1769 | 1581 | ||
| 1770 | test_calibrations(); | 1582 | test_calibrations(sched); |
| 1771 | 1583 | ||
| 1772 | read_events(true, NULL); | 1584 | if (perf_sched__read_events(sched, true, NULL)) |
| 1585 | return -1; | ||
| 1773 | 1586 | ||
| 1774 | printf("nr_run_events: %ld\n", nr_run_events); | 1587 | printf("nr_run_events: %ld\n", sched->nr_run_events); |
| 1775 | printf("nr_sleep_events: %ld\n", nr_sleep_events); | 1588 | printf("nr_sleep_events: %ld\n", sched->nr_sleep_events); |
| 1776 | printf("nr_wakeup_events: %ld\n", nr_wakeup_events); | 1589 | printf("nr_wakeup_events: %ld\n", sched->nr_wakeup_events); |
| 1777 | 1590 | ||
| 1778 | if (targetless_wakeups) | 1591 | if (sched->targetless_wakeups) |
| 1779 | printf("target-less wakeups: %ld\n", targetless_wakeups); | 1592 | printf("target-less wakeups: %ld\n", sched->targetless_wakeups); |
| 1780 | if (multitarget_wakeups) | 1593 | if (sched->multitarget_wakeups) |
| 1781 | printf("multi-target wakeups: %ld\n", multitarget_wakeups); | 1594 | printf("multi-target wakeups: %ld\n", sched->multitarget_wakeups); |
| 1782 | if (nr_run_events_optimized) | 1595 | if (sched->nr_run_events_optimized) |
| 1783 | printf("run atoms optimized: %ld\n", | 1596 | printf("run atoms optimized: %ld\n", |
| 1784 | nr_run_events_optimized); | 1597 | sched->nr_run_events_optimized); |
| 1785 | 1598 | ||
| 1786 | print_task_traces(); | 1599 | print_task_traces(sched); |
| 1787 | add_cross_task_wakeups(); | 1600 | add_cross_task_wakeups(sched); |
| 1788 | 1601 | ||
| 1789 | create_tasks(); | 1602 | create_tasks(sched); |
| 1790 | printf("------------------------------------------------------------\n"); | 1603 | printf("------------------------------------------------------------\n"); |
| 1791 | for (i = 0; i < replay_repeat; i++) | 1604 | for (i = 0; i < sched->replay_repeat; i++) |
| 1792 | run_one_test(); | 1605 | run_one_test(sched); |
| 1793 | } | ||
| 1794 | |||
| 1795 | |||
| 1796 | static const char * const sched_usage[] = { | ||
| 1797 | "perf sched [<options>] {record|latency|map|replay|script}", | ||
| 1798 | NULL | ||
| 1799 | }; | ||
| 1800 | |||
| 1801 | static const struct option sched_options[] = { | ||
| 1802 | OPT_STRING('i', "input", &input_name, "file", | ||
| 1803 | "input file name"), | ||
| 1804 | OPT_INCR('v', "verbose", &verbose, | ||
| 1805 | "be more verbose (show symbol address, etc)"), | ||
| 1806 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | ||
| 1807 | "dump raw trace in ASCII"), | ||
| 1808 | OPT_END() | ||
| 1809 | }; | ||
| 1810 | |||
| 1811 | static const char * const latency_usage[] = { | ||
| 1812 | "perf sched latency [<options>]", | ||
| 1813 | NULL | ||
| 1814 | }; | ||
| 1815 | |||
| 1816 | static const struct option latency_options[] = { | ||
| 1817 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | ||
| 1818 | "sort by key(s): runtime, switch, avg, max"), | ||
| 1819 | OPT_INCR('v', "verbose", &verbose, | ||
| 1820 | "be more verbose (show symbol address, etc)"), | ||
| 1821 | OPT_INTEGER('C', "CPU", &profile_cpu, | ||
| 1822 | "CPU to profile on"), | ||
| 1823 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | ||
| 1824 | "dump raw trace in ASCII"), | ||
| 1825 | OPT_END() | ||
| 1826 | }; | ||
| 1827 | |||
| 1828 | static const char * const replay_usage[] = { | ||
| 1829 | "perf sched replay [<options>]", | ||
| 1830 | NULL | ||
| 1831 | }; | ||
| 1832 | 1606 | ||
| 1833 | static const struct option replay_options[] = { | 1607 | return 0; |
| 1834 | OPT_UINTEGER('r', "repeat", &replay_repeat, | 1608 | } |
| 1835 | "repeat the workload replay N times (-1: infinite)"), | ||
| 1836 | OPT_INCR('v', "verbose", &verbose, | ||
| 1837 | "be more verbose (show symbol address, etc)"), | ||
| 1838 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | ||
| 1839 | "dump raw trace in ASCII"), | ||
| 1840 | OPT_END() | ||
| 1841 | }; | ||
| 1842 | 1609 | ||
| 1843 | static void setup_sorting(void) | 1610 | static void setup_sorting(struct perf_sched *sched, const struct option *options, |
| 1611 | const char * const usage_msg[]) | ||
| 1844 | { | 1612 | { |
| 1845 | char *tmp, *tok, *str = strdup(sort_order); | 1613 | char *tmp, *tok, *str = strdup(sched->sort_order); |
| 1846 | 1614 | ||
| 1847 | for (tok = strtok_r(str, ", ", &tmp); | 1615 | for (tok = strtok_r(str, ", ", &tmp); |
| 1848 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | 1616 | tok; tok = strtok_r(NULL, ", ", &tmp)) { |
| 1849 | if (sort_dimension__add(tok, &sort_list) < 0) { | 1617 | if (sort_dimension__add(tok, &sched->sort_list) < 0) { |
| 1850 | error("Unknown --sort key: `%s'", tok); | 1618 | error("Unknown --sort key: `%s'", tok); |
| 1851 | usage_with_options(latency_usage, latency_options); | 1619 | usage_with_options(usage_msg, options); |
| 1852 | } | 1620 | } |
| 1853 | } | 1621 | } |
| 1854 | 1622 | ||
| 1855 | free(str); | 1623 | free(str); |
| 1856 | 1624 | ||
| 1857 | sort_dimension__add("pid", &cmp_pid); | 1625 | sort_dimension__add("pid", &sched->cmp_pid); |
| 1858 | } | 1626 | } |
| 1859 | 1627 | ||
| 1860 | static const char *record_args[] = { | ||
| 1861 | "record", | ||
| 1862 | "-a", | ||
| 1863 | "-R", | ||
| 1864 | "-f", | ||
| 1865 | "-m", "1024", | ||
| 1866 | "-c", "1", | ||
| 1867 | "-e", "sched:sched_switch", | ||
| 1868 | "-e", "sched:sched_stat_wait", | ||
| 1869 | "-e", "sched:sched_stat_sleep", | ||
| 1870 | "-e", "sched:sched_stat_iowait", | ||
| 1871 | "-e", "sched:sched_stat_runtime", | ||
| 1872 | "-e", "sched:sched_process_exit", | ||
| 1873 | "-e", "sched:sched_process_fork", | ||
| 1874 | "-e", "sched:sched_wakeup", | ||
| 1875 | "-e", "sched:sched_migrate_task", | ||
| 1876 | }; | ||
| 1877 | |||
| 1878 | static int __cmd_record(int argc, const char **argv) | 1628 | static int __cmd_record(int argc, const char **argv) |
| 1879 | { | 1629 | { |
| 1880 | unsigned int rec_argc, i, j; | 1630 | unsigned int rec_argc, i, j; |
| 1881 | const char **rec_argv; | 1631 | const char **rec_argv; |
| 1632 | const char * const record_args[] = { | ||
| 1633 | "record", | ||
| 1634 | "-a", | ||
| 1635 | "-R", | ||
| 1636 | "-f", | ||
| 1637 | "-m", "1024", | ||
| 1638 | "-c", "1", | ||
| 1639 | "-e", "sched:sched_switch", | ||
| 1640 | "-e", "sched:sched_stat_wait", | ||
| 1641 | "-e", "sched:sched_stat_sleep", | ||
| 1642 | "-e", "sched:sched_stat_iowait", | ||
| 1643 | "-e", "sched:sched_stat_runtime", | ||
| 1644 | "-e", "sched:sched_process_exit", | ||
| 1645 | "-e", "sched:sched_process_fork", | ||
| 1646 | "-e", "sched:sched_wakeup", | ||
| 1647 | "-e", "sched:sched_migrate_task", | ||
| 1648 | }; | ||
| 1882 | 1649 | ||
| 1883 | rec_argc = ARRAY_SIZE(record_args) + argc - 1; | 1650 | rec_argc = ARRAY_SIZE(record_args) + argc - 1; |
| 1884 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | 1651 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
| @@ -1897,8 +1664,85 @@ static int __cmd_record(int argc, const char **argv) | |||
| 1897 | return cmd_record(i, rec_argv, NULL); | 1664 | return cmd_record(i, rec_argv, NULL); |
| 1898 | } | 1665 | } |
| 1899 | 1666 | ||
| 1900 | int cmd_sched(int argc, const char **argv, const char *prefix __used) | 1667 | int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) |
| 1901 | { | 1668 | { |
| 1669 | const char default_sort_order[] = "avg, max, switch, runtime"; | ||
| 1670 | struct perf_sched sched = { | ||
| 1671 | .tool = { | ||
| 1672 | .sample = perf_sched__process_tracepoint_sample, | ||
| 1673 | .comm = perf_event__process_comm, | ||
| 1674 | .lost = perf_event__process_lost, | ||
| 1675 | .fork = perf_event__process_task, | ||
| 1676 | .ordered_samples = true, | ||
| 1677 | }, | ||
| 1678 | .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid), | ||
| 1679 | .sort_list = LIST_HEAD_INIT(sched.sort_list), | ||
| 1680 | .start_work_mutex = PTHREAD_MUTEX_INITIALIZER, | ||
| 1681 | .work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER, | ||
| 1682 | .curr_pid = { [0 ... MAX_CPUS - 1] = -1 }, | ||
| 1683 | .sort_order = default_sort_order, | ||
| 1684 | .replay_repeat = 10, | ||
| 1685 | .profile_cpu = -1, | ||
| 1686 | .next_shortname1 = 'A', | ||
| 1687 | .next_shortname2 = '0', | ||
| 1688 | }; | ||
| 1689 | const struct option latency_options[] = { | ||
| 1690 | OPT_STRING('s', "sort", &sched.sort_order, "key[,key2...]", | ||
| 1691 | "sort by key(s): runtime, switch, avg, max"), | ||
| 1692 | OPT_INCR('v', "verbose", &verbose, | ||
| 1693 | "be more verbose (show symbol address, etc)"), | ||
| 1694 | OPT_INTEGER('C', "CPU", &sched.profile_cpu, | ||
| 1695 | "CPU to profile on"), | ||
| 1696 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | ||
| 1697 | "dump raw trace in ASCII"), | ||
| 1698 | OPT_END() | ||
| 1699 | }; | ||
| 1700 | const struct option replay_options[] = { | ||
| 1701 | OPT_UINTEGER('r', "repeat", &sched.replay_repeat, | ||
| 1702 | "repeat the workload replay N times (-1: infinite)"), | ||
| 1703 | OPT_INCR('v', "verbose", &verbose, | ||
| 1704 | "be more verbose (show symbol address, etc)"), | ||
| 1705 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | ||
| 1706 | "dump raw trace in ASCII"), | ||
| 1707 | OPT_END() | ||
| 1708 | }; | ||
| 1709 | const struct option sched_options[] = { | ||
| 1710 | OPT_STRING('i', "input", &sched.input_name, "file", | ||
| 1711 | "input file name"), | ||
| 1712 | OPT_INCR('v', "verbose", &verbose, | ||
| 1713 | "be more verbose (show symbol address, etc)"), | ||
| 1714 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | ||
| 1715 | "dump raw trace in ASCII"), | ||
| 1716 | OPT_END() | ||
| 1717 | }; | ||
| 1718 | const char * const latency_usage[] = { | ||
| 1719 | "perf sched latency [<options>]", | ||
| 1720 | NULL | ||
| 1721 | }; | ||
| 1722 | const char * const replay_usage[] = { | ||
| 1723 | "perf sched replay [<options>]", | ||
| 1724 | NULL | ||
| 1725 | }; | ||
| 1726 | const char * const sched_usage[] = { | ||
| 1727 | "perf sched [<options>] {record|latency|map|replay|script}", | ||
| 1728 | NULL | ||
| 1729 | }; | ||
| 1730 | struct trace_sched_handler lat_ops = { | ||
| 1731 | .wakeup_event = latency_wakeup_event, | ||
| 1732 | .switch_event = latency_switch_event, | ||
| 1733 | .runtime_event = latency_runtime_event, | ||
| 1734 | .fork_event = latency_fork_event, | ||
| 1735 | .migrate_task_event = latency_migrate_task_event, | ||
| 1736 | }; | ||
| 1737 | struct trace_sched_handler map_ops = { | ||
| 1738 | .switch_event = map_switch_event, | ||
| 1739 | }; | ||
| 1740 | struct trace_sched_handler replay_ops = { | ||
| 1741 | .wakeup_event = replay_wakeup_event, | ||
| 1742 | .switch_event = replay_switch_event, | ||
| 1743 | .fork_event = replay_fork_event, | ||
| 1744 | }; | ||
| 1745 | |||
| 1902 | argc = parse_options(argc, argv, sched_options, sched_usage, | 1746 | argc = parse_options(argc, argv, sched_options, sched_usage, |
| 1903 | PARSE_OPT_STOP_AT_NON_OPTION); | 1747 | PARSE_OPT_STOP_AT_NON_OPTION); |
| 1904 | if (!argc) | 1748 | if (!argc) |
| @@ -1914,26 +1758,26 @@ int cmd_sched(int argc, const char **argv, const char *prefix __used) | |||
| 1914 | if (!strncmp(argv[0], "rec", 3)) { | 1758 | if (!strncmp(argv[0], "rec", 3)) { |
| 1915 | return __cmd_record(argc, argv); | 1759 | return __cmd_record(argc, argv); |
| 1916 | } else if (!strncmp(argv[0], "lat", 3)) { | 1760 | } else if (!strncmp(argv[0], "lat", 3)) { |
| 1917 | trace_handler = &lat_ops; | 1761 | sched.tp_handler = &lat_ops; |
| 1918 | if (argc > 1) { | 1762 | if (argc > 1) { |
| 1919 | argc = parse_options(argc, argv, latency_options, latency_usage, 0); | 1763 | argc = parse_options(argc, argv, latency_options, latency_usage, 0); |
| 1920 | if (argc) | 1764 | if (argc) |
| 1921 | usage_with_options(latency_usage, latency_options); | 1765 | usage_with_options(latency_usage, latency_options); |
| 1922 | } | 1766 | } |
| 1923 | setup_sorting(); | 1767 | setup_sorting(&sched, latency_options, latency_usage); |
| 1924 | __cmd_lat(); | 1768 | return perf_sched__lat(&sched); |
| 1925 | } else if (!strcmp(argv[0], "map")) { | 1769 | } else if (!strcmp(argv[0], "map")) { |
| 1926 | trace_handler = &map_ops; | 1770 | sched.tp_handler = &map_ops; |
| 1927 | setup_sorting(); | 1771 | setup_sorting(&sched, latency_options, latency_usage); |
| 1928 | __cmd_map(); | 1772 | return perf_sched__map(&sched); |
| 1929 | } else if (!strncmp(argv[0], "rep", 3)) { | 1773 | } else if (!strncmp(argv[0], "rep", 3)) { |
| 1930 | trace_handler = &replay_ops; | 1774 | sched.tp_handler = &replay_ops; |
| 1931 | if (argc) { | 1775 | if (argc) { |
| 1932 | argc = parse_options(argc, argv, replay_options, replay_usage, 0); | 1776 | argc = parse_options(argc, argv, replay_options, replay_usage, 0); |
| 1933 | if (argc) | 1777 | if (argc) |
| 1934 | usage_with_options(replay_usage, replay_options); | 1778 | usage_with_options(replay_usage, replay_options); |
| 1935 | } | 1779 | } |
| 1936 | __cmd_replay(); | 1780 | return perf_sched__replay(&sched); |
| 1937 | } else { | 1781 | } else { |
| 1938 | usage_with_options(sched_usage, sched_options); | 1782 | usage_with_options(sched_usage, sched_options); |
| 1939 | } | 1783 | } |
