Newer
Older
event__process_mmap(event);
break;
default:
break;
}
struct mmap_data {
int counter;
void *base;
unsigned int prev;
};
static unsigned int mmap_read_head(struct mmap_data *md)
{
struct perf_event_mmap_page *pc = md->base;
int head;
head = pc->data_head;
rmb();
return head;
}

Frederic Weisbecker
committed
static void mmap_read_counter(struct mmap_data *md)
{
unsigned int head = mmap_read_head(md);
unsigned int old = md->prev;
unsigned char *data = md->base + page_size;
int diff;
/*
* If we're further behind than half the buffer, there's a chance
* the writer will bite our tail and mess up the samples under us.
*
* If we somehow ended up ahead of the head, we got messed up.
*
* In either case, truncate and restart at head.
*/
diff = head - old;
if (diff > md->mask / 2 || diff < 0) {
fprintf(stderr, "WARNING: failed to keep up with mmap data.\n");
/*
* head points to a known good entry, start there.
*/
old = head;
}
for (; old != head;) {
event_t *event = (event_t *)&data[old & md->mask];
event_t event_copy;
size_t size = event->header.size;
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
/*
* Event straddles the mmap boundary -- header should always
* be inside due to u64 alignment of output.
*/
if ((old & md->mask) + size != ((old + size) & md->mask)) {
unsigned int offset = old;
unsigned int len = min(sizeof(*event), size), cpy;
void *dst = &event_copy;
do {
cpy = min(md->mask + 1 - (offset & md->mask), len);
memcpy(dst, &data[offset & md->mask], cpy);
offset += cpy;
dst += cpy;
len -= cpy;
} while (len);
event = &event_copy;
}
if (event->header.type == PERF_RECORD_SAMPLE)
event__process_sample(event, md->counter);
else
event__process(event);
old += size;
}
md->prev = old;
}
static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS];
static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS];

Frederic Weisbecker
committed
static void mmap_read(void)
{
int i, counter;
for (i = 0; i < nr_cpus; i++) {
for (counter = 0; counter < nr_counters; counter++)
mmap_read_counter(&mmap_array[i][counter]);
}
}

Ingo Molnar
committed
int nr_poll;
int group_fd;
static void start_counter(int i, int counter)
struct perf_event_attr *attr;

Ingo Molnar
committed
cpu = profile_cpu;
if (target_pid == -1 && profile_cpu == -1)
cpu = i;
attr = attrs + counter;
attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
if (freq) {
attr->sample_type |= PERF_SAMPLE_PERIOD;
attr->freq = 1;
attr->sample_freq = freq;
}
attr->inherit = (cpu < 0) && inherit;

Ingo Molnar
committed
try_again:
fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0);

Ingo Molnar
committed
if (fd[i][counter] < 0) {
int err = errno;
die("No permission - are you root?\n");

Ingo Molnar
committed
/*
* If it's cycles then fall back to hrtimer
* based cpu-clock-tick sw counter, which
* is always available even if no PMU support:
*/
if (attr->type == PERF_TYPE_HARDWARE
&& attr->config == PERF_COUNT_HW_CPU_CYCLES) {

Ingo Molnar
committed
if (verbose)
warning(" ... trying to fall back to cpu-clock-ticks\n");

Ingo Molnar
committed
attr->type = PERF_TYPE_SOFTWARE;
attr->config = PERF_COUNT_SW_CPU_CLOCK;

Ingo Molnar
committed
goto try_again;
}
printf("\n");
error("perfcounter syscall returned with %d (%s)\n",
fd[i][counter], strerror(err));
die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");

Ingo Molnar
committed
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
exit(-1);
}
assert(fd[i][counter] >= 0);
fcntl(fd[i][counter], F_SETFL, O_NONBLOCK);
/*
* First counter acts as the group leader:
*/
if (group && group_fd == -1)
group_fd = fd[i][counter];
event_array[nr_poll].fd = fd[i][counter];
event_array[nr_poll].events = POLLIN;
nr_poll++;
mmap_array[i][counter].counter = counter;
mmap_array[i][counter].prev = 0;
mmap_array[i][counter].mask = mmap_pages*page_size - 1;
mmap_array[i][counter].base = mmap(NULL, (mmap_pages+1)*page_size,
PROT_READ, MAP_SHARED, fd[i][counter], 0);
if (mmap_array[i][counter].base == MAP_FAILED)
die("failed to mmap with %d (%s)\n", errno, strerror(errno));
}
static int __cmd_top(void)
{
pthread_t thread;
int i, counter;
if (target_pid != -1)
event__synthesize_thread(target_pid, event__process);
else
event__synthesize_threads(event__process);
for (i = 0; i < nr_cpus; i++) {
group_fd = -1;

Ingo Molnar
committed
for (counter = 0; counter < nr_counters; counter++)
start_counter(i, counter);

Frederic Weisbecker
committed
/* Wait for a minimal set of events before starting the snapshot */
poll(event_array, nr_poll, 100);
mmap_read();
if (pthread_create(&thread, NULL, display_thread, NULL)) {
printf("Could not create display thread.\n");
exit(-1);
}
if (realtime_prio) {
struct sched_param param;
param.sched_priority = realtime_prio;
if (sched_setscheduler(0, SCHED_FIFO, ¶m)) {
printf("Could not set realtime priority.\n");
exit(-1);
}
}
while (1) {

Frederic Weisbecker
committed
mmap_read();
ret = poll(event_array, nr_poll, 100);
}
return 0;
}
static const char * const top_usage[] = {
"perf top [<options>]",
NULL
};
static const struct option options[] = {
OPT_CALLBACK('e', "event", NULL, "event",
"event selector. use 'perf list' to list available events",
parse_events),
OPT_INTEGER('c', "count", &default_interval,
"event period to sample"),
OPT_INTEGER('p', "pid", &target_pid,
"profile events on existing pid"),
OPT_BOOLEAN('a', "all-cpus", &system_wide,
"system-wide collection from all CPUs"),
OPT_INTEGER('C', "CPU", &profile_cpu,
"CPU to profile on"),
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols,
"hide kernel symbols"),
OPT_INTEGER('m', "mmap-pages", &mmap_pages,
"number of mmap data pages"),
OPT_INTEGER('r', "realtime", &realtime_prio,
"collect data with this RT SCHED_FIFO priority"),
OPT_INTEGER('d', "delay", &delay_secs,
"number of seconds to delay between refreshes"),
OPT_BOOLEAN('D', "dump-symtab", &dump_symtab,
"dump the symbol table used for profiling"),
OPT_INTEGER('f', "count-filter", &count_filter,
"only display functions with more events than this"),
OPT_BOOLEAN('g', "group", &group,
"put the counters into a counter group"),
OPT_BOOLEAN('i', "inherit", &inherit,
"child tasks inherit counters"),

Mike Galbraith
committed
OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name",
"symbol to annotate - requires -k option"),
OPT_INTEGER('F', "freq", &freq,
OPT_INTEGER('E', "entries", &print_entries,
"display this many functions"),
OPT_BOOLEAN('U', "hide_user_symbols", &hide_user_symbols,
"hide user symbols"),
OPT_BOOLEAN('v', "verbose", &verbose,
"be more verbose (show counter open errors, etc)"),
int cmd_top(int argc, const char **argv, const char *prefix __used)
page_size = sysconf(_SC_PAGE_SIZE);
argc = parse_options(argc, argv, options, top_usage, 0);
if (argc)
usage_with_options(top_usage, options);
/* CPU and PID are mutually exclusive */
if (target_pid != -1 && profile_cpu != -1) {
printf("WARNING: PID switch overriding CPU\n");
sleep(1);
profile_cpu = -1;
}
if (!nr_counters)
symbol_conf.priv_size = (sizeof(struct sym_entry) +
(nr_counters + 1) * sizeof(unsigned long));
if (symbol_conf.vmlinux_name == NULL)
symbol_conf.try_vmlinux_path = true;
if (symbol__init(&symbol_conf) < 0)
return -1;
if (delay_secs < 1)
delay_secs = 1;

Mike Galbraith
committed
parse_source(sym_filter_entry);
/*
* User specified count overrides default frequency.
*/
if (default_interval)
freq = 0;
else if (freq) {
default_interval = freq;
} else {
fprintf(stderr, "frequency and count are zero, aborting\n");
exit(EXIT_FAILURE);
}
/*
* Fill in the ones not specifically initialized via -c:
*/
for (counter = 0; counter < nr_counters; counter++) {
if (attrs[counter].sample_period)
attrs[counter].sample_period = default_interval;
}
nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
assert(nr_cpus <= MAX_NR_CPUS);
assert(nr_cpus >= 0);
if (target_pid != -1 || profile_cpu != -1)
nr_cpus = 1;
get_term_dimensions(&winsize);
if (print_entries == 0) {
update_print_entries(&winsize);
signal(SIGWINCH, sig_winch_handler);
}