Linux Perf
builtin-timechart.c
Go to the documentation of this file.
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 <errno.h>
16 #include <inttypes.h>
17 #include <traceevent/event-parse.h>
18 
19 #include "builtin.h"
20 
21 #include "util/util.h"
22 
23 #include "util/color.h"
24 #include <linux/list.h>
25 #include "util/cache.h"
26 #include "util/evlist.h"
27 #include "util/evsel.h"
28 #include <linux/kernel.h>
29 #include <linux/rbtree.h>
30 #include <linux/time64.h>
31 #include "util/symbol.h"
32 #include "util/thread.h"
33 #include "util/callchain.h"
34 
35 #include "perf.h"
36 #include "util/header.h"
37 #include <subcmd/parse-options.h>
38 #include "util/parse-events.h"
39 #include "util/event.h"
40 #include "util/session.h"
41 #include "util/svghelper.h"
42 #include "util/tool.h"
43 #include "util/data.h"
44 #include "util/debug.h"
45 
46 #define SUPPORT_OLD_POWER_EVENTS 1
47 #define PWR_EVENT_EXIT -1
48 
49 struct per_pid;
50 struct power_event;
51 struct wake_event;
52 
53 struct timechart {
54  struct perf_tool tool;
55  struct per_pid *all_data;
58  int proc_num;
59  unsigned int numcpus;
60  u64 min_freq, /* Lowest CPU frequency seen */
61  max_freq, /* Highest CPU frequency seen */
64  bool power_only,
65  tasks_only,
67  topology;
68  bool force;
69  /* IO related settings */
70  bool io_only,
72  u64 io_events;
73  u64 min_time,
74  merge_dist;
75 };
76 
77 struct per_pidcomm;
78 struct cpu_sample;
79 struct io_sample;
80 
81 /*
82  * Datastructure layout:
83  * We keep an list of "pid"s, matching the kernels notion of a task struct.
84  * Each "pid" entry, has a list of "comm"s.
85  * this is because we want to track different programs different, while
86  * exec will reuse the original pid (by design).
87  * Each comm has a list of samples that will be used to draw
88  * final graph.
89  */
90 
91 struct per_pid {
92  struct per_pid *next;
93 
94  int pid;
95  int ppid;
96 
98  u64 end_time;
101  int display;
102 
103  struct per_pidcomm *all;
105 };
106 
107 
108 struct per_pidcomm {
109  struct per_pidcomm *next;
110 
112  u64 end_time;
116 
117  int Y;
118  int display;
119 
120  long state;
122 
123  char *comm;
124 
127 };
128 
131 
133  unsigned char data[0];
134 };
135 
136 #define TYPE_NONE 0
137 #define TYPE_RUNNING 1
138 #define TYPE_WAITING 2
139 #define TYPE_BLOCKED 3
140 
141 struct cpu_sample {
142  struct cpu_sample *next;
143 
145  u64 end_time;
146  int type;
147  int cpu;
148  const char *backtrace;
149 };
150 
151 enum {
158 };
159 
160 struct io_sample {
161  struct io_sample *next;
162 
164  u64 end_time;
165  u64 bytes;
166  int type;
167  int fd;
168  int err;
169  int merges;
170 };
171 
172 #define CSTATE 1
173 #define PSTATE 2
174 
175 struct power_event {
176  struct power_event *next;
177  int type;
178  int state;
180  u64 end_time;
181  int cpu;
182 };
183 
184 struct wake_event {
185  struct wake_event *next;
186  int waker;
187  int wakee;
188  u64 time;
189  const char *backtrace;
190 };
191 
193  char *name;
194  int pid;
196 };
197 
199 
200 
201 static struct per_pid *find_create_pid(struct timechart *tchart, int pid)
202 {
203  struct per_pid *cursor = tchart->all_data;
204 
205  while (cursor) {
206  if (cursor->pid == pid)
207  return cursor;
208  cursor = cursor->next;
209  }
210  cursor = zalloc(sizeof(*cursor));
211  assert(cursor != NULL);
212  cursor->pid = pid;
213  cursor->next = tchart->all_data;
214  tchart->all_data = cursor;
215  return cursor;
216 }
217 
218 static void pid_set_comm(struct timechart *tchart, int pid, char *comm)
219 {
220  struct per_pid *p;
221  struct per_pidcomm *c;
222  p = find_create_pid(tchart, pid);
223  c = p->all;
224  while (c) {
225  if (c->comm && strcmp(c->comm, comm) == 0) {
226  p->current = c;
227  return;
228  }
229  if (!c->comm) {
230  c->comm = strdup(comm);
231  p->current = c;
232  return;
233  }
234  c = c->next;
235  }
236  c = zalloc(sizeof(*c));
237  assert(c != NULL);
238  c->comm = strdup(comm);
239  p->current = c;
240  c->next = p->all;
241  p->all = c;
242 }
243 
244 static void pid_fork(struct timechart *tchart, int pid, int ppid, u64 timestamp)
245 {
246  struct per_pid *p, *pp;
247  p = find_create_pid(tchart, pid);
248  pp = find_create_pid(tchart, ppid);
249  p->ppid = ppid;
250  if (pp->current && pp->current->comm && !p->current)
251  pid_set_comm(tchart, pid, pp->current->comm);
252 
253  p->start_time = timestamp;
254  if (p->current && !p->current->start_time) {
255  p->current->start_time = timestamp;
256  p->current->state_since = timestamp;
257  }
258 }
259 
260 static void pid_exit(struct timechart *tchart, int pid, u64 timestamp)
261 {
262  struct per_pid *p;
263  p = find_create_pid(tchart, pid);
264  p->end_time = timestamp;
265  if (p->current)
266  p->current->end_time = timestamp;
267 }
268 
269 static void pid_put_sample(struct timechart *tchart, int pid, int type,
270  unsigned int cpu, u64 start, u64 end,
271  const char *backtrace)
272 {
273  struct per_pid *p;
274  struct per_pidcomm *c;
275  struct cpu_sample *sample;
276 
277  p = find_create_pid(tchart, pid);
278  c = p->current;
279  if (!c) {
280  c = zalloc(sizeof(*c));
281  assert(c != NULL);
282  p->current = c;
283  c->next = p->all;
284  p->all = c;
285  }
286 
287  sample = zalloc(sizeof(*sample));
288  assert(sample != NULL);
289  sample->start_time = start;
290  sample->end_time = end;
291  sample->type = type;
292  sample->next = c->samples;
293  sample->cpu = cpu;
294  sample->backtrace = backtrace;
295  c->samples = sample;
296 
297  if (sample->type == TYPE_RUNNING && end > start && start > 0) {
298  c->total_time += (end-start);
299  p->total_time += (end-start);
300  }
301 
302  if (c->start_time == 0 || c->start_time > start)
303  c->start_time = start;
304  if (p->start_time == 0 || p->start_time > start)
305  p->start_time = start;
306 }
307 
308 #define MAX_CPUS 4096
309 
314 
315 static int process_comm_event(struct perf_tool *tool,
316  union perf_event *event,
317  struct perf_sample *sample __maybe_unused,
318  struct machine *machine __maybe_unused)
319 {
320  struct timechart *tchart = container_of(tool, struct timechart, tool);
321  pid_set_comm(tchart, event->comm.tid, event->comm.comm);
322  return 0;
323 }
324 
325 static int process_fork_event(struct perf_tool *tool,
326  union perf_event *event,
327  struct perf_sample *sample __maybe_unused,
328  struct machine *machine __maybe_unused)
329 {
330  struct timechart *tchart = container_of(tool, struct timechart, tool);
331  pid_fork(tchart, event->fork.pid, event->fork.ppid, event->fork.time);
332  return 0;
333 }
334 
335 static int process_exit_event(struct perf_tool *tool,
336  union perf_event *event,
337  struct perf_sample *sample __maybe_unused,
338  struct machine *machine __maybe_unused)
339 {
340  struct timechart *tchart = container_of(tool, struct timechart, tool);
341  pid_exit(tchart, event->fork.pid, event->fork.time);
342  return 0;
343 }
344 
345 #ifdef SUPPORT_OLD_POWER_EVENTS
347 #endif
348 
349 static void c_state_start(int cpu, u64 timestamp, int state)
350 {
351  cpus_cstate_start_times[cpu] = timestamp;
352  cpus_cstate_state[cpu] = state;
353 }
354 
355 static void c_state_end(struct timechart *tchart, int cpu, u64 timestamp)
356 {
357  struct power_event *pwr = zalloc(sizeof(*pwr));
358 
359  if (!pwr)
360  return;
361 
362  pwr->state = cpus_cstate_state[cpu];
364  pwr->end_time = timestamp;
365  pwr->cpu = cpu;
366  pwr->type = CSTATE;
367  pwr->next = tchart->power_events;
368 
369  tchart->power_events = pwr;
370 }
371 
372 static void p_state_change(struct timechart *tchart, int cpu, u64 timestamp, u64 new_freq)
373 {
374  struct power_event *pwr;
375 
376  if (new_freq > 8000000) /* detect invalid data */
377  return;
378 
379  pwr = zalloc(sizeof(*pwr));
380  if (!pwr)
381  return;
382 
383  pwr->state = cpus_pstate_state[cpu];
385  pwr->end_time = timestamp;
386  pwr->cpu = cpu;
387  pwr->type = PSTATE;
388  pwr->next = tchart->power_events;
389 
390  if (!pwr->start_time)
391  pwr->start_time = tchart->first_time;
392 
393  tchart->power_events = pwr;
394 
395  cpus_pstate_state[cpu] = new_freq;
396  cpus_pstate_start_times[cpu] = timestamp;
397 
398  if ((u64)new_freq > tchart->max_freq)
399  tchart->max_freq = new_freq;
400 
401  if (new_freq < tchart->min_freq || tchart->min_freq == 0)
402  tchart->min_freq = new_freq;
403 
404  if (new_freq == tchart->max_freq - 1000)
405  tchart->turbo_frequency = tchart->max_freq;
406 }
407 
408 static void sched_wakeup(struct timechart *tchart, int cpu, u64 timestamp,
409  int waker, int wakee, u8 flags, const char *backtrace)
410 {
411  struct per_pid *p;
412  struct wake_event *we = zalloc(sizeof(*we));
413 
414  if (!we)
415  return;
416 
417  we->time = timestamp;
418  we->waker = waker;
419  we->backtrace = backtrace;
420 
421  if ((flags & TRACE_FLAG_HARDIRQ) || (flags & TRACE_FLAG_SOFTIRQ))
422  we->waker = -1;
423 
424  we->wakee = wakee;
425  we->next = tchart->wake_events;
426  tchart->wake_events = we;
427  p = find_create_pid(tchart, we->wakee);
428 
429  if (p && p->current && p->current->state == TYPE_NONE) {
430  p->current->state_since = timestamp;
431  p->current->state = TYPE_WAITING;
432  }
433  if (p && p->current && p->current->state == TYPE_BLOCKED) {
434  pid_put_sample(tchart, p->pid, p->current->state, cpu,
435  p->current->state_since, timestamp, NULL);
436  p->current->state_since = timestamp;
437  p->current->state = TYPE_WAITING;
438  }
439 }
440 
441 static void sched_switch(struct timechart *tchart, int cpu, u64 timestamp,
442  int prev_pid, int next_pid, u64 prev_state,
443  const char *backtrace)
444 {
445  struct per_pid *p = NULL, *prev_p;
446 
447  prev_p = find_create_pid(tchart, prev_pid);
448 
449  p = find_create_pid(tchart, next_pid);
450 
451  if (prev_p->current && prev_p->current->state != TYPE_NONE)
452  pid_put_sample(tchart, prev_pid, TYPE_RUNNING, cpu,
453  prev_p->current->state_since, timestamp,
454  backtrace);
455  if (p && p->current) {
456  if (p->current->state != TYPE_NONE)
457  pid_put_sample(tchart, next_pid, p->current->state, cpu,
458  p->current->state_since, timestamp,
459  backtrace);
460 
461  p->current->state_since = timestamp;
462  p->current->state = TYPE_RUNNING;
463  }
464 
465  if (prev_p->current) {
466  prev_p->current->state = TYPE_NONE;
467  prev_p->current->state_since = timestamp;
468  if (prev_state & 2)
469  prev_p->current->state = TYPE_BLOCKED;
470  if (prev_state == 0)
471  prev_p->current->state = TYPE_WAITING;
472  }
473 }
474 
475 static const char *cat_backtrace(union perf_event *event,
476  struct perf_sample *sample,
477  struct machine *machine)
478 {
479  struct addr_location al;
480  unsigned int i;
481  char *p = NULL;
482  size_t p_len;
483  u8 cpumode = PERF_RECORD_MISC_USER;
484  struct addr_location tal;
485  struct ip_callchain *chain = sample->callchain;
486  FILE *f = open_memstream(&p, &p_len);
487 
488  if (!f) {
489  perror("open_memstream error");
490  return NULL;
491  }
492 
493  if (!chain)
494  goto exit;
495 
496  if (machine__resolve(machine, &al, sample) < 0) {
497  fprintf(stderr, "problem processing %d event, skipping it.\n",
498  event->header.type);
499  goto exit;
500  }
501 
502  for (i = 0; i < chain->nr; i++) {
503  u64 ip;
504 
506  ip = chain->ips[i];
507  else
508  ip = chain->ips[chain->nr - i - 1];
509 
510  if (ip >= PERF_CONTEXT_MAX) {
511  switch (ip) {
512  case PERF_CONTEXT_HV:
513  cpumode = PERF_RECORD_MISC_HYPERVISOR;
514  break;
515  case PERF_CONTEXT_KERNEL:
516  cpumode = PERF_RECORD_MISC_KERNEL;
517  break;
518  case PERF_CONTEXT_USER:
519  cpumode = PERF_RECORD_MISC_USER;
520  break;
521  default:
522  pr_debug("invalid callchain context: "
523  "%"PRId64"\n", (s64) ip);
524 
525  /*
526  * It seems the callchain is corrupted.
527  * Discard all.
528  */
529  zfree(&p);
530  goto exit_put;
531  }
532  continue;
533  }
534 
535  tal.filtered = 0;
536  if (thread__find_symbol(al.thread, cpumode, ip, &tal))
537  fprintf(f, "..... %016" PRIx64 " %s\n", ip, tal.sym->name);
538  else
539  fprintf(f, "..... %016" PRIx64 "\n", ip);
540  }
541 exit_put:
542  addr_location__put(&al);
543 exit:
544  fclose(f);
545 
546  return p;
547 }
548 
549 typedef int (*tracepoint_handler)(struct timechart *tchart,
550  struct perf_evsel *evsel,
551  struct perf_sample *sample,
552  const char *backtrace);
553 
555  union perf_event *event,
556  struct perf_sample *sample,
557  struct perf_evsel *evsel,
558  struct machine *machine)
559 {
560  struct timechart *tchart = container_of(tool, struct timechart, tool);
561 
562  if (evsel->attr.sample_type & PERF_SAMPLE_TIME) {
563  if (!tchart->first_time || tchart->first_time > sample->time)
564  tchart->first_time = sample->time;
565  if (tchart->last_time < sample->time)
566  tchart->last_time = sample->time;
567  }
568 
569  if (evsel->handler != NULL) {
570  tracepoint_handler f = evsel->handler;
571  return f(tchart, evsel, sample,
572  cat_backtrace(event, sample, machine));
573  }
574 
575  return 0;
576 }
577 
578 static int
579 process_sample_cpu_idle(struct timechart *tchart __maybe_unused,
580  struct perf_evsel *evsel,
581  struct perf_sample *sample,
582  const char *backtrace __maybe_unused)
583 {
584  u32 state = perf_evsel__intval(evsel, sample, "state");
585  u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
586 
587  if (state == (u32)PWR_EVENT_EXIT)
588  c_state_end(tchart, cpu_id, sample->time);
589  else
590  c_state_start(cpu_id, sample->time, state);
591  return 0;
592 }
593 
594 static int
596  struct perf_evsel *evsel,
597  struct perf_sample *sample,
598  const char *backtrace __maybe_unused)
599 {
600  u32 state = perf_evsel__intval(evsel, sample, "state");
601  u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
602 
603  p_state_change(tchart, cpu_id, sample->time, state);
604  return 0;
605 }
606 
607 static int
609  struct perf_evsel *evsel,
610  struct perf_sample *sample,
611  const char *backtrace)
612 {
613  u8 flags = perf_evsel__intval(evsel, sample, "common_flags");
614  int waker = perf_evsel__intval(evsel, sample, "common_pid");
615  int wakee = perf_evsel__intval(evsel, sample, "pid");
616 
617  sched_wakeup(tchart, sample->cpu, sample->time, waker, wakee, flags, backtrace);
618  return 0;
619 }
620 
621 static int
623  struct perf_evsel *evsel,
624  struct perf_sample *sample,
625  const char *backtrace)
626 {
627  int prev_pid = perf_evsel__intval(evsel, sample, "prev_pid");
628  int next_pid = perf_evsel__intval(evsel, sample, "next_pid");
629  u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state");
630 
631  sched_switch(tchart, sample->cpu, sample->time, prev_pid, next_pid,
632  prev_state, backtrace);
633  return 0;
634 }
635 
636 #ifdef SUPPORT_OLD_POWER_EVENTS
637 static int
638 process_sample_power_start(struct timechart *tchart __maybe_unused,
639  struct perf_evsel *evsel,
640  struct perf_sample *sample,
641  const char *backtrace __maybe_unused)
642 {
643  u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
644  u64 value = perf_evsel__intval(evsel, sample, "value");
645 
646  c_state_start(cpu_id, sample->time, value);
647  return 0;
648 }
649 
650 static int
652  struct perf_evsel *evsel __maybe_unused,
653  struct perf_sample *sample,
654  const char *backtrace __maybe_unused)
655 {
656  c_state_end(tchart, sample->cpu, sample->time);
657  return 0;
658 }
659 
660 static int
662  struct perf_evsel *evsel,
663  struct perf_sample *sample,
664  const char *backtrace __maybe_unused)
665 {
666  u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
667  u64 value = perf_evsel__intval(evsel, sample, "value");
668 
669  p_state_change(tchart, cpu_id, sample->time, value);
670  return 0;
671 }
672 #endif /* SUPPORT_OLD_POWER_EVENTS */
673 
674 /*
675  * After the last sample we need to wrap up the current C/P state
676  * and close out each CPU for these.
677  */
678 static void end_sample_processing(struct timechart *tchart)
679 {
680  u64 cpu;
681  struct power_event *pwr;
682 
683  for (cpu = 0; cpu <= tchart->numcpus; cpu++) {
684  /* C state */
685 #if 0
686  pwr = zalloc(sizeof(*pwr));
687  if (!pwr)
688  return;
689 
690  pwr->state = cpus_cstate_state[cpu];
692  pwr->end_time = tchart->last_time;
693  pwr->cpu = cpu;
694  pwr->type = CSTATE;
695  pwr->next = tchart->power_events;
696 
697  tchart->power_events = pwr;
698 #endif
699  /* P state */
700 
701  pwr = zalloc(sizeof(*pwr));
702  if (!pwr)
703  return;
704 
705  pwr->state = cpus_pstate_state[cpu];
707  pwr->end_time = tchart->last_time;
708  pwr->cpu = cpu;
709  pwr->type = PSTATE;
710  pwr->next = tchart->power_events;
711 
712  if (!pwr->start_time)
713  pwr->start_time = tchart->first_time;
714  if (!pwr->state)
715  pwr->state = tchart->min_freq;
716  tchart->power_events = pwr;
717  }
718 }
719 
720 static int pid_begin_io_sample(struct timechart *tchart, int pid, int type,
721  u64 start, int fd)
722 {
723  struct per_pid *p = find_create_pid(tchart, pid);
724  struct per_pidcomm *c = p->current;
725  struct io_sample *sample;
726  struct io_sample *prev;
727 
728  if (!c) {
729  c = zalloc(sizeof(*c));
730  if (!c)
731  return -ENOMEM;
732  p->current = c;
733  c->next = p->all;
734  p->all = c;
735  }
736 
737  prev = c->io_samples;
738 
739  if (prev && prev->start_time && !prev->end_time) {
740  pr_warning("Skip invalid start event: "
741  "previous event already started!\n");
742 
743  /* remove previous event that has been started,
744  * we are not sure we will ever get an end for it */
745  c->io_samples = prev->next;
746  free(prev);
747  return 0;
748  }
749 
750  sample = zalloc(sizeof(*sample));
751  if (!sample)
752  return -ENOMEM;
753  sample->start_time = start;
754  sample->type = type;
755  sample->fd = fd;
756  sample->next = c->io_samples;
757  c->io_samples = sample;
758 
759  if (c->start_time == 0 || c->start_time > start)
760  c->start_time = start;
761 
762  return 0;
763 }
764 
765 static int pid_end_io_sample(struct timechart *tchart, int pid, int type,
766  u64 end, long ret)
767 {
768  struct per_pid *p = find_create_pid(tchart, pid);
769  struct per_pidcomm *c = p->current;
770  struct io_sample *sample, *prev;
771 
772  if (!c) {
773  pr_warning("Invalid pidcomm!\n");
774  return -1;
775  }
776 
777  sample = c->io_samples;
778 
779  if (!sample) /* skip partially captured events */
780  return 0;
781 
782  if (sample->end_time) {
783  pr_warning("Skip invalid end event: "
784  "previous event already ended!\n");
785  return 0;
786  }
787 
788  if (sample->type != type) {
789  pr_warning("Skip invalid end event: invalid event type!\n");
790  return 0;
791  }
792 
793  sample->end_time = end;
794  prev = sample->next;
795 
796  /* we want to be able to see small and fast transfers, so make them
797  * at least min_time long, but don't overlap them */
798  if (sample->end_time - sample->start_time < tchart->min_time)
799  sample->end_time = sample->start_time + tchart->min_time;
800  if (prev && sample->start_time < prev->end_time) {
801  if (prev->err) /* try to make errors more visible */
802  sample->start_time = prev->end_time;
803  else
804  prev->end_time = sample->start_time;
805  }
806 
807  if (ret < 0) {
808  sample->err = ret;
809  } else if (type == IOTYPE_READ || type == IOTYPE_WRITE ||
810  type == IOTYPE_TX || type == IOTYPE_RX) {
811 
812  if ((u64)ret > c->max_bytes)
813  c->max_bytes = ret;
814 
815  c->total_bytes += ret;
816  p->total_bytes += ret;
817  sample->bytes = ret;
818  }
819 
820  /* merge two requests to make svg smaller and render-friendly */
821  if (prev &&
822  prev->type == sample->type &&
823  prev->err == sample->err &&
824  prev->fd == sample->fd &&
825  prev->end_time + tchart->merge_dist >= sample->start_time) {
826 
827  sample->bytes += prev->bytes;
828  sample->merges += prev->merges + 1;
829 
830  sample->start_time = prev->start_time;
831  sample->next = prev->next;
832  free(prev);
833 
834  if (!sample->err && sample->bytes > c->max_bytes)
835  c->max_bytes = sample->bytes;
836  }
837 
838  tchart->io_events++;
839 
840  return 0;
841 }
842 
843 static int
845  struct perf_evsel *evsel,
846  struct perf_sample *sample)
847 {
848  long fd = perf_evsel__intval(evsel, sample, "fd");
849  return pid_begin_io_sample(tchart, sample->tid, IOTYPE_READ,
850  sample->time, fd);
851 }
852 
853 static int
855  struct perf_evsel *evsel,
856  struct perf_sample *sample)
857 {
858  long ret = perf_evsel__intval(evsel, sample, "ret");
859  return pid_end_io_sample(tchart, sample->tid, IOTYPE_READ,
860  sample->time, ret);
861 }
862 
863 static int
865  struct perf_evsel *evsel,
866  struct perf_sample *sample)
867 {
868  long fd = perf_evsel__intval(evsel, sample, "fd");
869  return pid_begin_io_sample(tchart, sample->tid, IOTYPE_WRITE,
870  sample->time, fd);
871 }
872 
873 static int
875  struct perf_evsel *evsel,
876  struct perf_sample *sample)
877 {
878  long ret = perf_evsel__intval(evsel, sample, "ret");
879  return pid_end_io_sample(tchart, sample->tid, IOTYPE_WRITE,
880  sample->time, ret);
881 }
882 
883 static int
885  struct perf_evsel *evsel,
886  struct perf_sample *sample)
887 {
888  long fd = perf_evsel__intval(evsel, sample, "fd");
889  return pid_begin_io_sample(tchart, sample->tid, IOTYPE_SYNC,
890  sample->time, fd);
891 }
892 
893 static int
895  struct perf_evsel *evsel,
896  struct perf_sample *sample)
897 {
898  long ret = perf_evsel__intval(evsel, sample, "ret");
899  return pid_end_io_sample(tchart, sample->tid, IOTYPE_SYNC,
900  sample->time, ret);
901 }
902 
903 static int
905  struct perf_evsel *evsel,
906  struct perf_sample *sample)
907 {
908  long fd = perf_evsel__intval(evsel, sample, "fd");
909  return pid_begin_io_sample(tchart, sample->tid, IOTYPE_TX,
910  sample->time, fd);
911 }
912 
913 static int
914 process_exit_tx(struct timechart *tchart,
915  struct perf_evsel *evsel,
916  struct perf_sample *sample)
917 {
918  long ret = perf_evsel__intval(evsel, sample, "ret");
919  return pid_end_io_sample(tchart, sample->tid, IOTYPE_TX,
920  sample->time, ret);
921 }
922 
923 static int
925  struct perf_evsel *evsel,
926  struct perf_sample *sample)
927 {
928  long fd = perf_evsel__intval(evsel, sample, "fd");
929  return pid_begin_io_sample(tchart, sample->tid, IOTYPE_RX,
930  sample->time, fd);
931 }
932 
933 static int
934 process_exit_rx(struct timechart *tchart,
935  struct perf_evsel *evsel,
936  struct perf_sample *sample)
937 {
938  long ret = perf_evsel__intval(evsel, sample, "ret");
939  return pid_end_io_sample(tchart, sample->tid, IOTYPE_RX,
940  sample->time, ret);
941 }
942 
943 static int
945  struct perf_evsel *evsel,
946  struct perf_sample *sample)
947 {
948  long fd = perf_evsel__intval(evsel, sample, "fd");
949  return pid_begin_io_sample(tchart, sample->tid, IOTYPE_POLL,
950  sample->time, fd);
951 }
952 
953 static int
955  struct perf_evsel *evsel,
956  struct perf_sample *sample)
957 {
958  long ret = perf_evsel__intval(evsel, sample, "ret");
959  return pid_end_io_sample(tchart, sample->tid, IOTYPE_POLL,
960  sample->time, ret);
961 }
962 
963 /*
964  * Sort the pid datastructure
965  */
966 static void sort_pids(struct timechart *tchart)
967 {
968  struct per_pid *new_list, *p, *cursor, *prev;
969  /* sort by ppid first, then by pid, lowest to highest */
970 
971  new_list = NULL;
972 
973  while (tchart->all_data) {
974  p = tchart->all_data;
975  tchart->all_data = p->next;
976  p->next = NULL;
977 
978  if (new_list == NULL) {
979  new_list = p;
980  p->next = NULL;
981  continue;
982  }
983  prev = NULL;
984  cursor = new_list;
985  while (cursor) {
986  if (cursor->ppid > p->ppid ||
987  (cursor->ppid == p->ppid && cursor->pid > p->pid)) {
988  /* must insert before */
989  if (prev) {
990  p->next = prev->next;
991  prev->next = p;
992  cursor = NULL;
993  continue;
994  } else {
995  p->next = new_list;
996  new_list = p;
997  cursor = NULL;
998  continue;
999  }
1000  }
1001 
1002  prev = cursor;
1003  cursor = cursor->next;
1004  if (!cursor)
1005  prev->next = p;
1006  }
1007  }
1008  tchart->all_data = new_list;
1009 }
1010 
1011 
1012 static void draw_c_p_states(struct timechart *tchart)
1013 {
1014  struct power_event *pwr;
1015  pwr = tchart->power_events;
1016 
1017  /*
1018  * two pass drawing so that the P state bars are on top of the C state blocks
1019  */
1020  while (pwr) {
1021  if (pwr->type == CSTATE)
1022  svg_cstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
1023  pwr = pwr->next;
1024  }
1025 
1026  pwr = tchart->power_events;
1027  while (pwr) {
1028  if (pwr->type == PSTATE) {
1029  if (!pwr->state)
1030  pwr->state = tchart->min_freq;
1031  svg_pstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
1032  }
1033  pwr = pwr->next;
1034  }
1035 }
1036 
1037 static void draw_wakeups(struct timechart *tchart)
1038 {
1039  struct wake_event *we;
1040  struct per_pid *p;
1041  struct per_pidcomm *c;
1042 
1043  we = tchart->wake_events;
1044  while (we) {
1045  int from = 0, to = 0;
1046  char *task_from = NULL, *task_to = NULL;
1047 
1048  /* locate the column of the waker and wakee */
1049  p = tchart->all_data;
1050  while (p) {
1051  if (p->pid == we->waker || p->pid == we->wakee) {
1052  c = p->all;
1053  while (c) {
1054  if (c->Y && c->start_time <= we->time && c->end_time >= we->time) {
1055  if (p->pid == we->waker && !from) {
1056  from = c->Y;
1057  task_from = strdup(c->comm);
1058  }
1059  if (p->pid == we->wakee && !to) {
1060  to = c->Y;
1061  task_to = strdup(c->comm);
1062  }
1063  }
1064  c = c->next;
1065  }
1066  c = p->all;
1067  while (c) {
1068  if (p->pid == we->waker && !from) {
1069  from = c->Y;
1070  task_from = strdup(c->comm);
1071  }
1072  if (p->pid == we->wakee && !to) {
1073  to = c->Y;
1074  task_to = strdup(c->comm);
1075  }
1076  c = c->next;
1077  }
1078  }
1079  p = p->next;
1080  }
1081 
1082  if (!task_from) {
1083  task_from = malloc(40);
1084  sprintf(task_from, "[%i]", we->waker);
1085  }
1086  if (!task_to) {
1087  task_to = malloc(40);
1088  sprintf(task_to, "[%i]", we->wakee);
1089  }
1090 
1091  if (we->waker == -1)
1092  svg_interrupt(we->time, to, we->backtrace);
1093  else if (from && to && abs(from - to) == 1)
1094  svg_wakeline(we->time, from, to, we->backtrace);
1095  else
1096  svg_partial_wakeline(we->time, from, task_from, to,
1097  task_to, we->backtrace);
1098  we = we->next;
1099 
1100  free(task_from);
1101  free(task_to);
1102  }
1103 }
1104 
1105 static void draw_cpu_usage(struct timechart *tchart)
1106 {
1107  struct per_pid *p;
1108  struct per_pidcomm *c;
1109  struct cpu_sample *sample;
1110  p = tchart->all_data;
1111  while (p) {
1112  c = p->all;
1113  while (c) {
1114  sample = c->samples;
1115  while (sample) {
1116  if (sample->type == TYPE_RUNNING) {
1117  svg_process(sample->cpu,
1118  sample->start_time,
1119  sample->end_time,
1120  p->pid,
1121  c->comm,
1122  sample->backtrace);
1123  }
1124 
1125  sample = sample->next;
1126  }
1127  c = c->next;
1128  }
1129  p = p->next;
1130  }
1131 }
1132 
1133 static void draw_io_bars(struct timechart *tchart)
1134 {
1135  const char *suf;
1136  double bytes;
1137  char comm[256];
1138  struct per_pid *p;
1139  struct per_pidcomm *c;
1140  struct io_sample *sample;
1141  int Y = 1;
1142 
1143  p = tchart->all_data;
1144  while (p) {
1145  c = p->all;
1146  while (c) {
1147  if (!c->display) {
1148  c->Y = 0;
1149  c = c->next;
1150  continue;
1151  }
1152 
1153  svg_box(Y, c->start_time, c->end_time, "process3");
1154  sample = c->io_samples;
1155  for (sample = c->io_samples; sample; sample = sample->next) {
1156  double h = (double)sample->bytes / c->max_bytes;
1157 
1158  if (tchart->skip_eagain &&
1159  sample->err == -EAGAIN)
1160  continue;
1161 
1162  if (sample->err)
1163  h = 1;
1164 
1165  if (sample->type == IOTYPE_SYNC)
1166  svg_fbox(Y,
1167  sample->start_time,
1168  sample->end_time,
1169  1,
1170  sample->err ? "error" : "sync",
1171  sample->fd,
1172  sample->err,
1173  sample->merges);
1174  else if (sample->type == IOTYPE_POLL)
1175  svg_fbox(Y,
1176  sample->start_time,
1177  sample->end_time,
1178  1,
1179  sample->err ? "error" : "poll",
1180  sample->fd,
1181  sample->err,
1182  sample->merges);
1183  else if (sample->type == IOTYPE_READ)
1184  svg_ubox(Y,
1185  sample->start_time,
1186  sample->end_time,
1187  h,
1188  sample->err ? "error" : "disk",
1189  sample->fd,
1190  sample->err,
1191  sample->merges);
1192  else if (sample->type == IOTYPE_WRITE)
1193  svg_lbox(Y,
1194  sample->start_time,
1195  sample->end_time,
1196  h,
1197  sample->err ? "error" : "disk",
1198  sample->fd,
1199  sample->err,
1200  sample->merges);
1201  else if (sample->type == IOTYPE_RX)
1202  svg_ubox(Y,
1203  sample->start_time,
1204  sample->end_time,
1205  h,
1206  sample->err ? "error" : "net",
1207  sample->fd,
1208  sample->err,
1209  sample->merges);
1210  else if (sample->type == IOTYPE_TX)
1211  svg_lbox(Y,
1212  sample->start_time,
1213  sample->end_time,
1214  h,
1215  sample->err ? "error" : "net",
1216  sample->fd,
1217  sample->err,
1218  sample->merges);
1219  }
1220 
1221  suf = "";
1222  bytes = c->total_bytes;
1223  if (bytes > 1024) {
1224  bytes = bytes / 1024;
1225  suf = "K";
1226  }
1227  if (bytes > 1024) {
1228  bytes = bytes / 1024;
1229  suf = "M";
1230  }
1231  if (bytes > 1024) {
1232  bytes = bytes / 1024;
1233  suf = "G";
1234  }
1235 
1236 
1237  sprintf(comm, "%s:%i (%3.1f %sbytes)", c->comm ?: "", p->pid, bytes, suf);
1238  svg_text(Y, c->start_time, comm);
1239 
1240  c->Y = Y;
1241  Y++;
1242  c = c->next;
1243  }
1244  p = p->next;
1245  }
1246 }
1247 
1248 static void draw_process_bars(struct timechart *tchart)
1249 {
1250  struct per_pid *p;
1251  struct per_pidcomm *c;
1252  struct cpu_sample *sample;
1253  int Y = 0;
1254 
1255  Y = 2 * tchart->numcpus + 2;
1256 
1257  p = tchart->all_data;
1258  while (p) {
1259  c = p->all;
1260  while (c) {
1261  if (!c->display) {
1262  c->Y = 0;
1263  c = c->next;
1264  continue;
1265  }
1266 
1267  svg_box(Y, c->start_time, c->end_time, "process");
1268  sample = c->samples;
1269  while (sample) {
1270  if (sample->type == TYPE_RUNNING)
1271  svg_running(Y, sample->cpu,
1272  sample->start_time,
1273  sample->end_time,
1274  sample->backtrace);
1275  if (sample->type == TYPE_BLOCKED)
1276  svg_blocked(Y, sample->cpu,
1277  sample->start_time,
1278  sample->end_time,
1279  sample->backtrace);
1280  if (sample->type == TYPE_WAITING)
1281  svg_waiting(Y, sample->cpu,
1282  sample->start_time,
1283  sample->end_time,
1284  sample->backtrace);
1285  sample = sample->next;
1286  }
1287 
1288  if (c->comm) {
1289  char comm[256];
1290  if (c->total_time > 5000000000) /* 5 seconds */
1291  sprintf(comm, "%s:%i (%2.2fs)", c->comm, p->pid, c->total_time / (double)NSEC_PER_SEC);
1292  else
1293  sprintf(comm, "%s:%i (%3.1fms)", c->comm, p->pid, c->total_time / (double)NSEC_PER_MSEC);
1294 
1295  svg_text(Y, c->start_time, comm);
1296  }
1297  c->Y = Y;
1298  Y++;
1299  c = c->next;
1300  }
1301  p = p->next;
1302  }
1303 }
1304 
1305 static void add_process_filter(const char *string)
1306 {
1307  int pid = strtoull(string, NULL, 10);
1308  struct process_filter *filt = malloc(sizeof(*filt));
1309 
1310  if (!filt)
1311  return;
1312 
1313  filt->name = strdup(string);
1314  filt->pid = pid;
1315  filt->next = process_filter;
1316 
1317  process_filter = filt;
1318 }
1319 
1320 static int passes_filter(struct per_pid *p, struct per_pidcomm *c)
1321 {
1322  struct process_filter *filt;
1323  if (!process_filter)
1324  return 1;
1325 
1326  filt = process_filter;
1327  while (filt) {
1328  if (filt->pid && p->pid == filt->pid)
1329  return 1;
1330  if (strcmp(filt->name, c->comm) == 0)
1331  return 1;
1332  filt = filt->next;
1333  }
1334  return 0;
1335 }
1336 
1338 {
1339  struct per_pid *p;
1340  struct per_pidcomm *c;
1341  int count = 0;
1342 
1343  p = tchart->all_data;
1344  while (p) {
1345  p->display = 0;
1346  if (p->start_time == 1)
1347  p->start_time = tchart->first_time;
1348 
1349  /* no exit marker, task kept running to the end */
1350  if (p->end_time == 0)
1351  p->end_time = tchart->last_time;
1352 
1353  c = p->all;
1354 
1355  while (c) {
1356  c->display = 0;
1357 
1358  if (c->start_time == 1)
1359  c->start_time = tchart->first_time;
1360 
1361  if (passes_filter(p, c)) {
1362  c->display = 1;
1363  p->display = 1;
1364  count++;
1365  }
1366 
1367  if (c->end_time == 0)
1368  c->end_time = tchart->last_time;
1369 
1370  c = c->next;
1371  }
1372  p = p->next;
1373  }
1374  return count;
1375 }
1376 
1377 static int determine_display_tasks(struct timechart *tchart, u64 threshold)
1378 {
1379  struct per_pid *p;
1380  struct per_pidcomm *c;
1381  int count = 0;
1382 
1383  p = tchart->all_data;
1384  while (p) {
1385  p->display = 0;
1386  if (p->start_time == 1)
1387  p->start_time = tchart->first_time;
1388 
1389  /* no exit marker, task kept running to the end */
1390  if (p->end_time == 0)
1391  p->end_time = tchart->last_time;
1392  if (p->total_time >= threshold)
1393  p->display = 1;
1394 
1395  c = p->all;
1396 
1397  while (c) {
1398  c->display = 0;
1399 
1400  if (c->start_time == 1)
1401  c->start_time = tchart->first_time;
1402 
1403  if (c->total_time >= threshold) {
1404  c->display = 1;
1405  count++;
1406  }
1407 
1408  if (c->end_time == 0)
1409  c->end_time = tchart->last_time;
1410 
1411  c = c->next;
1412  }
1413  p = p->next;
1414  }
1415  return count;
1416 }
1417 
1418 static int determine_display_io_tasks(struct timechart *timechart, u64 threshold)
1419 {
1420  struct per_pid *p;
1421  struct per_pidcomm *c;
1422  int count = 0;
1423 
1424  p = timechart->all_data;
1425  while (p) {
1426  /* no exit marker, task kept running to the end */
1427  if (p->end_time == 0)
1428  p->end_time = timechart->last_time;
1429 
1430  c = p->all;
1431 
1432  while (c) {
1433  c->display = 0;
1434 
1435  if (c->total_bytes >= threshold) {
1436  c->display = 1;
1437  count++;
1438  }
1439 
1440  if (c->end_time == 0)
1441  c->end_time = timechart->last_time;
1442 
1443  c = c->next;
1444  }
1445  p = p->next;
1446  }
1447  return count;
1448 }
1449 
1450 #define BYTES_THRESH (1 * 1024 * 1024)
1451 #define TIME_THRESH 10000000
1452 
1453 static void write_svg_file(struct timechart *tchart, const char *filename)
1454 {
1455  u64 i;
1456  int count;
1457  int thresh = tchart->io_events ? BYTES_THRESH : TIME_THRESH;
1458 
1459  if (tchart->power_only)
1460  tchart->proc_num = 0;
1461 
1462  /* We'd like to show at least proc_num tasks;
1463  * be less picky if we have fewer */
1464  do {
1465  if (process_filter)
1466  count = determine_display_tasks_filtered(tchart);
1467  else if (tchart->io_events)
1468  count = determine_display_io_tasks(tchart, thresh);
1469  else
1470  count = determine_display_tasks(tchart, thresh);
1471  thresh /= 10;
1472  } while (!process_filter && thresh && count < tchart->proc_num);
1473 
1474  if (!tchart->proc_num)
1475  count = 0;
1476 
1477  if (tchart->io_events) {
1478  open_svg(filename, 0, count, tchart->first_time, tchart->last_time);
1479 
1480  svg_time_grid(0.5);
1481  svg_io_legenda();
1482 
1483  draw_io_bars(tchart);
1484  } else {
1485  open_svg(filename, tchart->numcpus, count, tchart->first_time, tchart->last_time);
1486 
1487  svg_time_grid(0);
1488 
1489  svg_legenda();
1490 
1491  for (i = 0; i < tchart->numcpus; i++)
1492  svg_cpu_box(i, tchart->max_freq, tchart->turbo_frequency);
1493 
1494  draw_cpu_usage(tchart);
1495  if (tchart->proc_num)
1496  draw_process_bars(tchart);
1497  if (!tchart->tasks_only)
1498  draw_c_p_states(tchart);
1499  if (tchart->proc_num)
1500  draw_wakeups(tchart);
1501  }
1502 
1503  svg_close();
1504 }
1505 
1506 static int process_header(struct perf_file_section *section __maybe_unused,
1507  struct perf_header *ph,
1508  int feat,
1509  int fd __maybe_unused,
1510  void *data)
1511 {
1512  struct timechart *tchart = data;
1513 
1514  switch (feat) {
1515  case HEADER_NRCPUS:
1516  tchart->numcpus = ph->env.nr_cpus_avail;
1517  break;
1518 
1519  case HEADER_CPU_TOPOLOGY:
1520  if (!tchart->topology)
1521  break;
1522 
1524  ph->env.nr_sibling_cores,
1525  ph->env.sibling_threads,
1526  ph->env.nr_sibling_threads))
1527  fprintf(stderr, "problem building topology\n");
1528  break;
1529 
1530  default:
1531  break;
1532  }
1533 
1534  return 0;
1535 }
1536 
1537 static int __cmd_timechart(struct timechart *tchart, const char *output_name)
1538 {
1539  const struct perf_evsel_str_handler power_tracepoints[] = {
1540  { "power:cpu_idle", process_sample_cpu_idle },
1541  { "power:cpu_frequency", process_sample_cpu_frequency },
1542  { "sched:sched_wakeup", process_sample_sched_wakeup },
1543  { "sched:sched_switch", process_sample_sched_switch },
1544 #ifdef SUPPORT_OLD_POWER_EVENTS
1545  { "power:power_start", process_sample_power_start },
1546  { "power:power_end", process_sample_power_end },
1547  { "power:power_frequency", process_sample_power_frequency },
1548 #endif
1549 
1550  { "syscalls:sys_enter_read", process_enter_read },
1551  { "syscalls:sys_enter_pread64", process_enter_read },
1552  { "syscalls:sys_enter_readv", process_enter_read },
1553  { "syscalls:sys_enter_preadv", process_enter_read },
1554  { "syscalls:sys_enter_write", process_enter_write },
1555  { "syscalls:sys_enter_pwrite64", process_enter_write },
1556  { "syscalls:sys_enter_writev", process_enter_write },
1557  { "syscalls:sys_enter_pwritev", process_enter_write },
1558  { "syscalls:sys_enter_sync", process_enter_sync },
1559  { "syscalls:sys_enter_sync_file_range", process_enter_sync },
1560  { "syscalls:sys_enter_fsync", process_enter_sync },
1561  { "syscalls:sys_enter_msync", process_enter_sync },
1562  { "syscalls:sys_enter_recvfrom", process_enter_rx },
1563  { "syscalls:sys_enter_recvmmsg", process_enter_rx },
1564  { "syscalls:sys_enter_recvmsg", process_enter_rx },
1565  { "syscalls:sys_enter_sendto", process_enter_tx },
1566  { "syscalls:sys_enter_sendmsg", process_enter_tx },
1567  { "syscalls:sys_enter_sendmmsg", process_enter_tx },
1568  { "syscalls:sys_enter_epoll_pwait", process_enter_poll },
1569  { "syscalls:sys_enter_epoll_wait", process_enter_poll },
1570  { "syscalls:sys_enter_poll", process_enter_poll },
1571  { "syscalls:sys_enter_ppoll", process_enter_poll },
1572  { "syscalls:sys_enter_pselect6", process_enter_poll },
1573  { "syscalls:sys_enter_select", process_enter_poll },
1574 
1575  { "syscalls:sys_exit_read", process_exit_read },
1576  { "syscalls:sys_exit_pread64", process_exit_read },
1577  { "syscalls:sys_exit_readv", process_exit_read },
1578  { "syscalls:sys_exit_preadv", process_exit_read },
1579  { "syscalls:sys_exit_write", process_exit_write },
1580  { "syscalls:sys_exit_pwrite64", process_exit_write },
1581  { "syscalls:sys_exit_writev", process_exit_write },
1582  { "syscalls:sys_exit_pwritev", process_exit_write },
1583  { "syscalls:sys_exit_sync", process_exit_sync },
1584  { "syscalls:sys_exit_sync_file_range", process_exit_sync },
1585  { "syscalls:sys_exit_fsync", process_exit_sync },
1586  { "syscalls:sys_exit_msync", process_exit_sync },
1587  { "syscalls:sys_exit_recvfrom", process_exit_rx },
1588  { "syscalls:sys_exit_recvmmsg", process_exit_rx },
1589  { "syscalls:sys_exit_recvmsg", process_exit_rx },
1590  { "syscalls:sys_exit_sendto", process_exit_tx },
1591  { "syscalls:sys_exit_sendmsg", process_exit_tx },
1592  { "syscalls:sys_exit_sendmmsg", process_exit_tx },
1593  { "syscalls:sys_exit_epoll_pwait", process_exit_poll },
1594  { "syscalls:sys_exit_epoll_wait", process_exit_poll },
1595  { "syscalls:sys_exit_poll", process_exit_poll },
1596  { "syscalls:sys_exit_ppoll", process_exit_poll },
1597  { "syscalls:sys_exit_pselect6", process_exit_poll },
1598  { "syscalls:sys_exit_select", process_exit_poll },
1599  };
1600  struct perf_data data = {
1601  .file = {
1602  .path = input_name,
1603  },
1604  .mode = PERF_DATA_MODE_READ,
1605  .force = tchart->force,
1606  };
1607 
1608  struct perf_session *session = perf_session__new(&data, false,
1609  &tchart->tool);
1610  int ret = -EINVAL;
1611 
1612  if (session == NULL)
1613  return -1;
1614 
1615  symbol__init(&session->header.env);
1616 
1617  (void)perf_header__process_sections(&session->header,
1618  perf_data__fd(session->data),
1619  tchart,
1620  process_header);
1621 
1622  if (!perf_session__has_traces(session, "timechart record"))
1623  goto out_delete;
1624 
1626  power_tracepoints)) {
1627  pr_err("Initializing session tracepoint handlers failed\n");
1628  goto out_delete;
1629  }
1630 
1631  ret = perf_session__process_events(session);
1632  if (ret)
1633  goto out_delete;
1634 
1635  end_sample_processing(tchart);
1636 
1637  sort_pids(tchart);
1638 
1639  write_svg_file(tchart, output_name);
1640 
1641  pr_info("Written %2.1f seconds of trace to %s.\n",
1642  (tchart->last_time - tchart->first_time) / (double)NSEC_PER_SEC, output_name);
1643 out_delete:
1644  perf_session__delete(session);
1645  return ret;
1646 }
1647 
1648 static int timechart__io_record(int argc, const char **argv)
1649 {
1650  unsigned int rec_argc, i;
1651  const char **rec_argv;
1652  const char **p;
1653  char *filter = NULL;
1654 
1655  const char * const common_args[] = {
1656  "record", "-a", "-R", "-c", "1",
1657  };
1658  unsigned int common_args_nr = ARRAY_SIZE(common_args);
1659 
1660  const char * const disk_events[] = {
1661  "syscalls:sys_enter_read",
1662  "syscalls:sys_enter_pread64",
1663  "syscalls:sys_enter_readv",
1664  "syscalls:sys_enter_preadv",
1665  "syscalls:sys_enter_write",
1666  "syscalls:sys_enter_pwrite64",
1667  "syscalls:sys_enter_writev",
1668  "syscalls:sys_enter_pwritev",
1669  "syscalls:sys_enter_sync",
1670  "syscalls:sys_enter_sync_file_range",
1671  "syscalls:sys_enter_fsync",
1672  "syscalls:sys_enter_msync",
1673 
1674  "syscalls:sys_exit_read",
1675  "syscalls:sys_exit_pread64",
1676  "syscalls:sys_exit_readv",
1677  "syscalls:sys_exit_preadv",
1678  "syscalls:sys_exit_write",
1679  "syscalls:sys_exit_pwrite64",
1680  "syscalls:sys_exit_writev",
1681  "syscalls:sys_exit_pwritev",
1682  "syscalls:sys_exit_sync",
1683  "syscalls:sys_exit_sync_file_range",
1684  "syscalls:sys_exit_fsync",
1685  "syscalls:sys_exit_msync",
1686  };
1687  unsigned int disk_events_nr = ARRAY_SIZE(disk_events);
1688 
1689  const char * const net_events[] = {
1690  "syscalls:sys_enter_recvfrom",
1691  "syscalls:sys_enter_recvmmsg",
1692  "syscalls:sys_enter_recvmsg",
1693  "syscalls:sys_enter_sendto",
1694  "syscalls:sys_enter_sendmsg",
1695  "syscalls:sys_enter_sendmmsg",
1696 
1697  "syscalls:sys_exit_recvfrom",
1698  "syscalls:sys_exit_recvmmsg",
1699  "syscalls:sys_exit_recvmsg",
1700  "syscalls:sys_exit_sendto",
1701  "syscalls:sys_exit_sendmsg",
1702  "syscalls:sys_exit_sendmmsg",
1703  };
1704  unsigned int net_events_nr = ARRAY_SIZE(net_events);
1705 
1706  const char * const poll_events[] = {
1707  "syscalls:sys_enter_epoll_pwait",
1708  "syscalls:sys_enter_epoll_wait",
1709  "syscalls:sys_enter_poll",
1710  "syscalls:sys_enter_ppoll",
1711  "syscalls:sys_enter_pselect6",
1712  "syscalls:sys_enter_select",
1713 
1714  "syscalls:sys_exit_epoll_pwait",
1715  "syscalls:sys_exit_epoll_wait",
1716  "syscalls:sys_exit_poll",
1717  "syscalls:sys_exit_ppoll",
1718  "syscalls:sys_exit_pselect6",
1719  "syscalls:sys_exit_select",
1720  };
1721  unsigned int poll_events_nr = ARRAY_SIZE(poll_events);
1722 
1723  rec_argc = common_args_nr +
1724  disk_events_nr * 4 +
1725  net_events_nr * 4 +
1726  poll_events_nr * 4 +
1727  argc;
1728  rec_argv = calloc(rec_argc + 1, sizeof(char *));
1729 
1730  if (rec_argv == NULL)
1731  return -ENOMEM;
1732 
1733  if (asprintf(&filter, "common_pid != %d", getpid()) < 0) {
1734  free(rec_argv);
1735  return -ENOMEM;
1736  }
1737 
1738  p = rec_argv;
1739  for (i = 0; i < common_args_nr; i++)
1740  *p++ = strdup(common_args[i]);
1741 
1742  for (i = 0; i < disk_events_nr; i++) {
1743  if (!is_valid_tracepoint(disk_events[i])) {
1744  rec_argc -= 4;
1745  continue;
1746  }
1747 
1748  *p++ = "-e";
1749  *p++ = strdup(disk_events[i]);
1750  *p++ = "--filter";
1751  *p++ = filter;
1752  }
1753  for (i = 0; i < net_events_nr; i++) {
1754  if (!is_valid_tracepoint(net_events[i])) {
1755  rec_argc -= 4;
1756  continue;
1757  }
1758 
1759  *p++ = "-e";
1760  *p++ = strdup(net_events[i]);
1761  *p++ = "--filter";
1762  *p++ = filter;
1763  }
1764  for (i = 0; i < poll_events_nr; i++) {
1765  if (!is_valid_tracepoint(poll_events[i])) {
1766  rec_argc -= 4;
1767  continue;
1768  }
1769 
1770  *p++ = "-e";
1771  *p++ = strdup(poll_events[i]);
1772  *p++ = "--filter";
1773  *p++ = filter;
1774  }
1775 
1776  for (i = 0; i < (unsigned int)argc; i++)
1777  *p++ = argv[i];
1778 
1779  return cmd_record(rec_argc, rec_argv);
1780 }
1781 
1782 
1783 static int timechart__record(struct timechart *tchart, int argc, const char **argv)
1784 {
1785  unsigned int rec_argc, i, j;
1786  const char **rec_argv;
1787  const char **p;
1788  unsigned int record_elems;
1789 
1790  const char * const common_args[] = {
1791  "record", "-a", "-R", "-c", "1",
1792  };
1793  unsigned int common_args_nr = ARRAY_SIZE(common_args);
1794 
1795  const char * const backtrace_args[] = {
1796  "-g",
1797  };
1798  unsigned int backtrace_args_no = ARRAY_SIZE(backtrace_args);
1799 
1800  const char * const power_args[] = {
1801  "-e", "power:cpu_frequency",
1802  "-e", "power:cpu_idle",
1803  };
1804  unsigned int power_args_nr = ARRAY_SIZE(power_args);
1805 
1806  const char * const old_power_args[] = {
1807 #ifdef SUPPORT_OLD_POWER_EVENTS
1808  "-e", "power:power_start",
1809  "-e", "power:power_end",
1810  "-e", "power:power_frequency",
1811 #endif
1812  };
1813  unsigned int old_power_args_nr = ARRAY_SIZE(old_power_args);
1814 
1815  const char * const tasks_args[] = {
1816  "-e", "sched:sched_wakeup",
1817  "-e", "sched:sched_switch",
1818  };
1819  unsigned int tasks_args_nr = ARRAY_SIZE(tasks_args);
1820 
1821 #ifdef SUPPORT_OLD_POWER_EVENTS
1822  if (!is_valid_tracepoint("power:cpu_idle") &&
1823  is_valid_tracepoint("power:power_start")) {
1825  power_args_nr = 0;
1826  } else {
1827  old_power_args_nr = 0;
1828  }
1829 #endif
1830 
1831  if (tchart->power_only)
1832  tasks_args_nr = 0;
1833 
1834  if (tchart->tasks_only) {
1835  power_args_nr = 0;
1836  old_power_args_nr = 0;
1837  }
1838 
1839  if (!tchart->with_backtrace)
1840  backtrace_args_no = 0;
1841 
1842  record_elems = common_args_nr + tasks_args_nr +
1843  power_args_nr + old_power_args_nr + backtrace_args_no;
1844 
1845  rec_argc = record_elems + argc;
1846  rec_argv = calloc(rec_argc + 1, sizeof(char *));
1847 
1848  if (rec_argv == NULL)
1849  return -ENOMEM;
1850 
1851  p = rec_argv;
1852  for (i = 0; i < common_args_nr; i++)
1853  *p++ = strdup(common_args[i]);
1854 
1855  for (i = 0; i < backtrace_args_no; i++)
1856  *p++ = strdup(backtrace_args[i]);
1857 
1858  for (i = 0; i < tasks_args_nr; i++)
1859  *p++ = strdup(tasks_args[i]);
1860 
1861  for (i = 0; i < power_args_nr; i++)
1862  *p++ = strdup(power_args[i]);
1863 
1864  for (i = 0; i < old_power_args_nr; i++)
1865  *p++ = strdup(old_power_args[i]);
1866 
1867  for (j = 0; j < (unsigned int)argc; j++)
1868  *p++ = argv[j];
1869 
1870  return cmd_record(rec_argc, rec_argv);
1871 }
1872 
1873 static int
1874 parse_process(const struct option *opt __maybe_unused, const char *arg,
1875  int __maybe_unused unset)
1876 {
1877  if (arg)
1878  add_process_filter(arg);
1879  return 0;
1880 }
1881 
1882 static int
1883 parse_highlight(const struct option *opt __maybe_unused, const char *arg,
1884  int __maybe_unused unset)
1885 {
1886  unsigned long duration = strtoul(arg, NULL, 0);
1887 
1889  return -1;
1890 
1891  if (duration)
1892  svg_highlight = duration;
1893  else
1894  svg_highlight_name = strdup(arg);
1895 
1896  return 0;
1897 }
1898 
1899 static int
1900 parse_time(const struct option *opt, const char *arg, int __maybe_unused unset)
1901 {
1902  char unit = 'n';
1903  u64 *value = opt->value;
1904 
1905  if (sscanf(arg, "%" PRIu64 "%cs", value, &unit) > 0) {
1906  switch (unit) {
1907  case 'm':
1908  *value *= NSEC_PER_MSEC;
1909  break;
1910  case 'u':
1911  *value *= NSEC_PER_USEC;
1912  break;
1913  case 'n':
1914  break;
1915  default:
1916  return -1;
1917  }
1918  }
1919 
1920  return 0;
1921 }
1922 
1923 int cmd_timechart(int argc, const char **argv)
1924 {
1925  struct timechart tchart = {
1926  .tool = {
1928  .fork = process_fork_event,
1929  .exit = process_exit_event,
1930  .sample = process_sample_event,
1931  .ordered_events = true,
1932  },
1933  .proc_num = 15,
1934  .min_time = NSEC_PER_MSEC,
1935  .merge_dist = 1000,
1936  };
1937  const char *output_name = "output.svg";
1938  const struct option timechart_common_options[] = {
1939  OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"),
1940  OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, "output processes data only"),
1941  OPT_END()
1942  };
1943  const struct option timechart_options[] = {
1944  OPT_STRING('i', "input", &input_name, "file", "input file name"),
1945  OPT_STRING('o', "output", &output_name, "file", "output file name"),
1946  OPT_INTEGER('w', "width", &svg_page_width, "page width"),
1947  OPT_CALLBACK(0, "highlight", NULL, "duration or task name",
1948  "highlight tasks. Pass duration in ns or process name.",
1949  parse_highlight),
1950  OPT_CALLBACK('p', "process", NULL, "process",
1951  "process selector. Pass a pid or process name.",
1952  parse_process),
1953  OPT_CALLBACK(0, "symfs", NULL, "directory",
1954  "Look for files with symbols relative to this directory",
1956  OPT_INTEGER('n', "proc-num", &tchart.proc_num,
1957  "min. number of tasks to print"),
1958  OPT_BOOLEAN('t', "topology", &tchart.topology,
1959  "sort CPUs according to topology"),
1960  OPT_BOOLEAN(0, "io-skip-eagain", &tchart.skip_eagain,
1961  "skip EAGAIN errors"),
1962  OPT_CALLBACK(0, "io-min-time", &tchart.min_time, "time",
1963  "all IO faster than min-time will visually appear longer",
1964  parse_time),
1965  OPT_CALLBACK(0, "io-merge-dist", &tchart.merge_dist, "time",
1966  "merge events that are merge-dist us apart",
1967  parse_time),
1968  OPT_BOOLEAN('f', "force", &tchart.force, "don't complain, do it"),
1969  OPT_PARENT(timechart_common_options),
1970  };
1971  const char * const timechart_subcommands[] = { "record", NULL };
1972  const char *timechart_usage[] = {
1973  "perf timechart [<options>] {record}",
1974  NULL
1975  };
1976  const struct option timechart_record_options[] = {
1977  OPT_BOOLEAN('I', "io-only", &tchart.io_only,
1978  "record only IO data"),
1979  OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"),
1980  OPT_PARENT(timechart_common_options),
1981  };
1982  const char * const timechart_record_usage[] = {
1983  "perf timechart record [<options>]",
1984  NULL
1985  };
1986  argc = parse_options_subcommand(argc, argv, timechart_options, timechart_subcommands,
1987  timechart_usage, PARSE_OPT_STOP_AT_NON_OPTION);
1988 
1989  if (tchart.power_only && tchart.tasks_only) {
1990  pr_err("-P and -T options cannot be used at the same time.\n");
1991  return -1;
1992  }
1993 
1994  if (argc && !strncmp(argv[0], "rec", 3)) {
1995  argc = parse_options(argc, argv, timechart_record_options,
1996  timechart_record_usage,
1997  PARSE_OPT_STOP_AT_NON_OPTION);
1998 
1999  if (tchart.power_only && tchart.tasks_only) {
2000  pr_err("-P and -T options cannot be used at the same time.\n");
2001  return -1;
2002  }
2003 
2004  if (tchart.io_only)
2005  return timechart__io_record(argc, argv);
2006  else
2007  return timechart__record(&tchart, argc, argv);
2008  } else if (argc)
2009  usage_with_options(timechart_usage, timechart_options);
2010 
2011  setup_pager();
2012 
2013  return __cmd_timechart(&tchart, output_name);
2014 }
static int process_sample_cpu_frequency(struct timechart *tchart, struct perf_evsel *evsel, struct perf_sample *sample, const char *backtrace __maybe_unused)
void svg_time_grid(double min_thickness)
Definition: svghelper.c:653
static int parse_time(const struct option *opt, const char *arg, int __maybe_unused unset)
int value
Definition: python.c:1143
static struct process_filter * process_filter
int nr_sibling_cores
Definition: env.h:49
void svg_cpu_box(int cpu, u64 __max_freq, u64 __turbo_freq)
Definition: svghelper.c:358
struct perf_data * data
Definition: session.h:36
void svg_waiting(int Yslot, int cpu, u64 start, u64 end, const char *backtrace)
Definition: svghelper.c:290
void svg_box(int Yslot, u64 start, u64 end, const char *type)
Definition: svghelper.c:212
static void sched_switch(struct timechart *tchart, int cpu, u64 timestamp, int prev_pid, int next_pid, u64 prev_state, const char *backtrace)
int machine__resolve(struct machine *machine, struct addr_location *al, struct perf_sample *sample)
Definition: event.c:1601
#define MAX_CPUS
static int determine_display_tasks_filtered(struct timechart *tchart)
struct perf_tool tool
static int process_fork_event(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused)
static int process_exit_poll(struct timechart *tchart, struct perf_evsel *evsel, struct perf_sample *sample)
char * sibling_cores
Definition: env.h:57
#define TYPE_RUNNING
static int process_sample_power_frequency(struct timechart *tchart, struct perf_evsel *evsel, struct perf_sample *sample, const char *backtrace __maybe_unused)
void svg_text(int Yslot, u64 start, const char *text)
Definition: svghelper.c:603
static int timechart__record(struct timechart *tchart, int argc, const char **argv)
int(* tracepoint_handler)(struct timechart *tchart, struct perf_evsel *evsel, struct perf_sample *sample, const char *backtrace)
int cmd_record(int argc, const char **argv)
struct cpu_sample * samples
static void add_process_filter(const char *string)
static const char * cat_backtrace(union perf_event *event, struct perf_sample *sample, struct machine *machine)
u32 pid
Definition: event.h:52
static int process_enter_poll(struct timechart *tchart, struct perf_evsel *evsel, struct perf_sample *sample)
const char * filename
Definition: hists_common.c:26
u32 tid
Definition: event.h:39
void svg_wakeline(u64 start, int row1, int row2, const char *backtrace)
Definition: svghelper.c:554
struct per_pidcomm * next
dictionary data
Definition: stat-cpi.py:4
struct ip_callchain * callchain
Definition: event.h:211
void svg_fbox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges)
Definition: svghelper.c:193
int svg_page_width
Definition: svghelper.c:36
static int process_exit_write(struct timechart *tchart, struct perf_evsel *evsel, struct perf_sample *sample)
static int pid_end_io_sample(struct timechart *tchart, int pid, int type, u64 end, long ret)
#define BYTES_THRESH
static void write_svg_file(struct timechart *tchart, const char *filename)
struct perf_data_file file
Definition: data.h:18
u64 svg_highlight
Definition: svghelper.c:37
struct perf_env env
Definition: header.h:82
static void p_state_change(struct timechart *tchart, int cpu, u64 timestamp, u64 new_freq)
static void end_sample_processing(struct timechart *tchart)
static int process_sample_event(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct perf_evsel *evsel, struct machine *machine)
u64 ips[0]
Definition: event.h:137
static int determine_display_tasks(struct timechart *tchart, u64 threshold)
static int process_header(struct perf_file_section *section __maybe_unused, struct perf_header *ph, int feat, int fd __maybe_unused, void *data)
static const char * output_name
Definition: builtin-stat.c:176
#define PSTATE
bool perf_session__has_traces(struct perf_session *session, const char *msg)
Definition: session.c:1963
#define pr_err(fmt,...)
Definition: json.h:21
void svg_cstate(int cpu, u64 start, u64 end, int type)
Definition: svghelper.c:418
void perf_session__delete(struct perf_session *session)
Definition: session.c:187
#define TIME_THRESH
static int parse_process(const struct option *opt __maybe_unused, const char *arg, int __maybe_unused unset)
Definition: comm.h:11
struct cpu_sample * next
static u64 cpus_pstate_state[MAX_CPUS]
static int process_sample_sched_wakeup(struct timechart *tchart, struct perf_evsel *evsel, struct perf_sample *sample, const char *backtrace)
static int process_enter_write(struct timechart *tchart, struct perf_evsel *evsel, struct perf_sample *sample)
int cmd_timechart(int argc, const char **argv)
void svg_pstate(int cpu, u64 start, u64 end, u64 freq)
Definition: svghelper.c:475
void * malloc(YYSIZE_T)
if(!yyg->yy_init)
static int __cmd_timechart(struct timechart *tchart, const char *output_name)
void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end)
Definition: svghelper.c:87
void addr_location__put(struct addr_location *al)
Definition: event.c:1661
static void draw_process_bars(struct timechart *tchart)
static void pid_set_comm(struct timechart *tchart, int pid, char *comm)
static int parse_highlight(const struct option *opt __maybe_unused, const char *arg, int __maybe_unused unset)
int symbol__config_symfs(const struct option *opt __maybe_unused, const char *dir, int unset __maybe_unused)
Definition: symbol.c:2205
int nr_cpus_avail
Definition: env.h:42
static int process_enter_read(struct timechart *tchart, struct perf_evsel *evsel, struct perf_sample *sample)
void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2, const char *backtrace)
Definition: svghelper.c:496
#define PWR_EVENT_EXIT
#define pr_debug(fmt,...)
Definition: json.h:27
Definition: tool.h:44
int perf_header__process_sections(struct perf_header *header, int fd, void *data, int(*process)(struct perf_file_section *section, struct perf_header *ph, int feat, int fd, void *data))
Definition: header.c:2856
static struct perf_session * session
Definition: builtin-lock.c:34
struct process_filter * next
u64 nr
Definition: event.h:136
int is_valid_tracepoint(const char *event_string)
static int process_enter_tx(struct timechart *tchart, struct perf_evsel *evsel, struct perf_sample *sample)
static int cpus_cstate_state[MAX_CPUS]
static int process_sample_sched_switch(struct timechart *tchart, struct perf_evsel *evsel, struct perf_sample *sample, const char *backtrace)
void svg_lbox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges)
Definition: svghelper.c:174
void svg_interrupt(u64 start, int row, const char *backtrace)
Definition: svghelper.c:583
void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace)
Definition: svghelper.c:222
u32 tid
Definition: event.h:193
void svg_io_legenda(void)
Definition: svghelper.c:623
const char * svg_highlight_name
Definition: svghelper.c:38
static void c_state_end(struct timechart *tchart, int cpu, u64 timestamp)
char name[0]
Definition: symbol.h:66
static void c_state_start(int cpu, u64 timestamp, int state)
#define TYPE_WAITING
const char * input_name
Definition: perf.c:40
int svg_build_topology_map(char *sib_core, int sib_core_nr, char *sib_thr, int sib_thr_nr)
Definition: svghelper.c:757
struct symbol * sym
Definition: symbol.h:211
static int pid_begin_io_sample(struct timechart *tchart, int pid, int type, u64 start, int fd)
#define event
struct fork_event fork
Definition: event.h:629
u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample, const char *name)
Definition: evsel.c:2722
static int process_enter_sync(struct timechart *tchart, struct perf_evsel *evsel, struct perf_sample *sample)
#define TYPE_BLOCKED
u64 time
Definition: event.h:54
u32 cpu
Definition: event.h:201
static int process_exit_read(struct timechart *tchart, struct perf_evsel *evsel, struct perf_sample *sample)
Definition: data.h:17
static int perf_data__fd(struct perf_data *data)
Definition: data.h:40
static int use_old_power_events
struct thread * thread
Definition: symbol.h:209
const char * backtrace
void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace)
Definition: svghelper.c:236
static struct per_pid * find_create_pid(struct timechart *tchart, int pid)
static void pid_put_sample(struct timechart *tchart, int pid, int type, unsigned int cpu, u64 start, u64 end, const char *backtrace)
#define NSEC_PER_SEC
Definition: jvmti_agent.c:101
#define zfree(ptr)
Definition: util.h:25
struct power_event * power_events
int nr_sibling_threads
Definition: env.h:50
static int timechart__io_record(int argc, const char **argv)
struct per_pidcomm * all
struct perf_event_header header
Definition: event.h:624
u32 pid
Definition: hists_common.c:15
static void pid_exit(struct timechart *tchart, int pid, u64 timestamp)
unsigned int numcpus
struct strfilter * filter
Definition: builtin-probe.c:60
void svg_legenda(void)
Definition: svghelper.c:637
u64 start
Definition: hists_common.c:25
struct perf_session * perf_session__new(struct perf_data *data, bool repipe, struct perf_tool *tool)
Definition: session.c:116
static u64 cpus_cstate_start_times[MAX_CPUS]
void svg_ubox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges)
Definition: svghelper.c:155
int perf_session__process_events(struct perf_session *session)
Definition: session.c:1945
struct io_sample * next
static int process_sample_power_start(struct timechart *tchart __maybe_unused, struct perf_evsel *evsel, struct perf_sample *sample, const char *backtrace __maybe_unused)
static void draw_io_bars(struct timechart *tchart)
enum chain_order order
Definition: callchain.h:103
void svg_process(int cpu, u64 start, u64 end, int pid, const char *name, const char *backtrace)
Definition: svghelper.c:384
u64 time
Definition: event.h:194
u32 flags
struct symbol * thread__find_symbol(struct thread *thread, u8 cpumode, u64 addr, struct addr_location *al)
Definition: event.c:1588
struct sample_wrapper * next
static int process_exit_sync(struct timechart *tchart, struct perf_evsel *evsel, struct perf_sample *sample)
struct perf_header header
Definition: session.h:23
event_op comm
Definition: tool.h:47
static void sort_pids(struct timechart *tchart)
struct per_pid * all_data
int symbol__init(struct perf_env *env)
Definition: symbol.c:2112
static int process_sample_power_end(struct timechart *tchart, struct perf_evsel *evsel __maybe_unused, struct perf_sample *sample, const char *backtrace __maybe_unused)
#define pr_info(fmt,...)
Definition: json.h:24
struct comm_event comm
Definition: event.h:627
const char * backtrace
void free(void *)
struct power_event * next
static void draw_c_p_states(struct timechart *tchart)
char comm[16]
Definition: event.h:40
static int process_enter_rx(struct timechart *tchart, struct perf_evsel *evsel, struct perf_sample *sample)
struct per_pidcomm * current
#define CSTATE
static FILE * f
Definition: intel-pt-log.c:30
struct wake_event * wake_events
static int process_comm_event(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused)
static int passes_filter(struct per_pid *p, struct per_pidcomm *c)
#define pr_warning(fmt,...)
Definition: debug.h:25
#define perf_session__set_tracepoints_handlers(session, array)
Definition: session.h:112
struct per_pid * next
#define TYPE_NONE
void svg_close(void)
Definition: svghelper.c:682
u32 ppid
Definition: event.h:52
static void draw_wakeups(struct timechart *tchart)
static int process_exit_tx(struct timechart *tchart, struct perf_evsel *evsel, struct perf_sample *sample)
static u64 cpus_pstate_start_times[MAX_CPUS]
static void sched_wakeup(struct timechart *tchart, int cpu, u64 timestamp, int waker, int wakee, u8 flags, const char *backtrace)
static int determine_display_io_tasks(struct timechart *timechart, u64 threshold)
const char * path
Definition: data.h:13
struct wake_event * next
static void pid_fork(struct timechart *tchart, int pid, int ppid, u64 timestamp)
struct io_sample * io_samples
struct perf_event_attr attr
Definition: evsel.h:93
char * sibling_threads
Definition: env.h:58
static void draw_cpu_usage(struct timechart *tchart)
static int process_sample_cpu_idle(struct timechart *tchart __maybe_unused, struct perf_evsel *evsel, struct perf_sample *sample, const char *backtrace __maybe_unused)
static int process_exit_rx(struct timechart *tchart, struct perf_evsel *evsel, struct perf_sample *sample)
static int process_exit_event(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused)
void static void * zalloc(size_t size)
Definition: util.h:20
void * handler
Definition: evsel.h:111