blob: 0c955acc08a2a9dad9d79de814b263b8bf129850 [file] [log] [blame]
Arjan van de Ven10274982009-09-12 07:53:05 +02001/*
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
Jiri Olsac85cffa2013-07-11 17:28:29 +020015#include <traceevent/event-parse.h>
16
Arjan van de Ven10274982009-09-12 07:53:05 +020017#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"
Jiri Olsa59366782013-07-11 17:28:30 +020024#include "util/evlist.h"
Arnaldo Carvalho de Meloe3f42602011-11-16 17:02:54 -020025#include "util/evsel.h"
Arjan van de Ven10274982009-09-12 07:53:05 +020026#include <linux/rbtree.h>
27#include "util/symbol.h"
Arjan van de Ven10274982009-09-12 07:53:05 +020028#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"
Li Zefan5cbd0802009-12-01 14:05:16 +080035#include "util/event.h"
Arnaldo Carvalho de Melo301a0b02009-12-13 19:50:25 -020036#include "util/session.h"
Arjan van de Ven10274982009-09-12 07:53:05 +020037#include "util/svghelper.h"
Arnaldo Carvalho de Melo45694aa2011-11-28 08:30:20 -020038#include "util/tool.h"
Jiri Olsaf5fc14122013-10-15 16:27:32 +020039#include "util/data.h"
Arjan van de Ven10274982009-09-12 07:53:05 +020040
Thomas Renninger20c457b2011-01-03 17:50:45 +010041#define SUPPORT_OLD_POWER_EVENTS 1
42#define PWR_EVENT_EXIT -1
43
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -030044struct per_pid;
45
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -030046struct timechart {
47 struct perf_tool tool;
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -030048 struct per_pid *all_data;
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -030049 int proc_num;
50 unsigned int numcpus;
51 u64 min_freq, /* Lowest CPU frequency seen */
52 max_freq, /* Highest CPU frequency seen */
53 turbo_frequency,
54 first_time, last_time;
55 bool power_only,
56 tasks_only,
57 with_backtrace;
58};
Arjan van de Ven10274982009-09-12 07:53:05 +020059
Arjan van de Ven10274982009-09-12 07:53:05 +020060struct per_pidcomm;
Arjan van de Ven10274982009-09-12 07:53:05 +020061struct cpu_sample;
Arjan van de Ven10274982009-09-12 07:53:05 +020062
63/*
64 * Datastructure layout:
65 * We keep an list of "pid"s, matching the kernels notion of a task struct.
66 * Each "pid" entry, has a list of "comm"s.
67 * this is because we want to track different programs different, while
68 * exec will reuse the original pid (by design).
69 * Each comm has a list of samples that will be used to draw
70 * final graph.
71 */
72
73struct per_pid {
74 struct per_pid *next;
75
76 int pid;
77 int ppid;
78
79 u64 start_time;
80 u64 end_time;
81 u64 total_time;
82 int display;
83
84 struct per_pidcomm *all;
85 struct per_pidcomm *current;
Arjan van de Ven10274982009-09-12 07:53:05 +020086};
87
88
89struct per_pidcomm {
90 struct per_pidcomm *next;
91
92 u64 start_time;
93 u64 end_time;
94 u64 total_time;
95
96 int Y;
97 int display;
98
99 long state;
100 u64 state_since;
101
102 char *comm;
103
104 struct cpu_sample *samples;
105};
106
107struct sample_wrapper {
108 struct sample_wrapper *next;
109
110 u64 timestamp;
111 unsigned char data[0];
112};
113
114#define TYPE_NONE 0
115#define TYPE_RUNNING 1
116#define TYPE_WAITING 2
117#define TYPE_BLOCKED 3
118
119struct cpu_sample {
120 struct cpu_sample *next;
121
122 u64 start_time;
123 u64 end_time;
124 int type;
125 int cpu;
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400126 const char *backtrace;
Arjan van de Ven10274982009-09-12 07:53:05 +0200127};
128
Arjan van de Ven10274982009-09-12 07:53:05 +0200129#define CSTATE 1
130#define PSTATE 2
131
132struct power_event {
133 struct power_event *next;
134 int type;
135 int state;
136 u64 start_time;
137 u64 end_time;
138 int cpu;
139};
140
141struct wake_event {
142 struct wake_event *next;
143 int waker;
144 int wakee;
145 u64 time;
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400146 const char *backtrace;
Arjan van de Ven10274982009-09-12 07:53:05 +0200147};
148
149static struct power_event *power_events;
150static struct wake_event *wake_events;
151
Arjan van de Venbbe29872009-10-20 07:09:39 +0900152struct process_filter {
Li Zefan5cbd0802009-12-01 14:05:16 +0800153 char *name;
154 int pid;
155 struct process_filter *next;
Arjan van de Venbbe29872009-10-20 07:09:39 +0900156};
157
158static struct process_filter *process_filter;
159
160
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300161static struct per_pid *find_create_pid(struct timechart *tchart, int pid)
Arjan van de Ven10274982009-09-12 07:53:05 +0200162{
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300163 struct per_pid *cursor = tchart->all_data;
Arjan van de Ven10274982009-09-12 07:53:05 +0200164
165 while (cursor) {
166 if (cursor->pid == pid)
167 return cursor;
168 cursor = cursor->next;
169 }
Arnaldo Carvalho de Meloe0dcd6f2012-09-24 11:16:40 -0300170 cursor = zalloc(sizeof(*cursor));
Arjan van de Ven10274982009-09-12 07:53:05 +0200171 assert(cursor != NULL);
Arjan van de Ven10274982009-09-12 07:53:05 +0200172 cursor->pid = pid;
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300173 cursor->next = tchart->all_data;
174 tchart->all_data = cursor;
Arjan van de Ven10274982009-09-12 07:53:05 +0200175 return cursor;
176}
177
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300178static void pid_set_comm(struct timechart *tchart, int pid, char *comm)
Arjan van de Ven10274982009-09-12 07:53:05 +0200179{
180 struct per_pid *p;
181 struct per_pidcomm *c;
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300182 p = find_create_pid(tchart, pid);
Arjan van de Ven10274982009-09-12 07:53:05 +0200183 c = p->all;
184 while (c) {
185 if (c->comm && strcmp(c->comm, comm) == 0) {
186 p->current = c;
187 return;
188 }
189 if (!c->comm) {
190 c->comm = strdup(comm);
191 p->current = c;
192 return;
193 }
194 c = c->next;
195 }
Arnaldo Carvalho de Meloe0dcd6f2012-09-24 11:16:40 -0300196 c = zalloc(sizeof(*c));
Arjan van de Ven10274982009-09-12 07:53:05 +0200197 assert(c != NULL);
Arjan van de Ven10274982009-09-12 07:53:05 +0200198 c->comm = strdup(comm);
199 p->current = c;
200 c->next = p->all;
201 p->all = c;
202}
203
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300204static void pid_fork(struct timechart *tchart, int pid, int ppid, u64 timestamp)
Arjan van de Ven10274982009-09-12 07:53:05 +0200205{
206 struct per_pid *p, *pp;
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300207 p = find_create_pid(tchart, pid);
208 pp = find_create_pid(tchart, ppid);
Arjan van de Ven10274982009-09-12 07:53:05 +0200209 p->ppid = ppid;
210 if (pp->current && pp->current->comm && !p->current)
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300211 pid_set_comm(tchart, pid, pp->current->comm);
Arjan van de Ven10274982009-09-12 07:53:05 +0200212
213 p->start_time = timestamp;
214 if (p->current) {
215 p->current->start_time = timestamp;
216 p->current->state_since = timestamp;
217 }
218}
219
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300220static void pid_exit(struct timechart *tchart, int pid, u64 timestamp)
Arjan van de Ven10274982009-09-12 07:53:05 +0200221{
222 struct per_pid *p;
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300223 p = find_create_pid(tchart, pid);
Arjan van de Ven10274982009-09-12 07:53:05 +0200224 p->end_time = timestamp;
225 if (p->current)
226 p->current->end_time = timestamp;
227}
228
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300229static void pid_put_sample(struct timechart *tchart, int pid, int type,
230 unsigned int cpu, u64 start, u64 end,
231 const char *backtrace)
Arjan van de Ven10274982009-09-12 07:53:05 +0200232{
233 struct per_pid *p;
234 struct per_pidcomm *c;
235 struct cpu_sample *sample;
236
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300237 p = find_create_pid(tchart, pid);
Arjan van de Ven10274982009-09-12 07:53:05 +0200238 c = p->current;
239 if (!c) {
Arnaldo Carvalho de Meloe0dcd6f2012-09-24 11:16:40 -0300240 c = zalloc(sizeof(*c));
Arjan van de Ven10274982009-09-12 07:53:05 +0200241 assert(c != NULL);
Arjan van de Ven10274982009-09-12 07:53:05 +0200242 p->current = c;
243 c->next = p->all;
244 p->all = c;
245 }
246
Arnaldo Carvalho de Meloe0dcd6f2012-09-24 11:16:40 -0300247 sample = zalloc(sizeof(*sample));
Arjan van de Ven10274982009-09-12 07:53:05 +0200248 assert(sample != NULL);
Arjan van de Ven10274982009-09-12 07:53:05 +0200249 sample->start_time = start;
250 sample->end_time = end;
251 sample->type = type;
252 sample->next = c->samples;
253 sample->cpu = cpu;
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400254 sample->backtrace = backtrace;
Arjan van de Ven10274982009-09-12 07:53:05 +0200255 c->samples = sample;
256
257 if (sample->type == TYPE_RUNNING && end > start && start > 0) {
258 c->total_time += (end-start);
259 p->total_time += (end-start);
260 }
261
262 if (c->start_time == 0 || c->start_time > start)
263 c->start_time = start;
264 if (p->start_time == 0 || p->start_time > start)
265 p->start_time = start;
Arjan van de Ven10274982009-09-12 07:53:05 +0200266}
267
268#define MAX_CPUS 4096
269
270static u64 cpus_cstate_start_times[MAX_CPUS];
271static int cpus_cstate_state[MAX_CPUS];
272static u64 cpus_pstate_start_times[MAX_CPUS];
273static u64 cpus_pstate_state[MAX_CPUS];
274
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300275static int process_comm_event(struct perf_tool *tool,
Arnaldo Carvalho de Melod20deb62011-11-25 08:19:45 -0200276 union perf_event *event,
Irina Tirdea1d037ca2012-09-11 01:15:03 +0300277 struct perf_sample *sample __maybe_unused,
278 struct machine *machine __maybe_unused)
Arjan van de Ven10274982009-09-12 07:53:05 +0200279{
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300280 struct timechart *tchart = container_of(tool, struct timechart, tool);
281 pid_set_comm(tchart, event->comm.tid, event->comm.comm);
Arjan van de Ven10274982009-09-12 07:53:05 +0200282 return 0;
283}
Arnaldo Carvalho de Melod8f66242009-12-13 19:50:24 -0200284
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300285static int process_fork_event(struct perf_tool *tool,
Arnaldo Carvalho de Melod20deb62011-11-25 08:19:45 -0200286 union perf_event *event,
Irina Tirdea1d037ca2012-09-11 01:15:03 +0300287 struct perf_sample *sample __maybe_unused,
288 struct machine *machine __maybe_unused)
Arjan van de Ven10274982009-09-12 07:53:05 +0200289{
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300290 struct timechart *tchart = container_of(tool, struct timechart, tool);
291 pid_fork(tchart, event->fork.pid, event->fork.ppid, event->fork.time);
Arjan van de Ven10274982009-09-12 07:53:05 +0200292 return 0;
293}
294
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300295static int process_exit_event(struct perf_tool *tool,
Arnaldo Carvalho de Melod20deb62011-11-25 08:19:45 -0200296 union perf_event *event,
Irina Tirdea1d037ca2012-09-11 01:15:03 +0300297 struct perf_sample *sample __maybe_unused,
298 struct machine *machine __maybe_unused)
Arjan van de Ven10274982009-09-12 07:53:05 +0200299{
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300300 struct timechart *tchart = container_of(tool, struct timechart, tool);
301 pid_exit(tchart, event->fork.pid, event->fork.time);
Arjan van de Ven10274982009-09-12 07:53:05 +0200302 return 0;
303}
304
Thomas Renninger20c457b2011-01-03 17:50:45 +0100305#ifdef SUPPORT_OLD_POWER_EVENTS
306static int use_old_power_events;
Thomas Renninger20c457b2011-01-03 17:50:45 +0100307#endif
308
Arjan van de Ven10274982009-09-12 07:53:05 +0200309static void c_state_start(int cpu, u64 timestamp, int state)
310{
311 cpus_cstate_start_times[cpu] = timestamp;
312 cpus_cstate_state[cpu] = state;
313}
314
315static void c_state_end(int cpu, u64 timestamp)
316{
Arnaldo Carvalho de Meloe0dcd6f2012-09-24 11:16:40 -0300317 struct power_event *pwr = zalloc(sizeof(*pwr));
318
Arjan van de Ven10274982009-09-12 07:53:05 +0200319 if (!pwr)
320 return;
Arjan van de Ven10274982009-09-12 07:53:05 +0200321
322 pwr->state = cpus_cstate_state[cpu];
323 pwr->start_time = cpus_cstate_start_times[cpu];
324 pwr->end_time = timestamp;
325 pwr->cpu = cpu;
326 pwr->type = CSTATE;
327 pwr->next = power_events;
328
329 power_events = pwr;
330}
331
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300332static void p_state_change(struct timechart *tchart, int cpu, u64 timestamp, u64 new_freq)
Arjan van de Ven10274982009-09-12 07:53:05 +0200333{
334 struct power_event *pwr;
Arjan van de Ven10274982009-09-12 07:53:05 +0200335
336 if (new_freq > 8000000) /* detect invalid data */
337 return;
338
Arnaldo Carvalho de Meloe0dcd6f2012-09-24 11:16:40 -0300339 pwr = zalloc(sizeof(*pwr));
Arjan van de Ven10274982009-09-12 07:53:05 +0200340 if (!pwr)
341 return;
Arjan van de Ven10274982009-09-12 07:53:05 +0200342
343 pwr->state = cpus_pstate_state[cpu];
344 pwr->start_time = cpus_pstate_start_times[cpu];
345 pwr->end_time = timestamp;
346 pwr->cpu = cpu;
347 pwr->type = PSTATE;
348 pwr->next = power_events;
349
350 if (!pwr->start_time)
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300351 pwr->start_time = tchart->first_time;
Arjan van de Ven10274982009-09-12 07:53:05 +0200352
353 power_events = pwr;
354
355 cpus_pstate_state[cpu] = new_freq;
356 cpus_pstate_start_times[cpu] = timestamp;
357
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300358 if ((u64)new_freq > tchart->max_freq)
359 tchart->max_freq = new_freq;
Arjan van de Ven10274982009-09-12 07:53:05 +0200360
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300361 if (new_freq < tchart->min_freq || tchart->min_freq == 0)
362 tchart->min_freq = new_freq;
Arjan van de Ven10274982009-09-12 07:53:05 +0200363
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300364 if (new_freq == tchart->max_freq - 1000)
365 tchart->turbo_frequency = tchart->max_freq;
Arjan van de Ven10274982009-09-12 07:53:05 +0200366}
367
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300368static void sched_wakeup(struct timechart *tchart, int cpu, u64 timestamp,
369 int waker, int wakee, u8 flags, const char *backtrace)
Arjan van de Ven10274982009-09-12 07:53:05 +0200370{
Arjan van de Ven10274982009-09-12 07:53:05 +0200371 struct per_pid *p;
Arnaldo Carvalho de Meloe0dcd6f2012-09-24 11:16:40 -0300372 struct wake_event *we = zalloc(sizeof(*we));
Arjan van de Ven10274982009-09-12 07:53:05 +0200373
Arjan van de Ven10274982009-09-12 07:53:05 +0200374 if (!we)
375 return;
376
Arjan van de Ven10274982009-09-12 07:53:05 +0200377 we->time = timestamp;
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400378 we->waker = waker;
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400379 we->backtrace = backtrace;
Arjan van de Ven10274982009-09-12 07:53:05 +0200380
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400381 if ((flags & TRACE_FLAG_HARDIRQ) || (flags & TRACE_FLAG_SOFTIRQ))
Arjan van de Ven10274982009-09-12 07:53:05 +0200382 we->waker = -1;
383
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400384 we->wakee = wakee;
Arjan van de Ven10274982009-09-12 07:53:05 +0200385 we->next = wake_events;
386 wake_events = we;
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300387 p = find_create_pid(tchart, we->wakee);
Arjan van de Ven10274982009-09-12 07:53:05 +0200388
389 if (p && p->current && p->current->state == TYPE_NONE) {
390 p->current->state_since = timestamp;
391 p->current->state = TYPE_WAITING;
392 }
393 if (p && p->current && p->current->state == TYPE_BLOCKED) {
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300394 pid_put_sample(tchart, p->pid, p->current->state, cpu,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400395 p->current->state_since, timestamp, NULL);
Arjan van de Ven10274982009-09-12 07:53:05 +0200396 p->current->state_since = timestamp;
397 p->current->state = TYPE_WAITING;
398 }
399}
400
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300401static void sched_switch(struct timechart *tchart, int cpu, u64 timestamp,
402 int prev_pid, int next_pid, u64 prev_state,
403 const char *backtrace)
Arjan van de Ven10274982009-09-12 07:53:05 +0200404{
405 struct per_pid *p = NULL, *prev_p;
Arjan van de Ven10274982009-09-12 07:53:05 +0200406
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300407 prev_p = find_create_pid(tchart, prev_pid);
Arjan van de Ven10274982009-09-12 07:53:05 +0200408
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300409 p = find_create_pid(tchart, next_pid);
Arjan van de Ven10274982009-09-12 07:53:05 +0200410
411 if (prev_p->current && prev_p->current->state != TYPE_NONE)
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300412 pid_put_sample(tchart, prev_pid, TYPE_RUNNING, cpu,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400413 prev_p->current->state_since, timestamp,
414 backtrace);
Arjan van de Ven10274982009-09-12 07:53:05 +0200415 if (p && p->current) {
416 if (p->current->state != TYPE_NONE)
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300417 pid_put_sample(tchart, next_pid, p->current->state, cpu,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400418 p->current->state_since, timestamp,
419 backtrace);
Arjan van de Ven10274982009-09-12 07:53:05 +0200420
Julia Lawall33e26a12010-08-05 22:27:51 +0200421 p->current->state_since = timestamp;
422 p->current->state = TYPE_RUNNING;
Arjan van de Ven10274982009-09-12 07:53:05 +0200423 }
424
425 if (prev_p->current) {
426 prev_p->current->state = TYPE_NONE;
427 prev_p->current->state_since = timestamp;
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400428 if (prev_state & 2)
Arjan van de Ven10274982009-09-12 07:53:05 +0200429 prev_p->current->state = TYPE_BLOCKED;
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400430 if (prev_state == 0)
Arjan van de Ven10274982009-09-12 07:53:05 +0200431 prev_p->current->state = TYPE_WAITING;
432 }
433}
434
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400435static const char *cat_backtrace(union perf_event *event,
436 struct perf_sample *sample,
437 struct machine *machine)
438{
439 struct addr_location al;
440 unsigned int i;
441 char *p = NULL;
442 size_t p_len;
443 u8 cpumode = PERF_RECORD_MISC_USER;
444 struct addr_location tal;
445 struct ip_callchain *chain = sample->callchain;
446 FILE *f = open_memstream(&p, &p_len);
447
448 if (!f) {
449 perror("open_memstream error");
450 return NULL;
451 }
452
453 if (!chain)
454 goto exit;
455
456 if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
457 fprintf(stderr, "problem processing %d event, skipping it.\n",
458 event->header.type);
459 goto exit;
460 }
461
462 for (i = 0; i < chain->nr; i++) {
463 u64 ip;
464
465 if (callchain_param.order == ORDER_CALLEE)
466 ip = chain->ips[i];
467 else
468 ip = chain->ips[chain->nr - i - 1];
469
470 if (ip >= PERF_CONTEXT_MAX) {
471 switch (ip) {
472 case PERF_CONTEXT_HV:
473 cpumode = PERF_RECORD_MISC_HYPERVISOR;
474 break;
475 case PERF_CONTEXT_KERNEL:
476 cpumode = PERF_RECORD_MISC_KERNEL;
477 break;
478 case PERF_CONTEXT_USER:
479 cpumode = PERF_RECORD_MISC_USER;
480 break;
481 default:
482 pr_debug("invalid callchain context: "
483 "%"PRId64"\n", (s64) ip);
484
485 /*
486 * It seems the callchain is corrupted.
487 * Discard all.
488 */
489 free(p);
490 p = NULL;
491 goto exit;
492 }
493 continue;
494 }
495
496 tal.filtered = false;
497 thread__find_addr_location(al.thread, machine, cpumode,
498 MAP__FUNCTION, ip, &tal);
499
500 if (tal.sym)
501 fprintf(f, "..... %016" PRIx64 " %s\n", ip,
502 tal.sym->name);
503 else
504 fprintf(f, "..... %016" PRIx64 "\n", ip);
505 }
506
507exit:
508 fclose(f);
509
510 return p;
511}
512
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300513typedef int (*tracepoint_handler)(struct timechart *tchart,
514 struct perf_evsel *evsel,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400515 struct perf_sample *sample,
516 const char *backtrace);
Arjan van de Ven10274982009-09-12 07:53:05 +0200517
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300518static int process_sample_event(struct perf_tool *tool,
Arnaldo Carvalho de Melo972ec652013-11-27 16:32:56 -0300519 union perf_event *event,
Arnaldo Carvalho de Melo8d50e5b2011-01-29 13:02:00 -0200520 struct perf_sample *sample,
Arnaldo Carvalho de Meloe3f42602011-11-16 17:02:54 -0200521 struct perf_evsel *evsel,
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300522 struct machine *machine)
Arjan van de Ven10274982009-09-12 07:53:05 +0200523{
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300524 struct timechart *tchart = container_of(tool, struct timechart, tool);
525
Arnaldo Carvalho de Meloe3f42602011-11-16 17:02:54 -0200526 if (evsel->attr.sample_type & PERF_SAMPLE_TIME) {
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300527 if (!tchart->first_time || tchart->first_time > sample->time)
528 tchart->first_time = sample->time;
529 if (tchart->last_time < sample->time)
530 tchart->last_time = sample->time;
Arjan van de Ven10274982009-09-12 07:53:05 +0200531 }
Arjan van de Ven10274982009-09-12 07:53:05 +0200532
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300533 if (sample->cpu > tchart->numcpus)
534 tchart->numcpus = sample->cpu;
Arjan van de Ven10274982009-09-12 07:53:05 +0200535
Arnaldo Carvalho de Melo744a9712013-11-06 10:17:38 -0300536 if (evsel->handler != NULL) {
537 tracepoint_handler f = evsel->handler;
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300538 return f(tchart, evsel, sample, cat_backtrace(event, sample, machine));
Arjan van de Ven10274982009-09-12 07:53:05 +0200539 }
Jiri Olsa59366782013-07-11 17:28:30 +0200540
Arjan van de Ven10274982009-09-12 07:53:05 +0200541 return 0;
542}
543
Jiri Olsa59366782013-07-11 17:28:30 +0200544static int
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300545process_sample_cpu_idle(struct timechart *tchart __maybe_unused,
546 struct perf_evsel *evsel,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400547 struct perf_sample *sample,
548 const char *backtrace __maybe_unused)
Jiri Olsa59366782013-07-11 17:28:30 +0200549{
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400550 u32 state = perf_evsel__intval(evsel, sample, "state");
551 u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
Jiri Olsa59366782013-07-11 17:28:30 +0200552
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400553 if (state == (u32)PWR_EVENT_EXIT)
554 c_state_end(cpu_id, sample->time);
Jiri Olsa59366782013-07-11 17:28:30 +0200555 else
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400556 c_state_start(cpu_id, sample->time, state);
Jiri Olsa59366782013-07-11 17:28:30 +0200557 return 0;
558}
559
560static int
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300561process_sample_cpu_frequency(struct timechart *tchart,
562 struct perf_evsel *evsel,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400563 struct perf_sample *sample,
564 const char *backtrace __maybe_unused)
Jiri Olsa59366782013-07-11 17:28:30 +0200565{
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400566 u32 state = perf_evsel__intval(evsel, sample, "state");
567 u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
Jiri Olsa59366782013-07-11 17:28:30 +0200568
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300569 p_state_change(tchart, cpu_id, sample->time, state);
Jiri Olsa59366782013-07-11 17:28:30 +0200570 return 0;
571}
572
573static int
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300574process_sample_sched_wakeup(struct timechart *tchart,
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300575 struct perf_evsel *evsel,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400576 struct perf_sample *sample,
577 const char *backtrace)
Jiri Olsa59366782013-07-11 17:28:30 +0200578{
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400579 u8 flags = perf_evsel__intval(evsel, sample, "common_flags");
580 int waker = perf_evsel__intval(evsel, sample, "common_pid");
581 int wakee = perf_evsel__intval(evsel, sample, "pid");
Jiri Olsa59366782013-07-11 17:28:30 +0200582
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300583 sched_wakeup(tchart, sample->cpu, sample->time, waker, wakee, flags, backtrace);
Jiri Olsa59366782013-07-11 17:28:30 +0200584 return 0;
585}
586
587static int
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300588process_sample_sched_switch(struct timechart *tchart,
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300589 struct perf_evsel *evsel,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400590 struct perf_sample *sample,
591 const char *backtrace)
Jiri Olsa59366782013-07-11 17:28:30 +0200592{
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400593 int prev_pid = perf_evsel__intval(evsel, sample, "prev_pid");
594 int next_pid = perf_evsel__intval(evsel, sample, "next_pid");
595 u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state");
Jiri Olsa59366782013-07-11 17:28:30 +0200596
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300597 sched_switch(tchart, sample->cpu, sample->time, prev_pid, next_pid,
598 prev_state, backtrace);
Jiri Olsa59366782013-07-11 17:28:30 +0200599 return 0;
600}
601
602#ifdef SUPPORT_OLD_POWER_EVENTS
603static int
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300604process_sample_power_start(struct timechart *tchart __maybe_unused,
605 struct perf_evsel *evsel,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400606 struct perf_sample *sample,
607 const char *backtrace __maybe_unused)
Jiri Olsa59366782013-07-11 17:28:30 +0200608{
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400609 u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
610 u64 value = perf_evsel__intval(evsel, sample, "value");
Jiri Olsa59366782013-07-11 17:28:30 +0200611
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400612 c_state_start(cpu_id, sample->time, value);
Jiri Olsa59366782013-07-11 17:28:30 +0200613 return 0;
614}
615
616static int
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300617process_sample_power_end(struct timechart *tchart __maybe_unused,
618 struct perf_evsel *evsel __maybe_unused,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400619 struct perf_sample *sample,
620 const char *backtrace __maybe_unused)
Jiri Olsa59366782013-07-11 17:28:30 +0200621{
622 c_state_end(sample->cpu, sample->time);
623 return 0;
624}
625
626static int
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300627process_sample_power_frequency(struct timechart *tchart,
628 struct perf_evsel *evsel,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400629 struct perf_sample *sample,
630 const char *backtrace __maybe_unused)
Jiri Olsa59366782013-07-11 17:28:30 +0200631{
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400632 u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
633 u64 value = perf_evsel__intval(evsel, sample, "value");
Jiri Olsa59366782013-07-11 17:28:30 +0200634
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300635 p_state_change(tchart, cpu_id, sample->time, value);
Jiri Olsa59366782013-07-11 17:28:30 +0200636 return 0;
637}
638#endif /* SUPPORT_OLD_POWER_EVENTS */
639
Arjan van de Ven10274982009-09-12 07:53:05 +0200640/*
641 * After the last sample we need to wrap up the current C/P state
642 * and close out each CPU for these.
643 */
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300644static void end_sample_processing(struct timechart *tchart)
Arjan van de Ven10274982009-09-12 07:53:05 +0200645{
646 u64 cpu;
647 struct power_event *pwr;
648
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300649 for (cpu = 0; cpu <= tchart->numcpus; cpu++) {
Arjan van de Ven10274982009-09-12 07:53:05 +0200650 /* C state */
651#if 0
Arnaldo Carvalho de Meloe0dcd6f2012-09-24 11:16:40 -0300652 pwr = zalloc(sizeof(*pwr));
653 if (!pwr)
654 return;
655
Arjan van de Ven10274982009-09-12 07:53:05 +0200656 pwr->state = cpus_cstate_state[cpu];
657 pwr->start_time = cpus_cstate_start_times[cpu];
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300658 pwr->end_time = tchart->last_time;
Arjan van de Ven10274982009-09-12 07:53:05 +0200659 pwr->cpu = cpu;
660 pwr->type = CSTATE;
661 pwr->next = power_events;
662
663 power_events = pwr;
664#endif
665 /* P state */
666
Arnaldo Carvalho de Meloe0dcd6f2012-09-24 11:16:40 -0300667 pwr = zalloc(sizeof(*pwr));
Arjan van de Ven10274982009-09-12 07:53:05 +0200668 if (!pwr)
669 return;
Arjan van de Ven10274982009-09-12 07:53:05 +0200670
671 pwr->state = cpus_pstate_state[cpu];
672 pwr->start_time = cpus_pstate_start_times[cpu];
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300673 pwr->end_time = tchart->last_time;
Arjan van de Ven10274982009-09-12 07:53:05 +0200674 pwr->cpu = cpu;
675 pwr->type = PSTATE;
676 pwr->next = power_events;
677
678 if (!pwr->start_time)
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300679 pwr->start_time = tchart->first_time;
Arjan van de Ven10274982009-09-12 07:53:05 +0200680 if (!pwr->state)
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300681 pwr->state = tchart->min_freq;
Arjan van de Ven10274982009-09-12 07:53:05 +0200682 power_events = pwr;
683 }
684}
685
Arjan van de Ven10274982009-09-12 07:53:05 +0200686/*
687 * Sort the pid datastructure
688 */
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300689static void sort_pids(struct timechart *tchart)
Arjan van de Ven10274982009-09-12 07:53:05 +0200690{
691 struct per_pid *new_list, *p, *cursor, *prev;
692 /* sort by ppid first, then by pid, lowest to highest */
693
694 new_list = NULL;
695
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300696 while (tchart->all_data) {
697 p = tchart->all_data;
698 tchart->all_data = p->next;
Arjan van de Ven10274982009-09-12 07:53:05 +0200699 p->next = NULL;
700
701 if (new_list == NULL) {
702 new_list = p;
703 p->next = NULL;
704 continue;
705 }
706 prev = NULL;
707 cursor = new_list;
708 while (cursor) {
709 if (cursor->ppid > p->ppid ||
710 (cursor->ppid == p->ppid && cursor->pid > p->pid)) {
711 /* must insert before */
712 if (prev) {
713 p->next = prev->next;
714 prev->next = p;
715 cursor = NULL;
716 continue;
717 } else {
718 p->next = new_list;
719 new_list = p;
720 cursor = NULL;
721 continue;
722 }
723 }
724
725 prev = cursor;
726 cursor = cursor->next;
727 if (!cursor)
728 prev->next = p;
729 }
730 }
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300731 tchart->all_data = new_list;
Arjan van de Ven10274982009-09-12 07:53:05 +0200732}
733
734
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300735static void draw_c_p_states(struct timechart *tchart)
Arjan van de Ven10274982009-09-12 07:53:05 +0200736{
737 struct power_event *pwr;
738 pwr = power_events;
739
740 /*
741 * two pass drawing so that the P state bars are on top of the C state blocks
742 */
743 while (pwr) {
744 if (pwr->type == CSTATE)
745 svg_cstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
746 pwr = pwr->next;
747 }
748
749 pwr = power_events;
750 while (pwr) {
751 if (pwr->type == PSTATE) {
752 if (!pwr->state)
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300753 pwr->state = tchart->min_freq;
Arjan van de Ven10274982009-09-12 07:53:05 +0200754 svg_pstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
755 }
756 pwr = pwr->next;
757 }
758}
759
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300760static void draw_wakeups(struct timechart *tchart)
Arjan van de Ven10274982009-09-12 07:53:05 +0200761{
762 struct wake_event *we;
763 struct per_pid *p;
764 struct per_pidcomm *c;
765
766 we = wake_events;
767 while (we) {
768 int from = 0, to = 0;
Arjan van de Ven4f1202c2009-09-20 18:13:28 +0200769 char *task_from = NULL, *task_to = NULL;
Arjan van de Ven10274982009-09-12 07:53:05 +0200770
771 /* locate the column of the waker and wakee */
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300772 p = tchart->all_data;
Arjan van de Ven10274982009-09-12 07:53:05 +0200773 while (p) {
774 if (p->pid == we->waker || p->pid == we->wakee) {
775 c = p->all;
776 while (c) {
777 if (c->Y && c->start_time <= we->time && c->end_time >= we->time) {
Arjan van de Venbbe29872009-10-20 07:09:39 +0900778 if (p->pid == we->waker && !from) {
Arjan van de Ven10274982009-09-12 07:53:05 +0200779 from = c->Y;
Arjan van de Ven3bc2a392009-10-20 06:46:49 +0900780 task_from = strdup(c->comm);
Arjan van de Ven4f1202c2009-09-20 18:13:28 +0200781 }
Arjan van de Venbbe29872009-10-20 07:09:39 +0900782 if (p->pid == we->wakee && !to) {
Arjan van de Ven10274982009-09-12 07:53:05 +0200783 to = c->Y;
Arjan van de Ven3bc2a392009-10-20 06:46:49 +0900784 task_to = strdup(c->comm);
Arjan van de Ven4f1202c2009-09-20 18:13:28 +0200785 }
Arjan van de Ven10274982009-09-12 07:53:05 +0200786 }
787 c = c->next;
788 }
Arjan van de Ven3bc2a392009-10-20 06:46:49 +0900789 c = p->all;
790 while (c) {
791 if (p->pid == we->waker && !from) {
792 from = c->Y;
793 task_from = strdup(c->comm);
794 }
795 if (p->pid == we->wakee && !to) {
796 to = c->Y;
797 task_to = strdup(c->comm);
798 }
799 c = c->next;
800 }
Arjan van de Ven10274982009-09-12 07:53:05 +0200801 }
802 p = p->next;
803 }
804
Arjan van de Ven3bc2a392009-10-20 06:46:49 +0900805 if (!task_from) {
806 task_from = malloc(40);
807 sprintf(task_from, "[%i]", we->waker);
808 }
809 if (!task_to) {
810 task_to = malloc(40);
811 sprintf(task_to, "[%i]", we->wakee);
812 }
813
Arjan van de Ven10274982009-09-12 07:53:05 +0200814 if (we->waker == -1)
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400815 svg_interrupt(we->time, to, we->backtrace);
Arjan van de Ven10274982009-09-12 07:53:05 +0200816 else if (from && to && abs(from - to) == 1)
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400817 svg_wakeline(we->time, from, to, we->backtrace);
Arjan van de Ven10274982009-09-12 07:53:05 +0200818 else
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400819 svg_partial_wakeline(we->time, from, task_from, to,
820 task_to, we->backtrace);
Arjan van de Ven10274982009-09-12 07:53:05 +0200821 we = we->next;
Arjan van de Ven3bc2a392009-10-20 06:46:49 +0900822
823 free(task_from);
824 free(task_to);
Arjan van de Ven10274982009-09-12 07:53:05 +0200825 }
826}
827
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300828static void draw_cpu_usage(struct timechart *tchart)
Arjan van de Ven10274982009-09-12 07:53:05 +0200829{
830 struct per_pid *p;
831 struct per_pidcomm *c;
832 struct cpu_sample *sample;
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300833 p = tchart->all_data;
Arjan van de Ven10274982009-09-12 07:53:05 +0200834 while (p) {
835 c = p->all;
836 while (c) {
837 sample = c->samples;
838 while (sample) {
839 if (sample->type == TYPE_RUNNING)
840 svg_process(sample->cpu, sample->start_time, sample->end_time, "sample", c->comm);
841
842 sample = sample->next;
843 }
844 c = c->next;
845 }
846 p = p->next;
847 }
848}
849
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300850static void draw_process_bars(struct timechart *tchart)
Arjan van de Ven10274982009-09-12 07:53:05 +0200851{
852 struct per_pid *p;
853 struct per_pidcomm *c;
854 struct cpu_sample *sample;
855 int Y = 0;
856
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300857 Y = 2 * tchart->numcpus + 2;
Arjan van de Ven10274982009-09-12 07:53:05 +0200858
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300859 p = tchart->all_data;
Arjan van de Ven10274982009-09-12 07:53:05 +0200860 while (p) {
861 c = p->all;
862 while (c) {
863 if (!c->display) {
864 c->Y = 0;
865 c = c->next;
866 continue;
867 }
868
Arjan van de Vena92fe7b2009-09-20 18:13:53 +0200869 svg_box(Y, c->start_time, c->end_time, "process");
Arjan van de Ven10274982009-09-12 07:53:05 +0200870 sample = c->samples;
871 while (sample) {
872 if (sample->type == TYPE_RUNNING)
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400873 svg_running(Y, sample->cpu,
874 sample->start_time,
875 sample->end_time,
876 sample->backtrace);
Arjan van de Ven10274982009-09-12 07:53:05 +0200877 if (sample->type == TYPE_BLOCKED)
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400878 svg_blocked(Y, sample->cpu,
879 sample->start_time,
880 sample->end_time,
881 sample->backtrace);
Arjan van de Ven10274982009-09-12 07:53:05 +0200882 if (sample->type == TYPE_WAITING)
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400883 svg_waiting(Y, sample->cpu,
884 sample->start_time,
885 sample->end_time,
886 sample->backtrace);
Arjan van de Ven10274982009-09-12 07:53:05 +0200887 sample = sample->next;
888 }
889
890 if (c->comm) {
891 char comm[256];
892 if (c->total_time > 5000000000) /* 5 seconds */
893 sprintf(comm, "%s:%i (%2.2fs)", c->comm, p->pid, c->total_time / 1000000000.0);
894 else
895 sprintf(comm, "%s:%i (%3.1fms)", c->comm, p->pid, c->total_time / 1000000.0);
896
897 svg_text(Y, c->start_time, comm);
898 }
899 c->Y = Y;
900 Y++;
901 c = c->next;
902 }
903 p = p->next;
904 }
905}
906
Arjan van de Venbbe29872009-10-20 07:09:39 +0900907static void add_process_filter(const char *string)
908{
Arnaldo Carvalho de Meloe0dcd6f2012-09-24 11:16:40 -0300909 int pid = strtoull(string, NULL, 10);
910 struct process_filter *filt = malloc(sizeof(*filt));
Arjan van de Venbbe29872009-10-20 07:09:39 +0900911
Arjan van de Venbbe29872009-10-20 07:09:39 +0900912 if (!filt)
913 return;
914
915 filt->name = strdup(string);
916 filt->pid = pid;
917 filt->next = process_filter;
918
919 process_filter = filt;
920}
921
922static int passes_filter(struct per_pid *p, struct per_pidcomm *c)
923{
924 struct process_filter *filt;
925 if (!process_filter)
926 return 1;
927
928 filt = process_filter;
929 while (filt) {
930 if (filt->pid && p->pid == filt->pid)
931 return 1;
932 if (strcmp(filt->name, c->comm) == 0)
933 return 1;
934 filt = filt->next;
935 }
936 return 0;
937}
938
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300939static int determine_display_tasks_filtered(struct timechart *tchart)
Arjan van de Venbbe29872009-10-20 07:09:39 +0900940{
941 struct per_pid *p;
942 struct per_pidcomm *c;
943 int count = 0;
944
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300945 p = tchart->all_data;
Arjan van de Venbbe29872009-10-20 07:09:39 +0900946 while (p) {
947 p->display = 0;
948 if (p->start_time == 1)
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300949 p->start_time = tchart->first_time;
Arjan van de Venbbe29872009-10-20 07:09:39 +0900950
951 /* no exit marker, task kept running to the end */
952 if (p->end_time == 0)
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300953 p->end_time = tchart->last_time;
Arjan van de Venbbe29872009-10-20 07:09:39 +0900954
955 c = p->all;
956
957 while (c) {
958 c->display = 0;
959
960 if (c->start_time == 1)
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300961 c->start_time = tchart->first_time;
Arjan van de Venbbe29872009-10-20 07:09:39 +0900962
963 if (passes_filter(p, c)) {
964 c->display = 1;
965 p->display = 1;
966 count++;
967 }
968
969 if (c->end_time == 0)
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300970 c->end_time = tchart->last_time;
Arjan van de Venbbe29872009-10-20 07:09:39 +0900971
972 c = c->next;
973 }
974 p = p->next;
975 }
976 return count;
977}
978
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300979static int determine_display_tasks(struct timechart *tchart, u64 threshold)
Arjan van de Ven10274982009-09-12 07:53:05 +0200980{
981 struct per_pid *p;
982 struct per_pidcomm *c;
983 int count = 0;
984
Arjan van de Venbbe29872009-10-20 07:09:39 +0900985 if (process_filter)
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300986 return determine_display_tasks_filtered(tchart);
Arjan van de Venbbe29872009-10-20 07:09:39 +0900987
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -0300988 p = tchart->all_data;
Arjan van de Ven10274982009-09-12 07:53:05 +0200989 while (p) {
990 p->display = 0;
991 if (p->start_time == 1)
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300992 p->start_time = tchart->first_time;
Arjan van de Ven10274982009-09-12 07:53:05 +0200993
994 /* no exit marker, task kept running to the end */
995 if (p->end_time == 0)
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -0300996 p->end_time = tchart->last_time;
Stanislav Fomichev753c5052013-11-01 20:25:47 +0400997 if (p->total_time >= threshold)
Arjan van de Ven10274982009-09-12 07:53:05 +0200998 p->display = 1;
999
1000 c = p->all;
1001
1002 while (c) {
1003 c->display = 0;
1004
1005 if (c->start_time == 1)
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001006 c->start_time = tchart->first_time;
Arjan van de Ven10274982009-09-12 07:53:05 +02001007
Stanislav Fomichev753c5052013-11-01 20:25:47 +04001008 if (c->total_time >= threshold) {
Arjan van de Ven10274982009-09-12 07:53:05 +02001009 c->display = 1;
1010 count++;
1011 }
1012
1013 if (c->end_time == 0)
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001014 c->end_time = tchart->last_time;
Arjan van de Ven10274982009-09-12 07:53:05 +02001015
1016 c = c->next;
1017 }
1018 p = p->next;
1019 }
1020 return count;
1021}
1022
1023
1024
1025#define TIME_THRESH 10000000
1026
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001027static void write_svg_file(struct timechart *tchart, const char *filename)
Arjan van de Ven10274982009-09-12 07:53:05 +02001028{
1029 u64 i;
1030 int count;
Stanislav Fomichev0a8eb272013-11-01 20:25:45 +04001031 int thresh = TIME_THRESH;
Arjan van de Ven10274982009-09-12 07:53:05 +02001032
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001033 tchart->numcpus++;
Arjan van de Ven10274982009-09-12 07:53:05 +02001034
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001035 if (tchart->power_only)
1036 tchart->proc_num = 0;
Arjan van de Ven10274982009-09-12 07:53:05 +02001037
Stanislav Fomichev0a8eb272013-11-01 20:25:45 +04001038 /* We'd like to show at least proc_num tasks;
1039 * be less picky if we have fewer */
1040 do {
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001041 count = determine_display_tasks(tchart, thresh);
Stanislav Fomichev0a8eb272013-11-01 20:25:45 +04001042 thresh /= 10;
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001043 } while (!process_filter && thresh && count < tchart->proc_num);
Arjan van de Ven10274982009-09-12 07:53:05 +02001044
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001045 open_svg(filename, tchart->numcpus, count, tchart->first_time, tchart->last_time);
Arjan van de Ven10274982009-09-12 07:53:05 +02001046
Arjan van de Ven5094b652009-09-20 18:14:16 +02001047 svg_time_grid();
Arjan van de Ven10274982009-09-12 07:53:05 +02001048 svg_legenda();
1049
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001050 for (i = 0; i < tchart->numcpus; i++)
1051 svg_cpu_box(i, tchart->max_freq, tchart->turbo_frequency);
Arjan van de Ven10274982009-09-12 07:53:05 +02001052
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -03001053 draw_cpu_usage(tchart);
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001054 if (tchart->proc_num)
1055 draw_process_bars(tchart);
1056 if (!tchart->tasks_only)
1057 draw_c_p_states(tchart);
1058 if (tchart->proc_num)
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -03001059 draw_wakeups(tchart);
Arjan van de Ven10274982009-09-12 07:53:05 +02001060
1061 svg_close();
1062}
1063
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001064static int __cmd_timechart(struct timechart *tchart, const char *output_name)
Arjan van de Ven10274982009-09-12 07:53:05 +02001065{
Jiri Olsa59366782013-07-11 17:28:30 +02001066 const struct perf_evsel_str_handler power_tracepoints[] = {
1067 { "power:cpu_idle", process_sample_cpu_idle },
1068 { "power:cpu_frequency", process_sample_cpu_frequency },
1069 { "sched:sched_wakeup", process_sample_sched_wakeup },
1070 { "sched:sched_switch", process_sample_sched_switch },
1071#ifdef SUPPORT_OLD_POWER_EVENTS
1072 { "power:power_start", process_sample_power_start },
1073 { "power:power_end", process_sample_power_end },
1074 { "power:power_frequency", process_sample_power_frequency },
1075#endif
1076 };
Jiri Olsaf5fc14122013-10-15 16:27:32 +02001077 struct perf_data_file file = {
1078 .path = input_name,
1079 .mode = PERF_DATA_MODE_READ,
1080 };
1081
1082 struct perf_session *session = perf_session__new(&file, false,
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001083 &tchart->tool);
Arnaldo Carvalho de Melod549c7692009-12-27 21:37:02 -02001084 int ret = -EINVAL;
Arjan van de Ven10274982009-09-12 07:53:05 +02001085
Arnaldo Carvalho de Melo94c744b2009-12-11 21:24:02 -02001086 if (session == NULL)
1087 return -ENOMEM;
1088
Arnaldo Carvalho de Melod549c7692009-12-27 21:37:02 -02001089 if (!perf_session__has_traces(session, "timechart record"))
1090 goto out_delete;
1091
Jiri Olsa59366782013-07-11 17:28:30 +02001092 if (perf_session__set_tracepoints_handlers(session,
1093 power_tracepoints)) {
1094 pr_err("Initializing session tracepoint handlers failed\n");
1095 goto out_delete;
1096 }
1097
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001098 ret = perf_session__process_events(session, &tchart->tool);
Li Zefan5cbd0802009-12-01 14:05:16 +08001099 if (ret)
Arnaldo Carvalho de Melo94c744b2009-12-11 21:24:02 -02001100 goto out_delete;
Arjan van de Ven10274982009-09-12 07:53:05 +02001101
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001102 end_sample_processing(tchart);
Arjan van de Ven10274982009-09-12 07:53:05 +02001103
Arnaldo Carvalho de Melo5e22f6d2013-11-28 11:50:41 -03001104 sort_pids(tchart);
Arjan van de Ven10274982009-09-12 07:53:05 +02001105
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001106 write_svg_file(tchart, output_name);
Arjan van de Ven10274982009-09-12 07:53:05 +02001107
Arnaldo Carvalho de Melo6beba7a2009-10-21 17:34:06 -02001108 pr_info("Written %2.1f seconds of trace to %s.\n",
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001109 (tchart->last_time - tchart->first_time) / 1000000000.0, output_name);
Arnaldo Carvalho de Melo94c744b2009-12-11 21:24:02 -02001110out_delete:
1111 perf_session__delete(session);
1112 return ret;
Arjan van de Ven10274982009-09-12 07:53:05 +02001113}
1114
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001115static int timechart__record(struct timechart *tchart, int argc, const char **argv)
Arjan van de Ven3c09eeb2009-09-19 13:34:42 +02001116{
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001117 unsigned int rec_argc, i, j;
1118 const char **rec_argv;
1119 const char **p;
1120 unsigned int record_elems;
1121
1122 const char * const common_args[] = {
Jiri Olsa4a4d3712013-06-05 13:37:21 +02001123 "record", "-a", "-R", "-c", "1",
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001124 };
1125 unsigned int common_args_nr = ARRAY_SIZE(common_args);
1126
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +04001127 const char * const backtrace_args[] = {
1128 "-g",
1129 };
1130 unsigned int backtrace_args_no = ARRAY_SIZE(backtrace_args);
1131
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001132 const char * const power_args[] = {
1133 "-e", "power:cpu_frequency",
1134 "-e", "power:cpu_idle",
1135 };
1136 unsigned int power_args_nr = ARRAY_SIZE(power_args);
1137
1138 const char * const old_power_args[] = {
1139#ifdef SUPPORT_OLD_POWER_EVENTS
Arnaldo Carvalho de Melo73bdc712012-10-01 15:20:58 -03001140 "-e", "power:power_start",
1141 "-e", "power:power_end",
1142 "-e", "power:power_frequency",
Arnaldo Carvalho de Melo73bdc712012-10-01 15:20:58 -03001143#endif
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001144 };
1145 unsigned int old_power_args_nr = ARRAY_SIZE(old_power_args);
1146
1147 const char * const tasks_args[] = {
Arnaldo Carvalho de Melo73bdc712012-10-01 15:20:58 -03001148 "-e", "sched:sched_wakeup",
1149 "-e", "sched:sched_switch",
1150 };
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001151 unsigned int tasks_args_nr = ARRAY_SIZE(tasks_args);
Arjan van de Ven3c09eeb2009-09-19 13:34:42 +02001152
Thomas Renninger20c457b2011-01-03 17:50:45 +01001153#ifdef SUPPORT_OLD_POWER_EVENTS
1154 if (!is_valid_tracepoint("power:cpu_idle") &&
1155 is_valid_tracepoint("power:power_start")) {
1156 use_old_power_events = 1;
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001157 power_args_nr = 0;
1158 } else {
1159 old_power_args_nr = 0;
Thomas Renninger20c457b2011-01-03 17:50:45 +01001160 }
1161#endif
1162
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001163 if (tchart->power_only)
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001164 tasks_args_nr = 0;
1165
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001166 if (tchart->tasks_only) {
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001167 power_args_nr = 0;
1168 old_power_args_nr = 0;
1169 }
1170
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001171 if (!tchart->with_backtrace)
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +04001172 backtrace_args_no = 0;
1173
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001174 record_elems = common_args_nr + tasks_args_nr +
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +04001175 power_args_nr + old_power_args_nr + backtrace_args_no;
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001176
1177 rec_argc = record_elems + argc;
Arjan van de Ven3c09eeb2009-09-19 13:34:42 +02001178 rec_argv = calloc(rec_argc + 1, sizeof(char *));
1179
Chris Samuelce47dc52010-11-13 13:35:06 +11001180 if (rec_argv == NULL)
1181 return -ENOMEM;
1182
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001183 p = rec_argv;
1184 for (i = 0; i < common_args_nr; i++)
1185 *p++ = strdup(common_args[i]);
Arjan van de Ven3c09eeb2009-09-19 13:34:42 +02001186
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +04001187 for (i = 0; i < backtrace_args_no; i++)
1188 *p++ = strdup(backtrace_args[i]);
1189
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001190 for (i = 0; i < tasks_args_nr; i++)
1191 *p++ = strdup(tasks_args[i]);
Arjan van de Ven3c09eeb2009-09-19 13:34:42 +02001192
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001193 for (i = 0; i < power_args_nr; i++)
1194 *p++ = strdup(power_args[i]);
1195
1196 for (i = 0; i < old_power_args_nr; i++)
1197 *p++ = strdup(old_power_args[i]);
1198
1199 for (j = 1; j < (unsigned int)argc; j++)
1200 *p++ = argv[j];
1201
1202 return cmd_record(rec_argc, rec_argv, NULL);
Arjan van de Ven3c09eeb2009-09-19 13:34:42 +02001203}
1204
Arjan van de Venbbe29872009-10-20 07:09:39 +09001205static int
Irina Tirdea1d037ca2012-09-11 01:15:03 +03001206parse_process(const struct option *opt __maybe_unused, const char *arg,
1207 int __maybe_unused unset)
Arjan van de Venbbe29872009-10-20 07:09:39 +09001208{
1209 if (arg)
1210 add_process_filter(arg);
1211 return 0;
1212}
1213
Arnaldo Carvalho de Melo73bdc712012-10-01 15:20:58 -03001214int cmd_timechart(int argc, const char **argv,
1215 const char *prefix __maybe_unused)
1216{
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001217 struct timechart tchart = {
1218 .tool = {
1219 .comm = process_comm_event,
1220 .fork = process_fork_event,
1221 .exit = process_exit_event,
1222 .sample = process_sample_event,
1223 .ordered_samples = true,
1224 },
1225 .proc_num = 15,
1226 };
Arnaldo Carvalho de Melo73bdc712012-10-01 15:20:58 -03001227 const char *output_name = "output.svg";
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001228 const struct option timechart_options[] = {
Arnaldo Carvalho de Melo73bdc712012-10-01 15:20:58 -03001229 OPT_STRING('i', "input", &input_name, "file", "input file name"),
1230 OPT_STRING('o', "output", &output_name, "file", "output file name"),
1231 OPT_INTEGER('w', "width", &svg_page_width, "page width"),
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001232 OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"),
1233 OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only,
Stanislav Fomichevc87097d2013-11-01 20:25:48 +04001234 "output processes data only"),
Arjan van de Venbbe29872009-10-20 07:09:39 +09001235 OPT_CALLBACK('p', "process", NULL, "process",
1236 "process selector. Pass a pid or process name.",
1237 parse_process),
David Ahernec5761e2010-12-09 13:27:07 -07001238 OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
1239 "Look for files with symbols relative to this directory"),
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001240 OPT_INTEGER('n', "proc-num", &tchart.proc_num,
Stanislav Fomichev54874e32013-11-01 20:25:46 +04001241 "min. number of tasks to print"),
Arjan van de Ven10274982009-09-12 07:53:05 +02001242 OPT_END()
Arnaldo Carvalho de Melo73bdc712012-10-01 15:20:58 -03001243 };
1244 const char * const timechart_usage[] = {
1245 "perf timechart [<options>] {record}",
1246 NULL
1247 };
Arjan van de Ven10274982009-09-12 07:53:05 +02001248
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001249 const struct option record_options[] = {
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001250 OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"),
1251 OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only,
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001252 "output processes data only"),
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001253 OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"),
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001254 OPT_END()
1255 };
1256 const char * const record_usage[] = {
1257 "perf timechart record [<options>]",
1258 NULL
1259 };
1260 argc = parse_options(argc, argv, timechart_options, timechart_usage,
Arjan van de Ven3c09eeb2009-09-19 13:34:42 +02001261 PARSE_OPT_STOP_AT_NON_OPTION);
Arjan van de Ven10274982009-09-12 07:53:05 +02001262
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001263 if (tchart.power_only && tchart.tasks_only) {
Stanislav Fomichevc87097d2013-11-01 20:25:48 +04001264 pr_err("-P and -T options cannot be used at the same time.\n");
1265 return -1;
1266 }
1267
Arnaldo Carvalho de Melo655000e2009-12-15 20:04:40 -02001268 symbol__init();
1269
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001270 if (argc && !strncmp(argv[0], "rec", 3)) {
1271 argc = parse_options(argc, argv, record_options, record_usage,
1272 PARSE_OPT_STOP_AT_NON_OPTION);
1273
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001274 if (tchart.power_only && tchart.tasks_only) {
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001275 pr_err("-P and -T options cannot be used at the same time.\n");
1276 return -1;
1277 }
1278
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001279 return timechart__record(&tchart, argc, argv);
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001280 } else if (argc)
1281 usage_with_options(timechart_usage, timechart_options);
Arjan van de Ven10274982009-09-12 07:53:05 +02001282
1283 setup_pager();
1284
Arnaldo Carvalho de Melo985b12e2013-11-28 11:25:19 -03001285 return __cmd_timechart(&tchart, output_name);
Arjan van de Ven10274982009-09-12 07:53:05 +02001286}