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

Archive Download this file



interactive