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 "builtin.h"
#include "util/color.h"
#include "util/session.h"
#include "util/symbol.h"
#include "util/thread.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>

Mike Galbraith
committed
#include <termios.h>
#include <unistd.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;

Mike Galbraith
committed
/*
* 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;

Mike Galbraith
committed
/*
* Symbols
*/
struct sym_entry_source {
struct source_line *source;
struct source_line *lines;
struct source_line **lines_tail;
pthread_mutex_t lock;
};

Arnaldo Carvalho de Melo
committed
struct rb_node rb_node;
struct list_head node;
unsigned long snap_count;
double weight;
struct map *map;
struct sym_entry_source *src;
unsigned long count[0];

Mike Galbraith
committed
/*
* Source functions
*/

Arnaldo Carvalho de Melo
committed
static inline struct symbol *sym_entry__symbol(struct sym_entry *self)
{
return ((void *)self) + symbol_conf.priv_size;

Arnaldo Carvalho de Melo
committed
}
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);

Mike Galbraith
committed
static void parse_source(struct sym_entry *syme)
{
struct symbol *sym;
struct sym_entry_source *source;
struct map *map;

Mike Galbraith
committed
FILE *file;
const char *path;
u64 len;

Mike Galbraith
committed
if (!syme)
return;
if (syme->src == NULL) {
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);

Mike Galbraith
committed
goto out_assign;
}

Arnaldo Carvalho de Melo
committed
sym = sym_entry__symbol(syme);
map = syme->map;
path = map->dso->long_name;

Mike Galbraith
committed
len = sym->end - sym->start;
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);

Mike Galbraith
committed
file = popen(command, "r");
if (!file)
return;
pthread_mutex_lock(&source->lock);
source->lines_tail = &source->lines;

Mike Galbraith
committed
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;

Mike Galbraith
committed
if (strlen(src->line)>8 && src->line[8] == ':') {
src->eip = strtoull(src->line, NULL, 16);
src->eip = map->unmap_ip(map, src->eip);

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

Mike Galbraith
committed
}
}
pclose(file);
out_assign:
sym_filter_entry = syme;
pthread_mutex_unlock(&source->lock);

Mike Galbraith
committed
}
static void __zero_source_counters(struct sym_entry *syme)
{
int i;
struct source_line *line;
line = syme->src->lines;

Mike Galbraith
committed
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))

Mike Galbraith
committed
return;
if (syme->src == NULL || syme->src->source == NULL)

Mike Galbraith
committed
goto out_unlock;
for (line = syme->src->lines; line; line = line->next) {

Mike Galbraith
committed
if (line->eip == ip) {
line->count[counter]++;
break;
}
if (line->eip > ip)
break;
}
out_unlock:
pthread_mutex_unlock(&syme->src->lock);

Mike Galbraith
committed
}
static void lookup_sym_source(struct sym_entry *syme)
{

Arnaldo Carvalho de Melo
committed
struct symbol *symbol = sym_entry__symbol(syme);
Loading
Loading full blame...