xref: /openbmc/linux/tools/perf/builtin-timechart.c (revision 6c870213d6f3a25981c10728f46294a3bed1703f)
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/rbtree.h>
27 #include "util/symbol.h"
28 #include "util/callchain.h"
29 #include "util/strlist.h"
30 
31 #include "perf.h"
32 #include "util/header.h"
33 #include "util/parse-options.h"
34 #include "util/parse-events.h"
35 #include "util/event.h"
36 #include "util/session.h"
37 #include "util/svghelper.h"
38 #include "util/tool.h"
39 #include "util/data.h"
40 
41 #define SUPPORT_OLD_POWER_EVENTS 1
42 #define PWR_EVENT_EXIT -1
43 
44 struct per_pid;
45 struct power_event;
46 struct wake_event;
47 
48 struct timechart {
49 	struct perf_tool	tool;
50 	struct per_pid		*all_data;
51 	struct power_event	*power_events;
52 	struct wake_event	*wake_events;
53 	int			proc_num;
54 	unsigned int		numcpus;
55 	u64			min_freq,	/* Lowest CPU frequency seen */
56 				max_freq,	/* Highest CPU frequency seen */
57 				turbo_frequency,
58 				first_time, last_time;
59 	bool			power_only,
60 				tasks_only,
61 				with_backtrace,
62 				topology;
63 };
64 
65 struct per_pidcomm;
66 struct cpu_sample;
67 
68 /*
69  * Datastructure layout:
70  * We keep an list of "pid"s, matching the kernels notion of a task struct.
71  * Each "pid" entry, has a list of "comm"s.
72  *	this is because we want to track different programs different, while
73  *	exec will reuse the original pid (by design).
74  * Each comm has a list of samples that will be used to draw
75  * final graph.
76  */
77 
78 struct per_pid {
79 	struct per_pid *next;
80 
81 	int		pid;
82 	int		ppid;
83 
84 	u64		start_time;
85 	u64		end_time;
86 	u64		total_time;
87 	int		display;
88 
89 	struct per_pidcomm *all;
90 	struct per_pidcomm *current;
91 };
92 
93 
94 struct per_pidcomm {
95 	struct per_pidcomm *next;
96 
97 	u64		start_time;
98 	u64		end_time;
99 	u64		total_time;
100 
101 	int		Y;
102 	int		display;
103 
104 	long		state;
105 	u64		state_since;
106 
107 	char		*comm;
108 
109 	struct cpu_sample *samples;
110 };
111 
112 struct sample_wrapper {
113 	struct sample_wrapper *next;
114 
115 	u64		timestamp;
116 	unsigned char	data[0];
117 };
118 
119 #define TYPE_NONE	0
120 #define TYPE_RUNNING	1
121 #define TYPE_WAITING	2
122 #define TYPE_BLOCKED	3
123 
124 struct cpu_sample {
125 	struct cpu_sample *next;
126 
127 	u64 start_time;
128 	u64 end_time;
129 	int type;
130 	int cpu;
131 	const char *backtrace;
132 };
133 
134 #define CSTATE 1
135 #define PSTATE 2
136 
137 struct power_event {
138 	struct power_event *next;
139 	int type;
140 	int state;
141 	u64 start_time;
142 	u64 end_time;
143 	int cpu;
144 };
145 
146 struct wake_event {
147 	struct wake_event *next;
148 	int waker;
149 	int wakee;
150 	u64 time;
151 	const char *backtrace;
152 };
153 
154 struct process_filter {
155 	char			*name;
156 	int			pid;
157 	struct process_filter	*next;
158 };
159 
160 static struct process_filter *process_filter;
161 
162 
163 static struct per_pid *find_create_pid(struct timechart *tchart, int pid)
164 {
165 	struct per_pid *cursor = tchart->all_data;
166 
167 	while (cursor) {
168 		if (cursor->pid == pid)
169 			return cursor;
170 		cursor = cursor->next;
171 	}
172 	cursor = zalloc(sizeof(*cursor));
173 	assert(cursor != NULL);
174 	cursor->pid = pid;
175 	cursor->next = tchart->all_data;
176 	tchart->all_data = cursor;
177 	return cursor;
178 }
179 
180 static void pid_set_comm(struct timechart *tchart, int pid, char *comm)
181 {
182 	struct per_pid *p;
183 	struct per_pidcomm *c;
184 	p = find_create_pid(tchart, pid);
185 	c = p->all;
186 	while (c) {
187 		if (c->comm && strcmp(c->comm, comm) == 0) {
188 			p->current = c;
189 			return;
190 		}
191 		if (!c->comm) {
192 			c->comm = strdup(comm);
193 			p->current = c;
194 			return;
195 		}
196 		c = c->next;
197 	}
198 	c = zalloc(sizeof(*c));
199 	assert(c != NULL);
200 	c->comm = strdup(comm);
201 	p->current = c;
202 	c->next = p->all;
203 	p->all = c;
204 }
205 
206 static void pid_fork(struct timechart *tchart, int pid, int ppid, u64 timestamp)
207 {
208 	struct per_pid *p, *pp;
209 	p = find_create_pid(tchart, pid);
210 	pp = find_create_pid(tchart, ppid);
211 	p->ppid = ppid;
212 	if (pp->current && pp->current->comm && !p->current)
213 		pid_set_comm(tchart, pid, pp->current->comm);
214 
215 	p->start_time = timestamp;
216 	if (p->current) {
217 		p->current->start_time = timestamp;
218 		p->current->state_since = timestamp;
219 	}
220 }
221 
222 static void pid_exit(struct timechart *tchart, int pid, u64 timestamp)
223 {
224 	struct per_pid *p;
225 	p = find_create_pid(tchart, pid);
226 	p->end_time = timestamp;
227 	if (p->current)
228 		p->current->end_time = timestamp;
229 }
230 
231 static void pid_put_sample(struct timechart *tchart, int pid, int type,
232 			   unsigned int cpu, u64 start, u64 end,
233 			   const char *backtrace)
234 {
235 	struct per_pid *p;
236 	struct per_pidcomm *c;
237 	struct cpu_sample *sample;
238 
239 	p = find_create_pid(tchart, pid);
240 	c = p->current;
241 	if (!c) {
242 		c = zalloc(sizeof(*c));
243 		assert(c != NULL);
244 		p->current = c;
245 		c->next = p->all;
246 		p->all = c;
247 	}
248 
249 	sample = zalloc(sizeof(*sample));
250 	assert(sample != NULL);
251 	sample->start_time = start;
252 	sample->end_time = end;
253 	sample->type = type;
254 	sample->next = c->samples;
255 	sample->cpu = cpu;
256 	sample->backtrace = backtrace;
257 	c->samples = sample;
258 
259 	if (sample->type == TYPE_RUNNING && end > start && start > 0) {
260 		c->total_time += (end-start);
261 		p->total_time += (end-start);
262 	}
263 
264 	if (c->start_time == 0 || c->start_time > start)
265 		c->start_time = start;
266 	if (p->start_time == 0 || p->start_time > start)
267 		p->start_time = start;
268 }
269 
270 #define MAX_CPUS 4096
271 
272 static u64 cpus_cstate_start_times[MAX_CPUS];
273 static int cpus_cstate_state[MAX_CPUS];
274 static u64 cpus_pstate_start_times[MAX_CPUS];
275 static u64 cpus_pstate_state[MAX_CPUS];
276 
277 static int process_comm_event(struct perf_tool *tool,
278 			      union perf_event *event,
279 			      struct perf_sample *sample __maybe_unused,
280 			      struct machine *machine __maybe_unused)
281 {
282 	struct timechart *tchart = container_of(tool, struct timechart, tool);
283 	pid_set_comm(tchart, event->comm.tid, event->comm.comm);
284 	return 0;
285 }
286 
287 static int process_fork_event(struct perf_tool *tool,
288 			      union perf_event *event,
289 			      struct perf_sample *sample __maybe_unused,
290 			      struct machine *machine __maybe_unused)
291 {
292 	struct timechart *tchart = container_of(tool, struct timechart, tool);
293 	pid_fork(tchart, event->fork.pid, event->fork.ppid, event->fork.time);
294 	return 0;
295 }
296 
297 static int process_exit_event(struct perf_tool *tool,
298 			      union perf_event *event,
299 			      struct perf_sample *sample __maybe_unused,
300 			      struct machine *machine __maybe_unused)
301 {
302 	struct timechart *tchart = container_of(tool, struct timechart, tool);
303 	pid_exit(tchart, event->fork.pid, event->fork.time);
304 	return 0;
305 }
306 
307 #ifdef SUPPORT_OLD_POWER_EVENTS
308 static int use_old_power_events;
309 #endif
310 
311 static void c_state_start(int cpu, u64 timestamp, int state)
312 {
313 	cpus_cstate_start_times[cpu] = timestamp;
314 	cpus_cstate_state[cpu] = state;
315 }
316 
317 static void c_state_end(struct timechart *tchart, int cpu, u64 timestamp)
318 {
319 	struct power_event *pwr = zalloc(sizeof(*pwr));
320 
321 	if (!pwr)
322 		return;
323 
324 	pwr->state = cpus_cstate_state[cpu];
325 	pwr->start_time = cpus_cstate_start_times[cpu];
326 	pwr->end_time = timestamp;
327 	pwr->cpu = cpu;
328 	pwr->type = CSTATE;
329 	pwr->next = tchart->power_events;
330 
331 	tchart->power_events = pwr;
332 }
333 
334 static void p_state_change(struct timechart *tchart, int cpu, u64 timestamp, u64 new_freq)
335 {
336 	struct power_event *pwr;
337 
338 	if (new_freq > 8000000) /* detect invalid data */
339 		return;
340 
341 	pwr = zalloc(sizeof(*pwr));
342 	if (!pwr)
343 		return;
344 
345 	pwr->state = cpus_pstate_state[cpu];
346 	pwr->start_time = cpus_pstate_start_times[cpu];
347 	pwr->end_time = timestamp;
348 	pwr->cpu = cpu;
349 	pwr->type = PSTATE;
350 	pwr->next = tchart->power_events;
351 
352 	if (!pwr->start_time)
353 		pwr->start_time = tchart->first_time;
354 
355 	tchart->power_events = pwr;
356 
357 	cpus_pstate_state[cpu] = new_freq;
358 	cpus_pstate_start_times[cpu] = timestamp;
359 
360 	if ((u64)new_freq > tchart->max_freq)
361 		tchart->max_freq = new_freq;
362 
363 	if (new_freq < tchart->min_freq || tchart->min_freq == 0)
364 		tchart->min_freq = new_freq;
365 
366 	if (new_freq == tchart->max_freq - 1000)
367 		tchart->turbo_frequency = tchart->max_freq;
368 }
369 
370 static void sched_wakeup(struct timechart *tchart, int cpu, u64 timestamp,
371 			 int waker, int wakee, u8 flags, const char *backtrace)
372 {
373 	struct per_pid *p;
374 	struct wake_event *we = zalloc(sizeof(*we));
375 
376 	if (!we)
377 		return;
378 
379 	we->time = timestamp;
380 	we->waker = waker;
381 	we->backtrace = backtrace;
382 
383 	if ((flags & TRACE_FLAG_HARDIRQ) || (flags & TRACE_FLAG_SOFTIRQ))
384 		we->waker = -1;
385 
386 	we->wakee = wakee;
387 	we->next = tchart->wake_events;
388 	tchart->wake_events = we;
389 	p = find_create_pid(tchart, we->wakee);
390 
391 	if (p && p->current && p->current->state == TYPE_NONE) {
392 		p->current->state_since = timestamp;
393 		p->current->state = TYPE_WAITING;
394 	}
395 	if (p && p->current && p->current->state == TYPE_BLOCKED) {
396 		pid_put_sample(tchart, p->pid, p->current->state, cpu,
397 			       p->current->state_since, timestamp, NULL);
398 		p->current->state_since = timestamp;
399 		p->current->state = TYPE_WAITING;
400 	}
401 }
402 
403 static void sched_switch(struct timechart *tchart, int cpu, u64 timestamp,
404 			 int prev_pid, int next_pid, u64 prev_state,
405 			 const char *backtrace)
406 {
407 	struct per_pid *p = NULL, *prev_p;
408 
409 	prev_p = find_create_pid(tchart, prev_pid);
410 
411 	p = find_create_pid(tchart, next_pid);
412 
413 	if (prev_p->current && prev_p->current->state != TYPE_NONE)
414 		pid_put_sample(tchart, prev_pid, TYPE_RUNNING, cpu,
415 			       prev_p->current->state_since, timestamp,
416 			       backtrace);
417 	if (p && p->current) {
418 		if (p->current->state != TYPE_NONE)
419 			pid_put_sample(tchart, next_pid, p->current->state, cpu,
420 				       p->current->state_since, timestamp,
421 				       backtrace);
422 
423 		p->current->state_since = timestamp;
424 		p->current->state = TYPE_RUNNING;
425 	}
426 
427 	if (prev_p->current) {
428 		prev_p->current->state = TYPE_NONE;
429 		prev_p->current->state_since = timestamp;
430 		if (prev_state & 2)
431 			prev_p->current->state = TYPE_BLOCKED;
432 		if (prev_state == 0)
433 			prev_p->current->state = TYPE_WAITING;
434 	}
435 }
436 
437 static const char *cat_backtrace(union perf_event *event,
438 				 struct perf_sample *sample,
439 				 struct machine *machine)
440 {
441 	struct addr_location al;
442 	unsigned int i;
443 	char *p = NULL;
444 	size_t p_len;
445 	u8 cpumode = PERF_RECORD_MISC_USER;
446 	struct addr_location tal;
447 	struct ip_callchain *chain = sample->callchain;
448 	FILE *f = open_memstream(&p, &p_len);
449 
450 	if (!f) {
451 		perror("open_memstream error");
452 		return NULL;
453 	}
454 
455 	if (!chain)
456 		goto exit;
457 
458 	if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
459 		fprintf(stderr, "problem processing %d event, skipping it.\n",
460 			event->header.type);
461 		goto exit;
462 	}
463 
464 	for (i = 0; i < chain->nr; i++) {
465 		u64 ip;
466 
467 		if (callchain_param.order == ORDER_CALLEE)
468 			ip = chain->ips[i];
469 		else
470 			ip = chain->ips[chain->nr - i - 1];
471 
472 		if (ip >= PERF_CONTEXT_MAX) {
473 			switch (ip) {
474 			case PERF_CONTEXT_HV:
475 				cpumode = PERF_RECORD_MISC_HYPERVISOR;
476 				break;
477 			case PERF_CONTEXT_KERNEL:
478 				cpumode = PERF_RECORD_MISC_KERNEL;
479 				break;
480 			case PERF_CONTEXT_USER:
481 				cpumode = PERF_RECORD_MISC_USER;
482 				break;
483 			default:
484 				pr_debug("invalid callchain context: "
485 					 "%"PRId64"\n", (s64) ip);
486 
487 				/*
488 				 * It seems the callchain is corrupted.
489 				 * Discard all.
490 				 */
491 				zfree(&p);
492 				goto exit;
493 			}
494 			continue;
495 		}
496 
497 		tal.filtered = 0;
498 		thread__find_addr_location(al.thread, machine, cpumode,
499 					   MAP__FUNCTION, ip, &tal);
500 
501 		if (tal.sym)
502 			fprintf(f, "..... %016" PRIx64 " %s\n", ip,
503 				tal.sym->name);
504 		else
505 			fprintf(f, "..... %016" PRIx64 "\n", ip);
506 	}
507 
508 exit:
509 	fclose(f);
510 
511 	return p;
512 }
513 
514 typedef int (*tracepoint_handler)(struct timechart *tchart,
515 				  struct perf_evsel *evsel,
516 				  struct perf_sample *sample,
517 				  const char *backtrace);
518 
519 static int process_sample_event(struct perf_tool *tool,
520 				union perf_event *event,
521 				struct perf_sample *sample,
522 				struct perf_evsel *evsel,
523 				struct machine *machine)
524 {
525 	struct timechart *tchart = container_of(tool, struct timechart, tool);
526 
527 	if (evsel->attr.sample_type & PERF_SAMPLE_TIME) {
528 		if (!tchart->first_time || tchart->first_time > sample->time)
529 			tchart->first_time = sample->time;
530 		if (tchart->last_time < sample->time)
531 			tchart->last_time = sample->time;
532 	}
533 
534 	if (evsel->handler != NULL) {
535 		tracepoint_handler f = evsel->handler;
536 		return f(tchart, evsel, sample,
537 			 cat_backtrace(event, sample, machine));
538 	}
539 
540 	return 0;
541 }
542 
543 static int
544 process_sample_cpu_idle(struct timechart *tchart __maybe_unused,
545 			struct perf_evsel *evsel,
546 			struct perf_sample *sample,
547 			const char *backtrace __maybe_unused)
548 {
549 	u32 state = perf_evsel__intval(evsel, sample, "state");
550 	u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
551 
552 	if (state == (u32)PWR_EVENT_EXIT)
553 		c_state_end(tchart, cpu_id, sample->time);
554 	else
555 		c_state_start(cpu_id, sample->time, state);
556 	return 0;
557 }
558 
559 static int
560 process_sample_cpu_frequency(struct timechart *tchart,
561 			     struct perf_evsel *evsel,
562 			     struct perf_sample *sample,
563 			     const char *backtrace __maybe_unused)
564 {
565 	u32 state = perf_evsel__intval(evsel, sample, "state");
566 	u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
567 
568 	p_state_change(tchart, cpu_id, sample->time, state);
569 	return 0;
570 }
571 
572 static int
573 process_sample_sched_wakeup(struct timechart *tchart,
574 			    struct perf_evsel *evsel,
575 			    struct perf_sample *sample,
576 			    const char *backtrace)
577 {
578 	u8 flags = perf_evsel__intval(evsel, sample, "common_flags");
579 	int waker = perf_evsel__intval(evsel, sample, "common_pid");
580 	int wakee = perf_evsel__intval(evsel, sample, "pid");
581 
582 	sched_wakeup(tchart, sample->cpu, sample->time, waker, wakee, flags, backtrace);
583 	return 0;
584 }
585 
586 static int
587 process_sample_sched_switch(struct timechart *tchart,
588 			    struct perf_evsel *evsel,
589 			    struct perf_sample *sample,
590 			    const char *backtrace)
591 {
592 	int prev_pid = perf_evsel__intval(evsel, sample, "prev_pid");
593 	int next_pid = perf_evsel__intval(evsel, sample, "next_pid");
594 	u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state");
595 
596 	sched_switch(tchart, sample->cpu, sample->time, prev_pid, next_pid,
597 		     prev_state, backtrace);
598 	return 0;
599 }
600 
601 #ifdef SUPPORT_OLD_POWER_EVENTS
602 static int
603 process_sample_power_start(struct timechart *tchart __maybe_unused,
604 			   struct perf_evsel *evsel,
605 			   struct perf_sample *sample,
606 			   const char *backtrace __maybe_unused)
607 {
608 	u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
609 	u64 value = perf_evsel__intval(evsel, sample, "value");
610 
611 	c_state_start(cpu_id, sample->time, value);
612 	return 0;
613 }
614 
615 static int
616 process_sample_power_end(struct timechart *tchart,
617 			 struct perf_evsel *evsel __maybe_unused,
618 			 struct perf_sample *sample,
619 			 const char *backtrace __maybe_unused)
620 {
621 	c_state_end(tchart, sample->cpu, sample->time);
622 	return 0;
623 }
624 
625 static int
626 process_sample_power_frequency(struct timechart *tchart,
627 			       struct perf_evsel *evsel,
628 			       struct perf_sample *sample,
629 			       const char *backtrace __maybe_unused)
630 {
631 	u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
632 	u64 value = perf_evsel__intval(evsel, sample, "value");
633 
634 	p_state_change(tchart, cpu_id, sample->time, value);
635 	return 0;
636 }
637 #endif /* SUPPORT_OLD_POWER_EVENTS */
638 
639 /*
640  * After the last sample we need to wrap up the current C/P state
641  * and close out each CPU for these.
642  */
643 static void end_sample_processing(struct timechart *tchart)
644 {
645 	u64 cpu;
646 	struct power_event *pwr;
647 
648 	for (cpu = 0; cpu <= tchart->numcpus; cpu++) {
649 		/* C state */
650 #if 0
651 		pwr = zalloc(sizeof(*pwr));
652 		if (!pwr)
653 			return;
654 
655 		pwr->state = cpus_cstate_state[cpu];
656 		pwr->start_time = cpus_cstate_start_times[cpu];
657 		pwr->end_time = tchart->last_time;
658 		pwr->cpu = cpu;
659 		pwr->type = CSTATE;
660 		pwr->next = tchart->power_events;
661 
662 		tchart->power_events = pwr;
663 #endif
664 		/* P state */
665 
666 		pwr = zalloc(sizeof(*pwr));
667 		if (!pwr)
668 			return;
669 
670 		pwr->state = cpus_pstate_state[cpu];
671 		pwr->start_time = cpus_pstate_start_times[cpu];
672 		pwr->end_time = tchart->last_time;
673 		pwr->cpu = cpu;
674 		pwr->type = PSTATE;
675 		pwr->next = tchart->power_events;
676 
677 		if (!pwr->start_time)
678 			pwr->start_time = tchart->first_time;
679 		if (!pwr->state)
680 			pwr->state = tchart->min_freq;
681 		tchart->power_events = pwr;
682 	}
683 }
684 
685 /*
686  * Sort the pid datastructure
687  */
688 static void sort_pids(struct timechart *tchart)
689 {
690 	struct per_pid *new_list, *p, *cursor, *prev;
691 	/* sort by ppid first, then by pid, lowest to highest */
692 
693 	new_list = NULL;
694 
695 	while (tchart->all_data) {
696 		p = tchart->all_data;
697 		tchart->all_data = p->next;
698 		p->next = NULL;
699 
700 		if (new_list == NULL) {
701 			new_list = p;
702 			p->next = NULL;
703 			continue;
704 		}
705 		prev = NULL;
706 		cursor = new_list;
707 		while (cursor) {
708 			if (cursor->ppid > p->ppid ||
709 				(cursor->ppid == p->ppid && cursor->pid > p->pid)) {
710 				/* must insert before */
711 				if (prev) {
712 					p->next = prev->next;
713 					prev->next = p;
714 					cursor = NULL;
715 					continue;
716 				} else {
717 					p->next = new_list;
718 					new_list = p;
719 					cursor = NULL;
720 					continue;
721 				}
722 			}
723 
724 			prev = cursor;
725 			cursor = cursor->next;
726 			if (!cursor)
727 				prev->next = p;
728 		}
729 	}
730 	tchart->all_data = new_list;
731 }
732 
733 
734 static void draw_c_p_states(struct timechart *tchart)
735 {
736 	struct power_event *pwr;
737 	pwr = tchart->power_events;
738 
739 	/*
740 	 * two pass drawing so that the P state bars are on top of the C state blocks
741 	 */
742 	while (pwr) {
743 		if (pwr->type == CSTATE)
744 			svg_cstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
745 		pwr = pwr->next;
746 	}
747 
748 	pwr = tchart->power_events;
749 	while (pwr) {
750 		if (pwr->type == PSTATE) {
751 			if (!pwr->state)
752 				pwr->state = tchart->min_freq;
753 			svg_pstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
754 		}
755 		pwr = pwr->next;
756 	}
757 }
758 
759 static void draw_wakeups(struct timechart *tchart)
760 {
761 	struct wake_event *we;
762 	struct per_pid *p;
763 	struct per_pidcomm *c;
764 
765 	we = tchart->wake_events;
766 	while (we) {
767 		int from = 0, to = 0;
768 		char *task_from = NULL, *task_to = NULL;
769 
770 		/* locate the column of the waker and wakee */
771 		p = tchart->all_data;
772 		while (p) {
773 			if (p->pid == we->waker || p->pid == we->wakee) {
774 				c = p->all;
775 				while (c) {
776 					if (c->Y && c->start_time <= we->time && c->end_time >= we->time) {
777 						if (p->pid == we->waker && !from) {
778 							from = c->Y;
779 							task_from = strdup(c->comm);
780 						}
781 						if (p->pid == we->wakee && !to) {
782 							to = c->Y;
783 							task_to = strdup(c->comm);
784 						}
785 					}
786 					c = c->next;
787 				}
788 				c = p->all;
789 				while (c) {
790 					if (p->pid == we->waker && !from) {
791 						from = c->Y;
792 						task_from = strdup(c->comm);
793 					}
794 					if (p->pid == we->wakee && !to) {
795 						to = c->Y;
796 						task_to = strdup(c->comm);
797 					}
798 					c = c->next;
799 				}
800 			}
801 			p = p->next;
802 		}
803 
804 		if (!task_from) {
805 			task_from = malloc(40);
806 			sprintf(task_from, "[%i]", we->waker);
807 		}
808 		if (!task_to) {
809 			task_to = malloc(40);
810 			sprintf(task_to, "[%i]", we->wakee);
811 		}
812 
813 		if (we->waker == -1)
814 			svg_interrupt(we->time, to, we->backtrace);
815 		else if (from && to && abs(from - to) == 1)
816 			svg_wakeline(we->time, from, to, we->backtrace);
817 		else
818 			svg_partial_wakeline(we->time, from, task_from, to,
819 					     task_to, we->backtrace);
820 		we = we->next;
821 
822 		free(task_from);
823 		free(task_to);
824 	}
825 }
826 
827 static void draw_cpu_usage(struct timechart *tchart)
828 {
829 	struct per_pid *p;
830 	struct per_pidcomm *c;
831 	struct cpu_sample *sample;
832 	p = tchart->all_data;
833 	while (p) {
834 		c = p->all;
835 		while (c) {
836 			sample = c->samples;
837 			while (sample) {
838 				if (sample->type == TYPE_RUNNING) {
839 					svg_process(sample->cpu,
840 						    sample->start_time,
841 						    sample->end_time,
842 						    p->pid,
843 						    c->comm,
844 						    sample->backtrace);
845 				}
846 
847 				sample = sample->next;
848 			}
849 			c = c->next;
850 		}
851 		p = p->next;
852 	}
853 }
854 
855 static void draw_process_bars(struct timechart *tchart)
856 {
857 	struct per_pid *p;
858 	struct per_pidcomm *c;
859 	struct cpu_sample *sample;
860 	int Y = 0;
861 
862 	Y = 2 * tchart->numcpus + 2;
863 
864 	p = tchart->all_data;
865 	while (p) {
866 		c = p->all;
867 		while (c) {
868 			if (!c->display) {
869 				c->Y = 0;
870 				c = c->next;
871 				continue;
872 			}
873 
874 			svg_box(Y, c->start_time, c->end_time, "process");
875 			sample = c->samples;
876 			while (sample) {
877 				if (sample->type == TYPE_RUNNING)
878 					svg_running(Y, sample->cpu,
879 						    sample->start_time,
880 						    sample->end_time,
881 						    sample->backtrace);
882 				if (sample->type == TYPE_BLOCKED)
883 					svg_blocked(Y, sample->cpu,
884 						    sample->start_time,
885 						    sample->end_time,
886 						    sample->backtrace);
887 				if (sample->type == TYPE_WAITING)
888 					svg_waiting(Y, sample->cpu,
889 						    sample->start_time,
890 						    sample->end_time,
891 						    sample->backtrace);
892 				sample = sample->next;
893 			}
894 
895 			if (c->comm) {
896 				char comm[256];
897 				if (c->total_time > 5000000000) /* 5 seconds */
898 					sprintf(comm, "%s:%i (%2.2fs)", c->comm, p->pid, c->total_time / 1000000000.0);
899 				else
900 					sprintf(comm, "%s:%i (%3.1fms)", c->comm, p->pid, c->total_time / 1000000.0);
901 
902 				svg_text(Y, c->start_time, comm);
903 			}
904 			c->Y = Y;
905 			Y++;
906 			c = c->next;
907 		}
908 		p = p->next;
909 	}
910 }
911 
912 static void add_process_filter(const char *string)
913 {
914 	int pid = strtoull(string, NULL, 10);
915 	struct process_filter *filt = malloc(sizeof(*filt));
916 
917 	if (!filt)
918 		return;
919 
920 	filt->name = strdup(string);
921 	filt->pid  = pid;
922 	filt->next = process_filter;
923 
924 	process_filter = filt;
925 }
926 
927 static int passes_filter(struct per_pid *p, struct per_pidcomm *c)
928 {
929 	struct process_filter *filt;
930 	if (!process_filter)
931 		return 1;
932 
933 	filt = process_filter;
934 	while (filt) {
935 		if (filt->pid && p->pid == filt->pid)
936 			return 1;
937 		if (strcmp(filt->name, c->comm) == 0)
938 			return 1;
939 		filt = filt->next;
940 	}
941 	return 0;
942 }
943 
944 static int determine_display_tasks_filtered(struct timechart *tchart)
945 {
946 	struct per_pid *p;
947 	struct per_pidcomm *c;
948 	int count = 0;
949 
950 	p = tchart->all_data;
951 	while (p) {
952 		p->display = 0;
953 		if (p->start_time == 1)
954 			p->start_time = tchart->first_time;
955 
956 		/* no exit marker, task kept running to the end */
957 		if (p->end_time == 0)
958 			p->end_time = tchart->last_time;
959 
960 		c = p->all;
961 
962 		while (c) {
963 			c->display = 0;
964 
965 			if (c->start_time == 1)
966 				c->start_time = tchart->first_time;
967 
968 			if (passes_filter(p, c)) {
969 				c->display = 1;
970 				p->display = 1;
971 				count++;
972 			}
973 
974 			if (c->end_time == 0)
975 				c->end_time = tchart->last_time;
976 
977 			c = c->next;
978 		}
979 		p = p->next;
980 	}
981 	return count;
982 }
983 
984 static int determine_display_tasks(struct timechart *tchart, u64 threshold)
985 {
986 	struct per_pid *p;
987 	struct per_pidcomm *c;
988 	int count = 0;
989 
990 	if (process_filter)
991 		return determine_display_tasks_filtered(tchart);
992 
993 	p = tchart->all_data;
994 	while (p) {
995 		p->display = 0;
996 		if (p->start_time == 1)
997 			p->start_time = tchart->first_time;
998 
999 		/* no exit marker, task kept running to the end */
1000 		if (p->end_time == 0)
1001 			p->end_time = tchart->last_time;
1002 		if (p->total_time >= threshold)
1003 			p->display = 1;
1004 
1005 		c = p->all;
1006 
1007 		while (c) {
1008 			c->display = 0;
1009 
1010 			if (c->start_time == 1)
1011 				c->start_time = tchart->first_time;
1012 
1013 			if (c->total_time >= threshold) {
1014 				c->display = 1;
1015 				count++;
1016 			}
1017 
1018 			if (c->end_time == 0)
1019 				c->end_time = tchart->last_time;
1020 
1021 			c = c->next;
1022 		}
1023 		p = p->next;
1024 	}
1025 	return count;
1026 }
1027 
1028 
1029 
1030 #define TIME_THRESH 10000000
1031 
1032 static void write_svg_file(struct timechart *tchart, const char *filename)
1033 {
1034 	u64 i;
1035 	int count;
1036 	int thresh = TIME_THRESH;
1037 
1038 	if (tchart->power_only)
1039 		tchart->proc_num = 0;
1040 
1041 	/* We'd like to show at least proc_num tasks;
1042 	 * be less picky if we have fewer */
1043 	do {
1044 		count = determine_display_tasks(tchart, thresh);
1045 		thresh /= 10;
1046 	} while (!process_filter && thresh && count < tchart->proc_num);
1047 
1048 	if (!tchart->proc_num)
1049 		count = 0;
1050 
1051 	open_svg(filename, tchart->numcpus, count, tchart->first_time, tchart->last_time);
1052 
1053 	svg_time_grid();
1054 	svg_legenda();
1055 
1056 	for (i = 0; i < tchart->numcpus; i++)
1057 		svg_cpu_box(i, tchart->max_freq, tchart->turbo_frequency);
1058 
1059 	draw_cpu_usage(tchart);
1060 	if (tchart->proc_num)
1061 		draw_process_bars(tchart);
1062 	if (!tchart->tasks_only)
1063 		draw_c_p_states(tchart);
1064 	if (tchart->proc_num)
1065 		draw_wakeups(tchart);
1066 
1067 	svg_close();
1068 }
1069 
1070 static int process_header(struct perf_file_section *section __maybe_unused,
1071 			  struct perf_header *ph,
1072 			  int feat,
1073 			  int fd __maybe_unused,
1074 			  void *data)
1075 {
1076 	struct timechart *tchart = data;
1077 
1078 	switch (feat) {
1079 	case HEADER_NRCPUS:
1080 		tchart->numcpus = ph->env.nr_cpus_avail;
1081 		break;
1082 
1083 	case HEADER_CPU_TOPOLOGY:
1084 		if (!tchart->topology)
1085 			break;
1086 
1087 		if (svg_build_topology_map(ph->env.sibling_cores,
1088 					   ph->env.nr_sibling_cores,
1089 					   ph->env.sibling_threads,
1090 					   ph->env.nr_sibling_threads))
1091 			fprintf(stderr, "problem building topology\n");
1092 		break;
1093 
1094 	default:
1095 		break;
1096 	}
1097 
1098 	return 0;
1099 }
1100 
1101 static int __cmd_timechart(struct timechart *tchart, const char *output_name)
1102 {
1103 	const struct perf_evsel_str_handler power_tracepoints[] = {
1104 		{ "power:cpu_idle",		process_sample_cpu_idle },
1105 		{ "power:cpu_frequency",	process_sample_cpu_frequency },
1106 		{ "sched:sched_wakeup",		process_sample_sched_wakeup },
1107 		{ "sched:sched_switch",		process_sample_sched_switch },
1108 #ifdef SUPPORT_OLD_POWER_EVENTS
1109 		{ "power:power_start",		process_sample_power_start },
1110 		{ "power:power_end",		process_sample_power_end },
1111 		{ "power:power_frequency",	process_sample_power_frequency },
1112 #endif
1113 	};
1114 	struct perf_data_file file = {
1115 		.path = input_name,
1116 		.mode = PERF_DATA_MODE_READ,
1117 	};
1118 
1119 	struct perf_session *session = perf_session__new(&file, false,
1120 							 &tchart->tool);
1121 	int ret = -EINVAL;
1122 
1123 	if (session == NULL)
1124 		return -ENOMEM;
1125 
1126 	(void)perf_header__process_sections(&session->header,
1127 					    perf_data_file__fd(session->file),
1128 					    tchart,
1129 					    process_header);
1130 
1131 	if (!perf_session__has_traces(session, "timechart record"))
1132 		goto out_delete;
1133 
1134 	if (perf_session__set_tracepoints_handlers(session,
1135 						   power_tracepoints)) {
1136 		pr_err("Initializing session tracepoint handlers failed\n");
1137 		goto out_delete;
1138 	}
1139 
1140 	ret = perf_session__process_events(session, &tchart->tool);
1141 	if (ret)
1142 		goto out_delete;
1143 
1144 	end_sample_processing(tchart);
1145 
1146 	sort_pids(tchart);
1147 
1148 	write_svg_file(tchart, output_name);
1149 
1150 	pr_info("Written %2.1f seconds of trace to %s.\n",
1151 		(tchart->last_time - tchart->first_time) / 1000000000.0, output_name);
1152 out_delete:
1153 	perf_session__delete(session);
1154 	return ret;
1155 }
1156 
1157 static int timechart__record(struct timechart *tchart, int argc, const char **argv)
1158 {
1159 	unsigned int rec_argc, i, j;
1160 	const char **rec_argv;
1161 	const char **p;
1162 	unsigned int record_elems;
1163 
1164 	const char * const common_args[] = {
1165 		"record", "-a", "-R", "-c", "1",
1166 	};
1167 	unsigned int common_args_nr = ARRAY_SIZE(common_args);
1168 
1169 	const char * const backtrace_args[] = {
1170 		"-g",
1171 	};
1172 	unsigned int backtrace_args_no = ARRAY_SIZE(backtrace_args);
1173 
1174 	const char * const power_args[] = {
1175 		"-e", "power:cpu_frequency",
1176 		"-e", "power:cpu_idle",
1177 	};
1178 	unsigned int power_args_nr = ARRAY_SIZE(power_args);
1179 
1180 	const char * const old_power_args[] = {
1181 #ifdef SUPPORT_OLD_POWER_EVENTS
1182 		"-e", "power:power_start",
1183 		"-e", "power:power_end",
1184 		"-e", "power:power_frequency",
1185 #endif
1186 	};
1187 	unsigned int old_power_args_nr = ARRAY_SIZE(old_power_args);
1188 
1189 	const char * const tasks_args[] = {
1190 		"-e", "sched:sched_wakeup",
1191 		"-e", "sched:sched_switch",
1192 	};
1193 	unsigned int tasks_args_nr = ARRAY_SIZE(tasks_args);
1194 
1195 #ifdef SUPPORT_OLD_POWER_EVENTS
1196 	if (!is_valid_tracepoint("power:cpu_idle") &&
1197 	    is_valid_tracepoint("power:power_start")) {
1198 		use_old_power_events = 1;
1199 		power_args_nr = 0;
1200 	} else {
1201 		old_power_args_nr = 0;
1202 	}
1203 #endif
1204 
1205 	if (tchart->power_only)
1206 		tasks_args_nr = 0;
1207 
1208 	if (tchart->tasks_only) {
1209 		power_args_nr = 0;
1210 		old_power_args_nr = 0;
1211 	}
1212 
1213 	if (!tchart->with_backtrace)
1214 		backtrace_args_no = 0;
1215 
1216 	record_elems = common_args_nr + tasks_args_nr +
1217 		power_args_nr + old_power_args_nr + backtrace_args_no;
1218 
1219 	rec_argc = record_elems + argc;
1220 	rec_argv = calloc(rec_argc + 1, sizeof(char *));
1221 
1222 	if (rec_argv == NULL)
1223 		return -ENOMEM;
1224 
1225 	p = rec_argv;
1226 	for (i = 0; i < common_args_nr; i++)
1227 		*p++ = strdup(common_args[i]);
1228 
1229 	for (i = 0; i < backtrace_args_no; i++)
1230 		*p++ = strdup(backtrace_args[i]);
1231 
1232 	for (i = 0; i < tasks_args_nr; i++)
1233 		*p++ = strdup(tasks_args[i]);
1234 
1235 	for (i = 0; i < power_args_nr; i++)
1236 		*p++ = strdup(power_args[i]);
1237 
1238 	for (i = 0; i < old_power_args_nr; i++)
1239 		*p++ = strdup(old_power_args[i]);
1240 
1241 	for (j = 0; j < (unsigned int)argc; j++)
1242 		*p++ = argv[j];
1243 
1244 	return cmd_record(rec_argc, rec_argv, NULL);
1245 }
1246 
1247 static int
1248 parse_process(const struct option *opt __maybe_unused, const char *arg,
1249 	      int __maybe_unused unset)
1250 {
1251 	if (arg)
1252 		add_process_filter(arg);
1253 	return 0;
1254 }
1255 
1256 static int
1257 parse_highlight(const struct option *opt __maybe_unused, const char *arg,
1258 		int __maybe_unused unset)
1259 {
1260 	unsigned long duration = strtoul(arg, NULL, 0);
1261 
1262 	if (svg_highlight || svg_highlight_name)
1263 		return -1;
1264 
1265 	if (duration)
1266 		svg_highlight = duration;
1267 	else
1268 		svg_highlight_name = strdup(arg);
1269 
1270 	return 0;
1271 }
1272 
1273 int cmd_timechart(int argc, const char **argv,
1274 		  const char *prefix __maybe_unused)
1275 {
1276 	struct timechart tchart = {
1277 		.tool = {
1278 			.comm		 = process_comm_event,
1279 			.fork		 = process_fork_event,
1280 			.exit		 = process_exit_event,
1281 			.sample		 = process_sample_event,
1282 			.ordered_samples = true,
1283 		},
1284 		.proc_num = 15,
1285 	};
1286 	const char *output_name = "output.svg";
1287 	const struct option timechart_options[] = {
1288 	OPT_STRING('i', "input", &input_name, "file", "input file name"),
1289 	OPT_STRING('o', "output", &output_name, "file", "output file name"),
1290 	OPT_INTEGER('w', "width", &svg_page_width, "page width"),
1291 	OPT_CALLBACK(0, "highlight", NULL, "duration or task name",
1292 		      "highlight tasks. Pass duration in ns or process name.",
1293 		       parse_highlight),
1294 	OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"),
1295 	OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only,
1296 		    "output processes data only"),
1297 	OPT_CALLBACK('p', "process", NULL, "process",
1298 		      "process selector. Pass a pid or process name.",
1299 		       parse_process),
1300 	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
1301 		    "Look for files with symbols relative to this directory"),
1302 	OPT_INTEGER('n', "proc-num", &tchart.proc_num,
1303 		    "min. number of tasks to print"),
1304 	OPT_BOOLEAN('t', "topology", &tchart.topology,
1305 		    "sort CPUs according to topology"),
1306 	OPT_END()
1307 	};
1308 	const char * const timechart_usage[] = {
1309 		"perf timechart [<options>] {record}",
1310 		NULL
1311 	};
1312 
1313 	const struct option record_options[] = {
1314 	OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"),
1315 	OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only,
1316 		    "output processes data only"),
1317 	OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"),
1318 	OPT_END()
1319 	};
1320 	const char * const record_usage[] = {
1321 		"perf timechart record [<options>]",
1322 		NULL
1323 	};
1324 	argc = parse_options(argc, argv, timechart_options, timechart_usage,
1325 			PARSE_OPT_STOP_AT_NON_OPTION);
1326 
1327 	if (tchart.power_only && tchart.tasks_only) {
1328 		pr_err("-P and -T options cannot be used at the same time.\n");
1329 		return -1;
1330 	}
1331 
1332 	symbol__init();
1333 
1334 	if (argc && !strncmp(argv[0], "rec", 3)) {
1335 		argc = parse_options(argc, argv, record_options, record_usage,
1336 				     PARSE_OPT_STOP_AT_NON_OPTION);
1337 
1338 		if (tchart.power_only && tchart.tasks_only) {
1339 			pr_err("-P and -T options cannot be used at the same time.\n");
1340 			return -1;
1341 		}
1342 
1343 		return timechart__record(&tchart, argc, argv);
1344 	} else if (argc)
1345 		usage_with_options(timechart_usage, timechart_options);
1346 
1347 	setup_pager();
1348 
1349 	return __cmd_timechart(&tchart, output_name);
1350 }
1351