blob: 680632d7e26a1a372e8e918d92947cf8726ad166 [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 Olsaf5fc1412013-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
Stanislav Fomichev54874e32013-11-01 20:25:46 +040044static int proc_num = 15;
Thomas Renninger20c457b2011-01-03 17:50:45 +010045
Arjan van de Ven10274982009-09-12 07:53:05 +020046static unsigned int numcpus;
47static u64 min_freq; /* Lowest CPU frequency seen */
48static u64 max_freq; /* Highest CPU frequency seen */
49static u64 turbo_frequency;
50
51static u64 first_time, last_time;
52
Ian Munsiec0555642010-04-13 18:37:33 +100053static bool power_only;
Stanislav Fomichevc87097d2013-11-01 20:25:48 +040054static bool tasks_only;
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +040055static bool with_backtrace;
Arjan van de Ven39a90a82009-09-24 15:40:13 +020056
Arjan van de Ven10274982009-09-12 07:53:05 +020057
Arjan van de Ven10274982009-09-12 07:53:05 +020058struct per_pidcomm;
Arjan van de Ven10274982009-09-12 07:53:05 +020059struct cpu_sample;
Arjan van de Ven10274982009-09-12 07:53:05 +020060
61/*
62 * Datastructure layout:
63 * We keep an list of "pid"s, matching the kernels notion of a task struct.
64 * Each "pid" entry, has a list of "comm"s.
65 * this is because we want to track different programs different, while
66 * exec will reuse the original pid (by design).
67 * Each comm has a list of samples that will be used to draw
68 * final graph.
69 */
70
71struct per_pid {
72 struct per_pid *next;
73
74 int pid;
75 int ppid;
76
77 u64 start_time;
78 u64 end_time;
79 u64 total_time;
80 int display;
81
82 struct per_pidcomm *all;
83 struct per_pidcomm *current;
Arjan van de Ven10274982009-09-12 07:53:05 +020084};
85
86
87struct per_pidcomm {
88 struct per_pidcomm *next;
89
90 u64 start_time;
91 u64 end_time;
92 u64 total_time;
93
94 int Y;
95 int display;
96
97 long state;
98 u64 state_since;
99
100 char *comm;
101
102 struct cpu_sample *samples;
103};
104
105struct sample_wrapper {
106 struct sample_wrapper *next;
107
108 u64 timestamp;
109 unsigned char data[0];
110};
111
112#define TYPE_NONE 0
113#define TYPE_RUNNING 1
114#define TYPE_WAITING 2
115#define TYPE_BLOCKED 3
116
117struct cpu_sample {
118 struct cpu_sample *next;
119
120 u64 start_time;
121 u64 end_time;
122 int type;
123 int cpu;
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400124 const char *backtrace;
Arjan van de Ven10274982009-09-12 07:53:05 +0200125};
126
127static struct per_pid *all_data;
128
129#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
Arjan van de Ven10274982009-09-12 07:53:05 +0200161static struct per_pid *find_create_pid(int pid)
162{
163 struct per_pid *cursor = all_data;
164
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;
173 cursor->next = all_data;
174 all_data = cursor;
175 return cursor;
176}
177
178static void pid_set_comm(int pid, char *comm)
179{
180 struct per_pid *p;
181 struct per_pidcomm *c;
182 p = find_create_pid(pid);
183 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
204static void pid_fork(int pid, int ppid, u64 timestamp)
205{
206 struct per_pid *p, *pp;
207 p = find_create_pid(pid);
208 pp = find_create_pid(ppid);
209 p->ppid = ppid;
210 if (pp->current && pp->current->comm && !p->current)
211 pid_set_comm(pid, pp->current->comm);
212
213 p->start_time = timestamp;
214 if (p->current) {
215 p->current->start_time = timestamp;
216 p->current->state_since = timestamp;
217 }
218}
219
220static void pid_exit(int pid, u64 timestamp)
221{
222 struct per_pid *p;
223 p = find_create_pid(pid);
224 p->end_time = timestamp;
225 if (p->current)
226 p->current->end_time = timestamp;
227}
228
229static void
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400230pid_put_sample(int pid, int type, 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
237 p = find_create_pid(pid);
238 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
Irina Tirdea1d037ca2012-09-11 01:15:03 +0300275static int process_comm_event(struct perf_tool *tool __maybe_unused,
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{
Arjan van de Ven8f06d7e2010-01-16 12:53:19 -0800280 pid_set_comm(event->comm.tid, event->comm.comm);
Arjan van de Ven10274982009-09-12 07:53:05 +0200281 return 0;
282}
Arnaldo Carvalho de Melod8f66242009-12-13 19:50:24 -0200283
Irina Tirdea1d037ca2012-09-11 01:15:03 +0300284static int process_fork_event(struct perf_tool *tool __maybe_unused,
Arnaldo Carvalho de Melod20deb62011-11-25 08:19:45 -0200285 union perf_event *event,
Irina Tirdea1d037ca2012-09-11 01:15:03 +0300286 struct perf_sample *sample __maybe_unused,
287 struct machine *machine __maybe_unused)
Arjan van de Ven10274982009-09-12 07:53:05 +0200288{
289 pid_fork(event->fork.pid, event->fork.ppid, event->fork.time);
290 return 0;
291}
292
Irina Tirdea1d037ca2012-09-11 01:15:03 +0300293static int process_exit_event(struct perf_tool *tool __maybe_unused,
Arnaldo Carvalho de Melod20deb62011-11-25 08:19:45 -0200294 union perf_event *event,
Irina Tirdea1d037ca2012-09-11 01:15:03 +0300295 struct perf_sample *sample __maybe_unused,
296 struct machine *machine __maybe_unused)
Arjan van de Ven10274982009-09-12 07:53:05 +0200297{
298 pid_exit(event->fork.pid, event->fork.time);
299 return 0;
300}
301
Thomas Renninger20c457b2011-01-03 17:50:45 +0100302#ifdef SUPPORT_OLD_POWER_EVENTS
303static int use_old_power_events;
Thomas Renninger20c457b2011-01-03 17:50:45 +0100304#endif
305
Arjan van de Ven10274982009-09-12 07:53:05 +0200306static void c_state_start(int cpu, u64 timestamp, int state)
307{
308 cpus_cstate_start_times[cpu] = timestamp;
309 cpus_cstate_state[cpu] = state;
310}
311
312static void c_state_end(int cpu, u64 timestamp)
313{
Arnaldo Carvalho de Meloe0dcd6f2012-09-24 11:16:40 -0300314 struct power_event *pwr = zalloc(sizeof(*pwr));
315
Arjan van de Ven10274982009-09-12 07:53:05 +0200316 if (!pwr)
317 return;
Arjan van de Ven10274982009-09-12 07:53:05 +0200318
319 pwr->state = cpus_cstate_state[cpu];
320 pwr->start_time = cpus_cstate_start_times[cpu];
321 pwr->end_time = timestamp;
322 pwr->cpu = cpu;
323 pwr->type = CSTATE;
324 pwr->next = power_events;
325
326 power_events = pwr;
327}
328
329static void p_state_change(int cpu, u64 timestamp, u64 new_freq)
330{
331 struct power_event *pwr;
Arjan van de Ven10274982009-09-12 07:53:05 +0200332
333 if (new_freq > 8000000) /* detect invalid data */
334 return;
335
Arnaldo Carvalho de Meloe0dcd6f2012-09-24 11:16:40 -0300336 pwr = zalloc(sizeof(*pwr));
Arjan van de Ven10274982009-09-12 07:53:05 +0200337 if (!pwr)
338 return;
Arjan van de Ven10274982009-09-12 07:53:05 +0200339
340 pwr->state = cpus_pstate_state[cpu];
341 pwr->start_time = cpus_pstate_start_times[cpu];
342 pwr->end_time = timestamp;
343 pwr->cpu = cpu;
344 pwr->type = PSTATE;
345 pwr->next = power_events;
346
347 if (!pwr->start_time)
348 pwr->start_time = first_time;
349
350 power_events = pwr;
351
352 cpus_pstate_state[cpu] = new_freq;
353 cpus_pstate_start_times[cpu] = timestamp;
354
355 if ((u64)new_freq > max_freq)
356 max_freq = new_freq;
357
358 if (new_freq < min_freq || min_freq == 0)
359 min_freq = new_freq;
360
361 if (new_freq == max_freq - 1000)
362 turbo_frequency = max_freq;
363}
364
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400365static void sched_wakeup(int cpu, u64 timestamp, int waker, int wakee,
366 u8 flags, const char *backtrace)
Arjan van de Ven10274982009-09-12 07:53:05 +0200367{
Arjan van de Ven10274982009-09-12 07:53:05 +0200368 struct per_pid *p;
Arnaldo Carvalho de Meloe0dcd6f2012-09-24 11:16:40 -0300369 struct wake_event *we = zalloc(sizeof(*we));
Arjan van de Ven10274982009-09-12 07:53:05 +0200370
Arjan van de Ven10274982009-09-12 07:53:05 +0200371 if (!we)
372 return;
373
Arjan van de Ven10274982009-09-12 07:53:05 +0200374 we->time = timestamp;
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400375 we->waker = waker;
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400376 we->backtrace = backtrace;
Arjan van de Ven10274982009-09-12 07:53:05 +0200377
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400378 if ((flags & TRACE_FLAG_HARDIRQ) || (flags & TRACE_FLAG_SOFTIRQ))
Arjan van de Ven10274982009-09-12 07:53:05 +0200379 we->waker = -1;
380
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400381 we->wakee = wakee;
Arjan van de Ven10274982009-09-12 07:53:05 +0200382 we->next = wake_events;
383 wake_events = we;
384 p = find_create_pid(we->wakee);
385
386 if (p && p->current && p->current->state == TYPE_NONE) {
387 p->current->state_since = timestamp;
388 p->current->state = TYPE_WAITING;
389 }
390 if (p && p->current && p->current->state == TYPE_BLOCKED) {
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400391 pid_put_sample(p->pid, p->current->state, cpu,
392 p->current->state_since, timestamp, NULL);
Arjan van de Ven10274982009-09-12 07:53:05 +0200393 p->current->state_since = timestamp;
394 p->current->state = TYPE_WAITING;
395 }
396}
397
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400398static void sched_switch(int cpu, u64 timestamp, int prev_pid, int next_pid,
399 u64 prev_state, const char *backtrace)
Arjan van de Ven10274982009-09-12 07:53:05 +0200400{
401 struct per_pid *p = NULL, *prev_p;
Arjan van de Ven10274982009-09-12 07:53:05 +0200402
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400403 prev_p = find_create_pid(prev_pid);
Arjan van de Ven10274982009-09-12 07:53:05 +0200404
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400405 p = find_create_pid(next_pid);
Arjan van de Ven10274982009-09-12 07:53:05 +0200406
407 if (prev_p->current && prev_p->current->state != TYPE_NONE)
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400408 pid_put_sample(prev_pid, TYPE_RUNNING, cpu,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400409 prev_p->current->state_since, timestamp,
410 backtrace);
Arjan van de Ven10274982009-09-12 07:53:05 +0200411 if (p && p->current) {
412 if (p->current->state != TYPE_NONE)
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400413 pid_put_sample(next_pid, p->current->state, cpu,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400414 p->current->state_since, timestamp,
415 backtrace);
Arjan van de Ven10274982009-09-12 07:53:05 +0200416
Julia Lawall33e26a12010-08-05 22:27:51 +0200417 p->current->state_since = timestamp;
418 p->current->state = TYPE_RUNNING;
Arjan van de Ven10274982009-09-12 07:53:05 +0200419 }
420
421 if (prev_p->current) {
422 prev_p->current->state = TYPE_NONE;
423 prev_p->current->state_since = timestamp;
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400424 if (prev_state & 2)
Arjan van de Ven10274982009-09-12 07:53:05 +0200425 prev_p->current->state = TYPE_BLOCKED;
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400426 if (prev_state == 0)
Arjan van de Ven10274982009-09-12 07:53:05 +0200427 prev_p->current->state = TYPE_WAITING;
428 }
429}
430
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400431static const char *cat_backtrace(union perf_event *event,
432 struct perf_sample *sample,
433 struct machine *machine)
434{
435 struct addr_location al;
436 unsigned int i;
437 char *p = NULL;
438 size_t p_len;
439 u8 cpumode = PERF_RECORD_MISC_USER;
440 struct addr_location tal;
441 struct ip_callchain *chain = sample->callchain;
442 FILE *f = open_memstream(&p, &p_len);
443
444 if (!f) {
445 perror("open_memstream error");
446 return NULL;
447 }
448
449 if (!chain)
450 goto exit;
451
452 if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
453 fprintf(stderr, "problem processing %d event, skipping it.\n",
454 event->header.type);
455 goto exit;
456 }
457
458 for (i = 0; i < chain->nr; i++) {
459 u64 ip;
460
461 if (callchain_param.order == ORDER_CALLEE)
462 ip = chain->ips[i];
463 else
464 ip = chain->ips[chain->nr - i - 1];
465
466 if (ip >= PERF_CONTEXT_MAX) {
467 switch (ip) {
468 case PERF_CONTEXT_HV:
469 cpumode = PERF_RECORD_MISC_HYPERVISOR;
470 break;
471 case PERF_CONTEXT_KERNEL:
472 cpumode = PERF_RECORD_MISC_KERNEL;
473 break;
474 case PERF_CONTEXT_USER:
475 cpumode = PERF_RECORD_MISC_USER;
476 break;
477 default:
478 pr_debug("invalid callchain context: "
479 "%"PRId64"\n", (s64) ip);
480
481 /*
482 * It seems the callchain is corrupted.
483 * Discard all.
484 */
485 free(p);
486 p = NULL;
487 goto exit;
488 }
489 continue;
490 }
491
492 tal.filtered = false;
493 thread__find_addr_location(al.thread, machine, cpumode,
494 MAP__FUNCTION, ip, &tal);
495
496 if (tal.sym)
497 fprintf(f, "..... %016" PRIx64 " %s\n", ip,
498 tal.sym->name);
499 else
500 fprintf(f, "..... %016" PRIx64 "\n", ip);
501 }
502
503exit:
504 fclose(f);
505
506 return p;
507}
508
Jiri Olsa59366782013-07-11 17:28:30 +0200509typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400510 struct perf_sample *sample,
511 const char *backtrace);
Arjan van de Ven10274982009-09-12 07:53:05 +0200512
Irina Tirdea1d037ca2012-09-11 01:15:03 +0300513static int process_sample_event(struct perf_tool *tool __maybe_unused,
Arnaldo Carvalho de Melo972ec652013-11-27 16:32:56 -0300514 union perf_event *event,
Arnaldo Carvalho de Melo8d50e5b2011-01-29 13:02:00 -0200515 struct perf_sample *sample,
Arnaldo Carvalho de Meloe3f42602011-11-16 17:02:54 -0200516 struct perf_evsel *evsel,
Irina Tirdea1d037ca2012-09-11 01:15:03 +0300517 struct machine *machine __maybe_unused)
Arjan van de Ven10274982009-09-12 07:53:05 +0200518{
Arnaldo Carvalho de Meloe3f42602011-11-16 17:02:54 -0200519 if (evsel->attr.sample_type & PERF_SAMPLE_TIME) {
Arnaldo Carvalho de Melo640c03c2010-12-02 14:10:21 -0200520 if (!first_time || first_time > sample->time)
521 first_time = sample->time;
522 if (last_time < sample->time)
523 last_time = sample->time;
Arjan van de Ven10274982009-09-12 07:53:05 +0200524 }
Arjan van de Ven10274982009-09-12 07:53:05 +0200525
Jiri Olsa59366782013-07-11 17:28:30 +0200526 if (sample->cpu > numcpus)
527 numcpus = sample->cpu;
Arjan van de Ven10274982009-09-12 07:53:05 +0200528
Arnaldo Carvalho de Melo744a9712013-11-06 10:17:38 -0300529 if (evsel->handler != NULL) {
530 tracepoint_handler f = evsel->handler;
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400531 return f(evsel, sample, cat_backtrace(event, sample, machine));
Arjan van de Ven10274982009-09-12 07:53:05 +0200532 }
Jiri Olsa59366782013-07-11 17:28:30 +0200533
Arjan van de Ven10274982009-09-12 07:53:05 +0200534 return 0;
535}
536
Jiri Olsa59366782013-07-11 17:28:30 +0200537static int
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400538process_sample_cpu_idle(struct perf_evsel *evsel,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400539 struct perf_sample *sample,
540 const char *backtrace __maybe_unused)
Jiri Olsa59366782013-07-11 17:28:30 +0200541{
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400542 u32 state = perf_evsel__intval(evsel, sample, "state");
543 u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
Jiri Olsa59366782013-07-11 17:28:30 +0200544
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400545 if (state == (u32)PWR_EVENT_EXIT)
546 c_state_end(cpu_id, sample->time);
Jiri Olsa59366782013-07-11 17:28:30 +0200547 else
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400548 c_state_start(cpu_id, sample->time, state);
Jiri Olsa59366782013-07-11 17:28:30 +0200549 return 0;
550}
551
552static int
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400553process_sample_cpu_frequency(struct perf_evsel *evsel,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400554 struct perf_sample *sample,
555 const char *backtrace __maybe_unused)
Jiri Olsa59366782013-07-11 17:28:30 +0200556{
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400557 u32 state = perf_evsel__intval(evsel, sample, "state");
558 u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
Jiri Olsa59366782013-07-11 17:28:30 +0200559
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400560 p_state_change(cpu_id, sample->time, state);
Jiri Olsa59366782013-07-11 17:28:30 +0200561 return 0;
562}
563
564static int
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400565process_sample_sched_wakeup(struct perf_evsel *evsel,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400566 struct perf_sample *sample,
567 const char *backtrace)
Jiri Olsa59366782013-07-11 17:28:30 +0200568{
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400569 u8 flags = perf_evsel__intval(evsel, sample, "common_flags");
570 int waker = perf_evsel__intval(evsel, sample, "common_pid");
571 int wakee = perf_evsel__intval(evsel, sample, "pid");
Jiri Olsa59366782013-07-11 17:28:30 +0200572
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400573 sched_wakeup(sample->cpu, sample->time, waker, wakee, flags, backtrace);
Jiri Olsa59366782013-07-11 17:28:30 +0200574 return 0;
575}
576
577static int
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400578process_sample_sched_switch(struct perf_evsel *evsel,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400579 struct perf_sample *sample,
580 const char *backtrace)
Jiri Olsa59366782013-07-11 17:28:30 +0200581{
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400582 int prev_pid = perf_evsel__intval(evsel, sample, "prev_pid");
583 int next_pid = perf_evsel__intval(evsel, sample, "next_pid");
584 u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state");
Jiri Olsa59366782013-07-11 17:28:30 +0200585
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400586 sched_switch(sample->cpu, sample->time, prev_pid, next_pid, prev_state,
587 backtrace);
Jiri Olsa59366782013-07-11 17:28:30 +0200588 return 0;
589}
590
591#ifdef SUPPORT_OLD_POWER_EVENTS
592static int
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400593process_sample_power_start(struct perf_evsel *evsel,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400594 struct perf_sample *sample,
595 const char *backtrace __maybe_unused)
Jiri Olsa59366782013-07-11 17:28:30 +0200596{
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400597 u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
598 u64 value = perf_evsel__intval(evsel, sample, "value");
Jiri Olsa59366782013-07-11 17:28:30 +0200599
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400600 c_state_start(cpu_id, sample->time, value);
Jiri Olsa59366782013-07-11 17:28:30 +0200601 return 0;
602}
603
604static int
605process_sample_power_end(struct perf_evsel *evsel __maybe_unused,
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{
609 c_state_end(sample->cpu, sample->time);
610 return 0;
611}
612
613static int
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400614process_sample_power_frequency(struct perf_evsel *evsel,
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400615 struct perf_sample *sample,
616 const char *backtrace __maybe_unused)
Jiri Olsa59366782013-07-11 17:28:30 +0200617{
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400618 u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
619 u64 value = perf_evsel__intval(evsel, sample, "value");
Jiri Olsa59366782013-07-11 17:28:30 +0200620
Stanislav Fomichev3ed0d212013-11-27 14:45:00 +0400621 p_state_change(cpu_id, sample->time, value);
Jiri Olsa59366782013-07-11 17:28:30 +0200622 return 0;
623}
624#endif /* SUPPORT_OLD_POWER_EVENTS */
625
Arjan van de Ven10274982009-09-12 07:53:05 +0200626/*
627 * After the last sample we need to wrap up the current C/P state
628 * and close out each CPU for these.
629 */
630static void end_sample_processing(void)
631{
632 u64 cpu;
633 struct power_event *pwr;
634
Arjan van de Ven39a90a82009-09-24 15:40:13 +0200635 for (cpu = 0; cpu <= numcpus; cpu++) {
Arjan van de Ven10274982009-09-12 07:53:05 +0200636 /* C state */
637#if 0
Arnaldo Carvalho de Meloe0dcd6f2012-09-24 11:16:40 -0300638 pwr = zalloc(sizeof(*pwr));
639 if (!pwr)
640 return;
641
Arjan van de Ven10274982009-09-12 07:53:05 +0200642 pwr->state = cpus_cstate_state[cpu];
643 pwr->start_time = cpus_cstate_start_times[cpu];
644 pwr->end_time = last_time;
645 pwr->cpu = cpu;
646 pwr->type = CSTATE;
647 pwr->next = power_events;
648
649 power_events = pwr;
650#endif
651 /* P state */
652
Arnaldo Carvalho de Meloe0dcd6f2012-09-24 11:16:40 -0300653 pwr = zalloc(sizeof(*pwr));
Arjan van de Ven10274982009-09-12 07:53:05 +0200654 if (!pwr)
655 return;
Arjan van de Ven10274982009-09-12 07:53:05 +0200656
657 pwr->state = cpus_pstate_state[cpu];
658 pwr->start_time = cpus_pstate_start_times[cpu];
659 pwr->end_time = last_time;
660 pwr->cpu = cpu;
661 pwr->type = PSTATE;
662 pwr->next = power_events;
663
664 if (!pwr->start_time)
665 pwr->start_time = first_time;
666 if (!pwr->state)
667 pwr->state = min_freq;
668 power_events = pwr;
669 }
670}
671
Arjan van de Ven10274982009-09-12 07:53:05 +0200672/*
673 * Sort the pid datastructure
674 */
675static void sort_pids(void)
676{
677 struct per_pid *new_list, *p, *cursor, *prev;
678 /* sort by ppid first, then by pid, lowest to highest */
679
680 new_list = NULL;
681
682 while (all_data) {
683 p = all_data;
684 all_data = p->next;
685 p->next = NULL;
686
687 if (new_list == NULL) {
688 new_list = p;
689 p->next = NULL;
690 continue;
691 }
692 prev = NULL;
693 cursor = new_list;
694 while (cursor) {
695 if (cursor->ppid > p->ppid ||
696 (cursor->ppid == p->ppid && cursor->pid > p->pid)) {
697 /* must insert before */
698 if (prev) {
699 p->next = prev->next;
700 prev->next = p;
701 cursor = NULL;
702 continue;
703 } else {
704 p->next = new_list;
705 new_list = p;
706 cursor = NULL;
707 continue;
708 }
709 }
710
711 prev = cursor;
712 cursor = cursor->next;
713 if (!cursor)
714 prev->next = p;
715 }
716 }
717 all_data = new_list;
718}
719
720
721static void draw_c_p_states(void)
722{
723 struct power_event *pwr;
724 pwr = power_events;
725
726 /*
727 * two pass drawing so that the P state bars are on top of the C state blocks
728 */
729 while (pwr) {
730 if (pwr->type == CSTATE)
731 svg_cstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
732 pwr = pwr->next;
733 }
734
735 pwr = power_events;
736 while (pwr) {
737 if (pwr->type == PSTATE) {
738 if (!pwr->state)
739 pwr->state = min_freq;
740 svg_pstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
741 }
742 pwr = pwr->next;
743 }
744}
745
746static void draw_wakeups(void)
747{
748 struct wake_event *we;
749 struct per_pid *p;
750 struct per_pidcomm *c;
751
752 we = wake_events;
753 while (we) {
754 int from = 0, to = 0;
Arjan van de Ven4f1202c2009-09-20 18:13:28 +0200755 char *task_from = NULL, *task_to = NULL;
Arjan van de Ven10274982009-09-12 07:53:05 +0200756
757 /* locate the column of the waker and wakee */
758 p = all_data;
759 while (p) {
760 if (p->pid == we->waker || p->pid == we->wakee) {
761 c = p->all;
762 while (c) {
763 if (c->Y && c->start_time <= we->time && c->end_time >= we->time) {
Arjan van de Venbbe29872009-10-20 07:09:39 +0900764 if (p->pid == we->waker && !from) {
Arjan van de Ven10274982009-09-12 07:53:05 +0200765 from = c->Y;
Arjan van de Ven3bc2a392009-10-20 06:46:49 +0900766 task_from = strdup(c->comm);
Arjan van de Ven4f1202c2009-09-20 18:13:28 +0200767 }
Arjan van de Venbbe29872009-10-20 07:09:39 +0900768 if (p->pid == we->wakee && !to) {
Arjan van de Ven10274982009-09-12 07:53:05 +0200769 to = c->Y;
Arjan van de Ven3bc2a392009-10-20 06:46:49 +0900770 task_to = strdup(c->comm);
Arjan van de Ven4f1202c2009-09-20 18:13:28 +0200771 }
Arjan van de Ven10274982009-09-12 07:53:05 +0200772 }
773 c = c->next;
774 }
Arjan van de Ven3bc2a392009-10-20 06:46:49 +0900775 c = p->all;
776 while (c) {
777 if (p->pid == we->waker && !from) {
778 from = c->Y;
779 task_from = strdup(c->comm);
780 }
781 if (p->pid == we->wakee && !to) {
782 to = c->Y;
783 task_to = strdup(c->comm);
784 }
785 c = c->next;
786 }
Arjan van de Ven10274982009-09-12 07:53:05 +0200787 }
788 p = p->next;
789 }
790
Arjan van de Ven3bc2a392009-10-20 06:46:49 +0900791 if (!task_from) {
792 task_from = malloc(40);
793 sprintf(task_from, "[%i]", we->waker);
794 }
795 if (!task_to) {
796 task_to = malloc(40);
797 sprintf(task_to, "[%i]", we->wakee);
798 }
799
Arjan van de Ven10274982009-09-12 07:53:05 +0200800 if (we->waker == -1)
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400801 svg_interrupt(we->time, to, we->backtrace);
Arjan van de Ven10274982009-09-12 07:53:05 +0200802 else if (from && to && abs(from - to) == 1)
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400803 svg_wakeline(we->time, from, to, we->backtrace);
Arjan van de Ven10274982009-09-12 07:53:05 +0200804 else
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400805 svg_partial_wakeline(we->time, from, task_from, to,
806 task_to, we->backtrace);
Arjan van de Ven10274982009-09-12 07:53:05 +0200807 we = we->next;
Arjan van de Ven3bc2a392009-10-20 06:46:49 +0900808
809 free(task_from);
810 free(task_to);
Arjan van de Ven10274982009-09-12 07:53:05 +0200811 }
812}
813
814static void draw_cpu_usage(void)
815{
816 struct per_pid *p;
817 struct per_pidcomm *c;
818 struct cpu_sample *sample;
819 p = all_data;
820 while (p) {
821 c = p->all;
822 while (c) {
823 sample = c->samples;
824 while (sample) {
825 if (sample->type == TYPE_RUNNING)
826 svg_process(sample->cpu, sample->start_time, sample->end_time, "sample", c->comm);
827
828 sample = sample->next;
829 }
830 c = c->next;
831 }
832 p = p->next;
833 }
834}
835
836static void draw_process_bars(void)
837{
838 struct per_pid *p;
839 struct per_pidcomm *c;
840 struct cpu_sample *sample;
841 int Y = 0;
842
843 Y = 2 * numcpus + 2;
844
845 p = all_data;
846 while (p) {
847 c = p->all;
848 while (c) {
849 if (!c->display) {
850 c->Y = 0;
851 c = c->next;
852 continue;
853 }
854
Arjan van de Vena92fe7b2009-09-20 18:13:53 +0200855 svg_box(Y, c->start_time, c->end_time, "process");
Arjan van de Ven10274982009-09-12 07:53:05 +0200856 sample = c->samples;
857 while (sample) {
858 if (sample->type == TYPE_RUNNING)
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400859 svg_running(Y, sample->cpu,
860 sample->start_time,
861 sample->end_time,
862 sample->backtrace);
Arjan van de Ven10274982009-09-12 07:53:05 +0200863 if (sample->type == TYPE_BLOCKED)
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400864 svg_blocked(Y, sample->cpu,
865 sample->start_time,
866 sample->end_time,
867 sample->backtrace);
Arjan van de Ven10274982009-09-12 07:53:05 +0200868 if (sample->type == TYPE_WAITING)
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +0400869 svg_waiting(Y, sample->cpu,
870 sample->start_time,
871 sample->end_time,
872 sample->backtrace);
Arjan van de Ven10274982009-09-12 07:53:05 +0200873 sample = sample->next;
874 }
875
876 if (c->comm) {
877 char comm[256];
878 if (c->total_time > 5000000000) /* 5 seconds */
879 sprintf(comm, "%s:%i (%2.2fs)", c->comm, p->pid, c->total_time / 1000000000.0);
880 else
881 sprintf(comm, "%s:%i (%3.1fms)", c->comm, p->pid, c->total_time / 1000000.0);
882
883 svg_text(Y, c->start_time, comm);
884 }
885 c->Y = Y;
886 Y++;
887 c = c->next;
888 }
889 p = p->next;
890 }
891}
892
Arjan van de Venbbe29872009-10-20 07:09:39 +0900893static void add_process_filter(const char *string)
894{
Arnaldo Carvalho de Meloe0dcd6f2012-09-24 11:16:40 -0300895 int pid = strtoull(string, NULL, 10);
896 struct process_filter *filt = malloc(sizeof(*filt));
Arjan van de Venbbe29872009-10-20 07:09:39 +0900897
Arjan van de Venbbe29872009-10-20 07:09:39 +0900898 if (!filt)
899 return;
900
901 filt->name = strdup(string);
902 filt->pid = pid;
903 filt->next = process_filter;
904
905 process_filter = filt;
906}
907
908static int passes_filter(struct per_pid *p, struct per_pidcomm *c)
909{
910 struct process_filter *filt;
911 if (!process_filter)
912 return 1;
913
914 filt = process_filter;
915 while (filt) {
916 if (filt->pid && p->pid == filt->pid)
917 return 1;
918 if (strcmp(filt->name, c->comm) == 0)
919 return 1;
920 filt = filt->next;
921 }
922 return 0;
923}
924
925static int determine_display_tasks_filtered(void)
926{
927 struct per_pid *p;
928 struct per_pidcomm *c;
929 int count = 0;
930
931 p = all_data;
932 while (p) {
933 p->display = 0;
934 if (p->start_time == 1)
935 p->start_time = first_time;
936
937 /* no exit marker, task kept running to the end */
938 if (p->end_time == 0)
939 p->end_time = last_time;
940
941 c = p->all;
942
943 while (c) {
944 c->display = 0;
945
946 if (c->start_time == 1)
947 c->start_time = first_time;
948
949 if (passes_filter(p, c)) {
950 c->display = 1;
951 p->display = 1;
952 count++;
953 }
954
955 if (c->end_time == 0)
956 c->end_time = last_time;
957
958 c = c->next;
959 }
960 p = p->next;
961 }
962 return count;
963}
964
Arjan van de Ven10274982009-09-12 07:53:05 +0200965static int determine_display_tasks(u64 threshold)
966{
967 struct per_pid *p;
968 struct per_pidcomm *c;
969 int count = 0;
970
Arjan van de Venbbe29872009-10-20 07:09:39 +0900971 if (process_filter)
972 return determine_display_tasks_filtered();
973
Arjan van de Ven10274982009-09-12 07:53:05 +0200974 p = all_data;
975 while (p) {
976 p->display = 0;
977 if (p->start_time == 1)
978 p->start_time = first_time;
979
980 /* no exit marker, task kept running to the end */
981 if (p->end_time == 0)
982 p->end_time = last_time;
Stanislav Fomichev753c5052013-11-01 20:25:47 +0400983 if (p->total_time >= threshold)
Arjan van de Ven10274982009-09-12 07:53:05 +0200984 p->display = 1;
985
986 c = p->all;
987
988 while (c) {
989 c->display = 0;
990
991 if (c->start_time == 1)
992 c->start_time = first_time;
993
Stanislav Fomichev753c5052013-11-01 20:25:47 +0400994 if (c->total_time >= threshold) {
Arjan van de Ven10274982009-09-12 07:53:05 +0200995 c->display = 1;
996 count++;
997 }
998
999 if (c->end_time == 0)
1000 c->end_time = last_time;
1001
1002 c = c->next;
1003 }
1004 p = p->next;
1005 }
1006 return count;
1007}
1008
1009
1010
1011#define TIME_THRESH 10000000
1012
1013static void write_svg_file(const char *filename)
1014{
1015 u64 i;
1016 int count;
Stanislav Fomichev0a8eb272013-11-01 20:25:45 +04001017 int thresh = TIME_THRESH;
Arjan van de Ven10274982009-09-12 07:53:05 +02001018
1019 numcpus++;
1020
Stanislav Fomichev753c5052013-11-01 20:25:47 +04001021 if (power_only)
1022 proc_num = 0;
Arjan van de Ven10274982009-09-12 07:53:05 +02001023
Stanislav Fomichev0a8eb272013-11-01 20:25:45 +04001024 /* We'd like to show at least proc_num tasks;
1025 * be less picky if we have fewer */
1026 do {
1027 count = determine_display_tasks(thresh);
1028 thresh /= 10;
Stanislav Fomichev54874e32013-11-01 20:25:46 +04001029 } while (!process_filter && thresh && count < proc_num);
Arjan van de Ven10274982009-09-12 07:53:05 +02001030
Arjan van de Ven5094b652009-09-20 18:14:16 +02001031 open_svg(filename, numcpus, count, first_time, last_time);
Arjan van de Ven10274982009-09-12 07:53:05 +02001032
Arjan van de Ven5094b652009-09-20 18:14:16 +02001033 svg_time_grid();
Arjan van de Ven10274982009-09-12 07:53:05 +02001034 svg_legenda();
1035
1036 for (i = 0; i < numcpus; i++)
1037 svg_cpu_box(i, max_freq, turbo_frequency);
1038
1039 draw_cpu_usage();
Stanislav Fomichev753c5052013-11-01 20:25:47 +04001040 if (proc_num)
1041 draw_process_bars();
Stanislav Fomichevc87097d2013-11-01 20:25:48 +04001042 if (!tasks_only)
1043 draw_c_p_states();
Stanislav Fomichev753c5052013-11-01 20:25:47 +04001044 if (proc_num)
1045 draw_wakeups();
Arjan van de Ven10274982009-09-12 07:53:05 +02001046
1047 svg_close();
1048}
1049
Feng Tang70cb4e92012-10-30 11:56:02 +08001050static int __cmd_timechart(const char *output_name)
Arjan van de Ven10274982009-09-12 07:53:05 +02001051{
Arnaldo Carvalho de Melo73bdc712012-10-01 15:20:58 -03001052 struct perf_tool perf_timechart = {
1053 .comm = process_comm_event,
1054 .fork = process_fork_event,
1055 .exit = process_exit_event,
1056 .sample = process_sample_event,
1057 .ordered_samples = true,
1058 };
Jiri Olsa59366782013-07-11 17:28:30 +02001059 const struct perf_evsel_str_handler power_tracepoints[] = {
1060 { "power:cpu_idle", process_sample_cpu_idle },
1061 { "power:cpu_frequency", process_sample_cpu_frequency },
1062 { "sched:sched_wakeup", process_sample_sched_wakeup },
1063 { "sched:sched_switch", process_sample_sched_switch },
1064#ifdef SUPPORT_OLD_POWER_EVENTS
1065 { "power:power_start", process_sample_power_start },
1066 { "power:power_end", process_sample_power_end },
1067 { "power:power_frequency", process_sample_power_frequency },
1068#endif
1069 };
Jiri Olsaf5fc1412013-10-15 16:27:32 +02001070 struct perf_data_file file = {
1071 .path = input_name,
1072 .mode = PERF_DATA_MODE_READ,
1073 };
1074
1075 struct perf_session *session = perf_session__new(&file, false,
1076 &perf_timechart);
Arnaldo Carvalho de Melod549c7692009-12-27 21:37:02 -02001077 int ret = -EINVAL;
Arjan van de Ven10274982009-09-12 07:53:05 +02001078
Arnaldo Carvalho de Melo94c744b2009-12-11 21:24:02 -02001079 if (session == NULL)
1080 return -ENOMEM;
1081
Arnaldo Carvalho de Melod549c7692009-12-27 21:37:02 -02001082 if (!perf_session__has_traces(session, "timechart record"))
1083 goto out_delete;
1084
Jiri Olsa59366782013-07-11 17:28:30 +02001085 if (perf_session__set_tracepoints_handlers(session,
1086 power_tracepoints)) {
1087 pr_err("Initializing session tracepoint handlers failed\n");
1088 goto out_delete;
1089 }
1090
Arnaldo Carvalho de Melo45694aa2011-11-28 08:30:20 -02001091 ret = perf_session__process_events(session, &perf_timechart);
Li Zefan5cbd0802009-12-01 14:05:16 +08001092 if (ret)
Arnaldo Carvalho de Melo94c744b2009-12-11 21:24:02 -02001093 goto out_delete;
Arjan van de Ven10274982009-09-12 07:53:05 +02001094
Arjan van de Ven10274982009-09-12 07:53:05 +02001095 end_sample_processing();
1096
1097 sort_pids();
1098
1099 write_svg_file(output_name);
1100
Arnaldo Carvalho de Melo6beba7a2009-10-21 17:34:06 -02001101 pr_info("Written %2.1f seconds of trace to %s.\n",
1102 (last_time - first_time) / 1000000000.0, output_name);
Arnaldo Carvalho de Melo94c744b2009-12-11 21:24:02 -02001103out_delete:
1104 perf_session__delete(session);
1105 return ret;
Arjan van de Ven10274982009-09-12 07:53:05 +02001106}
1107
Arjan van de Ven3c09eeb2009-09-19 13:34:42 +02001108static int __cmd_record(int argc, const char **argv)
1109{
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001110 unsigned int rec_argc, i, j;
1111 const char **rec_argv;
1112 const char **p;
1113 unsigned int record_elems;
1114
1115 const char * const common_args[] = {
Jiri Olsa4a4d3712013-06-05 13:37:21 +02001116 "record", "-a", "-R", "-c", "1",
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001117 };
1118 unsigned int common_args_nr = ARRAY_SIZE(common_args);
1119
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +04001120 const char * const backtrace_args[] = {
1121 "-g",
1122 };
1123 unsigned int backtrace_args_no = ARRAY_SIZE(backtrace_args);
1124
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001125 const char * const power_args[] = {
1126 "-e", "power:cpu_frequency",
1127 "-e", "power:cpu_idle",
1128 };
1129 unsigned int power_args_nr = ARRAY_SIZE(power_args);
1130
1131 const char * const old_power_args[] = {
1132#ifdef SUPPORT_OLD_POWER_EVENTS
Arnaldo Carvalho de Melo73bdc712012-10-01 15:20:58 -03001133 "-e", "power:power_start",
1134 "-e", "power:power_end",
1135 "-e", "power:power_frequency",
Arnaldo Carvalho de Melo73bdc712012-10-01 15:20:58 -03001136#endif
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001137 };
1138 unsigned int old_power_args_nr = ARRAY_SIZE(old_power_args);
1139
1140 const char * const tasks_args[] = {
Arnaldo Carvalho de Melo73bdc712012-10-01 15:20:58 -03001141 "-e", "sched:sched_wakeup",
1142 "-e", "sched:sched_switch",
1143 };
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001144 unsigned int tasks_args_nr = ARRAY_SIZE(tasks_args);
Arjan van de Ven3c09eeb2009-09-19 13:34:42 +02001145
Thomas Renninger20c457b2011-01-03 17:50:45 +01001146#ifdef SUPPORT_OLD_POWER_EVENTS
1147 if (!is_valid_tracepoint("power:cpu_idle") &&
1148 is_valid_tracepoint("power:power_start")) {
1149 use_old_power_events = 1;
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001150 power_args_nr = 0;
1151 } else {
1152 old_power_args_nr = 0;
Thomas Renninger20c457b2011-01-03 17:50:45 +01001153 }
1154#endif
1155
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001156 if (power_only)
1157 tasks_args_nr = 0;
1158
1159 if (tasks_only) {
1160 power_args_nr = 0;
1161 old_power_args_nr = 0;
1162 }
1163
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +04001164 if (!with_backtrace)
1165 backtrace_args_no = 0;
1166
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001167 record_elems = common_args_nr + tasks_args_nr +
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +04001168 power_args_nr + old_power_args_nr + backtrace_args_no;
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001169
1170 rec_argc = record_elems + argc;
Arjan van de Ven3c09eeb2009-09-19 13:34:42 +02001171 rec_argv = calloc(rec_argc + 1, sizeof(char *));
1172
Chris Samuelce47dc52010-11-13 13:35:06 +11001173 if (rec_argv == NULL)
1174 return -ENOMEM;
1175
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001176 p = rec_argv;
1177 for (i = 0; i < common_args_nr; i++)
1178 *p++ = strdup(common_args[i]);
Arjan van de Ven3c09eeb2009-09-19 13:34:42 +02001179
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +04001180 for (i = 0; i < backtrace_args_no; i++)
1181 *p++ = strdup(backtrace_args[i]);
1182
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001183 for (i = 0; i < tasks_args_nr; i++)
1184 *p++ = strdup(tasks_args[i]);
Arjan van de Ven3c09eeb2009-09-19 13:34:42 +02001185
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001186 for (i = 0; i < power_args_nr; i++)
1187 *p++ = strdup(power_args[i]);
1188
1189 for (i = 0; i < old_power_args_nr; i++)
1190 *p++ = strdup(old_power_args[i]);
1191
1192 for (j = 1; j < (unsigned int)argc; j++)
1193 *p++ = argv[j];
1194
1195 return cmd_record(rec_argc, rec_argv, NULL);
Arjan van de Ven3c09eeb2009-09-19 13:34:42 +02001196}
1197
Arjan van de Venbbe29872009-10-20 07:09:39 +09001198static int
Irina Tirdea1d037ca2012-09-11 01:15:03 +03001199parse_process(const struct option *opt __maybe_unused, const char *arg,
1200 int __maybe_unused unset)
Arjan van de Venbbe29872009-10-20 07:09:39 +09001201{
1202 if (arg)
1203 add_process_filter(arg);
1204 return 0;
1205}
1206
Arnaldo Carvalho de Melo73bdc712012-10-01 15:20:58 -03001207int cmd_timechart(int argc, const char **argv,
1208 const char *prefix __maybe_unused)
1209{
Arnaldo Carvalho de Melo73bdc712012-10-01 15:20:58 -03001210 const char *output_name = "output.svg";
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001211 const struct option timechart_options[] = {
Arnaldo Carvalho de Melo73bdc712012-10-01 15:20:58 -03001212 OPT_STRING('i', "input", &input_name, "file", "input file name"),
1213 OPT_STRING('o', "output", &output_name, "file", "output file name"),
1214 OPT_INTEGER('w', "width", &svg_page_width, "page width"),
1215 OPT_BOOLEAN('P', "power-only", &power_only, "output power data only"),
Stanislav Fomichevc87097d2013-11-01 20:25:48 +04001216 OPT_BOOLEAN('T', "tasks-only", &tasks_only,
1217 "output processes data only"),
Arjan van de Venbbe29872009-10-20 07:09:39 +09001218 OPT_CALLBACK('p', "process", NULL, "process",
1219 "process selector. Pass a pid or process name.",
1220 parse_process),
David Ahernec5761e2010-12-09 13:27:07 -07001221 OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
1222 "Look for files with symbols relative to this directory"),
Stanislav Fomichev54874e32013-11-01 20:25:46 +04001223 OPT_INTEGER('n', "proc-num", &proc_num,
1224 "min. number of tasks to print"),
Arjan van de Ven10274982009-09-12 07:53:05 +02001225 OPT_END()
Arnaldo Carvalho de Melo73bdc712012-10-01 15:20:58 -03001226 };
1227 const char * const timechart_usage[] = {
1228 "perf timechart [<options>] {record}",
1229 NULL
1230 };
Arjan van de Ven10274982009-09-12 07:53:05 +02001231
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001232 const struct option record_options[] = {
1233 OPT_BOOLEAN('P', "power-only", &power_only, "output power data only"),
1234 OPT_BOOLEAN('T', "tasks-only", &tasks_only,
1235 "output processes data only"),
Stanislav Fomichev6f8d67f2013-11-01 20:25:51 +04001236 OPT_BOOLEAN('g', "callchain", &with_backtrace, "record callchain"),
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001237 OPT_END()
1238 };
1239 const char * const record_usage[] = {
1240 "perf timechart record [<options>]",
1241 NULL
1242 };
1243 argc = parse_options(argc, argv, timechart_options, timechart_usage,
Arjan van de Ven3c09eeb2009-09-19 13:34:42 +02001244 PARSE_OPT_STOP_AT_NON_OPTION);
Arjan van de Ven10274982009-09-12 07:53:05 +02001245
Stanislav Fomichevc87097d2013-11-01 20:25:48 +04001246 if (power_only && tasks_only) {
1247 pr_err("-P and -T options cannot be used at the same time.\n");
1248 return -1;
1249 }
1250
Arnaldo Carvalho de Melo655000e2009-12-15 20:04:40 -02001251 symbol__init();
1252
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001253 if (argc && !strncmp(argv[0], "rec", 3)) {
1254 argc = parse_options(argc, argv, record_options, record_usage,
1255 PARSE_OPT_STOP_AT_NON_OPTION);
1256
1257 if (power_only && tasks_only) {
1258 pr_err("-P and -T options cannot be used at the same time.\n");
1259 return -1;
1260 }
1261
Arjan van de Ven3c09eeb2009-09-19 13:34:42 +02001262 return __cmd_record(argc, argv);
Stanislav Fomichev367b3152013-11-01 20:25:50 +04001263 } else if (argc)
1264 usage_with_options(timechart_usage, timechart_options);
Arjan van de Ven10274982009-09-12 07:53:05 +02001265
1266 setup_pager();
1267
Feng Tang70cb4e92012-10-30 11:56:02 +08001268 return __cmd_timechart(output_name);
Arjan van de Ven10274982009-09-12 07:53:05 +02001269}