Version:  2.0.40 2.2.26 2.4.37 2.6.39 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15

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

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us