Skip to content
Snippets Groups Projects
builtin-top.c 30.2 KiB
Newer Older
 * builtin-top.c
 *
 * Builtin top command: Display a continuously updated profile of
 * any workload, CPU or specific PID.
 *
 * Copyright (C) 2008, Red Hat Inc, Ingo Molnar <mingo@redhat.com>
 *
 * Improvements and fixes by:
 *
 *   Arjan van de Ven <arjan@linux.intel.com>
 *   Yanmin Zhang <yanmin.zhang@intel.com>
 *   Wu Fengguang <fengguang.wu@intel.com>
 *   Mike Galbraith <efault@gmx.de>
 *   Paul Mackerras <paulus@samba.org>
 *
 * Released under the GPL v2. (and only v2, not any later version)
#include "perf.h"
#include "util/color.h"
#include "util/session.h"
#include "util/symbol.h"
#include "util/util.h"
#include <linux/rbtree.h>
#include "util/parse-options.h"
#include "util/parse-events.h"
#include "util/debug.h"

#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <sched.h>
#include <pthread.h>

#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <sys/uio.h>
#include <sys/mman.h>

#include <linux/unistd.h>
#include <linux/types.h>

static int			fd[MAX_NR_CPUS][MAX_COUNTERS];
static int			system_wide			=      0;
static int			default_interval		=      0;
static int			count_filter			=      5;
static int			print_entries;
static int			target_pid			=     -1;
static int			inherit				=      0;
static int			profile_cpu			=     -1;
static int			nr_cpus				=      0;
static unsigned int		realtime_prio			=      0;
static int			group				=      0;
static unsigned int		page_size;
static unsigned int		mmap_pages			=     16;
static int			freq				=   1000; /* 1 KHz */
static int			delay_secs			=      2;
static int			zero                            =      0;
static int			dump_symtab                     =      0;
static bool			hide_kernel_symbols		=  false;
static bool			hide_user_symbols		=  false;
static struct winsize		winsize;
/*
 * Source
 */

struct source_line {
	u64			eip;
	unsigned long		count[MAX_COUNTERS];
	char			*line;
	struct source_line	*next;
};

static char			*sym_filter			=   NULL;
struct sym_entry		*sym_filter_entry		=   NULL;
static int			sym_pcnt_filter			=      5;
static int			sym_counter			=      0;
static int			display_weighted		=     -1;
struct sym_entry_source {
	struct source_line	*source;
	struct source_line	*lines;
	struct source_line	**lines_tail;
	pthread_mutex_t		lock;
};

struct sym_entry {
	struct rb_node		rb_node;
	struct list_head	node;
	unsigned long		snap_count;
	double			weight;
static inline struct symbol *sym_entry__symbol(struct sym_entry *self)
{
       return ((void *)self) + symbol_conf.priv_size;
static void get_term_dimensions(struct winsize *ws)
	char *s = getenv("LINES");

	if (s != NULL) {
		ws->ws_row = atoi(s);
		s = getenv("COLUMNS");
		if (s != NULL) {
			ws->ws_col = atoi(s);
			if (ws->ws_row && ws->ws_col)
				return;
		}
#ifdef TIOCGWINSZ
	if (ioctl(1, TIOCGWINSZ, ws) == 0 &&
	    ws->ws_row && ws->ws_col)
		return;
	ws->ws_row = 25;
	ws->ws_col = 80;
static void update_print_entries(struct winsize *ws)
	print_entries = ws->ws_row;

	if (print_entries > 9)
		print_entries -= 9;
}

static void sig_winch_handler(int sig __used)
{
	get_term_dimensions(&winsize);
	update_print_entries(&winsize);
static void parse_source(struct sym_entry *syme)
{
	struct symbol *sym;
	char command[PATH_MAX*2];
		syme->src = zalloc(sizeof(*source));
		if (syme->src == NULL)
			return;
		pthread_mutex_init(&syme->src->lock, NULL);
	}

	source = syme->src;

	if (source->lines) {
		pthread_mutex_lock(&source->lock);
	map = syme->map;
	path = map->dso->long_name;
	sprintf(command,
		"objdump --start-address=0x%016Lx "
			 "--stop-address=0x%016Lx -dS %s",
		map->unmap_ip(map, sym->start),
		map->unmap_ip(map, sym->end), path);
	pthread_mutex_lock(&source->lock);
	source->lines_tail = &source->lines;
	while (!feof(file)) {
		struct source_line *src;
		size_t dummy = 0;
		char *c;

		src = malloc(sizeof(struct source_line));
		assert(src != NULL);
		memset(src, 0, sizeof(struct source_line));

		if (getline(&src->line, &dummy, file) < 0)
			break;
		if (!src->line)
			break;

		c = strchr(src->line, '\n');
		if (c)
			*c = 0;

		src->next = NULL;
		*source->lines_tail = src;
		source->lines_tail = &src->next;

		if (strlen(src->line)>8 && src->line[8] == ':') {
			src->eip = strtoull(src->line, NULL, 16);
			src->eip = map->unmap_ip(map, src->eip);
		}
		if (strlen(src->line)>8 && src->line[16] == ':') {
			src->eip = strtoull(src->line, NULL, 16);
			src->eip = map->unmap_ip(map, src->eip);
	pthread_mutex_unlock(&source->lock);
}

static void __zero_source_counters(struct sym_entry *syme)
{
	int i;
	struct source_line *line;

	while (line) {
		for (i = 0; i < nr_counters; i++)
			line->count[i] = 0;
		line = line->next;
	}
}

static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip)
{
	struct source_line *line;

	if (syme != sym_filter_entry)
		return;

	if (pthread_mutex_trylock(&syme->src->lock))
	if (syme->src == NULL || syme->src->source == NULL)
	for (line = syme->src->lines; line; line = line->next) {
		if (line->eip == ip) {
			line->count[counter]++;
			break;
		}
		if (line->eip > ip)
			break;
	}
out_unlock:
	pthread_mutex_unlock(&syme->src->lock);
}

static void lookup_sym_source(struct sym_entry *syme)
{
	struct symbol *symbol = sym_entry__symbol(syme);
Mike Galbraith's avatar
Loading
Loading full blame...