xref: /OK3568_Linux_fs/kernel/tools/perf/builtin-timechart.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * builtin-timechart.c - make an svg timechart of system activity
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * (C) Copyright 2009 Intel Corporation
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Authors:
8*4882a593Smuzhiyun  *     Arjan van de Ven <arjan@linux.intel.com>
9*4882a593Smuzhiyun  */
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include <errno.h>
12*4882a593Smuzhiyun #include <inttypes.h>
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include "builtin.h"
15*4882a593Smuzhiyun #include "util/color.h"
16*4882a593Smuzhiyun #include <linux/list.h>
17*4882a593Smuzhiyun #include "util/evlist.h" // for struct evsel_str_handler
18*4882a593Smuzhiyun #include "util/evsel.h"
19*4882a593Smuzhiyun #include <linux/kernel.h>
20*4882a593Smuzhiyun #include <linux/rbtree.h>
21*4882a593Smuzhiyun #include <linux/time64.h>
22*4882a593Smuzhiyun #include <linux/zalloc.h>
23*4882a593Smuzhiyun #include "util/symbol.h"
24*4882a593Smuzhiyun #include "util/thread.h"
25*4882a593Smuzhiyun #include "util/callchain.h"
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun #include "perf.h"
28*4882a593Smuzhiyun #include "util/header.h"
29*4882a593Smuzhiyun #include <subcmd/pager.h>
30*4882a593Smuzhiyun #include <subcmd/parse-options.h>
31*4882a593Smuzhiyun #include "util/parse-events.h"
32*4882a593Smuzhiyun #include "util/event.h"
33*4882a593Smuzhiyun #include "util/session.h"
34*4882a593Smuzhiyun #include "util/svghelper.h"
35*4882a593Smuzhiyun #include "util/tool.h"
36*4882a593Smuzhiyun #include "util/data.h"
37*4882a593Smuzhiyun #include "util/debug.h"
38*4882a593Smuzhiyun #include <linux/err.h>
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun #ifdef LACKS_OPEN_MEMSTREAM_PROTOTYPE
41*4882a593Smuzhiyun FILE *open_memstream(char **ptr, size_t *sizeloc);
42*4882a593Smuzhiyun #endif
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun #define SUPPORT_OLD_POWER_EVENTS 1
45*4882a593Smuzhiyun #define PWR_EVENT_EXIT -1
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun struct per_pid;
48*4882a593Smuzhiyun struct power_event;
49*4882a593Smuzhiyun struct wake_event;
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun struct timechart {
52*4882a593Smuzhiyun 	struct perf_tool	tool;
53*4882a593Smuzhiyun 	struct per_pid		*all_data;
54*4882a593Smuzhiyun 	struct power_event	*power_events;
55*4882a593Smuzhiyun 	struct wake_event	*wake_events;
56*4882a593Smuzhiyun 	int			proc_num;
57*4882a593Smuzhiyun 	unsigned int		numcpus;
58*4882a593Smuzhiyun 	u64			min_freq,	/* Lowest CPU frequency seen */
59*4882a593Smuzhiyun 				max_freq,	/* Highest CPU frequency seen */
60*4882a593Smuzhiyun 				turbo_frequency,
61*4882a593Smuzhiyun 				first_time, last_time;
62*4882a593Smuzhiyun 	bool			power_only,
63*4882a593Smuzhiyun 				tasks_only,
64*4882a593Smuzhiyun 				with_backtrace,
65*4882a593Smuzhiyun 				topology;
66*4882a593Smuzhiyun 	bool			force;
67*4882a593Smuzhiyun 	/* IO related settings */
68*4882a593Smuzhiyun 	bool			io_only,
69*4882a593Smuzhiyun 				skip_eagain;
70*4882a593Smuzhiyun 	u64			io_events;
71*4882a593Smuzhiyun 	u64			min_time,
72*4882a593Smuzhiyun 				merge_dist;
73*4882a593Smuzhiyun };
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun struct per_pidcomm;
76*4882a593Smuzhiyun struct cpu_sample;
77*4882a593Smuzhiyun struct io_sample;
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun /*
80*4882a593Smuzhiyun  * Datastructure layout:
81*4882a593Smuzhiyun  * We keep an list of "pid"s, matching the kernels notion of a task struct.
82*4882a593Smuzhiyun  * Each "pid" entry, has a list of "comm"s.
83*4882a593Smuzhiyun  *	this is because we want to track different programs different, while
84*4882a593Smuzhiyun  *	exec will reuse the original pid (by design).
85*4882a593Smuzhiyun  * Each comm has a list of samples that will be used to draw
86*4882a593Smuzhiyun  * final graph.
87*4882a593Smuzhiyun  */
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun struct per_pid {
90*4882a593Smuzhiyun 	struct per_pid *next;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	int		pid;
93*4882a593Smuzhiyun 	int		ppid;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	u64		start_time;
96*4882a593Smuzhiyun 	u64		end_time;
97*4882a593Smuzhiyun 	u64		total_time;
98*4882a593Smuzhiyun 	u64		total_bytes;
99*4882a593Smuzhiyun 	int		display;
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	struct per_pidcomm *all;
102*4882a593Smuzhiyun 	struct per_pidcomm *current;
103*4882a593Smuzhiyun };
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun struct per_pidcomm {
107*4882a593Smuzhiyun 	struct per_pidcomm *next;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	u64		start_time;
110*4882a593Smuzhiyun 	u64		end_time;
111*4882a593Smuzhiyun 	u64		total_time;
112*4882a593Smuzhiyun 	u64		max_bytes;
113*4882a593Smuzhiyun 	u64		total_bytes;
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	int		Y;
116*4882a593Smuzhiyun 	int		display;
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	long		state;
119*4882a593Smuzhiyun 	u64		state_since;
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	char		*comm;
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	struct cpu_sample *samples;
124*4882a593Smuzhiyun 	struct io_sample  *io_samples;
125*4882a593Smuzhiyun };
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun struct sample_wrapper {
128*4882a593Smuzhiyun 	struct sample_wrapper *next;
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	u64		timestamp;
131*4882a593Smuzhiyun 	unsigned char	data[];
132*4882a593Smuzhiyun };
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun #define TYPE_NONE	0
135*4882a593Smuzhiyun #define TYPE_RUNNING	1
136*4882a593Smuzhiyun #define TYPE_WAITING	2
137*4882a593Smuzhiyun #define TYPE_BLOCKED	3
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun struct cpu_sample {
140*4882a593Smuzhiyun 	struct cpu_sample *next;
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	u64 start_time;
143*4882a593Smuzhiyun 	u64 end_time;
144*4882a593Smuzhiyun 	int type;
145*4882a593Smuzhiyun 	int cpu;
146*4882a593Smuzhiyun 	const char *backtrace;
147*4882a593Smuzhiyun };
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun enum {
150*4882a593Smuzhiyun 	IOTYPE_READ,
151*4882a593Smuzhiyun 	IOTYPE_WRITE,
152*4882a593Smuzhiyun 	IOTYPE_SYNC,
153*4882a593Smuzhiyun 	IOTYPE_TX,
154*4882a593Smuzhiyun 	IOTYPE_RX,
155*4882a593Smuzhiyun 	IOTYPE_POLL,
156*4882a593Smuzhiyun };
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun struct io_sample {
159*4882a593Smuzhiyun 	struct io_sample *next;
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	u64 start_time;
162*4882a593Smuzhiyun 	u64 end_time;
163*4882a593Smuzhiyun 	u64 bytes;
164*4882a593Smuzhiyun 	int type;
165*4882a593Smuzhiyun 	int fd;
166*4882a593Smuzhiyun 	int err;
167*4882a593Smuzhiyun 	int merges;
168*4882a593Smuzhiyun };
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun #define CSTATE 1
171*4882a593Smuzhiyun #define PSTATE 2
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun struct power_event {
174*4882a593Smuzhiyun 	struct power_event *next;
175*4882a593Smuzhiyun 	int type;
176*4882a593Smuzhiyun 	int state;
177*4882a593Smuzhiyun 	u64 start_time;
178*4882a593Smuzhiyun 	u64 end_time;
179*4882a593Smuzhiyun 	int cpu;
180*4882a593Smuzhiyun };
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun struct wake_event {
183*4882a593Smuzhiyun 	struct wake_event *next;
184*4882a593Smuzhiyun 	int waker;
185*4882a593Smuzhiyun 	int wakee;
186*4882a593Smuzhiyun 	u64 time;
187*4882a593Smuzhiyun 	const char *backtrace;
188*4882a593Smuzhiyun };
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun struct process_filter {
191*4882a593Smuzhiyun 	char			*name;
192*4882a593Smuzhiyun 	int			pid;
193*4882a593Smuzhiyun 	struct process_filter	*next;
194*4882a593Smuzhiyun };
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun static struct process_filter *process_filter;
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 
find_create_pid(struct timechart * tchart,int pid)199*4882a593Smuzhiyun static struct per_pid *find_create_pid(struct timechart *tchart, int pid)
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun 	struct per_pid *cursor = tchart->all_data;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	while (cursor) {
204*4882a593Smuzhiyun 		if (cursor->pid == pid)
205*4882a593Smuzhiyun 			return cursor;
206*4882a593Smuzhiyun 		cursor = cursor->next;
207*4882a593Smuzhiyun 	}
208*4882a593Smuzhiyun 	cursor = zalloc(sizeof(*cursor));
209*4882a593Smuzhiyun 	assert(cursor != NULL);
210*4882a593Smuzhiyun 	cursor->pid = pid;
211*4882a593Smuzhiyun 	cursor->next = tchart->all_data;
212*4882a593Smuzhiyun 	tchart->all_data = cursor;
213*4882a593Smuzhiyun 	return cursor;
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun 
pid_set_comm(struct timechart * tchart,int pid,char * comm)216*4882a593Smuzhiyun static void pid_set_comm(struct timechart *tchart, int pid, char *comm)
217*4882a593Smuzhiyun {
218*4882a593Smuzhiyun 	struct per_pid *p;
219*4882a593Smuzhiyun 	struct per_pidcomm *c;
220*4882a593Smuzhiyun 	p = find_create_pid(tchart, pid);
221*4882a593Smuzhiyun 	c = p->all;
222*4882a593Smuzhiyun 	while (c) {
223*4882a593Smuzhiyun 		if (c->comm && strcmp(c->comm, comm) == 0) {
224*4882a593Smuzhiyun 			p->current = c;
225*4882a593Smuzhiyun 			return;
226*4882a593Smuzhiyun 		}
227*4882a593Smuzhiyun 		if (!c->comm) {
228*4882a593Smuzhiyun 			c->comm = strdup(comm);
229*4882a593Smuzhiyun 			p->current = c;
230*4882a593Smuzhiyun 			return;
231*4882a593Smuzhiyun 		}
232*4882a593Smuzhiyun 		c = c->next;
233*4882a593Smuzhiyun 	}
234*4882a593Smuzhiyun 	c = zalloc(sizeof(*c));
235*4882a593Smuzhiyun 	assert(c != NULL);
236*4882a593Smuzhiyun 	c->comm = strdup(comm);
237*4882a593Smuzhiyun 	p->current = c;
238*4882a593Smuzhiyun 	c->next = p->all;
239*4882a593Smuzhiyun 	p->all = c;
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun 
pid_fork(struct timechart * tchart,int pid,int ppid,u64 timestamp)242*4882a593Smuzhiyun static void pid_fork(struct timechart *tchart, int pid, int ppid, u64 timestamp)
243*4882a593Smuzhiyun {
244*4882a593Smuzhiyun 	struct per_pid *p, *pp;
245*4882a593Smuzhiyun 	p = find_create_pid(tchart, pid);
246*4882a593Smuzhiyun 	pp = find_create_pid(tchart, ppid);
247*4882a593Smuzhiyun 	p->ppid = ppid;
248*4882a593Smuzhiyun 	if (pp->current && pp->current->comm && !p->current)
249*4882a593Smuzhiyun 		pid_set_comm(tchart, pid, pp->current->comm);
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	p->start_time = timestamp;
252*4882a593Smuzhiyun 	if (p->current && !p->current->start_time) {
253*4882a593Smuzhiyun 		p->current->start_time = timestamp;
254*4882a593Smuzhiyun 		p->current->state_since = timestamp;
255*4882a593Smuzhiyun 	}
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun 
pid_exit(struct timechart * tchart,int pid,u64 timestamp)258*4882a593Smuzhiyun static void pid_exit(struct timechart *tchart, int pid, u64 timestamp)
259*4882a593Smuzhiyun {
260*4882a593Smuzhiyun 	struct per_pid *p;
261*4882a593Smuzhiyun 	p = find_create_pid(tchart, pid);
262*4882a593Smuzhiyun 	p->end_time = timestamp;
263*4882a593Smuzhiyun 	if (p->current)
264*4882a593Smuzhiyun 		p->current->end_time = timestamp;
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun 
pid_put_sample(struct timechart * tchart,int pid,int type,unsigned int cpu,u64 start,u64 end,const char * backtrace)267*4882a593Smuzhiyun static void pid_put_sample(struct timechart *tchart, int pid, int type,
268*4882a593Smuzhiyun 			   unsigned int cpu, u64 start, u64 end,
269*4882a593Smuzhiyun 			   const char *backtrace)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun 	struct per_pid *p;
272*4882a593Smuzhiyun 	struct per_pidcomm *c;
273*4882a593Smuzhiyun 	struct cpu_sample *sample;
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	p = find_create_pid(tchart, pid);
276*4882a593Smuzhiyun 	c = p->current;
277*4882a593Smuzhiyun 	if (!c) {
278*4882a593Smuzhiyun 		c = zalloc(sizeof(*c));
279*4882a593Smuzhiyun 		assert(c != NULL);
280*4882a593Smuzhiyun 		p->current = c;
281*4882a593Smuzhiyun 		c->next = p->all;
282*4882a593Smuzhiyun 		p->all = c;
283*4882a593Smuzhiyun 	}
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	sample = zalloc(sizeof(*sample));
286*4882a593Smuzhiyun 	assert(sample != NULL);
287*4882a593Smuzhiyun 	sample->start_time = start;
288*4882a593Smuzhiyun 	sample->end_time = end;
289*4882a593Smuzhiyun 	sample->type = type;
290*4882a593Smuzhiyun 	sample->next = c->samples;
291*4882a593Smuzhiyun 	sample->cpu = cpu;
292*4882a593Smuzhiyun 	sample->backtrace = backtrace;
293*4882a593Smuzhiyun 	c->samples = sample;
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun 	if (sample->type == TYPE_RUNNING && end > start && start > 0) {
296*4882a593Smuzhiyun 		c->total_time += (end-start);
297*4882a593Smuzhiyun 		p->total_time += (end-start);
298*4882a593Smuzhiyun 	}
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	if (c->start_time == 0 || c->start_time > start)
301*4882a593Smuzhiyun 		c->start_time = start;
302*4882a593Smuzhiyun 	if (p->start_time == 0 || p->start_time > start)
303*4882a593Smuzhiyun 		p->start_time = start;
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun #define MAX_CPUS 4096
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun static u64 cpus_cstate_start_times[MAX_CPUS];
309*4882a593Smuzhiyun static int cpus_cstate_state[MAX_CPUS];
310*4882a593Smuzhiyun static u64 cpus_pstate_start_times[MAX_CPUS];
311*4882a593Smuzhiyun static u64 cpus_pstate_state[MAX_CPUS];
312*4882a593Smuzhiyun 
process_comm_event(struct perf_tool * tool,union perf_event * event,struct perf_sample * sample __maybe_unused,struct machine * machine __maybe_unused)313*4882a593Smuzhiyun static int process_comm_event(struct perf_tool *tool,
314*4882a593Smuzhiyun 			      union perf_event *event,
315*4882a593Smuzhiyun 			      struct perf_sample *sample __maybe_unused,
316*4882a593Smuzhiyun 			      struct machine *machine __maybe_unused)
317*4882a593Smuzhiyun {
318*4882a593Smuzhiyun 	struct timechart *tchart = container_of(tool, struct timechart, tool);
319*4882a593Smuzhiyun 	pid_set_comm(tchart, event->comm.tid, event->comm.comm);
320*4882a593Smuzhiyun 	return 0;
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun 
process_fork_event(struct perf_tool * tool,union perf_event * event,struct perf_sample * sample __maybe_unused,struct machine * machine __maybe_unused)323*4882a593Smuzhiyun static int process_fork_event(struct perf_tool *tool,
324*4882a593Smuzhiyun 			      union perf_event *event,
325*4882a593Smuzhiyun 			      struct perf_sample *sample __maybe_unused,
326*4882a593Smuzhiyun 			      struct machine *machine __maybe_unused)
327*4882a593Smuzhiyun {
328*4882a593Smuzhiyun 	struct timechart *tchart = container_of(tool, struct timechart, tool);
329*4882a593Smuzhiyun 	pid_fork(tchart, event->fork.pid, event->fork.ppid, event->fork.time);
330*4882a593Smuzhiyun 	return 0;
331*4882a593Smuzhiyun }
332*4882a593Smuzhiyun 
process_exit_event(struct perf_tool * tool,union perf_event * event,struct perf_sample * sample __maybe_unused,struct machine * machine __maybe_unused)333*4882a593Smuzhiyun static int process_exit_event(struct perf_tool *tool,
334*4882a593Smuzhiyun 			      union perf_event *event,
335*4882a593Smuzhiyun 			      struct perf_sample *sample __maybe_unused,
336*4882a593Smuzhiyun 			      struct machine *machine __maybe_unused)
337*4882a593Smuzhiyun {
338*4882a593Smuzhiyun 	struct timechart *tchart = container_of(tool, struct timechart, tool);
339*4882a593Smuzhiyun 	pid_exit(tchart, event->fork.pid, event->fork.time);
340*4882a593Smuzhiyun 	return 0;
341*4882a593Smuzhiyun }
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun #ifdef SUPPORT_OLD_POWER_EVENTS
344*4882a593Smuzhiyun static int use_old_power_events;
345*4882a593Smuzhiyun #endif
346*4882a593Smuzhiyun 
c_state_start(int cpu,u64 timestamp,int state)347*4882a593Smuzhiyun static void c_state_start(int cpu, u64 timestamp, int state)
348*4882a593Smuzhiyun {
349*4882a593Smuzhiyun 	cpus_cstate_start_times[cpu] = timestamp;
350*4882a593Smuzhiyun 	cpus_cstate_state[cpu] = state;
351*4882a593Smuzhiyun }
352*4882a593Smuzhiyun 
c_state_end(struct timechart * tchart,int cpu,u64 timestamp)353*4882a593Smuzhiyun static void c_state_end(struct timechart *tchart, int cpu, u64 timestamp)
354*4882a593Smuzhiyun {
355*4882a593Smuzhiyun 	struct power_event *pwr = zalloc(sizeof(*pwr));
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun 	if (!pwr)
358*4882a593Smuzhiyun 		return;
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	pwr->state = cpus_cstate_state[cpu];
361*4882a593Smuzhiyun 	pwr->start_time = cpus_cstate_start_times[cpu];
362*4882a593Smuzhiyun 	pwr->end_time = timestamp;
363*4882a593Smuzhiyun 	pwr->cpu = cpu;
364*4882a593Smuzhiyun 	pwr->type = CSTATE;
365*4882a593Smuzhiyun 	pwr->next = tchart->power_events;
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun 	tchart->power_events = pwr;
368*4882a593Smuzhiyun }
369*4882a593Smuzhiyun 
p_state_change(struct timechart * tchart,int cpu,u64 timestamp,u64 new_freq)370*4882a593Smuzhiyun static void p_state_change(struct timechart *tchart, int cpu, u64 timestamp, u64 new_freq)
371*4882a593Smuzhiyun {
372*4882a593Smuzhiyun 	struct power_event *pwr;
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun 	if (new_freq > 8000000) /* detect invalid data */
375*4882a593Smuzhiyun 		return;
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 	pwr = zalloc(sizeof(*pwr));
378*4882a593Smuzhiyun 	if (!pwr)
379*4882a593Smuzhiyun 		return;
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun 	pwr->state = cpus_pstate_state[cpu];
382*4882a593Smuzhiyun 	pwr->start_time = cpus_pstate_start_times[cpu];
383*4882a593Smuzhiyun 	pwr->end_time = timestamp;
384*4882a593Smuzhiyun 	pwr->cpu = cpu;
385*4882a593Smuzhiyun 	pwr->type = PSTATE;
386*4882a593Smuzhiyun 	pwr->next = tchart->power_events;
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	if (!pwr->start_time)
389*4882a593Smuzhiyun 		pwr->start_time = tchart->first_time;
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	tchart->power_events = pwr;
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 	cpus_pstate_state[cpu] = new_freq;
394*4882a593Smuzhiyun 	cpus_pstate_start_times[cpu] = timestamp;
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun 	if ((u64)new_freq > tchart->max_freq)
397*4882a593Smuzhiyun 		tchart->max_freq = new_freq;
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun 	if (new_freq < tchart->min_freq || tchart->min_freq == 0)
400*4882a593Smuzhiyun 		tchart->min_freq = new_freq;
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	if (new_freq == tchart->max_freq - 1000)
403*4882a593Smuzhiyun 		tchart->turbo_frequency = tchart->max_freq;
404*4882a593Smuzhiyun }
405*4882a593Smuzhiyun 
sched_wakeup(struct timechart * tchart,int cpu,u64 timestamp,int waker,int wakee,u8 flags,const char * backtrace)406*4882a593Smuzhiyun static void sched_wakeup(struct timechart *tchart, int cpu, u64 timestamp,
407*4882a593Smuzhiyun 			 int waker, int wakee, u8 flags, const char *backtrace)
408*4882a593Smuzhiyun {
409*4882a593Smuzhiyun 	struct per_pid *p;
410*4882a593Smuzhiyun 	struct wake_event *we = zalloc(sizeof(*we));
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	if (!we)
413*4882a593Smuzhiyun 		return;
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	we->time = timestamp;
416*4882a593Smuzhiyun 	we->waker = waker;
417*4882a593Smuzhiyun 	we->backtrace = backtrace;
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun 	if ((flags & TRACE_FLAG_HARDIRQ) || (flags & TRACE_FLAG_SOFTIRQ))
420*4882a593Smuzhiyun 		we->waker = -1;
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 	we->wakee = wakee;
423*4882a593Smuzhiyun 	we->next = tchart->wake_events;
424*4882a593Smuzhiyun 	tchart->wake_events = we;
425*4882a593Smuzhiyun 	p = find_create_pid(tchart, we->wakee);
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun 	if (p && p->current && p->current->state == TYPE_NONE) {
428*4882a593Smuzhiyun 		p->current->state_since = timestamp;
429*4882a593Smuzhiyun 		p->current->state = TYPE_WAITING;
430*4882a593Smuzhiyun 	}
431*4882a593Smuzhiyun 	if (p && p->current && p->current->state == TYPE_BLOCKED) {
432*4882a593Smuzhiyun 		pid_put_sample(tchart, p->pid, p->current->state, cpu,
433*4882a593Smuzhiyun 			       p->current->state_since, timestamp, NULL);
434*4882a593Smuzhiyun 		p->current->state_since = timestamp;
435*4882a593Smuzhiyun 		p->current->state = TYPE_WAITING;
436*4882a593Smuzhiyun 	}
437*4882a593Smuzhiyun }
438*4882a593Smuzhiyun 
sched_switch(struct timechart * tchart,int cpu,u64 timestamp,int prev_pid,int next_pid,u64 prev_state,const char * backtrace)439*4882a593Smuzhiyun static void sched_switch(struct timechart *tchart, int cpu, u64 timestamp,
440*4882a593Smuzhiyun 			 int prev_pid, int next_pid, u64 prev_state,
441*4882a593Smuzhiyun 			 const char *backtrace)
442*4882a593Smuzhiyun {
443*4882a593Smuzhiyun 	struct per_pid *p = NULL, *prev_p;
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 	prev_p = find_create_pid(tchart, prev_pid);
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun 	p = find_create_pid(tchart, next_pid);
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun 	if (prev_p->current && prev_p->current->state != TYPE_NONE)
450*4882a593Smuzhiyun 		pid_put_sample(tchart, prev_pid, TYPE_RUNNING, cpu,
451*4882a593Smuzhiyun 			       prev_p->current->state_since, timestamp,
452*4882a593Smuzhiyun 			       backtrace);
453*4882a593Smuzhiyun 	if (p && p->current) {
454*4882a593Smuzhiyun 		if (p->current->state != TYPE_NONE)
455*4882a593Smuzhiyun 			pid_put_sample(tchart, next_pid, p->current->state, cpu,
456*4882a593Smuzhiyun 				       p->current->state_since, timestamp,
457*4882a593Smuzhiyun 				       backtrace);
458*4882a593Smuzhiyun 
459*4882a593Smuzhiyun 		p->current->state_since = timestamp;
460*4882a593Smuzhiyun 		p->current->state = TYPE_RUNNING;
461*4882a593Smuzhiyun 	}
462*4882a593Smuzhiyun 
463*4882a593Smuzhiyun 	if (prev_p->current) {
464*4882a593Smuzhiyun 		prev_p->current->state = TYPE_NONE;
465*4882a593Smuzhiyun 		prev_p->current->state_since = timestamp;
466*4882a593Smuzhiyun 		if (prev_state & 2)
467*4882a593Smuzhiyun 			prev_p->current->state = TYPE_BLOCKED;
468*4882a593Smuzhiyun 		if (prev_state == 0)
469*4882a593Smuzhiyun 			prev_p->current->state = TYPE_WAITING;
470*4882a593Smuzhiyun 	}
471*4882a593Smuzhiyun }
472*4882a593Smuzhiyun 
cat_backtrace(union perf_event * event,struct perf_sample * sample,struct machine * machine)473*4882a593Smuzhiyun static const char *cat_backtrace(union perf_event *event,
474*4882a593Smuzhiyun 				 struct perf_sample *sample,
475*4882a593Smuzhiyun 				 struct machine *machine)
476*4882a593Smuzhiyun {
477*4882a593Smuzhiyun 	struct addr_location al;
478*4882a593Smuzhiyun 	unsigned int i;
479*4882a593Smuzhiyun 	char *p = NULL;
480*4882a593Smuzhiyun 	size_t p_len;
481*4882a593Smuzhiyun 	u8 cpumode = PERF_RECORD_MISC_USER;
482*4882a593Smuzhiyun 	struct addr_location tal;
483*4882a593Smuzhiyun 	struct ip_callchain *chain = sample->callchain;
484*4882a593Smuzhiyun 	FILE *f = open_memstream(&p, &p_len);
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun 	if (!f) {
487*4882a593Smuzhiyun 		perror("open_memstream error");
488*4882a593Smuzhiyun 		return NULL;
489*4882a593Smuzhiyun 	}
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 	if (!chain)
492*4882a593Smuzhiyun 		goto exit;
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 	if (machine__resolve(machine, &al, sample) < 0) {
495*4882a593Smuzhiyun 		fprintf(stderr, "problem processing %d event, skipping it.\n",
496*4882a593Smuzhiyun 			event->header.type);
497*4882a593Smuzhiyun 		goto exit;
498*4882a593Smuzhiyun 	}
499*4882a593Smuzhiyun 
500*4882a593Smuzhiyun 	for (i = 0; i < chain->nr; i++) {
501*4882a593Smuzhiyun 		u64 ip;
502*4882a593Smuzhiyun 
503*4882a593Smuzhiyun 		if (callchain_param.order == ORDER_CALLEE)
504*4882a593Smuzhiyun 			ip = chain->ips[i];
505*4882a593Smuzhiyun 		else
506*4882a593Smuzhiyun 			ip = chain->ips[chain->nr - i - 1];
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun 		if (ip >= PERF_CONTEXT_MAX) {
509*4882a593Smuzhiyun 			switch (ip) {
510*4882a593Smuzhiyun 			case PERF_CONTEXT_HV:
511*4882a593Smuzhiyun 				cpumode = PERF_RECORD_MISC_HYPERVISOR;
512*4882a593Smuzhiyun 				break;
513*4882a593Smuzhiyun 			case PERF_CONTEXT_KERNEL:
514*4882a593Smuzhiyun 				cpumode = PERF_RECORD_MISC_KERNEL;
515*4882a593Smuzhiyun 				break;
516*4882a593Smuzhiyun 			case PERF_CONTEXT_USER:
517*4882a593Smuzhiyun 				cpumode = PERF_RECORD_MISC_USER;
518*4882a593Smuzhiyun 				break;
519*4882a593Smuzhiyun 			default:
520*4882a593Smuzhiyun 				pr_debug("invalid callchain context: "
521*4882a593Smuzhiyun 					 "%"PRId64"\n", (s64) ip);
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun 				/*
524*4882a593Smuzhiyun 				 * It seems the callchain is corrupted.
525*4882a593Smuzhiyun 				 * Discard all.
526*4882a593Smuzhiyun 				 */
527*4882a593Smuzhiyun 				zfree(&p);
528*4882a593Smuzhiyun 				goto exit_put;
529*4882a593Smuzhiyun 			}
530*4882a593Smuzhiyun 			continue;
531*4882a593Smuzhiyun 		}
532*4882a593Smuzhiyun 
533*4882a593Smuzhiyun 		tal.filtered = 0;
534*4882a593Smuzhiyun 		if (thread__find_symbol(al.thread, cpumode, ip, &tal))
535*4882a593Smuzhiyun 			fprintf(f, "..... %016" PRIx64 " %s\n", ip, tal.sym->name);
536*4882a593Smuzhiyun 		else
537*4882a593Smuzhiyun 			fprintf(f, "..... %016" PRIx64 "\n", ip);
538*4882a593Smuzhiyun 	}
539*4882a593Smuzhiyun exit_put:
540*4882a593Smuzhiyun 	addr_location__put(&al);
541*4882a593Smuzhiyun exit:
542*4882a593Smuzhiyun 	fclose(f);
543*4882a593Smuzhiyun 
544*4882a593Smuzhiyun 	return p;
545*4882a593Smuzhiyun }
546*4882a593Smuzhiyun 
547*4882a593Smuzhiyun typedef int (*tracepoint_handler)(struct timechart *tchart,
548*4882a593Smuzhiyun 				  struct evsel *evsel,
549*4882a593Smuzhiyun 				  struct perf_sample *sample,
550*4882a593Smuzhiyun 				  const char *backtrace);
551*4882a593Smuzhiyun 
process_sample_event(struct perf_tool * tool,union perf_event * event,struct perf_sample * sample,struct evsel * evsel,struct machine * machine)552*4882a593Smuzhiyun static int process_sample_event(struct perf_tool *tool,
553*4882a593Smuzhiyun 				union perf_event *event,
554*4882a593Smuzhiyun 				struct perf_sample *sample,
555*4882a593Smuzhiyun 				struct evsel *evsel,
556*4882a593Smuzhiyun 				struct machine *machine)
557*4882a593Smuzhiyun {
558*4882a593Smuzhiyun 	struct timechart *tchart = container_of(tool, struct timechart, tool);
559*4882a593Smuzhiyun 
560*4882a593Smuzhiyun 	if (evsel->core.attr.sample_type & PERF_SAMPLE_TIME) {
561*4882a593Smuzhiyun 		if (!tchart->first_time || tchart->first_time > sample->time)
562*4882a593Smuzhiyun 			tchart->first_time = sample->time;
563*4882a593Smuzhiyun 		if (tchart->last_time < sample->time)
564*4882a593Smuzhiyun 			tchart->last_time = sample->time;
565*4882a593Smuzhiyun 	}
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 	if (evsel->handler != NULL) {
568*4882a593Smuzhiyun 		tracepoint_handler f = evsel->handler;
569*4882a593Smuzhiyun 		return f(tchart, evsel, sample,
570*4882a593Smuzhiyun 			 cat_backtrace(event, sample, machine));
571*4882a593Smuzhiyun 	}
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun 	return 0;
574*4882a593Smuzhiyun }
575*4882a593Smuzhiyun 
576*4882a593Smuzhiyun static int
process_sample_cpu_idle(struct timechart * tchart __maybe_unused,struct evsel * evsel,struct perf_sample * sample,const char * backtrace __maybe_unused)577*4882a593Smuzhiyun process_sample_cpu_idle(struct timechart *tchart __maybe_unused,
578*4882a593Smuzhiyun 			struct evsel *evsel,
579*4882a593Smuzhiyun 			struct perf_sample *sample,
580*4882a593Smuzhiyun 			const char *backtrace __maybe_unused)
581*4882a593Smuzhiyun {
582*4882a593Smuzhiyun 	u32 state  = evsel__intval(evsel, sample, "state");
583*4882a593Smuzhiyun 	u32 cpu_id = evsel__intval(evsel, sample, "cpu_id");
584*4882a593Smuzhiyun 
585*4882a593Smuzhiyun 	if (state == (u32)PWR_EVENT_EXIT)
586*4882a593Smuzhiyun 		c_state_end(tchart, cpu_id, sample->time);
587*4882a593Smuzhiyun 	else
588*4882a593Smuzhiyun 		c_state_start(cpu_id, sample->time, state);
589*4882a593Smuzhiyun 	return 0;
590*4882a593Smuzhiyun }
591*4882a593Smuzhiyun 
592*4882a593Smuzhiyun static int
process_sample_cpu_frequency(struct timechart * tchart,struct evsel * evsel,struct perf_sample * sample,const char * backtrace __maybe_unused)593*4882a593Smuzhiyun process_sample_cpu_frequency(struct timechart *tchart,
594*4882a593Smuzhiyun 			     struct evsel *evsel,
595*4882a593Smuzhiyun 			     struct perf_sample *sample,
596*4882a593Smuzhiyun 			     const char *backtrace __maybe_unused)
597*4882a593Smuzhiyun {
598*4882a593Smuzhiyun 	u32 state  = evsel__intval(evsel, sample, "state");
599*4882a593Smuzhiyun 	u32 cpu_id = evsel__intval(evsel, sample, "cpu_id");
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 	p_state_change(tchart, cpu_id, sample->time, state);
602*4882a593Smuzhiyun 	return 0;
603*4882a593Smuzhiyun }
604*4882a593Smuzhiyun 
605*4882a593Smuzhiyun static int
process_sample_sched_wakeup(struct timechart * tchart,struct evsel * evsel,struct perf_sample * sample,const char * backtrace)606*4882a593Smuzhiyun process_sample_sched_wakeup(struct timechart *tchart,
607*4882a593Smuzhiyun 			    struct evsel *evsel,
608*4882a593Smuzhiyun 			    struct perf_sample *sample,
609*4882a593Smuzhiyun 			    const char *backtrace)
610*4882a593Smuzhiyun {
611*4882a593Smuzhiyun 	u8 flags  = evsel__intval(evsel, sample, "common_flags");
612*4882a593Smuzhiyun 	int waker = evsel__intval(evsel, sample, "common_pid");
613*4882a593Smuzhiyun 	int wakee = evsel__intval(evsel, sample, "pid");
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun 	sched_wakeup(tchart, sample->cpu, sample->time, waker, wakee, flags, backtrace);
616*4882a593Smuzhiyun 	return 0;
617*4882a593Smuzhiyun }
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun static int
process_sample_sched_switch(struct timechart * tchart,struct evsel * evsel,struct perf_sample * sample,const char * backtrace)620*4882a593Smuzhiyun process_sample_sched_switch(struct timechart *tchart,
621*4882a593Smuzhiyun 			    struct evsel *evsel,
622*4882a593Smuzhiyun 			    struct perf_sample *sample,
623*4882a593Smuzhiyun 			    const char *backtrace)
624*4882a593Smuzhiyun {
625*4882a593Smuzhiyun 	int prev_pid   = evsel__intval(evsel, sample, "prev_pid");
626*4882a593Smuzhiyun 	int next_pid   = evsel__intval(evsel, sample, "next_pid");
627*4882a593Smuzhiyun 	u64 prev_state = evsel__intval(evsel, sample, "prev_state");
628*4882a593Smuzhiyun 
629*4882a593Smuzhiyun 	sched_switch(tchart, sample->cpu, sample->time, prev_pid, next_pid,
630*4882a593Smuzhiyun 		     prev_state, backtrace);
631*4882a593Smuzhiyun 	return 0;
632*4882a593Smuzhiyun }
633*4882a593Smuzhiyun 
634*4882a593Smuzhiyun #ifdef SUPPORT_OLD_POWER_EVENTS
635*4882a593Smuzhiyun static int
process_sample_power_start(struct timechart * tchart __maybe_unused,struct evsel * evsel,struct perf_sample * sample,const char * backtrace __maybe_unused)636*4882a593Smuzhiyun process_sample_power_start(struct timechart *tchart __maybe_unused,
637*4882a593Smuzhiyun 			   struct evsel *evsel,
638*4882a593Smuzhiyun 			   struct perf_sample *sample,
639*4882a593Smuzhiyun 			   const char *backtrace __maybe_unused)
640*4882a593Smuzhiyun {
641*4882a593Smuzhiyun 	u64 cpu_id = evsel__intval(evsel, sample, "cpu_id");
642*4882a593Smuzhiyun 	u64 value  = evsel__intval(evsel, sample, "value");
643*4882a593Smuzhiyun 
644*4882a593Smuzhiyun 	c_state_start(cpu_id, sample->time, value);
645*4882a593Smuzhiyun 	return 0;
646*4882a593Smuzhiyun }
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun static int
process_sample_power_end(struct timechart * tchart,struct evsel * evsel __maybe_unused,struct perf_sample * sample,const char * backtrace __maybe_unused)649*4882a593Smuzhiyun process_sample_power_end(struct timechart *tchart,
650*4882a593Smuzhiyun 			 struct evsel *evsel __maybe_unused,
651*4882a593Smuzhiyun 			 struct perf_sample *sample,
652*4882a593Smuzhiyun 			 const char *backtrace __maybe_unused)
653*4882a593Smuzhiyun {
654*4882a593Smuzhiyun 	c_state_end(tchart, sample->cpu, sample->time);
655*4882a593Smuzhiyun 	return 0;
656*4882a593Smuzhiyun }
657*4882a593Smuzhiyun 
658*4882a593Smuzhiyun static int
process_sample_power_frequency(struct timechart * tchart,struct evsel * evsel,struct perf_sample * sample,const char * backtrace __maybe_unused)659*4882a593Smuzhiyun process_sample_power_frequency(struct timechart *tchart,
660*4882a593Smuzhiyun 			       struct evsel *evsel,
661*4882a593Smuzhiyun 			       struct perf_sample *sample,
662*4882a593Smuzhiyun 			       const char *backtrace __maybe_unused)
663*4882a593Smuzhiyun {
664*4882a593Smuzhiyun 	u64 cpu_id = evsel__intval(evsel, sample, "cpu_id");
665*4882a593Smuzhiyun 	u64 value  = evsel__intval(evsel, sample, "value");
666*4882a593Smuzhiyun 
667*4882a593Smuzhiyun 	p_state_change(tchart, cpu_id, sample->time, value);
668*4882a593Smuzhiyun 	return 0;
669*4882a593Smuzhiyun }
670*4882a593Smuzhiyun #endif /* SUPPORT_OLD_POWER_EVENTS */
671*4882a593Smuzhiyun 
672*4882a593Smuzhiyun /*
673*4882a593Smuzhiyun  * After the last sample we need to wrap up the current C/P state
674*4882a593Smuzhiyun  * and close out each CPU for these.
675*4882a593Smuzhiyun  */
end_sample_processing(struct timechart * tchart)676*4882a593Smuzhiyun static void end_sample_processing(struct timechart *tchart)
677*4882a593Smuzhiyun {
678*4882a593Smuzhiyun 	u64 cpu;
679*4882a593Smuzhiyun 	struct power_event *pwr;
680*4882a593Smuzhiyun 
681*4882a593Smuzhiyun 	for (cpu = 0; cpu <= tchart->numcpus; cpu++) {
682*4882a593Smuzhiyun 		/* C state */
683*4882a593Smuzhiyun #if 0
684*4882a593Smuzhiyun 		pwr = zalloc(sizeof(*pwr));
685*4882a593Smuzhiyun 		if (!pwr)
686*4882a593Smuzhiyun 			return;
687*4882a593Smuzhiyun 
688*4882a593Smuzhiyun 		pwr->state = cpus_cstate_state[cpu];
689*4882a593Smuzhiyun 		pwr->start_time = cpus_cstate_start_times[cpu];
690*4882a593Smuzhiyun 		pwr->end_time = tchart->last_time;
691*4882a593Smuzhiyun 		pwr->cpu = cpu;
692*4882a593Smuzhiyun 		pwr->type = CSTATE;
693*4882a593Smuzhiyun 		pwr->next = tchart->power_events;
694*4882a593Smuzhiyun 
695*4882a593Smuzhiyun 		tchart->power_events = pwr;
696*4882a593Smuzhiyun #endif
697*4882a593Smuzhiyun 		/* P state */
698*4882a593Smuzhiyun 
699*4882a593Smuzhiyun 		pwr = zalloc(sizeof(*pwr));
700*4882a593Smuzhiyun 		if (!pwr)
701*4882a593Smuzhiyun 			return;
702*4882a593Smuzhiyun 
703*4882a593Smuzhiyun 		pwr->state = cpus_pstate_state[cpu];
704*4882a593Smuzhiyun 		pwr->start_time = cpus_pstate_start_times[cpu];
705*4882a593Smuzhiyun 		pwr->end_time = tchart->last_time;
706*4882a593Smuzhiyun 		pwr->cpu = cpu;
707*4882a593Smuzhiyun 		pwr->type = PSTATE;
708*4882a593Smuzhiyun 		pwr->next = tchart->power_events;
709*4882a593Smuzhiyun 
710*4882a593Smuzhiyun 		if (!pwr->start_time)
711*4882a593Smuzhiyun 			pwr->start_time = tchart->first_time;
712*4882a593Smuzhiyun 		if (!pwr->state)
713*4882a593Smuzhiyun 			pwr->state = tchart->min_freq;
714*4882a593Smuzhiyun 		tchart->power_events = pwr;
715*4882a593Smuzhiyun 	}
716*4882a593Smuzhiyun }
717*4882a593Smuzhiyun 
pid_begin_io_sample(struct timechart * tchart,int pid,int type,u64 start,int fd)718*4882a593Smuzhiyun static int pid_begin_io_sample(struct timechart *tchart, int pid, int type,
719*4882a593Smuzhiyun 			       u64 start, int fd)
720*4882a593Smuzhiyun {
721*4882a593Smuzhiyun 	struct per_pid *p = find_create_pid(tchart, pid);
722*4882a593Smuzhiyun 	struct per_pidcomm *c = p->current;
723*4882a593Smuzhiyun 	struct io_sample *sample;
724*4882a593Smuzhiyun 	struct io_sample *prev;
725*4882a593Smuzhiyun 
726*4882a593Smuzhiyun 	if (!c) {
727*4882a593Smuzhiyun 		c = zalloc(sizeof(*c));
728*4882a593Smuzhiyun 		if (!c)
729*4882a593Smuzhiyun 			return -ENOMEM;
730*4882a593Smuzhiyun 		p->current = c;
731*4882a593Smuzhiyun 		c->next = p->all;
732*4882a593Smuzhiyun 		p->all = c;
733*4882a593Smuzhiyun 	}
734*4882a593Smuzhiyun 
735*4882a593Smuzhiyun 	prev = c->io_samples;
736*4882a593Smuzhiyun 
737*4882a593Smuzhiyun 	if (prev && prev->start_time && !prev->end_time) {
738*4882a593Smuzhiyun 		pr_warning("Skip invalid start event: "
739*4882a593Smuzhiyun 			   "previous event already started!\n");
740*4882a593Smuzhiyun 
741*4882a593Smuzhiyun 		/* remove previous event that has been started,
742*4882a593Smuzhiyun 		 * we are not sure we will ever get an end for it */
743*4882a593Smuzhiyun 		c->io_samples = prev->next;
744*4882a593Smuzhiyun 		free(prev);
745*4882a593Smuzhiyun 		return 0;
746*4882a593Smuzhiyun 	}
747*4882a593Smuzhiyun 
748*4882a593Smuzhiyun 	sample = zalloc(sizeof(*sample));
749*4882a593Smuzhiyun 	if (!sample)
750*4882a593Smuzhiyun 		return -ENOMEM;
751*4882a593Smuzhiyun 	sample->start_time = start;
752*4882a593Smuzhiyun 	sample->type = type;
753*4882a593Smuzhiyun 	sample->fd = fd;
754*4882a593Smuzhiyun 	sample->next = c->io_samples;
755*4882a593Smuzhiyun 	c->io_samples = sample;
756*4882a593Smuzhiyun 
757*4882a593Smuzhiyun 	if (c->start_time == 0 || c->start_time > start)
758*4882a593Smuzhiyun 		c->start_time = start;
759*4882a593Smuzhiyun 
760*4882a593Smuzhiyun 	return 0;
761*4882a593Smuzhiyun }
762*4882a593Smuzhiyun 
pid_end_io_sample(struct timechart * tchart,int pid,int type,u64 end,long ret)763*4882a593Smuzhiyun static int pid_end_io_sample(struct timechart *tchart, int pid, int type,
764*4882a593Smuzhiyun 			     u64 end, long ret)
765*4882a593Smuzhiyun {
766*4882a593Smuzhiyun 	struct per_pid *p = find_create_pid(tchart, pid);
767*4882a593Smuzhiyun 	struct per_pidcomm *c = p->current;
768*4882a593Smuzhiyun 	struct io_sample *sample, *prev;
769*4882a593Smuzhiyun 
770*4882a593Smuzhiyun 	if (!c) {
771*4882a593Smuzhiyun 		pr_warning("Invalid pidcomm!\n");
772*4882a593Smuzhiyun 		return -1;
773*4882a593Smuzhiyun 	}
774*4882a593Smuzhiyun 
775*4882a593Smuzhiyun 	sample = c->io_samples;
776*4882a593Smuzhiyun 
777*4882a593Smuzhiyun 	if (!sample) /* skip partially captured events */
778*4882a593Smuzhiyun 		return 0;
779*4882a593Smuzhiyun 
780*4882a593Smuzhiyun 	if (sample->end_time) {
781*4882a593Smuzhiyun 		pr_warning("Skip invalid end event: "
782*4882a593Smuzhiyun 			   "previous event already ended!\n");
783*4882a593Smuzhiyun 		return 0;
784*4882a593Smuzhiyun 	}
785*4882a593Smuzhiyun 
786*4882a593Smuzhiyun 	if (sample->type != type) {
787*4882a593Smuzhiyun 		pr_warning("Skip invalid end event: invalid event type!\n");
788*4882a593Smuzhiyun 		return 0;
789*4882a593Smuzhiyun 	}
790*4882a593Smuzhiyun 
791*4882a593Smuzhiyun 	sample->end_time = end;
792*4882a593Smuzhiyun 	prev = sample->next;
793*4882a593Smuzhiyun 
794*4882a593Smuzhiyun 	/* we want to be able to see small and fast transfers, so make them
795*4882a593Smuzhiyun 	 * at least min_time long, but don't overlap them */
796*4882a593Smuzhiyun 	if (sample->end_time - sample->start_time < tchart->min_time)
797*4882a593Smuzhiyun 		sample->end_time = sample->start_time + tchart->min_time;
798*4882a593Smuzhiyun 	if (prev && sample->start_time < prev->end_time) {
799*4882a593Smuzhiyun 		if (prev->err) /* try to make errors more visible */
800*4882a593Smuzhiyun 			sample->start_time = prev->end_time;
801*4882a593Smuzhiyun 		else
802*4882a593Smuzhiyun 			prev->end_time = sample->start_time;
803*4882a593Smuzhiyun 	}
804*4882a593Smuzhiyun 
805*4882a593Smuzhiyun 	if (ret < 0) {
806*4882a593Smuzhiyun 		sample->err = ret;
807*4882a593Smuzhiyun 	} else if (type == IOTYPE_READ || type == IOTYPE_WRITE ||
808*4882a593Smuzhiyun 		   type == IOTYPE_TX || type == IOTYPE_RX) {
809*4882a593Smuzhiyun 
810*4882a593Smuzhiyun 		if ((u64)ret > c->max_bytes)
811*4882a593Smuzhiyun 			c->max_bytes = ret;
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun 		c->total_bytes += ret;
814*4882a593Smuzhiyun 		p->total_bytes += ret;
815*4882a593Smuzhiyun 		sample->bytes = ret;
816*4882a593Smuzhiyun 	}
817*4882a593Smuzhiyun 
818*4882a593Smuzhiyun 	/* merge two requests to make svg smaller and render-friendly */
819*4882a593Smuzhiyun 	if (prev &&
820*4882a593Smuzhiyun 	    prev->type == sample->type &&
821*4882a593Smuzhiyun 	    prev->err == sample->err &&
822*4882a593Smuzhiyun 	    prev->fd == sample->fd &&
823*4882a593Smuzhiyun 	    prev->end_time + tchart->merge_dist >= sample->start_time) {
824*4882a593Smuzhiyun 
825*4882a593Smuzhiyun 		sample->bytes += prev->bytes;
826*4882a593Smuzhiyun 		sample->merges += prev->merges + 1;
827*4882a593Smuzhiyun 
828*4882a593Smuzhiyun 		sample->start_time = prev->start_time;
829*4882a593Smuzhiyun 		sample->next = prev->next;
830*4882a593Smuzhiyun 		free(prev);
831*4882a593Smuzhiyun 
832*4882a593Smuzhiyun 		if (!sample->err && sample->bytes > c->max_bytes)
833*4882a593Smuzhiyun 			c->max_bytes = sample->bytes;
834*4882a593Smuzhiyun 	}
835*4882a593Smuzhiyun 
836*4882a593Smuzhiyun 	tchart->io_events++;
837*4882a593Smuzhiyun 
838*4882a593Smuzhiyun 	return 0;
839*4882a593Smuzhiyun }
840*4882a593Smuzhiyun 
841*4882a593Smuzhiyun static int
process_enter_read(struct timechart * tchart,struct evsel * evsel,struct perf_sample * sample)842*4882a593Smuzhiyun process_enter_read(struct timechart *tchart,
843*4882a593Smuzhiyun 		   struct evsel *evsel,
844*4882a593Smuzhiyun 		   struct perf_sample *sample)
845*4882a593Smuzhiyun {
846*4882a593Smuzhiyun 	long fd = evsel__intval(evsel, sample, "fd");
847*4882a593Smuzhiyun 	return pid_begin_io_sample(tchart, sample->tid, IOTYPE_READ,
848*4882a593Smuzhiyun 				   sample->time, fd);
849*4882a593Smuzhiyun }
850*4882a593Smuzhiyun 
851*4882a593Smuzhiyun static int
process_exit_read(struct timechart * tchart,struct evsel * evsel,struct perf_sample * sample)852*4882a593Smuzhiyun process_exit_read(struct timechart *tchart,
853*4882a593Smuzhiyun 		  struct evsel *evsel,
854*4882a593Smuzhiyun 		  struct perf_sample *sample)
855*4882a593Smuzhiyun {
856*4882a593Smuzhiyun 	long ret = evsel__intval(evsel, sample, "ret");
857*4882a593Smuzhiyun 	return pid_end_io_sample(tchart, sample->tid, IOTYPE_READ,
858*4882a593Smuzhiyun 				 sample->time, ret);
859*4882a593Smuzhiyun }
860*4882a593Smuzhiyun 
861*4882a593Smuzhiyun static int
process_enter_write(struct timechart * tchart,struct evsel * evsel,struct perf_sample * sample)862*4882a593Smuzhiyun process_enter_write(struct timechart *tchart,
863*4882a593Smuzhiyun 		    struct evsel *evsel,
864*4882a593Smuzhiyun 		    struct perf_sample *sample)
865*4882a593Smuzhiyun {
866*4882a593Smuzhiyun 	long fd = evsel__intval(evsel, sample, "fd");
867*4882a593Smuzhiyun 	return pid_begin_io_sample(tchart, sample->tid, IOTYPE_WRITE,
868*4882a593Smuzhiyun 				   sample->time, fd);
869*4882a593Smuzhiyun }
870*4882a593Smuzhiyun 
871*4882a593Smuzhiyun static int
process_exit_write(struct timechart * tchart,struct evsel * evsel,struct perf_sample * sample)872*4882a593Smuzhiyun process_exit_write(struct timechart *tchart,
873*4882a593Smuzhiyun 		   struct evsel *evsel,
874*4882a593Smuzhiyun 		   struct perf_sample *sample)
875*4882a593Smuzhiyun {
876*4882a593Smuzhiyun 	long ret = evsel__intval(evsel, sample, "ret");
877*4882a593Smuzhiyun 	return pid_end_io_sample(tchart, sample->tid, IOTYPE_WRITE,
878*4882a593Smuzhiyun 				 sample->time, ret);
879*4882a593Smuzhiyun }
880*4882a593Smuzhiyun 
881*4882a593Smuzhiyun static int
process_enter_sync(struct timechart * tchart,struct evsel * evsel,struct perf_sample * sample)882*4882a593Smuzhiyun process_enter_sync(struct timechart *tchart,
883*4882a593Smuzhiyun 		   struct evsel *evsel,
884*4882a593Smuzhiyun 		   struct perf_sample *sample)
885*4882a593Smuzhiyun {
886*4882a593Smuzhiyun 	long fd = evsel__intval(evsel, sample, "fd");
887*4882a593Smuzhiyun 	return pid_begin_io_sample(tchart, sample->tid, IOTYPE_SYNC,
888*4882a593Smuzhiyun 				   sample->time, fd);
889*4882a593Smuzhiyun }
890*4882a593Smuzhiyun 
891*4882a593Smuzhiyun static int
process_exit_sync(struct timechart * tchart,struct evsel * evsel,struct perf_sample * sample)892*4882a593Smuzhiyun process_exit_sync(struct timechart *tchart,
893*4882a593Smuzhiyun 		  struct evsel *evsel,
894*4882a593Smuzhiyun 		  struct perf_sample *sample)
895*4882a593Smuzhiyun {
896*4882a593Smuzhiyun 	long ret = evsel__intval(evsel, sample, "ret");
897*4882a593Smuzhiyun 	return pid_end_io_sample(tchart, sample->tid, IOTYPE_SYNC,
898*4882a593Smuzhiyun 				 sample->time, ret);
899*4882a593Smuzhiyun }
900*4882a593Smuzhiyun 
901*4882a593Smuzhiyun static int
process_enter_tx(struct timechart * tchart,struct evsel * evsel,struct perf_sample * sample)902*4882a593Smuzhiyun process_enter_tx(struct timechart *tchart,
903*4882a593Smuzhiyun 		 struct evsel *evsel,
904*4882a593Smuzhiyun 		 struct perf_sample *sample)
905*4882a593Smuzhiyun {
906*4882a593Smuzhiyun 	long fd = evsel__intval(evsel, sample, "fd");
907*4882a593Smuzhiyun 	return pid_begin_io_sample(tchart, sample->tid, IOTYPE_TX,
908*4882a593Smuzhiyun 				   sample->time, fd);
909*4882a593Smuzhiyun }
910*4882a593Smuzhiyun 
911*4882a593Smuzhiyun static int
process_exit_tx(struct timechart * tchart,struct evsel * evsel,struct perf_sample * sample)912*4882a593Smuzhiyun process_exit_tx(struct timechart *tchart,
913*4882a593Smuzhiyun 		struct evsel *evsel,
914*4882a593Smuzhiyun 		struct perf_sample *sample)
915*4882a593Smuzhiyun {
916*4882a593Smuzhiyun 	long ret = evsel__intval(evsel, sample, "ret");
917*4882a593Smuzhiyun 	return pid_end_io_sample(tchart, sample->tid, IOTYPE_TX,
918*4882a593Smuzhiyun 				 sample->time, ret);
919*4882a593Smuzhiyun }
920*4882a593Smuzhiyun 
921*4882a593Smuzhiyun static int
process_enter_rx(struct timechart * tchart,struct evsel * evsel,struct perf_sample * sample)922*4882a593Smuzhiyun process_enter_rx(struct timechart *tchart,
923*4882a593Smuzhiyun 		 struct evsel *evsel,
924*4882a593Smuzhiyun 		 struct perf_sample *sample)
925*4882a593Smuzhiyun {
926*4882a593Smuzhiyun 	long fd = evsel__intval(evsel, sample, "fd");
927*4882a593Smuzhiyun 	return pid_begin_io_sample(tchart, sample->tid, IOTYPE_RX,
928*4882a593Smuzhiyun 				   sample->time, fd);
929*4882a593Smuzhiyun }
930*4882a593Smuzhiyun 
931*4882a593Smuzhiyun static int
process_exit_rx(struct timechart * tchart,struct evsel * evsel,struct perf_sample * sample)932*4882a593Smuzhiyun process_exit_rx(struct timechart *tchart,
933*4882a593Smuzhiyun 		struct evsel *evsel,
934*4882a593Smuzhiyun 		struct perf_sample *sample)
935*4882a593Smuzhiyun {
936*4882a593Smuzhiyun 	long ret = evsel__intval(evsel, sample, "ret");
937*4882a593Smuzhiyun 	return pid_end_io_sample(tchart, sample->tid, IOTYPE_RX,
938*4882a593Smuzhiyun 				 sample->time, ret);
939*4882a593Smuzhiyun }
940*4882a593Smuzhiyun 
941*4882a593Smuzhiyun static int
process_enter_poll(struct timechart * tchart,struct evsel * evsel,struct perf_sample * sample)942*4882a593Smuzhiyun process_enter_poll(struct timechart *tchart,
943*4882a593Smuzhiyun 		   struct evsel *evsel,
944*4882a593Smuzhiyun 		   struct perf_sample *sample)
945*4882a593Smuzhiyun {
946*4882a593Smuzhiyun 	long fd = evsel__intval(evsel, sample, "fd");
947*4882a593Smuzhiyun 	return pid_begin_io_sample(tchart, sample->tid, IOTYPE_POLL,
948*4882a593Smuzhiyun 				   sample->time, fd);
949*4882a593Smuzhiyun }
950*4882a593Smuzhiyun 
951*4882a593Smuzhiyun static int
process_exit_poll(struct timechart * tchart,struct evsel * evsel,struct perf_sample * sample)952*4882a593Smuzhiyun process_exit_poll(struct timechart *tchart,
953*4882a593Smuzhiyun 		  struct evsel *evsel,
954*4882a593Smuzhiyun 		  struct perf_sample *sample)
955*4882a593Smuzhiyun {
956*4882a593Smuzhiyun 	long ret = evsel__intval(evsel, sample, "ret");
957*4882a593Smuzhiyun 	return pid_end_io_sample(tchart, sample->tid, IOTYPE_POLL,
958*4882a593Smuzhiyun 				 sample->time, ret);
959*4882a593Smuzhiyun }
960*4882a593Smuzhiyun 
961*4882a593Smuzhiyun /*
962*4882a593Smuzhiyun  * Sort the pid datastructure
963*4882a593Smuzhiyun  */
sort_pids(struct timechart * tchart)964*4882a593Smuzhiyun static void sort_pids(struct timechart *tchart)
965*4882a593Smuzhiyun {
966*4882a593Smuzhiyun 	struct per_pid *new_list, *p, *cursor, *prev;
967*4882a593Smuzhiyun 	/* sort by ppid first, then by pid, lowest to highest */
968*4882a593Smuzhiyun 
969*4882a593Smuzhiyun 	new_list = NULL;
970*4882a593Smuzhiyun 
971*4882a593Smuzhiyun 	while (tchart->all_data) {
972*4882a593Smuzhiyun 		p = tchart->all_data;
973*4882a593Smuzhiyun 		tchart->all_data = p->next;
974*4882a593Smuzhiyun 		p->next = NULL;
975*4882a593Smuzhiyun 
976*4882a593Smuzhiyun 		if (new_list == NULL) {
977*4882a593Smuzhiyun 			new_list = p;
978*4882a593Smuzhiyun 			p->next = NULL;
979*4882a593Smuzhiyun 			continue;
980*4882a593Smuzhiyun 		}
981*4882a593Smuzhiyun 		prev = NULL;
982*4882a593Smuzhiyun 		cursor = new_list;
983*4882a593Smuzhiyun 		while (cursor) {
984*4882a593Smuzhiyun 			if (cursor->ppid > p->ppid ||
985*4882a593Smuzhiyun 				(cursor->ppid == p->ppid && cursor->pid > p->pid)) {
986*4882a593Smuzhiyun 				/* must insert before */
987*4882a593Smuzhiyun 				if (prev) {
988*4882a593Smuzhiyun 					p->next = prev->next;
989*4882a593Smuzhiyun 					prev->next = p;
990*4882a593Smuzhiyun 					cursor = NULL;
991*4882a593Smuzhiyun 					continue;
992*4882a593Smuzhiyun 				} else {
993*4882a593Smuzhiyun 					p->next = new_list;
994*4882a593Smuzhiyun 					new_list = p;
995*4882a593Smuzhiyun 					cursor = NULL;
996*4882a593Smuzhiyun 					continue;
997*4882a593Smuzhiyun 				}
998*4882a593Smuzhiyun 			}
999*4882a593Smuzhiyun 
1000*4882a593Smuzhiyun 			prev = cursor;
1001*4882a593Smuzhiyun 			cursor = cursor->next;
1002*4882a593Smuzhiyun 			if (!cursor)
1003*4882a593Smuzhiyun 				prev->next = p;
1004*4882a593Smuzhiyun 		}
1005*4882a593Smuzhiyun 	}
1006*4882a593Smuzhiyun 	tchart->all_data = new_list;
1007*4882a593Smuzhiyun }
1008*4882a593Smuzhiyun 
1009*4882a593Smuzhiyun 
draw_c_p_states(struct timechart * tchart)1010*4882a593Smuzhiyun static void draw_c_p_states(struct timechart *tchart)
1011*4882a593Smuzhiyun {
1012*4882a593Smuzhiyun 	struct power_event *pwr;
1013*4882a593Smuzhiyun 	pwr = tchart->power_events;
1014*4882a593Smuzhiyun 
1015*4882a593Smuzhiyun 	/*
1016*4882a593Smuzhiyun 	 * two pass drawing so that the P state bars are on top of the C state blocks
1017*4882a593Smuzhiyun 	 */
1018*4882a593Smuzhiyun 	while (pwr) {
1019*4882a593Smuzhiyun 		if (pwr->type == CSTATE)
1020*4882a593Smuzhiyun 			svg_cstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
1021*4882a593Smuzhiyun 		pwr = pwr->next;
1022*4882a593Smuzhiyun 	}
1023*4882a593Smuzhiyun 
1024*4882a593Smuzhiyun 	pwr = tchart->power_events;
1025*4882a593Smuzhiyun 	while (pwr) {
1026*4882a593Smuzhiyun 		if (pwr->type == PSTATE) {
1027*4882a593Smuzhiyun 			if (!pwr->state)
1028*4882a593Smuzhiyun 				pwr->state = tchart->min_freq;
1029*4882a593Smuzhiyun 			svg_pstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
1030*4882a593Smuzhiyun 		}
1031*4882a593Smuzhiyun 		pwr = pwr->next;
1032*4882a593Smuzhiyun 	}
1033*4882a593Smuzhiyun }
1034*4882a593Smuzhiyun 
draw_wakeups(struct timechart * tchart)1035*4882a593Smuzhiyun static void draw_wakeups(struct timechart *tchart)
1036*4882a593Smuzhiyun {
1037*4882a593Smuzhiyun 	struct wake_event *we;
1038*4882a593Smuzhiyun 	struct per_pid *p;
1039*4882a593Smuzhiyun 	struct per_pidcomm *c;
1040*4882a593Smuzhiyun 
1041*4882a593Smuzhiyun 	we = tchart->wake_events;
1042*4882a593Smuzhiyun 	while (we) {
1043*4882a593Smuzhiyun 		int from = 0, to = 0;
1044*4882a593Smuzhiyun 		char *task_from = NULL, *task_to = NULL;
1045*4882a593Smuzhiyun 
1046*4882a593Smuzhiyun 		/* locate the column of the waker and wakee */
1047*4882a593Smuzhiyun 		p = tchart->all_data;
1048*4882a593Smuzhiyun 		while (p) {
1049*4882a593Smuzhiyun 			if (p->pid == we->waker || p->pid == we->wakee) {
1050*4882a593Smuzhiyun 				c = p->all;
1051*4882a593Smuzhiyun 				while (c) {
1052*4882a593Smuzhiyun 					if (c->Y && c->start_time <= we->time && c->end_time >= we->time) {
1053*4882a593Smuzhiyun 						if (p->pid == we->waker && !from) {
1054*4882a593Smuzhiyun 							from = c->Y;
1055*4882a593Smuzhiyun 							task_from = strdup(c->comm);
1056*4882a593Smuzhiyun 						}
1057*4882a593Smuzhiyun 						if (p->pid == we->wakee && !to) {
1058*4882a593Smuzhiyun 							to = c->Y;
1059*4882a593Smuzhiyun 							task_to = strdup(c->comm);
1060*4882a593Smuzhiyun 						}
1061*4882a593Smuzhiyun 					}
1062*4882a593Smuzhiyun 					c = c->next;
1063*4882a593Smuzhiyun 				}
1064*4882a593Smuzhiyun 				c = p->all;
1065*4882a593Smuzhiyun 				while (c) {
1066*4882a593Smuzhiyun 					if (p->pid == we->waker && !from) {
1067*4882a593Smuzhiyun 						from = c->Y;
1068*4882a593Smuzhiyun 						task_from = strdup(c->comm);
1069*4882a593Smuzhiyun 					}
1070*4882a593Smuzhiyun 					if (p->pid == we->wakee && !to) {
1071*4882a593Smuzhiyun 						to = c->Y;
1072*4882a593Smuzhiyun 						task_to = strdup(c->comm);
1073*4882a593Smuzhiyun 					}
1074*4882a593Smuzhiyun 					c = c->next;
1075*4882a593Smuzhiyun 				}
1076*4882a593Smuzhiyun 			}
1077*4882a593Smuzhiyun 			p = p->next;
1078*4882a593Smuzhiyun 		}
1079*4882a593Smuzhiyun 
1080*4882a593Smuzhiyun 		if (!task_from) {
1081*4882a593Smuzhiyun 			task_from = malloc(40);
1082*4882a593Smuzhiyun 			sprintf(task_from, "[%i]", we->waker);
1083*4882a593Smuzhiyun 		}
1084*4882a593Smuzhiyun 		if (!task_to) {
1085*4882a593Smuzhiyun 			task_to = malloc(40);
1086*4882a593Smuzhiyun 			sprintf(task_to, "[%i]", we->wakee);
1087*4882a593Smuzhiyun 		}
1088*4882a593Smuzhiyun 
1089*4882a593Smuzhiyun 		if (we->waker == -1)
1090*4882a593Smuzhiyun 			svg_interrupt(we->time, to, we->backtrace);
1091*4882a593Smuzhiyun 		else if (from && to && abs(from - to) == 1)
1092*4882a593Smuzhiyun 			svg_wakeline(we->time, from, to, we->backtrace);
1093*4882a593Smuzhiyun 		else
1094*4882a593Smuzhiyun 			svg_partial_wakeline(we->time, from, task_from, to,
1095*4882a593Smuzhiyun 					     task_to, we->backtrace);
1096*4882a593Smuzhiyun 		we = we->next;
1097*4882a593Smuzhiyun 
1098*4882a593Smuzhiyun 		free(task_from);
1099*4882a593Smuzhiyun 		free(task_to);
1100*4882a593Smuzhiyun 	}
1101*4882a593Smuzhiyun }
1102*4882a593Smuzhiyun 
draw_cpu_usage(struct timechart * tchart)1103*4882a593Smuzhiyun static void draw_cpu_usage(struct timechart *tchart)
1104*4882a593Smuzhiyun {
1105*4882a593Smuzhiyun 	struct per_pid *p;
1106*4882a593Smuzhiyun 	struct per_pidcomm *c;
1107*4882a593Smuzhiyun 	struct cpu_sample *sample;
1108*4882a593Smuzhiyun 	p = tchart->all_data;
1109*4882a593Smuzhiyun 	while (p) {
1110*4882a593Smuzhiyun 		c = p->all;
1111*4882a593Smuzhiyun 		while (c) {
1112*4882a593Smuzhiyun 			sample = c->samples;
1113*4882a593Smuzhiyun 			while (sample) {
1114*4882a593Smuzhiyun 				if (sample->type == TYPE_RUNNING) {
1115*4882a593Smuzhiyun 					svg_process(sample->cpu,
1116*4882a593Smuzhiyun 						    sample->start_time,
1117*4882a593Smuzhiyun 						    sample->end_time,
1118*4882a593Smuzhiyun 						    p->pid,
1119*4882a593Smuzhiyun 						    c->comm,
1120*4882a593Smuzhiyun 						    sample->backtrace);
1121*4882a593Smuzhiyun 				}
1122*4882a593Smuzhiyun 
1123*4882a593Smuzhiyun 				sample = sample->next;
1124*4882a593Smuzhiyun 			}
1125*4882a593Smuzhiyun 			c = c->next;
1126*4882a593Smuzhiyun 		}
1127*4882a593Smuzhiyun 		p = p->next;
1128*4882a593Smuzhiyun 	}
1129*4882a593Smuzhiyun }
1130*4882a593Smuzhiyun 
draw_io_bars(struct timechart * tchart)1131*4882a593Smuzhiyun static void draw_io_bars(struct timechart *tchart)
1132*4882a593Smuzhiyun {
1133*4882a593Smuzhiyun 	const char *suf;
1134*4882a593Smuzhiyun 	double bytes;
1135*4882a593Smuzhiyun 	char comm[256];
1136*4882a593Smuzhiyun 	struct per_pid *p;
1137*4882a593Smuzhiyun 	struct per_pidcomm *c;
1138*4882a593Smuzhiyun 	struct io_sample *sample;
1139*4882a593Smuzhiyun 	int Y = 1;
1140*4882a593Smuzhiyun 
1141*4882a593Smuzhiyun 	p = tchart->all_data;
1142*4882a593Smuzhiyun 	while (p) {
1143*4882a593Smuzhiyun 		c = p->all;
1144*4882a593Smuzhiyun 		while (c) {
1145*4882a593Smuzhiyun 			if (!c->display) {
1146*4882a593Smuzhiyun 				c->Y = 0;
1147*4882a593Smuzhiyun 				c = c->next;
1148*4882a593Smuzhiyun 				continue;
1149*4882a593Smuzhiyun 			}
1150*4882a593Smuzhiyun 
1151*4882a593Smuzhiyun 			svg_box(Y, c->start_time, c->end_time, "process3");
1152*4882a593Smuzhiyun 			sample = c->io_samples;
1153*4882a593Smuzhiyun 			for (sample = c->io_samples; sample; sample = sample->next) {
1154*4882a593Smuzhiyun 				double h = (double)sample->bytes / c->max_bytes;
1155*4882a593Smuzhiyun 
1156*4882a593Smuzhiyun 				if (tchart->skip_eagain &&
1157*4882a593Smuzhiyun 				    sample->err == -EAGAIN)
1158*4882a593Smuzhiyun 					continue;
1159*4882a593Smuzhiyun 
1160*4882a593Smuzhiyun 				if (sample->err)
1161*4882a593Smuzhiyun 					h = 1;
1162*4882a593Smuzhiyun 
1163*4882a593Smuzhiyun 				if (sample->type == IOTYPE_SYNC)
1164*4882a593Smuzhiyun 					svg_fbox(Y,
1165*4882a593Smuzhiyun 						sample->start_time,
1166*4882a593Smuzhiyun 						sample->end_time,
1167*4882a593Smuzhiyun 						1,
1168*4882a593Smuzhiyun 						sample->err ? "error" : "sync",
1169*4882a593Smuzhiyun 						sample->fd,
1170*4882a593Smuzhiyun 						sample->err,
1171*4882a593Smuzhiyun 						sample->merges);
1172*4882a593Smuzhiyun 				else if (sample->type == IOTYPE_POLL)
1173*4882a593Smuzhiyun 					svg_fbox(Y,
1174*4882a593Smuzhiyun 						sample->start_time,
1175*4882a593Smuzhiyun 						sample->end_time,
1176*4882a593Smuzhiyun 						1,
1177*4882a593Smuzhiyun 						sample->err ? "error" : "poll",
1178*4882a593Smuzhiyun 						sample->fd,
1179*4882a593Smuzhiyun 						sample->err,
1180*4882a593Smuzhiyun 						sample->merges);
1181*4882a593Smuzhiyun 				else if (sample->type == IOTYPE_READ)
1182*4882a593Smuzhiyun 					svg_ubox(Y,
1183*4882a593Smuzhiyun 						sample->start_time,
1184*4882a593Smuzhiyun 						sample->end_time,
1185*4882a593Smuzhiyun 						h,
1186*4882a593Smuzhiyun 						sample->err ? "error" : "disk",
1187*4882a593Smuzhiyun 						sample->fd,
1188*4882a593Smuzhiyun 						sample->err,
1189*4882a593Smuzhiyun 						sample->merges);
1190*4882a593Smuzhiyun 				else if (sample->type == IOTYPE_WRITE)
1191*4882a593Smuzhiyun 					svg_lbox(Y,
1192*4882a593Smuzhiyun 						sample->start_time,
1193*4882a593Smuzhiyun 						sample->end_time,
1194*4882a593Smuzhiyun 						h,
1195*4882a593Smuzhiyun 						sample->err ? "error" : "disk",
1196*4882a593Smuzhiyun 						sample->fd,
1197*4882a593Smuzhiyun 						sample->err,
1198*4882a593Smuzhiyun 						sample->merges);
1199*4882a593Smuzhiyun 				else if (sample->type == IOTYPE_RX)
1200*4882a593Smuzhiyun 					svg_ubox(Y,
1201*4882a593Smuzhiyun 						sample->start_time,
1202*4882a593Smuzhiyun 						sample->end_time,
1203*4882a593Smuzhiyun 						h,
1204*4882a593Smuzhiyun 						sample->err ? "error" : "net",
1205*4882a593Smuzhiyun 						sample->fd,
1206*4882a593Smuzhiyun 						sample->err,
1207*4882a593Smuzhiyun 						sample->merges);
1208*4882a593Smuzhiyun 				else if (sample->type == IOTYPE_TX)
1209*4882a593Smuzhiyun 					svg_lbox(Y,
1210*4882a593Smuzhiyun 						sample->start_time,
1211*4882a593Smuzhiyun 						sample->end_time,
1212*4882a593Smuzhiyun 						h,
1213*4882a593Smuzhiyun 						sample->err ? "error" : "net",
1214*4882a593Smuzhiyun 						sample->fd,
1215*4882a593Smuzhiyun 						sample->err,
1216*4882a593Smuzhiyun 						sample->merges);
1217*4882a593Smuzhiyun 			}
1218*4882a593Smuzhiyun 
1219*4882a593Smuzhiyun 			suf = "";
1220*4882a593Smuzhiyun 			bytes = c->total_bytes;
1221*4882a593Smuzhiyun 			if (bytes > 1024) {
1222*4882a593Smuzhiyun 				bytes = bytes / 1024;
1223*4882a593Smuzhiyun 				suf = "K";
1224*4882a593Smuzhiyun 			}
1225*4882a593Smuzhiyun 			if (bytes > 1024) {
1226*4882a593Smuzhiyun 				bytes = bytes / 1024;
1227*4882a593Smuzhiyun 				suf = "M";
1228*4882a593Smuzhiyun 			}
1229*4882a593Smuzhiyun 			if (bytes > 1024) {
1230*4882a593Smuzhiyun 				bytes = bytes / 1024;
1231*4882a593Smuzhiyun 				suf = "G";
1232*4882a593Smuzhiyun 			}
1233*4882a593Smuzhiyun 
1234*4882a593Smuzhiyun 
1235*4882a593Smuzhiyun 			sprintf(comm, "%s:%i (%3.1f %sbytes)", c->comm ?: "", p->pid, bytes, suf);
1236*4882a593Smuzhiyun 			svg_text(Y, c->start_time, comm);
1237*4882a593Smuzhiyun 
1238*4882a593Smuzhiyun 			c->Y = Y;
1239*4882a593Smuzhiyun 			Y++;
1240*4882a593Smuzhiyun 			c = c->next;
1241*4882a593Smuzhiyun 		}
1242*4882a593Smuzhiyun 		p = p->next;
1243*4882a593Smuzhiyun 	}
1244*4882a593Smuzhiyun }
1245*4882a593Smuzhiyun 
draw_process_bars(struct timechart * tchart)1246*4882a593Smuzhiyun static void draw_process_bars(struct timechart *tchart)
1247*4882a593Smuzhiyun {
1248*4882a593Smuzhiyun 	struct per_pid *p;
1249*4882a593Smuzhiyun 	struct per_pidcomm *c;
1250*4882a593Smuzhiyun 	struct cpu_sample *sample;
1251*4882a593Smuzhiyun 	int Y = 0;
1252*4882a593Smuzhiyun 
1253*4882a593Smuzhiyun 	Y = 2 * tchart->numcpus + 2;
1254*4882a593Smuzhiyun 
1255*4882a593Smuzhiyun 	p = tchart->all_data;
1256*4882a593Smuzhiyun 	while (p) {
1257*4882a593Smuzhiyun 		c = p->all;
1258*4882a593Smuzhiyun 		while (c) {
1259*4882a593Smuzhiyun 			if (!c->display) {
1260*4882a593Smuzhiyun 				c->Y = 0;
1261*4882a593Smuzhiyun 				c = c->next;
1262*4882a593Smuzhiyun 				continue;
1263*4882a593Smuzhiyun 			}
1264*4882a593Smuzhiyun 
1265*4882a593Smuzhiyun 			svg_box(Y, c->start_time, c->end_time, "process");
1266*4882a593Smuzhiyun 			sample = c->samples;
1267*4882a593Smuzhiyun 			while (sample) {
1268*4882a593Smuzhiyun 				if (sample->type == TYPE_RUNNING)
1269*4882a593Smuzhiyun 					svg_running(Y, sample->cpu,
1270*4882a593Smuzhiyun 						    sample->start_time,
1271*4882a593Smuzhiyun 						    sample->end_time,
1272*4882a593Smuzhiyun 						    sample->backtrace);
1273*4882a593Smuzhiyun 				if (sample->type == TYPE_BLOCKED)
1274*4882a593Smuzhiyun 					svg_blocked(Y, sample->cpu,
1275*4882a593Smuzhiyun 						    sample->start_time,
1276*4882a593Smuzhiyun 						    sample->end_time,
1277*4882a593Smuzhiyun 						    sample->backtrace);
1278*4882a593Smuzhiyun 				if (sample->type == TYPE_WAITING)
1279*4882a593Smuzhiyun 					svg_waiting(Y, sample->cpu,
1280*4882a593Smuzhiyun 						    sample->start_time,
1281*4882a593Smuzhiyun 						    sample->end_time,
1282*4882a593Smuzhiyun 						    sample->backtrace);
1283*4882a593Smuzhiyun 				sample = sample->next;
1284*4882a593Smuzhiyun 			}
1285*4882a593Smuzhiyun 
1286*4882a593Smuzhiyun 			if (c->comm) {
1287*4882a593Smuzhiyun 				char comm[256];
1288*4882a593Smuzhiyun 				if (c->total_time > 5000000000) /* 5 seconds */
1289*4882a593Smuzhiyun 					sprintf(comm, "%s:%i (%2.2fs)", c->comm, p->pid, c->total_time / (double)NSEC_PER_SEC);
1290*4882a593Smuzhiyun 				else
1291*4882a593Smuzhiyun 					sprintf(comm, "%s:%i (%3.1fms)", c->comm, p->pid, c->total_time / (double)NSEC_PER_MSEC);
1292*4882a593Smuzhiyun 
1293*4882a593Smuzhiyun 				svg_text(Y, c->start_time, comm);
1294*4882a593Smuzhiyun 			}
1295*4882a593Smuzhiyun 			c->Y = Y;
1296*4882a593Smuzhiyun 			Y++;
1297*4882a593Smuzhiyun 			c = c->next;
1298*4882a593Smuzhiyun 		}
1299*4882a593Smuzhiyun 		p = p->next;
1300*4882a593Smuzhiyun 	}
1301*4882a593Smuzhiyun }
1302*4882a593Smuzhiyun 
add_process_filter(const char * string)1303*4882a593Smuzhiyun static void add_process_filter(const char *string)
1304*4882a593Smuzhiyun {
1305*4882a593Smuzhiyun 	int pid = strtoull(string, NULL, 10);
1306*4882a593Smuzhiyun 	struct process_filter *filt = malloc(sizeof(*filt));
1307*4882a593Smuzhiyun 
1308*4882a593Smuzhiyun 	if (!filt)
1309*4882a593Smuzhiyun 		return;
1310*4882a593Smuzhiyun 
1311*4882a593Smuzhiyun 	filt->name = strdup(string);
1312*4882a593Smuzhiyun 	filt->pid  = pid;
1313*4882a593Smuzhiyun 	filt->next = process_filter;
1314*4882a593Smuzhiyun 
1315*4882a593Smuzhiyun 	process_filter = filt;
1316*4882a593Smuzhiyun }
1317*4882a593Smuzhiyun 
passes_filter(struct per_pid * p,struct per_pidcomm * c)1318*4882a593Smuzhiyun static int passes_filter(struct per_pid *p, struct per_pidcomm *c)
1319*4882a593Smuzhiyun {
1320*4882a593Smuzhiyun 	struct process_filter *filt;
1321*4882a593Smuzhiyun 	if (!process_filter)
1322*4882a593Smuzhiyun 		return 1;
1323*4882a593Smuzhiyun 
1324*4882a593Smuzhiyun 	filt = process_filter;
1325*4882a593Smuzhiyun 	while (filt) {
1326*4882a593Smuzhiyun 		if (filt->pid && p->pid == filt->pid)
1327*4882a593Smuzhiyun 			return 1;
1328*4882a593Smuzhiyun 		if (strcmp(filt->name, c->comm) == 0)
1329*4882a593Smuzhiyun 			return 1;
1330*4882a593Smuzhiyun 		filt = filt->next;
1331*4882a593Smuzhiyun 	}
1332*4882a593Smuzhiyun 	return 0;
1333*4882a593Smuzhiyun }
1334*4882a593Smuzhiyun 
determine_display_tasks_filtered(struct timechart * tchart)1335*4882a593Smuzhiyun static int determine_display_tasks_filtered(struct timechart *tchart)
1336*4882a593Smuzhiyun {
1337*4882a593Smuzhiyun 	struct per_pid *p;
1338*4882a593Smuzhiyun 	struct per_pidcomm *c;
1339*4882a593Smuzhiyun 	int count = 0;
1340*4882a593Smuzhiyun 
1341*4882a593Smuzhiyun 	p = tchart->all_data;
1342*4882a593Smuzhiyun 	while (p) {
1343*4882a593Smuzhiyun 		p->display = 0;
1344*4882a593Smuzhiyun 		if (p->start_time == 1)
1345*4882a593Smuzhiyun 			p->start_time = tchart->first_time;
1346*4882a593Smuzhiyun 
1347*4882a593Smuzhiyun 		/* no exit marker, task kept running to the end */
1348*4882a593Smuzhiyun 		if (p->end_time == 0)
1349*4882a593Smuzhiyun 			p->end_time = tchart->last_time;
1350*4882a593Smuzhiyun 
1351*4882a593Smuzhiyun 		c = p->all;
1352*4882a593Smuzhiyun 
1353*4882a593Smuzhiyun 		while (c) {
1354*4882a593Smuzhiyun 			c->display = 0;
1355*4882a593Smuzhiyun 
1356*4882a593Smuzhiyun 			if (c->start_time == 1)
1357*4882a593Smuzhiyun 				c->start_time = tchart->first_time;
1358*4882a593Smuzhiyun 
1359*4882a593Smuzhiyun 			if (passes_filter(p, c)) {
1360*4882a593Smuzhiyun 				c->display = 1;
1361*4882a593Smuzhiyun 				p->display = 1;
1362*4882a593Smuzhiyun 				count++;
1363*4882a593Smuzhiyun 			}
1364*4882a593Smuzhiyun 
1365*4882a593Smuzhiyun 			if (c->end_time == 0)
1366*4882a593Smuzhiyun 				c->end_time = tchart->last_time;
1367*4882a593Smuzhiyun 
1368*4882a593Smuzhiyun 			c = c->next;
1369*4882a593Smuzhiyun 		}
1370*4882a593Smuzhiyun 		p = p->next;
1371*4882a593Smuzhiyun 	}
1372*4882a593Smuzhiyun 	return count;
1373*4882a593Smuzhiyun }
1374*4882a593Smuzhiyun 
determine_display_tasks(struct timechart * tchart,u64 threshold)1375*4882a593Smuzhiyun static int determine_display_tasks(struct timechart *tchart, u64 threshold)
1376*4882a593Smuzhiyun {
1377*4882a593Smuzhiyun 	struct per_pid *p;
1378*4882a593Smuzhiyun 	struct per_pidcomm *c;
1379*4882a593Smuzhiyun 	int count = 0;
1380*4882a593Smuzhiyun 
1381*4882a593Smuzhiyun 	p = tchart->all_data;
1382*4882a593Smuzhiyun 	while (p) {
1383*4882a593Smuzhiyun 		p->display = 0;
1384*4882a593Smuzhiyun 		if (p->start_time == 1)
1385*4882a593Smuzhiyun 			p->start_time = tchart->first_time;
1386*4882a593Smuzhiyun 
1387*4882a593Smuzhiyun 		/* no exit marker, task kept running to the end */
1388*4882a593Smuzhiyun 		if (p->end_time == 0)
1389*4882a593Smuzhiyun 			p->end_time = tchart->last_time;
1390*4882a593Smuzhiyun 		if (p->total_time >= threshold)
1391*4882a593Smuzhiyun 			p->display = 1;
1392*4882a593Smuzhiyun 
1393*4882a593Smuzhiyun 		c = p->all;
1394*4882a593Smuzhiyun 
1395*4882a593Smuzhiyun 		while (c) {
1396*4882a593Smuzhiyun 			c->display = 0;
1397*4882a593Smuzhiyun 
1398*4882a593Smuzhiyun 			if (c->start_time == 1)
1399*4882a593Smuzhiyun 				c->start_time = tchart->first_time;
1400*4882a593Smuzhiyun 
1401*4882a593Smuzhiyun 			if (c->total_time >= threshold) {
1402*4882a593Smuzhiyun 				c->display = 1;
1403*4882a593Smuzhiyun 				count++;
1404*4882a593Smuzhiyun 			}
1405*4882a593Smuzhiyun 
1406*4882a593Smuzhiyun 			if (c->end_time == 0)
1407*4882a593Smuzhiyun 				c->end_time = tchart->last_time;
1408*4882a593Smuzhiyun 
1409*4882a593Smuzhiyun 			c = c->next;
1410*4882a593Smuzhiyun 		}
1411*4882a593Smuzhiyun 		p = p->next;
1412*4882a593Smuzhiyun 	}
1413*4882a593Smuzhiyun 	return count;
1414*4882a593Smuzhiyun }
1415*4882a593Smuzhiyun 
determine_display_io_tasks(struct timechart * timechart,u64 threshold)1416*4882a593Smuzhiyun static int determine_display_io_tasks(struct timechart *timechart, u64 threshold)
1417*4882a593Smuzhiyun {
1418*4882a593Smuzhiyun 	struct per_pid *p;
1419*4882a593Smuzhiyun 	struct per_pidcomm *c;
1420*4882a593Smuzhiyun 	int count = 0;
1421*4882a593Smuzhiyun 
1422*4882a593Smuzhiyun 	p = timechart->all_data;
1423*4882a593Smuzhiyun 	while (p) {
1424*4882a593Smuzhiyun 		/* no exit marker, task kept running to the end */
1425*4882a593Smuzhiyun 		if (p->end_time == 0)
1426*4882a593Smuzhiyun 			p->end_time = timechart->last_time;
1427*4882a593Smuzhiyun 
1428*4882a593Smuzhiyun 		c = p->all;
1429*4882a593Smuzhiyun 
1430*4882a593Smuzhiyun 		while (c) {
1431*4882a593Smuzhiyun 			c->display = 0;
1432*4882a593Smuzhiyun 
1433*4882a593Smuzhiyun 			if (c->total_bytes >= threshold) {
1434*4882a593Smuzhiyun 				c->display = 1;
1435*4882a593Smuzhiyun 				count++;
1436*4882a593Smuzhiyun 			}
1437*4882a593Smuzhiyun 
1438*4882a593Smuzhiyun 			if (c->end_time == 0)
1439*4882a593Smuzhiyun 				c->end_time = timechart->last_time;
1440*4882a593Smuzhiyun 
1441*4882a593Smuzhiyun 			c = c->next;
1442*4882a593Smuzhiyun 		}
1443*4882a593Smuzhiyun 		p = p->next;
1444*4882a593Smuzhiyun 	}
1445*4882a593Smuzhiyun 	return count;
1446*4882a593Smuzhiyun }
1447*4882a593Smuzhiyun 
1448*4882a593Smuzhiyun #define BYTES_THRESH (1 * 1024 * 1024)
1449*4882a593Smuzhiyun #define TIME_THRESH 10000000
1450*4882a593Smuzhiyun 
write_svg_file(struct timechart * tchart,const char * filename)1451*4882a593Smuzhiyun static void write_svg_file(struct timechart *tchart, const char *filename)
1452*4882a593Smuzhiyun {
1453*4882a593Smuzhiyun 	u64 i;
1454*4882a593Smuzhiyun 	int count;
1455*4882a593Smuzhiyun 	int thresh = tchart->io_events ? BYTES_THRESH : TIME_THRESH;
1456*4882a593Smuzhiyun 
1457*4882a593Smuzhiyun 	if (tchart->power_only)
1458*4882a593Smuzhiyun 		tchart->proc_num = 0;
1459*4882a593Smuzhiyun 
1460*4882a593Smuzhiyun 	/* We'd like to show at least proc_num tasks;
1461*4882a593Smuzhiyun 	 * be less picky if we have fewer */
1462*4882a593Smuzhiyun 	do {
1463*4882a593Smuzhiyun 		if (process_filter)
1464*4882a593Smuzhiyun 			count = determine_display_tasks_filtered(tchart);
1465*4882a593Smuzhiyun 		else if (tchart->io_events)
1466*4882a593Smuzhiyun 			count = determine_display_io_tasks(tchart, thresh);
1467*4882a593Smuzhiyun 		else
1468*4882a593Smuzhiyun 			count = determine_display_tasks(tchart, thresh);
1469*4882a593Smuzhiyun 		thresh /= 10;
1470*4882a593Smuzhiyun 	} while (!process_filter && thresh && count < tchart->proc_num);
1471*4882a593Smuzhiyun 
1472*4882a593Smuzhiyun 	if (!tchart->proc_num)
1473*4882a593Smuzhiyun 		count = 0;
1474*4882a593Smuzhiyun 
1475*4882a593Smuzhiyun 	if (tchart->io_events) {
1476*4882a593Smuzhiyun 		open_svg(filename, 0, count, tchart->first_time, tchart->last_time);
1477*4882a593Smuzhiyun 
1478*4882a593Smuzhiyun 		svg_time_grid(0.5);
1479*4882a593Smuzhiyun 		svg_io_legenda();
1480*4882a593Smuzhiyun 
1481*4882a593Smuzhiyun 		draw_io_bars(tchart);
1482*4882a593Smuzhiyun 	} else {
1483*4882a593Smuzhiyun 		open_svg(filename, tchart->numcpus, count, tchart->first_time, tchart->last_time);
1484*4882a593Smuzhiyun 
1485*4882a593Smuzhiyun 		svg_time_grid(0);
1486*4882a593Smuzhiyun 
1487*4882a593Smuzhiyun 		svg_legenda();
1488*4882a593Smuzhiyun 
1489*4882a593Smuzhiyun 		for (i = 0; i < tchart->numcpus; i++)
1490*4882a593Smuzhiyun 			svg_cpu_box(i, tchart->max_freq, tchart->turbo_frequency);
1491*4882a593Smuzhiyun 
1492*4882a593Smuzhiyun 		draw_cpu_usage(tchart);
1493*4882a593Smuzhiyun 		if (tchart->proc_num)
1494*4882a593Smuzhiyun 			draw_process_bars(tchart);
1495*4882a593Smuzhiyun 		if (!tchart->tasks_only)
1496*4882a593Smuzhiyun 			draw_c_p_states(tchart);
1497*4882a593Smuzhiyun 		if (tchart->proc_num)
1498*4882a593Smuzhiyun 			draw_wakeups(tchart);
1499*4882a593Smuzhiyun 	}
1500*4882a593Smuzhiyun 
1501*4882a593Smuzhiyun 	svg_close();
1502*4882a593Smuzhiyun }
1503*4882a593Smuzhiyun 
process_header(struct perf_file_section * section __maybe_unused,struct perf_header * ph,int feat,int fd __maybe_unused,void * data)1504*4882a593Smuzhiyun static int process_header(struct perf_file_section *section __maybe_unused,
1505*4882a593Smuzhiyun 			  struct perf_header *ph,
1506*4882a593Smuzhiyun 			  int feat,
1507*4882a593Smuzhiyun 			  int fd __maybe_unused,
1508*4882a593Smuzhiyun 			  void *data)
1509*4882a593Smuzhiyun {
1510*4882a593Smuzhiyun 	struct timechart *tchart = data;
1511*4882a593Smuzhiyun 
1512*4882a593Smuzhiyun 	switch (feat) {
1513*4882a593Smuzhiyun 	case HEADER_NRCPUS:
1514*4882a593Smuzhiyun 		tchart->numcpus = ph->env.nr_cpus_avail;
1515*4882a593Smuzhiyun 		break;
1516*4882a593Smuzhiyun 
1517*4882a593Smuzhiyun 	case HEADER_CPU_TOPOLOGY:
1518*4882a593Smuzhiyun 		if (!tchart->topology)
1519*4882a593Smuzhiyun 			break;
1520*4882a593Smuzhiyun 
1521*4882a593Smuzhiyun 		if (svg_build_topology_map(&ph->env))
1522*4882a593Smuzhiyun 			fprintf(stderr, "problem building topology\n");
1523*4882a593Smuzhiyun 		break;
1524*4882a593Smuzhiyun 
1525*4882a593Smuzhiyun 	default:
1526*4882a593Smuzhiyun 		break;
1527*4882a593Smuzhiyun 	}
1528*4882a593Smuzhiyun 
1529*4882a593Smuzhiyun 	return 0;
1530*4882a593Smuzhiyun }
1531*4882a593Smuzhiyun 
__cmd_timechart(struct timechart * tchart,const char * output_name)1532*4882a593Smuzhiyun static int __cmd_timechart(struct timechart *tchart, const char *output_name)
1533*4882a593Smuzhiyun {
1534*4882a593Smuzhiyun 	const struct evsel_str_handler power_tracepoints[] = {
1535*4882a593Smuzhiyun 		{ "power:cpu_idle",		process_sample_cpu_idle },
1536*4882a593Smuzhiyun 		{ "power:cpu_frequency",	process_sample_cpu_frequency },
1537*4882a593Smuzhiyun 		{ "sched:sched_wakeup",		process_sample_sched_wakeup },
1538*4882a593Smuzhiyun 		{ "sched:sched_switch",		process_sample_sched_switch },
1539*4882a593Smuzhiyun #ifdef SUPPORT_OLD_POWER_EVENTS
1540*4882a593Smuzhiyun 		{ "power:power_start",		process_sample_power_start },
1541*4882a593Smuzhiyun 		{ "power:power_end",		process_sample_power_end },
1542*4882a593Smuzhiyun 		{ "power:power_frequency",	process_sample_power_frequency },
1543*4882a593Smuzhiyun #endif
1544*4882a593Smuzhiyun 
1545*4882a593Smuzhiyun 		{ "syscalls:sys_enter_read",		process_enter_read },
1546*4882a593Smuzhiyun 		{ "syscalls:sys_enter_pread64",		process_enter_read },
1547*4882a593Smuzhiyun 		{ "syscalls:sys_enter_readv",		process_enter_read },
1548*4882a593Smuzhiyun 		{ "syscalls:sys_enter_preadv",		process_enter_read },
1549*4882a593Smuzhiyun 		{ "syscalls:sys_enter_write",		process_enter_write },
1550*4882a593Smuzhiyun 		{ "syscalls:sys_enter_pwrite64",	process_enter_write },
1551*4882a593Smuzhiyun 		{ "syscalls:sys_enter_writev",		process_enter_write },
1552*4882a593Smuzhiyun 		{ "syscalls:sys_enter_pwritev",		process_enter_write },
1553*4882a593Smuzhiyun 		{ "syscalls:sys_enter_sync",		process_enter_sync },
1554*4882a593Smuzhiyun 		{ "syscalls:sys_enter_sync_file_range",	process_enter_sync },
1555*4882a593Smuzhiyun 		{ "syscalls:sys_enter_fsync",		process_enter_sync },
1556*4882a593Smuzhiyun 		{ "syscalls:sys_enter_msync",		process_enter_sync },
1557*4882a593Smuzhiyun 		{ "syscalls:sys_enter_recvfrom",	process_enter_rx },
1558*4882a593Smuzhiyun 		{ "syscalls:sys_enter_recvmmsg",	process_enter_rx },
1559*4882a593Smuzhiyun 		{ "syscalls:sys_enter_recvmsg",		process_enter_rx },
1560*4882a593Smuzhiyun 		{ "syscalls:sys_enter_sendto",		process_enter_tx },
1561*4882a593Smuzhiyun 		{ "syscalls:sys_enter_sendmsg",		process_enter_tx },
1562*4882a593Smuzhiyun 		{ "syscalls:sys_enter_sendmmsg",	process_enter_tx },
1563*4882a593Smuzhiyun 		{ "syscalls:sys_enter_epoll_pwait",	process_enter_poll },
1564*4882a593Smuzhiyun 		{ "syscalls:sys_enter_epoll_wait",	process_enter_poll },
1565*4882a593Smuzhiyun 		{ "syscalls:sys_enter_poll",		process_enter_poll },
1566*4882a593Smuzhiyun 		{ "syscalls:sys_enter_ppoll",		process_enter_poll },
1567*4882a593Smuzhiyun 		{ "syscalls:sys_enter_pselect6",	process_enter_poll },
1568*4882a593Smuzhiyun 		{ "syscalls:sys_enter_select",		process_enter_poll },
1569*4882a593Smuzhiyun 
1570*4882a593Smuzhiyun 		{ "syscalls:sys_exit_read",		process_exit_read },
1571*4882a593Smuzhiyun 		{ "syscalls:sys_exit_pread64",		process_exit_read },
1572*4882a593Smuzhiyun 		{ "syscalls:sys_exit_readv",		process_exit_read },
1573*4882a593Smuzhiyun 		{ "syscalls:sys_exit_preadv",		process_exit_read },
1574*4882a593Smuzhiyun 		{ "syscalls:sys_exit_write",		process_exit_write },
1575*4882a593Smuzhiyun 		{ "syscalls:sys_exit_pwrite64",		process_exit_write },
1576*4882a593Smuzhiyun 		{ "syscalls:sys_exit_writev",		process_exit_write },
1577*4882a593Smuzhiyun 		{ "syscalls:sys_exit_pwritev",		process_exit_write },
1578*4882a593Smuzhiyun 		{ "syscalls:sys_exit_sync",		process_exit_sync },
1579*4882a593Smuzhiyun 		{ "syscalls:sys_exit_sync_file_range",	process_exit_sync },
1580*4882a593Smuzhiyun 		{ "syscalls:sys_exit_fsync",		process_exit_sync },
1581*4882a593Smuzhiyun 		{ "syscalls:sys_exit_msync",		process_exit_sync },
1582*4882a593Smuzhiyun 		{ "syscalls:sys_exit_recvfrom",		process_exit_rx },
1583*4882a593Smuzhiyun 		{ "syscalls:sys_exit_recvmmsg",		process_exit_rx },
1584*4882a593Smuzhiyun 		{ "syscalls:sys_exit_recvmsg",		process_exit_rx },
1585*4882a593Smuzhiyun 		{ "syscalls:sys_exit_sendto",		process_exit_tx },
1586*4882a593Smuzhiyun 		{ "syscalls:sys_exit_sendmsg",		process_exit_tx },
1587*4882a593Smuzhiyun 		{ "syscalls:sys_exit_sendmmsg",		process_exit_tx },
1588*4882a593Smuzhiyun 		{ "syscalls:sys_exit_epoll_pwait",	process_exit_poll },
1589*4882a593Smuzhiyun 		{ "syscalls:sys_exit_epoll_wait",	process_exit_poll },
1590*4882a593Smuzhiyun 		{ "syscalls:sys_exit_poll",		process_exit_poll },
1591*4882a593Smuzhiyun 		{ "syscalls:sys_exit_ppoll",		process_exit_poll },
1592*4882a593Smuzhiyun 		{ "syscalls:sys_exit_pselect6",		process_exit_poll },
1593*4882a593Smuzhiyun 		{ "syscalls:sys_exit_select",		process_exit_poll },
1594*4882a593Smuzhiyun 	};
1595*4882a593Smuzhiyun 	struct perf_data data = {
1596*4882a593Smuzhiyun 		.path  = input_name,
1597*4882a593Smuzhiyun 		.mode  = PERF_DATA_MODE_READ,
1598*4882a593Smuzhiyun 		.force = tchart->force,
1599*4882a593Smuzhiyun 	};
1600*4882a593Smuzhiyun 
1601*4882a593Smuzhiyun 	struct perf_session *session = perf_session__new(&data, false,
1602*4882a593Smuzhiyun 							 &tchart->tool);
1603*4882a593Smuzhiyun 	int ret = -EINVAL;
1604*4882a593Smuzhiyun 
1605*4882a593Smuzhiyun 	if (IS_ERR(session))
1606*4882a593Smuzhiyun 		return PTR_ERR(session);
1607*4882a593Smuzhiyun 
1608*4882a593Smuzhiyun 	symbol__init(&session->header.env);
1609*4882a593Smuzhiyun 
1610*4882a593Smuzhiyun 	(void)perf_header__process_sections(&session->header,
1611*4882a593Smuzhiyun 					    perf_data__fd(session->data),
1612*4882a593Smuzhiyun 					    tchart,
1613*4882a593Smuzhiyun 					    process_header);
1614*4882a593Smuzhiyun 
1615*4882a593Smuzhiyun 	if (!perf_session__has_traces(session, "timechart record"))
1616*4882a593Smuzhiyun 		goto out_delete;
1617*4882a593Smuzhiyun 
1618*4882a593Smuzhiyun 	if (perf_session__set_tracepoints_handlers(session,
1619*4882a593Smuzhiyun 						   power_tracepoints)) {
1620*4882a593Smuzhiyun 		pr_err("Initializing session tracepoint handlers failed\n");
1621*4882a593Smuzhiyun 		goto out_delete;
1622*4882a593Smuzhiyun 	}
1623*4882a593Smuzhiyun 
1624*4882a593Smuzhiyun 	ret = perf_session__process_events(session);
1625*4882a593Smuzhiyun 	if (ret)
1626*4882a593Smuzhiyun 		goto out_delete;
1627*4882a593Smuzhiyun 
1628*4882a593Smuzhiyun 	end_sample_processing(tchart);
1629*4882a593Smuzhiyun 
1630*4882a593Smuzhiyun 	sort_pids(tchart);
1631*4882a593Smuzhiyun 
1632*4882a593Smuzhiyun 	write_svg_file(tchart, output_name);
1633*4882a593Smuzhiyun 
1634*4882a593Smuzhiyun 	pr_info("Written %2.1f seconds of trace to %s.\n",
1635*4882a593Smuzhiyun 		(tchart->last_time - tchart->first_time) / (double)NSEC_PER_SEC, output_name);
1636*4882a593Smuzhiyun out_delete:
1637*4882a593Smuzhiyun 	perf_session__delete(session);
1638*4882a593Smuzhiyun 	return ret;
1639*4882a593Smuzhiyun }
1640*4882a593Smuzhiyun 
timechart__io_record(int argc,const char ** argv)1641*4882a593Smuzhiyun static int timechart__io_record(int argc, const char **argv)
1642*4882a593Smuzhiyun {
1643*4882a593Smuzhiyun 	unsigned int rec_argc, i;
1644*4882a593Smuzhiyun 	const char **rec_argv;
1645*4882a593Smuzhiyun 	const char **p;
1646*4882a593Smuzhiyun 	char *filter = NULL;
1647*4882a593Smuzhiyun 
1648*4882a593Smuzhiyun 	const char * const common_args[] = {
1649*4882a593Smuzhiyun 		"record", "-a", "-R", "-c", "1",
1650*4882a593Smuzhiyun 	};
1651*4882a593Smuzhiyun 	unsigned int common_args_nr = ARRAY_SIZE(common_args);
1652*4882a593Smuzhiyun 
1653*4882a593Smuzhiyun 	const char * const disk_events[] = {
1654*4882a593Smuzhiyun 		"syscalls:sys_enter_read",
1655*4882a593Smuzhiyun 		"syscalls:sys_enter_pread64",
1656*4882a593Smuzhiyun 		"syscalls:sys_enter_readv",
1657*4882a593Smuzhiyun 		"syscalls:sys_enter_preadv",
1658*4882a593Smuzhiyun 		"syscalls:sys_enter_write",
1659*4882a593Smuzhiyun 		"syscalls:sys_enter_pwrite64",
1660*4882a593Smuzhiyun 		"syscalls:sys_enter_writev",
1661*4882a593Smuzhiyun 		"syscalls:sys_enter_pwritev",
1662*4882a593Smuzhiyun 		"syscalls:sys_enter_sync",
1663*4882a593Smuzhiyun 		"syscalls:sys_enter_sync_file_range",
1664*4882a593Smuzhiyun 		"syscalls:sys_enter_fsync",
1665*4882a593Smuzhiyun 		"syscalls:sys_enter_msync",
1666*4882a593Smuzhiyun 
1667*4882a593Smuzhiyun 		"syscalls:sys_exit_read",
1668*4882a593Smuzhiyun 		"syscalls:sys_exit_pread64",
1669*4882a593Smuzhiyun 		"syscalls:sys_exit_readv",
1670*4882a593Smuzhiyun 		"syscalls:sys_exit_preadv",
1671*4882a593Smuzhiyun 		"syscalls:sys_exit_write",
1672*4882a593Smuzhiyun 		"syscalls:sys_exit_pwrite64",
1673*4882a593Smuzhiyun 		"syscalls:sys_exit_writev",
1674*4882a593Smuzhiyun 		"syscalls:sys_exit_pwritev",
1675*4882a593Smuzhiyun 		"syscalls:sys_exit_sync",
1676*4882a593Smuzhiyun 		"syscalls:sys_exit_sync_file_range",
1677*4882a593Smuzhiyun 		"syscalls:sys_exit_fsync",
1678*4882a593Smuzhiyun 		"syscalls:sys_exit_msync",
1679*4882a593Smuzhiyun 	};
1680*4882a593Smuzhiyun 	unsigned int disk_events_nr = ARRAY_SIZE(disk_events);
1681*4882a593Smuzhiyun 
1682*4882a593Smuzhiyun 	const char * const net_events[] = {
1683*4882a593Smuzhiyun 		"syscalls:sys_enter_recvfrom",
1684*4882a593Smuzhiyun 		"syscalls:sys_enter_recvmmsg",
1685*4882a593Smuzhiyun 		"syscalls:sys_enter_recvmsg",
1686*4882a593Smuzhiyun 		"syscalls:sys_enter_sendto",
1687*4882a593Smuzhiyun 		"syscalls:sys_enter_sendmsg",
1688*4882a593Smuzhiyun 		"syscalls:sys_enter_sendmmsg",
1689*4882a593Smuzhiyun 
1690*4882a593Smuzhiyun 		"syscalls:sys_exit_recvfrom",
1691*4882a593Smuzhiyun 		"syscalls:sys_exit_recvmmsg",
1692*4882a593Smuzhiyun 		"syscalls:sys_exit_recvmsg",
1693*4882a593Smuzhiyun 		"syscalls:sys_exit_sendto",
1694*4882a593Smuzhiyun 		"syscalls:sys_exit_sendmsg",
1695*4882a593Smuzhiyun 		"syscalls:sys_exit_sendmmsg",
1696*4882a593Smuzhiyun 	};
1697*4882a593Smuzhiyun 	unsigned int net_events_nr = ARRAY_SIZE(net_events);
1698*4882a593Smuzhiyun 
1699*4882a593Smuzhiyun 	const char * const poll_events[] = {
1700*4882a593Smuzhiyun 		"syscalls:sys_enter_epoll_pwait",
1701*4882a593Smuzhiyun 		"syscalls:sys_enter_epoll_wait",
1702*4882a593Smuzhiyun 		"syscalls:sys_enter_poll",
1703*4882a593Smuzhiyun 		"syscalls:sys_enter_ppoll",
1704*4882a593Smuzhiyun 		"syscalls:sys_enter_pselect6",
1705*4882a593Smuzhiyun 		"syscalls:sys_enter_select",
1706*4882a593Smuzhiyun 
1707*4882a593Smuzhiyun 		"syscalls:sys_exit_epoll_pwait",
1708*4882a593Smuzhiyun 		"syscalls:sys_exit_epoll_wait",
1709*4882a593Smuzhiyun 		"syscalls:sys_exit_poll",
1710*4882a593Smuzhiyun 		"syscalls:sys_exit_ppoll",
1711*4882a593Smuzhiyun 		"syscalls:sys_exit_pselect6",
1712*4882a593Smuzhiyun 		"syscalls:sys_exit_select",
1713*4882a593Smuzhiyun 	};
1714*4882a593Smuzhiyun 	unsigned int poll_events_nr = ARRAY_SIZE(poll_events);
1715*4882a593Smuzhiyun 
1716*4882a593Smuzhiyun 	rec_argc = common_args_nr +
1717*4882a593Smuzhiyun 		disk_events_nr * 4 +
1718*4882a593Smuzhiyun 		net_events_nr * 4 +
1719*4882a593Smuzhiyun 		poll_events_nr * 4 +
1720*4882a593Smuzhiyun 		argc;
1721*4882a593Smuzhiyun 	rec_argv = calloc(rec_argc + 1, sizeof(char *));
1722*4882a593Smuzhiyun 
1723*4882a593Smuzhiyun 	if (rec_argv == NULL)
1724*4882a593Smuzhiyun 		return -ENOMEM;
1725*4882a593Smuzhiyun 
1726*4882a593Smuzhiyun 	if (asprintf(&filter, "common_pid != %d", getpid()) < 0) {
1727*4882a593Smuzhiyun 		free(rec_argv);
1728*4882a593Smuzhiyun 		return -ENOMEM;
1729*4882a593Smuzhiyun 	}
1730*4882a593Smuzhiyun 
1731*4882a593Smuzhiyun 	p = rec_argv;
1732*4882a593Smuzhiyun 	for (i = 0; i < common_args_nr; i++)
1733*4882a593Smuzhiyun 		*p++ = strdup(common_args[i]);
1734*4882a593Smuzhiyun 
1735*4882a593Smuzhiyun 	for (i = 0; i < disk_events_nr; i++) {
1736*4882a593Smuzhiyun 		if (!is_valid_tracepoint(disk_events[i])) {
1737*4882a593Smuzhiyun 			rec_argc -= 4;
1738*4882a593Smuzhiyun 			continue;
1739*4882a593Smuzhiyun 		}
1740*4882a593Smuzhiyun 
1741*4882a593Smuzhiyun 		*p++ = "-e";
1742*4882a593Smuzhiyun 		*p++ = strdup(disk_events[i]);
1743*4882a593Smuzhiyun 		*p++ = "--filter";
1744*4882a593Smuzhiyun 		*p++ = filter;
1745*4882a593Smuzhiyun 	}
1746*4882a593Smuzhiyun 	for (i = 0; i < net_events_nr; i++) {
1747*4882a593Smuzhiyun 		if (!is_valid_tracepoint(net_events[i])) {
1748*4882a593Smuzhiyun 			rec_argc -= 4;
1749*4882a593Smuzhiyun 			continue;
1750*4882a593Smuzhiyun 		}
1751*4882a593Smuzhiyun 
1752*4882a593Smuzhiyun 		*p++ = "-e";
1753*4882a593Smuzhiyun 		*p++ = strdup(net_events[i]);
1754*4882a593Smuzhiyun 		*p++ = "--filter";
1755*4882a593Smuzhiyun 		*p++ = filter;
1756*4882a593Smuzhiyun 	}
1757*4882a593Smuzhiyun 	for (i = 0; i < poll_events_nr; i++) {
1758*4882a593Smuzhiyun 		if (!is_valid_tracepoint(poll_events[i])) {
1759*4882a593Smuzhiyun 			rec_argc -= 4;
1760*4882a593Smuzhiyun 			continue;
1761*4882a593Smuzhiyun 		}
1762*4882a593Smuzhiyun 
1763*4882a593Smuzhiyun 		*p++ = "-e";
1764*4882a593Smuzhiyun 		*p++ = strdup(poll_events[i]);
1765*4882a593Smuzhiyun 		*p++ = "--filter";
1766*4882a593Smuzhiyun 		*p++ = filter;
1767*4882a593Smuzhiyun 	}
1768*4882a593Smuzhiyun 
1769*4882a593Smuzhiyun 	for (i = 0; i < (unsigned int)argc; i++)
1770*4882a593Smuzhiyun 		*p++ = argv[i];
1771*4882a593Smuzhiyun 
1772*4882a593Smuzhiyun 	return cmd_record(rec_argc, rec_argv);
1773*4882a593Smuzhiyun }
1774*4882a593Smuzhiyun 
1775*4882a593Smuzhiyun 
timechart__record(struct timechart * tchart,int argc,const char ** argv)1776*4882a593Smuzhiyun static int timechart__record(struct timechart *tchart, int argc, const char **argv)
1777*4882a593Smuzhiyun {
1778*4882a593Smuzhiyun 	unsigned int rec_argc, i, j;
1779*4882a593Smuzhiyun 	const char **rec_argv;
1780*4882a593Smuzhiyun 	const char **p;
1781*4882a593Smuzhiyun 	unsigned int record_elems;
1782*4882a593Smuzhiyun 
1783*4882a593Smuzhiyun 	const char * const common_args[] = {
1784*4882a593Smuzhiyun 		"record", "-a", "-R", "-c", "1",
1785*4882a593Smuzhiyun 	};
1786*4882a593Smuzhiyun 	unsigned int common_args_nr = ARRAY_SIZE(common_args);
1787*4882a593Smuzhiyun 
1788*4882a593Smuzhiyun 	const char * const backtrace_args[] = {
1789*4882a593Smuzhiyun 		"-g",
1790*4882a593Smuzhiyun 	};
1791*4882a593Smuzhiyun 	unsigned int backtrace_args_no = ARRAY_SIZE(backtrace_args);
1792*4882a593Smuzhiyun 
1793*4882a593Smuzhiyun 	const char * const power_args[] = {
1794*4882a593Smuzhiyun 		"-e", "power:cpu_frequency",
1795*4882a593Smuzhiyun 		"-e", "power:cpu_idle",
1796*4882a593Smuzhiyun 	};
1797*4882a593Smuzhiyun 	unsigned int power_args_nr = ARRAY_SIZE(power_args);
1798*4882a593Smuzhiyun 
1799*4882a593Smuzhiyun 	const char * const old_power_args[] = {
1800*4882a593Smuzhiyun #ifdef SUPPORT_OLD_POWER_EVENTS
1801*4882a593Smuzhiyun 		"-e", "power:power_start",
1802*4882a593Smuzhiyun 		"-e", "power:power_end",
1803*4882a593Smuzhiyun 		"-e", "power:power_frequency",
1804*4882a593Smuzhiyun #endif
1805*4882a593Smuzhiyun 	};
1806*4882a593Smuzhiyun 	unsigned int old_power_args_nr = ARRAY_SIZE(old_power_args);
1807*4882a593Smuzhiyun 
1808*4882a593Smuzhiyun 	const char * const tasks_args[] = {
1809*4882a593Smuzhiyun 		"-e", "sched:sched_wakeup",
1810*4882a593Smuzhiyun 		"-e", "sched:sched_switch",
1811*4882a593Smuzhiyun 	};
1812*4882a593Smuzhiyun 	unsigned int tasks_args_nr = ARRAY_SIZE(tasks_args);
1813*4882a593Smuzhiyun 
1814*4882a593Smuzhiyun #ifdef SUPPORT_OLD_POWER_EVENTS
1815*4882a593Smuzhiyun 	if (!is_valid_tracepoint("power:cpu_idle") &&
1816*4882a593Smuzhiyun 	    is_valid_tracepoint("power:power_start")) {
1817*4882a593Smuzhiyun 		use_old_power_events = 1;
1818*4882a593Smuzhiyun 		power_args_nr = 0;
1819*4882a593Smuzhiyun 	} else {
1820*4882a593Smuzhiyun 		old_power_args_nr = 0;
1821*4882a593Smuzhiyun 	}
1822*4882a593Smuzhiyun #endif
1823*4882a593Smuzhiyun 
1824*4882a593Smuzhiyun 	if (tchart->power_only)
1825*4882a593Smuzhiyun 		tasks_args_nr = 0;
1826*4882a593Smuzhiyun 
1827*4882a593Smuzhiyun 	if (tchart->tasks_only) {
1828*4882a593Smuzhiyun 		power_args_nr = 0;
1829*4882a593Smuzhiyun 		old_power_args_nr = 0;
1830*4882a593Smuzhiyun 	}
1831*4882a593Smuzhiyun 
1832*4882a593Smuzhiyun 	if (!tchart->with_backtrace)
1833*4882a593Smuzhiyun 		backtrace_args_no = 0;
1834*4882a593Smuzhiyun 
1835*4882a593Smuzhiyun 	record_elems = common_args_nr + tasks_args_nr +
1836*4882a593Smuzhiyun 		power_args_nr + old_power_args_nr + backtrace_args_no;
1837*4882a593Smuzhiyun 
1838*4882a593Smuzhiyun 	rec_argc = record_elems + argc;
1839*4882a593Smuzhiyun 	rec_argv = calloc(rec_argc + 1, sizeof(char *));
1840*4882a593Smuzhiyun 
1841*4882a593Smuzhiyun 	if (rec_argv == NULL)
1842*4882a593Smuzhiyun 		return -ENOMEM;
1843*4882a593Smuzhiyun 
1844*4882a593Smuzhiyun 	p = rec_argv;
1845*4882a593Smuzhiyun 	for (i = 0; i < common_args_nr; i++)
1846*4882a593Smuzhiyun 		*p++ = strdup(common_args[i]);
1847*4882a593Smuzhiyun 
1848*4882a593Smuzhiyun 	for (i = 0; i < backtrace_args_no; i++)
1849*4882a593Smuzhiyun 		*p++ = strdup(backtrace_args[i]);
1850*4882a593Smuzhiyun 
1851*4882a593Smuzhiyun 	for (i = 0; i < tasks_args_nr; i++)
1852*4882a593Smuzhiyun 		*p++ = strdup(tasks_args[i]);
1853*4882a593Smuzhiyun 
1854*4882a593Smuzhiyun 	for (i = 0; i < power_args_nr; i++)
1855*4882a593Smuzhiyun 		*p++ = strdup(power_args[i]);
1856*4882a593Smuzhiyun 
1857*4882a593Smuzhiyun 	for (i = 0; i < old_power_args_nr; i++)
1858*4882a593Smuzhiyun 		*p++ = strdup(old_power_args[i]);
1859*4882a593Smuzhiyun 
1860*4882a593Smuzhiyun 	for (j = 0; j < (unsigned int)argc; j++)
1861*4882a593Smuzhiyun 		*p++ = argv[j];
1862*4882a593Smuzhiyun 
1863*4882a593Smuzhiyun 	return cmd_record(rec_argc, rec_argv);
1864*4882a593Smuzhiyun }
1865*4882a593Smuzhiyun 
1866*4882a593Smuzhiyun static int
parse_process(const struct option * opt __maybe_unused,const char * arg,int __maybe_unused unset)1867*4882a593Smuzhiyun parse_process(const struct option *opt __maybe_unused, const char *arg,
1868*4882a593Smuzhiyun 	      int __maybe_unused unset)
1869*4882a593Smuzhiyun {
1870*4882a593Smuzhiyun 	if (arg)
1871*4882a593Smuzhiyun 		add_process_filter(arg);
1872*4882a593Smuzhiyun 	return 0;
1873*4882a593Smuzhiyun }
1874*4882a593Smuzhiyun 
1875*4882a593Smuzhiyun static int
parse_highlight(const struct option * opt __maybe_unused,const char * arg,int __maybe_unused unset)1876*4882a593Smuzhiyun parse_highlight(const struct option *opt __maybe_unused, const char *arg,
1877*4882a593Smuzhiyun 		int __maybe_unused unset)
1878*4882a593Smuzhiyun {
1879*4882a593Smuzhiyun 	unsigned long duration = strtoul(arg, NULL, 0);
1880*4882a593Smuzhiyun 
1881*4882a593Smuzhiyun 	if (svg_highlight || svg_highlight_name)
1882*4882a593Smuzhiyun 		return -1;
1883*4882a593Smuzhiyun 
1884*4882a593Smuzhiyun 	if (duration)
1885*4882a593Smuzhiyun 		svg_highlight = duration;
1886*4882a593Smuzhiyun 	else
1887*4882a593Smuzhiyun 		svg_highlight_name = strdup(arg);
1888*4882a593Smuzhiyun 
1889*4882a593Smuzhiyun 	return 0;
1890*4882a593Smuzhiyun }
1891*4882a593Smuzhiyun 
1892*4882a593Smuzhiyun static int
parse_time(const struct option * opt,const char * arg,int __maybe_unused unset)1893*4882a593Smuzhiyun parse_time(const struct option *opt, const char *arg, int __maybe_unused unset)
1894*4882a593Smuzhiyun {
1895*4882a593Smuzhiyun 	char unit = 'n';
1896*4882a593Smuzhiyun 	u64 *value = opt->value;
1897*4882a593Smuzhiyun 
1898*4882a593Smuzhiyun 	if (sscanf(arg, "%" PRIu64 "%cs", value, &unit) > 0) {
1899*4882a593Smuzhiyun 		switch (unit) {
1900*4882a593Smuzhiyun 		case 'm':
1901*4882a593Smuzhiyun 			*value *= NSEC_PER_MSEC;
1902*4882a593Smuzhiyun 			break;
1903*4882a593Smuzhiyun 		case 'u':
1904*4882a593Smuzhiyun 			*value *= NSEC_PER_USEC;
1905*4882a593Smuzhiyun 			break;
1906*4882a593Smuzhiyun 		case 'n':
1907*4882a593Smuzhiyun 			break;
1908*4882a593Smuzhiyun 		default:
1909*4882a593Smuzhiyun 			return -1;
1910*4882a593Smuzhiyun 		}
1911*4882a593Smuzhiyun 	}
1912*4882a593Smuzhiyun 
1913*4882a593Smuzhiyun 	return 0;
1914*4882a593Smuzhiyun }
1915*4882a593Smuzhiyun 
cmd_timechart(int argc,const char ** argv)1916*4882a593Smuzhiyun int cmd_timechart(int argc, const char **argv)
1917*4882a593Smuzhiyun {
1918*4882a593Smuzhiyun 	struct timechart tchart = {
1919*4882a593Smuzhiyun 		.tool = {
1920*4882a593Smuzhiyun 			.comm		 = process_comm_event,
1921*4882a593Smuzhiyun 			.fork		 = process_fork_event,
1922*4882a593Smuzhiyun 			.exit		 = process_exit_event,
1923*4882a593Smuzhiyun 			.sample		 = process_sample_event,
1924*4882a593Smuzhiyun 			.ordered_events	 = true,
1925*4882a593Smuzhiyun 		},
1926*4882a593Smuzhiyun 		.proc_num = 15,
1927*4882a593Smuzhiyun 		.min_time = NSEC_PER_MSEC,
1928*4882a593Smuzhiyun 		.merge_dist = 1000,
1929*4882a593Smuzhiyun 	};
1930*4882a593Smuzhiyun 	const char *output_name = "output.svg";
1931*4882a593Smuzhiyun 	const struct option timechart_common_options[] = {
1932*4882a593Smuzhiyun 	OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"),
1933*4882a593Smuzhiyun 	OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, "output processes data only"),
1934*4882a593Smuzhiyun 	OPT_END()
1935*4882a593Smuzhiyun 	};
1936*4882a593Smuzhiyun 	const struct option timechart_options[] = {
1937*4882a593Smuzhiyun 	OPT_STRING('i', "input", &input_name, "file", "input file name"),
1938*4882a593Smuzhiyun 	OPT_STRING('o', "output", &output_name, "file", "output file name"),
1939*4882a593Smuzhiyun 	OPT_INTEGER('w', "width", &svg_page_width, "page width"),
1940*4882a593Smuzhiyun 	OPT_CALLBACK(0, "highlight", NULL, "duration or task name",
1941*4882a593Smuzhiyun 		      "highlight tasks. Pass duration in ns or process name.",
1942*4882a593Smuzhiyun 		       parse_highlight),
1943*4882a593Smuzhiyun 	OPT_CALLBACK('p', "process", NULL, "process",
1944*4882a593Smuzhiyun 		      "process selector. Pass a pid or process name.",
1945*4882a593Smuzhiyun 		       parse_process),
1946*4882a593Smuzhiyun 	OPT_CALLBACK(0, "symfs", NULL, "directory",
1947*4882a593Smuzhiyun 		     "Look for files with symbols relative to this directory",
1948*4882a593Smuzhiyun 		     symbol__config_symfs),
1949*4882a593Smuzhiyun 	OPT_INTEGER('n', "proc-num", &tchart.proc_num,
1950*4882a593Smuzhiyun 		    "min. number of tasks to print"),
1951*4882a593Smuzhiyun 	OPT_BOOLEAN('t', "topology", &tchart.topology,
1952*4882a593Smuzhiyun 		    "sort CPUs according to topology"),
1953*4882a593Smuzhiyun 	OPT_BOOLEAN(0, "io-skip-eagain", &tchart.skip_eagain,
1954*4882a593Smuzhiyun 		    "skip EAGAIN errors"),
1955*4882a593Smuzhiyun 	OPT_CALLBACK(0, "io-min-time", &tchart.min_time, "time",
1956*4882a593Smuzhiyun 		     "all IO faster than min-time will visually appear longer",
1957*4882a593Smuzhiyun 		     parse_time),
1958*4882a593Smuzhiyun 	OPT_CALLBACK(0, "io-merge-dist", &tchart.merge_dist, "time",
1959*4882a593Smuzhiyun 		     "merge events that are merge-dist us apart",
1960*4882a593Smuzhiyun 		     parse_time),
1961*4882a593Smuzhiyun 	OPT_BOOLEAN('f', "force", &tchart.force, "don't complain, do it"),
1962*4882a593Smuzhiyun 	OPT_PARENT(timechart_common_options),
1963*4882a593Smuzhiyun 	};
1964*4882a593Smuzhiyun 	const char * const timechart_subcommands[] = { "record", NULL };
1965*4882a593Smuzhiyun 	const char *timechart_usage[] = {
1966*4882a593Smuzhiyun 		"perf timechart [<options>] {record}",
1967*4882a593Smuzhiyun 		NULL
1968*4882a593Smuzhiyun 	};
1969*4882a593Smuzhiyun 	const struct option timechart_record_options[] = {
1970*4882a593Smuzhiyun 	OPT_BOOLEAN('I', "io-only", &tchart.io_only,
1971*4882a593Smuzhiyun 		    "record only IO data"),
1972*4882a593Smuzhiyun 	OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"),
1973*4882a593Smuzhiyun 	OPT_PARENT(timechart_common_options),
1974*4882a593Smuzhiyun 	};
1975*4882a593Smuzhiyun 	const char * const timechart_record_usage[] = {
1976*4882a593Smuzhiyun 		"perf timechart record [<options>]",
1977*4882a593Smuzhiyun 		NULL
1978*4882a593Smuzhiyun 	};
1979*4882a593Smuzhiyun 	argc = parse_options_subcommand(argc, argv, timechart_options, timechart_subcommands,
1980*4882a593Smuzhiyun 			timechart_usage, PARSE_OPT_STOP_AT_NON_OPTION);
1981*4882a593Smuzhiyun 
1982*4882a593Smuzhiyun 	if (tchart.power_only && tchart.tasks_only) {
1983*4882a593Smuzhiyun 		pr_err("-P and -T options cannot be used at the same time.\n");
1984*4882a593Smuzhiyun 		return -1;
1985*4882a593Smuzhiyun 	}
1986*4882a593Smuzhiyun 
1987*4882a593Smuzhiyun 	if (argc && !strncmp(argv[0], "rec", 3)) {
1988*4882a593Smuzhiyun 		argc = parse_options(argc, argv, timechart_record_options,
1989*4882a593Smuzhiyun 				     timechart_record_usage,
1990*4882a593Smuzhiyun 				     PARSE_OPT_STOP_AT_NON_OPTION);
1991*4882a593Smuzhiyun 
1992*4882a593Smuzhiyun 		if (tchart.power_only && tchart.tasks_only) {
1993*4882a593Smuzhiyun 			pr_err("-P and -T options cannot be used at the same time.\n");
1994*4882a593Smuzhiyun 			return -1;
1995*4882a593Smuzhiyun 		}
1996*4882a593Smuzhiyun 
1997*4882a593Smuzhiyun 		if (tchart.io_only)
1998*4882a593Smuzhiyun 			return timechart__io_record(argc, argv);
1999*4882a593Smuzhiyun 		else
2000*4882a593Smuzhiyun 			return timechart__record(&tchart, argc, argv);
2001*4882a593Smuzhiyun 	} else if (argc)
2002*4882a593Smuzhiyun 		usage_with_options(timechart_usage, timechart_options);
2003*4882a593Smuzhiyun 
2004*4882a593Smuzhiyun 	setup_pager();
2005*4882a593Smuzhiyun 
2006*4882a593Smuzhiyun 	return __cmd_timechart(&tchart, output_name);
2007*4882a593Smuzhiyun }
2008