Release 4.10 tools/perf/builtin-timechart.c
/*
* 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 <traceevent/event-parse.h>
#include "builtin.h"
#include "util/util.h"
#include "util/color.h"
#include <linux/list.h>
#include "util/cache.h"
#include "util/evlist.h"
#include "util/evsel.h"
#include <linux/rbtree.h>
#include <linux/time64.h>
#include "util/symbol.h"
#include "util/callchain.h"
#include "util/strlist.h"
#include "perf.h"
#include "util/header.h"
#include <subcmd/parse-options.h>
#include "util/parse-events.h"
#include "util/event.h"
#include "util/session.h"
#include "util/svghelper.h"
#include "util/tool.h"
#include "util/data.h"
#include "util/debug.h"
#define SUPPORT_OLD_POWER_EVENTS 1
#define PWR_EVENT_EXIT -1
struct per_pid;
struct power_event;
struct wake_event;
struct timechart {
struct perf_tool tool;
struct per_pid *all_data;
struct power_event *power_events;
struct wake_event *wake_events;
int proc_num;
unsigned int numcpus;
u64 min_freq, /* Lowest CPU frequency seen */
max_freq, /* Highest CPU frequency seen */
turbo_frequency,
first_time, last_time;
bool power_only,
tasks_only,
with_backtrace,
topology;
bool force;
/* IO related settings */
bool io_only,
skip_eagain;
u64 io_events;
u64 min_time,
merge_dist;
};
struct per_pidcomm;
struct cpu_sample;
struct io_sample;
/*
* 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;
u64 total_bytes;
int display;
struct per_pidcomm *all;
struct per_pidcomm *current;
};
struct per_pidcomm {
struct per_pidcomm *next;
u64 start_time;
u64 end_time;
u64 total_time;
u64 max_bytes;
u64 total_bytes;
int Y;
int display;
long state;
u64 state_since;
char *comm;
struct cpu_sample *samples;
struct io_sample *io_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;
const char *backtrace;
};
enum {
IOTYPE_READ,
IOTYPE_WRITE,
IOTYPE_SYNC,
IOTYPE_TX,
IOTYPE_RX,
IOTYPE_POLL,
};
struct io_sample {
struct io_sample *next;
u64 start_time;
u64 end_time;
u64 bytes;
int type;
int fd;
int err;
int merges;
};
#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;
const char *backtrace;
};
struct process_filter {
char *name;
int pid;
struct process_filter *next;
};
static struct process_filter *process_filter;
static struct per_pid *find_create_pid(struct timechart *tchart, int pid)
{
struct per_pid *cursor = tchart->all_data;
while (cursor) {
if (cursor->pid == pid)
return cursor;
cursor = cursor->next;
}
cursor = zalloc(sizeof(*cursor));
assert(cursor != NULL);
cursor->pid = pid;
cursor->next = tchart->all_data;
tchart->all_data = cursor;
return cursor;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
arjan van de ven | arjan van de ven | 75 | 84.27% | 1 | 33.33% |
arnaldo carvalho de melo | arnaldo carvalho de melo | 14 | 15.73% | 2 | 66.67% |
| Total | 89 | 100.00% | 3 | 100.00% |
static void pid_set_comm(struct timechart *tchart, int pid, char *comm)
{
struct per_pid *p;
struct per_pidcomm *c;
p = find_create_pid(tchart, 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 = zalloc(sizeof(*c));
assert(c != NULL);
c->comm = strdup(comm);
p->current = c;
c->next = p->all;
p->all = c;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
arjan van de ven | arjan van de ven | 143 | 93.46% | 1 | 33.33% |
arnaldo carvalho de melo | arnaldo carvalho de melo | 10 | 6.54% | 2 | 66.67% |
| Total | 153 | 100.00% | 3 | 100.00% |
static void pid_fork(struct timechart *tchart, int pid, int ppid, u64 timestamp)
{
struct per_pid *p, *pp;
p = find_create_pid(tchart, pid);
pp = find_create_pid(tchart, ppid);
p->ppid = ppid;
if (pp->current && pp->current->comm && !p->current)
pid_set_comm(tchart, pid, pp->current->comm);
p->start_time = timestamp;
if (p->current && !p->current->start_time) {
p->current->start_time = timestamp;
p->current->state_since = timestamp;
}
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
arjan van de ven | arjan van de ven | 101 | 84.87% | 1 | 33.33% |
arnaldo carvalho de melo | arnaldo carvalho de melo | 11 | 9.24% | 1 | 33.33% |
stanislav fomichev | stanislav fomichev | 7 | 5.88% | 1 | 33.33% |
| Total | 119 | 100.00% | 3 | 100.00% |
static void pid_exit(struct timechart *tchart, int pid, u64 timestamp)
{
struct per_pid *p;
p = find_create_pid(tchart, pid);
p->end_time = timestamp;
if (p->current)
p->current->end_time = timestamp;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
arjan van de ven | arjan van de ven | 44 | 86.27% | 1 | 50.00% |
arnaldo carvalho de melo | arnaldo carvalho de melo | 7 | 13.73% | 1 | 50.00% |
| Total | 51 | 100.00% | 2 | 100.00% |
static void pid_put_sample(struct timechart *tchart, int pid, int type,
unsigned int cpu, u64 start, u64 end,
const char *backtrace)
{
struct per_pid *p;
struct per_pidcomm *c;
struct cpu_sample *sample;
p = find_create_pid(tchart, pid);
c = p->current;
if (!c) {
c = zalloc(sizeof(*c));
assert(c != NULL);
p->current = c;
c->next = p->all;
p->all = c;
}
sample = zalloc(sizeof(*sample));
assert(sample != NULL);
sample->start_time = start;
sample->end_time = end;
sample->type = type;
sample->next = c->samples;
sample->cpu = cpu;
sample->backtrace = backtrace;
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;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
arjan van de ven | arjan van de ven | 223 | 90.28% | 1 | 25.00% |
arnaldo carvalho de melo | arnaldo carvalho de melo | 13 | 5.26% | 2 | 50.00% |
stanislav fomichev | stanislav fomichev | 11 | 4.45% | 1 | 25.00% |
| Total | 247 | 100.00% | 4 | 100.00% |
#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];
static int process_comm_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
struct timechart *tchart = container_of(tool, struct timechart, tool);
pid_set_comm(tchart, event->comm.tid, event->comm.comm);
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
arnaldo carvalho de melo | arnaldo carvalho de melo | 34 | 53.97% | 8 | 72.73% |
arjan van de ven | arjan van de ven | 27 | 42.86% | 2 | 18.18% |
irina tirdea | irina tirdea | 2 | 3.17% | 1 | 9.09% |
| Total | 63 | 100.00% | 11 | 100.00% |
static int process_fork_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
struct timechart *tchart = container_of(tool, struct timechart, tool);
pid_fork(tchart, event->fork.pid, event->fork.ppid, event->fork.time);
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
arnaldo carvalho de melo | arnaldo carvalho de melo | 34 | 49.28% | 8 | 80.00% |
arjan van de ven | arjan van de ven | 33 | 47.83% | 1 | 10.00% |
irina tirdea | irina tirdea | 2 | 2.90% | 1 | 10.00% |
| Total | 69 | 100.00% | 10 | 100.00% |
static int process_exit_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
struct timechart *tchart = container_of(tool, struct timechart, tool);
pid_exit(tchart, event->fork.pid, event->fork.time);
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
arnaldo carvalho de melo | arnaldo carvalho de melo | 34 | 53.97% | 8 | 80.00% |
arjan van de ven | arjan van de ven | 27 | 42.86% | 1 | 10.00% |
irina tirdea | irina tirdea | 2 | 3.17% | 1 | 10.00% |
| Total | 63 | 100.00% | 10 | 100.00% |
#ifdef SUPPORT_OLD_POWER_EVENTS
static int use_old_power_events;
#endif
static void c_state_start(int cpu, u64 timestamp, int state)
{
cpus_cstate_start_times[cpu] = timestamp;
cpus_cstate_state[cpu] = state;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
arjan van de ven | arjan van de ven | 29 | 100.00% | 1 | 100.00% |
| Total | 29 | 100.00% | 1 | 100.00% |
static void c_state_end(struct timechart *tchart, int cpu, u64 timestamp)
{
struct power_event *pwr = zalloc(sizeof(*pwr));
if (!pwr)
return;
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 = tchart->power_events;
tchart->power_events = pwr;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
arjan van de ven | arjan van de ven | 73 | 83.91% | 1 | 33.33% |
arnaldo carvalho de melo | arnaldo carvalho de melo | 14 | 16.09% | 2 | 66.67% |
| Total | 87 | 100.00% | 3 | 100.00% |
static void p_state_change(struct timechart *tchart, int cpu, u64 timestamp, u64 new_freq)
{
struct power_event *pwr;
if (new_freq > 8000000) /* detect invalid data */
return;
pwr = zalloc(sizeof(*pwr));
if (!pwr)
return;
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 = tchart->power_events;
if (!pwr->start_time)
pwr->start_time = tchart->first_time;
tchart->power_events = pwr;
cpus_pstate_state[cpu] = new_freq;
cpus_pstate_start_times[cpu] = timestamp;
if ((u64)new_freq > tchart->max_freq)
tchart->max_freq = new_freq;
if (new_freq < tchart->min_freq || tchart->min_freq == 0)
tchart->min_freq = new_freq;
if (new_freq == tchart->max_freq - 1000)
tchart->turbo_frequency = tchart->max_freq;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
arjan van de ven | arjan van de ven | 147 | 79.89% | 1 | 25.00% |
arnaldo carvalho de melo | arnaldo carvalho de melo | 37 | 20.11% | 3 | 75.00% |
| Total | 184 | 100.00% | 4 | 100.00% |
static void sched_wakeup(struct timechart *tchart, int cpu, u64 timestamp,
int waker, int wakee, u8 flags, const char *backtrace)
{
struct per_pid *p;
struct wake_event *we = zalloc(sizeof(*we));
if (!we)
return;
we->time = timestamp;
we->waker = waker;
we->backtrace = backtrace;
if ((flags & TRACE_FLAG_HARDIRQ) || (flags & TRACE_FLAG_SOFTIRQ))
we->waker = -1;
we->wakee = wakee;
we->next = tchart->wake_events;
tchart->wake_events = we;
p = find_create_pid(tchart, 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(tchart, p->pid, p->current->state, cpu,
p->current->state_since, timestamp, NULL);
p->current->state_since = timestamp;
p->current->state = TYPE_WAITING;
}
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
arjan van de ven | arjan van de ven | 180 | 81.45% | 1 | 16.67% |
stanislav fomichev | stanislav fomichev | 21 | 9.50% | 2 | 33.33% |
arnaldo carvalho de melo | arnaldo carvalho de melo | 20 | 9.05% | 3 | 50.00% |
| Total | 221 | 100.00% | 6 | 100.00% |
static void sched_switch(struct timechart *tchart, int cpu, u64 timestamp,
int prev_pid, int next_pid, u64 prev_state,
const char *backtrace)
{
struct per_pid *p = NULL, *prev_p;
prev_p = find_create_pid(tchart, prev_pid);
p = find_create_pid(tchart, next_pid);
if (prev_p->current && prev_p->current->state != TYPE_NONE)
pid_put_sample(tchart, prev_pid, TYPE_RUNNING, cpu,
prev_p->current->state_since, timestamp,
backtrace);
if (p && p->current) {
if (p->current->state != TYPE_NONE)
pid_put_sample(tchart, next_pid, p->current->state, cpu,
p->current->state_since, timestamp,
backtrace);
p->current->state_since = timestamp;
p->current->state = TYPE_RUNNING;
}
if (prev_p->current) {
prev_p->current->state = TYPE_NONE;
prev_p->current->state_since = timestamp;
if (prev_state & 2)
prev_p->current->state = TYPE_BLOCKED;
if (prev_state == 0)
prev_p->current->state = TYPE_WAITING;
}
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
arjan van de ven | arjan van de ven | 177 | 85.51% | 1 | 25.00% |
stanislav fomichev | stanislav fomichev | 17 | 8.21% | 2 | 50.00% |
arnaldo carvalho de melo | arnaldo carvalho de melo | 13 | 6.28% | 1 | 25.00% |
| Total | 207 | 100.00% | 4 | 100.00% |
static const char *cat_backtrace(union perf_event *event,
struct perf_sample *sample,
struct machine *machine)
{
struct addr_location al;
unsigned int i;
char *p = NULL;
size_t p_len;
u8 cpumode = PERF_RECORD_MISC_USER;
struct addr_location tal;
struct ip_callchain *chain = sample->callchain;
FILE *f = open_memstream(&p, &p_len);
if (!f) {
perror("open_memstream error");
return NULL;
}
if (!chain)
goto exit;
if (machine__resolve(machine, &al, sample) < 0) {
fprintf(stderr, "problem processing %d event, skipping it.\n",
event->header.type);
goto exit;
}
for (i = 0; i < chain->nr; i++) {
u64 ip;
if (callchain_param.order == ORDER_CALLEE)
ip = chain->ips[i];
else
ip = chain->ips[chain->nr - i - 1];
if (ip >= PERF_CONTEXT_MAX) {
switch (ip) {
case PERF_CONTEXT_HV:
cpumode = PERF_RECORD_MISC_HYPERVISOR;
break;
case PERF_CONTEXT_KERNEL:
cpumode = PERF_RECORD_MISC_KERNEL;
break;
case PERF_CONTEXT_USER:
cpumode = PERF_RECORD_MISC_USER;
break;
default:
pr_debug("invalid callchain context: "
"%"PRId64"\n", (s64) ip);
/*
* It seems the callchain is corrupted.
* Discard all.
*/
zfree(&p);
goto exit_put;
}
continue;
}
tal.filtered = 0;
thread__find_addr_location(al.thread, cpumode,
MAP__FUNCTION, ip, &tal);
if (tal.sym)
fprintf(f, "..... %016" PRIx64 " %s\n", ip,
tal.sym->name);
else
fprintf(f, "..... %016" PRIx64 "\n", ip);
}
exit_put:
addr_location__put(&al);
exit:
fclose(f);
return p;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
stanislav fomichev | stanislav fomichev | 304 | 95.90% | 1 | 20.00% |
arnaldo carvalho de melo | arnaldo carvalho de melo | 12 | 3.79% | 3 | 60.00% |
namhyung kim | namhyung kim | 1 | 0.32% | 1 | 20.00% |
| Total | 317 | 100.00% | 5 | 100.00% |
typedef int (*tracepoint_handler)(struct timechart *tchart,
struct perf_evsel *evsel,
struct perf_sample *sample,
const char *backtrace);
static int process_sample_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
struct perf_evsel *evsel,
struct machine *machine)
{
struct timechart *tchart = container_of(tool, struct timechart, tool);
if (evsel->attr.sample_type & PERF_SAMPLE_TIME) {
if (!tchart->first_time || tchart->first_time > sample->time)
tchart->first_time = sample->time;
if (tchart->last_time < sample->time)
tchart->last_time = sample->time;
}
if (evsel->handler != NULL) {
tracepoint_handler f = evsel->handler;
return f(tchart, evsel, sample,
cat_backtrace(event, sample, machine));
}
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
arnaldo carvalho de melo | arnaldo carvalho de melo | 61 | 44.20% | 10 | 66.67% |
arjan van de ven | arjan van de ven | 38 | 27.54% | 1 | 6.67% |
jiri olsa | jiri olsa | 23 | 16.67% | 1 | 6.67% |
stanislav fomichev | stanislav fomichev | 9 | 6.52% | 1 | 6.67% |
hirofumi ogawa | hirofumi ogawa | 7 | 5.07% | 2 | 13.33% |
| Total | 138 | 100.00% | 15 | 100.00% |
static int
process_sample_cpu_idle(struct timechart *tchart __maybe_unused,
struct perf_evsel *evsel,
struct perf_sample *sample,
const char *backtrace __maybe_unused)
{
u32 state = perf_evsel__intval(evsel, sample, "state");
u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
if (state == (u32)PWR_EVENT_EXIT)
c_state_end(tchart, cpu_id, sample->time);
else
c_state_start(cpu_id, sample->time, state);
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
stanislav fomichev | stanislav fomichev | 27 | 31.03% | 2 | 20.00% |
thomas renninger | thomas renninger | 24 | 27.59% | 2 | 20.00% |
jiri olsa | jiri olsa | 18 | 20.69% | 1 | 10.00% |
arnaldo carvalho de melo | arnaldo carvalho de melo | 10 | 11.49% | 3 | 30.00% |
arjan van de ven | arjan van de ven | 7 | 8.05% | 1 | 10.00% |
hirofumi ogawa | hirofumi ogawa | 1 | 1.15% | 1 | 10.00% |
| Total | 87 | 100.00% | 10 | 100.00% |
static int
process_sample_cpu_frequency(struct timechart *tchart,
struct perf_evsel *evsel,
struct perf_sample *sample,
const char *backtrace __maybe_unused)
{
u32 state = perf_evsel__intval(evsel, sample, "state");
u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
p_state_change(tchart, cpu_id, sample->time, state);
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
stanislav fomichev | stanislav fomichev | 27 | 40.30% | 2 | 22.22% |
jiri olsa | jiri olsa | 18 | 26.87% | 1 | 11.11% |
arnaldo carvalho de melo | arnaldo carvalho de melo | 9 | 13.43% | 2 | 22.22% |
thomas renninger | thomas renninger | 7 | 10.45% | 2 | 22.22% |
arjan van de ven | arjan van de ven | 5 | 7.46% | 1 | 11.11% |
hirofumi ogawa | hirofumi ogawa | 1 | 1.49% | 1 | 11.11% |
| Total | 67 | 100.00% | 9 | 100.00% |
static int
process_sample_sched_wakeup(struct timechart *tchart,
struct perf_evsel *evsel,
struct perf_sample *sample,
const char *backtrace)
{
u8 flags = perf_evsel__intval(evsel, sample, "common_flags");
int waker = perf_evsel__intval(evsel, sample, "common_pid");
int wakee = perf_evsel__intval(evsel, sample, "pid");
sched_wakeup(tchart, sample->cpu, sample->time, waker, wakee, flags, backtrace);
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
stanislav fomichev | stanislav fomichev | 44 | 51.16% | 2 | 20.00% |
jiri olsa | jiri olsa | 21 | 24.42% | 1 | 10.00% |
arnaldo carvalho de melo | arnaldo carvalho de melo | 9 | 10.47% | 3 | 30.00% |
arjan van de ven | arjan van de ven | 6 | 6.98% | 1 | 10.00% |
thomas renninger | thomas renninger | 5 | 5.81% | 2 | 20.00% |
hirofumi ogawa | hirofumi ogawa | 1 | 1.16% | 1 | 10.00% |
| Total | 86 | 100.00% | 10 | 100.00% |
static int
process_sample_sched_switch(struct timechart *tchart,
struct perf_evsel *evsel,
struct perf_sample *sample,
const char *backtrace)
{
int prev_pid = perf_evsel__intval(evsel, sample, "prev_pid");
int next_pid = perf_evsel__intval(evsel, sample, "next_pid");
u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state");
sched_switch(tchart, sample->cpu, sample->time, prev_pid, next_pid,
prev_state, backtrace);
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
stanislav fomichev | stanislav fomichev | 45 | 52.33% | 2 | 22.22% |
jiri olsa | jiri olsa | 22 | 25.58% | 1 | 11.11% |
arnaldo carvalho de melo | arnaldo carvalho de melo | 11 | 12.79% | 3 | 33.33% |
arjan van de ven | arjan van de ven | 4 | 4.65% | 1 | 11.11% |
thomas renninger | thomas renninger | 3 | 3.49% | 1 | 11.11% |
hirofumi ogawa | hirofumi ogawa | 1 | 1.16% | 1 | 11.11% |
| Total | 86 | 100.00% | 9 | 100.00% |
#ifdef SUPPORT_OLD_POWER_EVENTS
static int
process_sample_power_start(struct timechart