perf timechart: Use proc_num to implement --power-only
[deliverable/linux.git] / tools / perf / builtin-timechart.c
CommitLineData
10274989
AV
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
c85cffa5
JO
15#include <traceevent/event-parse.h>
16
10274989
AV
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"
5936678e 24#include "util/evlist.h"
e3f42609 25#include "util/evsel.h"
10274989
AV
26#include <linux/rbtree.h>
27#include "util/symbol.h"
10274989
AV
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"
5cbd0805 35#include "util/event.h"
301a0b02 36#include "util/session.h"
10274989 37#include "util/svghelper.h"
45694aa7 38#include "util/tool.h"
f5fc1412 39#include "util/data.h"
10274989 40
20c457b8
TR
41#define SUPPORT_OLD_POWER_EVENTS 1
42#define PWR_EVENT_EXIT -1
43
54874e32 44static int proc_num = 15;
20c457b8 45
10274989
AV
46static unsigned int numcpus;
47static u64 min_freq; /* Lowest CPU frequency seen */
48static u64 max_freq; /* Highest CPU frequency seen */
49static u64 turbo_frequency;
50
51static u64 first_time, last_time;
52
c0555642 53static bool power_only;
39a90a8e 54
10274989 55
10274989
AV
56struct per_pid;
57struct per_pidcomm;
58
59struct cpu_sample;
60struct power_event;
61struct wake_event;
62
63struct sample_wrapper;
64
65/*
66 * Datastructure layout:
67 * We keep an list of "pid"s, matching the kernels notion of a task struct.
68 * Each "pid" entry, has a list of "comm"s.
69 * this is because we want to track different programs different, while
70 * exec will reuse the original pid (by design).
71 * Each comm has a list of samples that will be used to draw
72 * final graph.
73 */
74
75struct per_pid {
76 struct per_pid *next;
77
78 int pid;
79 int ppid;
80
81 u64 start_time;
82 u64 end_time;
83 u64 total_time;
84 int display;
85
86 struct per_pidcomm *all;
87 struct per_pidcomm *current;
10274989
AV
88};
89
90
91struct per_pidcomm {
92 struct per_pidcomm *next;
93
94 u64 start_time;
95 u64 end_time;
96 u64 total_time;
97
98 int Y;
99 int display;
100
101 long state;
102 u64 state_since;
103
104 char *comm;
105
106 struct cpu_sample *samples;
107};
108
109struct sample_wrapper {
110 struct sample_wrapper *next;
111
112 u64 timestamp;
113 unsigned char data[0];
114};
115
116#define TYPE_NONE 0
117#define TYPE_RUNNING 1
118#define TYPE_WAITING 2
119#define TYPE_BLOCKED 3
120
121struct cpu_sample {
122 struct cpu_sample *next;
123
124 u64 start_time;
125 u64 end_time;
126 int type;
127 int cpu;
128};
129
130static struct per_pid *all_data;
131
132#define CSTATE 1
133#define PSTATE 2
134
135struct power_event {
136 struct power_event *next;
137 int type;
138 int state;
139 u64 start_time;
140 u64 end_time;
141 int cpu;
142};
143
144struct wake_event {
145 struct wake_event *next;
146 int waker;
147 int wakee;
148 u64 time;
149};
150
151static struct power_event *power_events;
152static struct wake_event *wake_events;
153
bbe2987b
AV
154struct process_filter;
155struct process_filter {
5cbd0805
LZ
156 char *name;
157 int pid;
158 struct process_filter *next;
bbe2987b
AV
159};
160
161static struct process_filter *process_filter;
162
163
10274989
AV
164static struct per_pid *find_create_pid(int pid)
165{
166 struct per_pid *cursor = all_data;
167
168 while (cursor) {
169 if (cursor->pid == pid)
170 return cursor;
171 cursor = cursor->next;
172 }
e0dcd6fb 173 cursor = zalloc(sizeof(*cursor));
10274989 174 assert(cursor != NULL);
10274989
AV
175 cursor->pid = pid;
176 cursor->next = all_data;
177 all_data = cursor;
178 return cursor;
179}
180
181static void pid_set_comm(int pid, char *comm)
182{
183 struct per_pid *p;
184 struct per_pidcomm *c;
185 p = find_create_pid(pid);
186 c = p->all;
187 while (c) {
188 if (c->comm && strcmp(c->comm, comm) == 0) {
189 p->current = c;
190 return;
191 }
192 if (!c->comm) {
193 c->comm = strdup(comm);
194 p->current = c;
195 return;
196 }
197 c = c->next;
198 }
e0dcd6fb 199 c = zalloc(sizeof(*c));
10274989 200 assert(c != NULL);
10274989
AV
201 c->comm = strdup(comm);
202 p->current = c;
203 c->next = p->all;
204 p->all = c;
205}
206
207static void pid_fork(int pid, int ppid, u64 timestamp)
208{
209 struct per_pid *p, *pp;
210 p = find_create_pid(pid);
211 pp = find_create_pid(ppid);
212 p->ppid = ppid;
213 if (pp->current && pp->current->comm && !p->current)
214 pid_set_comm(pid, pp->current->comm);
215
216 p->start_time = timestamp;
217 if (p->current) {
218 p->current->start_time = timestamp;
219 p->current->state_since = timestamp;
220 }
221}
222
223static void pid_exit(int pid, u64 timestamp)
224{
225 struct per_pid *p;
226 p = find_create_pid(pid);
227 p->end_time = timestamp;
228 if (p->current)
229 p->current->end_time = timestamp;
230}
231
232static void
233pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end)
234{
235 struct per_pid *p;
236 struct per_pidcomm *c;
237 struct cpu_sample *sample;
238
239 p = find_create_pid(pid);
240 c = p->current;
241 if (!c) {
e0dcd6fb 242 c = zalloc(sizeof(*c));
10274989 243 assert(c != NULL);
10274989
AV
244 p->current = c;
245 c->next = p->all;
246 p->all = c;
247 }
248
e0dcd6fb 249 sample = zalloc(sizeof(*sample));
10274989 250 assert(sample != NULL);
10274989
AV
251 sample->start_time = start;
252 sample->end_time = end;
253 sample->type = type;
254 sample->next = c->samples;
255 sample->cpu = cpu;
256 c->samples = sample;
257
258 if (sample->type == TYPE_RUNNING && end > start && start > 0) {
259 c->total_time += (end-start);
260 p->total_time += (end-start);
261 }
262
263 if (c->start_time == 0 || c->start_time > start)
264 c->start_time = start;
265 if (p->start_time == 0 || p->start_time > start)
266 p->start_time = start;
10274989
AV
267}
268
269#define MAX_CPUS 4096
270
271static u64 cpus_cstate_start_times[MAX_CPUS];
272static int cpus_cstate_state[MAX_CPUS];
273static u64 cpus_pstate_start_times[MAX_CPUS];
274static u64 cpus_pstate_state[MAX_CPUS];
275
1d037ca1 276static int process_comm_event(struct perf_tool *tool __maybe_unused,
d20deb64 277 union perf_event *event,
1d037ca1
IT
278 struct perf_sample *sample __maybe_unused,
279 struct machine *machine __maybe_unused)
10274989 280{
8f06d7e6 281 pid_set_comm(event->comm.tid, event->comm.comm);
10274989
AV
282 return 0;
283}
d8f66248 284
1d037ca1 285static int process_fork_event(struct perf_tool *tool __maybe_unused,
d20deb64 286 union perf_event *event,
1d037ca1
IT
287 struct perf_sample *sample __maybe_unused,
288 struct machine *machine __maybe_unused)
10274989
AV
289{
290 pid_fork(event->fork.pid, event->fork.ppid, event->fork.time);
291 return 0;
292}
293
1d037ca1 294static int process_exit_event(struct perf_tool *tool __maybe_unused,
d20deb64 295 union perf_event *event,
1d037ca1
IT
296 struct perf_sample *sample __maybe_unused,
297 struct machine *machine __maybe_unused)
10274989
AV
298{
299 pid_exit(event->fork.pid, event->fork.time);
300 return 0;
301}
302
303struct trace_entry {
10274989
AV
304 unsigned short type;
305 unsigned char flags;
306 unsigned char preempt_count;
307 int pid;
028c5152 308 int lock_depth;
10274989
AV
309};
310
20c457b8
TR
311#ifdef SUPPORT_OLD_POWER_EVENTS
312static int use_old_power_events;
313struct power_entry_old {
10274989 314 struct trace_entry te;
4c21adf2
TR
315 u64 type;
316 u64 value;
317 u64 cpu_id;
10274989 318};
20c457b8
TR
319#endif
320
321struct power_processor_entry {
322 struct trace_entry te;
323 u32 state;
324 u32 cpu_id;
325};
10274989
AV
326
327#define TASK_COMM_LEN 16
328struct wakeup_entry {
329 struct trace_entry te;
330 char comm[TASK_COMM_LEN];
331 int pid;
332 int prio;
333 int success;
334};
335
10274989
AV
336struct sched_switch {
337 struct trace_entry te;
338 char prev_comm[TASK_COMM_LEN];
339 int prev_pid;
340 int prev_prio;
341 long prev_state; /* Arjan weeps. */
342 char next_comm[TASK_COMM_LEN];
343 int next_pid;
344 int next_prio;
345};
346
347static 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
353static void c_state_end(int cpu, u64 timestamp)
354{
e0dcd6fb
ACM
355 struct power_event *pwr = zalloc(sizeof(*pwr));
356
10274989
AV
357 if (!pwr)
358 return;
10274989
AV
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 = power_events;
366
367 power_events = pwr;
368}
369
370static void p_state_change(int cpu, u64 timestamp, u64 new_freq)
371{
372 struct power_event *pwr;
10274989
AV
373
374 if (new_freq > 8000000) /* detect invalid data */
375 return;
376
e0dcd6fb 377 pwr = zalloc(sizeof(*pwr));
10274989
AV
378 if (!pwr)
379 return;
10274989
AV
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 = power_events;
387
388 if (!pwr->start_time)
389 pwr->start_time = first_time;
390
391 power_events = pwr;
392
393 cpus_pstate_state[cpu] = new_freq;
394 cpus_pstate_start_times[cpu] = timestamp;
395
396 if ((u64)new_freq > max_freq)
397 max_freq = new_freq;
398
399 if (new_freq < min_freq || min_freq == 0)
400 min_freq = new_freq;
401
402 if (new_freq == max_freq - 1000)
403 turbo_frequency = max_freq;
404}
405
406static void
407sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te)
408{
10274989
AV
409 struct per_pid *p;
410 struct wakeup_entry *wake = (void *)te;
e0dcd6fb 411 struct wake_event *we = zalloc(sizeof(*we));
10274989 412
10274989
AV
413 if (!we)
414 return;
415
10274989
AV
416 we->time = timestamp;
417 we->waker = pid;
418
419 if ((te->flags & TRACE_FLAG_HARDIRQ) || (te->flags & TRACE_FLAG_SOFTIRQ))
420 we->waker = -1;
421
422 we->wakee = wake->pid;
423 we->next = wake_events;
424 wake_events = we;
425 p = find_create_pid(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(p->pid, p->current->state, cpu, p->current->state_since, timestamp);
433 p->current->state_since = timestamp;
434 p->current->state = TYPE_WAITING;
435 }
436}
437
438static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
439{
440 struct per_pid *p = NULL, *prev_p;
441 struct sched_switch *sw = (void *)te;
442
443
444 prev_p = find_create_pid(sw->prev_pid);
445
446 p = find_create_pid(sw->next_pid);
447
448 if (prev_p->current && prev_p->current->state != TYPE_NONE)
449 pid_put_sample(sw->prev_pid, TYPE_RUNNING, cpu, prev_p->current->state_since, timestamp);
450 if (p && p->current) {
451 if (p->current->state != TYPE_NONE)
452 pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp);
453
33e26a1b
JL
454 p->current->state_since = timestamp;
455 p->current->state = TYPE_RUNNING;
10274989
AV
456 }
457
458 if (prev_p->current) {
459 prev_p->current->state = TYPE_NONE;
460 prev_p->current->state_since = timestamp;
461 if (sw->prev_state & 2)
462 prev_p->current->state = TYPE_BLOCKED;
463 if (sw->prev_state == 0)
464 prev_p->current->state = TYPE_WAITING;
465 }
466}
467
5936678e
JO
468typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
469 struct perf_sample *sample);
10274989 470
1d037ca1
IT
471static int process_sample_event(struct perf_tool *tool __maybe_unused,
472 union perf_event *event __maybe_unused,
8d50e5b4 473 struct perf_sample *sample,
e3f42609 474 struct perf_evsel *evsel,
1d037ca1 475 struct machine *machine __maybe_unused)
10274989 476{
e3f42609 477 if (evsel->attr.sample_type & PERF_SAMPLE_TIME) {
640c03ce
ACM
478 if (!first_time || first_time > sample->time)
479 first_time = sample->time;
480 if (last_time < sample->time)
481 last_time = sample->time;
10274989 482 }
180f95e2 483
5936678e
JO
484 if (sample->cpu > numcpus)
485 numcpus = sample->cpu;
486
744a9719
ACM
487 if (evsel->handler != NULL) {
488 tracepoint_handler f = evsel->handler;
5936678e
JO
489 return f(evsel, sample);
490 }
491
492 return 0;
493}
494
495static int
496process_sample_cpu_idle(struct perf_evsel *evsel __maybe_unused,
497 struct perf_sample *sample)
498{
499 struct power_processor_entry *ppe = sample->raw_data;
500
501 if (ppe->state == (u32) PWR_EVENT_EXIT)
502 c_state_end(ppe->cpu_id, sample->time);
503 else
504 c_state_start(ppe->cpu_id, sample->time, ppe->state);
505 return 0;
506}
507
508static int
509process_sample_cpu_frequency(struct perf_evsel *evsel __maybe_unused,
510 struct perf_sample *sample)
511{
512 struct power_processor_entry *ppe = sample->raw_data;
513
514 p_state_change(ppe->cpu_id, sample->time, ppe->state);
515 return 0;
516}
517
518static int
519process_sample_sched_wakeup(struct perf_evsel *evsel __maybe_unused,
520 struct perf_sample *sample)
521{
522 struct trace_entry *te = sample->raw_data;
523
524 sched_wakeup(sample->cpu, sample->time, sample->pid, te);
525 return 0;
526}
10274989 527
5936678e
JO
528static int
529process_sample_sched_switch(struct perf_evsel *evsel __maybe_unused,
530 struct perf_sample *sample)
531{
532 struct trace_entry *te = sample->raw_data;
10274989 533
5936678e
JO
534 sched_switch(sample->cpu, sample->time, te);
535 return 0;
536}
20c457b8
TR
537
538#ifdef SUPPORT_OLD_POWER_EVENTS
5936678e
JO
539static int
540process_sample_power_start(struct perf_evsel *evsel __maybe_unused,
541 struct perf_sample *sample)
542{
543 struct power_entry_old *peo = sample->raw_data;
544
545 c_state_start(peo->cpu_id, sample->time, peo->value);
546 return 0;
547}
548
549static int
550process_sample_power_end(struct perf_evsel *evsel __maybe_unused,
551 struct perf_sample *sample)
552{
553 c_state_end(sample->cpu, sample->time);
554 return 0;
555}
556
557static int
558process_sample_power_frequency(struct perf_evsel *evsel __maybe_unused,
559 struct perf_sample *sample)
560{
561 struct power_entry_old *peo = sample->raw_data;
562
563 p_state_change(peo->cpu_id, sample->time, peo->value);
10274989
AV
564 return 0;
565}
5936678e 566#endif /* SUPPORT_OLD_POWER_EVENTS */
10274989
AV
567
568/*
569 * After the last sample we need to wrap up the current C/P state
570 * and close out each CPU for these.
571 */
572static void end_sample_processing(void)
573{
574 u64 cpu;
575 struct power_event *pwr;
576
39a90a8e 577 for (cpu = 0; cpu <= numcpus; cpu++) {
e0dcd6fb
ACM
578 /* C state */
579#if 0
580 pwr = zalloc(sizeof(*pwr));
10274989
AV
581 if (!pwr)
582 return;
10274989 583
10274989
AV
584 pwr->state = cpus_cstate_state[cpu];
585 pwr->start_time = cpus_cstate_start_times[cpu];
586 pwr->end_time = last_time;
587 pwr->cpu = cpu;
588 pwr->type = CSTATE;
589 pwr->next = power_events;
590
591 power_events = pwr;
592#endif
593 /* P state */
594
e0dcd6fb 595 pwr = zalloc(sizeof(*pwr));
10274989
AV
596 if (!pwr)
597 return;
10274989
AV
598
599 pwr->state = cpus_pstate_state[cpu];
600 pwr->start_time = cpus_pstate_start_times[cpu];
601 pwr->end_time = last_time;
602 pwr->cpu = cpu;
603 pwr->type = PSTATE;
604 pwr->next = power_events;
605
606 if (!pwr->start_time)
607 pwr->start_time = first_time;
608 if (!pwr->state)
609 pwr->state = min_freq;
610 power_events = pwr;
611 }
612}
613
10274989
AV
614/*
615 * Sort the pid datastructure
616 */
617static void sort_pids(void)
618{
619 struct per_pid *new_list, *p, *cursor, *prev;
620 /* sort by ppid first, then by pid, lowest to highest */
621
622 new_list = NULL;
623
624 while (all_data) {
625 p = all_data;
626 all_data = p->next;
627 p->next = NULL;
628
629 if (new_list == NULL) {
630 new_list = p;
631 p->next = NULL;
632 continue;
633 }
634 prev = NULL;
635 cursor = new_list;
636 while (cursor) {
637 if (cursor->ppid > p->ppid ||
638 (cursor->ppid == p->ppid && cursor->pid > p->pid)) {
639 /* must insert before */
640 if (prev) {
641 p->next = prev->next;
642 prev->next = p;
643 cursor = NULL;
644 continue;
645 } else {
646 p->next = new_list;
647 new_list = p;
648 cursor = NULL;
649 continue;
650 }
651 }
652
653 prev = cursor;
654 cursor = cursor->next;
655 if (!cursor)
656 prev->next = p;
657 }
658 }
659 all_data = new_list;
660}
661
662
663static void draw_c_p_states(void)
664{
665 struct power_event *pwr;
666 pwr = power_events;
667
668 /*
669 * two pass drawing so that the P state bars are on top of the C state blocks
670 */
671 while (pwr) {
672 if (pwr->type == CSTATE)
673 svg_cstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
674 pwr = pwr->next;
675 }
676
677 pwr = power_events;
678 while (pwr) {
679 if (pwr->type == PSTATE) {
680 if (!pwr->state)
681 pwr->state = min_freq;
682 svg_pstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
683 }
684 pwr = pwr->next;
685 }
686}
687
688static void draw_wakeups(void)
689{
690 struct wake_event *we;
691 struct per_pid *p;
692 struct per_pidcomm *c;
693
694 we = wake_events;
695 while (we) {
696 int from = 0, to = 0;
4f1202c8 697 char *task_from = NULL, *task_to = NULL;
10274989
AV
698
699 /* locate the column of the waker and wakee */
700 p = all_data;
701 while (p) {
702 if (p->pid == we->waker || p->pid == we->wakee) {
703 c = p->all;
704 while (c) {
705 if (c->Y && c->start_time <= we->time && c->end_time >= we->time) {
bbe2987b 706 if (p->pid == we->waker && !from) {
10274989 707 from = c->Y;
3bc2a39c 708 task_from = strdup(c->comm);
4f1202c8 709 }
bbe2987b 710 if (p->pid == we->wakee && !to) {
10274989 711 to = c->Y;
3bc2a39c 712 task_to = strdup(c->comm);
4f1202c8 713 }
10274989
AV
714 }
715 c = c->next;
716 }
3bc2a39c
AV
717 c = p->all;
718 while (c) {
719 if (p->pid == we->waker && !from) {
720 from = c->Y;
721 task_from = strdup(c->comm);
722 }
723 if (p->pid == we->wakee && !to) {
724 to = c->Y;
725 task_to = strdup(c->comm);
726 }
727 c = c->next;
728 }
10274989
AV
729 }
730 p = p->next;
731 }
732
3bc2a39c
AV
733 if (!task_from) {
734 task_from = malloc(40);
735 sprintf(task_from, "[%i]", we->waker);
736 }
737 if (!task_to) {
738 task_to = malloc(40);
739 sprintf(task_to, "[%i]", we->wakee);
740 }
741
10274989
AV
742 if (we->waker == -1)
743 svg_interrupt(we->time, to);
744 else if (from && to && abs(from - to) == 1)
745 svg_wakeline(we->time, from, to);
746 else
4f1202c8 747 svg_partial_wakeline(we->time, from, task_from, to, task_to);
10274989 748 we = we->next;
3bc2a39c
AV
749
750 free(task_from);
751 free(task_to);
10274989
AV
752 }
753}
754
755static void draw_cpu_usage(void)
756{
757 struct per_pid *p;
758 struct per_pidcomm *c;
759 struct cpu_sample *sample;
760 p = all_data;
761 while (p) {
762 c = p->all;
763 while (c) {
764 sample = c->samples;
765 while (sample) {
766 if (sample->type == TYPE_RUNNING)
767 svg_process(sample->cpu, sample->start_time, sample->end_time, "sample", c->comm);
768
769 sample = sample->next;
770 }
771 c = c->next;
772 }
773 p = p->next;
774 }
775}
776
777static void draw_process_bars(void)
778{
779 struct per_pid *p;
780 struct per_pidcomm *c;
781 struct cpu_sample *sample;
782 int Y = 0;
783
784 Y = 2 * numcpus + 2;
785
786 p = all_data;
787 while (p) {
788 c = p->all;
789 while (c) {
790 if (!c->display) {
791 c->Y = 0;
792 c = c->next;
793 continue;
794 }
795
a92fe7b3 796 svg_box(Y, c->start_time, c->end_time, "process");
10274989
AV
797 sample = c->samples;
798 while (sample) {
799 if (sample->type == TYPE_RUNNING)
a92fe7b3 800 svg_sample(Y, sample->cpu, sample->start_time, sample->end_time);
10274989
AV
801 if (sample->type == TYPE_BLOCKED)
802 svg_box(Y, sample->start_time, sample->end_time, "blocked");
803 if (sample->type == TYPE_WAITING)
a92fe7b3 804 svg_waiting(Y, sample->start_time, sample->end_time);
10274989
AV
805 sample = sample->next;
806 }
807
808 if (c->comm) {
809 char comm[256];
810 if (c->total_time > 5000000000) /* 5 seconds */
811 sprintf(comm, "%s:%i (%2.2fs)", c->comm, p->pid, c->total_time / 1000000000.0);
812 else
813 sprintf(comm, "%s:%i (%3.1fms)", c->comm, p->pid, c->total_time / 1000000.0);
814
815 svg_text(Y, c->start_time, comm);
816 }
817 c->Y = Y;
818 Y++;
819 c = c->next;
820 }
821 p = p->next;
822 }
823}
824
bbe2987b
AV
825static void add_process_filter(const char *string)
826{
e0dcd6fb
ACM
827 int pid = strtoull(string, NULL, 10);
828 struct process_filter *filt = malloc(sizeof(*filt));
bbe2987b 829
bbe2987b
AV
830 if (!filt)
831 return;
832
833 filt->name = strdup(string);
834 filt->pid = pid;
835 filt->next = process_filter;
836
837 process_filter = filt;
838}
839
840static int passes_filter(struct per_pid *p, struct per_pidcomm *c)
841{
842 struct process_filter *filt;
843 if (!process_filter)
844 return 1;
845
846 filt = process_filter;
847 while (filt) {
848 if (filt->pid && p->pid == filt->pid)
849 return 1;
850 if (strcmp(filt->name, c->comm) == 0)
851 return 1;
852 filt = filt->next;
853 }
854 return 0;
855}
856
857static int determine_display_tasks_filtered(void)
858{
859 struct per_pid *p;
860 struct per_pidcomm *c;
861 int count = 0;
862
863 p = all_data;
864 while (p) {
865 p->display = 0;
866 if (p->start_time == 1)
867 p->start_time = first_time;
868
869 /* no exit marker, task kept running to the end */
870 if (p->end_time == 0)
871 p->end_time = last_time;
872
873 c = p->all;
874
875 while (c) {
876 c->display = 0;
877
878 if (c->start_time == 1)
879 c->start_time = first_time;
880
881 if (passes_filter(p, c)) {
882 c->display = 1;
883 p->display = 1;
884 count++;
885 }
886
887 if (c->end_time == 0)
888 c->end_time = last_time;
889
890 c = c->next;
891 }
892 p = p->next;
893 }
894 return count;
895}
896
10274989
AV
897static int determine_display_tasks(u64 threshold)
898{
899 struct per_pid *p;
900 struct per_pidcomm *c;
901 int count = 0;
902
bbe2987b
AV
903 if (process_filter)
904 return determine_display_tasks_filtered();
905
10274989
AV
906 p = all_data;
907 while (p) {
908 p->display = 0;
909 if (p->start_time == 1)
910 p->start_time = first_time;
911
912 /* no exit marker, task kept running to the end */
913 if (p->end_time == 0)
914 p->end_time = last_time;
753c505d 915 if (p->total_time >= threshold)
10274989
AV
916 p->display = 1;
917
918 c = p->all;
919
920 while (c) {
921 c->display = 0;
922
923 if (c->start_time == 1)
924 c->start_time = first_time;
925
753c505d 926 if (c->total_time >= threshold) {
10274989
AV
927 c->display = 1;
928 count++;
929 }
930
931 if (c->end_time == 0)
932 c->end_time = last_time;
933
934 c = c->next;
935 }
936 p = p->next;
937 }
938 return count;
939}
940
941
942
943#define TIME_THRESH 10000000
944
945static void write_svg_file(const char *filename)
946{
947 u64 i;
948 int count;
0a8eb275 949 int thresh = TIME_THRESH;
10274989
AV
950
951 numcpus++;
952
753c505d
SF
953 if (power_only)
954 proc_num = 0;
10274989 955
0a8eb275
SF
956 /* We'd like to show at least proc_num tasks;
957 * be less picky if we have fewer */
958 do {
959 count = determine_display_tasks(thresh);
960 thresh /= 10;
54874e32 961 } while (!process_filter && thresh && count < proc_num);
10274989 962
5094b655 963 open_svg(filename, numcpus, count, first_time, last_time);
10274989 964
5094b655 965 svg_time_grid();
10274989
AV
966 svg_legenda();
967
968 for (i = 0; i < numcpus; i++)
969 svg_cpu_box(i, max_freq, turbo_frequency);
970
971 draw_cpu_usage();
753c505d
SF
972 if (proc_num)
973 draw_process_bars();
10274989 974 draw_c_p_states();
753c505d
SF
975 if (proc_num)
976 draw_wakeups();
10274989
AV
977
978 svg_close();
979}
980
70cb4e96 981static int __cmd_timechart(const char *output_name)
5cbd0805 982{
73bdc715
ACM
983 struct perf_tool perf_timechart = {
984 .comm = process_comm_event,
985 .fork = process_fork_event,
986 .exit = process_exit_event,
987 .sample = process_sample_event,
988 .ordered_samples = true,
989 };
5936678e
JO
990 const struct perf_evsel_str_handler power_tracepoints[] = {
991 { "power:cpu_idle", process_sample_cpu_idle },
992 { "power:cpu_frequency", process_sample_cpu_frequency },
993 { "sched:sched_wakeup", process_sample_sched_wakeup },
994 { "sched:sched_switch", process_sample_sched_switch },
995#ifdef SUPPORT_OLD_POWER_EVENTS
996 { "power:power_start", process_sample_power_start },
997 { "power:power_end", process_sample_power_end },
998 { "power:power_frequency", process_sample_power_frequency },
999#endif
1000 };
f5fc1412
JO
1001 struct perf_data_file file = {
1002 .path = input_name,
1003 .mode = PERF_DATA_MODE_READ,
1004 };
1005
1006 struct perf_session *session = perf_session__new(&file, false,
1007 &perf_timechart);
d549c769 1008 int ret = -EINVAL;
10274989 1009
94c744b6
ACM
1010 if (session == NULL)
1011 return -ENOMEM;
1012
d549c769
ACM
1013 if (!perf_session__has_traces(session, "timechart record"))
1014 goto out_delete;
1015
5936678e
JO
1016 if (perf_session__set_tracepoints_handlers(session,
1017 power_tracepoints)) {
1018 pr_err("Initializing session tracepoint handlers failed\n");
1019 goto out_delete;
1020 }
1021
45694aa7 1022 ret = perf_session__process_events(session, &perf_timechart);
5cbd0805 1023 if (ret)
94c744b6 1024 goto out_delete;
10274989 1025
10274989
AV
1026 end_sample_processing();
1027
1028 sort_pids();
1029
1030 write_svg_file(output_name);
1031
6beba7ad
ACM
1032 pr_info("Written %2.1f seconds of trace to %s.\n",
1033 (last_time - first_time) / 1000000000.0, output_name);
94c744b6
ACM
1034out_delete:
1035 perf_session__delete(session);
1036 return ret;
10274989
AV
1037}
1038
3c09eebd
AV
1039static int __cmd_record(int argc, const char **argv)
1040{
73bdc715
ACM
1041#ifdef SUPPORT_OLD_POWER_EVENTS
1042 const char * const record_old_args[] = {
4a4d371a 1043 "record", "-a", "-R", "-c", "1",
73bdc715
ACM
1044 "-e", "power:power_start",
1045 "-e", "power:power_end",
1046 "-e", "power:power_frequency",
1047 "-e", "sched:sched_wakeup",
1048 "-e", "sched:sched_switch",
1049 };
1050#endif
1051 const char * const record_new_args[] = {
4a4d371a 1052 "record", "-a", "-R", "-c", "1",
73bdc715
ACM
1053 "-e", "power:cpu_frequency",
1054 "-e", "power:cpu_idle",
1055 "-e", "sched:sched_wakeup",
1056 "-e", "sched:sched_switch",
1057 };
3c09eebd
AV
1058 unsigned int rec_argc, i, j;
1059 const char **rec_argv;
20c457b8
TR
1060 const char * const *record_args = record_new_args;
1061 unsigned int record_elems = ARRAY_SIZE(record_new_args);
1062
1063#ifdef SUPPORT_OLD_POWER_EVENTS
1064 if (!is_valid_tracepoint("power:cpu_idle") &&
1065 is_valid_tracepoint("power:power_start")) {
1066 use_old_power_events = 1;
1067 record_args = record_old_args;
1068 record_elems = ARRAY_SIZE(record_old_args);
1069 }
1070#endif
3c09eebd 1071
20c457b8 1072 rec_argc = record_elems + argc - 1;
3c09eebd
AV
1073 rec_argv = calloc(rec_argc + 1, sizeof(char *));
1074
ce47dc56
CS
1075 if (rec_argv == NULL)
1076 return -ENOMEM;
1077
20c457b8 1078 for (i = 0; i < record_elems; i++)
3c09eebd
AV
1079 rec_argv[i] = strdup(record_args[i]);
1080
1081 for (j = 1; j < (unsigned int)argc; j++, i++)
1082 rec_argv[i] = argv[j];
1083
1084 return cmd_record(i, rec_argv, NULL);
1085}
1086
bbe2987b 1087static int
1d037ca1
IT
1088parse_process(const struct option *opt __maybe_unused, const char *arg,
1089 int __maybe_unused unset)
bbe2987b
AV
1090{
1091 if (arg)
1092 add_process_filter(arg);
1093 return 0;
1094}
1095
73bdc715
ACM
1096int cmd_timechart(int argc, const char **argv,
1097 const char *prefix __maybe_unused)
1098{
73bdc715
ACM
1099 const char *output_name = "output.svg";
1100 const struct option options[] = {
1101 OPT_STRING('i', "input", &input_name, "file", "input file name"),
1102 OPT_STRING('o', "output", &output_name, "file", "output file name"),
1103 OPT_INTEGER('w', "width", &svg_page_width, "page width"),
1104 OPT_BOOLEAN('P', "power-only", &power_only, "output power data only"),
bbe2987b
AV
1105 OPT_CALLBACK('p', "process", NULL, "process",
1106 "process selector. Pass a pid or process name.",
1107 parse_process),
ec5761ea
DA
1108 OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
1109 "Look for files with symbols relative to this directory"),
54874e32
SF
1110 OPT_INTEGER('n', "proc-num", &proc_num,
1111 "min. number of tasks to print"),
10274989 1112 OPT_END()
73bdc715
ACM
1113 };
1114 const char * const timechart_usage[] = {
1115 "perf timechart [<options>] {record}",
1116 NULL
1117 };
10274989 1118
3c09eebd
AV
1119 argc = parse_options(argc, argv, options, timechart_usage,
1120 PARSE_OPT_STOP_AT_NON_OPTION);
10274989 1121
655000e7
ACM
1122 symbol__init();
1123
3c09eebd
AV
1124 if (argc && !strncmp(argv[0], "rec", 3))
1125 return __cmd_record(argc, argv);
1126 else if (argc)
1127 usage_with_options(timechart_usage, options);
10274989
AV
1128
1129 setup_pager();
1130
70cb4e96 1131 return __cmd_timechart(output_name);
10274989 1132}
This page took 0.248111 seconds and 5 git commands to generate.