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