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