Blame view

tools/perf/builtin-timechart.c 23.5 KB
10274989f   Arjan van de Ven   perf: Add the tim...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  /*
   * builtin-timechart.c - make an svg timechart of system activity
   *
   * (C) Copyright 2009 Intel Corporation
   *
   * Authors:
   *     Arjan van de Ven <arjan@linux.intel.com>
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; version 2
   * of the License.
   */
  
  #include "builtin.h"
  
  #include "util/util.h"
  
  #include "util/color.h"
  #include <linux/list.h>
  #include "util/cache.h"
e3f426096   Arnaldo Carvalho de Melo   perf tools: Use e...
22
  #include "util/evsel.h"
10274989f   Arjan van de Ven   perf: Add the tim...
23
24
  #include <linux/rbtree.h>
  #include "util/symbol.h"
10274989f   Arjan van de Ven   perf: Add the tim...
25
26
27
28
29
30
31
  #include "util/callchain.h"
  #include "util/strlist.h"
  
  #include "perf.h"
  #include "util/header.h"
  #include "util/parse-options.h"
  #include "util/parse-events.h"
5cbd08056   Li Zefan   perf timechart: R...
32
  #include "util/event.h"
301a0b020   Arnaldo Carvalho de Melo   perf session: Dit...
33
  #include "util/session.h"
10274989f   Arjan van de Ven   perf: Add the tim...
34
  #include "util/svghelper.h"
45694aa77   Arnaldo Carvalho de Melo   perf tools: Renam...
35
  #include "util/tool.h"
10274989f   Arjan van de Ven   perf: Add the tim...
36

20c457b85   Thomas Renninger   perf timechart: A...
37
38
  #define SUPPORT_OLD_POWER_EVENTS 1
  #define PWR_EVENT_EXIT -1
efad14150   Robert Richter   perf report: Acce...
39
40
  static const char	*input_name;
  static const char	*output_name = "output.svg";
10274989f   Arjan van de Ven   perf: Add the tim...
41

10274989f   Arjan van de Ven   perf: Add the tim...
42
43
44
45
46
47
  static unsigned int	numcpus;
  static u64		min_freq;	/* Lowest CPU frequency seen */
  static u64		max_freq;	/* Highest CPU frequency seen */
  static u64		turbo_frequency;
  
  static u64		first_time, last_time;
c05556421   Ian Munsie   perf: Fix endiann...
48
  static bool		power_only;
39a90a8ef   Arjan van de Ven   perf timechart: A...
49

10274989f   Arjan van de Ven   perf: Add the tim...
50

10274989f   Arjan van de Ven   perf: Add the tim...
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
  struct per_pid;
  struct per_pidcomm;
  
  struct cpu_sample;
  struct power_event;
  struct wake_event;
  
  struct sample_wrapper;
  
  /*
   * Datastructure layout:
   * We keep an list of "pid"s, matching the kernels notion of a task struct.
   * Each "pid" entry, has a list of "comm"s.
   *	this is because we want to track different programs different, while
   *	exec will reuse the original pid (by design).
   * Each comm has a list of samples that will be used to draw
   * final graph.
   */
  
  struct per_pid {
  	struct per_pid *next;
  
  	int		pid;
  	int		ppid;
  
  	u64		start_time;
  	u64		end_time;
  	u64		total_time;
  	int		display;
  
  	struct per_pidcomm *all;
  	struct per_pidcomm *current;
10274989f   Arjan van de Ven   perf: Add the tim...
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
  };
  
  
  struct per_pidcomm {
  	struct per_pidcomm *next;
  
  	u64		start_time;
  	u64		end_time;
  	u64		total_time;
  
  	int		Y;
  	int		display;
  
  	long		state;
  	u64		state_since;
  
  	char		*comm;
  
  	struct cpu_sample *samples;
  };
  
  struct sample_wrapper {
  	struct sample_wrapper *next;
  
  	u64		timestamp;
  	unsigned char	data[0];
  };
  
  #define TYPE_NONE	0
  #define TYPE_RUNNING	1
  #define TYPE_WAITING	2
  #define TYPE_BLOCKED	3
  
  struct cpu_sample {
  	struct cpu_sample *next;
  
  	u64 start_time;
  	u64 end_time;
  	int type;
  	int cpu;
  };
  
  static struct per_pid *all_data;
  
  #define CSTATE 1
  #define PSTATE 2
  
  struct power_event {
  	struct power_event *next;
  	int type;
  	int state;
  	u64 start_time;
  	u64 end_time;
  	int cpu;
  };
  
  struct wake_event {
  	struct wake_event *next;
  	int waker;
  	int wakee;
  	u64 time;
  };
  
  static struct power_event    *power_events;
  static struct wake_event     *wake_events;
bbe2987be   Arjan van de Ven   perf timechart: A...
148
149
  struct process_filter;
  struct process_filter {
5cbd08056   Li Zefan   perf timechart: R...
150
151
152
  	char			*name;
  	int			pid;
  	struct process_filter	*next;
bbe2987be   Arjan van de Ven   perf timechart: A...
153
154
155
  };
  
  static struct process_filter *process_filter;
10274989f   Arjan van de Ven   perf: Add the tim...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
  static struct per_pid *find_create_pid(int pid)
  {
  	struct per_pid *cursor = all_data;
  
  	while (cursor) {
  		if (cursor->pid == pid)
  			return cursor;
  		cursor = cursor->next;
  	}
  	cursor = malloc(sizeof(struct per_pid));
  	assert(cursor != NULL);
  	memset(cursor, 0, sizeof(struct per_pid));
  	cursor->pid = pid;
  	cursor->next = all_data;
  	all_data = cursor;
  	return cursor;
  }
  
  static void pid_set_comm(int pid, char *comm)
  {
  	struct per_pid *p;
  	struct per_pidcomm *c;
  	p = find_create_pid(pid);
  	c = p->all;
  	while (c) {
  		if (c->comm && strcmp(c->comm, comm) == 0) {
  			p->current = c;
  			return;
  		}
  		if (!c->comm) {
  			c->comm = strdup(comm);
  			p->current = c;
  			return;
  		}
  		c = c->next;
  	}
  	c = malloc(sizeof(struct per_pidcomm));
  	assert(c != NULL);
  	memset(c, 0, sizeof(struct per_pidcomm));
  	c->comm = strdup(comm);
  	p->current = c;
  	c->next = p->all;
  	p->all = c;
  }
  
  static void pid_fork(int pid, int ppid, u64 timestamp)
  {
  	struct per_pid *p, *pp;
  	p = find_create_pid(pid);
  	pp = find_create_pid(ppid);
  	p->ppid = ppid;
  	if (pp->current && pp->current->comm && !p->current)
  		pid_set_comm(pid, pp->current->comm);
  
  	p->start_time = timestamp;
  	if (p->current) {
  		p->current->start_time = timestamp;
  		p->current->state_since = timestamp;
  	}
  }
  
  static void pid_exit(int pid, u64 timestamp)
  {
  	struct per_pid *p;
  	p = find_create_pid(pid);
  	p->end_time = timestamp;
  	if (p->current)
  		p->current->end_time = timestamp;
  }
  
  static void
  pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end)
  {
  	struct per_pid *p;
  	struct per_pidcomm *c;
  	struct cpu_sample *sample;
  
  	p = find_create_pid(pid);
  	c = p->current;
  	if (!c) {
  		c = malloc(sizeof(struct per_pidcomm));
  		assert(c != NULL);
  		memset(c, 0, sizeof(struct per_pidcomm));
  		p->current = c;
  		c->next = p->all;
  		p->all = c;
  	}
  
  	sample = malloc(sizeof(struct cpu_sample));
  	assert(sample != NULL);
  	memset(sample, 0, sizeof(struct cpu_sample));
  	sample->start_time = start;
  	sample->end_time = end;
  	sample->type = type;
  	sample->next = c->samples;
  	sample->cpu = cpu;
  	c->samples = sample;
  
  	if (sample->type == TYPE_RUNNING && end > start && start > 0) {
  		c->total_time += (end-start);
  		p->total_time += (end-start);
  	}
  
  	if (c->start_time == 0 || c->start_time > start)
  		c->start_time = start;
  	if (p->start_time == 0 || p->start_time > start)
  		p->start_time = start;
10274989f   Arjan van de Ven   perf: Add the tim...
263
264
265
266
267
268
269
270
  }
  
  #define MAX_CPUS 4096
  
  static u64 cpus_cstate_start_times[MAX_CPUS];
  static int cpus_cstate_state[MAX_CPUS];
  static u64 cpus_pstate_start_times[MAX_CPUS];
  static u64 cpus_pstate_state[MAX_CPUS];
45694aa77   Arnaldo Carvalho de Melo   perf tools: Renam...
271
  static int process_comm_event(struct perf_tool *tool __used,
d20deb64e   Arnaldo Carvalho de Melo   perf tools: Pass ...
272
  			      union perf_event *event,
8115d60c3   Arnaldo Carvalho de Melo   perf tools: Kill ...
273
  			      struct perf_sample *sample __used,
743eb8686   Arnaldo Carvalho de Melo   perf tools: Resol...
274
  			      struct machine *machine __used)
10274989f   Arjan van de Ven   perf: Add the tim...
275
  {
8f06d7e6e   Arjan van de Ven   perf timechart: U...
276
  	pid_set_comm(event->comm.tid, event->comm.comm);
10274989f   Arjan van de Ven   perf: Add the tim...
277
278
  	return 0;
  }
d8f66248d   Arnaldo Carvalho de Melo   perf session: Pas...
279

45694aa77   Arnaldo Carvalho de Melo   perf tools: Renam...
280
  static int process_fork_event(struct perf_tool *tool __used,
d20deb64e   Arnaldo Carvalho de Melo   perf tools: Pass ...
281
  			      union perf_event *event,
8115d60c3   Arnaldo Carvalho de Melo   perf tools: Kill ...
282
  			      struct perf_sample *sample __used,
743eb8686   Arnaldo Carvalho de Melo   perf tools: Resol...
283
  			      struct machine *machine __used)
10274989f   Arjan van de Ven   perf: Add the tim...
284
285
286
287
  {
  	pid_fork(event->fork.pid, event->fork.ppid, event->fork.time);
  	return 0;
  }
45694aa77   Arnaldo Carvalho de Melo   perf tools: Renam...
288
  static int process_exit_event(struct perf_tool *tool __used,
d20deb64e   Arnaldo Carvalho de Melo   perf tools: Pass ...
289
  			      union perf_event *event,
8115d60c3   Arnaldo Carvalho de Melo   perf tools: Kill ...
290
  			      struct perf_sample *sample __used,
743eb8686   Arnaldo Carvalho de Melo   perf tools: Resol...
291
  			      struct machine *machine __used)
10274989f   Arjan van de Ven   perf: Add the tim...
292
293
294
295
296
297
  {
  	pid_exit(event->fork.pid, event->fork.time);
  	return 0;
  }
  
  struct trace_entry {
10274989f   Arjan van de Ven   perf: Add the tim...
298
299
300
301
  	unsigned short		type;
  	unsigned char		flags;
  	unsigned char		preempt_count;
  	int			pid;
028c51525   OGAWA Hirofumi   perf timechart: F...
302
  	int			lock_depth;
10274989f   Arjan van de Ven   perf: Add the tim...
303
  };
20c457b85   Thomas Renninger   perf timechart: A...
304
305
306
  #ifdef SUPPORT_OLD_POWER_EVENTS
  static int use_old_power_events;
  struct power_entry_old {
10274989f   Arjan van de Ven   perf: Add the tim...
307
  	struct trace_entry te;
4c21adf26   Thomas Renninger   x86 cpufreq, perf...
308
309
310
  	u64	type;
  	u64	value;
  	u64	cpu_id;
10274989f   Arjan van de Ven   perf: Add the tim...
311
  };
20c457b85   Thomas Renninger   perf timechart: A...
312
313
314
315
316
317
318
  #endif
  
  struct power_processor_entry {
  	struct trace_entry te;
  	u32	state;
  	u32	cpu_id;
  };
10274989f   Arjan van de Ven   perf: Add the tim...
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
  
  #define TASK_COMM_LEN 16
  struct wakeup_entry {
  	struct trace_entry te;
  	char comm[TASK_COMM_LEN];
  	int   pid;
  	int   prio;
  	int   success;
  };
  
  /*
   * trace_flag_type is an enumeration that holds different
   * states when a trace occurs. These are:
   *  IRQS_OFF            - interrupts were disabled
   *  IRQS_NOSUPPORT      - arch does not support irqs_disabled_flags
   *  NEED_RESCED         - reschedule is requested
   *  HARDIRQ             - inside an interrupt handler
   *  SOFTIRQ             - inside a softirq handler
   */
  enum trace_flag_type {
  	TRACE_FLAG_IRQS_OFF		= 0x01,
  	TRACE_FLAG_IRQS_NOSUPPORT	= 0x02,
  	TRACE_FLAG_NEED_RESCHED		= 0x04,
  	TRACE_FLAG_HARDIRQ		= 0x08,
  	TRACE_FLAG_SOFTIRQ		= 0x10,
  };
  
  
  
  struct sched_switch {
  	struct trace_entry te;
  	char prev_comm[TASK_COMM_LEN];
  	int  prev_pid;
  	int  prev_prio;
  	long prev_state; /* Arjan weeps. */
  	char next_comm[TASK_COMM_LEN];
  	int  next_pid;
  	int  next_prio;
  };
  
  static void c_state_start(int cpu, u64 timestamp, int state)
  {
  	cpus_cstate_start_times[cpu] = timestamp;
  	cpus_cstate_state[cpu] = state;
  }
  
  static void c_state_end(int cpu, u64 timestamp)
  {
  	struct power_event *pwr;
  	pwr = malloc(sizeof(struct power_event));
  	if (!pwr)
  		return;
  	memset(pwr, 0, sizeof(struct power_event));
  
  	pwr->state = cpus_cstate_state[cpu];
  	pwr->start_time = cpus_cstate_start_times[cpu];
  	pwr->end_time = timestamp;
  	pwr->cpu = cpu;
  	pwr->type = CSTATE;
  	pwr->next = power_events;
  
  	power_events = pwr;
  }
  
  static void p_state_change(int cpu, u64 timestamp, u64 new_freq)
  {
  	struct power_event *pwr;
  	pwr = malloc(sizeof(struct power_event));
  
  	if (new_freq > 8000000) /* detect invalid data */
  		return;
  
  	if (!pwr)
  		return;
  	memset(pwr, 0, sizeof(struct power_event));
  
  	pwr->state = cpus_pstate_state[cpu];
  	pwr->start_time = cpus_pstate_start_times[cpu];
  	pwr->end_time = timestamp;
  	pwr->cpu = cpu;
  	pwr->type = PSTATE;
  	pwr->next = power_events;
  
  	if (!pwr->start_time)
  		pwr->start_time = first_time;
  
  	power_events = pwr;
  
  	cpus_pstate_state[cpu] = new_freq;
  	cpus_pstate_start_times[cpu] = timestamp;
  
  	if ((u64)new_freq > max_freq)
  		max_freq = new_freq;
  
  	if (new_freq < min_freq || min_freq == 0)
  		min_freq = new_freq;
  
  	if (new_freq == max_freq - 1000)
  			turbo_frequency = max_freq;
  }
  
  static void
  sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te)
  {
  	struct wake_event *we;
  	struct per_pid *p;
  	struct wakeup_entry *wake = (void *)te;
  
  	we = malloc(sizeof(struct wake_event));
  	if (!we)
  		return;
  
  	memset(we, 0, sizeof(struct wake_event));
  	we->time = timestamp;
  	we->waker = pid;
  
  	if ((te->flags & TRACE_FLAG_HARDIRQ) || (te->flags & TRACE_FLAG_SOFTIRQ))
  		we->waker = -1;
  
  	we->wakee = wake->pid;
  	we->next = wake_events;
  	wake_events = we;
  	p = find_create_pid(we->wakee);
  
  	if (p && p->current && p->current->state == TYPE_NONE) {
  		p->current->state_since = timestamp;
  		p->current->state = TYPE_WAITING;
  	}
  	if (p && p->current && p->current->state == TYPE_BLOCKED) {
  		pid_put_sample(p->pid, p->current->state, cpu, p->current->state_since, timestamp);
  		p->current->state_since = timestamp;
  		p->current->state = TYPE_WAITING;
  	}
  }
  
  static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
  {
  	struct per_pid *p = NULL, *prev_p;
  	struct sched_switch *sw = (void *)te;
  
  
  	prev_p = find_create_pid(sw->prev_pid);
  
  	p = find_create_pid(sw->next_pid);
  
  	if (prev_p->current && prev_p->current->state != TYPE_NONE)
  		pid_put_sample(sw->prev_pid, TYPE_RUNNING, cpu, prev_p->current->state_since, timestamp);
  	if (p && p->current) {
  		if (p->current->state != TYPE_NONE)
  			pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp);
33e26a1b4   Julia Lawall   perf timechart: A...
469
470
  		p->current->state_since = timestamp;
  		p->current->state = TYPE_RUNNING;
10274989f   Arjan van de Ven   perf: Add the tim...
471
472
473
474
475
476
477
478
479
480
481
  	}
  
  	if (prev_p->current) {
  		prev_p->current->state = TYPE_NONE;
  		prev_p->current->state_since = timestamp;
  		if (sw->prev_state & 2)
  			prev_p->current->state = TYPE_BLOCKED;
  		if (sw->prev_state == 0)
  			prev_p->current->state = TYPE_WAITING;
  	}
  }
45694aa77   Arnaldo Carvalho de Melo   perf tools: Renam...
482
  static int process_sample_event(struct perf_tool *tool __used,
d20deb64e   Arnaldo Carvalho de Melo   perf tools: Pass ...
483
  				union perf_event *event __used,
8d50e5b41   Arnaldo Carvalho de Melo   perf tools: Renam...
484
  				struct perf_sample *sample,
e3f426096   Arnaldo Carvalho de Melo   perf tools: Use e...
485
  				struct perf_evsel *evsel,
743eb8686   Arnaldo Carvalho de Melo   perf tools: Resol...
486
  				struct machine *machine __used)
10274989f   Arjan van de Ven   perf: Add the tim...
487
  {
10274989f   Arjan van de Ven   perf: Add the tim...
488
  	struct trace_entry *te;
e3f426096   Arnaldo Carvalho de Melo   perf tools: Use e...
489
  	if (evsel->attr.sample_type & PERF_SAMPLE_TIME) {
640c03ce8   Arnaldo Carvalho de Melo   perf session: Par...
490
491
492
493
  		if (!first_time || first_time > sample->time)
  			first_time = sample->time;
  		if (last_time < sample->time)
  			last_time = sample->time;
10274989f   Arjan van de Ven   perf: Add the tim...
494
  	}
180f95e29   OGAWA Hirofumi   perf: Make common...
495

640c03ce8   Arnaldo Carvalho de Melo   perf session: Par...
496
  	te = (void *)sample->raw_data;
e3f426096   Arnaldo Carvalho de Melo   perf tools: Use e...
497
  	if ((evsel->attr.sample_type & PERF_SAMPLE_RAW) && sample->raw_size > 0) {
10274989f   Arjan van de Ven   perf: Add the tim...
498
  		char *event_str;
20c457b85   Thomas Renninger   perf timechart: A...
499
500
501
502
  #ifdef SUPPORT_OLD_POWER_EVENTS
  		struct power_entry_old *peo;
  		peo = (void *)te;
  #endif
9e69c2108   Arnaldo Carvalho de Melo   perf session: Pas...
503
504
505
506
507
508
509
510
511
512
  		/*
  		 * FIXME: use evsel, its already mapped from id to perf_evsel,
  		 * remove perf_header__find_event infrastructure bits.
  		 * Mapping all these "power:cpu_idle" strings to the tracepoint
  		 * ID and then just comparing against evsel->attr.config.
  		 *
  		 * e.g.:
  		 *
  		 * if (evsel->attr.config == power_cpu_idle_id)
  		 */
10274989f   Arjan van de Ven   perf: Add the tim...
513
514
515
516
  		event_str = perf_header__find_event(te->type);
  
  		if (!event_str)
  			return 0;
54b08f5f9   Thomas Renninger   perf timechart: F...
517
518
  		if (sample->cpu > numcpus)
  			numcpus = sample->cpu;
20c457b85   Thomas Renninger   perf timechart: A...
519
520
521
522
523
524
525
526
527
528
529
530
  		if (strcmp(event_str, "power:cpu_idle") == 0) {
  			struct power_processor_entry *ppe = (void *)te;
  			if (ppe->state == (u32)PWR_EVENT_EXIT)
  				c_state_end(ppe->cpu_id, sample->time);
  			else
  				c_state_start(ppe->cpu_id, sample->time,
  					      ppe->state);
  		}
  		else if (strcmp(event_str, "power:cpu_frequency") == 0) {
  			struct power_processor_entry *ppe = (void *)te;
  			p_state_change(ppe->cpu_id, sample->time, ppe->state);
  		}
10274989f   Arjan van de Ven   perf: Add the tim...
531

20c457b85   Thomas Renninger   perf timechart: A...
532
  		else if (strcmp(event_str, "sched:sched_wakeup") == 0)
640c03ce8   Arnaldo Carvalho de Melo   perf session: Par...
533
  			sched_wakeup(sample->cpu, sample->time, sample->pid, te);
10274989f   Arjan van de Ven   perf: Add the tim...
534

20c457b85   Thomas Renninger   perf timechart: A...
535
  		else if (strcmp(event_str, "sched:sched_switch") == 0)
640c03ce8   Arnaldo Carvalho de Melo   perf session: Par...
536
  			sched_switch(sample->cpu, sample->time, te);
20c457b85   Thomas Renninger   perf timechart: A...
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
  
  #ifdef SUPPORT_OLD_POWER_EVENTS
  		if (use_old_power_events) {
  			if (strcmp(event_str, "power:power_start") == 0)
  				c_state_start(peo->cpu_id, sample->time,
  					      peo->value);
  
  			else if (strcmp(event_str, "power:power_end") == 0)
  				c_state_end(sample->cpu, sample->time);
  
  			else if (strcmp(event_str,
  					"power:power_frequency") == 0)
  				p_state_change(peo->cpu_id, sample->time,
  					       peo->value);
  		}
  #endif
10274989f   Arjan van de Ven   perf: Add the tim...
553
554
555
556
557
558
559
560
561
562
563
564
  	}
  	return 0;
  }
  
  /*
   * After the last sample we need to wrap up the current C/P state
   * and close out each CPU for these.
   */
  static void end_sample_processing(void)
  {
  	u64 cpu;
  	struct power_event *pwr;
39a90a8ef   Arjan van de Ven   perf timechart: A...
565
  	for (cpu = 0; cpu <= numcpus; cpu++) {
10274989f   Arjan van de Ven   perf: Add the tim...
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
  		pwr = malloc(sizeof(struct power_event));
  		if (!pwr)
  			return;
  		memset(pwr, 0, sizeof(struct power_event));
  
  		/* C state */
  #if 0
  		pwr->state = cpus_cstate_state[cpu];
  		pwr->start_time = cpus_cstate_start_times[cpu];
  		pwr->end_time = last_time;
  		pwr->cpu = cpu;
  		pwr->type = CSTATE;
  		pwr->next = power_events;
  
  		power_events = pwr;
  #endif
  		/* P state */
  
  		pwr = malloc(sizeof(struct power_event));
  		if (!pwr)
  			return;
  		memset(pwr, 0, sizeof(struct power_event));
  
  		pwr->state = cpus_pstate_state[cpu];
  		pwr->start_time = cpus_pstate_start_times[cpu];
  		pwr->end_time = last_time;
  		pwr->cpu = cpu;
  		pwr->type = PSTATE;
  		pwr->next = power_events;
  
  		if (!pwr->start_time)
  			pwr->start_time = first_time;
  		if (!pwr->state)
  			pwr->state = min_freq;
  		power_events = pwr;
  	}
  }
10274989f   Arjan van de Ven   perf: Add the tim...
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
  /*
   * Sort the pid datastructure
   */
  static void sort_pids(void)
  {
  	struct per_pid *new_list, *p, *cursor, *prev;
  	/* sort by ppid first, then by pid, lowest to highest */
  
  	new_list = NULL;
  
  	while (all_data) {
  		p = all_data;
  		all_data = p->next;
  		p->next = NULL;
  
  		if (new_list == NULL) {
  			new_list = p;
  			p->next = NULL;
  			continue;
  		}
  		prev = NULL;
  		cursor = new_list;
  		while (cursor) {
  			if (cursor->ppid > p->ppid ||
  				(cursor->ppid == p->ppid && cursor->pid > p->pid)) {
  				/* must insert before */
  				if (prev) {
  					p->next = prev->next;
  					prev->next = p;
  					cursor = NULL;
  					continue;
  				} else {
  					p->next = new_list;
  					new_list = p;
  					cursor = NULL;
  					continue;
  				}
  			}
  
  			prev = cursor;
  			cursor = cursor->next;
  			if (!cursor)
  				prev->next = p;
  		}
  	}
  	all_data = new_list;
  }
  
  
  static void draw_c_p_states(void)
  {
  	struct power_event *pwr;
  	pwr = power_events;
  
  	/*
  	 * two pass drawing so that the P state bars are on top of the C state blocks
  	 */
  	while (pwr) {
  		if (pwr->type == CSTATE)
  			svg_cstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
  		pwr = pwr->next;
  	}
  
  	pwr = power_events;
  	while (pwr) {
  		if (pwr->type == PSTATE) {
  			if (!pwr->state)
  				pwr->state = min_freq;
  			svg_pstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);
  		}
  		pwr = pwr->next;
  	}
  }
  
  static void draw_wakeups(void)
  {
  	struct wake_event *we;
  	struct per_pid *p;
  	struct per_pidcomm *c;
  
  	we = wake_events;
  	while (we) {
  		int from = 0, to = 0;
4f1202c8e   Arjan van de Ven   perf timechart: S...
686
  		char *task_from = NULL, *task_to = NULL;
10274989f   Arjan van de Ven   perf: Add the tim...
687
688
689
690
691
692
693
694
  
  		/* locate the column of the waker and wakee */
  		p = all_data;
  		while (p) {
  			if (p->pid == we->waker || p->pid == we->wakee) {
  				c = p->all;
  				while (c) {
  					if (c->Y && c->start_time <= we->time && c->end_time >= we->time) {
bbe2987be   Arjan van de Ven   perf timechart: A...
695
  						if (p->pid == we->waker && !from) {
10274989f   Arjan van de Ven   perf: Add the tim...
696
  							from = c->Y;
3bc2a39c6   Arjan van de Ven   perf timechart: F...
697
  							task_from = strdup(c->comm);
4f1202c8e   Arjan van de Ven   perf timechart: S...
698
  						}
bbe2987be   Arjan van de Ven   perf timechart: A...
699
  						if (p->pid == we->wakee && !to) {
10274989f   Arjan van de Ven   perf: Add the tim...
700
  							to = c->Y;
3bc2a39c6   Arjan van de Ven   perf timechart: F...
701
  							task_to = strdup(c->comm);
4f1202c8e   Arjan van de Ven   perf timechart: S...
702
  						}
10274989f   Arjan van de Ven   perf: Add the tim...
703
704
705
  					}
  					c = c->next;
  				}
3bc2a39c6   Arjan van de Ven   perf timechart: F...
706
707
708
709
710
711
712
713
714
715
716
717
  				c = p->all;
  				while (c) {
  					if (p->pid == we->waker && !from) {
  						from = c->Y;
  						task_from = strdup(c->comm);
  					}
  					if (p->pid == we->wakee && !to) {
  						to = c->Y;
  						task_to = strdup(c->comm);
  					}
  					c = c->next;
  				}
10274989f   Arjan van de Ven   perf: Add the tim...
718
719
720
  			}
  			p = p->next;
  		}
3bc2a39c6   Arjan van de Ven   perf timechart: F...
721
722
723
724
725
726
727
728
  		if (!task_from) {
  			task_from = malloc(40);
  			sprintf(task_from, "[%i]", we->waker);
  		}
  		if (!task_to) {
  			task_to = malloc(40);
  			sprintf(task_to, "[%i]", we->wakee);
  		}
10274989f   Arjan van de Ven   perf: Add the tim...
729
730
731
732
733
  		if (we->waker == -1)
  			svg_interrupt(we->time, to);
  		else if (from && to && abs(from - to) == 1)
  			svg_wakeline(we->time, from, to);
  		else
4f1202c8e   Arjan van de Ven   perf timechart: S...
734
  			svg_partial_wakeline(we->time, from, task_from, to, task_to);
10274989f   Arjan van de Ven   perf: Add the tim...
735
  		we = we->next;
3bc2a39c6   Arjan van de Ven   perf timechart: F...
736
737
738
  
  		free(task_from);
  		free(task_to);
10274989f   Arjan van de Ven   perf: Add the tim...
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
  	}
  }
  
  static void draw_cpu_usage(void)
  {
  	struct per_pid *p;
  	struct per_pidcomm *c;
  	struct cpu_sample *sample;
  	p = all_data;
  	while (p) {
  		c = p->all;
  		while (c) {
  			sample = c->samples;
  			while (sample) {
  				if (sample->type == TYPE_RUNNING)
  					svg_process(sample->cpu, sample->start_time, sample->end_time, "sample", c->comm);
  
  				sample = sample->next;
  			}
  			c = c->next;
  		}
  		p = p->next;
  	}
  }
  
  static void draw_process_bars(void)
  {
  	struct per_pid *p;
  	struct per_pidcomm *c;
  	struct cpu_sample *sample;
  	int Y = 0;
  
  	Y = 2 * numcpus + 2;
  
  	p = all_data;
  	while (p) {
  		c = p->all;
  		while (c) {
  			if (!c->display) {
  				c->Y = 0;
  				c = c->next;
  				continue;
  			}
a92fe7b30   Arjan van de Ven   perf timechart: S...
782
  			svg_box(Y, c->start_time, c->end_time, "process");
10274989f   Arjan van de Ven   perf: Add the tim...
783
784
785
  			sample = c->samples;
  			while (sample) {
  				if (sample->type == TYPE_RUNNING)
a92fe7b30   Arjan van de Ven   perf timechart: S...
786
  					svg_sample(Y, sample->cpu, sample->start_time, sample->end_time);
10274989f   Arjan van de Ven   perf: Add the tim...
787
788
789
  				if (sample->type == TYPE_BLOCKED)
  					svg_box(Y, sample->start_time, sample->end_time, "blocked");
  				if (sample->type == TYPE_WAITING)
a92fe7b30   Arjan van de Ven   perf timechart: S...
790
  					svg_waiting(Y, sample->start_time, sample->end_time);
10274989f   Arjan van de Ven   perf: Add the tim...
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
  				sample = sample->next;
  			}
  
  			if (c->comm) {
  				char comm[256];
  				if (c->total_time > 5000000000) /* 5 seconds */
  					sprintf(comm, "%s:%i (%2.2fs)", c->comm, p->pid, c->total_time / 1000000000.0);
  				else
  					sprintf(comm, "%s:%i (%3.1fms)", c->comm, p->pid, c->total_time / 1000000.0);
  
  				svg_text(Y, c->start_time, comm);
  			}
  			c->Y = Y;
  			Y++;
  			c = c->next;
  		}
  		p = p->next;
  	}
  }
bbe2987be   Arjan van de Ven   perf timechart: A...
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
  static void add_process_filter(const char *string)
  {
  	struct process_filter *filt;
  	int pid;
  
  	pid = strtoull(string, NULL, 10);
  	filt = malloc(sizeof(struct process_filter));
  	if (!filt)
  		return;
  
  	filt->name = strdup(string);
  	filt->pid  = pid;
  	filt->next = process_filter;
  
  	process_filter = filt;
  }
  
  static int passes_filter(struct per_pid *p, struct per_pidcomm *c)
  {
  	struct process_filter *filt;
  	if (!process_filter)
  		return 1;
  
  	filt = process_filter;
  	while (filt) {
  		if (filt->pid && p->pid == filt->pid)
  			return 1;
  		if (strcmp(filt->name, c->comm) == 0)
  			return 1;
  		filt = filt->next;
  	}
  	return 0;
  }
  
  static int determine_display_tasks_filtered(void)
  {
  	struct per_pid *p;
  	struct per_pidcomm *c;
  	int count = 0;
  
  	p = all_data;
  	while (p) {
  		p->display = 0;
  		if (p->start_time == 1)
  			p->start_time = first_time;
  
  		/* no exit marker, task kept running to the end */
  		if (p->end_time == 0)
  			p->end_time = last_time;
  
  		c = p->all;
  
  		while (c) {
  			c->display = 0;
  
  			if (c->start_time == 1)
  				c->start_time = first_time;
  
  			if (passes_filter(p, c)) {
  				c->display = 1;
  				p->display = 1;
  				count++;
  			}
  
  			if (c->end_time == 0)
  				c->end_time = last_time;
  
  			c = c->next;
  		}
  		p = p->next;
  	}
  	return count;
  }
10274989f   Arjan van de Ven   perf: Add the tim...
883
884
885
886
887
  static int determine_display_tasks(u64 threshold)
  {
  	struct per_pid *p;
  	struct per_pidcomm *c;
  	int count = 0;
bbe2987be   Arjan van de Ven   perf timechart: A...
888
889
  	if (process_filter)
  		return determine_display_tasks_filtered();
10274989f   Arjan van de Ven   perf: Add the tim...
890
891
892
893
894
895
896
897
898
  	p = all_data;
  	while (p) {
  		p->display = 0;
  		if (p->start_time == 1)
  			p->start_time = first_time;
  
  		/* no exit marker, task kept running to the end */
  		if (p->end_time == 0)
  			p->end_time = last_time;
39a90a8ef   Arjan van de Ven   perf timechart: A...
899
  		if (p->total_time >= threshold && !power_only)
10274989f   Arjan van de Ven   perf: Add the tim...
900
901
902
903
904
905
906
907
908
  			p->display = 1;
  
  		c = p->all;
  
  		while (c) {
  			c->display = 0;
  
  			if (c->start_time == 1)
  				c->start_time = first_time;
39a90a8ef   Arjan van de Ven   perf timechart: A...
909
  			if (c->total_time >= threshold && !power_only) {
10274989f   Arjan van de Ven   perf: Add the tim...
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
  				c->display = 1;
  				count++;
  			}
  
  			if (c->end_time == 0)
  				c->end_time = last_time;
  
  			c = c->next;
  		}
  		p = p->next;
  	}
  	return count;
  }
  
  
  
  #define TIME_THRESH 10000000
  
  static void write_svg_file(const char *filename)
  {
  	u64 i;
  	int count;
  
  	numcpus++;
  
  
  	count = determine_display_tasks(TIME_THRESH);
  
  	/* We'd like to show at least 15 tasks; be less picky if we have fewer */
  	if (count < 15)
  		count = determine_display_tasks(TIME_THRESH / 10);
5094b6554   Arjan van de Ven   perf util: Make t...
941
  	open_svg(filename, numcpus, count, first_time, last_time);
10274989f   Arjan van de Ven   perf: Add the tim...
942

5094b6554   Arjan van de Ven   perf util: Make t...
943
  	svg_time_grid();
10274989f   Arjan van de Ven   perf: Add the tim...
944
945
946
947
948
949
950
951
952
953
954
955
  	svg_legenda();
  
  	for (i = 0; i < numcpus; i++)
  		svg_cpu_box(i, max_freq, turbo_frequency);
  
  	draw_cpu_usage();
  	draw_process_bars();
  	draw_c_p_states();
  	draw_wakeups();
  
  	svg_close();
  }
45694aa77   Arnaldo Carvalho de Melo   perf tools: Renam...
956
  static struct perf_tool perf_timechart = {
9df9bbba9   Frederic Weisbecker   perf: Use generic...
957
958
959
960
961
  	.comm			= process_comm_event,
  	.fork			= process_fork_event,
  	.exit			= process_exit_event,
  	.sample			= process_sample_event,
  	.ordered_samples	= true,
5cbd08056   Li Zefan   perf timechart: R...
962
  };
10274989f   Arjan van de Ven   perf: Add the tim...
963

5cbd08056   Li Zefan   perf timechart: R...
964
965
  static int __cmd_timechart(void)
  {
21ef97f05   Ian Munsie   perf session: Fal...
966
  	struct perf_session *session = perf_session__new(input_name, O_RDONLY,
45694aa77   Arnaldo Carvalho de Melo   perf tools: Renam...
967
  							 0, false, &perf_timechart);
d549c7690   Arnaldo Carvalho de Melo   perf session: Rem...
968
  	int ret = -EINVAL;
10274989f   Arjan van de Ven   perf: Add the tim...
969

94c744b6c   Arnaldo Carvalho de Melo   perf tools: Intro...
970
971
  	if (session == NULL)
  		return -ENOMEM;
d549c7690   Arnaldo Carvalho de Melo   perf session: Rem...
972
973
  	if (!perf_session__has_traces(session, "timechart record"))
  		goto out_delete;
45694aa77   Arnaldo Carvalho de Melo   perf tools: Renam...
974
  	ret = perf_session__process_events(session, &perf_timechart);
5cbd08056   Li Zefan   perf timechart: R...
975
  	if (ret)
94c744b6c   Arnaldo Carvalho de Melo   perf tools: Intro...
976
  		goto out_delete;
10274989f   Arjan van de Ven   perf: Add the tim...
977

10274989f   Arjan van de Ven   perf: Add the tim...
978
979
980
981
982
  	end_sample_processing();
  
  	sort_pids();
  
  	write_svg_file(output_name);
6beba7adb   Arnaldo Carvalho de Melo   perf tools: Unify...
983
984
985
  	pr_info("Written %2.1f seconds of trace to %s.
  ",
  		(last_time - first_time) / 1000000000.0, output_name);
94c744b6c   Arnaldo Carvalho de Melo   perf tools: Intro...
986
987
988
  out_delete:
  	perf_session__delete(session);
  	return ret;
10274989f   Arjan van de Ven   perf: Add the tim...
989
  }
3c09eebd6   Arjan van de Ven   perf timechart: A...
990
991
  static const char * const timechart_usage[] = {
  	"perf timechart [<options>] {record}",
10274989f   Arjan van de Ven   perf: Add the tim...
992
993
  	NULL
  };
20c457b85   Thomas Renninger   perf timechart: A...
994
995
  #ifdef SUPPORT_OLD_POWER_EVENTS
  static const char * const record_old_args[] = {
3c09eebd6   Arjan van de Ven   perf timechart: A...
996
997
998
  	"record",
  	"-a",
  	"-R",
3c09eebd6   Arjan van de Ven   perf timechart: A...
999
1000
1001
1002
1003
1004
1005
1006
  	"-f",
  	"-c", "1",
  	"-e", "power:power_start",
  	"-e", "power:power_end",
  	"-e", "power:power_frequency",
  	"-e", "sched:sched_wakeup",
  	"-e", "sched:sched_switch",
  };
20c457b85   Thomas Renninger   perf timechart: A...
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
  #endif
  
  static const char * const record_new_args[] = {
  	"record",
  	"-a",
  	"-R",
  	"-f",
  	"-c", "1",
  	"-e", "power:cpu_frequency",
  	"-e", "power:cpu_idle",
  	"-e", "sched:sched_wakeup",
  	"-e", "sched:sched_switch",
  };
3c09eebd6   Arjan van de Ven   perf timechart: A...
1020
1021
1022
1023
1024
  
  static int __cmd_record(int argc, const char **argv)
  {
  	unsigned int rec_argc, i, j;
  	const char **rec_argv;
20c457b85   Thomas Renninger   perf timechart: A...
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
  	const char * const *record_args = record_new_args;
  	unsigned int record_elems = ARRAY_SIZE(record_new_args);
  
  #ifdef SUPPORT_OLD_POWER_EVENTS
  	if (!is_valid_tracepoint("power:cpu_idle") &&
  	    is_valid_tracepoint("power:power_start")) {
  		use_old_power_events = 1;
  		record_args = record_old_args;
  		record_elems = ARRAY_SIZE(record_old_args);
  	}
  #endif
3c09eebd6   Arjan van de Ven   perf timechart: A...
1036

20c457b85   Thomas Renninger   perf timechart: A...
1037
  	rec_argc = record_elems + argc - 1;
3c09eebd6   Arjan van de Ven   perf timechart: A...
1038
  	rec_argv = calloc(rec_argc + 1, sizeof(char *));
ce47dc56a   Chris Samuel   perf tools: Catch...
1039
1040
  	if (rec_argv == NULL)
  		return -ENOMEM;
20c457b85   Thomas Renninger   perf timechart: A...
1041
  	for (i = 0; i < record_elems; i++)
3c09eebd6   Arjan van de Ven   perf timechart: A...
1042
1043
1044
1045
1046
1047
1048
  		rec_argv[i] = strdup(record_args[i]);
  
  	for (j = 1; j < (unsigned int)argc; j++, i++)
  		rec_argv[i] = argv[j];
  
  	return cmd_record(i, rec_argv, NULL);
  }
bbe2987be   Arjan van de Ven   perf timechart: A...
1049
1050
1051
1052
1053
1054
1055
  static int
  parse_process(const struct option *opt __used, const char *arg, int __used unset)
  {
  	if (arg)
  		add_process_filter(arg);
  	return 0;
  }
10274989f   Arjan van de Ven   perf: Add the tim...
1056
1057
1058
1059
1060
  static const struct option options[] = {
  	OPT_STRING('i', "input", &input_name, "file",
  		    "input file name"),
  	OPT_STRING('o', "output", &output_name, "file",
  		    "output file name"),
5094b6554   Arjan van de Ven   perf util: Make t...
1061
1062
  	OPT_INTEGER('w', "width", &svg_page_width,
  		    "page width"),
bbe2987be   Arjan van de Ven   perf timechart: A...
1063
  	OPT_BOOLEAN('P', "power-only", &power_only,
39a90a8ef   Arjan van de Ven   perf timechart: A...
1064
  		    "output power data only"),
bbe2987be   Arjan van de Ven   perf timechart: A...
1065
1066
1067
  	OPT_CALLBACK('p', "process", NULL, "process",
  		      "process selector. Pass a pid or process name.",
  		       parse_process),
ec5761eab   David Ahern   perf symbols: Add...
1068
1069
  	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
  		    "Look for files with symbols relative to this directory"),
10274989f   Arjan van de Ven   perf: Add the tim...
1070
1071
1072
1073
1074
1075
  	OPT_END()
  };
  
  
  int cmd_timechart(int argc, const char **argv, const char *prefix __used)
  {
3c09eebd6   Arjan van de Ven   perf timechart: A...
1076
1077
  	argc = parse_options(argc, argv, options, timechart_usage,
  			PARSE_OPT_STOP_AT_NON_OPTION);
10274989f   Arjan van de Ven   perf: Add the tim...
1078

655000e7c   Arnaldo Carvalho de Melo   perf symbols: Ado...
1079
  	symbol__init();
3c09eebd6   Arjan van de Ven   perf timechart: A...
1080
1081
1082
1083
  	if (argc && !strncmp(argv[0], "rec", 3))
  		return __cmd_record(argc, argv);
  	else if (argc)
  		usage_with_options(timechart_usage, options);
10274989f   Arjan van de Ven   perf: Add the tim...
1084
1085
1086
1087
1088
  
  	setup_pager();
  
  	return __cmd_timechart();
  }