perf top: Support userspace symbols too

Example:

Compiling the kernel with 'make -k 22 allyesconfig'

[root@emilia linux-2.6-tip]# perf top -r 90
------------------------------------------------------------------------------
   PerfTop:    3669 irqs/sec  kernel:59.9% [1000Hz cycles],  (all, 8 CPUs)
------------------------------------------------------------------------------

             samples  pcnt function                                 DSO
             _______ _____ ________________________________ ________________

             3062.00  6.5% clear_page_c                     [kernel]
             2233.00  4.8% _int_malloc                      /lib64/libc-2.5.so
             2100.00  4.5% yylex                            /home/acme/git/build/allyesconfig/scripts/genksyms/genksyms
             2029.00  4.3% memset                           /lib64/libc-2.5.so
             1224.00  2.6% page_fault                       [kernel]
             1075.00  2.3% __GI_strlen                      /lib64/libc-2.5.so
              863.00  1.8% sub_preempt_count                [kernel]
              822.00  1.8% __GI_memcpy                      /lib64/libc-2.5.so
              810.00  1.7% __GI_vfprintf                    /lib64/libc-2.5.so
              786.00  1.7% _int_free                        /lib64/libc-2.5.so
              775.00  1.7% __GI_strcmp                      /lib64/libc-2.5.so
              748.00  1.6% _spin_lock                       [kernel]
              699.00  1.5% main                             /home/acme/git/build/allyesconfig/scripts/basic/fixdep
              659.00  1.4% add_preempt_count                [kernel]
              649.00  1.4% yyparse                          /home/acme/git/build/allyesconfig/scripts/genksyms/genksyms
              645.00  1.4% preempt_trace                    [kernel]
              635.00  1.4% __GI___libc_free                 /lib64/libc-2.5.so
              597.00  1.3% trace_preempt_on                 [kernel]
              551.00  1.2% __GI___libc_malloc               /lib64/libc-2.5.so
              516.00  1.1% _spin_lock_irqsave               [kernel]
              481.00  1.0% copy_user_generic_string         [kernel]
              479.00  1.0% unmap_vmas                       [kernel]
              429.00  0.9% _IO_file_xsputn_internal         /lib64/libc-2.5.so
              425.00  0.9% __GI_strncpy                     /lib64/libc-2.5.so
              416.00  0.9% get_page_from_freelist           [kernel]
              414.00  0.9% malloc_consolidate               /lib64/libc-2.5.so
              406.00  0.9% get_parent_ip                    [kernel]
              362.00  0.8% __rmqueue                        [kernel]
              347.00  0.7% in_lock_functions                [kernel]
              316.00  0.7% __d_lookup                       [kernel]

[root@emilia linux-2.6-tip]#

More polishing is needed to print just DSO basename when not
--verbose, etc.

Supporting a 'comm' column requires some more reworking of 'perf
top' internals as we will need to use something like the hist
entries 'perf report' uses and will be done in another patch.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Mike Galbraith <efault@gmx.de>
LKML-Reference: <1256592199-9608-3-git-send-email-acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
Arnaldo Carvalho de Melo 2009-10-26 19:23:19 -02:00 committed by Ingo Molnar
parent 234fbbf508
commit 5b2bb75a0d

View file

@ -318,7 +318,7 @@ static void show_details(struct sym_entry *syme)
} }
/* /*
* Symbols will be added here in record_ip and will get out * Symbols will be added here in event__process_sample and will get out
* after decayed. * after decayed.
*/ */
static LIST_HEAD(active_symbols); static LIST_HEAD(active_symbols);
@ -459,18 +459,18 @@ static void print_sym_table(void)
} }
if (nr_counters == 1) if (nr_counters == 1)
printf(" samples pcnt"); printf(" samples pcnt");
else else
printf(" weight samples pcnt"); printf(" weight samples pcnt");
if (verbose) if (verbose)
printf(" RIP "); printf(" RIP ");
printf(" kernel function\n"); printf(" function DSO\n");
printf(" %s _______ _____", printf(" %s _______ _____",
nr_counters == 1 ? " " : "______"); nr_counters == 1 ? " " : "______");
if (verbose) if (verbose)
printf(" ________________"); printf(" ________________");
printf(" _______________\n\n"); printf(" ________________________________ ________________\n\n");
for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) {
struct symbol *sym; struct symbol *sym;
@ -486,16 +486,15 @@ static void print_sym_table(void)
sum_ksamples)); sum_ksamples));
if (nr_counters == 1 || !display_weighted) if (nr_counters == 1 || !display_weighted)
printf("%20.2f - ", syme->weight); printf("%20.2f ", syme->weight);
else else
printf("%9.1f %10ld - ", syme->weight, syme->snap_count); printf("%9.1f %10ld ", syme->weight, syme->snap_count);
percent_color_fprintf(stdout, "%4.1f%%", pcnt); percent_color_fprintf(stdout, "%4.1f%%", pcnt);
if (verbose) if (verbose)
printf(" - %016llx", sym->start); printf(" %016llx", sym->start);
printf(" : %s", sym->name); printf(" %-32s", sym->name);
if (syme->map->dso->name[0] == '[') printf(" %s", syme->map->dso->short_name);
printf(" \t%s", syme->map->dso->name);
printf("\n"); printf("\n");
} }
} }
@ -818,41 +817,97 @@ static int parse_symbols(void)
return 0; return 0;
} }
/* static void event__process_sample(const event_t *self, int counter)
* Binary search in the histogram table and record the hit:
*/
static void record_ip(u64 ip, int counter)
{ {
u64 ip = self->ip.ip;
struct map *map; struct map *map;
struct symbol *sym = kernel_maps__find_symbol(ip, &map); struct sym_entry *syme;
struct symbol *sym;
if (sym != NULL) { switch (self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK) {
struct sym_entry *syme = dso__sym_priv(map->dso, sym); case PERF_RECORD_MISC_USER: {
struct thread *thread = threads__findnew(self->ip.pid);
if (!syme->skip) { if (thread == NULL)
syme->count[counter]++;
record_precise_ip(syme, counter, ip);
pthread_mutex_lock(&active_symbols_lock);
if (list_empty(&syme->node) || !syme->node.next)
__list_insert_active_sym(syme);
pthread_mutex_unlock(&active_symbols_lock);
return; return;
map = thread__find_map(thread, ip);
if (map != NULL) {
ip = map->map_ip(map, ip);
sym = map->dso->find_symbol(map->dso, ip);
if (sym == NULL)
return;
userspace_samples++;
break;
} }
} }
/*
samples--; * If this is outside of all known maps,
} * and is a negative address, try to look it
* up in the kernel dso, as it might be a
static void process_event(u64 ip, int counter, int user) * vsyscall or vdso (which executes in user-mode).
{ */
samples++; if ((long long)ip >= 0)
return;
if (user) { /* Fall thru */
userspace_samples++; case PERF_RECORD_MISC_KERNEL:
sym = kernel_maps__find_symbol(ip, &map);
if (sym == NULL)
return;
break;
default:
return; return;
} }
record_ip(ip, counter); syme = dso__sym_priv(map->dso, sym);
if (!syme->skip) {
syme->count[counter]++;
record_precise_ip(syme, counter, ip);
pthread_mutex_lock(&active_symbols_lock);
if (list_empty(&syme->node) || !syme->node.next)
__list_insert_active_sym(syme);
pthread_mutex_unlock(&active_symbols_lock);
++samples;
return;
}
}
static void event__process_mmap(event_t *self)
{
struct thread *thread = threads__findnew(self->mmap.pid);
if (thread != NULL) {
struct map *map = map__new(&self->mmap, NULL, 0,
sizeof(struct sym_entry),
symbol_filter);
if (map != NULL)
thread__insert_map(thread, map);
}
}
static void event__process_comm(event_t *self)
{
struct thread *thread = threads__findnew(self->comm.pid);
if (thread != NULL)
thread__set_comm(thread, self->comm.comm);
}
static int event__process(event_t *event)
{
switch (event->header.type) {
case PERF_RECORD_COMM:
event__process_comm(event);
break;
case PERF_RECORD_MMAP:
event__process_mmap(event);
break;
default:
break;
}
return 0;
} }
struct mmap_data { struct mmap_data {
@ -925,13 +980,11 @@ static void mmap_read_counter(struct mmap_data *md)
event = &event_copy; event = &event_copy;
} }
if (event->header.type == PERF_RECORD_SAMPLE)
event__process_sample(event, md->counter);
else
event__process(event);
old += size; old += size;
if (event->header.type == PERF_RECORD_SAMPLE) {
int user =
(event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_USER;
process_event(event->ip.ip, md->counter, user);
}
} }
md->prev = old; md->prev = old;
@ -973,6 +1026,7 @@ static void start_counter(int i, int counter)
} }
attr->inherit = (cpu < 0) && inherit; attr->inherit = (cpu < 0) && inherit;
attr->mmap = 1;
try_again: try_again:
fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0); fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0);
@ -1031,6 +1085,11 @@ static int __cmd_top(void)
int i, counter; int i, counter;
int ret; int ret;
if (target_pid != -1)
event__synthesize_thread(target_pid, event__process);
else
event__synthesize_threads(event__process);
for (i = 0; i < nr_cpus; i++) { for (i = 0; i < nr_cpus; i++) {
group_fd = -1; group_fd = -1;
for (counter = 0; counter < nr_counters; counter++) for (counter = 0; counter < nr_counters; counter++)