// SPDX-License-Identifier: GPL-2.0-only/* * builtin-timechart.c - make an svg timechart of system activity * * (C) Copyright 2009 Intel Corporation * * Authors: * Arjan van de Ven <arjan@linux.intel.com> */ #include <errno.h>#include <inttypes.h> #include "builtin.h"#include "util/color.h"#include <linux/list.h>#include "util/evlist.h" // for struct evsel_str_handler#include "util/evsel.h"#include <linux/kernel.h>#include <linux/rbtree.h>#include <linux/time64.h>#include <linux/zalloc.h>#include "util/symbol.h"#include "util/thread.h"#include "util/callchain.h" #include "util/header.h"#include <subcmd/pager.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"#include "util/string2.h"#include "util/tracepoint.h"#include "util/util.h"#include <linux/err.h>#include <event-parse.h> #ifdef LACKS_OPEN_MEMSTREAM_PROTOTYPEFILE *open_memstream(char **ptr, size_t *sizeloc);#endif #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; struct perf_session *session; 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[];}; #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;} static struct per_pidcomm *create_pidcomm(struct per_pid *p){ struct per_pidcomm *c; c = zalloc(sizeof(*c)); if (!c) return NULL; p->current = c; c->next = p->all; p->all = c; return c;} 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 = create_pidcomm(p); assert(c != NULL); c->comm = strdup(comm);} 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; }} 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;} 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 = create_pidcomm(p); assert(c != NULL); } 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;} #define MAX_CPUS 4096 static u64 *cpus_cstate_start_times;static int *cpus_cstate_state;static u64 *cpus_pstate_start_times;static u64 *cpus_pstate_state; static int process_comm_event(const 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;} static int process_fork_event(const 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;} static int process_exit_event(const 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;} #ifdef SUPPORT_OLD_POWER_EVENTSstatic 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;} 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;} static struct power_event *p_state_end(struct timechart *tchart, int cpu, u64 timestamp){ struct power_event *pwr = zalloc(sizeof(*pwr)); if (!pwr) return NULL; 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; return pwr;} 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 = p_state_end(tchart, cpu, timestamp); if (!pwr) return; 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;} 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) { free((char *)backtrace); 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; }} 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; bool backtrace_used = false; 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); backtrace_used = true; } if (p && p->current) { if (p->current->state != TYPE_NONE) { if (backtrace && backtrace_used) backtrace = strdup(backtrace); pid_put_sample(tchart, next_pid, p->current->state, cpu, p->current->state_since, timestamp, backtrace); backtrace_used = true; } 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; }Lines 1–500 of 2164. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * builtin-timechart.c - make an svg timechart of system activity * * (C) Copyright 2009 Intel Corporation * * Authors: * Arjan van de Ven <arjan@linux.intel.com> */ #include <errno.h> #include <inttypes.h> #include "builtin.h" #include "util/color.h" #include <linux/list.h> #include "util/evlist.h" // for struct evsel_str_handler #include "util/evsel.h" #include <linux/kernel.h> #include <linux/rbtree.h> #include <linux/time64.h> #include <linux/zalloc.h> #include "util/symbol.h" #include "util/thread.h" #include "util/callchain.h" #include "util/header.h" #include <subcmd/pager.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" #include "util/string2.h" #include "util/tracepoint.h" #include "util/util.h" #include <linux/err.h> #include <event-parse.h> #ifdef LACKS_OPEN_MEMSTREAM_PROTOTYPE FILE *open_memstream(char **ptr, size_t *sizeloc); #endif #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; struct perf_session *session; 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[]; }; #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; } static struct per_pidcomm *create_pidcomm(struct per_pid *p) { struct per_pidcomm *c; c = zalloc(sizeof(*c)); if (!c) return NULL; p->current = c; c->next = p->all; p->all = c; return c; } 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 = create_pidcomm(p); assert(c != NULL); c->comm = strdup(comm); } 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; } } 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; } 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 = create_pidcomm(p); assert(c != NULL); } 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; } #define MAX_CPUS 4096 static u64 *cpus_cstate_start_times; static int *cpus_cstate_state; static u64 *cpus_pstate_start_times; static u64 *cpus_pstate_state; static int process_comm_event(const 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; } static int process_fork_event(const 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; } static int process_exit_event(const 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; } #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; } 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; } static struct power_event *p_state_end(struct timechart *tchart, int cpu, u64 timestamp) { struct power_event *pwr = zalloc(sizeof(*pwr)); if (!pwr) return NULL; 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; return pwr; } 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 = p_state_end(tchart, cpu, timestamp); if (!pwr) return; 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; } 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) { free((char *)backtrace); 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; } } 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; bool backtrace_used = false; 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); backtrace_used = true; } if (p && p->current) { if (p->current->state != TYPE_NONE) { if (backtrace && backtrace_used) backtrace = strdup(backtrace); pid_put_sample(tchart, next_pid, p->current->state, cpu, p->current->state_since, timestamp, backtrace); backtrace_used = true; } 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; }