xref: /openbmc/linux/tools/perf/builtin-sched.c (revision dfc66beff7fa95b9eb507ccb48fb325569bc2f74)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
20a02ad93SIngo Molnar #include "builtin.h"
3b1ffe8f3SIngo Molnar #include "perf.h"
491854f9aSArnaldo Carvalho de Melo #include "perf-sys.h"
50a02ad93SIngo Molnar 
687ffb6c6SArnaldo Carvalho de Melo #include "util/cpumap.h"
7ee29be62SArnaldo Carvalho de Melo #include "util/evlist.h"
8e3f42609SArnaldo Carvalho de Melo #include "util/evsel.h"
9ca125277SArnaldo Carvalho de Melo #include "util/evsel_fprintf.h"
100a02ad93SIngo Molnar #include "util/symbol.h"
110a02ad93SIngo Molnar #include "util/thread.h"
120a02ad93SIngo Molnar #include "util/header.h"
1394c744b6SArnaldo Carvalho de Melo #include "util/session.h"
1445694aa7SArnaldo Carvalho de Melo #include "util/tool.h"
1557480d2cSYann Droneaud #include "util/cloexec.h"
16a151a37aSJiri Olsa #include "util/thread_map.h"
178cd91195SJiri Olsa #include "util/color.h"
1849394a2aSDavid Ahern #include "util/stat.h"
196a9fa4e3SArnaldo Carvalho de Melo #include "util/string2.h"
206c973c90SDavid Ahern #include "util/callchain.h"
21853b7407SDavid Ahern #include "util/time-utils.h"
220a02ad93SIngo Molnar 
23fa0d9846SArnaldo Carvalho de Melo #include <subcmd/pager.h>
244b6ab94eSJosh Poimboeuf #include <subcmd/parse-options.h>
25b1ffe8f3SIngo Molnar #include "util/trace-event.h"
260a02ad93SIngo Molnar 
270a02ad93SIngo Molnar #include "util/debug.h"
28f12be047SArnaldo Carvalho de Melo #include "util/event.h"
290a02ad93SIngo Molnar 
30877a7a11SArnaldo Carvalho de Melo #include <linux/kernel.h>
3149394a2aSDavid Ahern #include <linux/log2.h>
327f7c536fSArnaldo Carvalho de Melo #include <linux/zalloc.h>
33b1ffe8f3SIngo Molnar #include <sys/prctl.h>
347b78f136SMarkus Trippelsdorf #include <sys/resource.h>
35fd20e811SArnaldo Carvalho de Melo #include <inttypes.h>
360a02ad93SIngo Molnar 
37a43783aeSArnaldo Carvalho de Melo #include <errno.h>
38b1ffe8f3SIngo Molnar #include <semaphore.h>
39b1ffe8f3SIngo Molnar #include <pthread.h>
40b1ffe8f3SIngo Molnar #include <math.h>
41cb06ac25SYunlong Song #include <api/fs/fs.h>
4287ffb6c6SArnaldo Carvalho de Melo #include <perf/cpumap.h>
434fc76e49SArnaldo Carvalho de Melo #include <linux/time64.h>
446ef81c55SMamatha Inamdar #include <linux/err.h>
45419ab0d6SFrederic Weisbecker 
463052ba56SArnaldo Carvalho de Melo #include <linux/ctype.h>
473d689ed6SArnaldo Carvalho de Melo 
48ec156764SIngo Molnar #define PR_SET_NAME		15               /* Set process name */
49b1ffe8f3SIngo Molnar #define MAX_CPUS		4096
50ec156764SIngo Molnar #define COMM_LEN		20
51ec156764SIngo Molnar #define SYM_LEN			129
52a35e27d0SYunlong Song #define MAX_PID			1024000
53ec156764SIngo Molnar 
54c30d630dSDavid Ahern static const char *cpu_list;
55c30d630dSDavid Ahern static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
56c30d630dSDavid Ahern 
5739aeb52fSmingo struct sched_atom;
58ec156764SIngo Molnar 
59ec156764SIngo Molnar struct task_desc {
60ec156764SIngo Molnar 	unsigned long		nr;
61ec156764SIngo Molnar 	unsigned long		pid;
62ec156764SIngo Molnar 	char			comm[COMM_LEN];
63ec156764SIngo Molnar 
64ec156764SIngo Molnar 	unsigned long		nr_events;
65ec156764SIngo Molnar 	unsigned long		curr_event;
6639aeb52fSmingo 	struct sched_atom	**atoms;
67ec156764SIngo Molnar 
68ec156764SIngo Molnar 	pthread_t		thread;
69ec156764SIngo Molnar 	sem_t			sleep_sem;
70ec156764SIngo Molnar 
71ec156764SIngo Molnar 	sem_t			ready_for_work;
72ec156764SIngo Molnar 	sem_t			work_done_sem;
73ec156764SIngo Molnar 
74b1ffe8f3SIngo Molnar 	u64			cpu_usage;
75ec156764SIngo Molnar };
76ec156764SIngo Molnar 
77ec156764SIngo Molnar enum sched_event_type {
78ec156764SIngo Molnar 	SCHED_EVENT_RUN,
79ec156764SIngo Molnar 	SCHED_EVENT_SLEEP,
80ec156764SIngo Molnar 	SCHED_EVENT_WAKEUP,
8155ffb7a6SMike Galbraith 	SCHED_EVENT_MIGRATION,
82ec156764SIngo Molnar };
83ec156764SIngo Molnar 
8439aeb52fSmingo struct sched_atom {
85ec156764SIngo Molnar 	enum sched_event_type	type;
86eed05fe7SArnaldo Carvalho de Melo 	int			specific_wait;
87b1ffe8f3SIngo Molnar 	u64			timestamp;
88b1ffe8f3SIngo Molnar 	u64			duration;
89ec156764SIngo Molnar 	unsigned long		nr;
90ec156764SIngo Molnar 	sem_t			*wait_sem;
91ec156764SIngo Molnar 	struct task_desc	*wakee;
92ec156764SIngo Molnar };
93ec156764SIngo Molnar 
94e936e8e4SDongsheng #define TASK_STATE_TO_CHAR_STR "RSDTtZXxKWP"
95b1ffe8f3SIngo Molnar 
96941bdea7SNamhyung Kim /* task state bitmask, copied from include/linux/sched.h */
97941bdea7SNamhyung Kim #define TASK_RUNNING		0
98941bdea7SNamhyung Kim #define TASK_INTERRUPTIBLE	1
99941bdea7SNamhyung Kim #define TASK_UNINTERRUPTIBLE	2
100941bdea7SNamhyung Kim #define __TASK_STOPPED		4
101941bdea7SNamhyung Kim #define __TASK_TRACED		8
102941bdea7SNamhyung Kim /* in tsk->exit_state */
103941bdea7SNamhyung Kim #define EXIT_DEAD		16
104941bdea7SNamhyung Kim #define EXIT_ZOMBIE		32
105941bdea7SNamhyung Kim #define EXIT_TRACE		(EXIT_ZOMBIE | EXIT_DEAD)
106941bdea7SNamhyung Kim /* in tsk->state again */
107941bdea7SNamhyung Kim #define TASK_DEAD		64
108941bdea7SNamhyung Kim #define TASK_WAKEKILL		128
109941bdea7SNamhyung Kim #define TASK_WAKING		256
110941bdea7SNamhyung Kim #define TASK_PARKED		512
111941bdea7SNamhyung Kim 
112b1ffe8f3SIngo Molnar enum thread_state {
113b1ffe8f3SIngo Molnar 	THREAD_SLEEPING = 0,
114b1ffe8f3SIngo Molnar 	THREAD_WAIT_CPU,
115b1ffe8f3SIngo Molnar 	THREAD_SCHED_IN,
116b1ffe8f3SIngo Molnar 	THREAD_IGNORE
117b1ffe8f3SIngo Molnar };
118b1ffe8f3SIngo Molnar 
119b1ffe8f3SIngo Molnar struct work_atom {
120b1ffe8f3SIngo Molnar 	struct list_head	list;
121b1ffe8f3SIngo Molnar 	enum thread_state	state;
122aa1ab9d2SFrederic Weisbecker 	u64			sched_out_time;
123b1ffe8f3SIngo Molnar 	u64			wake_up_time;
124b1ffe8f3SIngo Molnar 	u64			sched_in_time;
125b1ffe8f3SIngo Molnar 	u64			runtime;
126b1ffe8f3SIngo Molnar };
127b1ffe8f3SIngo Molnar 
12839aeb52fSmingo struct work_atoms {
12939aeb52fSmingo 	struct list_head	work_list;
130b1ffe8f3SIngo Molnar 	struct thread		*thread;
131b1ffe8f3SIngo Molnar 	struct rb_node		node;
132b1ffe8f3SIngo Molnar 	u64			max_lat;
133dc000c45SJoel Fernandes (Google) 	u64			max_lat_start;
134dc000c45SJoel Fernandes (Google) 	u64			max_lat_end;
135b1ffe8f3SIngo Molnar 	u64			total_lat;
136b1ffe8f3SIngo Molnar 	u64			nb_atoms;
137b1ffe8f3SIngo Molnar 	u64			total_runtime;
1382f80dd44SJosef Bacik 	int			num_merged;
139b1ffe8f3SIngo Molnar };
140b1ffe8f3SIngo Molnar 
14139aeb52fSmingo typedef int (*sort_fn_t)(struct work_atoms *, struct work_atoms *);
142b1ffe8f3SIngo Molnar 
1430e9b07e5SArnaldo Carvalho de Melo struct perf_sched;
1440e9b07e5SArnaldo Carvalho de Melo 
145419ab0d6SFrederic Weisbecker struct trace_sched_handler {
14632dcd021SJiri Olsa 	int (*switch_event)(struct perf_sched *sched, struct evsel *evsel,
1479ec3f4e4SArnaldo Carvalho de Melo 			    struct perf_sample *sample, struct machine *machine);
148419ab0d6SFrederic Weisbecker 
14932dcd021SJiri Olsa 	int (*runtime_event)(struct perf_sched *sched, struct evsel *evsel,
1509ec3f4e4SArnaldo Carvalho de Melo 			     struct perf_sample *sample, struct machine *machine);
15139aeb52fSmingo 
15232dcd021SJiri Olsa 	int (*wakeup_event)(struct perf_sched *sched, struct evsel *evsel,
1539ec3f4e4SArnaldo Carvalho de Melo 			    struct perf_sample *sample, struct machine *machine);
154419ab0d6SFrederic Weisbecker 
155cb627505SDavid Ahern 	/* PERF_RECORD_FORK event, not sched_process_fork tracepoint */
156cb627505SDavid Ahern 	int (*fork_event)(struct perf_sched *sched, union perf_event *event,
157cb627505SDavid Ahern 			  struct machine *machine);
15855ffb7a6SMike Galbraith 
1590e9b07e5SArnaldo Carvalho de Melo 	int (*migrate_task_event)(struct perf_sched *sched,
16032dcd021SJiri Olsa 				  struct evsel *evsel,
1619ec3f4e4SArnaldo Carvalho de Melo 				  struct perf_sample *sample,
1629ec3f4e4SArnaldo Carvalho de Melo 				  struct machine *machine);
163419ab0d6SFrederic Weisbecker };
164419ab0d6SFrederic Weisbecker 
165a151a37aSJiri Olsa #define COLOR_PIDS PERF_COLOR_BLUE
166cf294f24SJiri Olsa #define COLOR_CPUS PERF_COLOR_BG_RED
167a151a37aSJiri Olsa 
16899623c62SJiri Olsa struct perf_sched_map {
16999623c62SJiri Olsa 	DECLARE_BITMAP(comp_cpus_mask, MAX_CPUS);
17099623c62SJiri Olsa 	int			*comp_cpus;
17199623c62SJiri Olsa 	bool			 comp;
1729749b90eSJiri Olsa 	struct perf_thread_map *color_pids;
173a151a37aSJiri Olsa 	const char		*color_pids_str;
174f854839bSJiri Olsa 	struct perf_cpu_map	*color_cpus;
175cf294f24SJiri Olsa 	const char		*color_cpus_str;
176f854839bSJiri Olsa 	struct perf_cpu_map	*cpus;
17773643bb6SJiri Olsa 	const char		*cpus_str;
17899623c62SJiri Olsa };
17999623c62SJiri Olsa 
1800e9b07e5SArnaldo Carvalho de Melo struct perf_sched {
1810e9b07e5SArnaldo Carvalho de Melo 	struct perf_tool tool;
1820e9b07e5SArnaldo Carvalho de Melo 	const char	 *sort_order;
1830e9b07e5SArnaldo Carvalho de Melo 	unsigned long	 nr_tasks;
184cb06ac25SYunlong Song 	struct task_desc **pid_to_task;
1850e9b07e5SArnaldo Carvalho de Melo 	struct task_desc **tasks;
1860e9b07e5SArnaldo Carvalho de Melo 	const struct trace_sched_handler *tp_handler;
1870e9b07e5SArnaldo Carvalho de Melo 	pthread_mutex_t	 start_work_mutex;
1880e9b07e5SArnaldo Carvalho de Melo 	pthread_mutex_t	 work_done_wait_mutex;
1890e9b07e5SArnaldo Carvalho de Melo 	int		 profile_cpu;
1900e9b07e5SArnaldo Carvalho de Melo /*
1910e9b07e5SArnaldo Carvalho de Melo  * Track the current task - that way we can know whether there's any
1920e9b07e5SArnaldo Carvalho de Melo  * weird events, such as a task being switched away that is not current.
1930e9b07e5SArnaldo Carvalho de Melo  */
1940e9b07e5SArnaldo Carvalho de Melo 	int		 max_cpu;
1950e9b07e5SArnaldo Carvalho de Melo 	u32		 curr_pid[MAX_CPUS];
1960e9b07e5SArnaldo Carvalho de Melo 	struct thread	 *curr_thread[MAX_CPUS];
1970e9b07e5SArnaldo Carvalho de Melo 	char		 next_shortname1;
1980e9b07e5SArnaldo Carvalho de Melo 	char		 next_shortname2;
1990e9b07e5SArnaldo Carvalho de Melo 	unsigned int	 replay_repeat;
2000e9b07e5SArnaldo Carvalho de Melo 	unsigned long	 nr_run_events;
2010e9b07e5SArnaldo Carvalho de Melo 	unsigned long	 nr_sleep_events;
2020e9b07e5SArnaldo Carvalho de Melo 	unsigned long	 nr_wakeup_events;
2030e9b07e5SArnaldo Carvalho de Melo 	unsigned long	 nr_sleep_corrections;
2040e9b07e5SArnaldo Carvalho de Melo 	unsigned long	 nr_run_events_optimized;
2050e9b07e5SArnaldo Carvalho de Melo 	unsigned long	 targetless_wakeups;
2060e9b07e5SArnaldo Carvalho de Melo 	unsigned long	 multitarget_wakeups;
2070e9b07e5SArnaldo Carvalho de Melo 	unsigned long	 nr_runs;
2080e9b07e5SArnaldo Carvalho de Melo 	unsigned long	 nr_timestamps;
2090e9b07e5SArnaldo Carvalho de Melo 	unsigned long	 nr_unordered_timestamps;
2100e9b07e5SArnaldo Carvalho de Melo 	unsigned long	 nr_context_switch_bugs;
2110e9b07e5SArnaldo Carvalho de Melo 	unsigned long	 nr_events;
2120e9b07e5SArnaldo Carvalho de Melo 	unsigned long	 nr_lost_chunks;
2130e9b07e5SArnaldo Carvalho de Melo 	unsigned long	 nr_lost_events;
2140e9b07e5SArnaldo Carvalho de Melo 	u64		 run_measurement_overhead;
2150e9b07e5SArnaldo Carvalho de Melo 	u64		 sleep_measurement_overhead;
2160e9b07e5SArnaldo Carvalho de Melo 	u64		 start_time;
2170e9b07e5SArnaldo Carvalho de Melo 	u64		 cpu_usage;
2180e9b07e5SArnaldo Carvalho de Melo 	u64		 runavg_cpu_usage;
2190e9b07e5SArnaldo Carvalho de Melo 	u64		 parent_cpu_usage;
2200e9b07e5SArnaldo Carvalho de Melo 	u64		 runavg_parent_cpu_usage;
2210e9b07e5SArnaldo Carvalho de Melo 	u64		 sum_runtime;
2220e9b07e5SArnaldo Carvalho de Melo 	u64		 sum_fluct;
2230e9b07e5SArnaldo Carvalho de Melo 	u64		 run_avg;
2240e9b07e5SArnaldo Carvalho de Melo 	u64		 all_runtime;
2250e9b07e5SArnaldo Carvalho de Melo 	u64		 all_count;
2260e9b07e5SArnaldo Carvalho de Melo 	u64		 cpu_last_switched[MAX_CPUS];
227cb4c13a5SDavidlohr Bueso 	struct rb_root_cached atom_root, sorted_atom_root, merged_atom_root;
2280e9b07e5SArnaldo Carvalho de Melo 	struct list_head sort_list, cmp_pid;
229939cda52SYunlong Song 	bool force;
2302f80dd44SJosef Bacik 	bool skip_merge;
23199623c62SJiri Olsa 	struct perf_sched_map map;
23252df138cSDavid Ahern 
23352df138cSDavid Ahern 	/* options for timehist command */
23452df138cSDavid Ahern 	bool		summary;
23552df138cSDavid Ahern 	bool		summary_only;
236699b5b92SNamhyung Kim 	bool		idle_hist;
2376c973c90SDavid Ahern 	bool		show_callchain;
2386c973c90SDavid Ahern 	unsigned int	max_stack;
239a407b067SDavid Ahern 	bool		show_cpu_visual;
240fc1469f1SDavid Ahern 	bool		show_wakeups;
241292c4a8fSBrendan Gregg 	bool		show_next;
242350f54faSDavid Ahern 	bool		show_migrations;
243414e050cSNamhyung Kim 	bool		show_state;
24452df138cSDavid Ahern 	u64		skipped_samples;
245853b7407SDavid Ahern 	const char	*time_str;
246853b7407SDavid Ahern 	struct perf_time_interval ptime;
2479396c9cbSNamhyung Kim 	struct perf_time_interval hist_time;
2480e9b07e5SArnaldo Carvalho de Melo };
2490e9b07e5SArnaldo Carvalho de Melo 
25049394a2aSDavid Ahern /* per thread run time data */
25149394a2aSDavid Ahern struct thread_runtime {
25249394a2aSDavid Ahern 	u64 last_time;      /* time of previous sched in/out event */
25349394a2aSDavid Ahern 	u64 dt_run;         /* run time */
254941bdea7SNamhyung Kim 	u64 dt_sleep;       /* time between CPU access by sleep (off cpu) */
255941bdea7SNamhyung Kim 	u64 dt_iowait;      /* time between CPU access by iowait (off cpu) */
256941bdea7SNamhyung Kim 	u64 dt_preempt;     /* time between CPU access by preempt (off cpu) */
25749394a2aSDavid Ahern 	u64 dt_delay;       /* time between wakeup and sched-in */
25849394a2aSDavid Ahern 	u64 ready_to_run;   /* time of wakeup */
25949394a2aSDavid Ahern 
26049394a2aSDavid Ahern 	struct stats run_stats;
26149394a2aSDavid Ahern 	u64 total_run_time;
262587782c5SNamhyung Kim 	u64 total_sleep_time;
263587782c5SNamhyung Kim 	u64 total_iowait_time;
264587782c5SNamhyung Kim 	u64 total_preempt_time;
265587782c5SNamhyung Kim 	u64 total_delay_time;
266350f54faSDavid Ahern 
267941bdea7SNamhyung Kim 	int last_state;
2688640da9fSChangbin Du 
2698640da9fSChangbin Du 	char shortname[3];
27099a3c3a9SChangbin Du 	bool comm_changed;
27199a3c3a9SChangbin Du 
272350f54faSDavid Ahern 	u64 migrations;
27349394a2aSDavid Ahern };
27449394a2aSDavid Ahern 
27549394a2aSDavid Ahern /* per event run time data */
27649394a2aSDavid Ahern struct evsel_runtime {
27749394a2aSDavid Ahern 	u64 *last_time; /* time this event was last seen per cpu */
27849394a2aSDavid Ahern 	u32 ncpu;       /* highest cpu slot allocated */
27949394a2aSDavid Ahern };
28049394a2aSDavid Ahern 
2813bc2fa9cSNamhyung Kim /* per cpu idle time data */
2823bc2fa9cSNamhyung Kim struct idle_thread_runtime {
2833bc2fa9cSNamhyung Kim 	struct thread_runtime	tr;
2843bc2fa9cSNamhyung Kim 	struct thread		*last_thread;
285cb4c13a5SDavidlohr Bueso 	struct rb_root_cached	sorted_root;
2863bc2fa9cSNamhyung Kim 	struct callchain_root	callchain;
2873bc2fa9cSNamhyung Kim 	struct callchain_cursor	cursor;
2883bc2fa9cSNamhyung Kim };
2893bc2fa9cSNamhyung Kim 
29049394a2aSDavid Ahern /* track idle times per cpu */
29149394a2aSDavid Ahern static struct thread **idle_threads;
29249394a2aSDavid Ahern static int idle_max_cpu;
29349394a2aSDavid Ahern static char idle_comm[] = "<idle>";
29449394a2aSDavid Ahern 
2950e9b07e5SArnaldo Carvalho de Melo static u64 get_nsecs(void)
2960e9b07e5SArnaldo Carvalho de Melo {
2970e9b07e5SArnaldo Carvalho de Melo 	struct timespec ts;
2980e9b07e5SArnaldo Carvalho de Melo 
2990e9b07e5SArnaldo Carvalho de Melo 	clock_gettime(CLOCK_MONOTONIC, &ts);
3000e9b07e5SArnaldo Carvalho de Melo 
3014fc76e49SArnaldo Carvalho de Melo 	return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
3020e9b07e5SArnaldo Carvalho de Melo }
3030e9b07e5SArnaldo Carvalho de Melo 
3040e9b07e5SArnaldo Carvalho de Melo static void burn_nsecs(struct perf_sched *sched, u64 nsecs)
3050e9b07e5SArnaldo Carvalho de Melo {
3060e9b07e5SArnaldo Carvalho de Melo 	u64 T0 = get_nsecs(), T1;
3070e9b07e5SArnaldo Carvalho de Melo 
3080e9b07e5SArnaldo Carvalho de Melo 	do {
3090e9b07e5SArnaldo Carvalho de Melo 		T1 = get_nsecs();
3100e9b07e5SArnaldo Carvalho de Melo 	} while (T1 + sched->run_measurement_overhead < T0 + nsecs);
3110e9b07e5SArnaldo Carvalho de Melo }
3120e9b07e5SArnaldo Carvalho de Melo 
3130e9b07e5SArnaldo Carvalho de Melo static void sleep_nsecs(u64 nsecs)
3140e9b07e5SArnaldo Carvalho de Melo {
3150e9b07e5SArnaldo Carvalho de Melo 	struct timespec ts;
3160e9b07e5SArnaldo Carvalho de Melo 
3170e9b07e5SArnaldo Carvalho de Melo 	ts.tv_nsec = nsecs % 999999999;
3180e9b07e5SArnaldo Carvalho de Melo 	ts.tv_sec = nsecs / 999999999;
3190e9b07e5SArnaldo Carvalho de Melo 
3200e9b07e5SArnaldo Carvalho de Melo 	nanosleep(&ts, NULL);
3210e9b07e5SArnaldo Carvalho de Melo }
3220e9b07e5SArnaldo Carvalho de Melo 
3230e9b07e5SArnaldo Carvalho de Melo static void calibrate_run_measurement_overhead(struct perf_sched *sched)
3240e9b07e5SArnaldo Carvalho de Melo {
3254fc76e49SArnaldo Carvalho de Melo 	u64 T0, T1, delta, min_delta = NSEC_PER_SEC;
3260e9b07e5SArnaldo Carvalho de Melo 	int i;
3270e9b07e5SArnaldo Carvalho de Melo 
3280e9b07e5SArnaldo Carvalho de Melo 	for (i = 0; i < 10; i++) {
3290e9b07e5SArnaldo Carvalho de Melo 		T0 = get_nsecs();
3300e9b07e5SArnaldo Carvalho de Melo 		burn_nsecs(sched, 0);
3310e9b07e5SArnaldo Carvalho de Melo 		T1 = get_nsecs();
3320e9b07e5SArnaldo Carvalho de Melo 		delta = T1-T0;
3330e9b07e5SArnaldo Carvalho de Melo 		min_delta = min(min_delta, delta);
3340e9b07e5SArnaldo Carvalho de Melo 	}
3350e9b07e5SArnaldo Carvalho de Melo 	sched->run_measurement_overhead = min_delta;
3360e9b07e5SArnaldo Carvalho de Melo 
3370e9b07e5SArnaldo Carvalho de Melo 	printf("run measurement overhead: %" PRIu64 " nsecs\n", min_delta);
3380e9b07e5SArnaldo Carvalho de Melo }
3390e9b07e5SArnaldo Carvalho de Melo 
3400e9b07e5SArnaldo Carvalho de Melo static void calibrate_sleep_measurement_overhead(struct perf_sched *sched)
3410e9b07e5SArnaldo Carvalho de Melo {
3424fc76e49SArnaldo Carvalho de Melo 	u64 T0, T1, delta, min_delta = NSEC_PER_SEC;
3430e9b07e5SArnaldo Carvalho de Melo 	int i;
3440e9b07e5SArnaldo Carvalho de Melo 
3450e9b07e5SArnaldo Carvalho de Melo 	for (i = 0; i < 10; i++) {
3460e9b07e5SArnaldo Carvalho de Melo 		T0 = get_nsecs();
3470e9b07e5SArnaldo Carvalho de Melo 		sleep_nsecs(10000);
3480e9b07e5SArnaldo Carvalho de Melo 		T1 = get_nsecs();
3490e9b07e5SArnaldo Carvalho de Melo 		delta = T1-T0;
3500e9b07e5SArnaldo Carvalho de Melo 		min_delta = min(min_delta, delta);
3510e9b07e5SArnaldo Carvalho de Melo 	}
3520e9b07e5SArnaldo Carvalho de Melo 	min_delta -= 10000;
3530e9b07e5SArnaldo Carvalho de Melo 	sched->sleep_measurement_overhead = min_delta;
3540e9b07e5SArnaldo Carvalho de Melo 
3550e9b07e5SArnaldo Carvalho de Melo 	printf("sleep measurement overhead: %" PRIu64 " nsecs\n", min_delta);
3560e9b07e5SArnaldo Carvalho de Melo }
3570e9b07e5SArnaldo Carvalho de Melo 
3580e9b07e5SArnaldo Carvalho de Melo static struct sched_atom *
3590e9b07e5SArnaldo Carvalho de Melo get_new_event(struct task_desc *task, u64 timestamp)
3600e9b07e5SArnaldo Carvalho de Melo {
3610e9b07e5SArnaldo Carvalho de Melo 	struct sched_atom *event = zalloc(sizeof(*event));
3620e9b07e5SArnaldo Carvalho de Melo 	unsigned long idx = task->nr_events;
3630e9b07e5SArnaldo Carvalho de Melo 	size_t size;
3640e9b07e5SArnaldo Carvalho de Melo 
3650e9b07e5SArnaldo Carvalho de Melo 	event->timestamp = timestamp;
3660e9b07e5SArnaldo Carvalho de Melo 	event->nr = idx;
3670e9b07e5SArnaldo Carvalho de Melo 
3680e9b07e5SArnaldo Carvalho de Melo 	task->nr_events++;
3690e9b07e5SArnaldo Carvalho de Melo 	size = sizeof(struct sched_atom *) * task->nr_events;
3700e9b07e5SArnaldo Carvalho de Melo 	task->atoms = realloc(task->atoms, size);
3710e9b07e5SArnaldo Carvalho de Melo 	BUG_ON(!task->atoms);
3720e9b07e5SArnaldo Carvalho de Melo 
3730e9b07e5SArnaldo Carvalho de Melo 	task->atoms[idx] = event;
3740e9b07e5SArnaldo Carvalho de Melo 
3750e9b07e5SArnaldo Carvalho de Melo 	return event;
3760e9b07e5SArnaldo Carvalho de Melo }
3770e9b07e5SArnaldo Carvalho de Melo 
3780e9b07e5SArnaldo Carvalho de Melo static struct sched_atom *last_event(struct task_desc *task)
3790e9b07e5SArnaldo Carvalho de Melo {
3800e9b07e5SArnaldo Carvalho de Melo 	if (!task->nr_events)
3810e9b07e5SArnaldo Carvalho de Melo 		return NULL;
3820e9b07e5SArnaldo Carvalho de Melo 
3830e9b07e5SArnaldo Carvalho de Melo 	return task->atoms[task->nr_events - 1];
3840e9b07e5SArnaldo Carvalho de Melo }
3850e9b07e5SArnaldo Carvalho de Melo 
3860e9b07e5SArnaldo Carvalho de Melo static void add_sched_event_run(struct perf_sched *sched, struct task_desc *task,
3870e9b07e5SArnaldo Carvalho de Melo 				u64 timestamp, u64 duration)
3880e9b07e5SArnaldo Carvalho de Melo {
3890e9b07e5SArnaldo Carvalho de Melo 	struct sched_atom *event, *curr_event = last_event(task);
3900e9b07e5SArnaldo Carvalho de Melo 
3910e9b07e5SArnaldo Carvalho de Melo 	/*
3920e9b07e5SArnaldo Carvalho de Melo 	 * optimize an existing RUN event by merging this one
3930e9b07e5SArnaldo Carvalho de Melo 	 * to it:
3940e9b07e5SArnaldo Carvalho de Melo 	 */
3950e9b07e5SArnaldo Carvalho de Melo 	if (curr_event && curr_event->type == SCHED_EVENT_RUN) {
3960e9b07e5SArnaldo Carvalho de Melo 		sched->nr_run_events_optimized++;
3970e9b07e5SArnaldo Carvalho de Melo 		curr_event->duration += duration;
3980e9b07e5SArnaldo Carvalho de Melo 		return;
3990e9b07e5SArnaldo Carvalho de Melo 	}
4000e9b07e5SArnaldo Carvalho de Melo 
4010e9b07e5SArnaldo Carvalho de Melo 	event = get_new_event(task, timestamp);
4020e9b07e5SArnaldo Carvalho de Melo 
4030e9b07e5SArnaldo Carvalho de Melo 	event->type = SCHED_EVENT_RUN;
4040e9b07e5SArnaldo Carvalho de Melo 	event->duration = duration;
4050e9b07e5SArnaldo Carvalho de Melo 
4060e9b07e5SArnaldo Carvalho de Melo 	sched->nr_run_events++;
4070e9b07e5SArnaldo Carvalho de Melo }
4080e9b07e5SArnaldo Carvalho de Melo 
4090e9b07e5SArnaldo Carvalho de Melo static void add_sched_event_wakeup(struct perf_sched *sched, struct task_desc *task,
4100e9b07e5SArnaldo Carvalho de Melo 				   u64 timestamp, struct task_desc *wakee)
4110e9b07e5SArnaldo Carvalho de Melo {
4120e9b07e5SArnaldo Carvalho de Melo 	struct sched_atom *event, *wakee_event;
4130e9b07e5SArnaldo Carvalho de Melo 
4140e9b07e5SArnaldo Carvalho de Melo 	event = get_new_event(task, timestamp);
4150e9b07e5SArnaldo Carvalho de Melo 	event->type = SCHED_EVENT_WAKEUP;
4160e9b07e5SArnaldo Carvalho de Melo 	event->wakee = wakee;
4170e9b07e5SArnaldo Carvalho de Melo 
4180e9b07e5SArnaldo Carvalho de Melo 	wakee_event = last_event(wakee);
4190e9b07e5SArnaldo Carvalho de Melo 	if (!wakee_event || wakee_event->type != SCHED_EVENT_SLEEP) {
4200e9b07e5SArnaldo Carvalho de Melo 		sched->targetless_wakeups++;
4210e9b07e5SArnaldo Carvalho de Melo 		return;
4220e9b07e5SArnaldo Carvalho de Melo 	}
4230e9b07e5SArnaldo Carvalho de Melo 	if (wakee_event->wait_sem) {
4240e9b07e5SArnaldo Carvalho de Melo 		sched->multitarget_wakeups++;
4250e9b07e5SArnaldo Carvalho de Melo 		return;
4260e9b07e5SArnaldo Carvalho de Melo 	}
4270e9b07e5SArnaldo Carvalho de Melo 
4280e9b07e5SArnaldo Carvalho de Melo 	wakee_event->wait_sem = zalloc(sizeof(*wakee_event->wait_sem));
4290e9b07e5SArnaldo Carvalho de Melo 	sem_init(wakee_event->wait_sem, 0, 0);
4300e9b07e5SArnaldo Carvalho de Melo 	wakee_event->specific_wait = 1;
4310e9b07e5SArnaldo Carvalho de Melo 	event->wait_sem = wakee_event->wait_sem;
4320e9b07e5SArnaldo Carvalho de Melo 
4330e9b07e5SArnaldo Carvalho de Melo 	sched->nr_wakeup_events++;
4340e9b07e5SArnaldo Carvalho de Melo }
4350e9b07e5SArnaldo Carvalho de Melo 
4360e9b07e5SArnaldo Carvalho de Melo static void add_sched_event_sleep(struct perf_sched *sched, struct task_desc *task,
4370e9b07e5SArnaldo Carvalho de Melo 				  u64 timestamp, u64 task_state __maybe_unused)
4380e9b07e5SArnaldo Carvalho de Melo {
4390e9b07e5SArnaldo Carvalho de Melo 	struct sched_atom *event = get_new_event(task, timestamp);
4400e9b07e5SArnaldo Carvalho de Melo 
4410e9b07e5SArnaldo Carvalho de Melo 	event->type = SCHED_EVENT_SLEEP;
4420e9b07e5SArnaldo Carvalho de Melo 
4430e9b07e5SArnaldo Carvalho de Melo 	sched->nr_sleep_events++;
4440e9b07e5SArnaldo Carvalho de Melo }
4450e9b07e5SArnaldo Carvalho de Melo 
4460e9b07e5SArnaldo Carvalho de Melo static struct task_desc *register_pid(struct perf_sched *sched,
4470e9b07e5SArnaldo Carvalho de Melo 				      unsigned long pid, const char *comm)
4480e9b07e5SArnaldo Carvalho de Melo {
4490e9b07e5SArnaldo Carvalho de Melo 	struct task_desc *task;
450cb06ac25SYunlong Song 	static int pid_max;
4510e9b07e5SArnaldo Carvalho de Melo 
452cb06ac25SYunlong Song 	if (sched->pid_to_task == NULL) {
453cb06ac25SYunlong Song 		if (sysctl__read_int("kernel/pid_max", &pid_max) < 0)
454cb06ac25SYunlong Song 			pid_max = MAX_PID;
455cb06ac25SYunlong Song 		BUG_ON((sched->pid_to_task = calloc(pid_max, sizeof(struct task_desc *))) == NULL);
456cb06ac25SYunlong Song 	}
4573a423a5cSYunlong Song 	if (pid >= (unsigned long)pid_max) {
4583a423a5cSYunlong Song 		BUG_ON((sched->pid_to_task = realloc(sched->pid_to_task, (pid + 1) *
4593a423a5cSYunlong Song 			sizeof(struct task_desc *))) == NULL);
4603a423a5cSYunlong Song 		while (pid >= (unsigned long)pid_max)
4613a423a5cSYunlong Song 			sched->pid_to_task[pid_max++] = NULL;
4623a423a5cSYunlong Song 	}
4630e9b07e5SArnaldo Carvalho de Melo 
4640e9b07e5SArnaldo Carvalho de Melo 	task = sched->pid_to_task[pid];
4650e9b07e5SArnaldo Carvalho de Melo 
4660e9b07e5SArnaldo Carvalho de Melo 	if (task)
4670e9b07e5SArnaldo Carvalho de Melo 		return task;
4680e9b07e5SArnaldo Carvalho de Melo 
4690e9b07e5SArnaldo Carvalho de Melo 	task = zalloc(sizeof(*task));
4700e9b07e5SArnaldo Carvalho de Melo 	task->pid = pid;
4710e9b07e5SArnaldo Carvalho de Melo 	task->nr = sched->nr_tasks;
4720e9b07e5SArnaldo Carvalho de Melo 	strcpy(task->comm, comm);
4730e9b07e5SArnaldo Carvalho de Melo 	/*
4740e9b07e5SArnaldo Carvalho de Melo 	 * every task starts in sleeping state - this gets ignored
4750e9b07e5SArnaldo Carvalho de Melo 	 * if there's no wakeup pointing to this sleep state:
4760e9b07e5SArnaldo Carvalho de Melo 	 */
4770e9b07e5SArnaldo Carvalho de Melo 	add_sched_event_sleep(sched, task, 0, 0);
4780e9b07e5SArnaldo Carvalho de Melo 
4790e9b07e5SArnaldo Carvalho de Melo 	sched->pid_to_task[pid] = task;
4800e9b07e5SArnaldo Carvalho de Melo 	sched->nr_tasks++;
4810755bc4dSYunlong Song 	sched->tasks = realloc(sched->tasks, sched->nr_tasks * sizeof(struct task_desc *));
4820e9b07e5SArnaldo Carvalho de Melo 	BUG_ON(!sched->tasks);
4830e9b07e5SArnaldo Carvalho de Melo 	sched->tasks[task->nr] = task;
4840e9b07e5SArnaldo Carvalho de Melo 
485bb963e16SNamhyung Kim 	if (verbose > 0)
4860e9b07e5SArnaldo Carvalho de Melo 		printf("registered task #%ld, PID %ld (%s)\n", sched->nr_tasks, pid, comm);
4870e9b07e5SArnaldo Carvalho de Melo 
4880e9b07e5SArnaldo Carvalho de Melo 	return task;
4890e9b07e5SArnaldo Carvalho de Melo }
4900e9b07e5SArnaldo Carvalho de Melo 
4910e9b07e5SArnaldo Carvalho de Melo 
4920e9b07e5SArnaldo Carvalho de Melo static void print_task_traces(struct perf_sched *sched)
4930e9b07e5SArnaldo Carvalho de Melo {
4940e9b07e5SArnaldo Carvalho de Melo 	struct task_desc *task;
4950e9b07e5SArnaldo Carvalho de Melo 	unsigned long i;
4960e9b07e5SArnaldo Carvalho de Melo 
4970e9b07e5SArnaldo Carvalho de Melo 	for (i = 0; i < sched->nr_tasks; i++) {
4980e9b07e5SArnaldo Carvalho de Melo 		task = sched->tasks[i];
4990e9b07e5SArnaldo Carvalho de Melo 		printf("task %6ld (%20s:%10ld), nr_events: %ld\n",
5000e9b07e5SArnaldo Carvalho de Melo 			task->nr, task->comm, task->pid, task->nr_events);
5010e9b07e5SArnaldo Carvalho de Melo 	}
5020e9b07e5SArnaldo Carvalho de Melo }
5030e9b07e5SArnaldo Carvalho de Melo 
5040e9b07e5SArnaldo Carvalho de Melo static void add_cross_task_wakeups(struct perf_sched *sched)
5050e9b07e5SArnaldo Carvalho de Melo {
5060e9b07e5SArnaldo Carvalho de Melo 	struct task_desc *task1, *task2;
5070e9b07e5SArnaldo Carvalho de Melo 	unsigned long i, j;
5080e9b07e5SArnaldo Carvalho de Melo 
5090e9b07e5SArnaldo Carvalho de Melo 	for (i = 0; i < sched->nr_tasks; i++) {
5100e9b07e5SArnaldo Carvalho de Melo 		task1 = sched->tasks[i];
5110e9b07e5SArnaldo Carvalho de Melo 		j = i + 1;
5120e9b07e5SArnaldo Carvalho de Melo 		if (j == sched->nr_tasks)
5130e9b07e5SArnaldo Carvalho de Melo 			j = 0;
5140e9b07e5SArnaldo Carvalho de Melo 		task2 = sched->tasks[j];
5150e9b07e5SArnaldo Carvalho de Melo 		add_sched_event_wakeup(sched, task1, 0, task2);
5160e9b07e5SArnaldo Carvalho de Melo 	}
5170e9b07e5SArnaldo Carvalho de Melo }
5180e9b07e5SArnaldo Carvalho de Melo 
5190e9b07e5SArnaldo Carvalho de Melo static void perf_sched__process_event(struct perf_sched *sched,
5200e9b07e5SArnaldo Carvalho de Melo 				      struct sched_atom *atom)
5210e9b07e5SArnaldo Carvalho de Melo {
5220e9b07e5SArnaldo Carvalho de Melo 	int ret = 0;
5230e9b07e5SArnaldo Carvalho de Melo 
5240e9b07e5SArnaldo Carvalho de Melo 	switch (atom->type) {
5250e9b07e5SArnaldo Carvalho de Melo 		case SCHED_EVENT_RUN:
5260e9b07e5SArnaldo Carvalho de Melo 			burn_nsecs(sched, atom->duration);
5270e9b07e5SArnaldo Carvalho de Melo 			break;
5280e9b07e5SArnaldo Carvalho de Melo 		case SCHED_EVENT_SLEEP:
5290e9b07e5SArnaldo Carvalho de Melo 			if (atom->wait_sem)
5300e9b07e5SArnaldo Carvalho de Melo 				ret = sem_wait(atom->wait_sem);
5310e9b07e5SArnaldo Carvalho de Melo 			BUG_ON(ret);
5320e9b07e5SArnaldo Carvalho de Melo 			break;
5330e9b07e5SArnaldo Carvalho de Melo 		case SCHED_EVENT_WAKEUP:
5340e9b07e5SArnaldo Carvalho de Melo 			if (atom->wait_sem)
5350e9b07e5SArnaldo Carvalho de Melo 				ret = sem_post(atom->wait_sem);
5360e9b07e5SArnaldo Carvalho de Melo 			BUG_ON(ret);
5370e9b07e5SArnaldo Carvalho de Melo 			break;
5380e9b07e5SArnaldo Carvalho de Melo 		case SCHED_EVENT_MIGRATION:
5390e9b07e5SArnaldo Carvalho de Melo 			break;
5400e9b07e5SArnaldo Carvalho de Melo 		default:
5410e9b07e5SArnaldo Carvalho de Melo 			BUG_ON(1);
5420e9b07e5SArnaldo Carvalho de Melo 	}
5430e9b07e5SArnaldo Carvalho de Melo }
5440e9b07e5SArnaldo Carvalho de Melo 
5450e9b07e5SArnaldo Carvalho de Melo static u64 get_cpu_usage_nsec_parent(void)
5460e9b07e5SArnaldo Carvalho de Melo {
5470e9b07e5SArnaldo Carvalho de Melo 	struct rusage ru;
5480e9b07e5SArnaldo Carvalho de Melo 	u64 sum;
5490e9b07e5SArnaldo Carvalho de Melo 	int err;
5500e9b07e5SArnaldo Carvalho de Melo 
5510e9b07e5SArnaldo Carvalho de Melo 	err = getrusage(RUSAGE_SELF, &ru);
5520e9b07e5SArnaldo Carvalho de Melo 	BUG_ON(err);
5530e9b07e5SArnaldo Carvalho de Melo 
5544fc76e49SArnaldo Carvalho de Melo 	sum =  ru.ru_utime.tv_sec * NSEC_PER_SEC + ru.ru_utime.tv_usec * NSEC_PER_USEC;
5554fc76e49SArnaldo Carvalho de Melo 	sum += ru.ru_stime.tv_sec * NSEC_PER_SEC + ru.ru_stime.tv_usec * NSEC_PER_USEC;
5560e9b07e5SArnaldo Carvalho de Melo 
5570e9b07e5SArnaldo Carvalho de Melo 	return sum;
5580e9b07e5SArnaldo Carvalho de Melo }
5590e9b07e5SArnaldo Carvalho de Melo 
560939cda52SYunlong Song static int self_open_counters(struct perf_sched *sched, unsigned long cur_task)
5610e9b07e5SArnaldo Carvalho de Melo {
5620e9b07e5SArnaldo Carvalho de Melo 	struct perf_event_attr attr;
563939cda52SYunlong Song 	char sbuf[STRERR_BUFSIZE], info[STRERR_BUFSIZE];
5640e9b07e5SArnaldo Carvalho de Melo 	int fd;
565939cda52SYunlong Song 	struct rlimit limit;
566939cda52SYunlong Song 	bool need_privilege = false;
5670e9b07e5SArnaldo Carvalho de Melo 
5680e9b07e5SArnaldo Carvalho de Melo 	memset(&attr, 0, sizeof(attr));
5690e9b07e5SArnaldo Carvalho de Melo 
5700e9b07e5SArnaldo Carvalho de Melo 	attr.type = PERF_TYPE_SOFTWARE;
5710e9b07e5SArnaldo Carvalho de Melo 	attr.config = PERF_COUNT_SW_TASK_CLOCK;
5720e9b07e5SArnaldo Carvalho de Melo 
573939cda52SYunlong Song force_again:
57457480d2cSYann Droneaud 	fd = sys_perf_event_open(&attr, 0, -1, -1,
57557480d2cSYann Droneaud 				 perf_event_open_cloexec_flag());
5760e9b07e5SArnaldo Carvalho de Melo 
5771aff59beSYunlong Song 	if (fd < 0) {
578939cda52SYunlong Song 		if (errno == EMFILE) {
579939cda52SYunlong Song 			if (sched->force) {
580939cda52SYunlong Song 				BUG_ON(getrlimit(RLIMIT_NOFILE, &limit) == -1);
581939cda52SYunlong Song 				limit.rlim_cur += sched->nr_tasks - cur_task;
582939cda52SYunlong Song 				if (limit.rlim_cur > limit.rlim_max) {
583939cda52SYunlong Song 					limit.rlim_max = limit.rlim_cur;
584939cda52SYunlong Song 					need_privilege = true;
585939cda52SYunlong Song 				}
586939cda52SYunlong Song 				if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
587939cda52SYunlong Song 					if (need_privilege && errno == EPERM)
588939cda52SYunlong Song 						strcpy(info, "Need privilege\n");
589939cda52SYunlong Song 				} else
590939cda52SYunlong Song 					goto force_again;
591939cda52SYunlong Song 			} else
592939cda52SYunlong Song 				strcpy(info, "Have a try with -f option\n");
593939cda52SYunlong Song 		}
59460b7d14aSNamhyung Kim 		pr_err("Error: sys_perf_event_open() syscall returned "
595939cda52SYunlong Song 		       "with %d (%s)\n%s", fd,
596c8b5f2c9SArnaldo Carvalho de Melo 		       str_error_r(errno, sbuf, sizeof(sbuf)), info);
5971aff59beSYunlong Song 		exit(EXIT_FAILURE);
5981aff59beSYunlong Song 	}
5990e9b07e5SArnaldo Carvalho de Melo 	return fd;
6000e9b07e5SArnaldo Carvalho de Melo }
6010e9b07e5SArnaldo Carvalho de Melo 
6020e9b07e5SArnaldo Carvalho de Melo static u64 get_cpu_usage_nsec_self(int fd)
6030e9b07e5SArnaldo Carvalho de Melo {
6040e9b07e5SArnaldo Carvalho de Melo 	u64 runtime;
6050e9b07e5SArnaldo Carvalho de Melo 	int ret;
6060e9b07e5SArnaldo Carvalho de Melo 
6070e9b07e5SArnaldo Carvalho de Melo 	ret = read(fd, &runtime, sizeof(runtime));
6080e9b07e5SArnaldo Carvalho de Melo 	BUG_ON(ret != sizeof(runtime));
6090e9b07e5SArnaldo Carvalho de Melo 
6100e9b07e5SArnaldo Carvalho de Melo 	return runtime;
6110e9b07e5SArnaldo Carvalho de Melo }
6120e9b07e5SArnaldo Carvalho de Melo 
6130e9b07e5SArnaldo Carvalho de Melo struct sched_thread_parms {
6140e9b07e5SArnaldo Carvalho de Melo 	struct task_desc  *task;
6150e9b07e5SArnaldo Carvalho de Melo 	struct perf_sched *sched;
61608097abcSYunlong Song 	int fd;
6170e9b07e5SArnaldo Carvalho de Melo };
6180e9b07e5SArnaldo Carvalho de Melo 
6190e9b07e5SArnaldo Carvalho de Melo static void *thread_func(void *ctx)
6200e9b07e5SArnaldo Carvalho de Melo {
6210e9b07e5SArnaldo Carvalho de Melo 	struct sched_thread_parms *parms = ctx;
6220e9b07e5SArnaldo Carvalho de Melo 	struct task_desc *this_task = parms->task;
6230e9b07e5SArnaldo Carvalho de Melo 	struct perf_sched *sched = parms->sched;
6240e9b07e5SArnaldo Carvalho de Melo 	u64 cpu_usage_0, cpu_usage_1;
6250e9b07e5SArnaldo Carvalho de Melo 	unsigned long i, ret;
6260e9b07e5SArnaldo Carvalho de Melo 	char comm2[22];
62708097abcSYunlong Song 	int fd = parms->fd;
6280e9b07e5SArnaldo Carvalho de Melo 
62974cf249dSArnaldo Carvalho de Melo 	zfree(&parms);
6300e9b07e5SArnaldo Carvalho de Melo 
6310e9b07e5SArnaldo Carvalho de Melo 	sprintf(comm2, ":%s", this_task->comm);
6320e9b07e5SArnaldo Carvalho de Melo 	prctl(PR_SET_NAME, comm2);
6330e9b07e5SArnaldo Carvalho de Melo 	if (fd < 0)
6340e9b07e5SArnaldo Carvalho de Melo 		return NULL;
6350e9b07e5SArnaldo Carvalho de Melo again:
6360e9b07e5SArnaldo Carvalho de Melo 	ret = sem_post(&this_task->ready_for_work);
6370e9b07e5SArnaldo Carvalho de Melo 	BUG_ON(ret);
6380e9b07e5SArnaldo Carvalho de Melo 	ret = pthread_mutex_lock(&sched->start_work_mutex);
6390e9b07e5SArnaldo Carvalho de Melo 	BUG_ON(ret);
6400e9b07e5SArnaldo Carvalho de Melo 	ret = pthread_mutex_unlock(&sched->start_work_mutex);
6410e9b07e5SArnaldo Carvalho de Melo 	BUG_ON(ret);
6420e9b07e5SArnaldo Carvalho de Melo 
6430e9b07e5SArnaldo Carvalho de Melo 	cpu_usage_0 = get_cpu_usage_nsec_self(fd);
6440e9b07e5SArnaldo Carvalho de Melo 
6450e9b07e5SArnaldo Carvalho de Melo 	for (i = 0; i < this_task->nr_events; i++) {
6460e9b07e5SArnaldo Carvalho de Melo 		this_task->curr_event = i;
6470e9b07e5SArnaldo Carvalho de Melo 		perf_sched__process_event(sched, this_task->atoms[i]);
6480e9b07e5SArnaldo Carvalho de Melo 	}
6490e9b07e5SArnaldo Carvalho de Melo 
6500e9b07e5SArnaldo Carvalho de Melo 	cpu_usage_1 = get_cpu_usage_nsec_self(fd);
6510e9b07e5SArnaldo Carvalho de Melo 	this_task->cpu_usage = cpu_usage_1 - cpu_usage_0;
6520e9b07e5SArnaldo Carvalho de Melo 	ret = sem_post(&this_task->work_done_sem);
6530e9b07e5SArnaldo Carvalho de Melo 	BUG_ON(ret);
6540e9b07e5SArnaldo Carvalho de Melo 
6550e9b07e5SArnaldo Carvalho de Melo 	ret = pthread_mutex_lock(&sched->work_done_wait_mutex);
6560e9b07e5SArnaldo Carvalho de Melo 	BUG_ON(ret);
6570e9b07e5SArnaldo Carvalho de Melo 	ret = pthread_mutex_unlock(&sched->work_done_wait_mutex);
6580e9b07e5SArnaldo Carvalho de Melo 	BUG_ON(ret);
6590e9b07e5SArnaldo Carvalho de Melo 
6600e9b07e5SArnaldo Carvalho de Melo 	goto again;
6610e9b07e5SArnaldo Carvalho de Melo }
6620e9b07e5SArnaldo Carvalho de Melo 
6630e9b07e5SArnaldo Carvalho de Melo static void create_tasks(struct perf_sched *sched)
6640e9b07e5SArnaldo Carvalho de Melo {
6650e9b07e5SArnaldo Carvalho de Melo 	struct task_desc *task;
6660e9b07e5SArnaldo Carvalho de Melo 	pthread_attr_t attr;
6670e9b07e5SArnaldo Carvalho de Melo 	unsigned long i;
6680e9b07e5SArnaldo Carvalho de Melo 	int err;
6690e9b07e5SArnaldo Carvalho de Melo 
6700e9b07e5SArnaldo Carvalho de Melo 	err = pthread_attr_init(&attr);
6710e9b07e5SArnaldo Carvalho de Melo 	BUG_ON(err);
6720e9b07e5SArnaldo Carvalho de Melo 	err = pthread_attr_setstacksize(&attr,
673d08c84e0SArnaldo Carvalho de Melo 			(size_t) max(16 * 1024, (int)PTHREAD_STACK_MIN));
6740e9b07e5SArnaldo Carvalho de Melo 	BUG_ON(err);
6750e9b07e5SArnaldo Carvalho de Melo 	err = pthread_mutex_lock(&sched->start_work_mutex);
6760e9b07e5SArnaldo Carvalho de Melo 	BUG_ON(err);
6770e9b07e5SArnaldo Carvalho de Melo 	err = pthread_mutex_lock(&sched->work_done_wait_mutex);
6780e9b07e5SArnaldo Carvalho de Melo 	BUG_ON(err);
6790e9b07e5SArnaldo Carvalho de Melo 	for (i = 0; i < sched->nr_tasks; i++) {
6800e9b07e5SArnaldo Carvalho de Melo 		struct sched_thread_parms *parms = malloc(sizeof(*parms));
6810e9b07e5SArnaldo Carvalho de Melo 		BUG_ON(parms == NULL);
6820e9b07e5SArnaldo Carvalho de Melo 		parms->task = task = sched->tasks[i];
6830e9b07e5SArnaldo Carvalho de Melo 		parms->sched = sched;
684939cda52SYunlong Song 		parms->fd = self_open_counters(sched, i);
6850e9b07e5SArnaldo Carvalho de Melo 		sem_init(&task->sleep_sem, 0, 0);
6860e9b07e5SArnaldo Carvalho de Melo 		sem_init(&task->ready_for_work, 0, 0);
6870e9b07e5SArnaldo Carvalho de Melo 		sem_init(&task->work_done_sem, 0, 0);
6880e9b07e5SArnaldo Carvalho de Melo 		task->curr_event = 0;
6890e9b07e5SArnaldo Carvalho de Melo 		err = pthread_create(&task->thread, &attr, thread_func, parms);
6900e9b07e5SArnaldo Carvalho de Melo 		BUG_ON(err);
6910e9b07e5SArnaldo Carvalho de Melo 	}
6920e9b07e5SArnaldo Carvalho de Melo }
6930e9b07e5SArnaldo Carvalho de Melo 
6940e9b07e5SArnaldo Carvalho de Melo static void wait_for_tasks(struct perf_sched *sched)
6950e9b07e5SArnaldo Carvalho de Melo {
6960e9b07e5SArnaldo Carvalho de Melo 	u64 cpu_usage_0, cpu_usage_1;
6970e9b07e5SArnaldo Carvalho de Melo 	struct task_desc *task;
6980e9b07e5SArnaldo Carvalho de Melo 	unsigned long i, ret;
6990e9b07e5SArnaldo Carvalho de Melo 
7000e9b07e5SArnaldo Carvalho de Melo 	sched->start_time = get_nsecs();
7010e9b07e5SArnaldo Carvalho de Melo 	sched->cpu_usage = 0;
7020e9b07e5SArnaldo Carvalho de Melo 	pthread_mutex_unlock(&sched->work_done_wait_mutex);
7030e9b07e5SArnaldo Carvalho de Melo 
7040e9b07e5SArnaldo Carvalho de Melo 	for (i = 0; i < sched->nr_tasks; i++) {
7050e9b07e5SArnaldo Carvalho de Melo 		task = sched->tasks[i];
7060e9b07e5SArnaldo Carvalho de Melo 		ret = sem_wait(&task->ready_for_work);
7070e9b07e5SArnaldo Carvalho de Melo 		BUG_ON(ret);
7080e9b07e5SArnaldo Carvalho de Melo 		sem_init(&task->ready_for_work, 0, 0);
7090e9b07e5SArnaldo Carvalho de Melo 	}
7100e9b07e5SArnaldo Carvalho de Melo 	ret = pthread_mutex_lock(&sched->work_done_wait_mutex);
7110e9b07e5SArnaldo Carvalho de Melo 	BUG_ON(ret);
7120e9b07e5SArnaldo Carvalho de Melo 
7130e9b07e5SArnaldo Carvalho de Melo 	cpu_usage_0 = get_cpu_usage_nsec_parent();
7140e9b07e5SArnaldo Carvalho de Melo 
7150e9b07e5SArnaldo Carvalho de Melo 	pthread_mutex_unlock(&sched->start_work_mutex);
7160e9b07e5SArnaldo Carvalho de Melo 
7170e9b07e5SArnaldo Carvalho de Melo 	for (i = 0; i < sched->nr_tasks; i++) {
7180e9b07e5SArnaldo Carvalho de Melo 		task = sched->tasks[i];
7190e9b07e5SArnaldo Carvalho de Melo 		ret = sem_wait(&task->work_done_sem);
7200e9b07e5SArnaldo Carvalho de Melo 		BUG_ON(ret);
7210e9b07e5SArnaldo Carvalho de Melo 		sem_init(&task->work_done_sem, 0, 0);
7220e9b07e5SArnaldo Carvalho de Melo 		sched->cpu_usage += task->cpu_usage;
7230e9b07e5SArnaldo Carvalho de Melo 		task->cpu_usage = 0;
7240e9b07e5SArnaldo Carvalho de Melo 	}
7250e9b07e5SArnaldo Carvalho de Melo 
7260e9b07e5SArnaldo Carvalho de Melo 	cpu_usage_1 = get_cpu_usage_nsec_parent();
7270e9b07e5SArnaldo Carvalho de Melo 	if (!sched->runavg_cpu_usage)
7280e9b07e5SArnaldo Carvalho de Melo 		sched->runavg_cpu_usage = sched->cpu_usage;
729ff5f3bbdSYunlong Song 	sched->runavg_cpu_usage = (sched->runavg_cpu_usage * (sched->replay_repeat - 1) + sched->cpu_usage) / sched->replay_repeat;
7300e9b07e5SArnaldo Carvalho de Melo 
7310e9b07e5SArnaldo Carvalho de Melo 	sched->parent_cpu_usage = cpu_usage_1 - cpu_usage_0;
7320e9b07e5SArnaldo Carvalho de Melo 	if (!sched->runavg_parent_cpu_usage)
7330e9b07e5SArnaldo Carvalho de Melo 		sched->runavg_parent_cpu_usage = sched->parent_cpu_usage;
734ff5f3bbdSYunlong Song 	sched->runavg_parent_cpu_usage = (sched->runavg_parent_cpu_usage * (sched->replay_repeat - 1) +
735ff5f3bbdSYunlong Song 					 sched->parent_cpu_usage)/sched->replay_repeat;
7360e9b07e5SArnaldo Carvalho de Melo 
7370e9b07e5SArnaldo Carvalho de Melo 	ret = pthread_mutex_lock(&sched->start_work_mutex);
7380e9b07e5SArnaldo Carvalho de Melo 	BUG_ON(ret);
7390e9b07e5SArnaldo Carvalho de Melo 
7400e9b07e5SArnaldo Carvalho de Melo 	for (i = 0; i < sched->nr_tasks; i++) {
7410e9b07e5SArnaldo Carvalho de Melo 		task = sched->tasks[i];
7420e9b07e5SArnaldo Carvalho de Melo 		sem_init(&task->sleep_sem, 0, 0);
7430e9b07e5SArnaldo Carvalho de Melo 		task->curr_event = 0;
7440e9b07e5SArnaldo Carvalho de Melo 	}
7450e9b07e5SArnaldo Carvalho de Melo }
7460e9b07e5SArnaldo Carvalho de Melo 
7470e9b07e5SArnaldo Carvalho de Melo static void run_one_test(struct perf_sched *sched)
7480e9b07e5SArnaldo Carvalho de Melo {
7490e9b07e5SArnaldo Carvalho de Melo 	u64 T0, T1, delta, avg_delta, fluct;
7500e9b07e5SArnaldo Carvalho de Melo 
7510e9b07e5SArnaldo Carvalho de Melo 	T0 = get_nsecs();
7520e9b07e5SArnaldo Carvalho de Melo 	wait_for_tasks(sched);
7530e9b07e5SArnaldo Carvalho de Melo 	T1 = get_nsecs();
7540e9b07e5SArnaldo Carvalho de Melo 
7550e9b07e5SArnaldo Carvalho de Melo 	delta = T1 - T0;
7560e9b07e5SArnaldo Carvalho de Melo 	sched->sum_runtime += delta;
7570e9b07e5SArnaldo Carvalho de Melo 	sched->nr_runs++;
7580e9b07e5SArnaldo Carvalho de Melo 
7590e9b07e5SArnaldo Carvalho de Melo 	avg_delta = sched->sum_runtime / sched->nr_runs;
7600e9b07e5SArnaldo Carvalho de Melo 	if (delta < avg_delta)
7610e9b07e5SArnaldo Carvalho de Melo 		fluct = avg_delta - delta;
7620e9b07e5SArnaldo Carvalho de Melo 	else
7630e9b07e5SArnaldo Carvalho de Melo 		fluct = delta - avg_delta;
7640e9b07e5SArnaldo Carvalho de Melo 	sched->sum_fluct += fluct;
7650e9b07e5SArnaldo Carvalho de Melo 	if (!sched->run_avg)
7660e9b07e5SArnaldo Carvalho de Melo 		sched->run_avg = delta;
767ff5f3bbdSYunlong Song 	sched->run_avg = (sched->run_avg * (sched->replay_repeat - 1) + delta) / sched->replay_repeat;
7680e9b07e5SArnaldo Carvalho de Melo 
7694fc76e49SArnaldo Carvalho de Melo 	printf("#%-3ld: %0.3f, ", sched->nr_runs, (double)delta / NSEC_PER_MSEC);
7700e9b07e5SArnaldo Carvalho de Melo 
7714fc76e49SArnaldo Carvalho de Melo 	printf("ravg: %0.2f, ", (double)sched->run_avg / NSEC_PER_MSEC);
7720e9b07e5SArnaldo Carvalho de Melo 
7730e9b07e5SArnaldo Carvalho de Melo 	printf("cpu: %0.2f / %0.2f",
7744fc76e49SArnaldo Carvalho de Melo 		(double)sched->cpu_usage / NSEC_PER_MSEC, (double)sched->runavg_cpu_usage / NSEC_PER_MSEC);
7750e9b07e5SArnaldo Carvalho de Melo 
7760e9b07e5SArnaldo Carvalho de Melo #if 0
7770e9b07e5SArnaldo Carvalho de Melo 	/*
7780e9b07e5SArnaldo Carvalho de Melo 	 * rusage statistics done by the parent, these are less
7790e9b07e5SArnaldo Carvalho de Melo 	 * accurate than the sched->sum_exec_runtime based statistics:
7800e9b07e5SArnaldo Carvalho de Melo 	 */
7810e9b07e5SArnaldo Carvalho de Melo 	printf(" [%0.2f / %0.2f]",
7824fc76e49SArnaldo Carvalho de Melo 		(double)sched->parent_cpu_usage / NSEC_PER_MSEC,
7834fc76e49SArnaldo Carvalho de Melo 		(double)sched->runavg_parent_cpu_usage / NSEC_PER_MSEC);
7840e9b07e5SArnaldo Carvalho de Melo #endif
7850e9b07e5SArnaldo Carvalho de Melo 
7860e9b07e5SArnaldo Carvalho de Melo 	printf("\n");
7870e9b07e5SArnaldo Carvalho de Melo 
7880e9b07e5SArnaldo Carvalho de Melo 	if (sched->nr_sleep_corrections)
7890e9b07e5SArnaldo Carvalho de Melo 		printf(" (%ld sleep corrections)\n", sched->nr_sleep_corrections);
7900e9b07e5SArnaldo Carvalho de Melo 	sched->nr_sleep_corrections = 0;
7910e9b07e5SArnaldo Carvalho de Melo }
7920e9b07e5SArnaldo Carvalho de Melo 
7930e9b07e5SArnaldo Carvalho de Melo static void test_calibrations(struct perf_sched *sched)
7940e9b07e5SArnaldo Carvalho de Melo {
7950e9b07e5SArnaldo Carvalho de Melo 	u64 T0, T1;
7960e9b07e5SArnaldo Carvalho de Melo 
7970e9b07e5SArnaldo Carvalho de Melo 	T0 = get_nsecs();
7984fc76e49SArnaldo Carvalho de Melo 	burn_nsecs(sched, NSEC_PER_MSEC);
7990e9b07e5SArnaldo Carvalho de Melo 	T1 = get_nsecs();
8000e9b07e5SArnaldo Carvalho de Melo 
8010e9b07e5SArnaldo Carvalho de Melo 	printf("the run test took %" PRIu64 " nsecs\n", T1 - T0);
8020e9b07e5SArnaldo Carvalho de Melo 
8030e9b07e5SArnaldo Carvalho de Melo 	T0 = get_nsecs();
8044fc76e49SArnaldo Carvalho de Melo 	sleep_nsecs(NSEC_PER_MSEC);
8050e9b07e5SArnaldo Carvalho de Melo 	T1 = get_nsecs();
8060e9b07e5SArnaldo Carvalho de Melo 
8070e9b07e5SArnaldo Carvalho de Melo 	printf("the sleep test took %" PRIu64 " nsecs\n", T1 - T0);
8080e9b07e5SArnaldo Carvalho de Melo }
8090e9b07e5SArnaldo Carvalho de Melo 
810a116e05dSArnaldo Carvalho de Melo static int
8110e9b07e5SArnaldo Carvalho de Melo replay_wakeup_event(struct perf_sched *sched,
81232dcd021SJiri Olsa 		    struct evsel *evsel, struct perf_sample *sample,
8139ec3f4e4SArnaldo Carvalho de Melo 		    struct machine *machine __maybe_unused)
814ec156764SIngo Molnar {
815efc0cdc9SArnaldo Carvalho de Melo 	const char *comm = evsel__strval(evsel, sample, "comm");
816efc0cdc9SArnaldo Carvalho de Melo 	const u32 pid	 = evsel__intval(evsel, sample, "pid");
817419ab0d6SFrederic Weisbecker 	struct task_desc *waker, *wakee;
818419ab0d6SFrederic Weisbecker 
819bb963e16SNamhyung Kim 	if (verbose > 0) {
8202b7fcbc5SArnaldo Carvalho de Melo 		printf("sched_wakeup event %p\n", evsel);
821419ab0d6SFrederic Weisbecker 
8229ec3f4e4SArnaldo Carvalho de Melo 		printf(" ... pid %d woke up %s/%d\n", sample->tid, comm, pid);
823419ab0d6SFrederic Weisbecker 	}
824419ab0d6SFrederic Weisbecker 
8252b7fcbc5SArnaldo Carvalho de Melo 	waker = register_pid(sched, sample->tid, "<unknown>");
8269ec3f4e4SArnaldo Carvalho de Melo 	wakee = register_pid(sched, pid, comm);
827419ab0d6SFrederic Weisbecker 
8280e9b07e5SArnaldo Carvalho de Melo 	add_sched_event_wakeup(sched, waker, sample->time, wakee);
829a116e05dSArnaldo Carvalho de Melo 	return 0;
830419ab0d6SFrederic Weisbecker }
831419ab0d6SFrederic Weisbecker 
8329ec3f4e4SArnaldo Carvalho de Melo static int replay_switch_event(struct perf_sched *sched,
83332dcd021SJiri Olsa 			       struct evsel *evsel,
8349ec3f4e4SArnaldo Carvalho de Melo 			       struct perf_sample *sample,
8359ec3f4e4SArnaldo Carvalho de Melo 			       struct machine *machine __maybe_unused)
836419ab0d6SFrederic Weisbecker {
837efc0cdc9SArnaldo Carvalho de Melo 	const char *prev_comm  = evsel__strval(evsel, sample, "prev_comm"),
838efc0cdc9SArnaldo Carvalho de Melo 		   *next_comm  = evsel__strval(evsel, sample, "next_comm");
839efc0cdc9SArnaldo Carvalho de Melo 	const u32 prev_pid = evsel__intval(evsel, sample, "prev_pid"),
840efc0cdc9SArnaldo Carvalho de Melo 		  next_pid = evsel__intval(evsel, sample, "next_pid");
841efc0cdc9SArnaldo Carvalho de Melo 	const u64 prev_state = evsel__intval(evsel, sample, "prev_state");
8421d037ca1SIrina Tirdea 	struct task_desc *prev, __maybe_unused *next;
8437f7f8d0bSArnaldo Carvalho de Melo 	u64 timestamp0, timestamp = sample->time;
8447f7f8d0bSArnaldo Carvalho de Melo 	int cpu = sample->cpu;
845fbf94829SIngo Molnar 	s64 delta;
846fbf94829SIngo Molnar 
847bb963e16SNamhyung Kim 	if (verbose > 0)
8482b7fcbc5SArnaldo Carvalho de Melo 		printf("sched_switch event %p\n", evsel);
849ad236fd2SIngo Molnar 
850fbf94829SIngo Molnar 	if (cpu >= MAX_CPUS || cpu < 0)
851a116e05dSArnaldo Carvalho de Melo 		return 0;
852fbf94829SIngo Molnar 
8530e9b07e5SArnaldo Carvalho de Melo 	timestamp0 = sched->cpu_last_switched[cpu];
854fbf94829SIngo Molnar 	if (timestamp0)
855fbf94829SIngo Molnar 		delta = timestamp - timestamp0;
856fbf94829SIngo Molnar 	else
857fbf94829SIngo Molnar 		delta = 0;
858fbf94829SIngo Molnar 
859a116e05dSArnaldo Carvalho de Melo 	if (delta < 0) {
86060b7d14aSNamhyung Kim 		pr_err("hm, delta: %" PRIu64 " < 0 ?\n", delta);
861a116e05dSArnaldo Carvalho de Melo 		return -1;
862a116e05dSArnaldo Carvalho de Melo 	}
863fbf94829SIngo Molnar 
8649ec3f4e4SArnaldo Carvalho de Melo 	pr_debug(" ... switch from %s/%d to %s/%d [ran %" PRIu64 " nsecs]\n",
8659ec3f4e4SArnaldo Carvalho de Melo 		 prev_comm, prev_pid, next_comm, next_pid, delta);
866fbf94829SIngo Molnar 
8679ec3f4e4SArnaldo Carvalho de Melo 	prev = register_pid(sched, prev_pid, prev_comm);
8689ec3f4e4SArnaldo Carvalho de Melo 	next = register_pid(sched, next_pid, next_comm);
869fbf94829SIngo Molnar 
8700e9b07e5SArnaldo Carvalho de Melo 	sched->cpu_last_switched[cpu] = timestamp;
871fbf94829SIngo Molnar 
8720e9b07e5SArnaldo Carvalho de Melo 	add_sched_event_run(sched, prev, timestamp, delta);
8739ec3f4e4SArnaldo Carvalho de Melo 	add_sched_event_sleep(sched, prev, timestamp, prev_state);
874a116e05dSArnaldo Carvalho de Melo 
875a116e05dSArnaldo Carvalho de Melo 	return 0;
876fbf94829SIngo Molnar }
877fbf94829SIngo Molnar 
878cb627505SDavid Ahern static int replay_fork_event(struct perf_sched *sched,
879cb627505SDavid Ahern 			     union perf_event *event,
880cb627505SDavid Ahern 			     struct machine *machine)
881419ab0d6SFrederic Weisbecker {
882cb627505SDavid Ahern 	struct thread *child, *parent;
8839ec3f4e4SArnaldo Carvalho de Melo 
884314add6bSAdrian Hunter 	child = machine__findnew_thread(machine, event->fork.pid,
885314add6bSAdrian Hunter 					event->fork.tid);
886314add6bSAdrian Hunter 	parent = machine__findnew_thread(machine, event->fork.ppid,
887314add6bSAdrian Hunter 					 event->fork.ptid);
888cb627505SDavid Ahern 
889cb627505SDavid Ahern 	if (child == NULL || parent == NULL) {
890cb627505SDavid Ahern 		pr_debug("thread does not exist on fork event: child %p, parent %p\n",
891cb627505SDavid Ahern 				 child, parent);
892b91fc39fSArnaldo Carvalho de Melo 		goto out_put;
893419ab0d6SFrederic Weisbecker 	}
8949ec3f4e4SArnaldo Carvalho de Melo 
895bb963e16SNamhyung Kim 	if (verbose > 0) {
896cb627505SDavid Ahern 		printf("fork event\n");
897b9c5143aSFrederic Weisbecker 		printf("... parent: %s/%d\n", thread__comm_str(parent), parent->tid);
898b9c5143aSFrederic Weisbecker 		printf("...  child: %s/%d\n", thread__comm_str(child), child->tid);
899cb627505SDavid Ahern 	}
900cb627505SDavid Ahern 
901b9c5143aSFrederic Weisbecker 	register_pid(sched, parent->tid, thread__comm_str(parent));
902b9c5143aSFrederic Weisbecker 	register_pid(sched, child->tid, thread__comm_str(child));
903b91fc39fSArnaldo Carvalho de Melo out_put:
904b91fc39fSArnaldo Carvalho de Melo 	thread__put(child);
905b91fc39fSArnaldo Carvalho de Melo 	thread__put(parent);
906a116e05dSArnaldo Carvalho de Melo 	return 0;
907419ab0d6SFrederic Weisbecker }
908419ab0d6SFrederic Weisbecker 
909b1ffe8f3SIngo Molnar struct sort_dimension {
910b1ffe8f3SIngo Molnar 	const char		*name;
911b5fae128SIngo Molnar 	sort_fn_t		cmp;
912b1ffe8f3SIngo Molnar 	struct list_head	list;
913b1ffe8f3SIngo Molnar };
914b1ffe8f3SIngo Molnar 
9158640da9fSChangbin Du /*
9168640da9fSChangbin Du  * handle runtime stats saved per thread
9178640da9fSChangbin Du  */
9188640da9fSChangbin Du static struct thread_runtime *thread__init_runtime(struct thread *thread)
9198640da9fSChangbin Du {
9208640da9fSChangbin Du 	struct thread_runtime *r;
9218640da9fSChangbin Du 
9228640da9fSChangbin Du 	r = zalloc(sizeof(struct thread_runtime));
9238640da9fSChangbin Du 	if (!r)
9248640da9fSChangbin Du 		return NULL;
9258640da9fSChangbin Du 
9268640da9fSChangbin Du 	init_stats(&r->run_stats);
9278640da9fSChangbin Du 	thread__set_priv(thread, r);
9288640da9fSChangbin Du 
9298640da9fSChangbin Du 	return r;
9308640da9fSChangbin Du }
9318640da9fSChangbin Du 
9328640da9fSChangbin Du static struct thread_runtime *thread__get_runtime(struct thread *thread)
9338640da9fSChangbin Du {
9348640da9fSChangbin Du 	struct thread_runtime *tr;
9358640da9fSChangbin Du 
9368640da9fSChangbin Du 	tr = thread__priv(thread);
9378640da9fSChangbin Du 	if (tr == NULL) {
9388640da9fSChangbin Du 		tr = thread__init_runtime(thread);
9398640da9fSChangbin Du 		if (tr == NULL)
9408640da9fSChangbin Du 			pr_debug("Failed to malloc memory for runtime data.\n");
9418640da9fSChangbin Du 	}
9428640da9fSChangbin Du 
9438640da9fSChangbin Du 	return tr;
9448640da9fSChangbin Du }
9458640da9fSChangbin Du 
946daa1d7a5SFrederic Weisbecker static int
94739aeb52fSmingo thread_lat_cmp(struct list_head *list, struct work_atoms *l, struct work_atoms *r)
948daa1d7a5SFrederic Weisbecker {
949daa1d7a5SFrederic Weisbecker 	struct sort_dimension *sort;
950daa1d7a5SFrederic Weisbecker 	int ret = 0;
951daa1d7a5SFrederic Weisbecker 
952b5fae128SIngo Molnar 	BUG_ON(list_empty(list));
953b5fae128SIngo Molnar 
954daa1d7a5SFrederic Weisbecker 	list_for_each_entry(sort, list, list) {
955daa1d7a5SFrederic Weisbecker 		ret = sort->cmp(l, r);
956daa1d7a5SFrederic Weisbecker 		if (ret)
957daa1d7a5SFrederic Weisbecker 			return ret;
958daa1d7a5SFrederic Weisbecker 	}
959daa1d7a5SFrederic Weisbecker 
960daa1d7a5SFrederic Weisbecker 	return ret;
961daa1d7a5SFrederic Weisbecker }
962daa1d7a5SFrederic Weisbecker 
96339aeb52fSmingo static struct work_atoms *
964cb4c13a5SDavidlohr Bueso thread_atoms_search(struct rb_root_cached *root, struct thread *thread,
965b5fae128SIngo Molnar 			 struct list_head *sort_list)
966b5fae128SIngo Molnar {
967cb4c13a5SDavidlohr Bueso 	struct rb_node *node = root->rb_root.rb_node;
96839aeb52fSmingo 	struct work_atoms key = { .thread = thread };
969b5fae128SIngo Molnar 
970b5fae128SIngo Molnar 	while (node) {
97139aeb52fSmingo 		struct work_atoms *atoms;
972b5fae128SIngo Molnar 		int cmp;
973b5fae128SIngo Molnar 
97439aeb52fSmingo 		atoms = container_of(node, struct work_atoms, node);
975b5fae128SIngo Molnar 
976b5fae128SIngo Molnar 		cmp = thread_lat_cmp(sort_list, &key, atoms);
977b5fae128SIngo Molnar 		if (cmp > 0)
978b5fae128SIngo Molnar 			node = node->rb_left;
979b5fae128SIngo Molnar 		else if (cmp < 0)
980b5fae128SIngo Molnar 			node = node->rb_right;
981b5fae128SIngo Molnar 		else {
982b5fae128SIngo Molnar 			BUG_ON(thread != atoms->thread);
983b5fae128SIngo Molnar 			return atoms;
984b5fae128SIngo Molnar 		}
985b5fae128SIngo Molnar 	}
986b5fae128SIngo Molnar 	return NULL;
987b5fae128SIngo Molnar }
988b5fae128SIngo Molnar 
989cdce9d73SFrederic Weisbecker static void
990cb4c13a5SDavidlohr Bueso __thread_latency_insert(struct rb_root_cached *root, struct work_atoms *data,
991daa1d7a5SFrederic Weisbecker 			 struct list_head *sort_list)
992cdce9d73SFrederic Weisbecker {
993cb4c13a5SDavidlohr Bueso 	struct rb_node **new = &(root->rb_root.rb_node), *parent = NULL;
994cb4c13a5SDavidlohr Bueso 	bool leftmost = true;
995cdce9d73SFrederic Weisbecker 
996cdce9d73SFrederic Weisbecker 	while (*new) {
99739aeb52fSmingo 		struct work_atoms *this;
998daa1d7a5SFrederic Weisbecker 		int cmp;
999cdce9d73SFrederic Weisbecker 
100039aeb52fSmingo 		this = container_of(*new, struct work_atoms, node);
1001cdce9d73SFrederic Weisbecker 		parent = *new;
1002daa1d7a5SFrederic Weisbecker 
1003daa1d7a5SFrederic Weisbecker 		cmp = thread_lat_cmp(sort_list, data, this);
1004daa1d7a5SFrederic Weisbecker 
1005daa1d7a5SFrederic Weisbecker 		if (cmp > 0)
1006cdce9d73SFrederic Weisbecker 			new = &((*new)->rb_left);
1007cb4c13a5SDavidlohr Bueso 		else {
1008daa1d7a5SFrederic Weisbecker 			new = &((*new)->rb_right);
1009cb4c13a5SDavidlohr Bueso 			leftmost = false;
1010cb4c13a5SDavidlohr Bueso 		}
1011cdce9d73SFrederic Weisbecker 	}
1012cdce9d73SFrederic Weisbecker 
1013cdce9d73SFrederic Weisbecker 	rb_link_node(&data->node, parent, new);
1014cb4c13a5SDavidlohr Bueso 	rb_insert_color_cached(&data->node, root, leftmost);
1015cdce9d73SFrederic Weisbecker }
1016cdce9d73SFrederic Weisbecker 
10170e9b07e5SArnaldo Carvalho de Melo static int thread_atoms_insert(struct perf_sched *sched, struct thread *thread)
1018cdce9d73SFrederic Weisbecker {
101936479484SArnaldo Carvalho de Melo 	struct work_atoms *atoms = zalloc(sizeof(*atoms));
1020a116e05dSArnaldo Carvalho de Melo 	if (!atoms) {
1021a116e05dSArnaldo Carvalho de Melo 		pr_err("No memory at %s\n", __func__);
1022a116e05dSArnaldo Carvalho de Melo 		return -1;
1023a116e05dSArnaldo Carvalho de Melo 	}
1024cdce9d73SFrederic Weisbecker 
1025f3b623b8SArnaldo Carvalho de Melo 	atoms->thread = thread__get(thread);
102639aeb52fSmingo 	INIT_LIST_HEAD(&atoms->work_list);
10270e9b07e5SArnaldo Carvalho de Melo 	__thread_latency_insert(&sched->atom_root, atoms, &sched->cmp_pid);
1028a116e05dSArnaldo Carvalho de Melo 	return 0;
1029cdce9d73SFrederic Weisbecker }
1030cdce9d73SFrederic Weisbecker 
10319ec3f4e4SArnaldo Carvalho de Melo static char sched_out_state(u64 prev_state)
1032cdce9d73SFrederic Weisbecker {
1033cdce9d73SFrederic Weisbecker 	const char *str = TASK_STATE_TO_CHAR_STR;
1034cdce9d73SFrederic Weisbecker 
10359ec3f4e4SArnaldo Carvalho de Melo 	return str[prev_state];
1036cdce9d73SFrederic Weisbecker }
1037cdce9d73SFrederic Weisbecker 
1038a116e05dSArnaldo Carvalho de Melo static int
103939aeb52fSmingo add_sched_out_event(struct work_atoms *atoms,
104039aeb52fSmingo 		    char run_state,
1041c6ced611SFrederic Weisbecker 		    u64 timestamp)
1042cdce9d73SFrederic Weisbecker {
104336479484SArnaldo Carvalho de Melo 	struct work_atom *atom = zalloc(sizeof(*atom));
1044a116e05dSArnaldo Carvalho de Melo 	if (!atom) {
1045a116e05dSArnaldo Carvalho de Melo 		pr_err("Non memory at %s", __func__);
1046a116e05dSArnaldo Carvalho de Melo 		return -1;
1047a116e05dSArnaldo Carvalho de Melo 	}
1048cdce9d73SFrederic Weisbecker 
1049aa1ab9d2SFrederic Weisbecker 	atom->sched_out_time = timestamp;
1050aa1ab9d2SFrederic Weisbecker 
105139aeb52fSmingo 	if (run_state == 'R') {
1052b1ffe8f3SIngo Molnar 		atom->state = THREAD_WAIT_CPU;
1053aa1ab9d2SFrederic Weisbecker 		atom->wake_up_time = atom->sched_out_time;
1054c6ced611SFrederic Weisbecker 	}
1055c6ced611SFrederic Weisbecker 
105639aeb52fSmingo 	list_add_tail(&atom->list, &atoms->work_list);
1057a116e05dSArnaldo Carvalho de Melo 	return 0;
1058cdce9d73SFrederic Weisbecker }
1059cdce9d73SFrederic Weisbecker 
1060cdce9d73SFrederic Weisbecker static void
10611d037ca1SIrina Tirdea add_runtime_event(struct work_atoms *atoms, u64 delta,
10621d037ca1SIrina Tirdea 		  u64 timestamp __maybe_unused)
106339aeb52fSmingo {
106439aeb52fSmingo 	struct work_atom *atom;
106539aeb52fSmingo 
106639aeb52fSmingo 	BUG_ON(list_empty(&atoms->work_list));
106739aeb52fSmingo 
106839aeb52fSmingo 	atom = list_entry(atoms->work_list.prev, struct work_atom, list);
106939aeb52fSmingo 
107039aeb52fSmingo 	atom->runtime += delta;
107139aeb52fSmingo 	atoms->total_runtime += delta;
107239aeb52fSmingo }
107339aeb52fSmingo 
107439aeb52fSmingo static void
107539aeb52fSmingo add_sched_in_event(struct work_atoms *atoms, u64 timestamp)
1076cdce9d73SFrederic Weisbecker {
1077b1ffe8f3SIngo Molnar 	struct work_atom *atom;
107866685678SFrederic Weisbecker 	u64 delta;
1079cdce9d73SFrederic Weisbecker 
108039aeb52fSmingo 	if (list_empty(&atoms->work_list))
1081cdce9d73SFrederic Weisbecker 		return;
1082cdce9d73SFrederic Weisbecker 
108339aeb52fSmingo 	atom = list_entry(atoms->work_list.prev, struct work_atom, list);
1084cdce9d73SFrederic Weisbecker 
1085b1ffe8f3SIngo Molnar 	if (atom->state != THREAD_WAIT_CPU)
1086cdce9d73SFrederic Weisbecker 		return;
1087cdce9d73SFrederic Weisbecker 
1088b1ffe8f3SIngo Molnar 	if (timestamp < atom->wake_up_time) {
1089b1ffe8f3SIngo Molnar 		atom->state = THREAD_IGNORE;
1090cdce9d73SFrederic Weisbecker 		return;
1091cdce9d73SFrederic Weisbecker 	}
1092cdce9d73SFrederic Weisbecker 
1093b1ffe8f3SIngo Molnar 	atom->state = THREAD_SCHED_IN;
1094b1ffe8f3SIngo Molnar 	atom->sched_in_time = timestamp;
109566685678SFrederic Weisbecker 
1096b1ffe8f3SIngo Molnar 	delta = atom->sched_in_time - atom->wake_up_time;
109766685678SFrederic Weisbecker 	atoms->total_lat += delta;
10983786310aSFrederic Weisbecker 	if (delta > atoms->max_lat) {
109966685678SFrederic Weisbecker 		atoms->max_lat = delta;
1100dc000c45SJoel Fernandes (Google) 		atoms->max_lat_start = atom->wake_up_time;
1101dc000c45SJoel Fernandes (Google) 		atoms->max_lat_end = timestamp;
11023786310aSFrederic Weisbecker 	}
110366685678SFrederic Weisbecker 	atoms->nb_atoms++;
1104cdce9d73SFrederic Weisbecker }
1105cdce9d73SFrederic Weisbecker 
11069ec3f4e4SArnaldo Carvalho de Melo static int latency_switch_event(struct perf_sched *sched,
110732dcd021SJiri Olsa 				struct evsel *evsel,
11089ec3f4e4SArnaldo Carvalho de Melo 				struct perf_sample *sample,
11099ec3f4e4SArnaldo Carvalho de Melo 				struct machine *machine)
1110cdce9d73SFrederic Weisbecker {
1111efc0cdc9SArnaldo Carvalho de Melo 	const u32 prev_pid = evsel__intval(evsel, sample, "prev_pid"),
1112efc0cdc9SArnaldo Carvalho de Melo 		  next_pid = evsel__intval(evsel, sample, "next_pid");
1113efc0cdc9SArnaldo Carvalho de Melo 	const u64 prev_state = evsel__intval(evsel, sample, "prev_state");
111439aeb52fSmingo 	struct work_atoms *out_events, *in_events;
1115cdce9d73SFrederic Weisbecker 	struct thread *sched_out, *sched_in;
11167f7f8d0bSArnaldo Carvalho de Melo 	u64 timestamp0, timestamp = sample->time;
1117b91fc39fSArnaldo Carvalho de Melo 	int cpu = sample->cpu, err = -1;
1118ea92ed5aSIngo Molnar 	s64 delta;
1119ea92ed5aSIngo Molnar 
112039aeb52fSmingo 	BUG_ON(cpu >= MAX_CPUS || cpu < 0);
1121ea92ed5aSIngo Molnar 
11220e9b07e5SArnaldo Carvalho de Melo 	timestamp0 = sched->cpu_last_switched[cpu];
11230e9b07e5SArnaldo Carvalho de Melo 	sched->cpu_last_switched[cpu] = timestamp;
1124ea92ed5aSIngo Molnar 	if (timestamp0)
1125ea92ed5aSIngo Molnar 		delta = timestamp - timestamp0;
1126ea92ed5aSIngo Molnar 	else
1127ea92ed5aSIngo Molnar 		delta = 0;
1128ea92ed5aSIngo Molnar 
1129a116e05dSArnaldo Carvalho de Melo 	if (delta < 0) {
1130a116e05dSArnaldo Carvalho de Melo 		pr_err("hm, delta: %" PRIu64 " < 0 ?\n", delta);
1131a116e05dSArnaldo Carvalho de Melo 		return -1;
1132a116e05dSArnaldo Carvalho de Melo 	}
1133cdce9d73SFrederic Weisbecker 
11341fcb8768SAdrian Hunter 	sched_out = machine__findnew_thread(machine, -1, prev_pid);
11351fcb8768SAdrian Hunter 	sched_in = machine__findnew_thread(machine, -1, next_pid);
1136b91fc39fSArnaldo Carvalho de Melo 	if (sched_out == NULL || sched_in == NULL)
1137b91fc39fSArnaldo Carvalho de Melo 		goto out_put;
1138cdce9d73SFrederic Weisbecker 
11390e9b07e5SArnaldo Carvalho de Melo 	out_events = thread_atoms_search(&sched->atom_root, sched_out, &sched->cmp_pid);
114039aeb52fSmingo 	if (!out_events) {
11410e9b07e5SArnaldo Carvalho de Melo 		if (thread_atoms_insert(sched, sched_out))
1142b91fc39fSArnaldo Carvalho de Melo 			goto out_put;
11430e9b07e5SArnaldo Carvalho de Melo 		out_events = thread_atoms_search(&sched->atom_root, sched_out, &sched->cmp_pid);
1144a116e05dSArnaldo Carvalho de Melo 		if (!out_events) {
1145a116e05dSArnaldo Carvalho de Melo 			pr_err("out-event: Internal tree error");
1146b91fc39fSArnaldo Carvalho de Melo 			goto out_put;
114739aeb52fSmingo 		}
1148a116e05dSArnaldo Carvalho de Melo 	}
11499ec3f4e4SArnaldo Carvalho de Melo 	if (add_sched_out_event(out_events, sched_out_state(prev_state), timestamp))
1150a116e05dSArnaldo Carvalho de Melo 		return -1;
115139aeb52fSmingo 
11520e9b07e5SArnaldo Carvalho de Melo 	in_events = thread_atoms_search(&sched->atom_root, sched_in, &sched->cmp_pid);
115339aeb52fSmingo 	if (!in_events) {
11540e9b07e5SArnaldo Carvalho de Melo 		if (thread_atoms_insert(sched, sched_in))
1155b91fc39fSArnaldo Carvalho de Melo 			goto out_put;
11560e9b07e5SArnaldo Carvalho de Melo 		in_events = thread_atoms_search(&sched->atom_root, sched_in, &sched->cmp_pid);
1157a116e05dSArnaldo Carvalho de Melo 		if (!in_events) {
1158a116e05dSArnaldo Carvalho de Melo 			pr_err("in-event: Internal tree error");
1159b91fc39fSArnaldo Carvalho de Melo 			goto out_put;
1160a116e05dSArnaldo Carvalho de Melo 		}
116139aeb52fSmingo 		/*
116239aeb52fSmingo 		 * Take came in we have not heard about yet,
116339aeb52fSmingo 		 * add in an initial atom in runnable state:
116439aeb52fSmingo 		 */
1165a116e05dSArnaldo Carvalho de Melo 		if (add_sched_out_event(in_events, 'R', timestamp))
1166b91fc39fSArnaldo Carvalho de Melo 			goto out_put;
116739aeb52fSmingo 	}
116839aeb52fSmingo 	add_sched_in_event(in_events, timestamp);
1169b91fc39fSArnaldo Carvalho de Melo 	err = 0;
1170b91fc39fSArnaldo Carvalho de Melo out_put:
1171b91fc39fSArnaldo Carvalho de Melo 	thread__put(sched_out);
1172b91fc39fSArnaldo Carvalho de Melo 	thread__put(sched_in);
1173b91fc39fSArnaldo Carvalho de Melo 	return err;
1174cdce9d73SFrederic Weisbecker }
1175cdce9d73SFrederic Weisbecker 
11769ec3f4e4SArnaldo Carvalho de Melo static int latency_runtime_event(struct perf_sched *sched,
117732dcd021SJiri Olsa 				 struct evsel *evsel,
11789ec3f4e4SArnaldo Carvalho de Melo 				 struct perf_sample *sample,
11799ec3f4e4SArnaldo Carvalho de Melo 				 struct machine *machine)
118039aeb52fSmingo {
1181efc0cdc9SArnaldo Carvalho de Melo 	const u32 pid	   = evsel__intval(evsel, sample, "pid");
1182efc0cdc9SArnaldo Carvalho de Melo 	const u64 runtime  = evsel__intval(evsel, sample, "runtime");
11831fcb8768SAdrian Hunter 	struct thread *thread = machine__findnew_thread(machine, -1, pid);
11840e9b07e5SArnaldo Carvalho de Melo 	struct work_atoms *atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid);
11857f7f8d0bSArnaldo Carvalho de Melo 	u64 timestamp = sample->time;
1186b91fc39fSArnaldo Carvalho de Melo 	int cpu = sample->cpu, err = -1;
1187b91fc39fSArnaldo Carvalho de Melo 
1188b91fc39fSArnaldo Carvalho de Melo 	if (thread == NULL)
1189b91fc39fSArnaldo Carvalho de Melo 		return -1;
119039aeb52fSmingo 
119139aeb52fSmingo 	BUG_ON(cpu >= MAX_CPUS || cpu < 0);
119239aeb52fSmingo 	if (!atoms) {
11930e9b07e5SArnaldo Carvalho de Melo 		if (thread_atoms_insert(sched, thread))
1194b91fc39fSArnaldo Carvalho de Melo 			goto out_put;
11950e9b07e5SArnaldo Carvalho de Melo 		atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid);
1196a116e05dSArnaldo Carvalho de Melo 		if (!atoms) {
119760b7d14aSNamhyung Kim 			pr_err("in-event: Internal tree error");
1198b91fc39fSArnaldo Carvalho de Melo 			goto out_put;
1199a116e05dSArnaldo Carvalho de Melo 		}
1200a116e05dSArnaldo Carvalho de Melo 		if (add_sched_out_event(atoms, 'R', timestamp))
1201b91fc39fSArnaldo Carvalho de Melo 			goto out_put;
120239aeb52fSmingo 	}
120339aeb52fSmingo 
12049ec3f4e4SArnaldo Carvalho de Melo 	add_runtime_event(atoms, runtime, timestamp);
1205b91fc39fSArnaldo Carvalho de Melo 	err = 0;
1206b91fc39fSArnaldo Carvalho de Melo out_put:
1207b91fc39fSArnaldo Carvalho de Melo 	thread__put(thread);
1208b91fc39fSArnaldo Carvalho de Melo 	return err;
1209cdce9d73SFrederic Weisbecker }
1210cdce9d73SFrederic Weisbecker 
12119ec3f4e4SArnaldo Carvalho de Melo static int latency_wakeup_event(struct perf_sched *sched,
121232dcd021SJiri Olsa 				struct evsel *evsel,
12139ec3f4e4SArnaldo Carvalho de Melo 				struct perf_sample *sample,
12149ec3f4e4SArnaldo Carvalho de Melo 				struct machine *machine)
1215cdce9d73SFrederic Weisbecker {
1216efc0cdc9SArnaldo Carvalho de Melo 	const u32 pid	  = evsel__intval(evsel, sample, "pid");
121739aeb52fSmingo 	struct work_atoms *atoms;
1218b1ffe8f3SIngo Molnar 	struct work_atom *atom;
1219cdce9d73SFrederic Weisbecker 	struct thread *wakee;
12207f7f8d0bSArnaldo Carvalho de Melo 	u64 timestamp = sample->time;
1221b91fc39fSArnaldo Carvalho de Melo 	int err = -1;
1222cdce9d73SFrederic Weisbecker 
12231fcb8768SAdrian Hunter 	wakee = machine__findnew_thread(machine, -1, pid);
1224b91fc39fSArnaldo Carvalho de Melo 	if (wakee == NULL)
1225b91fc39fSArnaldo Carvalho de Melo 		return -1;
12260e9b07e5SArnaldo Carvalho de Melo 	atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid);
122717562205SFrederic Weisbecker 	if (!atoms) {
12280e9b07e5SArnaldo Carvalho de Melo 		if (thread_atoms_insert(sched, wakee))
1229b91fc39fSArnaldo Carvalho de Melo 			goto out_put;
12300e9b07e5SArnaldo Carvalho de Melo 		atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid);
1231a116e05dSArnaldo Carvalho de Melo 		if (!atoms) {
123260b7d14aSNamhyung Kim 			pr_err("wakeup-event: Internal tree error");
1233b91fc39fSArnaldo Carvalho de Melo 			goto out_put;
1234a116e05dSArnaldo Carvalho de Melo 		}
1235a116e05dSArnaldo Carvalho de Melo 		if (add_sched_out_event(atoms, 'S', timestamp))
1236b91fc39fSArnaldo Carvalho de Melo 			goto out_put;
1237cdce9d73SFrederic Weisbecker 	}
1238cdce9d73SFrederic Weisbecker 
123939aeb52fSmingo 	BUG_ON(list_empty(&atoms->work_list));
1240cdce9d73SFrederic Weisbecker 
124139aeb52fSmingo 	atom = list_entry(atoms->work_list.prev, struct work_atom, list);
1242cdce9d73SFrederic Weisbecker 
124355ffb7a6SMike Galbraith 	/*
124467d6259dSDongsheng Yang 	 * As we do not guarantee the wakeup event happens when
124567d6259dSDongsheng Yang 	 * task is out of run queue, also may happen when task is
124667d6259dSDongsheng Yang 	 * on run queue and wakeup only change ->state to TASK_RUNNING,
124767d6259dSDongsheng Yang 	 * then we should not set the ->wake_up_time when wake up a
124867d6259dSDongsheng Yang 	 * task which is on run queue.
124967d6259dSDongsheng Yang 	 *
125055ffb7a6SMike Galbraith 	 * You WILL be missing events if you've recorded only
125155ffb7a6SMike Galbraith 	 * one CPU, or are only looking at only one, so don't
125267d6259dSDongsheng Yang 	 * skip in this case.
125355ffb7a6SMike Galbraith 	 */
12540e9b07e5SArnaldo Carvalho de Melo 	if (sched->profile_cpu == -1 && atom->state != THREAD_SLEEPING)
1255b91fc39fSArnaldo Carvalho de Melo 		goto out_ok;
1256cdce9d73SFrederic Weisbecker 
12570e9b07e5SArnaldo Carvalho de Melo 	sched->nr_timestamps++;
1258ea57c4f5SIngo Molnar 	if (atom->sched_out_time > timestamp) {
12590e9b07e5SArnaldo Carvalho de Melo 		sched->nr_unordered_timestamps++;
1260b91fc39fSArnaldo Carvalho de Melo 		goto out_ok;
1261ea57c4f5SIngo Molnar 	}
1262aa1ab9d2SFrederic Weisbecker 
1263b1ffe8f3SIngo Molnar 	atom->state = THREAD_WAIT_CPU;
1264b1ffe8f3SIngo Molnar 	atom->wake_up_time = timestamp;
1265b91fc39fSArnaldo Carvalho de Melo out_ok:
1266b91fc39fSArnaldo Carvalho de Melo 	err = 0;
1267b91fc39fSArnaldo Carvalho de Melo out_put:
1268b91fc39fSArnaldo Carvalho de Melo 	thread__put(wakee);
1269b91fc39fSArnaldo Carvalho de Melo 	return err;
1270cdce9d73SFrederic Weisbecker }
1271cdce9d73SFrederic Weisbecker 
12729ec3f4e4SArnaldo Carvalho de Melo static int latency_migrate_task_event(struct perf_sched *sched,
127332dcd021SJiri Olsa 				      struct evsel *evsel,
12749ec3f4e4SArnaldo Carvalho de Melo 				      struct perf_sample *sample,
12759ec3f4e4SArnaldo Carvalho de Melo 				      struct machine *machine)
127655ffb7a6SMike Galbraith {
1277efc0cdc9SArnaldo Carvalho de Melo 	const u32 pid = evsel__intval(evsel, sample, "pid");
12787f7f8d0bSArnaldo Carvalho de Melo 	u64 timestamp = sample->time;
127955ffb7a6SMike Galbraith 	struct work_atoms *atoms;
128055ffb7a6SMike Galbraith 	struct work_atom *atom;
128155ffb7a6SMike Galbraith 	struct thread *migrant;
1282b91fc39fSArnaldo Carvalho de Melo 	int err = -1;
128355ffb7a6SMike Galbraith 
128455ffb7a6SMike Galbraith 	/*
128555ffb7a6SMike Galbraith 	 * Only need to worry about migration when profiling one CPU.
128655ffb7a6SMike Galbraith 	 */
12870e9b07e5SArnaldo Carvalho de Melo 	if (sched->profile_cpu == -1)
1288a116e05dSArnaldo Carvalho de Melo 		return 0;
128955ffb7a6SMike Galbraith 
12901fcb8768SAdrian Hunter 	migrant = machine__findnew_thread(machine, -1, pid);
1291b91fc39fSArnaldo Carvalho de Melo 	if (migrant == NULL)
1292b91fc39fSArnaldo Carvalho de Melo 		return -1;
12930e9b07e5SArnaldo Carvalho de Melo 	atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid);
129455ffb7a6SMike Galbraith 	if (!atoms) {
12950e9b07e5SArnaldo Carvalho de Melo 		if (thread_atoms_insert(sched, migrant))
1296b91fc39fSArnaldo Carvalho de Melo 			goto out_put;
1297b9c5143aSFrederic Weisbecker 		register_pid(sched, migrant->tid, thread__comm_str(migrant));
12980e9b07e5SArnaldo Carvalho de Melo 		atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid);
1299a116e05dSArnaldo Carvalho de Melo 		if (!atoms) {
130060b7d14aSNamhyung Kim 			pr_err("migration-event: Internal tree error");
1301b91fc39fSArnaldo Carvalho de Melo 			goto out_put;
1302a116e05dSArnaldo Carvalho de Melo 		}
1303a116e05dSArnaldo Carvalho de Melo 		if (add_sched_out_event(atoms, 'R', timestamp))
1304b91fc39fSArnaldo Carvalho de Melo 			goto out_put;
130555ffb7a6SMike Galbraith 	}
130655ffb7a6SMike Galbraith 
130755ffb7a6SMike Galbraith 	BUG_ON(list_empty(&atoms->work_list));
130855ffb7a6SMike Galbraith 
130955ffb7a6SMike Galbraith 	atom = list_entry(atoms->work_list.prev, struct work_atom, list);
131055ffb7a6SMike Galbraith 	atom->sched_in_time = atom->sched_out_time = atom->wake_up_time = timestamp;
131155ffb7a6SMike Galbraith 
13120e9b07e5SArnaldo Carvalho de Melo 	sched->nr_timestamps++;
131355ffb7a6SMike Galbraith 
131455ffb7a6SMike Galbraith 	if (atom->sched_out_time > timestamp)
13150e9b07e5SArnaldo Carvalho de Melo 		sched->nr_unordered_timestamps++;
1316b91fc39fSArnaldo Carvalho de Melo 	err = 0;
1317b91fc39fSArnaldo Carvalho de Melo out_put:
1318b91fc39fSArnaldo Carvalho de Melo 	thread__put(migrant);
1319b91fc39fSArnaldo Carvalho de Melo 	return err;
132055ffb7a6SMike Galbraith }
132155ffb7a6SMike Galbraith 
13220e9b07e5SArnaldo Carvalho de Melo static void output_lat_thread(struct perf_sched *sched, struct work_atoms *work_list)
1323cdce9d73SFrederic Weisbecker {
1324cdce9d73SFrederic Weisbecker 	int i;
1325cdce9d73SFrederic Weisbecker 	int ret;
132666685678SFrederic Weisbecker 	u64 avg;
1327dc000c45SJoel Fernandes (Google) 	char max_lat_start[32], max_lat_end[32];
1328cdce9d73SFrederic Weisbecker 
132939aeb52fSmingo 	if (!work_list->nb_atoms)
1330cdce9d73SFrederic Weisbecker 		return;
1331ea57c4f5SIngo Molnar 	/*
1332ea57c4f5SIngo Molnar 	 * Ignore idle threads:
1333ea57c4f5SIngo Molnar 	 */
1334b9c5143aSFrederic Weisbecker 	if (!strcmp(thread__comm_str(work_list->thread), "swapper"))
1335ea57c4f5SIngo Molnar 		return;
1336cdce9d73SFrederic Weisbecker 
13370e9b07e5SArnaldo Carvalho de Melo 	sched->all_runtime += work_list->total_runtime;
13380e9b07e5SArnaldo Carvalho de Melo 	sched->all_count   += work_list->nb_atoms;
133966685678SFrederic Weisbecker 
13402f80dd44SJosef Bacik 	if (work_list->num_merged > 1)
13412f80dd44SJosef Bacik 		ret = printf("  %s:(%d) ", thread__comm_str(work_list->thread), work_list->num_merged);
13422f80dd44SJosef Bacik 	else
1343b9c5143aSFrederic Weisbecker 		ret = printf("  %s:%d ", thread__comm_str(work_list->thread), work_list->thread->tid);
1344cdce9d73SFrederic Weisbecker 
134508f69e6cSmingo 	for (i = 0; i < 24 - ret; i++)
1346cdce9d73SFrederic Weisbecker 		printf(" ");
1347cdce9d73SFrederic Weisbecker 
134839aeb52fSmingo 	avg = work_list->total_lat / work_list->nb_atoms;
1349dc000c45SJoel Fernandes (Google) 	timestamp__scnprintf_usec(work_list->max_lat_start, max_lat_start, sizeof(max_lat_start));
1350dc000c45SJoel Fernandes (Google) 	timestamp__scnprintf_usec(work_list->max_lat_end, max_lat_end, sizeof(max_lat_end));
1351cdce9d73SFrederic Weisbecker 
1352dc000c45SJoel Fernandes (Google) 	printf("|%11.3f ms |%9" PRIu64 " | avg:%8.3f ms | max:%8.3f ms | max start: %12s s | max end: %12s s\n",
13534fc76e49SArnaldo Carvalho de Melo 	      (double)work_list->total_runtime / NSEC_PER_MSEC,
13544fc76e49SArnaldo Carvalho de Melo 		 work_list->nb_atoms, (double)avg / NSEC_PER_MSEC,
13554fc76e49SArnaldo Carvalho de Melo 		 (double)work_list->max_lat / NSEC_PER_MSEC,
1356dc000c45SJoel Fernandes (Google) 		 max_lat_start, max_lat_end);
1357cdce9d73SFrederic Weisbecker }
1358cdce9d73SFrederic Weisbecker 
135939aeb52fSmingo static int pid_cmp(struct work_atoms *l, struct work_atoms *r)
1360daa1d7a5SFrederic Weisbecker {
13610014de17SJiri Olsa 	if (l->thread == r->thread)
13620014de17SJiri Olsa 		return 0;
136338051234SAdrian Hunter 	if (l->thread->tid < r->thread->tid)
1364daa1d7a5SFrederic Weisbecker 		return -1;
136538051234SAdrian Hunter 	if (l->thread->tid > r->thread->tid)
1366daa1d7a5SFrederic Weisbecker 		return 1;
13670014de17SJiri Olsa 	return (int)(l->thread - r->thread);
1368daa1d7a5SFrederic Weisbecker }
1369daa1d7a5SFrederic Weisbecker 
137039aeb52fSmingo static int avg_cmp(struct work_atoms *l, struct work_atoms *r)
1371daa1d7a5SFrederic Weisbecker {
1372daa1d7a5SFrederic Weisbecker 	u64 avgl, avgr;
1373daa1d7a5SFrederic Weisbecker 
1374daa1d7a5SFrederic Weisbecker 	if (!l->nb_atoms)
1375daa1d7a5SFrederic Weisbecker 		return -1;
1376daa1d7a5SFrederic Weisbecker 
1377daa1d7a5SFrederic Weisbecker 	if (!r->nb_atoms)
1378daa1d7a5SFrederic Weisbecker 		return 1;
1379daa1d7a5SFrederic Weisbecker 
1380daa1d7a5SFrederic Weisbecker 	avgl = l->total_lat / l->nb_atoms;
1381daa1d7a5SFrederic Weisbecker 	avgr = r->total_lat / r->nb_atoms;
1382daa1d7a5SFrederic Weisbecker 
1383daa1d7a5SFrederic Weisbecker 	if (avgl < avgr)
1384daa1d7a5SFrederic Weisbecker 		return -1;
1385daa1d7a5SFrederic Weisbecker 	if (avgl > avgr)
1386daa1d7a5SFrederic Weisbecker 		return 1;
1387daa1d7a5SFrederic Weisbecker 
1388daa1d7a5SFrederic Weisbecker 	return 0;
1389daa1d7a5SFrederic Weisbecker }
1390daa1d7a5SFrederic Weisbecker 
139139aeb52fSmingo static int max_cmp(struct work_atoms *l, struct work_atoms *r)
1392daa1d7a5SFrederic Weisbecker {
1393daa1d7a5SFrederic Weisbecker 	if (l->max_lat < r->max_lat)
1394daa1d7a5SFrederic Weisbecker 		return -1;
1395daa1d7a5SFrederic Weisbecker 	if (l->max_lat > r->max_lat)
1396daa1d7a5SFrederic Weisbecker 		return 1;
1397daa1d7a5SFrederic Weisbecker 
1398daa1d7a5SFrederic Weisbecker 	return 0;
1399daa1d7a5SFrederic Weisbecker }
1400daa1d7a5SFrederic Weisbecker 
140139aeb52fSmingo static int switch_cmp(struct work_atoms *l, struct work_atoms *r)
1402daa1d7a5SFrederic Weisbecker {
1403daa1d7a5SFrederic Weisbecker 	if (l->nb_atoms < r->nb_atoms)
1404daa1d7a5SFrederic Weisbecker 		return -1;
1405daa1d7a5SFrederic Weisbecker 	if (l->nb_atoms > r->nb_atoms)
1406daa1d7a5SFrederic Weisbecker 		return 1;
1407daa1d7a5SFrederic Weisbecker 
1408daa1d7a5SFrederic Weisbecker 	return 0;
1409daa1d7a5SFrederic Weisbecker }
1410daa1d7a5SFrederic Weisbecker 
141139aeb52fSmingo static int runtime_cmp(struct work_atoms *l, struct work_atoms *r)
1412daa1d7a5SFrederic Weisbecker {
1413daa1d7a5SFrederic Weisbecker 	if (l->total_runtime < r->total_runtime)
1414daa1d7a5SFrederic Weisbecker 		return -1;
1415daa1d7a5SFrederic Weisbecker 	if (l->total_runtime > r->total_runtime)
1416daa1d7a5SFrederic Weisbecker 		return 1;
1417daa1d7a5SFrederic Weisbecker 
1418daa1d7a5SFrederic Weisbecker 	return 0;
1419daa1d7a5SFrederic Weisbecker }
1420daa1d7a5SFrederic Weisbecker 
14210e9b07e5SArnaldo Carvalho de Melo static int sort_dimension__add(const char *tok, struct list_head *list)
14220e9b07e5SArnaldo Carvalho de Melo {
14230e9b07e5SArnaldo Carvalho de Melo 	size_t i;
14240e9b07e5SArnaldo Carvalho de Melo 	static struct sort_dimension avg_sort_dimension = {
14250e9b07e5SArnaldo Carvalho de Melo 		.name = "avg",
14260e9b07e5SArnaldo Carvalho de Melo 		.cmp  = avg_cmp,
14270e9b07e5SArnaldo Carvalho de Melo 	};
14280e9b07e5SArnaldo Carvalho de Melo 	static struct sort_dimension max_sort_dimension = {
14290e9b07e5SArnaldo Carvalho de Melo 		.name = "max",
14300e9b07e5SArnaldo Carvalho de Melo 		.cmp  = max_cmp,
14310e9b07e5SArnaldo Carvalho de Melo 	};
14320e9b07e5SArnaldo Carvalho de Melo 	static struct sort_dimension pid_sort_dimension = {
14330e9b07e5SArnaldo Carvalho de Melo 		.name = "pid",
14340e9b07e5SArnaldo Carvalho de Melo 		.cmp  = pid_cmp,
14350e9b07e5SArnaldo Carvalho de Melo 	};
1436daa1d7a5SFrederic Weisbecker 	static struct sort_dimension runtime_sort_dimension = {
1437daa1d7a5SFrederic Weisbecker 		.name = "runtime",
1438daa1d7a5SFrederic Weisbecker 		.cmp  = runtime_cmp,
1439daa1d7a5SFrederic Weisbecker 	};
14400e9b07e5SArnaldo Carvalho de Melo 	static struct sort_dimension switch_sort_dimension = {
14410e9b07e5SArnaldo Carvalho de Melo 		.name = "switch",
14420e9b07e5SArnaldo Carvalho de Melo 		.cmp  = switch_cmp,
14430e9b07e5SArnaldo Carvalho de Melo 	};
14440e9b07e5SArnaldo Carvalho de Melo 	struct sort_dimension *available_sorts[] = {
1445daa1d7a5SFrederic Weisbecker 		&pid_sort_dimension,
1446daa1d7a5SFrederic Weisbecker 		&avg_sort_dimension,
1447daa1d7a5SFrederic Weisbecker 		&max_sort_dimension,
1448daa1d7a5SFrederic Weisbecker 		&switch_sort_dimension,
1449daa1d7a5SFrederic Weisbecker 		&runtime_sort_dimension,
1450daa1d7a5SFrederic Weisbecker 	};
1451daa1d7a5SFrederic Weisbecker 
14520e9b07e5SArnaldo Carvalho de Melo 	for (i = 0; i < ARRAY_SIZE(available_sorts); i++) {
1453daa1d7a5SFrederic Weisbecker 		if (!strcmp(available_sorts[i]->name, tok)) {
1454daa1d7a5SFrederic Weisbecker 			list_add_tail(&available_sorts[i]->list, list);
1455daa1d7a5SFrederic Weisbecker 
1456daa1d7a5SFrederic Weisbecker 			return 0;
1457daa1d7a5SFrederic Weisbecker 		}
1458daa1d7a5SFrederic Weisbecker 	}
1459daa1d7a5SFrederic Weisbecker 
1460daa1d7a5SFrederic Weisbecker 	return -1;
1461daa1d7a5SFrederic Weisbecker }
1462daa1d7a5SFrederic Weisbecker 
14630e9b07e5SArnaldo Carvalho de Melo static void perf_sched__sort_lat(struct perf_sched *sched)
1464daa1d7a5SFrederic Weisbecker {
1465daa1d7a5SFrederic Weisbecker 	struct rb_node *node;
1466cb4c13a5SDavidlohr Bueso 	struct rb_root_cached *root = &sched->atom_root;
14672f80dd44SJosef Bacik again:
1468daa1d7a5SFrederic Weisbecker 	for (;;) {
146939aeb52fSmingo 		struct work_atoms *data;
1470cb4c13a5SDavidlohr Bueso 		node = rb_first_cached(root);
1471daa1d7a5SFrederic Weisbecker 		if (!node)
1472daa1d7a5SFrederic Weisbecker 			break;
1473daa1d7a5SFrederic Weisbecker 
1474cb4c13a5SDavidlohr Bueso 		rb_erase_cached(node, root);
147539aeb52fSmingo 		data = rb_entry(node, struct work_atoms, node);
14760e9b07e5SArnaldo Carvalho de Melo 		__thread_latency_insert(&sched->sorted_atom_root, data, &sched->sort_list);
1477daa1d7a5SFrederic Weisbecker 	}
14782f80dd44SJosef Bacik 	if (root == &sched->atom_root) {
14792f80dd44SJosef Bacik 		root = &sched->merged_atom_root;
14802f80dd44SJosef Bacik 		goto again;
14812f80dd44SJosef Bacik 	}
1482daa1d7a5SFrederic Weisbecker }
1483daa1d7a5SFrederic Weisbecker 
14840e9b07e5SArnaldo Carvalho de Melo static int process_sched_wakeup_event(struct perf_tool *tool,
148532dcd021SJiri Olsa 				      struct evsel *evsel,
1486ee29be62SArnaldo Carvalho de Melo 				      struct perf_sample *sample,
14874218e673SArnaldo Carvalho de Melo 				      struct machine *machine)
1488419ab0d6SFrederic Weisbecker {
14890e9b07e5SArnaldo Carvalho de Melo 	struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
1490419ab0d6SFrederic Weisbecker 
14919ec3f4e4SArnaldo Carvalho de Melo 	if (sched->tp_handler->wakeup_event)
14929ec3f4e4SArnaldo Carvalho de Melo 		return sched->tp_handler->wakeup_event(sched, evsel, sample, machine);
1493419ab0d6SFrederic Weisbecker 
14942b7fcbc5SArnaldo Carvalho de Melo 	return 0;
1495419ab0d6SFrederic Weisbecker }
1496419ab0d6SFrederic Weisbecker 
1497a151a37aSJiri Olsa union map_priv {
1498a151a37aSJiri Olsa 	void	*ptr;
1499a151a37aSJiri Olsa 	bool	 color;
1500a151a37aSJiri Olsa };
1501a151a37aSJiri Olsa 
1502a151a37aSJiri Olsa static bool thread__has_color(struct thread *thread)
1503a151a37aSJiri Olsa {
1504a151a37aSJiri Olsa 	union map_priv priv = {
1505a151a37aSJiri Olsa 		.ptr = thread__priv(thread),
1506a151a37aSJiri Olsa 	};
1507a151a37aSJiri Olsa 
1508a151a37aSJiri Olsa 	return priv.color;
1509a151a37aSJiri Olsa }
1510a151a37aSJiri Olsa 
1511a151a37aSJiri Olsa static struct thread*
1512a151a37aSJiri Olsa map__findnew_thread(struct perf_sched *sched, struct machine *machine, pid_t pid, pid_t tid)
1513a151a37aSJiri Olsa {
1514a151a37aSJiri Olsa 	struct thread *thread = machine__findnew_thread(machine, pid, tid);
1515a151a37aSJiri Olsa 	union map_priv priv = {
1516a151a37aSJiri Olsa 		.color = false,
1517a151a37aSJiri Olsa 	};
1518a151a37aSJiri Olsa 
1519a151a37aSJiri Olsa 	if (!sched->map.color_pids || !thread || thread__priv(thread))
1520a151a37aSJiri Olsa 		return thread;
1521a151a37aSJiri Olsa 
1522a151a37aSJiri Olsa 	if (thread_map__has(sched->map.color_pids, tid))
1523a151a37aSJiri Olsa 		priv.color = true;
1524a151a37aSJiri Olsa 
1525a151a37aSJiri Olsa 	thread__set_priv(thread, priv.ptr);
1526a151a37aSJiri Olsa 	return thread;
1527a151a37aSJiri Olsa }
1528a151a37aSJiri Olsa 
152932dcd021SJiri Olsa static int map_switch_event(struct perf_sched *sched, struct evsel *evsel,
15309ec3f4e4SArnaldo Carvalho de Melo 			    struct perf_sample *sample, struct machine *machine)
15310ec04e16SIngo Molnar {
1532efc0cdc9SArnaldo Carvalho de Melo 	const u32 next_pid = evsel__intval(evsel, sample, "next_pid");
15339d372ca5SDongsheng Yang 	struct thread *sched_in;
15348640da9fSChangbin Du 	struct thread_runtime *tr;
15350ec04e16SIngo Molnar 	int new_shortname;
15367f7f8d0bSArnaldo Carvalho de Melo 	u64 timestamp0, timestamp = sample->time;
15370ec04e16SIngo Molnar 	s64 delta;
153899623c62SJiri Olsa 	int i, this_cpu = sample->cpu;
153999623c62SJiri Olsa 	int cpus_nr;
154099623c62SJiri Olsa 	bool new_cpu = false;
15418cd91195SJiri Olsa 	const char *color = PERF_COLOR_NORMAL;
154299620a5dSNamhyung Kim 	char stimestamp[32];
15430ec04e16SIngo Molnar 
15440ec04e16SIngo Molnar 	BUG_ON(this_cpu >= MAX_CPUS || this_cpu < 0);
15450ec04e16SIngo Molnar 
15460e9b07e5SArnaldo Carvalho de Melo 	if (this_cpu > sched->max_cpu)
15470e9b07e5SArnaldo Carvalho de Melo 		sched->max_cpu = this_cpu;
15480ec04e16SIngo Molnar 
154999623c62SJiri Olsa 	if (sched->map.comp) {
155099623c62SJiri Olsa 		cpus_nr = bitmap_weight(sched->map.comp_cpus_mask, MAX_CPUS);
155199623c62SJiri Olsa 		if (!test_and_set_bit(this_cpu, sched->map.comp_cpus_mask)) {
155299623c62SJiri Olsa 			sched->map.comp_cpus[cpus_nr++] = this_cpu;
155399623c62SJiri Olsa 			new_cpu = true;
155499623c62SJiri Olsa 		}
155599623c62SJiri Olsa 	} else
155699623c62SJiri Olsa 		cpus_nr = sched->max_cpu;
155799623c62SJiri Olsa 
15580e9b07e5SArnaldo Carvalho de Melo 	timestamp0 = sched->cpu_last_switched[this_cpu];
15590e9b07e5SArnaldo Carvalho de Melo 	sched->cpu_last_switched[this_cpu] = timestamp;
15600ec04e16SIngo Molnar 	if (timestamp0)
15610ec04e16SIngo Molnar 		delta = timestamp - timestamp0;
15620ec04e16SIngo Molnar 	else
15630ec04e16SIngo Molnar 		delta = 0;
15640ec04e16SIngo Molnar 
1565a116e05dSArnaldo Carvalho de Melo 	if (delta < 0) {
156660b7d14aSNamhyung Kim 		pr_err("hm, delta: %" PRIu64 " < 0 ?\n", delta);
1567a116e05dSArnaldo Carvalho de Melo 		return -1;
1568a116e05dSArnaldo Carvalho de Melo 	}
15690ec04e16SIngo Molnar 
1570a151a37aSJiri Olsa 	sched_in = map__findnew_thread(sched, machine, -1, next_pid);
1571b91fc39fSArnaldo Carvalho de Melo 	if (sched_in == NULL)
1572b91fc39fSArnaldo Carvalho de Melo 		return -1;
15730ec04e16SIngo Molnar 
15748640da9fSChangbin Du 	tr = thread__get_runtime(sched_in);
15758640da9fSChangbin Du 	if (tr == NULL) {
15768640da9fSChangbin Du 		thread__put(sched_in);
15778640da9fSChangbin Du 		return -1;
15788640da9fSChangbin Du 	}
15798640da9fSChangbin Du 
1580b91fc39fSArnaldo Carvalho de Melo 	sched->curr_thread[this_cpu] = thread__get(sched_in);
15810ec04e16SIngo Molnar 
15820ec04e16SIngo Molnar 	printf("  ");
15830ec04e16SIngo Molnar 
15840ec04e16SIngo Molnar 	new_shortname = 0;
15858640da9fSChangbin Du 	if (!tr->shortname[0]) {
15866bcab4e1SDongsheng 		if (!strcmp(thread__comm_str(sched_in), "swapper")) {
15876bcab4e1SDongsheng 			/*
15886bcab4e1SDongsheng 			 * Don't allocate a letter-number for swapper:0
15896bcab4e1SDongsheng 			 * as a shortname. Instead, we use '.' for it.
15906bcab4e1SDongsheng 			 */
15918640da9fSChangbin Du 			tr->shortname[0] = '.';
15928640da9fSChangbin Du 			tr->shortname[1] = ' ';
15936bcab4e1SDongsheng 		} else {
15948640da9fSChangbin Du 			tr->shortname[0] = sched->next_shortname1;
15958640da9fSChangbin Du 			tr->shortname[1] = sched->next_shortname2;
15960ec04e16SIngo Molnar 
15970e9b07e5SArnaldo Carvalho de Melo 			if (sched->next_shortname1 < 'Z') {
15980e9b07e5SArnaldo Carvalho de Melo 				sched->next_shortname1++;
15990ec04e16SIngo Molnar 			} else {
16000e9b07e5SArnaldo Carvalho de Melo 				sched->next_shortname1 = 'A';
16016bcab4e1SDongsheng 				if (sched->next_shortname2 < '9')
16020e9b07e5SArnaldo Carvalho de Melo 					sched->next_shortname2++;
16036bcab4e1SDongsheng 				else
16040e9b07e5SArnaldo Carvalho de Melo 					sched->next_shortname2 = '0';
16050ec04e16SIngo Molnar 			}
16060ec04e16SIngo Molnar 		}
16070ec04e16SIngo Molnar 		new_shortname = 1;
16080ec04e16SIngo Molnar 	}
16090ec04e16SIngo Molnar 
161099623c62SJiri Olsa 	for (i = 0; i < cpus_nr; i++) {
161199623c62SJiri Olsa 		int cpu = sched->map.comp ? sched->map.comp_cpus[i] : i;
1612a151a37aSJiri Olsa 		struct thread *curr_thread = sched->curr_thread[cpu];
16138640da9fSChangbin Du 		struct thread_runtime *curr_tr;
1614a151a37aSJiri Olsa 		const char *pid_color = color;
1615cf294f24SJiri Olsa 		const char *cpu_color = color;
1616a151a37aSJiri Olsa 
1617a151a37aSJiri Olsa 		if (curr_thread && thread__has_color(curr_thread))
1618a151a37aSJiri Olsa 			pid_color = COLOR_PIDS;
161999623c62SJiri Olsa 
1620*dfc66befSIan Rogers 		if (sched->map.cpus && !perf_cpu_map__has(sched->map.cpus, cpu))
162173643bb6SJiri Olsa 			continue;
162273643bb6SJiri Olsa 
1623*dfc66befSIan Rogers 		if (sched->map.color_cpus && perf_cpu_map__has(sched->map.color_cpus, cpu))
1624cf294f24SJiri Olsa 			cpu_color = COLOR_CPUS;
1625cf294f24SJiri Olsa 
16260ec04e16SIngo Molnar 		if (cpu != this_cpu)
16271208bb27SNamhyung Kim 			color_fprintf(stdout, color, " ");
16280ec04e16SIngo Molnar 		else
1629cf294f24SJiri Olsa 			color_fprintf(stdout, cpu_color, "*");
16300ec04e16SIngo Molnar 
16318640da9fSChangbin Du 		if (sched->curr_thread[cpu]) {
16328640da9fSChangbin Du 			curr_tr = thread__get_runtime(sched->curr_thread[cpu]);
16338640da9fSChangbin Du 			if (curr_tr == NULL) {
16348640da9fSChangbin Du 				thread__put(sched_in);
16358640da9fSChangbin Du 				return -1;
16368640da9fSChangbin Du 			}
16378640da9fSChangbin Du 			color_fprintf(stdout, pid_color, "%2s ", curr_tr->shortname);
16388640da9fSChangbin Du 		} else
16398cd91195SJiri Olsa 			color_fprintf(stdout, color, "   ");
16400ec04e16SIngo Molnar 	}
16410ec04e16SIngo Molnar 
1642*dfc66befSIan Rogers 	if (sched->map.cpus && !perf_cpu_map__has(sched->map.cpus, this_cpu))
164373643bb6SJiri Olsa 		goto out;
164473643bb6SJiri Olsa 
164599620a5dSNamhyung Kim 	timestamp__scnprintf_usec(timestamp, stimestamp, sizeof(stimestamp));
164699620a5dSNamhyung Kim 	color_fprintf(stdout, color, "  %12s secs ", stimestamp);
164799a3c3a9SChangbin Du 	if (new_shortname || tr->comm_changed || (verbose > 0 && sched_in->tid)) {
1648a151a37aSJiri Olsa 		const char *pid_color = color;
1649a151a37aSJiri Olsa 
1650a151a37aSJiri Olsa 		if (thread__has_color(sched_in))
1651a151a37aSJiri Olsa 			pid_color = COLOR_PIDS;
1652a151a37aSJiri Olsa 
1653a151a37aSJiri Olsa 		color_fprintf(stdout, pid_color, "%s => %s:%d",
16548640da9fSChangbin Du 		       tr->shortname, thread__comm_str(sched_in), sched_in->tid);
165599a3c3a9SChangbin Du 		tr->comm_changed = false;
16560ec04e16SIngo Molnar 	}
1657a116e05dSArnaldo Carvalho de Melo 
165899623c62SJiri Olsa 	if (sched->map.comp && new_cpu)
16598cd91195SJiri Olsa 		color_fprintf(stdout, color, " (CPU %d)", this_cpu);
166099623c62SJiri Olsa 
166173643bb6SJiri Olsa out:
16628cd91195SJiri Olsa 	color_fprintf(stdout, color, "\n");
166399623c62SJiri Olsa 
1664b91fc39fSArnaldo Carvalho de Melo 	thread__put(sched_in);
1665b91fc39fSArnaldo Carvalho de Melo 
1666a116e05dSArnaldo Carvalho de Melo 	return 0;
16670ec04e16SIngo Molnar }
16680ec04e16SIngo Molnar 
16690e9b07e5SArnaldo Carvalho de Melo static int process_sched_switch_event(struct perf_tool *tool,
167032dcd021SJiri Olsa 				      struct evsel *evsel,
1671ee29be62SArnaldo Carvalho de Melo 				      struct perf_sample *sample,
16724218e673SArnaldo Carvalho de Melo 				      struct machine *machine)
1673419ab0d6SFrederic Weisbecker {
16740e9b07e5SArnaldo Carvalho de Melo 	struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
1675a116e05dSArnaldo Carvalho de Melo 	int this_cpu = sample->cpu, err = 0;
1676efc0cdc9SArnaldo Carvalho de Melo 	u32 prev_pid = evsel__intval(evsel, sample, "prev_pid"),
1677efc0cdc9SArnaldo Carvalho de Melo 	    next_pid = evsel__intval(evsel, sample, "next_pid");
1678419ab0d6SFrederic Weisbecker 
16790e9b07e5SArnaldo Carvalho de Melo 	if (sched->curr_pid[this_cpu] != (u32)-1) {
1680c8a37751SIngo Molnar 		/*
1681c8a37751SIngo Molnar 		 * Are we trying to switch away a PID that is
1682c8a37751SIngo Molnar 		 * not current?
1683c8a37751SIngo Molnar 		 */
16842b7fcbc5SArnaldo Carvalho de Melo 		if (sched->curr_pid[this_cpu] != prev_pid)
16850e9b07e5SArnaldo Carvalho de Melo 			sched->nr_context_switch_bugs++;
1686c8a37751SIngo Molnar 	}
1687c8a37751SIngo Molnar 
16889ec3f4e4SArnaldo Carvalho de Melo 	if (sched->tp_handler->switch_event)
16899ec3f4e4SArnaldo Carvalho de Melo 		err = sched->tp_handler->switch_event(sched, evsel, sample, machine);
16902b7fcbc5SArnaldo Carvalho de Melo 
16912b7fcbc5SArnaldo Carvalho de Melo 	sched->curr_pid[this_cpu] = next_pid;
1692a116e05dSArnaldo Carvalho de Melo 	return err;
1693419ab0d6SFrederic Weisbecker }
1694419ab0d6SFrederic Weisbecker 
16950e9b07e5SArnaldo Carvalho de Melo static int process_sched_runtime_event(struct perf_tool *tool,
169632dcd021SJiri Olsa 				       struct evsel *evsel,
1697ee29be62SArnaldo Carvalho de Melo 				       struct perf_sample *sample,
16984218e673SArnaldo Carvalho de Melo 				       struct machine *machine)
169939aeb52fSmingo {
17000e9b07e5SArnaldo Carvalho de Melo 	struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
170139aeb52fSmingo 
17029ec3f4e4SArnaldo Carvalho de Melo 	if (sched->tp_handler->runtime_event)
17039ec3f4e4SArnaldo Carvalho de Melo 		return sched->tp_handler->runtime_event(sched, evsel, sample, machine);
170439aeb52fSmingo 
1705a116e05dSArnaldo Carvalho de Melo 	return 0;
1706ec156764SIngo Molnar }
1707ec156764SIngo Molnar 
1708cb627505SDavid Ahern static int perf_sched__process_fork_event(struct perf_tool *tool,
1709cb627505SDavid Ahern 					  union perf_event *event,
17102b7fcbc5SArnaldo Carvalho de Melo 					  struct perf_sample *sample,
1711cb627505SDavid Ahern 					  struct machine *machine)
17122b7fcbc5SArnaldo Carvalho de Melo {
17132b7fcbc5SArnaldo Carvalho de Melo 	struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
17142b7fcbc5SArnaldo Carvalho de Melo 
17154d39c89fSIngo Molnar 	/* run the fork event through the perf machinery */
1716cb627505SDavid Ahern 	perf_event__process_fork(tool, event, sample, machine);
1717cb627505SDavid Ahern 
1718cb627505SDavid Ahern 	/* and then run additional processing needed for this command */
17199ec3f4e4SArnaldo Carvalho de Melo 	if (sched->tp_handler->fork_event)
1720cb627505SDavid Ahern 		return sched->tp_handler->fork_event(sched, event, machine);
17212b7fcbc5SArnaldo Carvalho de Melo 
17222b7fcbc5SArnaldo Carvalho de Melo 	return 0;
17232b7fcbc5SArnaldo Carvalho de Melo }
17242b7fcbc5SArnaldo Carvalho de Melo 
17250e9b07e5SArnaldo Carvalho de Melo static int process_sched_migrate_task_event(struct perf_tool *tool,
172632dcd021SJiri Olsa 					    struct evsel *evsel,
1727ee29be62SArnaldo Carvalho de Melo 					    struct perf_sample *sample,
17284218e673SArnaldo Carvalho de Melo 					    struct machine *machine)
172955ffb7a6SMike Galbraith {
17300e9b07e5SArnaldo Carvalho de Melo 	struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
173155ffb7a6SMike Galbraith 
17329ec3f4e4SArnaldo Carvalho de Melo 	if (sched->tp_handler->migrate_task_event)
17339ec3f4e4SArnaldo Carvalho de Melo 		return sched->tp_handler->migrate_task_event(sched, evsel, sample, machine);
173455ffb7a6SMike Galbraith 
17352b7fcbc5SArnaldo Carvalho de Melo 	return 0;
173655ffb7a6SMike Galbraith }
173755ffb7a6SMike Galbraith 
1738a116e05dSArnaldo Carvalho de Melo typedef int (*tracepoint_handler)(struct perf_tool *tool,
173932dcd021SJiri Olsa 				  struct evsel *evsel,
1740ee29be62SArnaldo Carvalho de Melo 				  struct perf_sample *sample,
17414218e673SArnaldo Carvalho de Melo 				  struct machine *machine);
1742ec156764SIngo Molnar 
17431d037ca1SIrina Tirdea static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_unused,
17441d037ca1SIrina Tirdea 						 union perf_event *event __maybe_unused,
17458115d60cSArnaldo Carvalho de Melo 						 struct perf_sample *sample,
174632dcd021SJiri Olsa 						 struct evsel *evsel,
1747743eb868SArnaldo Carvalho de Melo 						 struct machine *machine)
17480a02ad93SIngo Molnar {
1749a116e05dSArnaldo Carvalho de Melo 	int err = 0;
1750a80deb62SArnaldo Carvalho de Melo 
1751744a9719SArnaldo Carvalho de Melo 	if (evsel->handler != NULL) {
1752744a9719SArnaldo Carvalho de Melo 		tracepoint_handler f = evsel->handler;
17532b7fcbc5SArnaldo Carvalho de Melo 		err = f(tool, evsel, sample, machine);
1754ee29be62SArnaldo Carvalho de Melo 	}
17550a02ad93SIngo Molnar 
1756a116e05dSArnaldo Carvalho de Melo 	return err;
17570a02ad93SIngo Molnar }
17580a02ad93SIngo Molnar 
175999a3c3a9SChangbin Du static int perf_sched__process_comm(struct perf_tool *tool __maybe_unused,
176099a3c3a9SChangbin Du 				    union perf_event *event,
176199a3c3a9SChangbin Du 				    struct perf_sample *sample,
176299a3c3a9SChangbin Du 				    struct machine *machine)
176399a3c3a9SChangbin Du {
176499a3c3a9SChangbin Du 	struct thread *thread;
176599a3c3a9SChangbin Du 	struct thread_runtime *tr;
176699a3c3a9SChangbin Du 	int err;
176799a3c3a9SChangbin Du 
176899a3c3a9SChangbin Du 	err = perf_event__process_comm(tool, event, sample, machine);
176999a3c3a9SChangbin Du 	if (err)
177099a3c3a9SChangbin Du 		return err;
177199a3c3a9SChangbin Du 
177299a3c3a9SChangbin Du 	thread = machine__find_thread(machine, sample->pid, sample->tid);
177399a3c3a9SChangbin Du 	if (!thread) {
177499a3c3a9SChangbin Du 		pr_err("Internal error: can't find thread\n");
177599a3c3a9SChangbin Du 		return -1;
177699a3c3a9SChangbin Du 	}
177799a3c3a9SChangbin Du 
177899a3c3a9SChangbin Du 	tr = thread__get_runtime(thread);
177999a3c3a9SChangbin Du 	if (tr == NULL) {
178099a3c3a9SChangbin Du 		thread__put(thread);
178199a3c3a9SChangbin Du 		return -1;
178299a3c3a9SChangbin Du 	}
178399a3c3a9SChangbin Du 
178499a3c3a9SChangbin Du 	tr->comm_changed = true;
178599a3c3a9SChangbin Du 	thread__put(thread);
178699a3c3a9SChangbin Du 
178799a3c3a9SChangbin Du 	return 0;
178899a3c3a9SChangbin Du }
178999a3c3a9SChangbin Du 
1790ae536acfSArnaldo Carvalho de Melo static int perf_sched__read_events(struct perf_sched *sched)
17910a02ad93SIngo Molnar {
179232dcd021SJiri Olsa 	const struct evsel_str_handler handlers[] = {
1793ee29be62SArnaldo Carvalho de Melo 		{ "sched:sched_switch",	      process_sched_switch_event, },
1794ee29be62SArnaldo Carvalho de Melo 		{ "sched:sched_stat_runtime", process_sched_runtime_event, },
1795ee29be62SArnaldo Carvalho de Melo 		{ "sched:sched_wakeup",	      process_sched_wakeup_event, },
1796ee29be62SArnaldo Carvalho de Melo 		{ "sched:sched_wakeup_new",   process_sched_wakeup_event, },
1797ee29be62SArnaldo Carvalho de Melo 		{ "sched:sched_migrate_task", process_sched_migrate_task_event, },
1798ee29be62SArnaldo Carvalho de Melo 	};
1799da378962SArnaldo Carvalho de Melo 	struct perf_session *session;
18008ceb41d7SJiri Olsa 	struct perf_data data = {
1801f5fc1412SJiri Olsa 		.path  = input_name,
1802f5fc1412SJiri Olsa 		.mode  = PERF_DATA_MODE_READ,
1803f0dd330fSYunlong Song 		.force = sched->force,
1804f5fc1412SJiri Olsa 	};
1805ae536acfSArnaldo Carvalho de Melo 	int rc = -1;
1806da378962SArnaldo Carvalho de Melo 
18072681bd85SNamhyung Kim 	session = perf_session__new(&data, &sched->tool);
18086ef81c55SMamatha Inamdar 	if (IS_ERR(session)) {
18096ef81c55SMamatha Inamdar 		pr_debug("Error creating perf session");
18106ef81c55SMamatha Inamdar 		return PTR_ERR(session);
1811a116e05dSArnaldo Carvalho de Melo 	}
181294c744b6SArnaldo Carvalho de Melo 
18130a7e6d1bSNamhyung Kim 	symbol__init(&session->header.env);
181404934106SNamhyung Kim 
1815a116e05dSArnaldo Carvalho de Melo 	if (perf_session__set_tracepoints_handlers(session, handlers))
1816a116e05dSArnaldo Carvalho de Melo 		goto out_delete;
1817ee29be62SArnaldo Carvalho de Melo 
1818cee75ac7SArnaldo Carvalho de Melo 	if (perf_session__has_traces(session, "record -R")) {
1819b7b61cbeSArnaldo Carvalho de Melo 		int err = perf_session__process_events(session);
1820a116e05dSArnaldo Carvalho de Melo 		if (err) {
1821a116e05dSArnaldo Carvalho de Melo 			pr_err("Failed to process events, error %d", err);
1822a116e05dSArnaldo Carvalho de Melo 			goto out_delete;
1823a116e05dSArnaldo Carvalho de Melo 		}
18244c09bafaSJiri Olsa 
182575be989aSArnaldo Carvalho de Melo 		sched->nr_events      = session->evlist->stats.nr_events[0];
182675be989aSArnaldo Carvalho de Melo 		sched->nr_lost_events = session->evlist->stats.total_lost;
182775be989aSArnaldo Carvalho de Melo 		sched->nr_lost_chunks = session->evlist->stats.nr_events[PERF_RECORD_LOST];
1828cee75ac7SArnaldo Carvalho de Melo 	}
1829d549c769SArnaldo Carvalho de Melo 
1830ae536acfSArnaldo Carvalho de Melo 	rc = 0;
1831a116e05dSArnaldo Carvalho de Melo out_delete:
1832a116e05dSArnaldo Carvalho de Melo 	perf_session__delete(session);
1833ae536acfSArnaldo Carvalho de Melo 	return rc;
18340a02ad93SIngo Molnar }
18350a02ad93SIngo Molnar 
183649394a2aSDavid Ahern /*
183749394a2aSDavid Ahern  * scheduling times are printed as msec.usec
183849394a2aSDavid Ahern  */
183949394a2aSDavid Ahern static inline void print_sched_time(unsigned long long nsecs, int width)
184049394a2aSDavid Ahern {
184149394a2aSDavid Ahern 	unsigned long msecs;
184249394a2aSDavid Ahern 	unsigned long usecs;
184349394a2aSDavid Ahern 
184449394a2aSDavid Ahern 	msecs  = nsecs / NSEC_PER_MSEC;
184549394a2aSDavid Ahern 	nsecs -= msecs * NSEC_PER_MSEC;
184649394a2aSDavid Ahern 	usecs  = nsecs / NSEC_PER_USEC;
184749394a2aSDavid Ahern 	printf("%*lu.%03lu ", width, msecs, usecs);
184849394a2aSDavid Ahern }
184949394a2aSDavid Ahern 
185049394a2aSDavid Ahern /*
185149394a2aSDavid Ahern  * returns runtime data for event, allocating memory for it the
185249394a2aSDavid Ahern  * first time it is used.
185349394a2aSDavid Ahern  */
18543b7313f2SArnaldo Carvalho de Melo static struct evsel_runtime *evsel__get_runtime(struct evsel *evsel)
185549394a2aSDavid Ahern {
185649394a2aSDavid Ahern 	struct evsel_runtime *r = evsel->priv;
185749394a2aSDavid Ahern 
185849394a2aSDavid Ahern 	if (r == NULL) {
185949394a2aSDavid Ahern 		r = zalloc(sizeof(struct evsel_runtime));
186049394a2aSDavid Ahern 		evsel->priv = r;
186149394a2aSDavid Ahern 	}
186249394a2aSDavid Ahern 
186349394a2aSDavid Ahern 	return r;
186449394a2aSDavid Ahern }
186549394a2aSDavid Ahern 
186649394a2aSDavid Ahern /*
186749394a2aSDavid Ahern  * save last time event was seen per cpu
186849394a2aSDavid Ahern  */
18693b7313f2SArnaldo Carvalho de Melo static void evsel__save_time(struct evsel *evsel, u64 timestamp, u32 cpu)
187049394a2aSDavid Ahern {
18713b7313f2SArnaldo Carvalho de Melo 	struct evsel_runtime *r = evsel__get_runtime(evsel);
187249394a2aSDavid Ahern 
187349394a2aSDavid Ahern 	if (r == NULL)
187449394a2aSDavid Ahern 		return;
187549394a2aSDavid Ahern 
187649394a2aSDavid Ahern 	if ((cpu >= r->ncpu) || (r->last_time == NULL)) {
187749394a2aSDavid Ahern 		int i, n = __roundup_pow_of_two(cpu+1);
187849394a2aSDavid Ahern 		void *p = r->last_time;
187949394a2aSDavid Ahern 
188049394a2aSDavid Ahern 		p = realloc(r->last_time, n * sizeof(u64));
188149394a2aSDavid Ahern 		if (!p)
188249394a2aSDavid Ahern 			return;
188349394a2aSDavid Ahern 
188449394a2aSDavid Ahern 		r->last_time = p;
188549394a2aSDavid Ahern 		for (i = r->ncpu; i < n; ++i)
188649394a2aSDavid Ahern 			r->last_time[i] = (u64) 0;
188749394a2aSDavid Ahern 
188849394a2aSDavid Ahern 		r->ncpu = n;
188949394a2aSDavid Ahern 	}
189049394a2aSDavid Ahern 
189149394a2aSDavid Ahern 	r->last_time[cpu] = timestamp;
189249394a2aSDavid Ahern }
189349394a2aSDavid Ahern 
189449394a2aSDavid Ahern /* returns last time this event was seen on the given cpu */
18953b7313f2SArnaldo Carvalho de Melo static u64 evsel__get_time(struct evsel *evsel, u32 cpu)
189649394a2aSDavid Ahern {
18973b7313f2SArnaldo Carvalho de Melo 	struct evsel_runtime *r = evsel__get_runtime(evsel);
189849394a2aSDavid Ahern 
189949394a2aSDavid Ahern 	if ((r == NULL) || (r->last_time == NULL) || (cpu >= r->ncpu))
190049394a2aSDavid Ahern 		return 0;
190149394a2aSDavid Ahern 
190249394a2aSDavid Ahern 	return r->last_time[cpu];
190349394a2aSDavid Ahern }
190449394a2aSDavid Ahern 
19059b8087d7SNamhyung Kim static int comm_width = 30;
190649394a2aSDavid Ahern 
190749394a2aSDavid Ahern static char *timehist_get_commstr(struct thread *thread)
190849394a2aSDavid Ahern {
190949394a2aSDavid Ahern 	static char str[32];
191049394a2aSDavid Ahern 	const char *comm = thread__comm_str(thread);
191149394a2aSDavid Ahern 	pid_t tid = thread->tid;
191249394a2aSDavid Ahern 	pid_t pid = thread->pid_;
191349394a2aSDavid Ahern 	int n;
191449394a2aSDavid Ahern 
191549394a2aSDavid Ahern 	if (pid == 0)
191649394a2aSDavid Ahern 		n = scnprintf(str, sizeof(str), "%s", comm);
191749394a2aSDavid Ahern 
191849394a2aSDavid Ahern 	else if (tid != pid)
191949394a2aSDavid Ahern 		n = scnprintf(str, sizeof(str), "%s[%d/%d]", comm, tid, pid);
192049394a2aSDavid Ahern 
192149394a2aSDavid Ahern 	else
192249394a2aSDavid Ahern 		n = scnprintf(str, sizeof(str), "%s[%d]", comm, tid);
192349394a2aSDavid Ahern 
192449394a2aSDavid Ahern 	if (n > comm_width)
192549394a2aSDavid Ahern 		comm_width = n;
192649394a2aSDavid Ahern 
192749394a2aSDavid Ahern 	return str;
192849394a2aSDavid Ahern }
192949394a2aSDavid Ahern 
1930a407b067SDavid Ahern static void timehist_header(struct perf_sched *sched)
193149394a2aSDavid Ahern {
1932a407b067SDavid Ahern 	u32 ncpus = sched->max_cpu + 1;
1933a407b067SDavid Ahern 	u32 i, j;
1934a407b067SDavid Ahern 
193549394a2aSDavid Ahern 	printf("%15s %6s ", "time", "cpu");
193649394a2aSDavid Ahern 
1937a407b067SDavid Ahern 	if (sched->show_cpu_visual) {
1938a407b067SDavid Ahern 		printf(" ");
1939a407b067SDavid Ahern 		for (i = 0, j = 0; i < ncpus; ++i) {
1940a407b067SDavid Ahern 			printf("%x", j++);
1941a407b067SDavid Ahern 			if (j > 15)
1942a407b067SDavid Ahern 				j = 0;
1943a407b067SDavid Ahern 		}
1944a407b067SDavid Ahern 		printf(" ");
1945a407b067SDavid Ahern 	}
1946a407b067SDavid Ahern 
19470e6758e8SNamhyung Kim 	printf(" %-*s  %9s  %9s  %9s", comm_width,
194849394a2aSDavid Ahern 		"task name", "wait time", "sch delay", "run time");
194949394a2aSDavid Ahern 
1950414e050cSNamhyung Kim 	if (sched->show_state)
1951414e050cSNamhyung Kim 		printf("  %s", "state");
1952414e050cSNamhyung Kim 
195349394a2aSDavid Ahern 	printf("\n");
195449394a2aSDavid Ahern 
195549394a2aSDavid Ahern 	/*
195649394a2aSDavid Ahern 	 * units row
195749394a2aSDavid Ahern 	 */
195849394a2aSDavid Ahern 	printf("%15s %-6s ", "", "");
195949394a2aSDavid Ahern 
1960a407b067SDavid Ahern 	if (sched->show_cpu_visual)
1961a407b067SDavid Ahern 		printf(" %*s ", ncpus, "");
1962a407b067SDavid Ahern 
1963414e050cSNamhyung Kim 	printf(" %-*s  %9s  %9s  %9s", comm_width,
19640e6758e8SNamhyung Kim 	       "[tid/pid]", "(msec)", "(msec)", "(msec)");
196549394a2aSDavid Ahern 
1966414e050cSNamhyung Kim 	if (sched->show_state)
1967414e050cSNamhyung Kim 		printf("  %5s", "");
1968414e050cSNamhyung Kim 
1969414e050cSNamhyung Kim 	printf("\n");
1970414e050cSNamhyung Kim 
197149394a2aSDavid Ahern 	/*
197249394a2aSDavid Ahern 	 * separator
197349394a2aSDavid Ahern 	 */
197449394a2aSDavid Ahern 	printf("%.15s %.6s ", graph_dotted_line, graph_dotted_line);
197549394a2aSDavid Ahern 
1976a407b067SDavid Ahern 	if (sched->show_cpu_visual)
1977a407b067SDavid Ahern 		printf(" %.*s ", ncpus, graph_dotted_line);
1978a407b067SDavid Ahern 
19790e6758e8SNamhyung Kim 	printf(" %.*s  %.9s  %.9s  %.9s", comm_width,
198049394a2aSDavid Ahern 		graph_dotted_line, graph_dotted_line, graph_dotted_line,
198149394a2aSDavid Ahern 		graph_dotted_line);
198249394a2aSDavid Ahern 
1983414e050cSNamhyung Kim 	if (sched->show_state)
1984414e050cSNamhyung Kim 		printf("  %.5s", graph_dotted_line);
1985414e050cSNamhyung Kim 
198649394a2aSDavid Ahern 	printf("\n");
198749394a2aSDavid Ahern }
198849394a2aSDavid Ahern 
1989414e050cSNamhyung Kim static char task_state_char(struct thread *thread, int state)
1990414e050cSNamhyung Kim {
1991414e050cSNamhyung Kim 	static const char state_to_char[] = TASK_STATE_TO_CHAR_STR;
1992414e050cSNamhyung Kim 	unsigned bit = state ? ffs(state) : 0;
1993414e050cSNamhyung Kim 
1994414e050cSNamhyung Kim 	/* 'I' for idle */
1995414e050cSNamhyung Kim 	if (thread->tid == 0)
1996414e050cSNamhyung Kim 		return 'I';
1997414e050cSNamhyung Kim 
1998414e050cSNamhyung Kim 	return bit < sizeof(state_to_char) - 1 ? state_to_char[bit] : '?';
1999414e050cSNamhyung Kim }
2000414e050cSNamhyung Kim 
2001fc1469f1SDavid Ahern static void timehist_print_sample(struct perf_sched *sched,
200232dcd021SJiri Olsa 				  struct evsel *evsel,
2003fc1469f1SDavid Ahern 				  struct perf_sample *sample,
20046c973c90SDavid Ahern 				  struct addr_location *al,
2005853b7407SDavid Ahern 				  struct thread *thread,
2006414e050cSNamhyung Kim 				  u64 t, int state)
200749394a2aSDavid Ahern {
200849394a2aSDavid Ahern 	struct thread_runtime *tr = thread__priv(thread);
2009efc0cdc9SArnaldo Carvalho de Melo 	const char *next_comm = evsel__strval(evsel, sample, "next_comm");
2010efc0cdc9SArnaldo Carvalho de Melo 	const u32 next_pid = evsel__intval(evsel, sample, "next_pid");
2011a407b067SDavid Ahern 	u32 max_cpus = sched->max_cpu + 1;
201249394a2aSDavid Ahern 	char tstr[64];
2013292c4a8fSBrendan Gregg 	char nstr[30];
2014941bdea7SNamhyung Kim 	u64 wait_time;
201549394a2aSDavid Ahern 
2016c30d630dSDavid Ahern 	if (cpu_list && !test_bit(sample->cpu, cpu_bitmap))
2017c30d630dSDavid Ahern 		return;
2018c30d630dSDavid Ahern 
2019853b7407SDavid Ahern 	timestamp__scnprintf_usec(t, tstr, sizeof(tstr));
202049394a2aSDavid Ahern 	printf("%15s [%04d] ", tstr, sample->cpu);
202149394a2aSDavid Ahern 
2022a407b067SDavid Ahern 	if (sched->show_cpu_visual) {
2023a407b067SDavid Ahern 		u32 i;
2024a407b067SDavid Ahern 		char c;
2025a407b067SDavid Ahern 
2026a407b067SDavid Ahern 		printf(" ");
2027a407b067SDavid Ahern 		for (i = 0; i < max_cpus; ++i) {
2028a407b067SDavid Ahern 			/* flag idle times with 'i'; others are sched events */
2029a407b067SDavid Ahern 			if (i == sample->cpu)
2030a407b067SDavid Ahern 				c = (thread->tid == 0) ? 'i' : 's';
2031a407b067SDavid Ahern 			else
2032a407b067SDavid Ahern 				c = ' ';
2033a407b067SDavid Ahern 			printf("%c", c);
2034a407b067SDavid Ahern 		}
2035a407b067SDavid Ahern 		printf(" ");
2036a407b067SDavid Ahern 	}
2037a407b067SDavid Ahern 
203849394a2aSDavid Ahern 	printf(" %-*s ", comm_width, timehist_get_commstr(thread));
203949394a2aSDavid Ahern 
2040941bdea7SNamhyung Kim 	wait_time = tr->dt_sleep + tr->dt_iowait + tr->dt_preempt;
2041941bdea7SNamhyung Kim 	print_sched_time(wait_time, 6);
2042941bdea7SNamhyung Kim 
204349394a2aSDavid Ahern 	print_sched_time(tr->dt_delay, 6);
204449394a2aSDavid Ahern 	print_sched_time(tr->dt_run, 6);
2045fc1469f1SDavid Ahern 
2046414e050cSNamhyung Kim 	if (sched->show_state)
2047414e050cSNamhyung Kim 		printf(" %5c ", task_state_char(thread, state));
2048414e050cSNamhyung Kim 
2049292c4a8fSBrendan Gregg 	if (sched->show_next) {
2050292c4a8fSBrendan Gregg 		snprintf(nstr, sizeof(nstr), "next: %s[%d]", next_comm, next_pid);
2051292c4a8fSBrendan Gregg 		printf(" %-*s", comm_width, nstr);
2052292c4a8fSBrendan Gregg 	}
2053292c4a8fSBrendan Gregg 
2054292c4a8fSBrendan Gregg 	if (sched->show_wakeups && !sched->show_next)
2055fc1469f1SDavid Ahern 		printf("  %-*s", comm_width, "");
2056fc1469f1SDavid Ahern 
20576c973c90SDavid Ahern 	if (thread->tid == 0)
20586c973c90SDavid Ahern 		goto out;
20596c973c90SDavid Ahern 
20606c973c90SDavid Ahern 	if (sched->show_callchain)
20616c973c90SDavid Ahern 		printf("  ");
20626c973c90SDavid Ahern 
20636c973c90SDavid Ahern 	sample__fprintf_sym(sample, al, 0,
20646c973c90SDavid Ahern 			    EVSEL__PRINT_SYM | EVSEL__PRINT_ONELINE |
20652d9bbf6eSNamhyung Kim 			    EVSEL__PRINT_CALLCHAIN_ARROW |
20662d9bbf6eSNamhyung Kim 			    EVSEL__PRINT_SKIP_IGNORED,
20679620bc36SArnaldo Carvalho de Melo 			    &callchain_cursor, symbol_conf.bt_stop_list,  stdout);
20686c973c90SDavid Ahern 
20696c973c90SDavid Ahern out:
207049394a2aSDavid Ahern 	printf("\n");
207149394a2aSDavid Ahern }
207249394a2aSDavid Ahern 
207349394a2aSDavid Ahern /*
207449394a2aSDavid Ahern  * Explanation of delta-time stats:
207549394a2aSDavid Ahern  *
207649394a2aSDavid Ahern  *            t = time of current schedule out event
207749394a2aSDavid Ahern  *        tprev = time of previous sched out event
207849394a2aSDavid Ahern  *                also time of schedule-in event for current task
207949394a2aSDavid Ahern  *    last_time = time of last sched change event for current task
208049394a2aSDavid Ahern  *                (i.e, time process was last scheduled out)
208149394a2aSDavid Ahern  * ready_to_run = time of wakeup for current task
208249394a2aSDavid Ahern  *
208349394a2aSDavid Ahern  * -----|------------|------------|------------|------
208449394a2aSDavid Ahern  *    last         ready        tprev          t
208549394a2aSDavid Ahern  *    time         to run
208649394a2aSDavid Ahern  *
208749394a2aSDavid Ahern  *      |-------- dt_wait --------|
208849394a2aSDavid Ahern  *                   |- dt_delay -|-- dt_run --|
208949394a2aSDavid Ahern  *
209049394a2aSDavid Ahern  *   dt_run = run time of current task
209149394a2aSDavid Ahern  *  dt_wait = time between last schedule out event for task and tprev
209249394a2aSDavid Ahern  *            represents time spent off the cpu
209349394a2aSDavid Ahern  * dt_delay = time between wakeup and schedule-in of task
209449394a2aSDavid Ahern  */
209549394a2aSDavid Ahern 
209649394a2aSDavid Ahern static void timehist_update_runtime_stats(struct thread_runtime *r,
209749394a2aSDavid Ahern 					 u64 t, u64 tprev)
209849394a2aSDavid Ahern {
209949394a2aSDavid Ahern 	r->dt_delay   = 0;
2100941bdea7SNamhyung Kim 	r->dt_sleep   = 0;
2101941bdea7SNamhyung Kim 	r->dt_iowait  = 0;
2102941bdea7SNamhyung Kim 	r->dt_preempt = 0;
210349394a2aSDavid Ahern 	r->dt_run     = 0;
2104941bdea7SNamhyung Kim 
210549394a2aSDavid Ahern 	if (tprev) {
210649394a2aSDavid Ahern 		r->dt_run = t - tprev;
210749394a2aSDavid Ahern 		if (r->ready_to_run) {
210849394a2aSDavid Ahern 			if (r->ready_to_run > tprev)
210949394a2aSDavid Ahern 				pr_debug("time travel: wakeup time for task > previous sched_switch event\n");
211049394a2aSDavid Ahern 			else
211149394a2aSDavid Ahern 				r->dt_delay = tprev - r->ready_to_run;
211249394a2aSDavid Ahern 		}
211349394a2aSDavid Ahern 
211449394a2aSDavid Ahern 		if (r->last_time > tprev)
211549394a2aSDavid Ahern 			pr_debug("time travel: last sched out time for task > previous sched_switch event\n");
2116941bdea7SNamhyung Kim 		else if (r->last_time) {
2117941bdea7SNamhyung Kim 			u64 dt_wait = tprev - r->last_time;
2118941bdea7SNamhyung Kim 
2119941bdea7SNamhyung Kim 			if (r->last_state == TASK_RUNNING)
2120941bdea7SNamhyung Kim 				r->dt_preempt = dt_wait;
2121941bdea7SNamhyung Kim 			else if (r->last_state == TASK_UNINTERRUPTIBLE)
2122941bdea7SNamhyung Kim 				r->dt_iowait = dt_wait;
2123941bdea7SNamhyung Kim 			else
2124941bdea7SNamhyung Kim 				r->dt_sleep = dt_wait;
2125941bdea7SNamhyung Kim 		}
212649394a2aSDavid Ahern 	}
212749394a2aSDavid Ahern 
212849394a2aSDavid Ahern 	update_stats(&r->run_stats, r->dt_run);
2129587782c5SNamhyung Kim 
213049394a2aSDavid Ahern 	r->total_run_time     += r->dt_run;
2131587782c5SNamhyung Kim 	r->total_delay_time   += r->dt_delay;
2132587782c5SNamhyung Kim 	r->total_sleep_time   += r->dt_sleep;
2133587782c5SNamhyung Kim 	r->total_iowait_time  += r->dt_iowait;
2134587782c5SNamhyung Kim 	r->total_preempt_time += r->dt_preempt;
213549394a2aSDavid Ahern }
213649394a2aSDavid Ahern 
213796039c7cSNamhyung Kim static bool is_idle_sample(struct perf_sample *sample,
213832dcd021SJiri Olsa 			   struct evsel *evsel)
213996039c7cSNamhyung Kim {
214096039c7cSNamhyung Kim 	/* pid 0 == swapper == idle task */
21418ab2e96dSArnaldo Carvalho de Melo 	if (strcmp(evsel__name(evsel), "sched:sched_switch") == 0)
2142efc0cdc9SArnaldo Carvalho de Melo 		return evsel__intval(evsel, sample, "prev_pid") == 0;
214396039c7cSNamhyung Kim 
214496039c7cSNamhyung Kim 	return sample->pid == 0;
214596039c7cSNamhyung Kim }
214696039c7cSNamhyung Kim 
214796039c7cSNamhyung Kim static void save_task_callchain(struct perf_sched *sched,
21486c973c90SDavid Ahern 				struct perf_sample *sample,
214932dcd021SJiri Olsa 				struct evsel *evsel,
21506c973c90SDavid Ahern 				struct machine *machine)
215149394a2aSDavid Ahern {
21526c973c90SDavid Ahern 	struct callchain_cursor *cursor = &callchain_cursor;
215396039c7cSNamhyung Kim 	struct thread *thread;
21546c973c90SDavid Ahern 
21556c973c90SDavid Ahern 	/* want main thread for process - has maps */
21566c973c90SDavid Ahern 	thread = machine__findnew_thread(machine, sample->pid, sample->pid);
21576c973c90SDavid Ahern 	if (thread == NULL) {
21586c973c90SDavid Ahern 		pr_debug("Failed to get thread for pid %d.\n", sample->pid);
215996039c7cSNamhyung Kim 		return;
21606c973c90SDavid Ahern 	}
21616c973c90SDavid Ahern 
21624c50563dSArnaldo Carvalho de Melo 	if (!sched->show_callchain || sample->callchain == NULL)
216396039c7cSNamhyung Kim 		return;
21646c973c90SDavid Ahern 
21656c973c90SDavid Ahern 	if (thread__resolve_callchain(thread, cursor, evsel, sample,
21668388deb3SNamhyung Kim 				      NULL, NULL, sched->max_stack + 2) != 0) {
2167bb963e16SNamhyung Kim 		if (verbose > 0)
216862d94b00SArnaldo Carvalho de Melo 			pr_err("Failed to resolve callchain. Skipping\n");
21696c973c90SDavid Ahern 
217096039c7cSNamhyung Kim 		return;
21716c973c90SDavid Ahern 	}
2172cdeb01bfSNamhyung Kim 
21736c973c90SDavid Ahern 	callchain_cursor_commit(cursor);
2174cdeb01bfSNamhyung Kim 
2175cdeb01bfSNamhyung Kim 	while (true) {
2176cdeb01bfSNamhyung Kim 		struct callchain_cursor_node *node;
2177cdeb01bfSNamhyung Kim 		struct symbol *sym;
2178cdeb01bfSNamhyung Kim 
2179cdeb01bfSNamhyung Kim 		node = callchain_cursor_current(cursor);
2180cdeb01bfSNamhyung Kim 		if (node == NULL)
2181cdeb01bfSNamhyung Kim 			break;
2182cdeb01bfSNamhyung Kim 
21835f0fef8aSArnaldo Carvalho de Melo 		sym = node->ms.sym;
2184a7c3899cSArnaldo Carvalho de Melo 		if (sym) {
2185cdeb01bfSNamhyung Kim 			if (!strcmp(sym->name, "schedule") ||
2186cdeb01bfSNamhyung Kim 			    !strcmp(sym->name, "__schedule") ||
2187cdeb01bfSNamhyung Kim 			    !strcmp(sym->name, "preempt_schedule"))
2188cdeb01bfSNamhyung Kim 				sym->ignore = 1;
2189cdeb01bfSNamhyung Kim 		}
2190cdeb01bfSNamhyung Kim 
2191cdeb01bfSNamhyung Kim 		callchain_cursor_advance(cursor);
2192cdeb01bfSNamhyung Kim 	}
219349394a2aSDavid Ahern }
219449394a2aSDavid Ahern 
21953bc2fa9cSNamhyung Kim static int init_idle_thread(struct thread *thread)
21963bc2fa9cSNamhyung Kim {
21973bc2fa9cSNamhyung Kim 	struct idle_thread_runtime *itr;
21983bc2fa9cSNamhyung Kim 
21993bc2fa9cSNamhyung Kim 	thread__set_comm(thread, idle_comm, 0);
22003bc2fa9cSNamhyung Kim 
22013bc2fa9cSNamhyung Kim 	itr = zalloc(sizeof(*itr));
22023bc2fa9cSNamhyung Kim 	if (itr == NULL)
22033bc2fa9cSNamhyung Kim 		return -ENOMEM;
22043bc2fa9cSNamhyung Kim 
22053bc2fa9cSNamhyung Kim 	init_stats(&itr->tr.run_stats);
22063bc2fa9cSNamhyung Kim 	callchain_init(&itr->callchain);
22073bc2fa9cSNamhyung Kim 	callchain_cursor_reset(&itr->cursor);
22083bc2fa9cSNamhyung Kim 	thread__set_priv(thread, itr);
22093bc2fa9cSNamhyung Kim 
22103bc2fa9cSNamhyung Kim 	return 0;
22113bc2fa9cSNamhyung Kim }
22123bc2fa9cSNamhyung Kim 
221349394a2aSDavid Ahern /*
221449394a2aSDavid Ahern  * Track idle stats per cpu by maintaining a local thread
221549394a2aSDavid Ahern  * struct for the idle task on each cpu.
221649394a2aSDavid Ahern  */
221749394a2aSDavid Ahern static int init_idle_threads(int ncpu)
221849394a2aSDavid Ahern {
22193bc2fa9cSNamhyung Kim 	int i, ret;
222049394a2aSDavid Ahern 
222149394a2aSDavid Ahern 	idle_threads = zalloc(ncpu * sizeof(struct thread *));
222249394a2aSDavid Ahern 	if (!idle_threads)
222349394a2aSDavid Ahern 		return -ENOMEM;
222449394a2aSDavid Ahern 
2225b336352bSNamhyung Kim 	idle_max_cpu = ncpu;
222649394a2aSDavid Ahern 
222749394a2aSDavid Ahern 	/* allocate the actual thread struct if needed */
222849394a2aSDavid Ahern 	for (i = 0; i < ncpu; ++i) {
222949394a2aSDavid Ahern 		idle_threads[i] = thread__new(0, 0);
223049394a2aSDavid Ahern 		if (idle_threads[i] == NULL)
223149394a2aSDavid Ahern 			return -ENOMEM;
223249394a2aSDavid Ahern 
22333bc2fa9cSNamhyung Kim 		ret = init_idle_thread(idle_threads[i]);
22343bc2fa9cSNamhyung Kim 		if (ret < 0)
22353bc2fa9cSNamhyung Kim 			return ret;
223649394a2aSDavid Ahern 	}
223749394a2aSDavid Ahern 
223849394a2aSDavid Ahern 	return 0;
223949394a2aSDavid Ahern }
224049394a2aSDavid Ahern 
224149394a2aSDavid Ahern static void free_idle_threads(void)
224249394a2aSDavid Ahern {
224349394a2aSDavid Ahern 	int i;
224449394a2aSDavid Ahern 
224549394a2aSDavid Ahern 	if (idle_threads == NULL)
224649394a2aSDavid Ahern 		return;
224749394a2aSDavid Ahern 
2248b336352bSNamhyung Kim 	for (i = 0; i < idle_max_cpu; ++i) {
224949394a2aSDavid Ahern 		if ((idle_threads[i]))
225049394a2aSDavid Ahern 			thread__delete(idle_threads[i]);
225149394a2aSDavid Ahern 	}
225249394a2aSDavid Ahern 
225349394a2aSDavid Ahern 	free(idle_threads);
225449394a2aSDavid Ahern }
225549394a2aSDavid Ahern 
225649394a2aSDavid Ahern static struct thread *get_idle_thread(int cpu)
225749394a2aSDavid Ahern {
225849394a2aSDavid Ahern 	/*
225949394a2aSDavid Ahern 	 * expand/allocate array of pointers to local thread
226049394a2aSDavid Ahern 	 * structs if needed
226149394a2aSDavid Ahern 	 */
226249394a2aSDavid Ahern 	if ((cpu >= idle_max_cpu) || (idle_threads == NULL)) {
226349394a2aSDavid Ahern 		int i, j = __roundup_pow_of_two(cpu+1);
226449394a2aSDavid Ahern 		void *p;
226549394a2aSDavid Ahern 
226649394a2aSDavid Ahern 		p = realloc(idle_threads, j * sizeof(struct thread *));
226749394a2aSDavid Ahern 		if (!p)
226849394a2aSDavid Ahern 			return NULL;
226949394a2aSDavid Ahern 
227049394a2aSDavid Ahern 		idle_threads = (struct thread **) p;
2271b336352bSNamhyung Kim 		for (i = idle_max_cpu; i < j; ++i)
227249394a2aSDavid Ahern 			idle_threads[i] = NULL;
227349394a2aSDavid Ahern 
227449394a2aSDavid Ahern 		idle_max_cpu = j;
227549394a2aSDavid Ahern 	}
227649394a2aSDavid Ahern 
227749394a2aSDavid Ahern 	/* allocate a new thread struct if needed */
227849394a2aSDavid Ahern 	if (idle_threads[cpu] == NULL) {
227949394a2aSDavid Ahern 		idle_threads[cpu] = thread__new(0, 0);
228049394a2aSDavid Ahern 
228149394a2aSDavid Ahern 		if (idle_threads[cpu]) {
22823bc2fa9cSNamhyung Kim 			if (init_idle_thread(idle_threads[cpu]) < 0)
22833bc2fa9cSNamhyung Kim 				return NULL;
228449394a2aSDavid Ahern 		}
228549394a2aSDavid Ahern 	}
228649394a2aSDavid Ahern 
228749394a2aSDavid Ahern 	return idle_threads[cpu];
228849394a2aSDavid Ahern }
228949394a2aSDavid Ahern 
22904c50563dSArnaldo Carvalho de Melo static void save_idle_callchain(struct perf_sched *sched,
22914c50563dSArnaldo Carvalho de Melo 				struct idle_thread_runtime *itr,
2292699b5b92SNamhyung Kim 				struct perf_sample *sample)
2293699b5b92SNamhyung Kim {
22944c50563dSArnaldo Carvalho de Melo 	if (!sched->show_callchain || sample->callchain == NULL)
2295699b5b92SNamhyung Kim 		return;
2296699b5b92SNamhyung Kim 
2297699b5b92SNamhyung Kim 	callchain_cursor__copy(&itr->cursor, &callchain_cursor);
2298699b5b92SNamhyung Kim }
2299699b5b92SNamhyung Kim 
23006c973c90SDavid Ahern static struct thread *timehist_get_thread(struct perf_sched *sched,
23016c973c90SDavid Ahern 					  struct perf_sample *sample,
230249394a2aSDavid Ahern 					  struct machine *machine,
230332dcd021SJiri Olsa 					  struct evsel *evsel)
230449394a2aSDavid Ahern {
230549394a2aSDavid Ahern 	struct thread *thread;
230649394a2aSDavid Ahern 
230796039c7cSNamhyung Kim 	if (is_idle_sample(sample, evsel)) {
230849394a2aSDavid Ahern 		thread = get_idle_thread(sample->cpu);
230949394a2aSDavid Ahern 		if (thread == NULL)
231049394a2aSDavid Ahern 			pr_err("Failed to get idle thread for cpu %d.\n", sample->cpu);
231149394a2aSDavid Ahern 
231249394a2aSDavid Ahern 	} else {
23135d92d96aSNamhyung Kim 		/* there were samples with tid 0 but non-zero pid */
23145d92d96aSNamhyung Kim 		thread = machine__findnew_thread(machine, sample->pid,
23155d92d96aSNamhyung Kim 						 sample->tid ?: sample->pid);
231649394a2aSDavid Ahern 		if (thread == NULL) {
231749394a2aSDavid Ahern 			pr_debug("Failed to get thread for tid %d. skipping sample.\n",
231849394a2aSDavid Ahern 				 sample->tid);
231949394a2aSDavid Ahern 		}
232096039c7cSNamhyung Kim 
232196039c7cSNamhyung Kim 		save_task_callchain(sched, sample, evsel, machine);
2322699b5b92SNamhyung Kim 		if (sched->idle_hist) {
2323699b5b92SNamhyung Kim 			struct thread *idle;
2324699b5b92SNamhyung Kim 			struct idle_thread_runtime *itr;
2325699b5b92SNamhyung Kim 
2326699b5b92SNamhyung Kim 			idle = get_idle_thread(sample->cpu);
2327699b5b92SNamhyung Kim 			if (idle == NULL) {
2328699b5b92SNamhyung Kim 				pr_err("Failed to get idle thread for cpu %d.\n", sample->cpu);
2329699b5b92SNamhyung Kim 				return NULL;
2330699b5b92SNamhyung Kim 			}
2331699b5b92SNamhyung Kim 
2332699b5b92SNamhyung Kim 			itr = thread__priv(idle);
2333699b5b92SNamhyung Kim 			if (itr == NULL)
2334699b5b92SNamhyung Kim 				return NULL;
2335699b5b92SNamhyung Kim 
2336699b5b92SNamhyung Kim 			itr->last_thread = thread;
2337699b5b92SNamhyung Kim 
2338699b5b92SNamhyung Kim 			/* copy task callchain when entering to idle */
2339efc0cdc9SArnaldo Carvalho de Melo 			if (evsel__intval(evsel, sample, "next_pid") == 0)
23404c50563dSArnaldo Carvalho de Melo 				save_idle_callchain(sched, itr, sample);
2341699b5b92SNamhyung Kim 		}
234249394a2aSDavid Ahern 	}
234349394a2aSDavid Ahern 
234449394a2aSDavid Ahern 	return thread;
234549394a2aSDavid Ahern }
234649394a2aSDavid Ahern 
234752df138cSDavid Ahern static bool timehist_skip_sample(struct perf_sched *sched,
2348a4b2b6f5SNamhyung Kim 				 struct thread *thread,
234932dcd021SJiri Olsa 				 struct evsel *evsel,
2350a4b2b6f5SNamhyung Kim 				 struct perf_sample *sample)
235149394a2aSDavid Ahern {
235249394a2aSDavid Ahern 	bool rc = false;
235349394a2aSDavid Ahern 
235452df138cSDavid Ahern 	if (thread__is_filtered(thread)) {
235549394a2aSDavid Ahern 		rc = true;
235652df138cSDavid Ahern 		sched->skipped_samples++;
235752df138cSDavid Ahern 	}
235849394a2aSDavid Ahern 
2359a4b2b6f5SNamhyung Kim 	if (sched->idle_hist) {
23608ab2e96dSArnaldo Carvalho de Melo 		if (strcmp(evsel__name(evsel), "sched:sched_switch"))
2361a4b2b6f5SNamhyung Kim 			rc = true;
2362efc0cdc9SArnaldo Carvalho de Melo 		else if (evsel__intval(evsel, sample, "prev_pid") != 0 &&
2363efc0cdc9SArnaldo Carvalho de Melo 			 evsel__intval(evsel, sample, "next_pid") != 0)
2364a4b2b6f5SNamhyung Kim 			rc = true;
2365a4b2b6f5SNamhyung Kim 	}
2366a4b2b6f5SNamhyung Kim 
236749394a2aSDavid Ahern 	return rc;
236849394a2aSDavid Ahern }
236949394a2aSDavid Ahern 
2370fc1469f1SDavid Ahern static void timehist_print_wakeup_event(struct perf_sched *sched,
237132dcd021SJiri Olsa 					struct evsel *evsel,
2372fc1469f1SDavid Ahern 					struct perf_sample *sample,
2373fc1469f1SDavid Ahern 					struct machine *machine,
2374fc1469f1SDavid Ahern 					struct thread *awakened)
2375fc1469f1SDavid Ahern {
2376fc1469f1SDavid Ahern 	struct thread *thread;
2377fc1469f1SDavid Ahern 	char tstr[64];
2378fc1469f1SDavid Ahern 
2379fc1469f1SDavid Ahern 	thread = machine__findnew_thread(machine, sample->pid, sample->tid);
2380fc1469f1SDavid Ahern 	if (thread == NULL)
2381fc1469f1SDavid Ahern 		return;
2382fc1469f1SDavid Ahern 
2383fc1469f1SDavid Ahern 	/* show wakeup unless both awakee and awaker are filtered */
2384a4b2b6f5SNamhyung Kim 	if (timehist_skip_sample(sched, thread, evsel, sample) &&
2385a4b2b6f5SNamhyung Kim 	    timehist_skip_sample(sched, awakened, evsel, sample)) {
2386fc1469f1SDavid Ahern 		return;
2387fc1469f1SDavid Ahern 	}
2388fc1469f1SDavid Ahern 
2389fc1469f1SDavid Ahern 	timestamp__scnprintf_usec(sample->time, tstr, sizeof(tstr));
2390fc1469f1SDavid Ahern 	printf("%15s [%04d] ", tstr, sample->cpu);
2391a407b067SDavid Ahern 	if (sched->show_cpu_visual)
2392a407b067SDavid Ahern 		printf(" %*s ", sched->max_cpu + 1, "");
2393fc1469f1SDavid Ahern 
2394fc1469f1SDavid Ahern 	printf(" %-*s ", comm_width, timehist_get_commstr(thread));
2395fc1469f1SDavid Ahern 
2396fc1469f1SDavid Ahern 	/* dt spacer */
2397fc1469f1SDavid Ahern 	printf("  %9s  %9s  %9s ", "", "", "");
2398fc1469f1SDavid Ahern 
2399fc1469f1SDavid Ahern 	printf("awakened: %s", timehist_get_commstr(awakened));
2400fc1469f1SDavid Ahern 
2401fc1469f1SDavid Ahern 	printf("\n");
2402fc1469f1SDavid Ahern }
2403fc1469f1SDavid Ahern 
2404d566a9c2SDavid Ahern static int timehist_sched_wakeup_ignore(struct perf_tool *tool __maybe_unused,
2405d566a9c2SDavid Ahern 					union perf_event *event __maybe_unused,
2406d566a9c2SDavid Ahern 					struct evsel *evsel __maybe_unused,
2407d566a9c2SDavid Ahern 					struct perf_sample *sample __maybe_unused,
2408d566a9c2SDavid Ahern 					struct machine *machine __maybe_unused)
2409d566a9c2SDavid Ahern {
2410d566a9c2SDavid Ahern 	return 0;
2411d566a9c2SDavid Ahern }
2412d566a9c2SDavid Ahern 
2413fc1469f1SDavid Ahern static int timehist_sched_wakeup_event(struct perf_tool *tool,
241449394a2aSDavid Ahern 				       union perf_event *event __maybe_unused,
241532dcd021SJiri Olsa 				       struct evsel *evsel,
241649394a2aSDavid Ahern 				       struct perf_sample *sample,
241749394a2aSDavid Ahern 				       struct machine *machine)
241849394a2aSDavid Ahern {
2419fc1469f1SDavid Ahern 	struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
242049394a2aSDavid Ahern 	struct thread *thread;
242149394a2aSDavid Ahern 	struct thread_runtime *tr = NULL;
242249394a2aSDavid Ahern 	/* want pid of awakened task not pid in sample */
2423efc0cdc9SArnaldo Carvalho de Melo 	const u32 pid = evsel__intval(evsel, sample, "pid");
242449394a2aSDavid Ahern 
242549394a2aSDavid Ahern 	thread = machine__findnew_thread(machine, 0, pid);
242649394a2aSDavid Ahern 	if (thread == NULL)
242749394a2aSDavid Ahern 		return -1;
242849394a2aSDavid Ahern 
242949394a2aSDavid Ahern 	tr = thread__get_runtime(thread);
243049394a2aSDavid Ahern 	if (tr == NULL)
243149394a2aSDavid Ahern 		return -1;
243249394a2aSDavid Ahern 
243349394a2aSDavid Ahern 	if (tr->ready_to_run == 0)
243449394a2aSDavid Ahern 		tr->ready_to_run = sample->time;
243549394a2aSDavid Ahern 
2436fc1469f1SDavid Ahern 	/* show wakeups if requested */
2437853b7407SDavid Ahern 	if (sched->show_wakeups &&
2438853b7407SDavid Ahern 	    !perf_time__skip_sample(&sched->ptime, sample->time))
2439a4b2b6f5SNamhyung Kim 		timehist_print_wakeup_event(sched, evsel, sample, machine, thread);
2440fc1469f1SDavid Ahern 
244149394a2aSDavid Ahern 	return 0;
244249394a2aSDavid Ahern }
244349394a2aSDavid Ahern 
2444350f54faSDavid Ahern static void timehist_print_migration_event(struct perf_sched *sched,
244532dcd021SJiri Olsa 					struct evsel *evsel,
2446350f54faSDavid Ahern 					struct perf_sample *sample,
2447350f54faSDavid Ahern 					struct machine *machine,
2448350f54faSDavid Ahern 					struct thread *migrated)
2449350f54faSDavid Ahern {
2450350f54faSDavid Ahern 	struct thread *thread;
2451350f54faSDavid Ahern 	char tstr[64];
2452350f54faSDavid Ahern 	u32 max_cpus = sched->max_cpu + 1;
2453350f54faSDavid Ahern 	u32 ocpu, dcpu;
2454350f54faSDavid Ahern 
2455350f54faSDavid Ahern 	if (sched->summary_only)
2456350f54faSDavid Ahern 		return;
2457350f54faSDavid Ahern 
2458350f54faSDavid Ahern 	max_cpus = sched->max_cpu + 1;
2459efc0cdc9SArnaldo Carvalho de Melo 	ocpu = evsel__intval(evsel, sample, "orig_cpu");
2460efc0cdc9SArnaldo Carvalho de Melo 	dcpu = evsel__intval(evsel, sample, "dest_cpu");
2461350f54faSDavid Ahern 
2462350f54faSDavid Ahern 	thread = machine__findnew_thread(machine, sample->pid, sample->tid);
2463350f54faSDavid Ahern 	if (thread == NULL)
2464350f54faSDavid Ahern 		return;
2465350f54faSDavid Ahern 
2466a4b2b6f5SNamhyung Kim 	if (timehist_skip_sample(sched, thread, evsel, sample) &&
2467a4b2b6f5SNamhyung Kim 	    timehist_skip_sample(sched, migrated, evsel, sample)) {
2468350f54faSDavid Ahern 		return;
2469350f54faSDavid Ahern 	}
2470350f54faSDavid Ahern 
2471350f54faSDavid Ahern 	timestamp__scnprintf_usec(sample->time, tstr, sizeof(tstr));
2472350f54faSDavid Ahern 	printf("%15s [%04d] ", tstr, sample->cpu);
2473350f54faSDavid Ahern 
2474350f54faSDavid Ahern 	if (sched->show_cpu_visual) {
2475350f54faSDavid Ahern 		u32 i;
2476350f54faSDavid Ahern 		char c;
2477350f54faSDavid Ahern 
2478350f54faSDavid Ahern 		printf("  ");
2479350f54faSDavid Ahern 		for (i = 0; i < max_cpus; ++i) {
2480350f54faSDavid Ahern 			c = (i == sample->cpu) ? 'm' : ' ';
2481350f54faSDavid Ahern 			printf("%c", c);
2482350f54faSDavid Ahern 		}
2483350f54faSDavid Ahern 		printf("  ");
2484350f54faSDavid Ahern 	}
2485350f54faSDavid Ahern 
2486350f54faSDavid Ahern 	printf(" %-*s ", comm_width, timehist_get_commstr(thread));
2487350f54faSDavid Ahern 
2488350f54faSDavid Ahern 	/* dt spacer */
2489350f54faSDavid Ahern 	printf("  %9s  %9s  %9s ", "", "", "");
2490350f54faSDavid Ahern 
2491350f54faSDavid Ahern 	printf("migrated: %s", timehist_get_commstr(migrated));
2492350f54faSDavid Ahern 	printf(" cpu %d => %d", ocpu, dcpu);
2493350f54faSDavid Ahern 
2494350f54faSDavid Ahern 	printf("\n");
2495350f54faSDavid Ahern }
2496350f54faSDavid Ahern 
2497350f54faSDavid Ahern static int timehist_migrate_task_event(struct perf_tool *tool,
2498350f54faSDavid Ahern 				       union perf_event *event __maybe_unused,
249932dcd021SJiri Olsa 				       struct evsel *evsel,
2500350f54faSDavid Ahern 				       struct perf_sample *sample,
2501350f54faSDavid Ahern 				       struct machine *machine)
2502350f54faSDavid Ahern {
2503350f54faSDavid Ahern 	struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
2504350f54faSDavid Ahern 	struct thread *thread;
2505350f54faSDavid Ahern 	struct thread_runtime *tr = NULL;
2506350f54faSDavid Ahern 	/* want pid of migrated task not pid in sample */
2507efc0cdc9SArnaldo Carvalho de Melo 	const u32 pid = evsel__intval(evsel, sample, "pid");
2508350f54faSDavid Ahern 
2509350f54faSDavid Ahern 	thread = machine__findnew_thread(machine, 0, pid);
2510350f54faSDavid Ahern 	if (thread == NULL)
2511350f54faSDavid Ahern 		return -1;
2512350f54faSDavid Ahern 
2513350f54faSDavid Ahern 	tr = thread__get_runtime(thread);
2514350f54faSDavid Ahern 	if (tr == NULL)
2515350f54faSDavid Ahern 		return -1;
2516350f54faSDavid Ahern 
2517350f54faSDavid Ahern 	tr->migrations++;
2518350f54faSDavid Ahern 
2519350f54faSDavid Ahern 	/* show migrations if requested */
2520350f54faSDavid Ahern 	timehist_print_migration_event(sched, evsel, sample, machine, thread);
2521350f54faSDavid Ahern 
2522350f54faSDavid Ahern 	return 0;
2523350f54faSDavid Ahern }
2524350f54faSDavid Ahern 
252552df138cSDavid Ahern static int timehist_sched_change_event(struct perf_tool *tool,
252649394a2aSDavid Ahern 				       union perf_event *event,
252732dcd021SJiri Olsa 				       struct evsel *evsel,
252849394a2aSDavid Ahern 				       struct perf_sample *sample,
252949394a2aSDavid Ahern 				       struct machine *machine)
253049394a2aSDavid Ahern {
2531fc1469f1SDavid Ahern 	struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
2532853b7407SDavid Ahern 	struct perf_time_interval *ptime = &sched->ptime;
253349394a2aSDavid Ahern 	struct addr_location al;
253449394a2aSDavid Ahern 	struct thread *thread;
253549394a2aSDavid Ahern 	struct thread_runtime *tr = NULL;
2536853b7407SDavid Ahern 	u64 tprev, t = sample->time;
253749394a2aSDavid Ahern 	int rc = 0;
2538efc0cdc9SArnaldo Carvalho de Melo 	int state = evsel__intval(evsel, sample, "prev_state");
253949394a2aSDavid Ahern 
254049394a2aSDavid Ahern 	if (machine__resolve(machine, &al, sample) < 0) {
254149394a2aSDavid Ahern 		pr_err("problem processing %d event. skipping it\n",
254249394a2aSDavid Ahern 		       event->header.type);
254349394a2aSDavid Ahern 		rc = -1;
254449394a2aSDavid Ahern 		goto out;
254549394a2aSDavid Ahern 	}
254649394a2aSDavid Ahern 
25476c973c90SDavid Ahern 	thread = timehist_get_thread(sched, sample, machine, evsel);
254849394a2aSDavid Ahern 	if (thread == NULL) {
254949394a2aSDavid Ahern 		rc = -1;
255049394a2aSDavid Ahern 		goto out;
255149394a2aSDavid Ahern 	}
255249394a2aSDavid Ahern 
2553a4b2b6f5SNamhyung Kim 	if (timehist_skip_sample(sched, thread, evsel, sample))
255449394a2aSDavid Ahern 		goto out;
255549394a2aSDavid Ahern 
255649394a2aSDavid Ahern 	tr = thread__get_runtime(thread);
255749394a2aSDavid Ahern 	if (tr == NULL) {
255849394a2aSDavid Ahern 		rc = -1;
255949394a2aSDavid Ahern 		goto out;
256049394a2aSDavid Ahern 	}
256149394a2aSDavid Ahern 
25623b7313f2SArnaldo Carvalho de Melo 	tprev = evsel__get_time(evsel, sample->cpu);
256349394a2aSDavid Ahern 
2564853b7407SDavid Ahern 	/*
2565853b7407SDavid Ahern 	 * If start time given:
2566853b7407SDavid Ahern 	 * - sample time is under window user cares about - skip sample
2567853b7407SDavid Ahern 	 * - tprev is under window user cares about  - reset to start of window
2568853b7407SDavid Ahern 	 */
2569853b7407SDavid Ahern 	if (ptime->start && ptime->start > t)
2570853b7407SDavid Ahern 		goto out;
2571853b7407SDavid Ahern 
2572bdd75729SNamhyung Kim 	if (tprev && ptime->start > tprev)
2573853b7407SDavid Ahern 		tprev = ptime->start;
2574853b7407SDavid Ahern 
2575853b7407SDavid Ahern 	/*
2576853b7407SDavid Ahern 	 * If end time given:
2577853b7407SDavid Ahern 	 * - previous sched event is out of window - we are done
2578853b7407SDavid Ahern 	 * - sample time is beyond window user cares about - reset it
2579853b7407SDavid Ahern 	 *   to close out stats for time window interest
2580853b7407SDavid Ahern 	 */
2581853b7407SDavid Ahern 	if (ptime->end) {
2582853b7407SDavid Ahern 		if (tprev > ptime->end)
2583853b7407SDavid Ahern 			goto out;
2584853b7407SDavid Ahern 
2585853b7407SDavid Ahern 		if (t > ptime->end)
2586853b7407SDavid Ahern 			t = ptime->end;
2587853b7407SDavid Ahern 	}
2588853b7407SDavid Ahern 
258907235f84SNamhyung Kim 	if (!sched->idle_hist || thread->tid == 0) {
2590a74eaf16SDavid Ahern 		if (!cpu_list || test_bit(sample->cpu, cpu_bitmap))
2591853b7407SDavid Ahern 			timehist_update_runtime_stats(tr, t, tprev);
2592853b7407SDavid Ahern 
259307235f84SNamhyung Kim 		if (sched->idle_hist) {
259407235f84SNamhyung Kim 			struct idle_thread_runtime *itr = (void *)tr;
259507235f84SNamhyung Kim 			struct thread_runtime *last_tr;
259607235f84SNamhyung Kim 
259707235f84SNamhyung Kim 			BUG_ON(thread->tid != 0);
259807235f84SNamhyung Kim 
259907235f84SNamhyung Kim 			if (itr->last_thread == NULL)
260007235f84SNamhyung Kim 				goto out;
260107235f84SNamhyung Kim 
260207235f84SNamhyung Kim 			/* add current idle time as last thread's runtime */
260307235f84SNamhyung Kim 			last_tr = thread__get_runtime(itr->last_thread);
260407235f84SNamhyung Kim 			if (last_tr == NULL)
260507235f84SNamhyung Kim 				goto out;
260607235f84SNamhyung Kim 
260707235f84SNamhyung Kim 			timehist_update_runtime_stats(last_tr, t, tprev);
260807235f84SNamhyung Kim 			/*
260907235f84SNamhyung Kim 			 * remove delta time of last thread as it's not updated
261007235f84SNamhyung Kim 			 * and otherwise it will show an invalid value next
261107235f84SNamhyung Kim 			 * time.  we only care total run time and run stat.
261207235f84SNamhyung Kim 			 */
261307235f84SNamhyung Kim 			last_tr->dt_run = 0;
261407235f84SNamhyung Kim 			last_tr->dt_delay = 0;
2615941bdea7SNamhyung Kim 			last_tr->dt_sleep = 0;
2616941bdea7SNamhyung Kim 			last_tr->dt_iowait = 0;
2617941bdea7SNamhyung Kim 			last_tr->dt_preempt = 0;
261807235f84SNamhyung Kim 
2619ba957ebbSNamhyung Kim 			if (itr->cursor.nr)
2620ba957ebbSNamhyung Kim 				callchain_append(&itr->callchain, &itr->cursor, t - tprev);
2621ba957ebbSNamhyung Kim 
262207235f84SNamhyung Kim 			itr->last_thread = NULL;
262307235f84SNamhyung Kim 		}
262407235f84SNamhyung Kim 	}
262507235f84SNamhyung Kim 
262652df138cSDavid Ahern 	if (!sched->summary_only)
2627292c4a8fSBrendan Gregg 		timehist_print_sample(sched, evsel, sample, &al, thread, t, state);
262849394a2aSDavid Ahern 
262949394a2aSDavid Ahern out:
26309396c9cbSNamhyung Kim 	if (sched->hist_time.start == 0 && t >= ptime->start)
26319396c9cbSNamhyung Kim 		sched->hist_time.start = t;
26329396c9cbSNamhyung Kim 	if (ptime->end == 0 || t <= ptime->end)
26339396c9cbSNamhyung Kim 		sched->hist_time.end = t;
26349396c9cbSNamhyung Kim 
263549394a2aSDavid Ahern 	if (tr) {
263649394a2aSDavid Ahern 		/* time of this sched_switch event becomes last time task seen */
263749394a2aSDavid Ahern 		tr->last_time = sample->time;
263849394a2aSDavid Ahern 
2639941bdea7SNamhyung Kim 		/* last state is used to determine where to account wait time */
2640414e050cSNamhyung Kim 		tr->last_state = state;
2641941bdea7SNamhyung Kim 
264249394a2aSDavid Ahern 		/* sched out event for task so reset ready to run time */
264349394a2aSDavid Ahern 		tr->ready_to_run = 0;
264449394a2aSDavid Ahern 	}
264549394a2aSDavid Ahern 
26463b7313f2SArnaldo Carvalho de Melo 	evsel__save_time(evsel, sample->time, sample->cpu);
264749394a2aSDavid Ahern 
264849394a2aSDavid Ahern 	return rc;
264949394a2aSDavid Ahern }
265049394a2aSDavid Ahern 
265149394a2aSDavid Ahern static int timehist_sched_switch_event(struct perf_tool *tool,
265249394a2aSDavid Ahern 			     union perf_event *event,
265332dcd021SJiri Olsa 			     struct evsel *evsel,
265449394a2aSDavid Ahern 			     struct perf_sample *sample,
265549394a2aSDavid Ahern 			     struct machine *machine __maybe_unused)
265649394a2aSDavid Ahern {
265749394a2aSDavid Ahern 	return timehist_sched_change_event(tool, event, evsel, sample, machine);
265849394a2aSDavid Ahern }
265949394a2aSDavid Ahern 
266049394a2aSDavid Ahern static int process_lost(struct perf_tool *tool __maybe_unused,
266149394a2aSDavid Ahern 			union perf_event *event,
266249394a2aSDavid Ahern 			struct perf_sample *sample,
266349394a2aSDavid Ahern 			struct machine *machine __maybe_unused)
266449394a2aSDavid Ahern {
266549394a2aSDavid Ahern 	char tstr[64];
266649394a2aSDavid Ahern 
266749394a2aSDavid Ahern 	timestamp__scnprintf_usec(sample->time, tstr, sizeof(tstr));
266849394a2aSDavid Ahern 	printf("%15s ", tstr);
26695290ed69SJiri Olsa 	printf("lost %" PRI_lu64 " events on cpu %d\n", event->lost.lost, sample->cpu);
267049394a2aSDavid Ahern 
267149394a2aSDavid Ahern 	return 0;
267249394a2aSDavid Ahern }
267349394a2aSDavid Ahern 
267449394a2aSDavid Ahern 
267552df138cSDavid Ahern static void print_thread_runtime(struct thread *t,
267652df138cSDavid Ahern 				 struct thread_runtime *r)
267752df138cSDavid Ahern {
267852df138cSDavid Ahern 	double mean = avg_stats(&r->run_stats);
267952df138cSDavid Ahern 	float stddev;
268052df138cSDavid Ahern 
268152df138cSDavid Ahern 	printf("%*s   %5d  %9" PRIu64 " ",
268252df138cSDavid Ahern 	       comm_width, timehist_get_commstr(t), t->ppid,
268352df138cSDavid Ahern 	       (u64) r->run_stats.n);
268452df138cSDavid Ahern 
268552df138cSDavid Ahern 	print_sched_time(r->total_run_time, 8);
268652df138cSDavid Ahern 	stddev = rel_stddev_stats(stddev_stats(&r->run_stats), mean);
268752df138cSDavid Ahern 	print_sched_time(r->run_stats.min, 6);
268852df138cSDavid Ahern 	printf(" ");
268952df138cSDavid Ahern 	print_sched_time((u64) mean, 6);
269052df138cSDavid Ahern 	printf(" ");
269152df138cSDavid Ahern 	print_sched_time(r->run_stats.max, 6);
269252df138cSDavid Ahern 	printf("  ");
269352df138cSDavid Ahern 	printf("%5.2f", stddev);
2694350f54faSDavid Ahern 	printf("   %5" PRIu64, r->migrations);
269552df138cSDavid Ahern 	printf("\n");
269652df138cSDavid Ahern }
269752df138cSDavid Ahern 
2698587782c5SNamhyung Kim static void print_thread_waittime(struct thread *t,
2699587782c5SNamhyung Kim 				  struct thread_runtime *r)
2700587782c5SNamhyung Kim {
2701587782c5SNamhyung Kim 	printf("%*s   %5d  %9" PRIu64 " ",
2702587782c5SNamhyung Kim 	       comm_width, timehist_get_commstr(t), t->ppid,
2703587782c5SNamhyung Kim 	       (u64) r->run_stats.n);
2704587782c5SNamhyung Kim 
2705587782c5SNamhyung Kim 	print_sched_time(r->total_run_time, 8);
2706587782c5SNamhyung Kim 	print_sched_time(r->total_sleep_time, 6);
2707587782c5SNamhyung Kim 	printf(" ");
2708587782c5SNamhyung Kim 	print_sched_time(r->total_iowait_time, 6);
2709587782c5SNamhyung Kim 	printf(" ");
2710587782c5SNamhyung Kim 	print_sched_time(r->total_preempt_time, 6);
2711587782c5SNamhyung Kim 	printf(" ");
2712587782c5SNamhyung Kim 	print_sched_time(r->total_delay_time, 6);
2713587782c5SNamhyung Kim 	printf("\n");
2714587782c5SNamhyung Kim }
2715587782c5SNamhyung Kim 
271652df138cSDavid Ahern struct total_run_stats {
2717587782c5SNamhyung Kim 	struct perf_sched *sched;
271852df138cSDavid Ahern 	u64  sched_count;
271952df138cSDavid Ahern 	u64  task_count;
272052df138cSDavid Ahern 	u64  total_run_time;
272152df138cSDavid Ahern };
272252df138cSDavid Ahern 
272352df138cSDavid Ahern static int __show_thread_runtime(struct thread *t, void *priv)
272452df138cSDavid Ahern {
272552df138cSDavid Ahern 	struct total_run_stats *stats = priv;
272652df138cSDavid Ahern 	struct thread_runtime *r;
272752df138cSDavid Ahern 
272852df138cSDavid Ahern 	if (thread__is_filtered(t))
272952df138cSDavid Ahern 		return 0;
273052df138cSDavid Ahern 
273152df138cSDavid Ahern 	r = thread__priv(t);
273252df138cSDavid Ahern 	if (r && r->run_stats.n) {
273352df138cSDavid Ahern 		stats->task_count++;
273452df138cSDavid Ahern 		stats->sched_count += r->run_stats.n;
273552df138cSDavid Ahern 		stats->total_run_time += r->total_run_time;
2736587782c5SNamhyung Kim 
2737587782c5SNamhyung Kim 		if (stats->sched->show_state)
2738587782c5SNamhyung Kim 			print_thread_waittime(t, r);
2739587782c5SNamhyung Kim 		else
274052df138cSDavid Ahern 			print_thread_runtime(t, r);
274152df138cSDavid Ahern 	}
274252df138cSDavid Ahern 
274352df138cSDavid Ahern 	return 0;
274452df138cSDavid Ahern }
274552df138cSDavid Ahern 
274652df138cSDavid Ahern static int show_thread_runtime(struct thread *t, void *priv)
274752df138cSDavid Ahern {
274852df138cSDavid Ahern 	if (t->dead)
274952df138cSDavid Ahern 		return 0;
275052df138cSDavid Ahern 
275152df138cSDavid Ahern 	return __show_thread_runtime(t, priv);
275252df138cSDavid Ahern }
275352df138cSDavid Ahern 
275452df138cSDavid Ahern static int show_deadthread_runtime(struct thread *t, void *priv)
275552df138cSDavid Ahern {
275652df138cSDavid Ahern 	if (!t->dead)
275752df138cSDavid Ahern 		return 0;
275852df138cSDavid Ahern 
275952df138cSDavid Ahern 	return __show_thread_runtime(t, priv);
276052df138cSDavid Ahern }
276152df138cSDavid Ahern 
2762ba957ebbSNamhyung Kim static size_t callchain__fprintf_folded(FILE *fp, struct callchain_node *node)
2763ba957ebbSNamhyung Kim {
2764ba957ebbSNamhyung Kim 	const char *sep = " <- ";
2765ba957ebbSNamhyung Kim 	struct callchain_list *chain;
2766ba957ebbSNamhyung Kim 	size_t ret = 0;
2767ba957ebbSNamhyung Kim 	char bf[1024];
2768ba957ebbSNamhyung Kim 	bool first;
2769ba957ebbSNamhyung Kim 
2770ba957ebbSNamhyung Kim 	if (node == NULL)
2771ba957ebbSNamhyung Kim 		return 0;
2772ba957ebbSNamhyung Kim 
2773ba957ebbSNamhyung Kim 	ret = callchain__fprintf_folded(fp, node->parent);
2774ba957ebbSNamhyung Kim 	first = (ret == 0);
2775ba957ebbSNamhyung Kim 
2776ba957ebbSNamhyung Kim 	list_for_each_entry(chain, &node->val, list) {
2777ba957ebbSNamhyung Kim 		if (chain->ip >= PERF_CONTEXT_MAX)
2778ba957ebbSNamhyung Kim 			continue;
2779ba957ebbSNamhyung Kim 		if (chain->ms.sym && chain->ms.sym->ignore)
2780ba957ebbSNamhyung Kim 			continue;
2781ba957ebbSNamhyung Kim 		ret += fprintf(fp, "%s%s", first ? "" : sep,
2782ba957ebbSNamhyung Kim 			       callchain_list__sym_name(chain, bf, sizeof(bf),
2783ba957ebbSNamhyung Kim 							false));
2784ba957ebbSNamhyung Kim 		first = false;
2785ba957ebbSNamhyung Kim 	}
2786ba957ebbSNamhyung Kim 
2787ba957ebbSNamhyung Kim 	return ret;
2788ba957ebbSNamhyung Kim }
2789ba957ebbSNamhyung Kim 
2790cb4c13a5SDavidlohr Bueso static size_t timehist_print_idlehist_callchain(struct rb_root_cached *root)
2791ba957ebbSNamhyung Kim {
2792ba957ebbSNamhyung Kim 	size_t ret = 0;
2793ba957ebbSNamhyung Kim 	FILE *fp = stdout;
2794ba957ebbSNamhyung Kim 	struct callchain_node *chain;
2795cb4c13a5SDavidlohr Bueso 	struct rb_node *rb_node = rb_first_cached(root);
2796ba957ebbSNamhyung Kim 
2797ba957ebbSNamhyung Kim 	printf("  %16s  %8s  %s\n", "Idle time (msec)", "Count", "Callchains");
2798ba957ebbSNamhyung Kim 	printf("  %.16s  %.8s  %.50s\n", graph_dotted_line, graph_dotted_line,
2799ba957ebbSNamhyung Kim 	       graph_dotted_line);
2800ba957ebbSNamhyung Kim 
2801ba957ebbSNamhyung Kim 	while (rb_node) {
2802ba957ebbSNamhyung Kim 		chain = rb_entry(rb_node, struct callchain_node, rb_node);
2803ba957ebbSNamhyung Kim 		rb_node = rb_next(rb_node);
2804ba957ebbSNamhyung Kim 
2805ba957ebbSNamhyung Kim 		ret += fprintf(fp, "  ");
2806ba957ebbSNamhyung Kim 		print_sched_time(chain->hit, 12);
2807ba957ebbSNamhyung Kim 		ret += 16;  /* print_sched_time returns 2nd arg + 4 */
2808ba957ebbSNamhyung Kim 		ret += fprintf(fp, " %8d  ", chain->count);
2809ba957ebbSNamhyung Kim 		ret += callchain__fprintf_folded(fp, chain);
2810ba957ebbSNamhyung Kim 		ret += fprintf(fp, "\n");
2811ba957ebbSNamhyung Kim 	}
2812ba957ebbSNamhyung Kim 
2813ba957ebbSNamhyung Kim 	return ret;
2814ba957ebbSNamhyung Kim }
2815ba957ebbSNamhyung Kim 
281652df138cSDavid Ahern static void timehist_print_summary(struct perf_sched *sched,
281752df138cSDavid Ahern 				   struct perf_session *session)
281852df138cSDavid Ahern {
281952df138cSDavid Ahern 	struct machine *m = &session->machines.host;
282052df138cSDavid Ahern 	struct total_run_stats totals;
282152df138cSDavid Ahern 	u64 task_count;
282252df138cSDavid Ahern 	struct thread *t;
282352df138cSDavid Ahern 	struct thread_runtime *r;
282452df138cSDavid Ahern 	int i;
28259396c9cbSNamhyung Kim 	u64 hist_time = sched->hist_time.end - sched->hist_time.start;
282652df138cSDavid Ahern 
282752df138cSDavid Ahern 	memset(&totals, 0, sizeof(totals));
2828587782c5SNamhyung Kim 	totals.sched = sched;
282952df138cSDavid Ahern 
283007235f84SNamhyung Kim 	if (sched->idle_hist) {
283107235f84SNamhyung Kim 		printf("\nIdle-time summary\n");
283207235f84SNamhyung Kim 		printf("%*s  parent  sched-out  ", comm_width, "comm");
283307235f84SNamhyung Kim 		printf("  idle-time   min-idle    avg-idle    max-idle  stddev  migrations\n");
2834587782c5SNamhyung Kim 	} else if (sched->show_state) {
2835587782c5SNamhyung Kim 		printf("\nWait-time summary\n");
2836587782c5SNamhyung Kim 		printf("%*s  parent   sched-in  ", comm_width, "comm");
2837587782c5SNamhyung Kim 		printf("   run-time      sleep      iowait     preempt       delay\n");
283807235f84SNamhyung Kim 	} else {
283952df138cSDavid Ahern 		printf("\nRuntime summary\n");
284052df138cSDavid Ahern 		printf("%*s  parent   sched-in  ", comm_width, "comm");
2841350f54faSDavid Ahern 		printf("   run-time    min-run     avg-run     max-run  stddev  migrations\n");
284207235f84SNamhyung Kim 	}
284352df138cSDavid Ahern 	printf("%*s            (count)  ", comm_width, "");
2844587782c5SNamhyung Kim 	printf("     (msec)     (msec)      (msec)      (msec)       %s\n",
2845587782c5SNamhyung Kim 	       sched->show_state ? "(msec)" : "%");
2846350f54faSDavid Ahern 	printf("%.117s\n", graph_dotted_line);
284752df138cSDavid Ahern 
284852df138cSDavid Ahern 	machine__for_each_thread(m, show_thread_runtime, &totals);
284952df138cSDavid Ahern 	task_count = totals.task_count;
285052df138cSDavid Ahern 	if (!task_count)
285152df138cSDavid Ahern 		printf("<no still running tasks>\n");
285252df138cSDavid Ahern 
285352df138cSDavid Ahern 	printf("\nTerminated tasks:\n");
285452df138cSDavid Ahern 	machine__for_each_thread(m, show_deadthread_runtime, &totals);
285552df138cSDavid Ahern 	if (task_count == totals.task_count)
285652df138cSDavid Ahern 		printf("<no terminated tasks>\n");
285752df138cSDavid Ahern 
285852df138cSDavid Ahern 	/* CPU idle stats not tracked when samples were skipped */
285907235f84SNamhyung Kim 	if (sched->skipped_samples && !sched->idle_hist)
286052df138cSDavid Ahern 		return;
286152df138cSDavid Ahern 
286252df138cSDavid Ahern 	printf("\nIdle stats:\n");
2863b336352bSNamhyung Kim 	for (i = 0; i < idle_max_cpu; ++i) {
2864a74eaf16SDavid Ahern 		if (cpu_list && !test_bit(i, cpu_bitmap))
2865a74eaf16SDavid Ahern 			continue;
2866a74eaf16SDavid Ahern 
286752df138cSDavid Ahern 		t = idle_threads[i];
286852df138cSDavid Ahern 		if (!t)
286952df138cSDavid Ahern 			continue;
287052df138cSDavid Ahern 
287152df138cSDavid Ahern 		r = thread__priv(t);
287252df138cSDavid Ahern 		if (r && r->run_stats.n) {
287352df138cSDavid Ahern 			totals.sched_count += r->run_stats.n;
287452df138cSDavid Ahern 			printf("    CPU %2d idle for ", i);
287552df138cSDavid Ahern 			print_sched_time(r->total_run_time, 6);
28769396c9cbSNamhyung Kim 			printf(" msec  (%6.2f%%)\n", 100.0 * r->total_run_time / hist_time);
287752df138cSDavid Ahern 		} else
287852df138cSDavid Ahern 			printf("    CPU %2d idle entire time window\n", i);
287952df138cSDavid Ahern 	}
288052df138cSDavid Ahern 
28814c50563dSArnaldo Carvalho de Melo 	if (sched->idle_hist && sched->show_callchain) {
2882ba957ebbSNamhyung Kim 		callchain_param.mode  = CHAIN_FOLDED;
2883ba957ebbSNamhyung Kim 		callchain_param.value = CCVAL_PERIOD;
2884ba957ebbSNamhyung Kim 
2885ba957ebbSNamhyung Kim 		callchain_register_param(&callchain_param);
2886ba957ebbSNamhyung Kim 
2887ba957ebbSNamhyung Kim 		printf("\nIdle stats by callchain:\n");
2888ba957ebbSNamhyung Kim 		for (i = 0; i < idle_max_cpu; ++i) {
2889ba957ebbSNamhyung Kim 			struct idle_thread_runtime *itr;
2890ba957ebbSNamhyung Kim 
2891ba957ebbSNamhyung Kim 			t = idle_threads[i];
2892ba957ebbSNamhyung Kim 			if (!t)
2893ba957ebbSNamhyung Kim 				continue;
2894ba957ebbSNamhyung Kim 
2895ba957ebbSNamhyung Kim 			itr = thread__priv(t);
2896ba957ebbSNamhyung Kim 			if (itr == NULL)
2897ba957ebbSNamhyung Kim 				continue;
2898ba957ebbSNamhyung Kim 
2899cb4c13a5SDavidlohr Bueso 			callchain_param.sort(&itr->sorted_root.rb_root, &itr->callchain,
2900ba957ebbSNamhyung Kim 					     0, &callchain_param);
2901ba957ebbSNamhyung Kim 
2902ba957ebbSNamhyung Kim 			printf("  CPU %2d:", i);
2903ba957ebbSNamhyung Kim 			print_sched_time(itr->tr.total_run_time, 6);
2904ba957ebbSNamhyung Kim 			printf(" msec\n");
2905ba957ebbSNamhyung Kim 			timehist_print_idlehist_callchain(&itr->sorted_root);
2906ba957ebbSNamhyung Kim 			printf("\n");
2907ba957ebbSNamhyung Kim 		}
2908ba957ebbSNamhyung Kim 	}
2909ba957ebbSNamhyung Kim 
291052df138cSDavid Ahern 	printf("\n"
291152df138cSDavid Ahern 	       "    Total number of unique tasks: %" PRIu64 "\n"
29129396c9cbSNamhyung Kim 	       "Total number of context switches: %" PRIu64 "\n",
291352df138cSDavid Ahern 	       totals.task_count, totals.sched_count);
291452df138cSDavid Ahern 
29159396c9cbSNamhyung Kim 	printf("           Total run time (msec): ");
291652df138cSDavid Ahern 	print_sched_time(totals.total_run_time, 2);
291752df138cSDavid Ahern 	printf("\n");
29189396c9cbSNamhyung Kim 
29199396c9cbSNamhyung Kim 	printf("    Total scheduling time (msec): ");
29209396c9cbSNamhyung Kim 	print_sched_time(hist_time, 2);
29219396c9cbSNamhyung Kim 	printf(" (x %d)\n", sched->max_cpu);
292252df138cSDavid Ahern }
292352df138cSDavid Ahern 
292449394a2aSDavid Ahern typedef int (*sched_handler)(struct perf_tool *tool,
292549394a2aSDavid Ahern 			  union perf_event *event,
292632dcd021SJiri Olsa 			  struct evsel *evsel,
292749394a2aSDavid Ahern 			  struct perf_sample *sample,
292849394a2aSDavid Ahern 			  struct machine *machine);
292949394a2aSDavid Ahern 
293049394a2aSDavid Ahern static int perf_timehist__process_sample(struct perf_tool *tool,
293149394a2aSDavid Ahern 					 union perf_event *event,
293249394a2aSDavid Ahern 					 struct perf_sample *sample,
293332dcd021SJiri Olsa 					 struct evsel *evsel,
293449394a2aSDavid Ahern 					 struct machine *machine)
293549394a2aSDavid Ahern {
293649394a2aSDavid Ahern 	struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
293749394a2aSDavid Ahern 	int err = 0;
293849394a2aSDavid Ahern 	int this_cpu = sample->cpu;
293949394a2aSDavid Ahern 
294049394a2aSDavid Ahern 	if (this_cpu > sched->max_cpu)
294149394a2aSDavid Ahern 		sched->max_cpu = this_cpu;
294249394a2aSDavid Ahern 
294349394a2aSDavid Ahern 	if (evsel->handler != NULL) {
294449394a2aSDavid Ahern 		sched_handler f = evsel->handler;
294549394a2aSDavid Ahern 
294649394a2aSDavid Ahern 		err = f(tool, event, evsel, sample, machine);
294749394a2aSDavid Ahern 	}
294849394a2aSDavid Ahern 
294949394a2aSDavid Ahern 	return err;
295049394a2aSDavid Ahern }
295149394a2aSDavid Ahern 
29526c973c90SDavid Ahern static int timehist_check_attr(struct perf_sched *sched,
295363503dbaSJiri Olsa 			       struct evlist *evlist)
29546c973c90SDavid Ahern {
295532dcd021SJiri Olsa 	struct evsel *evsel;
29566c973c90SDavid Ahern 	struct evsel_runtime *er;
29576c973c90SDavid Ahern 
2958ce9036a6SJiri Olsa 	list_for_each_entry(evsel, &evlist->core.entries, core.node) {
29593b7313f2SArnaldo Carvalho de Melo 		er = evsel__get_runtime(evsel);
29606c973c90SDavid Ahern 		if (er == NULL) {
29616c973c90SDavid Ahern 			pr_err("Failed to allocate memory for evsel runtime data\n");
29626c973c90SDavid Ahern 			return -1;
29636c973c90SDavid Ahern 		}
29646c973c90SDavid Ahern 
296527de9b2bSArnaldo Carvalho de Melo 		if (sched->show_callchain && !evsel__has_callchain(evsel)) {
29666c973c90SDavid Ahern 			pr_info("Samples do not have callchains.\n");
29676c973c90SDavid Ahern 			sched->show_callchain = 0;
29686c973c90SDavid Ahern 			symbol_conf.use_callchain = 0;
29696c973c90SDavid Ahern 		}
29706c973c90SDavid Ahern 	}
29716c973c90SDavid Ahern 
29726c973c90SDavid Ahern 	return 0;
29736c973c90SDavid Ahern }
29746c973c90SDavid Ahern 
297549394a2aSDavid Ahern static int perf_sched__timehist(struct perf_sched *sched)
297649394a2aSDavid Ahern {
2977d566a9c2SDavid Ahern 	struct evsel_str_handler handlers[] = {
297849394a2aSDavid Ahern 		{ "sched:sched_switch",       timehist_sched_switch_event, },
297949394a2aSDavid Ahern 		{ "sched:sched_wakeup",	      timehist_sched_wakeup_event, },
2980d566a9c2SDavid Ahern 		{ "sched:sched_waking",       timehist_sched_wakeup_event, },
298149394a2aSDavid Ahern 		{ "sched:sched_wakeup_new",   timehist_sched_wakeup_event, },
298249394a2aSDavid Ahern 	};
298332dcd021SJiri Olsa 	const struct evsel_str_handler migrate_handlers[] = {
2984350f54faSDavid Ahern 		{ "sched:sched_migrate_task", timehist_migrate_task_event, },
2985350f54faSDavid Ahern 	};
29868ceb41d7SJiri Olsa 	struct perf_data data = {
298749394a2aSDavid Ahern 		.path  = input_name,
298849394a2aSDavid Ahern 		.mode  = PERF_DATA_MODE_READ,
29896fa94258SNamhyung Kim 		.force = sched->force,
299049394a2aSDavid Ahern 	};
299149394a2aSDavid Ahern 
299249394a2aSDavid Ahern 	struct perf_session *session;
299363503dbaSJiri Olsa 	struct evlist *evlist;
299449394a2aSDavid Ahern 	int err = -1;
299549394a2aSDavid Ahern 
299649394a2aSDavid Ahern 	/*
299749394a2aSDavid Ahern 	 * event handlers for timehist option
299849394a2aSDavid Ahern 	 */
299949394a2aSDavid Ahern 	sched->tool.sample	 = perf_timehist__process_sample;
300049394a2aSDavid Ahern 	sched->tool.mmap	 = perf_event__process_mmap;
300149394a2aSDavid Ahern 	sched->tool.comm	 = perf_event__process_comm;
300249394a2aSDavid Ahern 	sched->tool.exit	 = perf_event__process_exit;
300349394a2aSDavid Ahern 	sched->tool.fork	 = perf_event__process_fork;
300449394a2aSDavid Ahern 	sched->tool.lost	 = process_lost;
300549394a2aSDavid Ahern 	sched->tool.attr	 = perf_event__process_attr;
300649394a2aSDavid Ahern 	sched->tool.tracing_data = perf_event__process_tracing_data;
300749394a2aSDavid Ahern 	sched->tool.build_id	 = perf_event__process_build_id;
300849394a2aSDavid Ahern 
300949394a2aSDavid Ahern 	sched->tool.ordered_events = true;
301049394a2aSDavid Ahern 	sched->tool.ordering_requires_timestamps = true;
301149394a2aSDavid Ahern 
30126c973c90SDavid Ahern 	symbol_conf.use_callchain = sched->show_callchain;
30136c973c90SDavid Ahern 
30142681bd85SNamhyung Kim 	session = perf_session__new(&data, &sched->tool);
30156ef81c55SMamatha Inamdar 	if (IS_ERR(session))
30166ef81c55SMamatha Inamdar 		return PTR_ERR(session);
301749394a2aSDavid Ahern 
3018c30d630dSDavid Ahern 	if (cpu_list) {
3019c30d630dSDavid Ahern 		err = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap);
3020c30d630dSDavid Ahern 		if (err < 0)
3021c30d630dSDavid Ahern 			goto out;
3022c30d630dSDavid Ahern 	}
3023c30d630dSDavid Ahern 
302452df138cSDavid Ahern 	evlist = session->evlist;
302552df138cSDavid Ahern 
302649394a2aSDavid Ahern 	symbol__init(&session->header.env);
302749394a2aSDavid Ahern 
3028853b7407SDavid Ahern 	if (perf_time__parse_str(&sched->ptime, sched->time_str) != 0) {
3029853b7407SDavid Ahern 		pr_err("Invalid time string\n");
3030853b7407SDavid Ahern 		return -EINVAL;
3031853b7407SDavid Ahern 	}
3032853b7407SDavid Ahern 
30336c973c90SDavid Ahern 	if (timehist_check_attr(sched, evlist) != 0)
30346c973c90SDavid Ahern 		goto out;
30356c973c90SDavid Ahern 
303649394a2aSDavid Ahern 	setup_pager();
303749394a2aSDavid Ahern 
3038d566a9c2SDavid Ahern 	/* prefer sched_waking if it is captured */
3039b02736f7SArnaldo Carvalho de Melo 	if (evlist__find_tracepoint_by_name(session->evlist, "sched:sched_waking"))
3040d566a9c2SDavid Ahern 		handlers[1].handler = timehist_sched_wakeup_ignore;
3041d566a9c2SDavid Ahern 
304249394a2aSDavid Ahern 	/* setup per-evsel handlers */
304349394a2aSDavid Ahern 	if (perf_session__set_tracepoints_handlers(session, handlers))
304449394a2aSDavid Ahern 		goto out;
304549394a2aSDavid Ahern 
3046f45bf8d3SDavid Ahern 	/* sched_switch event at a minimum needs to exist */
3047b02736f7SArnaldo Carvalho de Melo 	if (!evlist__find_tracepoint_by_name(session->evlist, "sched:sched_switch")) {
3048f45bf8d3SDavid Ahern 		pr_err("No sched_switch events found. Have you run 'perf sched record'?\n");
304949394a2aSDavid Ahern 		goto out;
3050f45bf8d3SDavid Ahern 	}
305149394a2aSDavid Ahern 
3052350f54faSDavid Ahern 	if (sched->show_migrations &&
3053350f54faSDavid Ahern 	    perf_session__set_tracepoints_handlers(session, migrate_handlers))
3054350f54faSDavid Ahern 		goto out;
3055350f54faSDavid Ahern 
305649394a2aSDavid Ahern 	/* pre-allocate struct for per-CPU idle stats */
305749394a2aSDavid Ahern 	sched->max_cpu = session->header.env.nr_cpus_online;
305849394a2aSDavid Ahern 	if (sched->max_cpu == 0)
305949394a2aSDavid Ahern 		sched->max_cpu = 4;
306049394a2aSDavid Ahern 	if (init_idle_threads(sched->max_cpu))
306149394a2aSDavid Ahern 		goto out;
306249394a2aSDavid Ahern 
306352df138cSDavid Ahern 	/* summary_only implies summary option, but don't overwrite summary if set */
306452df138cSDavid Ahern 	if (sched->summary_only)
306552df138cSDavid Ahern 		sched->summary = sched->summary_only;
306652df138cSDavid Ahern 
306752df138cSDavid Ahern 	if (!sched->summary_only)
3068a407b067SDavid Ahern 		timehist_header(sched);
306949394a2aSDavid Ahern 
307049394a2aSDavid Ahern 	err = perf_session__process_events(session);
307149394a2aSDavid Ahern 	if (err) {
307249394a2aSDavid Ahern 		pr_err("Failed to process events, error %d", err);
307349394a2aSDavid Ahern 		goto out;
307449394a2aSDavid Ahern 	}
307549394a2aSDavid Ahern 
307652df138cSDavid Ahern 	sched->nr_events      = evlist->stats.nr_events[0];
307752df138cSDavid Ahern 	sched->nr_lost_events = evlist->stats.total_lost;
307852df138cSDavid Ahern 	sched->nr_lost_chunks = evlist->stats.nr_events[PERF_RECORD_LOST];
307952df138cSDavid Ahern 
308052df138cSDavid Ahern 	if (sched->summary)
308152df138cSDavid Ahern 		timehist_print_summary(sched, session);
308252df138cSDavid Ahern 
308349394a2aSDavid Ahern out:
308449394a2aSDavid Ahern 	free_idle_threads();
308549394a2aSDavid Ahern 	perf_session__delete(session);
308649394a2aSDavid Ahern 
308749394a2aSDavid Ahern 	return err;
308849394a2aSDavid Ahern }
308949394a2aSDavid Ahern 
309049394a2aSDavid Ahern 
30910e9b07e5SArnaldo Carvalho de Melo static void print_bad_events(struct perf_sched *sched)
30920ec04e16SIngo Molnar {
30930e9b07e5SArnaldo Carvalho de Melo 	if (sched->nr_unordered_timestamps && sched->nr_timestamps) {
30940ec04e16SIngo Molnar 		printf("  INFO: %.3f%% unordered timestamps (%ld out of %ld)\n",
30950e9b07e5SArnaldo Carvalho de Melo 			(double)sched->nr_unordered_timestamps/(double)sched->nr_timestamps*100.0,
30960e9b07e5SArnaldo Carvalho de Melo 			sched->nr_unordered_timestamps, sched->nr_timestamps);
30970ec04e16SIngo Molnar 	}
30980e9b07e5SArnaldo Carvalho de Melo 	if (sched->nr_lost_events && sched->nr_events) {
30990ec04e16SIngo Molnar 		printf("  INFO: %.3f%% lost events (%ld out of %ld, in %ld chunks)\n",
31000e9b07e5SArnaldo Carvalho de Melo 			(double)sched->nr_lost_events/(double)sched->nr_events * 100.0,
31010e9b07e5SArnaldo Carvalho de Melo 			sched->nr_lost_events, sched->nr_events, sched->nr_lost_chunks);
31020ec04e16SIngo Molnar 	}
31030e9b07e5SArnaldo Carvalho de Melo 	if (sched->nr_context_switch_bugs && sched->nr_timestamps) {
31040ec04e16SIngo Molnar 		printf("  INFO: %.3f%% context switch bugs (%ld out of %ld)",
31050e9b07e5SArnaldo Carvalho de Melo 			(double)sched->nr_context_switch_bugs/(double)sched->nr_timestamps*100.0,
31060e9b07e5SArnaldo Carvalho de Melo 			sched->nr_context_switch_bugs, sched->nr_timestamps);
31070e9b07e5SArnaldo Carvalho de Melo 		if (sched->nr_lost_events)
31080ec04e16SIngo Molnar 			printf(" (due to lost events?)");
31090ec04e16SIngo Molnar 		printf("\n");
31100ec04e16SIngo Molnar 	}
31110ec04e16SIngo Molnar }
31120ec04e16SIngo Molnar 
3113cb4c13a5SDavidlohr Bueso static void __merge_work_atoms(struct rb_root_cached *root, struct work_atoms *data)
31142f80dd44SJosef Bacik {
3115cb4c13a5SDavidlohr Bueso 	struct rb_node **new = &(root->rb_root.rb_node), *parent = NULL;
31162f80dd44SJosef Bacik 	struct work_atoms *this;
31172f80dd44SJosef Bacik 	const char *comm = thread__comm_str(data->thread), *this_comm;
3118cb4c13a5SDavidlohr Bueso 	bool leftmost = true;
31192f80dd44SJosef Bacik 
31202f80dd44SJosef Bacik 	while (*new) {
31212f80dd44SJosef Bacik 		int cmp;
31222f80dd44SJosef Bacik 
31232f80dd44SJosef Bacik 		this = container_of(*new, struct work_atoms, node);
31242f80dd44SJosef Bacik 		parent = *new;
31252f80dd44SJosef Bacik 
31262f80dd44SJosef Bacik 		this_comm = thread__comm_str(this->thread);
31272f80dd44SJosef Bacik 		cmp = strcmp(comm, this_comm);
31282f80dd44SJosef Bacik 		if (cmp > 0) {
31292f80dd44SJosef Bacik 			new = &((*new)->rb_left);
31302f80dd44SJosef Bacik 		} else if (cmp < 0) {
31312f80dd44SJosef Bacik 			new = &((*new)->rb_right);
3132cb4c13a5SDavidlohr Bueso 			leftmost = false;
31332f80dd44SJosef Bacik 		} else {
31342f80dd44SJosef Bacik 			this->num_merged++;
31352f80dd44SJosef Bacik 			this->total_runtime += data->total_runtime;
31362f80dd44SJosef Bacik 			this->nb_atoms += data->nb_atoms;
31372f80dd44SJosef Bacik 			this->total_lat += data->total_lat;
31382f80dd44SJosef Bacik 			list_splice(&data->work_list, &this->work_list);
31392f80dd44SJosef Bacik 			if (this->max_lat < data->max_lat) {
31402f80dd44SJosef Bacik 				this->max_lat = data->max_lat;
3141dc000c45SJoel Fernandes (Google) 				this->max_lat_start = data->max_lat_start;
3142dc000c45SJoel Fernandes (Google) 				this->max_lat_end = data->max_lat_end;
31432f80dd44SJosef Bacik 			}
31442f80dd44SJosef Bacik 			zfree(&data);
31452f80dd44SJosef Bacik 			return;
31462f80dd44SJosef Bacik 		}
31472f80dd44SJosef Bacik 	}
31482f80dd44SJosef Bacik 
31492f80dd44SJosef Bacik 	data->num_merged++;
31502f80dd44SJosef Bacik 	rb_link_node(&data->node, parent, new);
3151cb4c13a5SDavidlohr Bueso 	rb_insert_color_cached(&data->node, root, leftmost);
31522f80dd44SJosef Bacik }
31532f80dd44SJosef Bacik 
31542f80dd44SJosef Bacik static void perf_sched__merge_lat(struct perf_sched *sched)
31552f80dd44SJosef Bacik {
31562f80dd44SJosef Bacik 	struct work_atoms *data;
31572f80dd44SJosef Bacik 	struct rb_node *node;
31582f80dd44SJosef Bacik 
31592f80dd44SJosef Bacik 	if (sched->skip_merge)
31602f80dd44SJosef Bacik 		return;
31612f80dd44SJosef Bacik 
3162cb4c13a5SDavidlohr Bueso 	while ((node = rb_first_cached(&sched->atom_root))) {
3163cb4c13a5SDavidlohr Bueso 		rb_erase_cached(node, &sched->atom_root);
31642f80dd44SJosef Bacik 		data = rb_entry(node, struct work_atoms, node);
31652f80dd44SJosef Bacik 		__merge_work_atoms(&sched->merged_atom_root, data);
31662f80dd44SJosef Bacik 	}
31672f80dd44SJosef Bacik }
31682f80dd44SJosef Bacik 
31690e9b07e5SArnaldo Carvalho de Melo static int perf_sched__lat(struct perf_sched *sched)
31700ec04e16SIngo Molnar {
31710ec04e16SIngo Molnar 	struct rb_node *next;
31720ec04e16SIngo Molnar 
31730ec04e16SIngo Molnar 	setup_pager();
3174ad9def7cSDavid Ahern 
3175ae536acfSArnaldo Carvalho de Melo 	if (perf_sched__read_events(sched))
3176a116e05dSArnaldo Carvalho de Melo 		return -1;
3177ad9def7cSDavid Ahern 
31782f80dd44SJosef Bacik 	perf_sched__merge_lat(sched);
31790e9b07e5SArnaldo Carvalho de Melo 	perf_sched__sort_lat(sched);
31800ec04e16SIngo Molnar 
3181dc000c45SJoel Fernandes (Google) 	printf("\n -------------------------------------------------------------------------------------------------------------------------------------------\n");
3182dc000c45SJoel Fernandes (Google) 	printf("  Task                  |   Runtime ms  | Switches | Avg delay ms    | Max delay ms    | Max delay start           | Max delay end          |\n");
3183dc000c45SJoel Fernandes (Google) 	printf(" -------------------------------------------------------------------------------------------------------------------------------------------\n");
31840ec04e16SIngo Molnar 
3185cb4c13a5SDavidlohr Bueso 	next = rb_first_cached(&sched->sorted_atom_root);
31860ec04e16SIngo Molnar 
31870ec04e16SIngo Molnar 	while (next) {
31880ec04e16SIngo Molnar 		struct work_atoms *work_list;
31890ec04e16SIngo Molnar 
31900ec04e16SIngo Molnar 		work_list = rb_entry(next, struct work_atoms, node);
31910e9b07e5SArnaldo Carvalho de Melo 		output_lat_thread(sched, work_list);
31920ec04e16SIngo Molnar 		next = rb_next(next);
3193ae536acfSArnaldo Carvalho de Melo 		thread__zput(work_list->thread);
31940ec04e16SIngo Molnar 	}
31950ec04e16SIngo Molnar 
319680790e0bSRamkumar Ramachandra 	printf(" -----------------------------------------------------------------------------------------------------------------\n");
31979486aa38SArnaldo Carvalho de Melo 	printf("  TOTAL:                |%11.3f ms |%9" PRIu64 " |\n",
31984fc76e49SArnaldo Carvalho de Melo 		(double)sched->all_runtime / NSEC_PER_MSEC, sched->all_count);
31990ec04e16SIngo Molnar 
32000ec04e16SIngo Molnar 	printf(" ---------------------------------------------------\n");
32010ec04e16SIngo Molnar 
32020e9b07e5SArnaldo Carvalho de Melo 	print_bad_events(sched);
32030ec04e16SIngo Molnar 	printf("\n");
32040ec04e16SIngo Molnar 
3205a116e05dSArnaldo Carvalho de Melo 	return 0;
32060ec04e16SIngo Molnar }
32070ec04e16SIngo Molnar 
320899623c62SJiri Olsa static int setup_map_cpus(struct perf_sched *sched)
32090ec04e16SIngo Molnar {
3210f854839bSJiri Olsa 	struct perf_cpu_map *map;
321173643bb6SJiri Olsa 
32120e9b07e5SArnaldo Carvalho de Melo 	sched->max_cpu  = sysconf(_SC_NPROCESSORS_CONF);
321340749d0fSIngo Molnar 
321499623c62SJiri Olsa 	if (sched->map.comp) {
321599623c62SJiri Olsa 		sched->map.comp_cpus = zalloc(sched->max_cpu * sizeof(int));
3216cf294f24SJiri Olsa 		if (!sched->map.comp_cpus)
3217cf294f24SJiri Olsa 			return -1;
321899623c62SJiri Olsa 	}
321999623c62SJiri Olsa 
322073643bb6SJiri Olsa 	if (!sched->map.cpus_str)
322173643bb6SJiri Olsa 		return 0;
322273643bb6SJiri Olsa 
32239c3516d1SJiri Olsa 	map = perf_cpu_map__new(sched->map.cpus_str);
322473643bb6SJiri Olsa 	if (!map) {
322573643bb6SJiri Olsa 		pr_err("failed to get cpus map from %s\n", sched->map.cpus_str);
322673643bb6SJiri Olsa 		return -1;
322773643bb6SJiri Olsa 	}
322873643bb6SJiri Olsa 
322973643bb6SJiri Olsa 	sched->map.cpus = map;
323099623c62SJiri Olsa 	return 0;
323199623c62SJiri Olsa }
323299623c62SJiri Olsa 
3233a151a37aSJiri Olsa static int setup_color_pids(struct perf_sched *sched)
3234a151a37aSJiri Olsa {
32359749b90eSJiri Olsa 	struct perf_thread_map *map;
3236a151a37aSJiri Olsa 
3237a151a37aSJiri Olsa 	if (!sched->map.color_pids_str)
3238a151a37aSJiri Olsa 		return 0;
3239a151a37aSJiri Olsa 
3240a151a37aSJiri Olsa 	map = thread_map__new_by_tid_str(sched->map.color_pids_str);
3241a151a37aSJiri Olsa 	if (!map) {
3242a151a37aSJiri Olsa 		pr_err("failed to get thread map from %s\n", sched->map.color_pids_str);
3243a151a37aSJiri Olsa 		return -1;
3244a151a37aSJiri Olsa 	}
3245a151a37aSJiri Olsa 
3246a151a37aSJiri Olsa 	sched->map.color_pids = map;
3247a151a37aSJiri Olsa 	return 0;
3248a151a37aSJiri Olsa }
3249a151a37aSJiri Olsa 
3250cf294f24SJiri Olsa static int setup_color_cpus(struct perf_sched *sched)
3251cf294f24SJiri Olsa {
3252f854839bSJiri Olsa 	struct perf_cpu_map *map;
3253cf294f24SJiri Olsa 
3254cf294f24SJiri Olsa 	if (!sched->map.color_cpus_str)
3255cf294f24SJiri Olsa 		return 0;
3256cf294f24SJiri Olsa 
32579c3516d1SJiri Olsa 	map = perf_cpu_map__new(sched->map.color_cpus_str);
3258cf294f24SJiri Olsa 	if (!map) {
3259cf294f24SJiri Olsa 		pr_err("failed to get thread map from %s\n", sched->map.color_cpus_str);
3260cf294f24SJiri Olsa 		return -1;
3261cf294f24SJiri Olsa 	}
3262cf294f24SJiri Olsa 
3263cf294f24SJiri Olsa 	sched->map.color_cpus = map;
3264cf294f24SJiri Olsa 	return 0;
3265cf294f24SJiri Olsa }
3266cf294f24SJiri Olsa 
326799623c62SJiri Olsa static int perf_sched__map(struct perf_sched *sched)
326899623c62SJiri Olsa {
326999623c62SJiri Olsa 	if (setup_map_cpus(sched))
327099623c62SJiri Olsa 		return -1;
327199623c62SJiri Olsa 
3272a151a37aSJiri Olsa 	if (setup_color_pids(sched))
3273a151a37aSJiri Olsa 		return -1;
3274a151a37aSJiri Olsa 
3275cf294f24SJiri Olsa 	if (setup_color_cpus(sched))
3276cf294f24SJiri Olsa 		return -1;
3277cf294f24SJiri Olsa 
32780ec04e16SIngo Molnar 	setup_pager();
3279ae536acfSArnaldo Carvalho de Melo 	if (perf_sched__read_events(sched))
3280a116e05dSArnaldo Carvalho de Melo 		return -1;
32810e9b07e5SArnaldo Carvalho de Melo 	print_bad_events(sched);
3282a116e05dSArnaldo Carvalho de Melo 	return 0;
32830ec04e16SIngo Molnar }
32840ec04e16SIngo Molnar 
32850e9b07e5SArnaldo Carvalho de Melo static int perf_sched__replay(struct perf_sched *sched)
32860ec04e16SIngo Molnar {
32870ec04e16SIngo Molnar 	unsigned long i;
32880ec04e16SIngo Molnar 
32890e9b07e5SArnaldo Carvalho de Melo 	calibrate_run_measurement_overhead(sched);
32900e9b07e5SArnaldo Carvalho de Melo 	calibrate_sleep_measurement_overhead(sched);
32910ec04e16SIngo Molnar 
32920e9b07e5SArnaldo Carvalho de Melo 	test_calibrations(sched);
32930ec04e16SIngo Molnar 
3294ae536acfSArnaldo Carvalho de Melo 	if (perf_sched__read_events(sched))
3295a116e05dSArnaldo Carvalho de Melo 		return -1;
32960ec04e16SIngo Molnar 
32970e9b07e5SArnaldo Carvalho de Melo 	printf("nr_run_events:        %ld\n", sched->nr_run_events);
32980e9b07e5SArnaldo Carvalho de Melo 	printf("nr_sleep_events:      %ld\n", sched->nr_sleep_events);
32990e9b07e5SArnaldo Carvalho de Melo 	printf("nr_wakeup_events:     %ld\n", sched->nr_wakeup_events);
33000ec04e16SIngo Molnar 
33010e9b07e5SArnaldo Carvalho de Melo 	if (sched->targetless_wakeups)
33020e9b07e5SArnaldo Carvalho de Melo 		printf("target-less wakeups:  %ld\n", sched->targetless_wakeups);
33030e9b07e5SArnaldo Carvalho de Melo 	if (sched->multitarget_wakeups)
33040e9b07e5SArnaldo Carvalho de Melo 		printf("multi-target wakeups: %ld\n", sched->multitarget_wakeups);
33050e9b07e5SArnaldo Carvalho de Melo 	if (sched->nr_run_events_optimized)
33060ec04e16SIngo Molnar 		printf("run atoms optimized: %ld\n",
33070e9b07e5SArnaldo Carvalho de Melo 			sched->nr_run_events_optimized);
33080ec04e16SIngo Molnar 
33090e9b07e5SArnaldo Carvalho de Melo 	print_task_traces(sched);
33100e9b07e5SArnaldo Carvalho de Melo 	add_cross_task_wakeups(sched);
33110ec04e16SIngo Molnar 
33120e9b07e5SArnaldo Carvalho de Melo 	create_tasks(sched);
33130ec04e16SIngo Molnar 	printf("------------------------------------------------------------\n");
33140e9b07e5SArnaldo Carvalho de Melo 	for (i = 0; i < sched->replay_repeat; i++)
33150e9b07e5SArnaldo Carvalho de Melo 		run_one_test(sched);
3316a116e05dSArnaldo Carvalho de Melo 
3317a116e05dSArnaldo Carvalho de Melo 	return 0;
33180ec04e16SIngo Molnar }
33190ec04e16SIngo Molnar 
33200e9b07e5SArnaldo Carvalho de Melo static void setup_sorting(struct perf_sched *sched, const struct option *options,
33210e9b07e5SArnaldo Carvalho de Melo 			  const char * const usage_msg[])
3322daa1d7a5SFrederic Weisbecker {
33230e9b07e5SArnaldo Carvalho de Melo 	char *tmp, *tok, *str = strdup(sched->sort_order);
3324daa1d7a5SFrederic Weisbecker 
3325daa1d7a5SFrederic Weisbecker 	for (tok = strtok_r(str, ", ", &tmp);
3326daa1d7a5SFrederic Weisbecker 			tok; tok = strtok_r(NULL, ", ", &tmp)) {
33270e9b07e5SArnaldo Carvalho de Melo 		if (sort_dimension__add(tok, &sched->sort_list) < 0) {
3328c7118369SNamhyung Kim 			usage_with_options_msg(usage_msg, options,
3329c7118369SNamhyung Kim 					"Unknown --sort key: `%s'", tok);
3330daa1d7a5SFrederic Weisbecker 		}
3331daa1d7a5SFrederic Weisbecker 	}
3332daa1d7a5SFrederic Weisbecker 
3333daa1d7a5SFrederic Weisbecker 	free(str);
3334daa1d7a5SFrederic Weisbecker 
33350e9b07e5SArnaldo Carvalho de Melo 	sort_dimension__add("pid", &sched->cmp_pid);
3336daa1d7a5SFrederic Weisbecker }
3337daa1d7a5SFrederic Weisbecker 
3338b0f00855SYang Jihong static bool schedstat_events_exposed(void)
3339b0f00855SYang Jihong {
3340b0f00855SYang Jihong 	/*
3341b0f00855SYang Jihong 	 * Select "sched:sched_stat_wait" event to check
3342b0f00855SYang Jihong 	 * whether schedstat tracepoints are exposed.
3343b0f00855SYang Jihong 	 */
3344b0f00855SYang Jihong 	return IS_ERR(trace_event__tp_format("sched", "sched_stat_wait")) ?
3345b0f00855SYang Jihong 		false : true;
3346b0f00855SYang Jihong }
3347b0f00855SYang Jihong 
33480e9b07e5SArnaldo Carvalho de Melo static int __cmd_record(int argc, const char **argv)
33490e9b07e5SArnaldo Carvalho de Melo {
33500e9b07e5SArnaldo Carvalho de Melo 	unsigned int rec_argc, i, j;
33510e9b07e5SArnaldo Carvalho de Melo 	const char **rec_argv;
33520e9b07e5SArnaldo Carvalho de Melo 	const char * const record_args[] = {
33531fc35b29SIngo Molnar 		"record",
33541fc35b29SIngo Molnar 		"-a",
33551fc35b29SIngo Molnar 		"-R",
3356dc02bf71SIngo Molnar 		"-m", "1024",
33571fc35b29SIngo Molnar 		"-c", "1",
33589710118bSStephane Eranian 		"-e", "sched:sched_switch",
33599710118bSStephane Eranian 		"-e", "sched:sched_stat_runtime",
33609710118bSStephane Eranian 		"-e", "sched:sched_process_fork",
33617fff9597SDongsheng 		"-e", "sched:sched_wakeup_new",
33629710118bSStephane Eranian 		"-e", "sched:sched_migrate_task",
33631fc35b29SIngo Molnar 	};
3364b0f00855SYang Jihong 
3365b0f00855SYang Jihong 	/*
3366b0f00855SYang Jihong 	 * The tracepoints trace_sched_stat_{wait, sleep, iowait}
3367b0f00855SYang Jihong 	 * are not exposed to user if CONFIG_SCHEDSTATS is not set,
3368b0f00855SYang Jihong 	 * to prevent "perf sched record" execution failure, determine
3369b0f00855SYang Jihong 	 * whether to record schedstat events according to actual situation.
3370b0f00855SYang Jihong 	 */
3371b0f00855SYang Jihong 	const char * const schedstat_args[] = {
3372b0f00855SYang Jihong 		"-e", "sched:sched_stat_wait",
3373b0f00855SYang Jihong 		"-e", "sched:sched_stat_sleep",
3374b0f00855SYang Jihong 		"-e", "sched:sched_stat_iowait",
3375b0f00855SYang Jihong 	};
3376b0f00855SYang Jihong 	unsigned int schedstat_argc = schedstat_events_exposed() ?
3377b0f00855SYang Jihong 		ARRAY_SIZE(schedstat_args) : 0;
3378b0f00855SYang Jihong 
3379d566a9c2SDavid Ahern 	struct tep_event *waking_event;
33801fc35b29SIngo Molnar 
3381d566a9c2SDavid Ahern 	/*
3382d566a9c2SDavid Ahern 	 * +2 for either "-e", "sched:sched_wakeup" or
3383d566a9c2SDavid Ahern 	 * "-e", "sched:sched_waking"
3384d566a9c2SDavid Ahern 	 */
3385b0f00855SYang Jihong 	rec_argc = ARRAY_SIZE(record_args) + 2 + schedstat_argc + argc - 1;
33861fc35b29SIngo Molnar 	rec_argv = calloc(rec_argc + 1, sizeof(char *));
33871fc35b29SIngo Molnar 
3388e462dc55SArnaldo Carvalho de Melo 	if (rec_argv == NULL)
3389ce47dc56SChris Samuel 		return -ENOMEM;
3390ce47dc56SChris Samuel 
33911fc35b29SIngo Molnar 	for (i = 0; i < ARRAY_SIZE(record_args); i++)
33921fc35b29SIngo Molnar 		rec_argv[i] = strdup(record_args[i]);
33931fc35b29SIngo Molnar 
3394d566a9c2SDavid Ahern 	rec_argv[i++] = "-e";
3395d566a9c2SDavid Ahern 	waking_event = trace_event__tp_format("sched", "sched_waking");
3396d566a9c2SDavid Ahern 	if (!IS_ERR(waking_event))
3397d566a9c2SDavid Ahern 		rec_argv[i++] = strdup("sched:sched_waking");
3398d566a9c2SDavid Ahern 	else
3399d566a9c2SDavid Ahern 		rec_argv[i++] = strdup("sched:sched_wakeup");
3400d566a9c2SDavid Ahern 
3401b0f00855SYang Jihong 	for (j = 0; j < schedstat_argc; j++)
3402b0f00855SYang Jihong 		rec_argv[i++] = strdup(schedstat_args[j]);
3403b0f00855SYang Jihong 
34041fc35b29SIngo Molnar 	for (j = 1; j < (unsigned int)argc; j++, i++)
34051fc35b29SIngo Molnar 		rec_argv[i] = argv[j];
34061fc35b29SIngo Molnar 
34071fc35b29SIngo Molnar 	BUG_ON(i != rec_argc);
34081fc35b29SIngo Molnar 
3409b0ad8ea6SArnaldo Carvalho de Melo 	return cmd_record(i, rec_argv);
34101fc35b29SIngo Molnar }
34111fc35b29SIngo Molnar 
3412b0ad8ea6SArnaldo Carvalho de Melo int cmd_sched(int argc, const char **argv)
34138a39df8fSAdrian Hunter {
341449b8e2beSRasmus Villemoes 	static const char default_sort_order[] = "avg, max, switch, runtime";
34158a39df8fSAdrian Hunter 	struct perf_sched sched = {
34160e9b07e5SArnaldo Carvalho de Melo 		.tool = {
34170e9b07e5SArnaldo Carvalho de Melo 			.sample		 = perf_sched__process_tracepoint_sample,
341899a3c3a9SChangbin Du 			.comm		 = perf_sched__process_comm,
3419f3b3614aSHari Bathini 			.namespaces	 = perf_event__process_namespaces,
34200e9b07e5SArnaldo Carvalho de Melo 			.lost		 = perf_event__process_lost,
3421cb627505SDavid Ahern 			.fork		 = perf_sched__process_fork_event,
34220a8cb85cSJiri Olsa 			.ordered_events = true,
34230e9b07e5SArnaldo Carvalho de Melo 		},
34240e9b07e5SArnaldo Carvalho de Melo 		.cmp_pid	      = LIST_HEAD_INIT(sched.cmp_pid),
34250e9b07e5SArnaldo Carvalho de Melo 		.sort_list	      = LIST_HEAD_INIT(sched.sort_list),
34260e9b07e5SArnaldo Carvalho de Melo 		.start_work_mutex     = PTHREAD_MUTEX_INITIALIZER,
34270e9b07e5SArnaldo Carvalho de Melo 		.work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER,
34280e9b07e5SArnaldo Carvalho de Melo 		.sort_order	      = default_sort_order,
34290e9b07e5SArnaldo Carvalho de Melo 		.replay_repeat	      = 10,
34300e9b07e5SArnaldo Carvalho de Melo 		.profile_cpu	      = -1,
34310e9b07e5SArnaldo Carvalho de Melo 		.next_shortname1      = 'A',
34320e9b07e5SArnaldo Carvalho de Melo 		.next_shortname2      = '0',
34332f80dd44SJosef Bacik 		.skip_merge           = 0,
34346c973c90SDavid Ahern 		.show_callchain	      = 1,
34356c973c90SDavid Ahern 		.max_stack            = 5,
34360e9b07e5SArnaldo Carvalho de Melo 	};
343777f02f44SNamhyung Kim 	const struct option sched_options[] = {
343877f02f44SNamhyung Kim 	OPT_STRING('i', "input", &input_name, "file",
343977f02f44SNamhyung Kim 		    "input file name"),
344077f02f44SNamhyung Kim 	OPT_INCR('v', "verbose", &verbose,
344177f02f44SNamhyung Kim 		    "be more verbose (show symbol address, etc)"),
344277f02f44SNamhyung Kim 	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
344377f02f44SNamhyung Kim 		    "dump raw trace in ASCII"),
34446fa94258SNamhyung Kim 	OPT_BOOLEAN('f', "force", &sched.force, "don't complain, do it"),
344577f02f44SNamhyung Kim 	OPT_END()
344677f02f44SNamhyung Kim 	};
34470e9b07e5SArnaldo Carvalho de Melo 	const struct option latency_options[] = {
34480e9b07e5SArnaldo Carvalho de Melo 	OPT_STRING('s', "sort", &sched.sort_order, "key[,key2...]",
34490e9b07e5SArnaldo Carvalho de Melo 		   "sort by key(s): runtime, switch, avg, max"),
34500e9b07e5SArnaldo Carvalho de Melo 	OPT_INTEGER('C', "CPU", &sched.profile_cpu,
34510e9b07e5SArnaldo Carvalho de Melo 		    "CPU to profile on"),
34522f80dd44SJosef Bacik 	OPT_BOOLEAN('p', "pids", &sched.skip_merge,
34532f80dd44SJosef Bacik 		    "latency stats per pid instead of per comm"),
345477f02f44SNamhyung Kim 	OPT_PARENT(sched_options)
34550e9b07e5SArnaldo Carvalho de Melo 	};
34560e9b07e5SArnaldo Carvalho de Melo 	const struct option replay_options[] = {
34570e9b07e5SArnaldo Carvalho de Melo 	OPT_UINTEGER('r', "repeat", &sched.replay_repeat,
34580e9b07e5SArnaldo Carvalho de Melo 		     "repeat the workload replay N times (-1: infinite)"),
345977f02f44SNamhyung Kim 	OPT_PARENT(sched_options)
34600e9b07e5SArnaldo Carvalho de Melo 	};
346199623c62SJiri Olsa 	const struct option map_options[] = {
346299623c62SJiri Olsa 	OPT_BOOLEAN(0, "compact", &sched.map.comp,
346399623c62SJiri Olsa 		    "map output in compact mode"),
3464a151a37aSJiri Olsa 	OPT_STRING(0, "color-pids", &sched.map.color_pids_str, "pids",
3465a151a37aSJiri Olsa 		   "highlight given pids in map"),
3466cf294f24SJiri Olsa 	OPT_STRING(0, "color-cpus", &sched.map.color_cpus_str, "cpus",
3467cf294f24SJiri Olsa                     "highlight given CPUs in map"),
346873643bb6SJiri Olsa 	OPT_STRING(0, "cpus", &sched.map.cpus_str, "cpus",
346973643bb6SJiri Olsa                     "display given CPUs in map"),
347077f02f44SNamhyung Kim 	OPT_PARENT(sched_options)
347199623c62SJiri Olsa 	};
347249394a2aSDavid Ahern 	const struct option timehist_options[] = {
347349394a2aSDavid Ahern 	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
347449394a2aSDavid Ahern 		   "file", "vmlinux pathname"),
347549394a2aSDavid Ahern 	OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
347649394a2aSDavid Ahern 		   "file", "kallsyms pathname"),
34776c973c90SDavid Ahern 	OPT_BOOLEAN('g', "call-graph", &sched.show_callchain,
34786c973c90SDavid Ahern 		    "Display call chains if present (default on)"),
34796c973c90SDavid Ahern 	OPT_UINTEGER(0, "max-stack", &sched.max_stack,
34806c973c90SDavid Ahern 		   "Maximum number of functions to display backtrace."),
348149394a2aSDavid Ahern 	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
348249394a2aSDavid Ahern 		    "Look for files with symbols relative to this directory"),
348352df138cSDavid Ahern 	OPT_BOOLEAN('s', "summary", &sched.summary_only,
348452df138cSDavid Ahern 		    "Show only syscall summary with statistics"),
348552df138cSDavid Ahern 	OPT_BOOLEAN('S', "with-summary", &sched.summary,
348652df138cSDavid Ahern 		    "Show all syscalls and summary with statistics"),
3487fc1469f1SDavid Ahern 	OPT_BOOLEAN('w', "wakeups", &sched.show_wakeups, "Show wakeup events"),
3488292c4a8fSBrendan Gregg 	OPT_BOOLEAN('n', "next", &sched.show_next, "Show next task"),
3489350f54faSDavid Ahern 	OPT_BOOLEAN('M', "migrations", &sched.show_migrations, "Show migration events"),
3490a407b067SDavid Ahern 	OPT_BOOLEAN('V', "cpu-visual", &sched.show_cpu_visual, "Add CPU visual"),
349107235f84SNamhyung Kim 	OPT_BOOLEAN('I', "idle-hist", &sched.idle_hist, "Show idle events only"),
3492853b7407SDavid Ahern 	OPT_STRING(0, "time", &sched.time_str, "str",
3493853b7407SDavid Ahern 		   "Time span for analysis (start,stop)"),
3494414e050cSNamhyung Kim 	OPT_BOOLEAN(0, "state", &sched.show_state, "Show task state when sched-out"),
34950f59d7a3SDavid Ahern 	OPT_STRING('p', "pid", &symbol_conf.pid_list_str, "pid[,pid...]",
34960f59d7a3SDavid Ahern 		   "analyze events only for given process id(s)"),
34970f59d7a3SDavid Ahern 	OPT_STRING('t', "tid", &symbol_conf.tid_list_str, "tid[,tid...]",
34980f59d7a3SDavid Ahern 		   "analyze events only for given thread id(s)"),
3499c30d630dSDavid Ahern 	OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
350049394a2aSDavid Ahern 	OPT_PARENT(sched_options)
350149394a2aSDavid Ahern 	};
350249394a2aSDavid Ahern 
35030e9b07e5SArnaldo Carvalho de Melo 	const char * const latency_usage[] = {
35040e9b07e5SArnaldo Carvalho de Melo 		"perf sched latency [<options>]",
35050e9b07e5SArnaldo Carvalho de Melo 		NULL
35060e9b07e5SArnaldo Carvalho de Melo 	};
35070e9b07e5SArnaldo Carvalho de Melo 	const char * const replay_usage[] = {
35080e9b07e5SArnaldo Carvalho de Melo 		"perf sched replay [<options>]",
35090e9b07e5SArnaldo Carvalho de Melo 		NULL
35100e9b07e5SArnaldo Carvalho de Melo 	};
351199623c62SJiri Olsa 	const char * const map_usage[] = {
351299623c62SJiri Olsa 		"perf sched map [<options>]",
351399623c62SJiri Olsa 		NULL
351499623c62SJiri Olsa 	};
351549394a2aSDavid Ahern 	const char * const timehist_usage[] = {
351649394a2aSDavid Ahern 		"perf sched timehist [<options>]",
351749394a2aSDavid Ahern 		NULL
351849394a2aSDavid Ahern 	};
3519a83edb2dSRamkumar Ramachandra 	const char *const sched_subcommands[] = { "record", "latency", "map",
352049394a2aSDavid Ahern 						  "replay", "script",
352149394a2aSDavid Ahern 						  "timehist", NULL };
3522a83edb2dSRamkumar Ramachandra 	const char *sched_usage[] = {
3523a83edb2dSRamkumar Ramachandra 		NULL,
35240e9b07e5SArnaldo Carvalho de Melo 		NULL
35250e9b07e5SArnaldo Carvalho de Melo 	};
35260e9b07e5SArnaldo Carvalho de Melo 	struct trace_sched_handler lat_ops  = {
35270e9b07e5SArnaldo Carvalho de Melo 		.wakeup_event	    = latency_wakeup_event,
35280e9b07e5SArnaldo Carvalho de Melo 		.switch_event	    = latency_switch_event,
35290e9b07e5SArnaldo Carvalho de Melo 		.runtime_event	    = latency_runtime_event,
35300e9b07e5SArnaldo Carvalho de Melo 		.migrate_task_event = latency_migrate_task_event,
35310e9b07e5SArnaldo Carvalho de Melo 	};
35320e9b07e5SArnaldo Carvalho de Melo 	struct trace_sched_handler map_ops  = {
35330e9b07e5SArnaldo Carvalho de Melo 		.switch_event	    = map_switch_event,
35340e9b07e5SArnaldo Carvalho de Melo 	};
35350e9b07e5SArnaldo Carvalho de Melo 	struct trace_sched_handler replay_ops  = {
35360e9b07e5SArnaldo Carvalho de Melo 		.wakeup_event	    = replay_wakeup_event,
35370e9b07e5SArnaldo Carvalho de Melo 		.switch_event	    = replay_switch_event,
35380e9b07e5SArnaldo Carvalho de Melo 		.fork_event	    = replay_fork_event,
35390e9b07e5SArnaldo Carvalho de Melo 	};
3540156a2b02SAdrian Hunter 	unsigned int i;
35417cc72553SJames Clark 	int ret;
3542156a2b02SAdrian Hunter 
3543156a2b02SAdrian Hunter 	for (i = 0; i < ARRAY_SIZE(sched.curr_pid); i++)
3544156a2b02SAdrian Hunter 		sched.curr_pid[i] = -1;
35450e9b07e5SArnaldo Carvalho de Melo 
3546a83edb2dSRamkumar Ramachandra 	argc = parse_options_subcommand(argc, argv, sched_options, sched_subcommands,
3547a83edb2dSRamkumar Ramachandra 					sched_usage, PARSE_OPT_STOP_AT_NON_OPTION);
3548f2858d8aSIngo Molnar 	if (!argc)
3549f2858d8aSIngo Molnar 		usage_with_options(sched_usage, sched_options);
3550f2858d8aSIngo Molnar 
3551c0777c5aSXiao Guangrong 	/*
3552133dc4c3SIngo Molnar 	 * Aliased to 'perf script' for now:
3553c0777c5aSXiao Guangrong 	 */
3554133dc4c3SIngo Molnar 	if (!strcmp(argv[0], "script"))
3555b0ad8ea6SArnaldo Carvalho de Melo 		return cmd_script(argc, argv);
3556c0777c5aSXiao Guangrong 
35571fc35b29SIngo Molnar 	if (!strncmp(argv[0], "rec", 3)) {
35581fc35b29SIngo Molnar 		return __cmd_record(argc, argv);
35591fc35b29SIngo Molnar 	} else if (!strncmp(argv[0], "lat", 3)) {
35600e9b07e5SArnaldo Carvalho de Melo 		sched.tp_handler = &lat_ops;
3561f2858d8aSIngo Molnar 		if (argc > 1) {
3562f2858d8aSIngo Molnar 			argc = parse_options(argc, argv, latency_options, latency_usage, 0);
3563f2858d8aSIngo Molnar 			if (argc)
3564f2858d8aSIngo Molnar 				usage_with_options(latency_usage, latency_options);
3565f2858d8aSIngo Molnar 		}
35660e9b07e5SArnaldo Carvalho de Melo 		setup_sorting(&sched, latency_options, latency_usage);
35670e9b07e5SArnaldo Carvalho de Melo 		return perf_sched__lat(&sched);
35680ec04e16SIngo Molnar 	} else if (!strcmp(argv[0], "map")) {
356999623c62SJiri Olsa 		if (argc) {
3570a151a37aSJiri Olsa 			argc = parse_options(argc, argv, map_options, map_usage, 0);
357199623c62SJiri Olsa 			if (argc)
357299623c62SJiri Olsa 				usage_with_options(map_usage, map_options);
357399623c62SJiri Olsa 		}
35740e9b07e5SArnaldo Carvalho de Melo 		sched.tp_handler = &map_ops;
35750e9b07e5SArnaldo Carvalho de Melo 		setup_sorting(&sched, latency_options, latency_usage);
35760e9b07e5SArnaldo Carvalho de Melo 		return perf_sched__map(&sched);
3577f2858d8aSIngo Molnar 	} else if (!strncmp(argv[0], "rep", 3)) {
35780e9b07e5SArnaldo Carvalho de Melo 		sched.tp_handler = &replay_ops;
35790a02ad93SIngo Molnar 		if (argc) {
3580f2858d8aSIngo Molnar 			argc = parse_options(argc, argv, replay_options, replay_usage, 0);
3581f2858d8aSIngo Molnar 			if (argc)
3582f2858d8aSIngo Molnar 				usage_with_options(replay_usage, replay_options);
3583f2858d8aSIngo Molnar 		}
35840e9b07e5SArnaldo Carvalho de Melo 		return perf_sched__replay(&sched);
358549394a2aSDavid Ahern 	} else if (!strcmp(argv[0], "timehist")) {
358649394a2aSDavid Ahern 		if (argc) {
358749394a2aSDavid Ahern 			argc = parse_options(argc, argv, timehist_options,
358849394a2aSDavid Ahern 					     timehist_usage, 0);
358949394a2aSDavid Ahern 			if (argc)
359049394a2aSDavid Ahern 				usage_with_options(timehist_usage, timehist_options);
359149394a2aSDavid Ahern 		}
3592292c4a8fSBrendan Gregg 		if ((sched.show_wakeups || sched.show_next) &&
3593292c4a8fSBrendan Gregg 		    sched.summary_only) {
3594292c4a8fSBrendan Gregg 			pr_err(" Error: -s and -[n|w] are mutually exclusive.\n");
3595fc1469f1SDavid Ahern 			parse_options_usage(timehist_usage, timehist_options, "s", true);
3596292c4a8fSBrendan Gregg 			if (sched.show_wakeups)
3597fc1469f1SDavid Ahern 				parse_options_usage(NULL, timehist_options, "w", true);
3598292c4a8fSBrendan Gregg 			if (sched.show_next)
3599292c4a8fSBrendan Gregg 				parse_options_usage(NULL, timehist_options, "n", true);
3600fc1469f1SDavid Ahern 			return -EINVAL;
3601fc1469f1SDavid Ahern 		}
36027cc72553SJames Clark 		ret = symbol__validate_sym_arguments();
36037cc72553SJames Clark 		if (ret)
36047cc72553SJames Clark 			return ret;
3605fc1469f1SDavid Ahern 
360649394a2aSDavid Ahern 		return perf_sched__timehist(&sched);
3607f2858d8aSIngo Molnar 	} else {
3608f2858d8aSIngo Molnar 		usage_with_options(sched_usage, sched_options);
36090a02ad93SIngo Molnar 	}
36100a02ad93SIngo Molnar 
3611ec156764SIngo Molnar 	return 0;
36120a02ad93SIngo Molnar }
3613