2164 lines · 50.2 KB · C
RawDownload
// 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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t