blob: c0ac0c9557e843a6062fd57f7057bc8d77e3e7da [file] [log] [blame]
Ingo Molnar0a02ad92009-09-11 12:12:54 +02001#include "builtin.h"
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +02002#include "perf.h"
Ingo Molnar0a02ad92009-09-11 12:12:54 +02003
4#include "util/util.h"
Arnaldo Carvalho de Meloee29be62011-11-28 17:57:40 -02005#include "util/evlist.h"
Ingo Molnar0a02ad92009-09-11 12:12:54 +02006#include "util/cache.h"
Arnaldo Carvalho de Meloe3f42602011-11-16 17:02:54 -02007#include "util/evsel.h"
Ingo Molnar0a02ad92009-09-11 12:12:54 +02008#include "util/symbol.h"
9#include "util/thread.h"
10#include "util/header.h"
Arnaldo Carvalho de Melo94c744b2009-12-11 21:24:02 -020011#include "util/session.h"
Arnaldo Carvalho de Melo45694aa2011-11-28 08:30:20 -020012#include "util/tool.h"
Yann Droneaud57480d22014-06-30 22:28:47 +020013#include "util/cloexec.h"
Jiri Olsaa151a372016-04-12 15:29:29 +020014#include "util/thread_map.h"
Jiri Olsa8cd91192016-04-12 15:29:27 +020015#include "util/color.h"
David Ahern49394a22016-11-16 15:06:29 +090016#include "util/stat.h"
Ingo Molnar0a02ad92009-09-11 12:12:54 +020017
Josh Poimboeuf4b6ab942015-12-15 09:39:39 -060018#include <subcmd/parse-options.h>
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +020019#include "util/trace-event.h"
Ingo Molnar0a02ad92009-09-11 12:12:54 +020020
Ingo Molnar0a02ad92009-09-11 12:12:54 +020021#include "util/debug.h"
22
David Ahern49394a22016-11-16 15:06:29 +090023#include <linux/log2.h>
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +020024#include <sys/prctl.h>
Markus Trippelsdorf7b78f132012-04-04 10:45:27 +020025#include <sys/resource.h>
Ingo Molnar0a02ad92009-09-11 12:12:54 +020026
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +020027#include <semaphore.h>
28#include <pthread.h>
29#include <math.h>
Yunlong Songcb06ac22015-03-31 21:46:30 +080030#include <api/fs/fs.h>
Arnaldo Carvalho de Melo4fc76e42016-08-08 12:23:49 -030031#include <linux/time64.h>
Frederic Weisbecker419ab0d2009-09-12 03:59:01 +020032
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +020033#define PR_SET_NAME 15 /* Set process name */
34#define MAX_CPUS 4096
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +020035#define COMM_LEN 20
36#define SYM_LEN 129
Yunlong Songa35e27d2015-03-31 21:46:29 +080037#define MAX_PID 1024000
Ingo Molnarec156762009-09-11 12:12:54 +020038
mingo39aeb522009-09-14 20:04:48 +020039struct sched_atom;
Ingo Molnarec156762009-09-11 12:12:54 +020040
41struct task_desc {
42 unsigned long nr;
43 unsigned long pid;
44 char comm[COMM_LEN];
45
46 unsigned long nr_events;
47 unsigned long curr_event;
mingo39aeb522009-09-14 20:04:48 +020048 struct sched_atom **atoms;
Ingo Molnarec156762009-09-11 12:12:54 +020049
50 pthread_t thread;
51 sem_t sleep_sem;
52
53 sem_t ready_for_work;
54 sem_t work_done_sem;
55
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +020056 u64 cpu_usage;
Ingo Molnarec156762009-09-11 12:12:54 +020057};
58
59enum sched_event_type {
60 SCHED_EVENT_RUN,
61 SCHED_EVENT_SLEEP,
62 SCHED_EVENT_WAKEUP,
Mike Galbraith55ffb7a2009-10-10 14:46:04 +020063 SCHED_EVENT_MIGRATION,
Ingo Molnarec156762009-09-11 12:12:54 +020064};
65
mingo39aeb522009-09-14 20:04:48 +020066struct sched_atom {
Ingo Molnarec156762009-09-11 12:12:54 +020067 enum sched_event_type type;
Arnaldo Carvalho de Meloeed05fe2010-04-05 12:53:45 -030068 int specific_wait;
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +020069 u64 timestamp;
70 u64 duration;
Ingo Molnarec156762009-09-11 12:12:54 +020071 unsigned long nr;
Ingo Molnarec156762009-09-11 12:12:54 +020072 sem_t *wait_sem;
73 struct task_desc *wakee;
74};
75
Dongshenge936e8e2014-05-05 16:05:54 +090076#define TASK_STATE_TO_CHAR_STR "RSDTtZXxKWP"
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +020077
78enum thread_state {
79 THREAD_SLEEPING = 0,
80 THREAD_WAIT_CPU,
81 THREAD_SCHED_IN,
82 THREAD_IGNORE
83};
84
85struct work_atom {
86 struct list_head list;
87 enum thread_state state;
Frederic Weisbeckeraa1ab9d2009-09-14 03:01:12 +020088 u64 sched_out_time;
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +020089 u64 wake_up_time;
90 u64 sched_in_time;
91 u64 runtime;
92};
93
mingo39aeb522009-09-14 20:04:48 +020094struct work_atoms {
95 struct list_head work_list;
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +020096 struct thread *thread;
97 struct rb_node node;
98 u64 max_lat;
Frederic Weisbecker3786310a2009-12-09 21:40:08 +010099 u64 max_lat_at;
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200100 u64 total_lat;
101 u64 nb_atoms;
102 u64 total_runtime;
Josef Bacik2f80dd42015-05-22 09:18:40 -0400103 int num_merged;
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200104};
105
mingo39aeb522009-09-14 20:04:48 +0200106typedef int (*sort_fn_t)(struct work_atoms *, struct work_atoms *);
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200107
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300108struct perf_sched;
109
110struct trace_sched_handler {
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -0300111 int (*switch_event)(struct perf_sched *sched, struct perf_evsel *evsel,
112 struct perf_sample *sample, struct machine *machine);
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300113
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -0300114 int (*runtime_event)(struct perf_sched *sched, struct perf_evsel *evsel,
115 struct perf_sample *sample, struct machine *machine);
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300116
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -0300117 int (*wakeup_event)(struct perf_sched *sched, struct perf_evsel *evsel,
118 struct perf_sample *sample, struct machine *machine);
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300119
David Aherncb627502013-08-07 22:50:47 -0400120 /* PERF_RECORD_FORK event, not sched_process_fork tracepoint */
121 int (*fork_event)(struct perf_sched *sched, union perf_event *event,
122 struct machine *machine);
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300123
124 int (*migrate_task_event)(struct perf_sched *sched,
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -0300125 struct perf_evsel *evsel,
126 struct perf_sample *sample,
127 struct machine *machine);
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300128};
129
Jiri Olsaa151a372016-04-12 15:29:29 +0200130#define COLOR_PIDS PERF_COLOR_BLUE
Jiri Olsacf294f22016-04-12 15:29:30 +0200131#define COLOR_CPUS PERF_COLOR_BG_RED
Jiri Olsaa151a372016-04-12 15:29:29 +0200132
Jiri Olsa99623c62016-04-12 15:29:26 +0200133struct perf_sched_map {
134 DECLARE_BITMAP(comp_cpus_mask, MAX_CPUS);
135 int *comp_cpus;
136 bool comp;
Jiri Olsaa151a372016-04-12 15:29:29 +0200137 struct thread_map *color_pids;
138 const char *color_pids_str;
Jiri Olsacf294f22016-04-12 15:29:30 +0200139 struct cpu_map *color_cpus;
140 const char *color_cpus_str;
Jiri Olsa73643bb2016-04-12 15:29:31 +0200141 struct cpu_map *cpus;
142 const char *cpus_str;
Jiri Olsa99623c62016-04-12 15:29:26 +0200143};
144
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300145struct perf_sched {
146 struct perf_tool tool;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300147 const char *sort_order;
148 unsigned long nr_tasks;
Yunlong Songcb06ac22015-03-31 21:46:30 +0800149 struct task_desc **pid_to_task;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300150 struct task_desc **tasks;
151 const struct trace_sched_handler *tp_handler;
152 pthread_mutex_t start_work_mutex;
153 pthread_mutex_t work_done_wait_mutex;
154 int profile_cpu;
155/*
156 * Track the current task - that way we can know whether there's any
157 * weird events, such as a task being switched away that is not current.
158 */
159 int max_cpu;
160 u32 curr_pid[MAX_CPUS];
161 struct thread *curr_thread[MAX_CPUS];
162 char next_shortname1;
163 char next_shortname2;
164 unsigned int replay_repeat;
165 unsigned long nr_run_events;
166 unsigned long nr_sleep_events;
167 unsigned long nr_wakeup_events;
168 unsigned long nr_sleep_corrections;
169 unsigned long nr_run_events_optimized;
170 unsigned long targetless_wakeups;
171 unsigned long multitarget_wakeups;
172 unsigned long nr_runs;
173 unsigned long nr_timestamps;
174 unsigned long nr_unordered_timestamps;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300175 unsigned long nr_context_switch_bugs;
176 unsigned long nr_events;
177 unsigned long nr_lost_chunks;
178 unsigned long nr_lost_events;
179 u64 run_measurement_overhead;
180 u64 sleep_measurement_overhead;
181 u64 start_time;
182 u64 cpu_usage;
183 u64 runavg_cpu_usage;
184 u64 parent_cpu_usage;
185 u64 runavg_parent_cpu_usage;
186 u64 sum_runtime;
187 u64 sum_fluct;
188 u64 run_avg;
189 u64 all_runtime;
190 u64 all_count;
191 u64 cpu_last_switched[MAX_CPUS];
Josef Bacik2f80dd42015-05-22 09:18:40 -0400192 struct rb_root atom_root, sorted_atom_root, merged_atom_root;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300193 struct list_head sort_list, cmp_pid;
Yunlong Song939cda52015-03-31 21:46:34 +0800194 bool force;
Josef Bacik2f80dd42015-05-22 09:18:40 -0400195 bool skip_merge;
Jiri Olsa99623c62016-04-12 15:29:26 +0200196 struct perf_sched_map map;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300197};
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200198
David Ahern49394a22016-11-16 15:06:29 +0900199/* per thread run time data */
200struct thread_runtime {
201 u64 last_time; /* time of previous sched in/out event */
202 u64 dt_run; /* run time */
203 u64 dt_wait; /* time between CPU access (off cpu) */
204 u64 dt_delay; /* time between wakeup and sched-in */
205 u64 ready_to_run; /* time of wakeup */
206
207 struct stats run_stats;
208 u64 total_run_time;
209};
210
211/* per event run time data */
212struct evsel_runtime {
213 u64 *last_time; /* time this event was last seen per cpu */
214 u32 ncpu; /* highest cpu slot allocated */
215};
216
217/* track idle times per cpu */
218static struct thread **idle_threads;
219static int idle_max_cpu;
220static char idle_comm[] = "<idle>";
221
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200222static u64 get_nsecs(void)
223{
224 struct timespec ts;
225
226 clock_gettime(CLOCK_MONOTONIC, &ts);
227
Arnaldo Carvalho de Melo4fc76e42016-08-08 12:23:49 -0300228 return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200229}
230
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300231static void burn_nsecs(struct perf_sched *sched, u64 nsecs)
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200232{
233 u64 T0 = get_nsecs(), T1;
234
235 do {
236 T1 = get_nsecs();
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300237 } while (T1 + sched->run_measurement_overhead < T0 + nsecs);
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200238}
239
240static void sleep_nsecs(u64 nsecs)
241{
242 struct timespec ts;
243
244 ts.tv_nsec = nsecs % 999999999;
245 ts.tv_sec = nsecs / 999999999;
246
247 nanosleep(&ts, NULL);
248}
249
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300250static void calibrate_run_measurement_overhead(struct perf_sched *sched)
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200251{
Arnaldo Carvalho de Melo4fc76e42016-08-08 12:23:49 -0300252 u64 T0, T1, delta, min_delta = NSEC_PER_SEC;
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200253 int i;
254
255 for (i = 0; i < 10; i++) {
256 T0 = get_nsecs();
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300257 burn_nsecs(sched, 0);
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200258 T1 = get_nsecs();
259 delta = T1-T0;
260 min_delta = min(min_delta, delta);
261 }
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300262 sched->run_measurement_overhead = min_delta;
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200263
Arnaldo Carvalho de Melo9486aa32011-01-22 20:37:02 -0200264 printf("run measurement overhead: %" PRIu64 " nsecs\n", min_delta);
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200265}
266
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300267static void calibrate_sleep_measurement_overhead(struct perf_sched *sched)
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200268{
Arnaldo Carvalho de Melo4fc76e42016-08-08 12:23:49 -0300269 u64 T0, T1, delta, min_delta = NSEC_PER_SEC;
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200270 int i;
271
272 for (i = 0; i < 10; i++) {
273 T0 = get_nsecs();
274 sleep_nsecs(10000);
275 T1 = get_nsecs();
276 delta = T1-T0;
277 min_delta = min(min_delta, delta);
278 }
279 min_delta -= 10000;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300280 sched->sleep_measurement_overhead = min_delta;
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200281
Arnaldo Carvalho de Melo9486aa32011-01-22 20:37:02 -0200282 printf("sleep measurement overhead: %" PRIu64 " nsecs\n", min_delta);
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200283}
284
mingo39aeb522009-09-14 20:04:48 +0200285static struct sched_atom *
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200286get_new_event(struct task_desc *task, u64 timestamp)
Ingo Molnarec156762009-09-11 12:12:54 +0200287{
Arnaldo Carvalho de Melo36479482009-11-24 12:05:16 -0200288 struct sched_atom *event = zalloc(sizeof(*event));
Ingo Molnarec156762009-09-11 12:12:54 +0200289 unsigned long idx = task->nr_events;
290 size_t size;
291
292 event->timestamp = timestamp;
293 event->nr = idx;
294
295 task->nr_events++;
mingo39aeb522009-09-14 20:04:48 +0200296 size = sizeof(struct sched_atom *) * task->nr_events;
297 task->atoms = realloc(task->atoms, size);
298 BUG_ON(!task->atoms);
Ingo Molnarec156762009-09-11 12:12:54 +0200299
mingo39aeb522009-09-14 20:04:48 +0200300 task->atoms[idx] = event;
Ingo Molnarec156762009-09-11 12:12:54 +0200301
302 return event;
303}
304
mingo39aeb522009-09-14 20:04:48 +0200305static struct sched_atom *last_event(struct task_desc *task)
Ingo Molnarec156762009-09-11 12:12:54 +0200306{
307 if (!task->nr_events)
308 return NULL;
309
mingo39aeb522009-09-14 20:04:48 +0200310 return task->atoms[task->nr_events - 1];
Ingo Molnarec156762009-09-11 12:12:54 +0200311}
312
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300313static void add_sched_event_run(struct perf_sched *sched, struct task_desc *task,
314 u64 timestamp, u64 duration)
Ingo Molnarec156762009-09-11 12:12:54 +0200315{
mingo39aeb522009-09-14 20:04:48 +0200316 struct sched_atom *event, *curr_event = last_event(task);
Ingo Molnarec156762009-09-11 12:12:54 +0200317
318 /*
Ingo Molnarfbf94822009-09-11 12:12:54 +0200319 * optimize an existing RUN event by merging this one
320 * to it:
321 */
Ingo Molnarec156762009-09-11 12:12:54 +0200322 if (curr_event && curr_event->type == SCHED_EVENT_RUN) {
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300323 sched->nr_run_events_optimized++;
Ingo Molnarec156762009-09-11 12:12:54 +0200324 curr_event->duration += duration;
325 return;
326 }
327
328 event = get_new_event(task, timestamp);
329
330 event->type = SCHED_EVENT_RUN;
331 event->duration = duration;
332
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300333 sched->nr_run_events++;
Ingo Molnarec156762009-09-11 12:12:54 +0200334}
335
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300336static void add_sched_event_wakeup(struct perf_sched *sched, struct task_desc *task,
337 u64 timestamp, struct task_desc *wakee)
Ingo Molnarec156762009-09-11 12:12:54 +0200338{
mingo39aeb522009-09-14 20:04:48 +0200339 struct sched_atom *event, *wakee_event;
Ingo Molnarec156762009-09-11 12:12:54 +0200340
341 event = get_new_event(task, timestamp);
342 event->type = SCHED_EVENT_WAKEUP;
343 event->wakee = wakee;
344
345 wakee_event = last_event(wakee);
346 if (!wakee_event || wakee_event->type != SCHED_EVENT_SLEEP) {
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300347 sched->targetless_wakeups++;
Ingo Molnarec156762009-09-11 12:12:54 +0200348 return;
349 }
350 if (wakee_event->wait_sem) {
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300351 sched->multitarget_wakeups++;
Ingo Molnarec156762009-09-11 12:12:54 +0200352 return;
353 }
354
Arnaldo Carvalho de Melo36479482009-11-24 12:05:16 -0200355 wakee_event->wait_sem = zalloc(sizeof(*wakee_event->wait_sem));
Ingo Molnarec156762009-09-11 12:12:54 +0200356 sem_init(wakee_event->wait_sem, 0, 0);
357 wakee_event->specific_wait = 1;
358 event->wait_sem = wakee_event->wait_sem;
359
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300360 sched->nr_wakeup_events++;
Ingo Molnarec156762009-09-11 12:12:54 +0200361}
362
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300363static void add_sched_event_sleep(struct perf_sched *sched, struct task_desc *task,
364 u64 timestamp, u64 task_state __maybe_unused)
Ingo Molnarec156762009-09-11 12:12:54 +0200365{
mingo39aeb522009-09-14 20:04:48 +0200366 struct sched_atom *event = get_new_event(task, timestamp);
Ingo Molnarec156762009-09-11 12:12:54 +0200367
368 event->type = SCHED_EVENT_SLEEP;
369
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300370 sched->nr_sleep_events++;
Ingo Molnarec156762009-09-11 12:12:54 +0200371}
372
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300373static struct task_desc *register_pid(struct perf_sched *sched,
374 unsigned long pid, const char *comm)
Ingo Molnarec156762009-09-11 12:12:54 +0200375{
376 struct task_desc *task;
Yunlong Songcb06ac22015-03-31 21:46:30 +0800377 static int pid_max;
Ingo Molnarec156762009-09-11 12:12:54 +0200378
Yunlong Songcb06ac22015-03-31 21:46:30 +0800379 if (sched->pid_to_task == NULL) {
380 if (sysctl__read_int("kernel/pid_max", &pid_max) < 0)
381 pid_max = MAX_PID;
382 BUG_ON((sched->pid_to_task = calloc(pid_max, sizeof(struct task_desc *))) == NULL);
383 }
Yunlong Song3a423a52015-03-31 21:46:31 +0800384 if (pid >= (unsigned long)pid_max) {
385 BUG_ON((sched->pid_to_task = realloc(sched->pid_to_task, (pid + 1) *
386 sizeof(struct task_desc *))) == NULL);
387 while (pid >= (unsigned long)pid_max)
388 sched->pid_to_task[pid_max++] = NULL;
389 }
Ingo Molnarec156762009-09-11 12:12:54 +0200390
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300391 task = sched->pid_to_task[pid];
Ingo Molnarec156762009-09-11 12:12:54 +0200392
393 if (task)
394 return task;
395
Arnaldo Carvalho de Melo36479482009-11-24 12:05:16 -0200396 task = zalloc(sizeof(*task));
Ingo Molnarec156762009-09-11 12:12:54 +0200397 task->pid = pid;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300398 task->nr = sched->nr_tasks;
Ingo Molnarec156762009-09-11 12:12:54 +0200399 strcpy(task->comm, comm);
400 /*
401 * every task starts in sleeping state - this gets ignored
402 * if there's no wakeup pointing to this sleep state:
403 */
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300404 add_sched_event_sleep(sched, task, 0, 0);
Ingo Molnarec156762009-09-11 12:12:54 +0200405
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300406 sched->pid_to_task[pid] = task;
407 sched->nr_tasks++;
Yunlong Song0755bc42015-03-31 21:46:28 +0800408 sched->tasks = realloc(sched->tasks, sched->nr_tasks * sizeof(struct task_desc *));
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300409 BUG_ON(!sched->tasks);
410 sched->tasks[task->nr] = task;
Ingo Molnarec156762009-09-11 12:12:54 +0200411
Ingo Molnarad236fd2009-09-11 12:12:54 +0200412 if (verbose)
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300413 printf("registered task #%ld, PID %ld (%s)\n", sched->nr_tasks, pid, comm);
Ingo Molnarec156762009-09-11 12:12:54 +0200414
415 return task;
416}
417
418
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300419static void print_task_traces(struct perf_sched *sched)
Ingo Molnarec156762009-09-11 12:12:54 +0200420{
421 struct task_desc *task;
422 unsigned long i;
423
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300424 for (i = 0; i < sched->nr_tasks; i++) {
425 task = sched->tasks[i];
Ingo Molnarad236fd2009-09-11 12:12:54 +0200426 printf("task %6ld (%20s:%10ld), nr_events: %ld\n",
Ingo Molnarec156762009-09-11 12:12:54 +0200427 task->nr, task->comm, task->pid, task->nr_events);
428 }
429}
430
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300431static void add_cross_task_wakeups(struct perf_sched *sched)
Ingo Molnarec156762009-09-11 12:12:54 +0200432{
433 struct task_desc *task1, *task2;
434 unsigned long i, j;
435
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300436 for (i = 0; i < sched->nr_tasks; i++) {
437 task1 = sched->tasks[i];
Ingo Molnarec156762009-09-11 12:12:54 +0200438 j = i + 1;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300439 if (j == sched->nr_tasks)
Ingo Molnarec156762009-09-11 12:12:54 +0200440 j = 0;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300441 task2 = sched->tasks[j];
442 add_sched_event_wakeup(sched, task1, 0, task2);
Ingo Molnarec156762009-09-11 12:12:54 +0200443 }
444}
445
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300446static void perf_sched__process_event(struct perf_sched *sched,
447 struct sched_atom *atom)
Ingo Molnarec156762009-09-11 12:12:54 +0200448{
449 int ret = 0;
Ingo Molnarec156762009-09-11 12:12:54 +0200450
mingo39aeb522009-09-14 20:04:48 +0200451 switch (atom->type) {
Ingo Molnarec156762009-09-11 12:12:54 +0200452 case SCHED_EVENT_RUN:
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300453 burn_nsecs(sched, atom->duration);
Ingo Molnarec156762009-09-11 12:12:54 +0200454 break;
455 case SCHED_EVENT_SLEEP:
mingo39aeb522009-09-14 20:04:48 +0200456 if (atom->wait_sem)
457 ret = sem_wait(atom->wait_sem);
Ingo Molnarec156762009-09-11 12:12:54 +0200458 BUG_ON(ret);
459 break;
460 case SCHED_EVENT_WAKEUP:
mingo39aeb522009-09-14 20:04:48 +0200461 if (atom->wait_sem)
462 ret = sem_post(atom->wait_sem);
Ingo Molnarec156762009-09-11 12:12:54 +0200463 BUG_ON(ret);
464 break;
Mike Galbraith55ffb7a2009-10-10 14:46:04 +0200465 case SCHED_EVENT_MIGRATION:
466 break;
Ingo Molnarec156762009-09-11 12:12:54 +0200467 default:
468 BUG_ON(1);
469 }
470}
471
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200472static u64 get_cpu_usage_nsec_parent(void)
Ingo Molnarec156762009-09-11 12:12:54 +0200473{
474 struct rusage ru;
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200475 u64 sum;
Ingo Molnarec156762009-09-11 12:12:54 +0200476 int err;
477
478 err = getrusage(RUSAGE_SELF, &ru);
479 BUG_ON(err);
480
Arnaldo Carvalho de Melo4fc76e42016-08-08 12:23:49 -0300481 sum = ru.ru_utime.tv_sec * NSEC_PER_SEC + ru.ru_utime.tv_usec * NSEC_PER_USEC;
482 sum += ru.ru_stime.tv_sec * NSEC_PER_SEC + ru.ru_stime.tv_usec * NSEC_PER_USEC;
Ingo Molnarec156762009-09-11 12:12:54 +0200483
484 return sum;
485}
486
Yunlong Song939cda52015-03-31 21:46:34 +0800487static int self_open_counters(struct perf_sched *sched, unsigned long cur_task)
Ingo Molnarec156762009-09-11 12:12:54 +0200488{
Xiao Guangrongc0c9e722009-12-09 17:51:30 +0800489 struct perf_event_attr attr;
Yunlong Song939cda52015-03-31 21:46:34 +0800490 char sbuf[STRERR_BUFSIZE], info[STRERR_BUFSIZE];
Xiao Guangrongc0c9e722009-12-09 17:51:30 +0800491 int fd;
Yunlong Song939cda52015-03-31 21:46:34 +0800492 struct rlimit limit;
493 bool need_privilege = false;
Xiao Guangrongc0c9e722009-12-09 17:51:30 +0800494
495 memset(&attr, 0, sizeof(attr));
496
497 attr.type = PERF_TYPE_SOFTWARE;
498 attr.config = PERF_COUNT_SW_TASK_CLOCK;
499
Yunlong Song939cda52015-03-31 21:46:34 +0800500force_again:
Yann Droneaud57480d22014-06-30 22:28:47 +0200501 fd = sys_perf_event_open(&attr, 0, -1, -1,
502 perf_event_open_cloexec_flag());
Xiao Guangrongc0c9e722009-12-09 17:51:30 +0800503
Yunlong Song1aff59b2015-03-31 21:46:33 +0800504 if (fd < 0) {
Yunlong Song939cda52015-03-31 21:46:34 +0800505 if (errno == EMFILE) {
506 if (sched->force) {
507 BUG_ON(getrlimit(RLIMIT_NOFILE, &limit) == -1);
508 limit.rlim_cur += sched->nr_tasks - cur_task;
509 if (limit.rlim_cur > limit.rlim_max) {
510 limit.rlim_max = limit.rlim_cur;
511 need_privilege = true;
512 }
513 if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
514 if (need_privilege && errno == EPERM)
515 strcpy(info, "Need privilege\n");
516 } else
517 goto force_again;
518 } else
519 strcpy(info, "Have a try with -f option\n");
520 }
Namhyung Kim60b7d142012-09-12 11:11:06 +0900521 pr_err("Error: sys_perf_event_open() syscall returned "
Yunlong Song939cda52015-03-31 21:46:34 +0800522 "with %d (%s)\n%s", fd,
Arnaldo Carvalho de Meloc8b5f2c2016-07-06 11:56:20 -0300523 str_error_r(errno, sbuf, sizeof(sbuf)), info);
Yunlong Song1aff59b2015-03-31 21:46:33 +0800524 exit(EXIT_FAILURE);
525 }
Xiao Guangrongc0c9e722009-12-09 17:51:30 +0800526 return fd;
527}
528
529static u64 get_cpu_usage_nsec_self(int fd)
530{
531 u64 runtime;
Ingo Molnarec156762009-09-11 12:12:54 +0200532 int ret;
533
Xiao Guangrongc0c9e722009-12-09 17:51:30 +0800534 ret = read(fd, &runtime, sizeof(runtime));
535 BUG_ON(ret != sizeof(runtime));
Ingo Molnarec156762009-09-11 12:12:54 +0200536
Xiao Guangrongc0c9e722009-12-09 17:51:30 +0800537 return runtime;
Ingo Molnarec156762009-09-11 12:12:54 +0200538}
539
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300540struct sched_thread_parms {
541 struct task_desc *task;
542 struct perf_sched *sched;
Yunlong Song08097ab2015-03-31 21:46:32 +0800543 int fd;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300544};
545
Ingo Molnarec156762009-09-11 12:12:54 +0200546static void *thread_func(void *ctx)
547{
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300548 struct sched_thread_parms *parms = ctx;
549 struct task_desc *this_task = parms->task;
550 struct perf_sched *sched = parms->sched;
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200551 u64 cpu_usage_0, cpu_usage_1;
Ingo Molnarec156762009-09-11 12:12:54 +0200552 unsigned long i, ret;
553 char comm2[22];
Yunlong Song08097ab2015-03-31 21:46:32 +0800554 int fd = parms->fd;
Ingo Molnarec156762009-09-11 12:12:54 +0200555
Arnaldo Carvalho de Melo74cf2492013-12-27 16:55:14 -0300556 zfree(&parms);
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300557
Ingo Molnarec156762009-09-11 12:12:54 +0200558 sprintf(comm2, ":%s", this_task->comm);
559 prctl(PR_SET_NAME, comm2);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -0300560 if (fd < 0)
561 return NULL;
Ingo Molnarec156762009-09-11 12:12:54 +0200562again:
563 ret = sem_post(&this_task->ready_for_work);
564 BUG_ON(ret);
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300565 ret = pthread_mutex_lock(&sched->start_work_mutex);
Ingo Molnarec156762009-09-11 12:12:54 +0200566 BUG_ON(ret);
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300567 ret = pthread_mutex_unlock(&sched->start_work_mutex);
Ingo Molnarec156762009-09-11 12:12:54 +0200568 BUG_ON(ret);
Ingo Molnarec156762009-09-11 12:12:54 +0200569
Xiao Guangrongc0c9e722009-12-09 17:51:30 +0800570 cpu_usage_0 = get_cpu_usage_nsec_self(fd);
Ingo Molnarec156762009-09-11 12:12:54 +0200571
572 for (i = 0; i < this_task->nr_events; i++) {
573 this_task->curr_event = i;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300574 perf_sched__process_event(sched, this_task->atoms[i]);
Ingo Molnarec156762009-09-11 12:12:54 +0200575 }
576
Xiao Guangrongc0c9e722009-12-09 17:51:30 +0800577 cpu_usage_1 = get_cpu_usage_nsec_self(fd);
Ingo Molnarec156762009-09-11 12:12:54 +0200578 this_task->cpu_usage = cpu_usage_1 - cpu_usage_0;
Ingo Molnarec156762009-09-11 12:12:54 +0200579 ret = sem_post(&this_task->work_done_sem);
580 BUG_ON(ret);
Ingo Molnarec156762009-09-11 12:12:54 +0200581
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300582 ret = pthread_mutex_lock(&sched->work_done_wait_mutex);
Ingo Molnarec156762009-09-11 12:12:54 +0200583 BUG_ON(ret);
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300584 ret = pthread_mutex_unlock(&sched->work_done_wait_mutex);
Ingo Molnarec156762009-09-11 12:12:54 +0200585 BUG_ON(ret);
Ingo Molnarec156762009-09-11 12:12:54 +0200586
587 goto again;
588}
589
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300590static void create_tasks(struct perf_sched *sched)
Ingo Molnarec156762009-09-11 12:12:54 +0200591{
592 struct task_desc *task;
593 pthread_attr_t attr;
594 unsigned long i;
595 int err;
596
597 err = pthread_attr_init(&attr);
598 BUG_ON(err);
Jiri Pirko12f7e032011-01-10 14:14:23 -0200599 err = pthread_attr_setstacksize(&attr,
600 (size_t) max(16 * 1024, PTHREAD_STACK_MIN));
Ingo Molnarec156762009-09-11 12:12:54 +0200601 BUG_ON(err);
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300602 err = pthread_mutex_lock(&sched->start_work_mutex);
Ingo Molnarec156762009-09-11 12:12:54 +0200603 BUG_ON(err);
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300604 err = pthread_mutex_lock(&sched->work_done_wait_mutex);
Ingo Molnarec156762009-09-11 12:12:54 +0200605 BUG_ON(err);
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300606 for (i = 0; i < sched->nr_tasks; i++) {
607 struct sched_thread_parms *parms = malloc(sizeof(*parms));
608 BUG_ON(parms == NULL);
609 parms->task = task = sched->tasks[i];
610 parms->sched = sched;
Yunlong Song939cda52015-03-31 21:46:34 +0800611 parms->fd = self_open_counters(sched, i);
Ingo Molnarec156762009-09-11 12:12:54 +0200612 sem_init(&task->sleep_sem, 0, 0);
613 sem_init(&task->ready_for_work, 0, 0);
614 sem_init(&task->work_done_sem, 0, 0);
615 task->curr_event = 0;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300616 err = pthread_create(&task->thread, &attr, thread_func, parms);
Ingo Molnarec156762009-09-11 12:12:54 +0200617 BUG_ON(err);
618 }
619}
620
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300621static void wait_for_tasks(struct perf_sched *sched)
Ingo Molnarec156762009-09-11 12:12:54 +0200622{
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200623 u64 cpu_usage_0, cpu_usage_1;
Ingo Molnarec156762009-09-11 12:12:54 +0200624 struct task_desc *task;
625 unsigned long i, ret;
626
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300627 sched->start_time = get_nsecs();
628 sched->cpu_usage = 0;
629 pthread_mutex_unlock(&sched->work_done_wait_mutex);
Ingo Molnarec156762009-09-11 12:12:54 +0200630
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300631 for (i = 0; i < sched->nr_tasks; i++) {
632 task = sched->tasks[i];
Ingo Molnarec156762009-09-11 12:12:54 +0200633 ret = sem_wait(&task->ready_for_work);
634 BUG_ON(ret);
635 sem_init(&task->ready_for_work, 0, 0);
636 }
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300637 ret = pthread_mutex_lock(&sched->work_done_wait_mutex);
Ingo Molnarec156762009-09-11 12:12:54 +0200638 BUG_ON(ret);
639
640 cpu_usage_0 = get_cpu_usage_nsec_parent();
641
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300642 pthread_mutex_unlock(&sched->start_work_mutex);
Ingo Molnarec156762009-09-11 12:12:54 +0200643
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300644 for (i = 0; i < sched->nr_tasks; i++) {
645 task = sched->tasks[i];
Ingo Molnarec156762009-09-11 12:12:54 +0200646 ret = sem_wait(&task->work_done_sem);
647 BUG_ON(ret);
648 sem_init(&task->work_done_sem, 0, 0);
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300649 sched->cpu_usage += task->cpu_usage;
Ingo Molnarec156762009-09-11 12:12:54 +0200650 task->cpu_usage = 0;
651 }
652
653 cpu_usage_1 = get_cpu_usage_nsec_parent();
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300654 if (!sched->runavg_cpu_usage)
655 sched->runavg_cpu_usage = sched->cpu_usage;
Yunlong Songff5f3bb2015-03-31 21:46:36 +0800656 sched->runavg_cpu_usage = (sched->runavg_cpu_usage * (sched->replay_repeat - 1) + sched->cpu_usage) / sched->replay_repeat;
Ingo Molnarec156762009-09-11 12:12:54 +0200657
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300658 sched->parent_cpu_usage = cpu_usage_1 - cpu_usage_0;
659 if (!sched->runavg_parent_cpu_usage)
660 sched->runavg_parent_cpu_usage = sched->parent_cpu_usage;
Yunlong Songff5f3bb2015-03-31 21:46:36 +0800661 sched->runavg_parent_cpu_usage = (sched->runavg_parent_cpu_usage * (sched->replay_repeat - 1) +
662 sched->parent_cpu_usage)/sched->replay_repeat;
Ingo Molnarec156762009-09-11 12:12:54 +0200663
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300664 ret = pthread_mutex_lock(&sched->start_work_mutex);
Ingo Molnarec156762009-09-11 12:12:54 +0200665 BUG_ON(ret);
666
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300667 for (i = 0; i < sched->nr_tasks; i++) {
668 task = sched->tasks[i];
Ingo Molnarec156762009-09-11 12:12:54 +0200669 sem_init(&task->sleep_sem, 0, 0);
670 task->curr_event = 0;
671 }
672}
673
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300674static void run_one_test(struct perf_sched *sched)
Ingo Molnarec156762009-09-11 12:12:54 +0200675{
Kyle McMartinfb7d0b32011-01-24 11:13:04 -0500676 u64 T0, T1, delta, avg_delta, fluct;
Ingo Molnarec156762009-09-11 12:12:54 +0200677
678 T0 = get_nsecs();
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300679 wait_for_tasks(sched);
Ingo Molnarec156762009-09-11 12:12:54 +0200680 T1 = get_nsecs();
681
682 delta = T1 - T0;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300683 sched->sum_runtime += delta;
684 sched->nr_runs++;
Ingo Molnarec156762009-09-11 12:12:54 +0200685
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300686 avg_delta = sched->sum_runtime / sched->nr_runs;
Ingo Molnarec156762009-09-11 12:12:54 +0200687 if (delta < avg_delta)
688 fluct = avg_delta - delta;
689 else
690 fluct = delta - avg_delta;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300691 sched->sum_fluct += fluct;
692 if (!sched->run_avg)
693 sched->run_avg = delta;
Yunlong Songff5f3bb2015-03-31 21:46:36 +0800694 sched->run_avg = (sched->run_avg * (sched->replay_repeat - 1) + delta) / sched->replay_repeat;
Ingo Molnarec156762009-09-11 12:12:54 +0200695
Arnaldo Carvalho de Melo4fc76e42016-08-08 12:23:49 -0300696 printf("#%-3ld: %0.3f, ", sched->nr_runs, (double)delta / NSEC_PER_MSEC);
Ingo Molnarec156762009-09-11 12:12:54 +0200697
Arnaldo Carvalho de Melo4fc76e42016-08-08 12:23:49 -0300698 printf("ravg: %0.2f, ", (double)sched->run_avg / NSEC_PER_MSEC);
Ingo Molnarec156762009-09-11 12:12:54 +0200699
Ingo Molnarad236fd2009-09-11 12:12:54 +0200700 printf("cpu: %0.2f / %0.2f",
Arnaldo Carvalho de Melo4fc76e42016-08-08 12:23:49 -0300701 (double)sched->cpu_usage / NSEC_PER_MSEC, (double)sched->runavg_cpu_usage / NSEC_PER_MSEC);
Ingo Molnarec156762009-09-11 12:12:54 +0200702
703#if 0
704 /*
Ingo Molnarfbf94822009-09-11 12:12:54 +0200705 * rusage statistics done by the parent, these are less
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300706 * accurate than the sched->sum_exec_runtime based statistics:
Ingo Molnarfbf94822009-09-11 12:12:54 +0200707 */
Ingo Molnarad236fd2009-09-11 12:12:54 +0200708 printf(" [%0.2f / %0.2f]",
Arnaldo Carvalho de Melo4fc76e42016-08-08 12:23:49 -0300709 (double)sched->parent_cpu_usage / NSEC_PER_MSEC,
710 (double)sched->runavg_parent_cpu_usage / NSEC_PER_MSEC);
Ingo Molnarec156762009-09-11 12:12:54 +0200711#endif
712
Ingo Molnarad236fd2009-09-11 12:12:54 +0200713 printf("\n");
Ingo Molnarec156762009-09-11 12:12:54 +0200714
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300715 if (sched->nr_sleep_corrections)
716 printf(" (%ld sleep corrections)\n", sched->nr_sleep_corrections);
717 sched->nr_sleep_corrections = 0;
Ingo Molnarec156762009-09-11 12:12:54 +0200718}
719
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300720static void test_calibrations(struct perf_sched *sched)
Ingo Molnarec156762009-09-11 12:12:54 +0200721{
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200722 u64 T0, T1;
Ingo Molnarec156762009-09-11 12:12:54 +0200723
724 T0 = get_nsecs();
Arnaldo Carvalho de Melo4fc76e42016-08-08 12:23:49 -0300725 burn_nsecs(sched, NSEC_PER_MSEC);
Ingo Molnarec156762009-09-11 12:12:54 +0200726 T1 = get_nsecs();
727
Arnaldo Carvalho de Melo9486aa32011-01-22 20:37:02 -0200728 printf("the run test took %" PRIu64 " nsecs\n", T1 - T0);
Ingo Molnarec156762009-09-11 12:12:54 +0200729
730 T0 = get_nsecs();
Arnaldo Carvalho de Melo4fc76e42016-08-08 12:23:49 -0300731 sleep_nsecs(NSEC_PER_MSEC);
Ingo Molnarec156762009-09-11 12:12:54 +0200732 T1 = get_nsecs();
733
Arnaldo Carvalho de Melo9486aa32011-01-22 20:37:02 -0200734 printf("the sleep test took %" PRIu64 " nsecs\n", T1 - T0);
Ingo Molnarec156762009-09-11 12:12:54 +0200735}
736
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -0300737static int
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300738replay_wakeup_event(struct perf_sched *sched,
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -0300739 struct perf_evsel *evsel, struct perf_sample *sample,
740 struct machine *machine __maybe_unused)
Ingo Molnarec156762009-09-11 12:12:54 +0200741{
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -0300742 const char *comm = perf_evsel__strval(evsel, sample, "comm");
743 const u32 pid = perf_evsel__intval(evsel, sample, "pid");
Frederic Weisbecker419ab0d2009-09-12 03:59:01 +0200744 struct task_desc *waker, *wakee;
745
746 if (verbose) {
Arnaldo Carvalho de Melo2b7fcbc2012-09-11 19:29:17 -0300747 printf("sched_wakeup event %p\n", evsel);
Frederic Weisbecker419ab0d2009-09-12 03:59:01 +0200748
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -0300749 printf(" ... pid %d woke up %s/%d\n", sample->tid, comm, pid);
Frederic Weisbecker419ab0d2009-09-12 03:59:01 +0200750 }
751
Arnaldo Carvalho de Melo2b7fcbc2012-09-11 19:29:17 -0300752 waker = register_pid(sched, sample->tid, "<unknown>");
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -0300753 wakee = register_pid(sched, pid, comm);
Frederic Weisbecker419ab0d2009-09-12 03:59:01 +0200754
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300755 add_sched_event_wakeup(sched, waker, sample->time, wakee);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -0300756 return 0;
Frederic Weisbecker419ab0d2009-09-12 03:59:01 +0200757}
758
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -0300759static int replay_switch_event(struct perf_sched *sched,
760 struct perf_evsel *evsel,
761 struct perf_sample *sample,
762 struct machine *machine __maybe_unused)
Frederic Weisbecker419ab0d2009-09-12 03:59:01 +0200763{
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -0300764 const char *prev_comm = perf_evsel__strval(evsel, sample, "prev_comm"),
765 *next_comm = perf_evsel__strval(evsel, sample, "next_comm");
766 const u32 prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"),
767 next_pid = perf_evsel__intval(evsel, sample, "next_pid");
768 const u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state");
Irina Tirdea1d037ca2012-09-11 01:15:03 +0300769 struct task_desc *prev, __maybe_unused *next;
Arnaldo Carvalho de Melo7f7f8d02012-08-07 11:33:42 -0300770 u64 timestamp0, timestamp = sample->time;
771 int cpu = sample->cpu;
Ingo Molnarfbf94822009-09-11 12:12:54 +0200772 s64 delta;
773
Ingo Molnarad236fd2009-09-11 12:12:54 +0200774 if (verbose)
Arnaldo Carvalho de Melo2b7fcbc2012-09-11 19:29:17 -0300775 printf("sched_switch event %p\n", evsel);
Ingo Molnarad236fd2009-09-11 12:12:54 +0200776
Ingo Molnarfbf94822009-09-11 12:12:54 +0200777 if (cpu >= MAX_CPUS || cpu < 0)
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -0300778 return 0;
Ingo Molnarfbf94822009-09-11 12:12:54 +0200779
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300780 timestamp0 = sched->cpu_last_switched[cpu];
Ingo Molnarfbf94822009-09-11 12:12:54 +0200781 if (timestamp0)
782 delta = timestamp - timestamp0;
783 else
784 delta = 0;
785
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -0300786 if (delta < 0) {
Namhyung Kim60b7d142012-09-12 11:11:06 +0900787 pr_err("hm, delta: %" PRIu64 " < 0 ?\n", delta);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -0300788 return -1;
789 }
Ingo Molnarfbf94822009-09-11 12:12:54 +0200790
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -0300791 pr_debug(" ... switch from %s/%d to %s/%d [ran %" PRIu64 " nsecs]\n",
792 prev_comm, prev_pid, next_comm, next_pid, delta);
Ingo Molnarfbf94822009-09-11 12:12:54 +0200793
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -0300794 prev = register_pid(sched, prev_pid, prev_comm);
795 next = register_pid(sched, next_pid, next_comm);
Ingo Molnarfbf94822009-09-11 12:12:54 +0200796
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300797 sched->cpu_last_switched[cpu] = timestamp;
Ingo Molnarfbf94822009-09-11 12:12:54 +0200798
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300799 add_sched_event_run(sched, prev, timestamp, delta);
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -0300800 add_sched_event_sleep(sched, prev, timestamp, prev_state);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -0300801
802 return 0;
Ingo Molnarfbf94822009-09-11 12:12:54 +0200803}
804
David Aherncb627502013-08-07 22:50:47 -0400805static int replay_fork_event(struct perf_sched *sched,
806 union perf_event *event,
807 struct machine *machine)
Frederic Weisbecker419ab0d2009-09-12 03:59:01 +0200808{
David Aherncb627502013-08-07 22:50:47 -0400809 struct thread *child, *parent;
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -0300810
Adrian Hunter314add62013-08-27 11:23:03 +0300811 child = machine__findnew_thread(machine, event->fork.pid,
812 event->fork.tid);
813 parent = machine__findnew_thread(machine, event->fork.ppid,
814 event->fork.ptid);
David Aherncb627502013-08-07 22:50:47 -0400815
816 if (child == NULL || parent == NULL) {
817 pr_debug("thread does not exist on fork event: child %p, parent %p\n",
818 child, parent);
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -0300819 goto out_put;
Frederic Weisbecker419ab0d2009-09-12 03:59:01 +0200820 }
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -0300821
David Aherncb627502013-08-07 22:50:47 -0400822 if (verbose) {
823 printf("fork event\n");
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +0200824 printf("... parent: %s/%d\n", thread__comm_str(parent), parent->tid);
825 printf("... child: %s/%d\n", thread__comm_str(child), child->tid);
David Aherncb627502013-08-07 22:50:47 -0400826 }
827
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +0200828 register_pid(sched, parent->tid, thread__comm_str(parent));
829 register_pid(sched, child->tid, thread__comm_str(child));
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -0300830out_put:
831 thread__put(child);
832 thread__put(parent);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -0300833 return 0;
Frederic Weisbecker419ab0d2009-09-12 03:59:01 +0200834}
835
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200836struct sort_dimension {
837 const char *name;
Ingo Molnarb5fae122009-09-11 12:12:54 +0200838 sort_fn_t cmp;
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200839 struct list_head list;
840};
841
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +0200842static int
mingo39aeb522009-09-14 20:04:48 +0200843thread_lat_cmp(struct list_head *list, struct work_atoms *l, struct work_atoms *r)
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +0200844{
845 struct sort_dimension *sort;
846 int ret = 0;
847
Ingo Molnarb5fae122009-09-11 12:12:54 +0200848 BUG_ON(list_empty(list));
849
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +0200850 list_for_each_entry(sort, list, list) {
851 ret = sort->cmp(l, r);
852 if (ret)
853 return ret;
854 }
855
856 return ret;
857}
858
mingo39aeb522009-09-14 20:04:48 +0200859static struct work_atoms *
Ingo Molnarb5fae122009-09-11 12:12:54 +0200860thread_atoms_search(struct rb_root *root, struct thread *thread,
861 struct list_head *sort_list)
862{
863 struct rb_node *node = root->rb_node;
mingo39aeb522009-09-14 20:04:48 +0200864 struct work_atoms key = { .thread = thread };
Ingo Molnarb5fae122009-09-11 12:12:54 +0200865
866 while (node) {
mingo39aeb522009-09-14 20:04:48 +0200867 struct work_atoms *atoms;
Ingo Molnarb5fae122009-09-11 12:12:54 +0200868 int cmp;
869
mingo39aeb522009-09-14 20:04:48 +0200870 atoms = container_of(node, struct work_atoms, node);
Ingo Molnarb5fae122009-09-11 12:12:54 +0200871
872 cmp = thread_lat_cmp(sort_list, &key, atoms);
873 if (cmp > 0)
874 node = node->rb_left;
875 else if (cmp < 0)
876 node = node->rb_right;
877 else {
878 BUG_ON(thread != atoms->thread);
879 return atoms;
880 }
881 }
882 return NULL;
883}
884
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200885static void
mingo39aeb522009-09-14 20:04:48 +0200886__thread_latency_insert(struct rb_root *root, struct work_atoms *data,
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +0200887 struct list_head *sort_list)
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200888{
889 struct rb_node **new = &(root->rb_node), *parent = NULL;
890
891 while (*new) {
mingo39aeb522009-09-14 20:04:48 +0200892 struct work_atoms *this;
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +0200893 int cmp;
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200894
mingo39aeb522009-09-14 20:04:48 +0200895 this = container_of(*new, struct work_atoms, node);
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200896 parent = *new;
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +0200897
898 cmp = thread_lat_cmp(sort_list, data, this);
899
900 if (cmp > 0)
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200901 new = &((*new)->rb_left);
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200902 else
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +0200903 new = &((*new)->rb_right);
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200904 }
905
906 rb_link_node(&data->node, parent, new);
907 rb_insert_color(&data->node, root);
908}
909
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300910static int thread_atoms_insert(struct perf_sched *sched, struct thread *thread)
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200911{
Arnaldo Carvalho de Melo36479482009-11-24 12:05:16 -0200912 struct work_atoms *atoms = zalloc(sizeof(*atoms));
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -0300913 if (!atoms) {
914 pr_err("No memory at %s\n", __func__);
915 return -1;
916 }
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200917
Arnaldo Carvalho de Melof3b623b2015-03-02 22:21:35 -0300918 atoms->thread = thread__get(thread);
mingo39aeb522009-09-14 20:04:48 +0200919 INIT_LIST_HEAD(&atoms->work_list);
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -0300920 __thread_latency_insert(&sched->atom_root, atoms, &sched->cmp_pid);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -0300921 return 0;
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200922}
923
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -0300924static char sched_out_state(u64 prev_state)
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200925{
926 const char *str = TASK_STATE_TO_CHAR_STR;
927
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -0300928 return str[prev_state];
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200929}
930
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -0300931static int
mingo39aeb522009-09-14 20:04:48 +0200932add_sched_out_event(struct work_atoms *atoms,
933 char run_state,
934 u64 timestamp)
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200935{
Arnaldo Carvalho de Melo36479482009-11-24 12:05:16 -0200936 struct work_atom *atom = zalloc(sizeof(*atom));
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -0300937 if (!atom) {
938 pr_err("Non memory at %s", __func__);
939 return -1;
940 }
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200941
Frederic Weisbeckeraa1ab9d2009-09-14 03:01:12 +0200942 atom->sched_out_time = timestamp;
943
mingo39aeb522009-09-14 20:04:48 +0200944 if (run_state == 'R') {
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200945 atom->state = THREAD_WAIT_CPU;
Frederic Weisbeckeraa1ab9d2009-09-14 03:01:12 +0200946 atom->wake_up_time = atom->sched_out_time;
Frederic Weisbeckerc6ced612009-09-13 00:46:19 +0200947 }
948
mingo39aeb522009-09-14 20:04:48 +0200949 list_add_tail(&atom->list, &atoms->work_list);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -0300950 return 0;
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200951}
952
953static void
Irina Tirdea1d037ca2012-09-11 01:15:03 +0300954add_runtime_event(struct work_atoms *atoms, u64 delta,
955 u64 timestamp __maybe_unused)
mingo39aeb522009-09-14 20:04:48 +0200956{
957 struct work_atom *atom;
958
959 BUG_ON(list_empty(&atoms->work_list));
960
961 atom = list_entry(atoms->work_list.prev, struct work_atom, list);
962
963 atom->runtime += delta;
964 atoms->total_runtime += delta;
965}
966
967static void
968add_sched_in_event(struct work_atoms *atoms, u64 timestamp)
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200969{
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200970 struct work_atom *atom;
Frederic Weisbecker66685672009-09-13 01:56:25 +0200971 u64 delta;
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200972
mingo39aeb522009-09-14 20:04:48 +0200973 if (list_empty(&atoms->work_list))
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200974 return;
975
mingo39aeb522009-09-14 20:04:48 +0200976 atom = list_entry(atoms->work_list.prev, struct work_atom, list);
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200977
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200978 if (atom->state != THREAD_WAIT_CPU)
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200979 return;
980
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200981 if (timestamp < atom->wake_up_time) {
982 atom->state = THREAD_IGNORE;
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200983 return;
984 }
985
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200986 atom->state = THREAD_SCHED_IN;
987 atom->sched_in_time = timestamp;
Frederic Weisbecker66685672009-09-13 01:56:25 +0200988
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +0200989 delta = atom->sched_in_time - atom->wake_up_time;
Frederic Weisbecker66685672009-09-13 01:56:25 +0200990 atoms->total_lat += delta;
Frederic Weisbecker3786310a2009-12-09 21:40:08 +0100991 if (delta > atoms->max_lat) {
Frederic Weisbecker66685672009-09-13 01:56:25 +0200992 atoms->max_lat = delta;
Frederic Weisbecker3786310a2009-12-09 21:40:08 +0100993 atoms->max_lat_at = timestamp;
994 }
Frederic Weisbecker66685672009-09-13 01:56:25 +0200995 atoms->nb_atoms++;
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +0200996}
997
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -0300998static int latency_switch_event(struct perf_sched *sched,
999 struct perf_evsel *evsel,
1000 struct perf_sample *sample,
1001 struct machine *machine)
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001002{
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -03001003 const u32 prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"),
1004 next_pid = perf_evsel__intval(evsel, sample, "next_pid");
1005 const u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state");
mingo39aeb522009-09-14 20:04:48 +02001006 struct work_atoms *out_events, *in_events;
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001007 struct thread *sched_out, *sched_in;
Arnaldo Carvalho de Melo7f7f8d02012-08-07 11:33:42 -03001008 u64 timestamp0, timestamp = sample->time;
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001009 int cpu = sample->cpu, err = -1;
Ingo Molnarea92ed52009-09-12 10:08:34 +02001010 s64 delta;
1011
mingo39aeb522009-09-14 20:04:48 +02001012 BUG_ON(cpu >= MAX_CPUS || cpu < 0);
Ingo Molnarea92ed52009-09-12 10:08:34 +02001013
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001014 timestamp0 = sched->cpu_last_switched[cpu];
1015 sched->cpu_last_switched[cpu] = timestamp;
Ingo Molnarea92ed52009-09-12 10:08:34 +02001016 if (timestamp0)
1017 delta = timestamp - timestamp0;
1018 else
1019 delta = 0;
1020
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001021 if (delta < 0) {
1022 pr_err("hm, delta: %" PRIu64 " < 0 ?\n", delta);
1023 return -1;
1024 }
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001025
Adrian Hunter1fcb8762014-07-14 13:02:25 +03001026 sched_out = machine__findnew_thread(machine, -1, prev_pid);
1027 sched_in = machine__findnew_thread(machine, -1, next_pid);
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001028 if (sched_out == NULL || sched_in == NULL)
1029 goto out_put;
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001030
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001031 out_events = thread_atoms_search(&sched->atom_root, sched_out, &sched->cmp_pid);
mingo39aeb522009-09-14 20:04:48 +02001032 if (!out_events) {
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001033 if (thread_atoms_insert(sched, sched_out))
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001034 goto out_put;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001035 out_events = thread_atoms_search(&sched->atom_root, sched_out, &sched->cmp_pid);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001036 if (!out_events) {
1037 pr_err("out-event: Internal tree error");
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001038 goto out_put;
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001039 }
mingo39aeb522009-09-14 20:04:48 +02001040 }
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -03001041 if (add_sched_out_event(out_events, sched_out_state(prev_state), timestamp))
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001042 return -1;
mingo39aeb522009-09-14 20:04:48 +02001043
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001044 in_events = thread_atoms_search(&sched->atom_root, sched_in, &sched->cmp_pid);
mingo39aeb522009-09-14 20:04:48 +02001045 if (!in_events) {
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001046 if (thread_atoms_insert(sched, sched_in))
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001047 goto out_put;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001048 in_events = thread_atoms_search(&sched->atom_root, sched_in, &sched->cmp_pid);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001049 if (!in_events) {
1050 pr_err("in-event: Internal tree error");
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001051 goto out_put;
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001052 }
mingo39aeb522009-09-14 20:04:48 +02001053 /*
1054 * Take came in we have not heard about yet,
1055 * add in an initial atom in runnable state:
1056 */
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001057 if (add_sched_out_event(in_events, 'R', timestamp))
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001058 goto out_put;
mingo39aeb522009-09-14 20:04:48 +02001059 }
1060 add_sched_in_event(in_events, timestamp);
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001061 err = 0;
1062out_put:
1063 thread__put(sched_out);
1064 thread__put(sched_in);
1065 return err;
mingo39aeb522009-09-14 20:04:48 +02001066}
1067
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -03001068static int latency_runtime_event(struct perf_sched *sched,
1069 struct perf_evsel *evsel,
1070 struct perf_sample *sample,
1071 struct machine *machine)
mingo39aeb522009-09-14 20:04:48 +02001072{
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -03001073 const u32 pid = perf_evsel__intval(evsel, sample, "pid");
1074 const u64 runtime = perf_evsel__intval(evsel, sample, "runtime");
Adrian Hunter1fcb8762014-07-14 13:02:25 +03001075 struct thread *thread = machine__findnew_thread(machine, -1, pid);
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001076 struct work_atoms *atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid);
Arnaldo Carvalho de Melo7f7f8d02012-08-07 11:33:42 -03001077 u64 timestamp = sample->time;
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001078 int cpu = sample->cpu, err = -1;
1079
1080 if (thread == NULL)
1081 return -1;
mingo39aeb522009-09-14 20:04:48 +02001082
1083 BUG_ON(cpu >= MAX_CPUS || cpu < 0);
mingo39aeb522009-09-14 20:04:48 +02001084 if (!atoms) {
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001085 if (thread_atoms_insert(sched, thread))
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001086 goto out_put;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001087 atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001088 if (!atoms) {
Namhyung Kim60b7d142012-09-12 11:11:06 +09001089 pr_err("in-event: Internal tree error");
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001090 goto out_put;
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001091 }
1092 if (add_sched_out_event(atoms, 'R', timestamp))
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001093 goto out_put;
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001094 }
1095
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -03001096 add_runtime_event(atoms, runtime, timestamp);
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001097 err = 0;
1098out_put:
1099 thread__put(thread);
1100 return err;
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001101}
1102
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -03001103static int latency_wakeup_event(struct perf_sched *sched,
1104 struct perf_evsel *evsel,
1105 struct perf_sample *sample,
1106 struct machine *machine)
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001107{
Peter Zijlstra0680ee72014-05-12 20:19:46 +02001108 const u32 pid = perf_evsel__intval(evsel, sample, "pid");
mingo39aeb522009-09-14 20:04:48 +02001109 struct work_atoms *atoms;
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +02001110 struct work_atom *atom;
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001111 struct thread *wakee;
Arnaldo Carvalho de Melo7f7f8d02012-08-07 11:33:42 -03001112 u64 timestamp = sample->time;
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001113 int err = -1;
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001114
Adrian Hunter1fcb8762014-07-14 13:02:25 +03001115 wakee = machine__findnew_thread(machine, -1, pid);
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001116 if (wakee == NULL)
1117 return -1;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001118 atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid);
Frederic Weisbecker17562202009-09-12 23:11:32 +02001119 if (!atoms) {
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001120 if (thread_atoms_insert(sched, wakee))
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001121 goto out_put;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001122 atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001123 if (!atoms) {
Namhyung Kim60b7d142012-09-12 11:11:06 +09001124 pr_err("wakeup-event: Internal tree error");
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001125 goto out_put;
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001126 }
1127 if (add_sched_out_event(atoms, 'S', timestamp))
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001128 goto out_put;
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001129 }
1130
mingo39aeb522009-09-14 20:04:48 +02001131 BUG_ON(list_empty(&atoms->work_list));
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001132
mingo39aeb522009-09-14 20:04:48 +02001133 atom = list_entry(atoms->work_list.prev, struct work_atom, list);
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001134
Mike Galbraith55ffb7a2009-10-10 14:46:04 +02001135 /*
Dongsheng Yang67d62592014-05-13 10:38:21 +09001136 * As we do not guarantee the wakeup event happens when
1137 * task is out of run queue, also may happen when task is
1138 * on run queue and wakeup only change ->state to TASK_RUNNING,
1139 * then we should not set the ->wake_up_time when wake up a
1140 * task which is on run queue.
1141 *
Mike Galbraith55ffb7a2009-10-10 14:46:04 +02001142 * You WILL be missing events if you've recorded only
1143 * one CPU, or are only looking at only one, so don't
Dongsheng Yang67d62592014-05-13 10:38:21 +09001144 * skip in this case.
Mike Galbraith55ffb7a2009-10-10 14:46:04 +02001145 */
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001146 if (sched->profile_cpu == -1 && atom->state != THREAD_SLEEPING)
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001147 goto out_ok;
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001148
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001149 sched->nr_timestamps++;
Ingo Molnarea57c4f2009-09-13 18:15:54 +02001150 if (atom->sched_out_time > timestamp) {
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001151 sched->nr_unordered_timestamps++;
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001152 goto out_ok;
Ingo Molnarea57c4f2009-09-13 18:15:54 +02001153 }
Frederic Weisbeckeraa1ab9d2009-09-14 03:01:12 +02001154
Ingo Molnarb1ffe8f2009-09-11 12:12:54 +02001155 atom->state = THREAD_WAIT_CPU;
1156 atom->wake_up_time = timestamp;
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001157out_ok:
1158 err = 0;
1159out_put:
1160 thread__put(wakee);
1161 return err;
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001162}
1163
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -03001164static int latency_migrate_task_event(struct perf_sched *sched,
1165 struct perf_evsel *evsel,
1166 struct perf_sample *sample,
1167 struct machine *machine)
Mike Galbraith55ffb7a2009-10-10 14:46:04 +02001168{
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -03001169 const u32 pid = perf_evsel__intval(evsel, sample, "pid");
Arnaldo Carvalho de Melo7f7f8d02012-08-07 11:33:42 -03001170 u64 timestamp = sample->time;
Mike Galbraith55ffb7a2009-10-10 14:46:04 +02001171 struct work_atoms *atoms;
1172 struct work_atom *atom;
1173 struct thread *migrant;
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001174 int err = -1;
Mike Galbraith55ffb7a2009-10-10 14:46:04 +02001175
1176 /*
1177 * Only need to worry about migration when profiling one CPU.
1178 */
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001179 if (sched->profile_cpu == -1)
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001180 return 0;
Mike Galbraith55ffb7a2009-10-10 14:46:04 +02001181
Adrian Hunter1fcb8762014-07-14 13:02:25 +03001182 migrant = machine__findnew_thread(machine, -1, pid);
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001183 if (migrant == NULL)
1184 return -1;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001185 atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid);
Mike Galbraith55ffb7a2009-10-10 14:46:04 +02001186 if (!atoms) {
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001187 if (thread_atoms_insert(sched, migrant))
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001188 goto out_put;
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001189 register_pid(sched, migrant->tid, thread__comm_str(migrant));
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001190 atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001191 if (!atoms) {
Namhyung Kim60b7d142012-09-12 11:11:06 +09001192 pr_err("migration-event: Internal tree error");
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001193 goto out_put;
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001194 }
1195 if (add_sched_out_event(atoms, 'R', timestamp))
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001196 goto out_put;
Mike Galbraith55ffb7a2009-10-10 14:46:04 +02001197 }
1198
1199 BUG_ON(list_empty(&atoms->work_list));
1200
1201 atom = list_entry(atoms->work_list.prev, struct work_atom, list);
1202 atom->sched_in_time = atom->sched_out_time = atom->wake_up_time = timestamp;
1203
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001204 sched->nr_timestamps++;
Mike Galbraith55ffb7a2009-10-10 14:46:04 +02001205
1206 if (atom->sched_out_time > timestamp)
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001207 sched->nr_unordered_timestamps++;
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001208 err = 0;
1209out_put:
1210 thread__put(migrant);
1211 return err;
Mike Galbraith55ffb7a2009-10-10 14:46:04 +02001212}
1213
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001214static void output_lat_thread(struct perf_sched *sched, struct work_atoms *work_list)
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001215{
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001216 int i;
1217 int ret;
Frederic Weisbecker66685672009-09-13 01:56:25 +02001218 u64 avg;
Namhyung Kim99620a52016-10-24 11:02:45 +09001219 char max_lat_at[32];
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001220
mingo39aeb522009-09-14 20:04:48 +02001221 if (!work_list->nb_atoms)
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001222 return;
Ingo Molnarea57c4f2009-09-13 18:15:54 +02001223 /*
1224 * Ignore idle threads:
1225 */
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001226 if (!strcmp(thread__comm_str(work_list->thread), "swapper"))
Ingo Molnarea57c4f2009-09-13 18:15:54 +02001227 return;
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001228
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001229 sched->all_runtime += work_list->total_runtime;
1230 sched->all_count += work_list->nb_atoms;
Frederic Weisbecker66685672009-09-13 01:56:25 +02001231
Josef Bacik2f80dd42015-05-22 09:18:40 -04001232 if (work_list->num_merged > 1)
1233 ret = printf(" %s:(%d) ", thread__comm_str(work_list->thread), work_list->num_merged);
1234 else
1235 ret = printf(" %s:%d ", thread__comm_str(work_list->thread), work_list->thread->tid);
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001236
mingo08f69e62009-09-14 18:30:44 +02001237 for (i = 0; i < 24 - ret; i++)
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001238 printf(" ");
1239
mingo39aeb522009-09-14 20:04:48 +02001240 avg = work_list->total_lat / work_list->nb_atoms;
Namhyung Kim99620a52016-10-24 11:02:45 +09001241 timestamp__scnprintf_usec(work_list->max_lat_at, max_lat_at, sizeof(max_lat_at));
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001242
Namhyung Kim99620a52016-10-24 11:02:45 +09001243 printf("|%11.3f ms |%9" PRIu64 " | avg:%9.3f ms | max:%9.3f ms | max at: %13s s\n",
Arnaldo Carvalho de Melo4fc76e42016-08-08 12:23:49 -03001244 (double)work_list->total_runtime / NSEC_PER_MSEC,
1245 work_list->nb_atoms, (double)avg / NSEC_PER_MSEC,
1246 (double)work_list->max_lat / NSEC_PER_MSEC,
Namhyung Kim99620a52016-10-24 11:02:45 +09001247 max_lat_at);
Frederic Weisbeckercdce9d72009-09-12 08:06:14 +02001248}
1249
mingo39aeb522009-09-14 20:04:48 +02001250static int pid_cmp(struct work_atoms *l, struct work_atoms *r)
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02001251{
Jiri Olsa0014de12015-11-02 12:10:25 +01001252 if (l->thread == r->thread)
1253 return 0;
Adrian Hunter38051232013-07-04 16:20:31 +03001254 if (l->thread->tid < r->thread->tid)
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02001255 return -1;
Adrian Hunter38051232013-07-04 16:20:31 +03001256 if (l->thread->tid > r->thread->tid)
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02001257 return 1;
Jiri Olsa0014de12015-11-02 12:10:25 +01001258 return (int)(l->thread - r->thread);
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02001259}
1260
mingo39aeb522009-09-14 20:04:48 +02001261static int avg_cmp(struct work_atoms *l, struct work_atoms *r)
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02001262{
1263 u64 avgl, avgr;
1264
1265 if (!l->nb_atoms)
1266 return -1;
1267
1268 if (!r->nb_atoms)
1269 return 1;
1270
1271 avgl = l->total_lat / l->nb_atoms;
1272 avgr = r->total_lat / r->nb_atoms;
1273
1274 if (avgl < avgr)
1275 return -1;
1276 if (avgl > avgr)
1277 return 1;
1278
1279 return 0;
1280}
1281
mingo39aeb522009-09-14 20:04:48 +02001282static int max_cmp(struct work_atoms *l, struct work_atoms *r)
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02001283{
1284 if (l->max_lat < r->max_lat)
1285 return -1;
1286 if (l->max_lat > r->max_lat)
1287 return 1;
1288
1289 return 0;
1290}
1291
mingo39aeb522009-09-14 20:04:48 +02001292static int switch_cmp(struct work_atoms *l, struct work_atoms *r)
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02001293{
1294 if (l->nb_atoms < r->nb_atoms)
1295 return -1;
1296 if (l->nb_atoms > r->nb_atoms)
1297 return 1;
1298
1299 return 0;
1300}
1301
mingo39aeb522009-09-14 20:04:48 +02001302static int runtime_cmp(struct work_atoms *l, struct work_atoms *r)
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02001303{
1304 if (l->total_runtime < r->total_runtime)
1305 return -1;
1306 if (l->total_runtime > r->total_runtime)
1307 return 1;
1308
1309 return 0;
1310}
1311
Randy Dunlapcbef79a2009-10-05 13:17:29 -07001312static int sort_dimension__add(const char *tok, struct list_head *list)
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02001313{
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001314 size_t i;
1315 static struct sort_dimension avg_sort_dimension = {
1316 .name = "avg",
1317 .cmp = avg_cmp,
1318 };
1319 static struct sort_dimension max_sort_dimension = {
1320 .name = "max",
1321 .cmp = max_cmp,
1322 };
1323 static struct sort_dimension pid_sort_dimension = {
1324 .name = "pid",
1325 .cmp = pid_cmp,
1326 };
1327 static struct sort_dimension runtime_sort_dimension = {
1328 .name = "runtime",
1329 .cmp = runtime_cmp,
1330 };
1331 static struct sort_dimension switch_sort_dimension = {
1332 .name = "switch",
1333 .cmp = switch_cmp,
1334 };
1335 struct sort_dimension *available_sorts[] = {
1336 &pid_sort_dimension,
1337 &avg_sort_dimension,
1338 &max_sort_dimension,
1339 &switch_sort_dimension,
1340 &runtime_sort_dimension,
1341 };
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02001342
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001343 for (i = 0; i < ARRAY_SIZE(available_sorts); i++) {
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02001344 if (!strcmp(available_sorts[i]->name, tok)) {
1345 list_add_tail(&available_sorts[i]->list, list);
1346
1347 return 0;
1348 }
1349 }
1350
1351 return -1;
1352}
1353
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001354static void perf_sched__sort_lat(struct perf_sched *sched)
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02001355{
1356 struct rb_node *node;
Josef Bacik2f80dd42015-05-22 09:18:40 -04001357 struct rb_root *root = &sched->atom_root;
1358again:
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02001359 for (;;) {
mingo39aeb522009-09-14 20:04:48 +02001360 struct work_atoms *data;
Josef Bacik2f80dd42015-05-22 09:18:40 -04001361 node = rb_first(root);
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02001362 if (!node)
1363 break;
1364
Josef Bacik2f80dd42015-05-22 09:18:40 -04001365 rb_erase(node, root);
mingo39aeb522009-09-14 20:04:48 +02001366 data = rb_entry(node, struct work_atoms, node);
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001367 __thread_latency_insert(&sched->sorted_atom_root, data, &sched->sort_list);
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02001368 }
Josef Bacik2f80dd42015-05-22 09:18:40 -04001369 if (root == &sched->atom_root) {
1370 root = &sched->merged_atom_root;
1371 goto again;
1372 }
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02001373}
1374
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001375static int process_sched_wakeup_event(struct perf_tool *tool,
Arnaldo Carvalho de Melo2b7fcbc2012-09-11 19:29:17 -03001376 struct perf_evsel *evsel,
Irina Tirdea1d037ca2012-09-11 01:15:03 +03001377 struct perf_sample *sample,
Arnaldo Carvalho de Melo4218e672012-09-11 13:18:47 -03001378 struct machine *machine)
Frederic Weisbecker419ab0d2009-09-12 03:59:01 +02001379{
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001380 struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
Frederic Weisbecker419ab0d2009-09-12 03:59:01 +02001381
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -03001382 if (sched->tp_handler->wakeup_event)
1383 return sched->tp_handler->wakeup_event(sched, evsel, sample, machine);
Frederic Weisbecker419ab0d2009-09-12 03:59:01 +02001384
Arnaldo Carvalho de Melo2b7fcbc2012-09-11 19:29:17 -03001385 return 0;
Frederic Weisbecker419ab0d2009-09-12 03:59:01 +02001386}
1387
Jiri Olsaa151a372016-04-12 15:29:29 +02001388union map_priv {
1389 void *ptr;
1390 bool color;
1391};
1392
1393static bool thread__has_color(struct thread *thread)
1394{
1395 union map_priv priv = {
1396 .ptr = thread__priv(thread),
1397 };
1398
1399 return priv.color;
1400}
1401
1402static struct thread*
1403map__findnew_thread(struct perf_sched *sched, struct machine *machine, pid_t pid, pid_t tid)
1404{
1405 struct thread *thread = machine__findnew_thread(machine, pid, tid);
1406 union map_priv priv = {
1407 .color = false,
1408 };
1409
1410 if (!sched->map.color_pids || !thread || thread__priv(thread))
1411 return thread;
1412
1413 if (thread_map__has(sched->map.color_pids, tid))
1414 priv.color = true;
1415
1416 thread__set_priv(thread, priv.ptr);
1417 return thread;
1418}
1419
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -03001420static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
1421 struct perf_sample *sample, struct machine *machine)
Ingo Molnar0ec04e12009-09-16 17:40:48 +02001422{
Dongsheng Yang9d372ca2014-05-16 14:37:05 +09001423 const u32 next_pid = perf_evsel__intval(evsel, sample, "next_pid");
1424 struct thread *sched_in;
Ingo Molnar0ec04e12009-09-16 17:40:48 +02001425 int new_shortname;
Arnaldo Carvalho de Melo7f7f8d02012-08-07 11:33:42 -03001426 u64 timestamp0, timestamp = sample->time;
Ingo Molnar0ec04e12009-09-16 17:40:48 +02001427 s64 delta;
Jiri Olsa99623c62016-04-12 15:29:26 +02001428 int i, this_cpu = sample->cpu;
1429 int cpus_nr;
1430 bool new_cpu = false;
Jiri Olsa8cd91192016-04-12 15:29:27 +02001431 const char *color = PERF_COLOR_NORMAL;
Namhyung Kim99620a52016-10-24 11:02:45 +09001432 char stimestamp[32];
Ingo Molnar0ec04e12009-09-16 17:40:48 +02001433
1434 BUG_ON(this_cpu >= MAX_CPUS || this_cpu < 0);
1435
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001436 if (this_cpu > sched->max_cpu)
1437 sched->max_cpu = this_cpu;
Ingo Molnar0ec04e12009-09-16 17:40:48 +02001438
Jiri Olsa99623c62016-04-12 15:29:26 +02001439 if (sched->map.comp) {
1440 cpus_nr = bitmap_weight(sched->map.comp_cpus_mask, MAX_CPUS);
1441 if (!test_and_set_bit(this_cpu, sched->map.comp_cpus_mask)) {
1442 sched->map.comp_cpus[cpus_nr++] = this_cpu;
1443 new_cpu = true;
1444 }
1445 } else
1446 cpus_nr = sched->max_cpu;
1447
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001448 timestamp0 = sched->cpu_last_switched[this_cpu];
1449 sched->cpu_last_switched[this_cpu] = timestamp;
Ingo Molnar0ec04e12009-09-16 17:40:48 +02001450 if (timestamp0)
1451 delta = timestamp - timestamp0;
1452 else
1453 delta = 0;
1454
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001455 if (delta < 0) {
Namhyung Kim60b7d142012-09-12 11:11:06 +09001456 pr_err("hm, delta: %" PRIu64 " < 0 ?\n", delta);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001457 return -1;
1458 }
Ingo Molnar0ec04e12009-09-16 17:40:48 +02001459
Jiri Olsaa151a372016-04-12 15:29:29 +02001460 sched_in = map__findnew_thread(sched, machine, -1, next_pid);
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001461 if (sched_in == NULL)
1462 return -1;
Ingo Molnar0ec04e12009-09-16 17:40:48 +02001463
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001464 sched->curr_thread[this_cpu] = thread__get(sched_in);
Ingo Molnar0ec04e12009-09-16 17:40:48 +02001465
1466 printf(" ");
1467
1468 new_shortname = 0;
1469 if (!sched_in->shortname[0]) {
Dongsheng6bcab4e2014-05-06 14:39:01 +09001470 if (!strcmp(thread__comm_str(sched_in), "swapper")) {
1471 /*
1472 * Don't allocate a letter-number for swapper:0
1473 * as a shortname. Instead, we use '.' for it.
1474 */
1475 sched_in->shortname[0] = '.';
1476 sched_in->shortname[1] = ' ';
Ingo Molnar0ec04e12009-09-16 17:40:48 +02001477 } else {
Dongsheng6bcab4e2014-05-06 14:39:01 +09001478 sched_in->shortname[0] = sched->next_shortname1;
1479 sched_in->shortname[1] = sched->next_shortname2;
1480
1481 if (sched->next_shortname1 < 'Z') {
1482 sched->next_shortname1++;
Ingo Molnar0ec04e12009-09-16 17:40:48 +02001483 } else {
Dongsheng6bcab4e2014-05-06 14:39:01 +09001484 sched->next_shortname1 = 'A';
1485 if (sched->next_shortname2 < '9')
1486 sched->next_shortname2++;
1487 else
1488 sched->next_shortname2 = '0';
Ingo Molnar0ec04e12009-09-16 17:40:48 +02001489 }
1490 }
1491 new_shortname = 1;
1492 }
1493
Jiri Olsa99623c62016-04-12 15:29:26 +02001494 for (i = 0; i < cpus_nr; i++) {
1495 int cpu = sched->map.comp ? sched->map.comp_cpus[i] : i;
Jiri Olsaa151a372016-04-12 15:29:29 +02001496 struct thread *curr_thread = sched->curr_thread[cpu];
1497 const char *pid_color = color;
Jiri Olsacf294f22016-04-12 15:29:30 +02001498 const char *cpu_color = color;
Jiri Olsaa151a372016-04-12 15:29:29 +02001499
1500 if (curr_thread && thread__has_color(curr_thread))
1501 pid_color = COLOR_PIDS;
Jiri Olsa99623c62016-04-12 15:29:26 +02001502
Jiri Olsa73643bb2016-04-12 15:29:31 +02001503 if (sched->map.cpus && !cpu_map__has(sched->map.cpus, cpu))
1504 continue;
1505
Jiri Olsacf294f22016-04-12 15:29:30 +02001506 if (sched->map.color_cpus && cpu_map__has(sched->map.color_cpus, cpu))
1507 cpu_color = COLOR_CPUS;
1508
Ingo Molnar0ec04e12009-09-16 17:40:48 +02001509 if (cpu != this_cpu)
Namhyung Kim1208bb22016-10-24 11:02:43 +09001510 color_fprintf(stdout, color, " ");
Ingo Molnar0ec04e12009-09-16 17:40:48 +02001511 else
Jiri Olsacf294f22016-04-12 15:29:30 +02001512 color_fprintf(stdout, cpu_color, "*");
Ingo Molnar0ec04e12009-09-16 17:40:48 +02001513
Dongsheng6bcab4e2014-05-06 14:39:01 +09001514 if (sched->curr_thread[cpu])
Jiri Olsaa151a372016-04-12 15:29:29 +02001515 color_fprintf(stdout, pid_color, "%2s ", sched->curr_thread[cpu]->shortname);
Dongsheng6bcab4e2014-05-06 14:39:01 +09001516 else
Jiri Olsa8cd91192016-04-12 15:29:27 +02001517 color_fprintf(stdout, color, " ");
Ingo Molnar0ec04e12009-09-16 17:40:48 +02001518 }
1519
Jiri Olsa73643bb2016-04-12 15:29:31 +02001520 if (sched->map.cpus && !cpu_map__has(sched->map.cpus, this_cpu))
1521 goto out;
1522
Namhyung Kim99620a52016-10-24 11:02:45 +09001523 timestamp__scnprintf_usec(timestamp, stimestamp, sizeof(stimestamp));
1524 color_fprintf(stdout, color, " %12s secs ", stimestamp);
Namhyung Kime107f122016-10-24 11:02:44 +09001525 if (new_shortname || (verbose && sched_in->tid)) {
Jiri Olsaa151a372016-04-12 15:29:29 +02001526 const char *pid_color = color;
1527
1528 if (thread__has_color(sched_in))
1529 pid_color = COLOR_PIDS;
1530
1531 color_fprintf(stdout, pid_color, "%s => %s:%d",
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001532 sched_in->shortname, thread__comm_str(sched_in), sched_in->tid);
Ingo Molnar0ec04e12009-09-16 17:40:48 +02001533 }
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001534
Jiri Olsa99623c62016-04-12 15:29:26 +02001535 if (sched->map.comp && new_cpu)
Jiri Olsa8cd91192016-04-12 15:29:27 +02001536 color_fprintf(stdout, color, " (CPU %d)", this_cpu);
Jiri Olsa99623c62016-04-12 15:29:26 +02001537
Jiri Olsa73643bb2016-04-12 15:29:31 +02001538out:
Jiri Olsa8cd91192016-04-12 15:29:27 +02001539 color_fprintf(stdout, color, "\n");
Jiri Olsa99623c62016-04-12 15:29:26 +02001540
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -03001541 thread__put(sched_in);
1542
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001543 return 0;
Ingo Molnar0ec04e12009-09-16 17:40:48 +02001544}
1545
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001546static int process_sched_switch_event(struct perf_tool *tool,
Arnaldo Carvalho de Melo2b7fcbc2012-09-11 19:29:17 -03001547 struct perf_evsel *evsel,
Irina Tirdea1d037ca2012-09-11 01:15:03 +03001548 struct perf_sample *sample,
Arnaldo Carvalho de Melo4218e672012-09-11 13:18:47 -03001549 struct machine *machine)
Frederic Weisbecker419ab0d2009-09-12 03:59:01 +02001550{
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001551 struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001552 int this_cpu = sample->cpu, err = 0;
Arnaldo Carvalho de Melo2b7fcbc2012-09-11 19:29:17 -03001553 u32 prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"),
1554 next_pid = perf_evsel__intval(evsel, sample, "next_pid");
Frederic Weisbecker419ab0d2009-09-12 03:59:01 +02001555
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001556 if (sched->curr_pid[this_cpu] != (u32)-1) {
Ingo Molnarc8a37752009-09-16 14:07:00 +02001557 /*
1558 * Are we trying to switch away a PID that is
1559 * not current?
1560 */
Arnaldo Carvalho de Melo2b7fcbc2012-09-11 19:29:17 -03001561 if (sched->curr_pid[this_cpu] != prev_pid)
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001562 sched->nr_context_switch_bugs++;
Ingo Molnarc8a37752009-09-16 14:07:00 +02001563 }
Ingo Molnarc8a37752009-09-16 14:07:00 +02001564
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -03001565 if (sched->tp_handler->switch_event)
1566 err = sched->tp_handler->switch_event(sched, evsel, sample, machine);
Arnaldo Carvalho de Melo2b7fcbc2012-09-11 19:29:17 -03001567
1568 sched->curr_pid[this_cpu] = next_pid;
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001569 return err;
Frederic Weisbecker419ab0d2009-09-12 03:59:01 +02001570}
1571
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001572static int process_sched_runtime_event(struct perf_tool *tool,
Arnaldo Carvalho de Melo2b7fcbc2012-09-11 19:29:17 -03001573 struct perf_evsel *evsel,
Irina Tirdea1d037ca2012-09-11 01:15:03 +03001574 struct perf_sample *sample,
Arnaldo Carvalho de Melo4218e672012-09-11 13:18:47 -03001575 struct machine *machine)
mingo39aeb522009-09-14 20:04:48 +02001576{
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001577 struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
mingo39aeb522009-09-14 20:04:48 +02001578
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -03001579 if (sched->tp_handler->runtime_event)
1580 return sched->tp_handler->runtime_event(sched, evsel, sample, machine);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001581
1582 return 0;
Ingo Molnarec156762009-09-11 12:12:54 +02001583}
1584
David Aherncb627502013-08-07 22:50:47 -04001585static int perf_sched__process_fork_event(struct perf_tool *tool,
1586 union perf_event *event,
1587 struct perf_sample *sample,
1588 struct machine *machine)
Arnaldo Carvalho de Melo2b7fcbc2012-09-11 19:29:17 -03001589{
1590 struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
1591
David Aherncb627502013-08-07 22:50:47 -04001592 /* run the fork event through the perf machineruy */
1593 perf_event__process_fork(tool, event, sample, machine);
1594
1595 /* and then run additional processing needed for this command */
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -03001596 if (sched->tp_handler->fork_event)
David Aherncb627502013-08-07 22:50:47 -04001597 return sched->tp_handler->fork_event(sched, event, machine);
Arnaldo Carvalho de Melo2b7fcbc2012-09-11 19:29:17 -03001598
1599 return 0;
1600}
1601
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001602static int process_sched_migrate_task_event(struct perf_tool *tool,
Arnaldo Carvalho de Melo2b7fcbc2012-09-11 19:29:17 -03001603 struct perf_evsel *evsel,
Irina Tirdea1d037ca2012-09-11 01:15:03 +03001604 struct perf_sample *sample,
Arnaldo Carvalho de Melo4218e672012-09-11 13:18:47 -03001605 struct machine *machine)
Mike Galbraith55ffb7a2009-10-10 14:46:04 +02001606{
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03001607 struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
Mike Galbraith55ffb7a2009-10-10 14:46:04 +02001608
Arnaldo Carvalho de Melo9ec3f4e2012-09-11 19:29:17 -03001609 if (sched->tp_handler->migrate_task_event)
1610 return sched->tp_handler->migrate_task_event(sched, evsel, sample, machine);
Mike Galbraith55ffb7a2009-10-10 14:46:04 +02001611
Arnaldo Carvalho de Melo2b7fcbc2012-09-11 19:29:17 -03001612 return 0;
Mike Galbraith55ffb7a2009-10-10 14:46:04 +02001613}
1614
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001615typedef int (*tracepoint_handler)(struct perf_tool *tool,
Arnaldo Carvalho de Melo2b7fcbc2012-09-11 19:29:17 -03001616 struct perf_evsel *evsel,
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001617 struct perf_sample *sample,
Arnaldo Carvalho de Melo4218e672012-09-11 13:18:47 -03001618 struct machine *machine);
Arnaldo Carvalho de Meloee29be62011-11-28 17:57:40 -02001619
Irina Tirdea1d037ca2012-09-11 01:15:03 +03001620static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_unused,
1621 union perf_event *event __maybe_unused,
Arnaldo Carvalho de Meloee29be62011-11-28 17:57:40 -02001622 struct perf_sample *sample,
1623 struct perf_evsel *evsel,
1624 struct machine *machine)
Ingo Molnarec156762009-09-11 12:12:54 +02001625{
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001626 int err = 0;
Ingo Molnarec156762009-09-11 12:12:54 +02001627
Arnaldo Carvalho de Melo744a9712013-11-06 10:17:38 -03001628 if (evsel->handler != NULL) {
1629 tracepoint_handler f = evsel->handler;
Arnaldo Carvalho de Melo2b7fcbc2012-09-11 19:29:17 -03001630 err = f(tool, evsel, sample, machine);
Arnaldo Carvalho de Meloee29be62011-11-28 17:57:40 -02001631 }
Ingo Molnar0a02ad92009-09-11 12:12:54 +02001632
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001633 return err;
Ingo Molnar0a02ad92009-09-11 12:12:54 +02001634}
1635
Arnaldo Carvalho de Meloae536ac2015-03-02 22:28:41 -03001636static int perf_sched__read_events(struct perf_sched *sched)
Ingo Molnar0a02ad92009-09-11 12:12:54 +02001637{
Arnaldo Carvalho de Meloee29be62011-11-28 17:57:40 -02001638 const struct perf_evsel_str_handler handlers[] = {
1639 { "sched:sched_switch", process_sched_switch_event, },
1640 { "sched:sched_stat_runtime", process_sched_runtime_event, },
1641 { "sched:sched_wakeup", process_sched_wakeup_event, },
1642 { "sched:sched_wakeup_new", process_sched_wakeup_event, },
Arnaldo Carvalho de Meloee29be62011-11-28 17:57:40 -02001643 { "sched:sched_migrate_task", process_sched_migrate_task_event, },
1644 };
Arnaldo Carvalho de Meloda378962012-06-27 13:08:42 -03001645 struct perf_session *session;
Jiri Olsaf5fc14122013-10-15 16:27:32 +02001646 struct perf_data_file file = {
1647 .path = input_name,
1648 .mode = PERF_DATA_MODE_READ,
Yunlong Songf0dd3302015-03-31 21:46:35 +08001649 .force = sched->force,
Jiri Olsaf5fc14122013-10-15 16:27:32 +02001650 };
Arnaldo Carvalho de Meloae536ac2015-03-02 22:28:41 -03001651 int rc = -1;
Arnaldo Carvalho de Meloda378962012-06-27 13:08:42 -03001652
Jiri Olsaf5fc14122013-10-15 16:27:32 +02001653 session = perf_session__new(&file, false, &sched->tool);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001654 if (session == NULL) {
1655 pr_debug("No Memory for session\n");
1656 return -1;
1657 }
Arnaldo Carvalho de Melo94c744b2009-12-11 21:24:02 -02001658
Namhyung Kim0a7e6d12014-08-12 15:40:45 +09001659 symbol__init(&session->header.env);
Namhyung Kim04934102014-08-12 15:40:41 +09001660
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001661 if (perf_session__set_tracepoints_handlers(session, handlers))
1662 goto out_delete;
Arnaldo Carvalho de Meloee29be62011-11-28 17:57:40 -02001663
Arnaldo Carvalho de Melocee75ac2010-05-14 13:16:55 -03001664 if (perf_session__has_traces(session, "record -R")) {
Arnaldo Carvalho de Melob7b61cb2015-03-03 11:58:45 -03001665 int err = perf_session__process_events(session);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001666 if (err) {
1667 pr_err("Failed to process events, error %d", err);
1668 goto out_delete;
1669 }
Jiri Olsa4c09baf2011-08-08 23:03:34 +02001670
Arnaldo Carvalho de Melo75be9892015-02-14 14:50:11 -03001671 sched->nr_events = session->evlist->stats.nr_events[0];
1672 sched->nr_lost_events = session->evlist->stats.total_lost;
1673 sched->nr_lost_chunks = session->evlist->stats.nr_events[PERF_RECORD_LOST];
Arnaldo Carvalho de Melocee75ac2010-05-14 13:16:55 -03001674 }
Arnaldo Carvalho de Melod549c7692009-12-27 21:37:02 -02001675
Arnaldo Carvalho de Meloae536ac2015-03-02 22:28:41 -03001676 rc = 0;
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03001677out_delete:
1678 perf_session__delete(session);
Arnaldo Carvalho de Meloae536ac2015-03-02 22:28:41 -03001679 return rc;
Ingo Molnar0a02ad92009-09-11 12:12:54 +02001680}
1681
David Ahern49394a22016-11-16 15:06:29 +09001682/*
1683 * scheduling times are printed as msec.usec
1684 */
1685static inline void print_sched_time(unsigned long long nsecs, int width)
1686{
1687 unsigned long msecs;
1688 unsigned long usecs;
1689
1690 msecs = nsecs / NSEC_PER_MSEC;
1691 nsecs -= msecs * NSEC_PER_MSEC;
1692 usecs = nsecs / NSEC_PER_USEC;
1693 printf("%*lu.%03lu ", width, msecs, usecs);
1694}
1695
1696/*
1697 * returns runtime data for event, allocating memory for it the
1698 * first time it is used.
1699 */
1700static struct evsel_runtime *perf_evsel__get_runtime(struct perf_evsel *evsel)
1701{
1702 struct evsel_runtime *r = evsel->priv;
1703
1704 if (r == NULL) {
1705 r = zalloc(sizeof(struct evsel_runtime));
1706 evsel->priv = r;
1707 }
1708
1709 return r;
1710}
1711
1712/*
1713 * save last time event was seen per cpu
1714 */
1715static void perf_evsel__save_time(struct perf_evsel *evsel,
1716 u64 timestamp, u32 cpu)
1717{
1718 struct evsel_runtime *r = perf_evsel__get_runtime(evsel);
1719
1720 if (r == NULL)
1721 return;
1722
1723 if ((cpu >= r->ncpu) || (r->last_time == NULL)) {
1724 int i, n = __roundup_pow_of_two(cpu+1);
1725 void *p = r->last_time;
1726
1727 p = realloc(r->last_time, n * sizeof(u64));
1728 if (!p)
1729 return;
1730
1731 r->last_time = p;
1732 for (i = r->ncpu; i < n; ++i)
1733 r->last_time[i] = (u64) 0;
1734
1735 r->ncpu = n;
1736 }
1737
1738 r->last_time[cpu] = timestamp;
1739}
1740
1741/* returns last time this event was seen on the given cpu */
1742static u64 perf_evsel__get_time(struct perf_evsel *evsel, u32 cpu)
1743{
1744 struct evsel_runtime *r = perf_evsel__get_runtime(evsel);
1745
1746 if ((r == NULL) || (r->last_time == NULL) || (cpu >= r->ncpu))
1747 return 0;
1748
1749 return r->last_time[cpu];
1750}
1751
1752static int comm_width = 20;
1753
1754static char *timehist_get_commstr(struct thread *thread)
1755{
1756 static char str[32];
1757 const char *comm = thread__comm_str(thread);
1758 pid_t tid = thread->tid;
1759 pid_t pid = thread->pid_;
1760 int n;
1761
1762 if (pid == 0)
1763 n = scnprintf(str, sizeof(str), "%s", comm);
1764
1765 else if (tid != pid)
1766 n = scnprintf(str, sizeof(str), "%s[%d/%d]", comm, tid, pid);
1767
1768 else
1769 n = scnprintf(str, sizeof(str), "%s[%d]", comm, tid);
1770
1771 if (n > comm_width)
1772 comm_width = n;
1773
1774 return str;
1775}
1776
1777static void timehist_header(void)
1778{
1779 printf("%15s %6s ", "time", "cpu");
1780
1781 printf(" %-20s %9s %9s %9s",
1782 "task name", "wait time", "sch delay", "run time");
1783
1784 printf("\n");
1785
1786 /*
1787 * units row
1788 */
1789 printf("%15s %-6s ", "", "");
1790
1791 printf(" %-20s %9s %9s %9s\n", "[tid/pid]", "(msec)", "(msec)", "(msec)");
1792
1793 /*
1794 * separator
1795 */
1796 printf("%.15s %.6s ", graph_dotted_line, graph_dotted_line);
1797
1798 printf(" %.20s %.9s %.9s %.9s",
1799 graph_dotted_line, graph_dotted_line, graph_dotted_line,
1800 graph_dotted_line);
1801
1802 printf("\n");
1803}
1804
1805static void timehist_print_sample(struct perf_sample *sample,
1806 struct thread *thread)
1807{
1808 struct thread_runtime *tr = thread__priv(thread);
1809 char tstr[64];
1810
1811 timestamp__scnprintf_usec(sample->time, tstr, sizeof(tstr));
1812 printf("%15s [%04d] ", tstr, sample->cpu);
1813
1814 printf(" %-*s ", comm_width, timehist_get_commstr(thread));
1815
1816 print_sched_time(tr->dt_wait, 6);
1817 print_sched_time(tr->dt_delay, 6);
1818 print_sched_time(tr->dt_run, 6);
1819 printf("\n");
1820}
1821
1822/*
1823 * Explanation of delta-time stats:
1824 *
1825 * t = time of current schedule out event
1826 * tprev = time of previous sched out event
1827 * also time of schedule-in event for current task
1828 * last_time = time of last sched change event for current task
1829 * (i.e, time process was last scheduled out)
1830 * ready_to_run = time of wakeup for current task
1831 *
1832 * -----|------------|------------|------------|------
1833 * last ready tprev t
1834 * time to run
1835 *
1836 * |-------- dt_wait --------|
1837 * |- dt_delay -|-- dt_run --|
1838 *
1839 * dt_run = run time of current task
1840 * dt_wait = time between last schedule out event for task and tprev
1841 * represents time spent off the cpu
1842 * dt_delay = time between wakeup and schedule-in of task
1843 */
1844
1845static void timehist_update_runtime_stats(struct thread_runtime *r,
1846 u64 t, u64 tprev)
1847{
1848 r->dt_delay = 0;
1849 r->dt_wait = 0;
1850 r->dt_run = 0;
1851 if (tprev) {
1852 r->dt_run = t - tprev;
1853 if (r->ready_to_run) {
1854 if (r->ready_to_run > tprev)
1855 pr_debug("time travel: wakeup time for task > previous sched_switch event\n");
1856 else
1857 r->dt_delay = tprev - r->ready_to_run;
1858 }
1859
1860 if (r->last_time > tprev)
1861 pr_debug("time travel: last sched out time for task > previous sched_switch event\n");
1862 else if (r->last_time)
1863 r->dt_wait = tprev - r->last_time;
1864 }
1865
1866 update_stats(&r->run_stats, r->dt_run);
1867 r->total_run_time += r->dt_run;
1868}
1869
1870static bool is_idle_sample(struct perf_sample *sample,
1871 struct perf_evsel *evsel)
1872{
1873 /* pid 0 == swapper == idle task */
1874 if (sample->pid == 0)
1875 return true;
1876
1877 if (strcmp(perf_evsel__name(evsel), "sched:sched_switch") == 0) {
1878 if (perf_evsel__intval(evsel, sample, "prev_pid") == 0)
1879 return true;
1880 }
1881 return false;
1882}
1883
1884/*
1885 * Track idle stats per cpu by maintaining a local thread
1886 * struct for the idle task on each cpu.
1887 */
1888static int init_idle_threads(int ncpu)
1889{
1890 int i;
1891
1892 idle_threads = zalloc(ncpu * sizeof(struct thread *));
1893 if (!idle_threads)
1894 return -ENOMEM;
1895
1896 idle_max_cpu = ncpu - 1;
1897
1898 /* allocate the actual thread struct if needed */
1899 for (i = 0; i < ncpu; ++i) {
1900 idle_threads[i] = thread__new(0, 0);
1901 if (idle_threads[i] == NULL)
1902 return -ENOMEM;
1903
1904 thread__set_comm(idle_threads[i], idle_comm, 0);
1905 }
1906
1907 return 0;
1908}
1909
1910static void free_idle_threads(void)
1911{
1912 int i;
1913
1914 if (idle_threads == NULL)
1915 return;
1916
1917 for (i = 0; i <= idle_max_cpu; ++i) {
1918 if ((idle_threads[i]))
1919 thread__delete(idle_threads[i]);
1920 }
1921
1922 free(idle_threads);
1923}
1924
1925static struct thread *get_idle_thread(int cpu)
1926{
1927 /*
1928 * expand/allocate array of pointers to local thread
1929 * structs if needed
1930 */
1931 if ((cpu >= idle_max_cpu) || (idle_threads == NULL)) {
1932 int i, j = __roundup_pow_of_two(cpu+1);
1933 void *p;
1934
1935 p = realloc(idle_threads, j * sizeof(struct thread *));
1936 if (!p)
1937 return NULL;
1938
1939 idle_threads = (struct thread **) p;
1940 i = idle_max_cpu ? idle_max_cpu + 1 : 0;
1941 for (; i < j; ++i)
1942 idle_threads[i] = NULL;
1943
1944 idle_max_cpu = j;
1945 }
1946
1947 /* allocate a new thread struct if needed */
1948 if (idle_threads[cpu] == NULL) {
1949 idle_threads[cpu] = thread__new(0, 0);
1950
1951 if (idle_threads[cpu]) {
1952 idle_threads[cpu]->tid = 0;
1953 thread__set_comm(idle_threads[cpu], idle_comm, 0);
1954 }
1955 }
1956
1957 return idle_threads[cpu];
1958}
1959
1960/*
1961 * handle runtime stats saved per thread
1962 */
1963static struct thread_runtime *thread__init_runtime(struct thread *thread)
1964{
1965 struct thread_runtime *r;
1966
1967 r = zalloc(sizeof(struct thread_runtime));
1968 if (!r)
1969 return NULL;
1970
1971 init_stats(&r->run_stats);
1972 thread__set_priv(thread, r);
1973
1974 return r;
1975}
1976
1977static struct thread_runtime *thread__get_runtime(struct thread *thread)
1978{
1979 struct thread_runtime *tr;
1980
1981 tr = thread__priv(thread);
1982 if (tr == NULL) {
1983 tr = thread__init_runtime(thread);
1984 if (tr == NULL)
1985 pr_debug("Failed to malloc memory for runtime data.\n");
1986 }
1987
1988 return tr;
1989}
1990
1991static struct thread *timehist_get_thread(struct perf_sample *sample,
1992 struct machine *machine,
1993 struct perf_evsel *evsel)
1994{
1995 struct thread *thread;
1996
1997 if (is_idle_sample(sample, evsel)) {
1998 thread = get_idle_thread(sample->cpu);
1999 if (thread == NULL)
2000 pr_err("Failed to get idle thread for cpu %d.\n", sample->cpu);
2001
2002 } else {
2003 thread = machine__findnew_thread(machine, sample->pid, sample->tid);
2004 if (thread == NULL) {
2005 pr_debug("Failed to get thread for tid %d. skipping sample.\n",
2006 sample->tid);
2007 }
2008 }
2009
2010 return thread;
2011}
2012
2013static bool timehist_skip_sample(struct thread *thread)
2014{
2015 bool rc = false;
2016
2017 if (thread__is_filtered(thread))
2018 rc = true;
2019
2020 return rc;
2021}
2022
2023static int timehist_sched_wakeup_event(struct perf_tool *tool __maybe_unused,
2024 union perf_event *event __maybe_unused,
2025 struct perf_evsel *evsel,
2026 struct perf_sample *sample,
2027 struct machine *machine)
2028{
2029 struct thread *thread;
2030 struct thread_runtime *tr = NULL;
2031 /* want pid of awakened task not pid in sample */
2032 const u32 pid = perf_evsel__intval(evsel, sample, "pid");
2033
2034 thread = machine__findnew_thread(machine, 0, pid);
2035 if (thread == NULL)
2036 return -1;
2037
2038 tr = thread__get_runtime(thread);
2039 if (tr == NULL)
2040 return -1;
2041
2042 if (tr->ready_to_run == 0)
2043 tr->ready_to_run = sample->time;
2044
2045 return 0;
2046}
2047
2048static int timehist_sched_change_event(struct perf_tool *tool __maybe_unused,
2049 union perf_event *event,
2050 struct perf_evsel *evsel,
2051 struct perf_sample *sample,
2052 struct machine *machine)
2053{
2054 struct addr_location al;
2055 struct thread *thread;
2056 struct thread_runtime *tr = NULL;
2057 u64 tprev;
2058 int rc = 0;
2059
2060 if (machine__resolve(machine, &al, sample) < 0) {
2061 pr_err("problem processing %d event. skipping it\n",
2062 event->header.type);
2063 rc = -1;
2064 goto out;
2065 }
2066
2067 thread = timehist_get_thread(sample, machine, evsel);
2068 if (thread == NULL) {
2069 rc = -1;
2070 goto out;
2071 }
2072
2073 if (timehist_skip_sample(thread))
2074 goto out;
2075
2076 tr = thread__get_runtime(thread);
2077 if (tr == NULL) {
2078 rc = -1;
2079 goto out;
2080 }
2081
2082 tprev = perf_evsel__get_time(evsel, sample->cpu);
2083
2084 timehist_update_runtime_stats(tr, sample->time, tprev);
2085 timehist_print_sample(sample, thread);
2086
2087out:
2088 if (tr) {
2089 /* time of this sched_switch event becomes last time task seen */
2090 tr->last_time = sample->time;
2091
2092 /* sched out event for task so reset ready to run time */
2093 tr->ready_to_run = 0;
2094 }
2095
2096 perf_evsel__save_time(evsel, sample->time, sample->cpu);
2097
2098 return rc;
2099}
2100
2101static int timehist_sched_switch_event(struct perf_tool *tool,
2102 union perf_event *event,
2103 struct perf_evsel *evsel,
2104 struct perf_sample *sample,
2105 struct machine *machine __maybe_unused)
2106{
2107 return timehist_sched_change_event(tool, event, evsel, sample, machine);
2108}
2109
2110static int process_lost(struct perf_tool *tool __maybe_unused,
2111 union perf_event *event,
2112 struct perf_sample *sample,
2113 struct machine *machine __maybe_unused)
2114{
2115 char tstr[64];
2116
2117 timestamp__scnprintf_usec(sample->time, tstr, sizeof(tstr));
2118 printf("%15s ", tstr);
2119 printf("lost %" PRIu64 " events on cpu %d\n", event->lost.lost, sample->cpu);
2120
2121 return 0;
2122}
2123
2124
2125typedef int (*sched_handler)(struct perf_tool *tool,
2126 union perf_event *event,
2127 struct perf_evsel *evsel,
2128 struct perf_sample *sample,
2129 struct machine *machine);
2130
2131static int perf_timehist__process_sample(struct perf_tool *tool,
2132 union perf_event *event,
2133 struct perf_sample *sample,
2134 struct perf_evsel *evsel,
2135 struct machine *machine)
2136{
2137 struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
2138 int err = 0;
2139 int this_cpu = sample->cpu;
2140
2141 if (this_cpu > sched->max_cpu)
2142 sched->max_cpu = this_cpu;
2143
2144 if (evsel->handler != NULL) {
2145 sched_handler f = evsel->handler;
2146
2147 err = f(tool, event, evsel, sample, machine);
2148 }
2149
2150 return err;
2151}
2152
2153static int perf_sched__timehist(struct perf_sched *sched)
2154{
2155 const struct perf_evsel_str_handler handlers[] = {
2156 { "sched:sched_switch", timehist_sched_switch_event, },
2157 { "sched:sched_wakeup", timehist_sched_wakeup_event, },
2158 { "sched:sched_wakeup_new", timehist_sched_wakeup_event, },
2159 };
2160 struct perf_data_file file = {
2161 .path = input_name,
2162 .mode = PERF_DATA_MODE_READ,
2163 };
2164
2165 struct perf_session *session;
2166 int err = -1;
2167
2168 /*
2169 * event handlers for timehist option
2170 */
2171 sched->tool.sample = perf_timehist__process_sample;
2172 sched->tool.mmap = perf_event__process_mmap;
2173 sched->tool.comm = perf_event__process_comm;
2174 sched->tool.exit = perf_event__process_exit;
2175 sched->tool.fork = perf_event__process_fork;
2176 sched->tool.lost = process_lost;
2177 sched->tool.attr = perf_event__process_attr;
2178 sched->tool.tracing_data = perf_event__process_tracing_data;
2179 sched->tool.build_id = perf_event__process_build_id;
2180
2181 sched->tool.ordered_events = true;
2182 sched->tool.ordering_requires_timestamps = true;
2183
2184 session = perf_session__new(&file, false, &sched->tool);
2185 if (session == NULL)
2186 return -ENOMEM;
2187
2188 symbol__init(&session->header.env);
2189
2190 setup_pager();
2191
2192 /* setup per-evsel handlers */
2193 if (perf_session__set_tracepoints_handlers(session, handlers))
2194 goto out;
2195
2196 if (!perf_session__has_traces(session, "record -R"))
2197 goto out;
2198
2199 /* pre-allocate struct for per-CPU idle stats */
2200 sched->max_cpu = session->header.env.nr_cpus_online;
2201 if (sched->max_cpu == 0)
2202 sched->max_cpu = 4;
2203 if (init_idle_threads(sched->max_cpu))
2204 goto out;
2205
2206 timehist_header();
2207
2208 err = perf_session__process_events(session);
2209 if (err) {
2210 pr_err("Failed to process events, error %d", err);
2211 goto out;
2212 }
2213
2214out:
2215 free_idle_threads();
2216 perf_session__delete(session);
2217
2218 return err;
2219}
2220
2221
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002222static void print_bad_events(struct perf_sched *sched)
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002223{
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002224 if (sched->nr_unordered_timestamps && sched->nr_timestamps) {
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002225 printf(" INFO: %.3f%% unordered timestamps (%ld out of %ld)\n",
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002226 (double)sched->nr_unordered_timestamps/(double)sched->nr_timestamps*100.0,
2227 sched->nr_unordered_timestamps, sched->nr_timestamps);
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002228 }
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002229 if (sched->nr_lost_events && sched->nr_events) {
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002230 printf(" INFO: %.3f%% lost events (%ld out of %ld, in %ld chunks)\n",
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002231 (double)sched->nr_lost_events/(double)sched->nr_events * 100.0,
2232 sched->nr_lost_events, sched->nr_events, sched->nr_lost_chunks);
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002233 }
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002234 if (sched->nr_context_switch_bugs && sched->nr_timestamps) {
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002235 printf(" INFO: %.3f%% context switch bugs (%ld out of %ld)",
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002236 (double)sched->nr_context_switch_bugs/(double)sched->nr_timestamps*100.0,
2237 sched->nr_context_switch_bugs, sched->nr_timestamps);
2238 if (sched->nr_lost_events)
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002239 printf(" (due to lost events?)");
2240 printf("\n");
2241 }
2242}
2243
Josef Bacik2f80dd42015-05-22 09:18:40 -04002244static void __merge_work_atoms(struct rb_root *root, struct work_atoms *data)
2245{
2246 struct rb_node **new = &(root->rb_node), *parent = NULL;
2247 struct work_atoms *this;
2248 const char *comm = thread__comm_str(data->thread), *this_comm;
2249
2250 while (*new) {
2251 int cmp;
2252
2253 this = container_of(*new, struct work_atoms, node);
2254 parent = *new;
2255
2256 this_comm = thread__comm_str(this->thread);
2257 cmp = strcmp(comm, this_comm);
2258 if (cmp > 0) {
2259 new = &((*new)->rb_left);
2260 } else if (cmp < 0) {
2261 new = &((*new)->rb_right);
2262 } else {
2263 this->num_merged++;
2264 this->total_runtime += data->total_runtime;
2265 this->nb_atoms += data->nb_atoms;
2266 this->total_lat += data->total_lat;
2267 list_splice(&data->work_list, &this->work_list);
2268 if (this->max_lat < data->max_lat) {
2269 this->max_lat = data->max_lat;
2270 this->max_lat_at = data->max_lat_at;
2271 }
2272 zfree(&data);
2273 return;
2274 }
2275 }
2276
2277 data->num_merged++;
2278 rb_link_node(&data->node, parent, new);
2279 rb_insert_color(&data->node, root);
2280}
2281
2282static void perf_sched__merge_lat(struct perf_sched *sched)
2283{
2284 struct work_atoms *data;
2285 struct rb_node *node;
2286
2287 if (sched->skip_merge)
2288 return;
2289
2290 while ((node = rb_first(&sched->atom_root))) {
2291 rb_erase(node, &sched->atom_root);
2292 data = rb_entry(node, struct work_atoms, node);
2293 __merge_work_atoms(&sched->merged_atom_root, data);
2294 }
2295}
2296
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002297static int perf_sched__lat(struct perf_sched *sched)
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002298{
2299 struct rb_node *next;
2300
2301 setup_pager();
David Ahernad9def72013-08-07 22:50:44 -04002302
Arnaldo Carvalho de Meloae536ac2015-03-02 22:28:41 -03002303 if (perf_sched__read_events(sched))
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03002304 return -1;
David Ahernad9def72013-08-07 22:50:44 -04002305
Josef Bacik2f80dd42015-05-22 09:18:40 -04002306 perf_sched__merge_lat(sched);
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002307 perf_sched__sort_lat(sched);
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002308
Ramkumar Ramachandra80790e02014-03-17 10:18:21 -04002309 printf("\n -----------------------------------------------------------------------------------------------------------------\n");
2310 printf(" Task | Runtime ms | Switches | Average delay ms | Maximum delay ms | Maximum delay at |\n");
2311 printf(" -----------------------------------------------------------------------------------------------------------------\n");
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002312
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002313 next = rb_first(&sched->sorted_atom_root);
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002314
2315 while (next) {
2316 struct work_atoms *work_list;
2317
2318 work_list = rb_entry(next, struct work_atoms, node);
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002319 output_lat_thread(sched, work_list);
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002320 next = rb_next(next);
Arnaldo Carvalho de Meloae536ac2015-03-02 22:28:41 -03002321 thread__zput(work_list->thread);
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002322 }
2323
Ramkumar Ramachandra80790e02014-03-17 10:18:21 -04002324 printf(" -----------------------------------------------------------------------------------------------------------------\n");
Arnaldo Carvalho de Melo9486aa32011-01-22 20:37:02 -02002325 printf(" TOTAL: |%11.3f ms |%9" PRIu64 " |\n",
Arnaldo Carvalho de Melo4fc76e42016-08-08 12:23:49 -03002326 (double)sched->all_runtime / NSEC_PER_MSEC, sched->all_count);
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002327
2328 printf(" ---------------------------------------------------\n");
2329
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002330 print_bad_events(sched);
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002331 printf("\n");
2332
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03002333 return 0;
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002334}
2335
Jiri Olsa99623c62016-04-12 15:29:26 +02002336static int setup_map_cpus(struct perf_sched *sched)
2337{
Jiri Olsa73643bb2016-04-12 15:29:31 +02002338 struct cpu_map *map;
2339
Jiri Olsa99623c62016-04-12 15:29:26 +02002340 sched->max_cpu = sysconf(_SC_NPROCESSORS_CONF);
2341
2342 if (sched->map.comp) {
2343 sched->map.comp_cpus = zalloc(sched->max_cpu * sizeof(int));
Jiri Olsacf294f22016-04-12 15:29:30 +02002344 if (!sched->map.comp_cpus)
2345 return -1;
Jiri Olsa99623c62016-04-12 15:29:26 +02002346 }
2347
Jiri Olsa73643bb2016-04-12 15:29:31 +02002348 if (!sched->map.cpus_str)
2349 return 0;
2350
2351 map = cpu_map__new(sched->map.cpus_str);
2352 if (!map) {
2353 pr_err("failed to get cpus map from %s\n", sched->map.cpus_str);
2354 return -1;
2355 }
2356
2357 sched->map.cpus = map;
Jiri Olsa99623c62016-04-12 15:29:26 +02002358 return 0;
2359}
2360
Jiri Olsaa151a372016-04-12 15:29:29 +02002361static int setup_color_pids(struct perf_sched *sched)
2362{
2363 struct thread_map *map;
2364
2365 if (!sched->map.color_pids_str)
2366 return 0;
2367
2368 map = thread_map__new_by_tid_str(sched->map.color_pids_str);
2369 if (!map) {
2370 pr_err("failed to get thread map from %s\n", sched->map.color_pids_str);
2371 return -1;
2372 }
2373
2374 sched->map.color_pids = map;
2375 return 0;
2376}
2377
Jiri Olsacf294f22016-04-12 15:29:30 +02002378static int setup_color_cpus(struct perf_sched *sched)
2379{
2380 struct cpu_map *map;
2381
2382 if (!sched->map.color_cpus_str)
2383 return 0;
2384
2385 map = cpu_map__new(sched->map.color_cpus_str);
2386 if (!map) {
2387 pr_err("failed to get thread map from %s\n", sched->map.color_cpus_str);
2388 return -1;
2389 }
2390
2391 sched->map.color_cpus = map;
2392 return 0;
2393}
2394
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002395static int perf_sched__map(struct perf_sched *sched)
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002396{
Jiri Olsa99623c62016-04-12 15:29:26 +02002397 if (setup_map_cpus(sched))
2398 return -1;
Ingo Molnar40749d02009-09-17 18:24:55 +02002399
Jiri Olsaa151a372016-04-12 15:29:29 +02002400 if (setup_color_pids(sched))
2401 return -1;
2402
Jiri Olsacf294f22016-04-12 15:29:30 +02002403 if (setup_color_cpus(sched))
2404 return -1;
2405
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002406 setup_pager();
Arnaldo Carvalho de Meloae536ac2015-03-02 22:28:41 -03002407 if (perf_sched__read_events(sched))
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03002408 return -1;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002409 print_bad_events(sched);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03002410 return 0;
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002411}
2412
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002413static int perf_sched__replay(struct perf_sched *sched)
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002414{
2415 unsigned long i;
2416
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002417 calibrate_run_measurement_overhead(sched);
2418 calibrate_sleep_measurement_overhead(sched);
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002419
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002420 test_calibrations(sched);
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002421
Arnaldo Carvalho de Meloae536ac2015-03-02 22:28:41 -03002422 if (perf_sched__read_events(sched))
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03002423 return -1;
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002424
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002425 printf("nr_run_events: %ld\n", sched->nr_run_events);
2426 printf("nr_sleep_events: %ld\n", sched->nr_sleep_events);
2427 printf("nr_wakeup_events: %ld\n", sched->nr_wakeup_events);
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002428
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002429 if (sched->targetless_wakeups)
2430 printf("target-less wakeups: %ld\n", sched->targetless_wakeups);
2431 if (sched->multitarget_wakeups)
2432 printf("multi-target wakeups: %ld\n", sched->multitarget_wakeups);
2433 if (sched->nr_run_events_optimized)
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002434 printf("run atoms optimized: %ld\n",
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002435 sched->nr_run_events_optimized);
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002436
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002437 print_task_traces(sched);
2438 add_cross_task_wakeups(sched);
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002439
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002440 create_tasks(sched);
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002441 printf("------------------------------------------------------------\n");
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002442 for (i = 0; i < sched->replay_repeat; i++)
2443 run_one_test(sched);
Arnaldo Carvalho de Meloa116e052012-09-08 22:53:06 -03002444
2445 return 0;
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002446}
2447
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002448static void setup_sorting(struct perf_sched *sched, const struct option *options,
2449 const char * const usage_msg[])
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02002450{
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002451 char *tmp, *tok, *str = strdup(sched->sort_order);
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02002452
2453 for (tok = strtok_r(str, ", ", &tmp);
2454 tok; tok = strtok_r(NULL, ", ", &tmp)) {
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002455 if (sort_dimension__add(tok, &sched->sort_list) < 0) {
Namhyung Kimc7118362015-10-25 00:49:27 +09002456 usage_with_options_msg(usage_msg, options,
2457 "Unknown --sort key: `%s'", tok);
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02002458 }
2459 }
2460
2461 free(str);
2462
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002463 sort_dimension__add("pid", &sched->cmp_pid);
Frederic Weisbeckerdaa1d7a2009-09-13 03:36:29 +02002464}
2465
Ingo Molnar1fc35b22009-09-13 09:44:29 +02002466static int __cmd_record(int argc, const char **argv)
2467{
2468 unsigned int rec_argc, i, j;
2469 const char **rec_argv;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002470 const char * const record_args[] = {
2471 "record",
2472 "-a",
2473 "-R",
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002474 "-m", "1024",
2475 "-c", "1",
2476 "-e", "sched:sched_switch",
2477 "-e", "sched:sched_stat_wait",
2478 "-e", "sched:sched_stat_sleep",
2479 "-e", "sched:sched_stat_iowait",
2480 "-e", "sched:sched_stat_runtime",
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002481 "-e", "sched:sched_process_fork",
2482 "-e", "sched:sched_wakeup",
Dongsheng7fff9592014-05-05 16:05:53 +09002483 "-e", "sched:sched_wakeup_new",
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002484 "-e", "sched:sched_migrate_task",
2485 };
Ingo Molnar1fc35b22009-09-13 09:44:29 +02002486
2487 rec_argc = ARRAY_SIZE(record_args) + argc - 1;
2488 rec_argv = calloc(rec_argc + 1, sizeof(char *));
2489
Arnaldo Carvalho de Meloe462dc52011-01-10 10:48:47 -02002490 if (rec_argv == NULL)
Chris Samuelce47dc52010-11-13 13:35:06 +11002491 return -ENOMEM;
2492
Ingo Molnar1fc35b22009-09-13 09:44:29 +02002493 for (i = 0; i < ARRAY_SIZE(record_args); i++)
2494 rec_argv[i] = strdup(record_args[i]);
2495
2496 for (j = 1; j < (unsigned int)argc; j++, i++)
2497 rec_argv[i] = argv[j];
2498
2499 BUG_ON(i != rec_argc);
2500
2501 return cmd_record(i, rec_argv, NULL);
2502}
2503
Irina Tirdea1d037ca2012-09-11 01:15:03 +03002504int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
Ingo Molnar0a02ad92009-09-11 12:12:54 +02002505{
Adrian Hunter8a39df82013-10-22 10:34:15 +03002506 const char default_sort_order[] = "avg, max, switch, runtime";
2507 struct perf_sched sched = {
2508 .tool = {
2509 .sample = perf_sched__process_tracepoint_sample,
2510 .comm = perf_event__process_comm,
2511 .lost = perf_event__process_lost,
2512 .fork = perf_sched__process_fork_event,
Jiri Olsa0a8cb852014-07-06 14:18:21 +02002513 .ordered_events = true,
Adrian Hunter8a39df82013-10-22 10:34:15 +03002514 },
2515 .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid),
2516 .sort_list = LIST_HEAD_INIT(sched.sort_list),
2517 .start_work_mutex = PTHREAD_MUTEX_INITIALIZER,
2518 .work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER,
Adrian Hunter8a39df82013-10-22 10:34:15 +03002519 .sort_order = default_sort_order,
2520 .replay_repeat = 10,
2521 .profile_cpu = -1,
2522 .next_shortname1 = 'A',
2523 .next_shortname2 = '0',
Josef Bacik2f80dd42015-05-22 09:18:40 -04002524 .skip_merge = 0,
Adrian Hunter8a39df82013-10-22 10:34:15 +03002525 };
Namhyung Kim77f02f42016-10-24 12:00:03 +09002526 const struct option sched_options[] = {
2527 OPT_STRING('i', "input", &input_name, "file",
2528 "input file name"),
2529 OPT_INCR('v', "verbose", &verbose,
2530 "be more verbose (show symbol address, etc)"),
2531 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
2532 "dump raw trace in ASCII"),
2533 OPT_END()
2534 };
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002535 const struct option latency_options[] = {
2536 OPT_STRING('s', "sort", &sched.sort_order, "key[,key2...]",
2537 "sort by key(s): runtime, switch, avg, max"),
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002538 OPT_INTEGER('C', "CPU", &sched.profile_cpu,
2539 "CPU to profile on"),
2540 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
2541 "dump raw trace in ASCII"),
Josef Bacik2f80dd42015-05-22 09:18:40 -04002542 OPT_BOOLEAN('p', "pids", &sched.skip_merge,
2543 "latency stats per pid instead of per comm"),
Namhyung Kim77f02f42016-10-24 12:00:03 +09002544 OPT_PARENT(sched_options)
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002545 };
2546 const struct option replay_options[] = {
2547 OPT_UINTEGER('r', "repeat", &sched.replay_repeat,
2548 "repeat the workload replay N times (-1: infinite)"),
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002549 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
2550 "dump raw trace in ASCII"),
Yunlong Song939cda52015-03-31 21:46:34 +08002551 OPT_BOOLEAN('f', "force", &sched.force, "don't complain, do it"),
Namhyung Kim77f02f42016-10-24 12:00:03 +09002552 OPT_PARENT(sched_options)
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002553 };
Jiri Olsa99623c62016-04-12 15:29:26 +02002554 const struct option map_options[] = {
2555 OPT_BOOLEAN(0, "compact", &sched.map.comp,
2556 "map output in compact mode"),
Jiri Olsaa151a372016-04-12 15:29:29 +02002557 OPT_STRING(0, "color-pids", &sched.map.color_pids_str, "pids",
2558 "highlight given pids in map"),
Jiri Olsacf294f22016-04-12 15:29:30 +02002559 OPT_STRING(0, "color-cpus", &sched.map.color_cpus_str, "cpus",
2560 "highlight given CPUs in map"),
Jiri Olsa73643bb2016-04-12 15:29:31 +02002561 OPT_STRING(0, "cpus", &sched.map.cpus_str, "cpus",
2562 "display given CPUs in map"),
Namhyung Kim77f02f42016-10-24 12:00:03 +09002563 OPT_PARENT(sched_options)
Jiri Olsa99623c62016-04-12 15:29:26 +02002564 };
David Ahern49394a22016-11-16 15:06:29 +09002565 const struct option timehist_options[] = {
2566 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
2567 "file", "vmlinux pathname"),
2568 OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
2569 "file", "kallsyms pathname"),
2570 OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
2571 "Look for files with symbols relative to this directory"),
2572 OPT_PARENT(sched_options)
2573 };
2574
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002575 const char * const latency_usage[] = {
2576 "perf sched latency [<options>]",
2577 NULL
2578 };
2579 const char * const replay_usage[] = {
2580 "perf sched replay [<options>]",
2581 NULL
2582 };
Jiri Olsa99623c62016-04-12 15:29:26 +02002583 const char * const map_usage[] = {
2584 "perf sched map [<options>]",
2585 NULL
2586 };
David Ahern49394a22016-11-16 15:06:29 +09002587 const char * const timehist_usage[] = {
2588 "perf sched timehist [<options>]",
2589 NULL
2590 };
Ramkumar Ramachandraa83edb22014-03-14 23:17:54 -04002591 const char *const sched_subcommands[] = { "record", "latency", "map",
David Ahern49394a22016-11-16 15:06:29 +09002592 "replay", "script",
2593 "timehist", NULL };
Ramkumar Ramachandraa83edb22014-03-14 23:17:54 -04002594 const char *sched_usage[] = {
2595 NULL,
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002596 NULL
2597 };
2598 struct trace_sched_handler lat_ops = {
2599 .wakeup_event = latency_wakeup_event,
2600 .switch_event = latency_switch_event,
2601 .runtime_event = latency_runtime_event,
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002602 .migrate_task_event = latency_migrate_task_event,
2603 };
2604 struct trace_sched_handler map_ops = {
2605 .switch_event = map_switch_event,
2606 };
2607 struct trace_sched_handler replay_ops = {
2608 .wakeup_event = replay_wakeup_event,
2609 .switch_event = replay_switch_event,
2610 .fork_event = replay_fork_event,
2611 };
Adrian Hunter156a2b02013-10-22 10:34:16 +03002612 unsigned int i;
2613
2614 for (i = 0; i < ARRAY_SIZE(sched.curr_pid); i++)
2615 sched.curr_pid[i] = -1;
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002616
Ramkumar Ramachandraa83edb22014-03-14 23:17:54 -04002617 argc = parse_options_subcommand(argc, argv, sched_options, sched_subcommands,
2618 sched_usage, PARSE_OPT_STOP_AT_NON_OPTION);
Ingo Molnarf2858d82009-09-11 12:12:54 +02002619 if (!argc)
2620 usage_with_options(sched_usage, sched_options);
2621
Xiao Guangrongc0777c52009-12-07 12:04:49 +08002622 /*
Ingo Molnar133dc4c2010-11-16 18:45:39 +01002623 * Aliased to 'perf script' for now:
Xiao Guangrongc0777c52009-12-07 12:04:49 +08002624 */
Ingo Molnar133dc4c2010-11-16 18:45:39 +01002625 if (!strcmp(argv[0], "script"))
2626 return cmd_script(argc, argv, prefix);
Xiao Guangrongc0777c52009-12-07 12:04:49 +08002627
Ingo Molnar1fc35b22009-09-13 09:44:29 +02002628 if (!strncmp(argv[0], "rec", 3)) {
2629 return __cmd_record(argc, argv);
2630 } else if (!strncmp(argv[0], "lat", 3)) {
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002631 sched.tp_handler = &lat_ops;
Ingo Molnarf2858d82009-09-11 12:12:54 +02002632 if (argc > 1) {
2633 argc = parse_options(argc, argv, latency_options, latency_usage, 0);
2634 if (argc)
2635 usage_with_options(latency_usage, latency_options);
Ingo Molnarf2858d82009-09-11 12:12:54 +02002636 }
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002637 setup_sorting(&sched, latency_options, latency_usage);
2638 return perf_sched__lat(&sched);
Ingo Molnar0ec04e12009-09-16 17:40:48 +02002639 } else if (!strcmp(argv[0], "map")) {
Jiri Olsa99623c62016-04-12 15:29:26 +02002640 if (argc) {
Jiri Olsaa151a372016-04-12 15:29:29 +02002641 argc = parse_options(argc, argv, map_options, map_usage, 0);
Jiri Olsa99623c62016-04-12 15:29:26 +02002642 if (argc)
2643 usage_with_options(map_usage, map_options);
2644 }
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002645 sched.tp_handler = &map_ops;
2646 setup_sorting(&sched, latency_options, latency_usage);
2647 return perf_sched__map(&sched);
Ingo Molnarf2858d82009-09-11 12:12:54 +02002648 } else if (!strncmp(argv[0], "rep", 3)) {
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002649 sched.tp_handler = &replay_ops;
Ingo Molnarf2858d82009-09-11 12:12:54 +02002650 if (argc) {
2651 argc = parse_options(argc, argv, replay_options, replay_usage, 0);
2652 if (argc)
2653 usage_with_options(replay_usage, replay_options);
2654 }
Arnaldo Carvalho de Melo0e9b07e2012-09-11 17:29:27 -03002655 return perf_sched__replay(&sched);
David Ahern49394a22016-11-16 15:06:29 +09002656 } else if (!strcmp(argv[0], "timehist")) {
2657 if (argc) {
2658 argc = parse_options(argc, argv, timehist_options,
2659 timehist_usage, 0);
2660 if (argc)
2661 usage_with_options(timehist_usage, timehist_options);
2662 }
2663 return perf_sched__timehist(&sched);
Ingo Molnarf2858d82009-09-11 12:12:54 +02002664 } else {
2665 usage_with_options(sched_usage, sched_options);
Ingo Molnar0a02ad92009-09-11 12:12:54 +02002666 }
2667
Ingo Molnarec156762009-09-11 12:12:54 +02002668 return 0;
Ingo Molnar0a02ad92009-09-11 12:12:54 +02002669}