aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/builtin-top.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/builtin-top.c')
-rw-r--r--tools/perf/builtin-top.c283
1 files changed, 85 insertions, 198 deletions
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index b6998e055767..ce2e50c891c7 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -21,10 +21,12 @@
#include "perf.h"
#include "util/color.h"
+#include "util/evlist.h"
#include "util/evsel.h"
#include "util/session.h"
#include "util/symbol.h"
#include "util/thread.h"
+#include "util/thread_map.h"
#include "util/util.h"
#include <linux/rbtree.h>
#include "util/parse-options.h"
@@ -60,6 +62,8 @@
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
+struct perf_evlist *evsel_list;
+
static bool system_wide = false;
static int default_interval = 0;
@@ -75,7 +79,7 @@ static struct cpu_map *cpus;
static int realtime_prio = 0;
static bool group = false;
static unsigned int page_size;
-static unsigned int mmap_pages = 16;
+static unsigned int mmap_pages = 128;
static int freq = 1000; /* 1 KHz */
static int delay_secs = 2;
@@ -267,7 +271,7 @@ static void __zero_source_counters(struct sym_entry *syme)
line = syme->src->lines;
while (line) {
- for (i = 0; i < nr_counters; i++)
+ for (i = 0; i < evsel_list->nr_entries; i++)
line->count[i] = 0;
line = line->next;
}
@@ -414,7 +418,7 @@ static double sym_weight(const struct sym_entry *sym)
if (!display_weighted)
return weight;
- for (counter = 1; counter < nr_counters-1; counter++)
+ for (counter = 1; counter < evsel_list->nr_entries - 1; counter++)
weight *= sym->count[counter];
weight /= (sym->count[counter] + 1);
@@ -501,7 +505,7 @@ static void print_sym_table(void)
rb_insert_active_sym(&tmp, syme);
sum_ksamples += syme->snap_count;
- for (j = 0; j < nr_counters; j++)
+ for (j = 0; j < evsel_list->nr_entries; j++)
syme->count[j] = zero ? 0 : syme->count[j] * 7 / 8;
} else
list_remove_active_sym(syme);
@@ -535,9 +539,9 @@ static void print_sym_table(void)
esamples_percent);
}
- if (nr_counters == 1 || !display_weighted) {
+ if (evsel_list->nr_entries == 1 || !display_weighted) {
struct perf_evsel *first;
- first = list_entry(evsel_list.next, struct perf_evsel, node);
+ first = list_entry(evsel_list->entries.next, struct perf_evsel, node);
printf("%" PRIu64, (uint64_t)first->attr.sample_period);
if (freq)
printf("Hz ");
@@ -547,7 +551,7 @@ static void print_sym_table(void)
if (!display_weighted)
printf("%s", event_name(sym_evsel));
- else list_for_each_entry(counter, &evsel_list, node) {
+ else list_for_each_entry(counter, &evsel_list->entries, node) {
if (counter->idx)
printf("/");
@@ -606,7 +610,7 @@ static void print_sym_table(void)
sym_width = winsize.ws_col - dso_width - 29;
}
putchar('\n');
- if (nr_counters == 1)
+ if (evsel_list->nr_entries == 1)
printf(" samples pcnt");
else
printf(" weight samples pcnt");
@@ -615,7 +619,7 @@ static void print_sym_table(void)
printf(" RIP ");
printf(" %-*.*s DSO\n", sym_width, sym_width, "function");
printf(" %s _______ _____",
- nr_counters == 1 ? " " : "______");
+ evsel_list->nr_entries == 1 ? " " : "______");
if (verbose)
printf(" ________________");
printf(" %-*.*s", sym_width, sym_width, graph_line);
@@ -634,7 +638,7 @@ static void print_sym_table(void)
pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) /
sum_ksamples));
- if (nr_counters == 1 || !display_weighted)
+ if (evsel_list->nr_entries == 1 || !display_weighted)
printf("%20.2f ", syme->weight);
else
printf("%9.1f %10ld ", syme->weight, syme->snap_count);
@@ -744,7 +748,7 @@ static void print_mapped_keys(void)
fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", delay_secs);
fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries);
- if (nr_counters > 1)
+ if (evsel_list->nr_entries > 1)
fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_evsel));
fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter);
@@ -753,7 +757,7 @@ static void print_mapped_keys(void)
fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL");
fprintf(stdout, "\t[S] stop annotation.\n");
- if (nr_counters > 1)
+ if (evsel_list->nr_entries > 1)
fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0);
fprintf(stdout,
@@ -783,7 +787,7 @@ static int key_mapped(int c)
return 1;
case 'E':
case 'w':
- return nr_counters > 1 ? 1 : 0;
+ return evsel_list->nr_entries > 1 ? 1 : 0;
default:
break;
}
@@ -831,22 +835,22 @@ static void handle_keypress(struct perf_session *session, int c)
signal(SIGWINCH, SIG_DFL);
break;
case 'E':
- if (nr_counters > 1) {
+ if (evsel_list->nr_entries > 1) {
fprintf(stderr, "\nAvailable events:");
- list_for_each_entry(sym_evsel, &evsel_list, node)
+ list_for_each_entry(sym_evsel, &evsel_list->entries, node)
fprintf(stderr, "\n\t%d %s", sym_evsel->idx, event_name(sym_evsel));
prompt_integer(&sym_counter, "Enter details event counter");
- if (sym_counter >= nr_counters) {
- sym_evsel = list_entry(evsel_list.next, struct perf_evsel, node);
+ if (sym_counter >= evsel_list->nr_entries) {
+ sym_evsel = list_entry(evsel_list->entries.next, struct perf_evsel, node);
sym_counter = 0;
fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(sym_evsel));
sleep(1);
break;
}
- list_for_each_entry(sym_evsel, &evsel_list, node)
+ list_for_each_entry(sym_evsel, &evsel_list->entries, node)
if (sym_evsel->idx == sym_counter)
break;
} else sym_counter = 0;
@@ -930,6 +934,7 @@ repeat:
/* Tag samples to be skipped. */
static const char *skip_symbols[] = {
"default_idle",
+ "native_safe_halt",
"cpu_idle",
"enter_idle",
"exit_idle",
@@ -988,8 +993,7 @@ static int symbol_filter(struct map *map, struct symbol *sym)
static void event__process_sample(const event_t *self,
struct sample_data *sample,
- struct perf_session *session,
- struct perf_evsel *evsel)
+ struct perf_session *session)
{
u64 ip = self->ip.ip;
struct sym_entry *syme;
@@ -1082,8 +1086,12 @@ static void event__process_sample(const event_t *self,
syme = symbol__priv(al.sym);
if (!syme->skip) {
- syme->count[evsel->idx]++;
+ struct perf_evsel *evsel;
+
syme->origin = origin;
+ evsel = perf_evlist__id2evsel(evsel_list, sample->id);
+ assert(evsel != NULL);
+ syme->count[evsel->idx]++;
record_precise_ip(syme, evsel->idx, ip);
pthread_mutex_lock(&active_symbols_lock);
if (list_empty(&syme->node) || !syme->node.next)
@@ -1092,156 +1100,52 @@ static void event__process_sample(const event_t *self,
}
}
-struct mmap_data {
- void *base;
- int mask;
- unsigned int prev;
-};
-
-static int perf_evsel__alloc_mmap_per_thread(struct perf_evsel *evsel,
- int ncpus, int nthreads)
-{
- evsel->priv = xyarray__new(ncpus, nthreads, sizeof(struct mmap_data));
- return evsel->priv != NULL ? 0 : -ENOMEM;
-}
-
-static void perf_evsel__free_mmap(struct perf_evsel *evsel)
-{
- xyarray__delete(evsel->priv);
- evsel->priv = NULL;
-}
-
-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;
-}
-
-static void perf_session__mmap_read_counter(struct perf_session *self,
- struct perf_evsel *evsel,
- int cpu, int thread_idx)
+static void perf_session__mmap_read_cpu(struct perf_session *self, int cpu)
{
- struct xyarray *mmap_array = evsel->priv;
- struct mmap_data *md = xyarray__entry(mmap_array, cpu, thread_idx);
- unsigned int head = mmap_read_head(md);
- unsigned int old = md->prev;
- unsigned char *data = md->base + page_size;
struct sample_data sample;
- 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;
- event_t event_copy;
-
- size_t size = event->header.size;
-
- /*
- * 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;
- }
+ while ((event = perf_evlist__read_on_cpu(evsel_list, cpu)) != NULL) {
+ perf_session__parse_sample(self, event, &sample);
- event__parse_sample(event, self, &sample);
if (event->header.type == PERF_RECORD_SAMPLE)
- event__process_sample(event, &sample, self, evsel);
+ event__process_sample(event, &sample, self);
else
event__process(event, &sample, self);
- old += size;
}
-
- md->prev = old;
}
-static struct pollfd *event_array;
-
static void perf_session__mmap_read(struct perf_session *self)
{
- struct perf_evsel *counter;
- int i, thread_index;
-
- for (i = 0; i < cpus->nr; i++) {
- list_for_each_entry(counter, &evsel_list, node) {
- for (thread_index = 0;
- thread_index < threads->nr;
- thread_index++) {
- perf_session__mmap_read_counter(self,
- counter, i, thread_index);
- }
- }
- }
-}
+ int i;
-int nr_poll;
-int group_fd;
+ for (i = 0; i < cpus->nr; i++)
+ perf_session__mmap_read_cpu(self, i);
+}
-static void start_counter(int i, struct perf_evsel *evsel)
+static void start_counters(struct perf_evlist *evlist)
{
- struct xyarray *mmap_array = evsel->priv;
- struct mmap_data *mm;
- struct perf_event_attr *attr;
- int cpu = -1;
- int thread_index;
-
- if (target_tid == -1)
- cpu = cpus->map[i];
+ struct perf_evsel *counter;
- attr = &evsel->attr;
+ list_for_each_entry(counter, &evlist->entries, node) {
+ struct perf_event_attr *attr = &counter->attr;
- attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
+ attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
- if (freq) {
- attr->sample_type |= PERF_SAMPLE_PERIOD;
- attr->freq = 1;
- attr->sample_freq = freq;
- }
+ if (freq) {
+ attr->sample_type |= PERF_SAMPLE_PERIOD;
+ attr->freq = 1;
+ attr->sample_freq = freq;
+ }
- attr->inherit = (cpu < 0) && inherit;
- attr->mmap = 1;
+ if (evlist->nr_entries > 1) {
+ attr->sample_type |= PERF_SAMPLE_ID;
+ attr->read_format |= PERF_FORMAT_ID;
+ }
- for (thread_index = 0; thread_index < threads->nr; thread_index++) {
+ attr->mmap = 1;
try_again:
- FD(evsel, i, thread_index) = sys_perf_event_open(attr,
- threads->map[thread_index], cpu, group_fd, 0);
-
- if (FD(evsel, i, thread_index) < 0) {
+ if (perf_evsel__open(counter, cpus, threads, group, inherit) < 0) {
int err = errno;
if (err == EPERM || err == EACCES)
@@ -1253,8 +1157,8 @@ try_again:
* 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) {
+ if (attr->type == PERF_TYPE_HARDWARE &&
+ attr->config == PERF_COUNT_HW_CPU_CYCLES) {
if (verbose)
warning(" ... trying to fall back to cpu-clock-ticks\n");
@@ -1264,39 +1168,23 @@ try_again:
goto try_again;
}
printf("\n");
- error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n",
- FD(evsel, i, thread_index), strerror(err));
+ error("sys_perf_event_open() syscall returned with %d "
+ "(%s). /bin/dmesg may provide additional information.\n",
+ err, strerror(err));
die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
exit(-1);
}
- assert(FD(evsel, i, thread_index) >= 0);
- fcntl(FD(evsel, i, thread_index), F_SETFL, O_NONBLOCK);
-
- /*
- * First counter acts as the group leader:
- */
- if (group && group_fd == -1)
- group_fd = FD(evsel, i, thread_index);
-
- event_array[nr_poll].fd = FD(evsel, i, thread_index);
- event_array[nr_poll].events = POLLIN;
- nr_poll++;
-
- mm = xyarray__entry(mmap_array, i, thread_index);
- mm->prev = 0;
- mm->mask = mmap_pages*page_size - 1;
- mm->base = mmap(NULL, (mmap_pages+1)*page_size,
- PROT_READ, MAP_SHARED, FD(evsel, i, thread_index), 0);
- if (mm->base == MAP_FAILED)
- die("failed to mmap with %d (%s)\n", errno, strerror(errno));
}
+
+ if (perf_evlist__mmap(evlist, cpus, threads, mmap_pages, true) < 0)
+ die("failed to mmap with %d (%s)\n", errno, strerror(errno));
}
static int __cmd_top(void)
{
pthread_t thread;
- struct perf_evsel *counter;
- int i, ret;
+ struct perf_evsel *first;
+ int ret;
/*
* FIXME: perf_session__new should allow passing a O_MMAP, so that all this
* mmap reading, etc is encapsulated in it. Use O_WRONLY for now.
@@ -1310,14 +1198,12 @@ static int __cmd_top(void)
else
event__synthesize_threads(event__process, session);
- for (i = 0; i < cpus->nr; i++) {
- group_fd = -1;
- list_for_each_entry(counter, &evsel_list, node)
- start_counter(i, counter);
- }
+ start_counters(evsel_list);
+ first = list_entry(evsel_list->entries.next, struct perf_evsel, node);
+ perf_session__set_sample_type(session, first->attr.sample_type);
/* Wait for a minimal set of events before starting the snapshot */
- poll(&event_array[0], nr_poll, 100);
+ poll(evsel_list->pollfd, evsel_list->nr_fds, 100);
perf_session__mmap_read(session);
@@ -1342,7 +1228,7 @@ static int __cmd_top(void)
perf_session__mmap_read(session);
if (hits == samples)
- ret = poll(event_array, nr_poll, 100);
+ ret = poll(evsel_list->pollfd, evsel_list->nr_fds, 100);
}
return 0;
@@ -1354,7 +1240,7 @@ static const char * const top_usage[] = {
};
static const struct option options[] = {
- OPT_CALLBACK('e', "event", NULL, "event",
+ OPT_CALLBACK('e', "event", &evsel_list, "event",
"event selector. use 'perf list' to list available events",
parse_events),
OPT_INTEGER('c', "count", &default_interval,
@@ -1404,6 +1290,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
struct perf_evsel *pos;
int status = -ENOMEM;
+ evsel_list = perf_evlist__new();
+ if (evsel_list == NULL)
+ return -ENOMEM;
+
page_size = sysconf(_SC_PAGE_SIZE);
argc = parse_options(argc, argv, options, top_usage, 0);
@@ -1419,11 +1309,6 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
usage_with_options(top_usage, options);
}
- event_array = malloc((sizeof(struct pollfd) *
- MAX_NR_CPUS * MAX_COUNTERS * threads->nr));
- if (!event_array)
- return -ENOMEM;
-
/* CPU and PID are mutually exclusive */
if (target_tid > 0 && cpu_list) {
printf("WARNING: PID switch overriding CPU\n");
@@ -1431,7 +1316,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
cpu_list = NULL;
}
- if (!nr_counters && perf_evsel_list__create_default() < 0) {
+ if (!evsel_list->nr_entries &&
+ perf_evlist__add_default(evsel_list) < 0) {
pr_err("Not enough memory for event selector list\n");
return -ENOMEM;
}
@@ -1459,9 +1345,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
if (cpus == NULL)
usage_with_options(top_usage, options);
- list_for_each_entry(pos, &evsel_list, node) {
- if (perf_evsel__alloc_mmap_per_thread(pos, cpus->nr, threads->nr) < 0 ||
- perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0)
+ list_for_each_entry(pos, &evsel_list->entries, node) {
+ if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0)
goto out_free_fd;
/*
* Fill in the ones not specifically initialized via -c:
@@ -1472,10 +1357,14 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
pos->attr.sample_period = default_interval;
}
- sym_evsel = list_entry(evsel_list.next, struct perf_evsel, node);
+ if (perf_evlist__alloc_pollfd(evsel_list, cpus->nr, threads->nr) < 0 ||
+ perf_evlist__alloc_mmap(evsel_list, cpus->nr) < 0)
+ goto out_free_fd;
+
+ sym_evsel = list_entry(evsel_list->entries.next, struct perf_evsel, node);
symbol_conf.priv_size = (sizeof(struct sym_entry) +
- (nr_counters + 1) * sizeof(unsigned long));
+ (evsel_list->nr_entries + 1) * sizeof(unsigned long));
symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
if (symbol__init() < 0)
@@ -1489,9 +1378,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
status = __cmd_top();
out_free_fd:
- list_for_each_entry(pos, &evsel_list, node)
- perf_evsel__free_mmap(pos);
- perf_evsel_list__delete();
+ perf_evlist__delete(evsel_list);
return status;
}