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