perf annotate: Handle samples not at objdump output addr boundaries
Without this patch we get this for need_resched: [root@mica ~]# perf annotate need_resched ------------------------------------------------ Percent | Source code & Disassembly of vmlinux ------------------------------------------------ : : : Disassembly of section .text: : : ffffffff810095ed <need_resched>: : return (state & TASK_INTERRUPTIBLE) || __fatal_signal_pending(p); : } : : static inline int need_resched(void) : { 0.00 : ffffffff810095ed: 55 push %rbp : return unlikely(test_thread_flag(TIF_NEED_RESCHED)); 0.00 : ffffffff810095ee: be 03 00 00 00 mov $0x3,%esi : : static inline struct thread_info *current_thread_info(void) : { : struct thread_info *ti; : ti = (void *)(percpu_read_stable(kernel_stack) + 0.00 : ffffffff810095f3: 65 48 8b 3c 25 48 b5 mov %gs:0xb548,%rdi 0.00 : ffffffff810095fa: 00 00 : return (state & TASK_INTERRUPTIBLE) || __fatal_signal_pending(p); : } : : static inline int need_resched(void) : { 0.00 : ffffffff810095fc: 48 89 e5 mov %rsp,%rbp : return unlikely(test_thread_flag(TIF_NEED_RESCHED)); 0.00 : ffffffff810095ff: 48 81 ef d8 1f 00 00 sub $0x1fd8,%rdi 0.00 : ffffffff81009606: e8 9d ff ff ff callq ffffffff810095a8 <test_ti_thread_flag> : } 0.00 : ffffffff8100960b: c9 leaveq 0.00 : ffffffff8100960c: 85 c0 test %eax,%eax 0.00 : ffffffff8100960e: 0f 95 c0 setne %al 0.00 : ffffffff81009611: 0f b6 c0 movzbl %al,%eax : Disassembly of section .vsyscall_0: : Disassembly of section .vsyscall_fn: : Disassembly of section .vsyscall_1: : Disassembly of section .vsyscall_2: : Disassembly of section .init.text: : Disassembly of section .altinstr_replacement: : Disassembly of section .exit.text: [root@mica ~]# But from the 'perf report' result we know that there are hits for need_resched on a 4 way machine mostly doing nothing, so after adding code to show what is in each hist offset and collapsing IP hits for what happens between objdump lines we get, for the same perf.data file: [root@mica ~]# perf annotate -v need_resched ------------------------------------------------ Percent | Source code & Disassembly of vmlinux ------------------------------------------------ : : : Disassembly of section .text: : : ffffffff810095ed <need_resched>: : return (state & TASK_INTERRUPTIBLE) || __fatal_signal_pending(p); : } : : static inline int need_resched(void) : { 0.00 : ffffffff810095ed: 55 push %rbp : return unlikely(test_thread_flag(TIF_NEED_RESCHED)); 52.78 : ffffffff810095ee: be 03 00 00 00 mov $0x3,%esi : : static inline struct thread_info *current_thread_info(void) : { : struct thread_info *ti; : ti = (void *)(percpu_read_stable(kernel_stack) + 0.00 : ffffffff810095f3: 65 48 8b 3c 25 48 b5 mov %gs:0xb548,%rdi 0.00 : ffffffff810095fa: 00 00 : return (state & TASK_INTERRUPTIBLE) || __fatal_signal_pending(p); : } : : static inline int need_resched(void) : { 0.00 : ffffffff810095fc: 48 89 e5 mov %rsp,%rbp : return unlikely(test_thread_flag(TIF_NEED_RESCHED)); 9.72 : ffffffff810095ff: 48 81 ef d8 1f 00 00 sub $0x1fd8,%rdi 0.00 : ffffffff81009606: e8 9d ff ff ff callq ffffffff810095a8 <test_ti_thread_flag> : } 0.00 : ffffffff8100960b: c9 leaveq 0.00 : ffffffff8100960c: 85 c0 test %eax,%eax 37.50 : ffffffff8100960e: 0f 95 c0 setne %al 0.00 : ffffffff81009611: 0f b6 c0 movzbl %al,%eax : Disassembly of section .vsyscall_0: : Disassembly of section .vsyscall_fn: : Disassembly of section .vsyscall_1: : Disassembly of section .vsyscall_2: : Disassembly of section .init.text: : Disassembly of section .altinstr_replacement: : Disassembly of section .exit.text: [root@mica ~]# And now 'perf annotate -v', verbose mode, will show the hits per precise IP, so that one can make sense of the attribution to each objdumop line: [root@mica ~]# perf annotate -v need_resched Looking at the vmlinux_path (5 entries long) Using /lib/modules/2.6.33-rc8-tip-00784-g3471df5-dirty/build/vmlinux for symbols annotate_sym: filename=/lib/modules/2.6.33-rc8-tip-00784-g3471df5-dirty/build/vmlinux, sym=need_resched, start=0xffffffff810095ed, end=0xffffffff81009614 ------------------------------------------------ Percent | Source code & Disassembly of vmlinux ------------------------------------------------ ffffffff810095f1: 152 ffffffff81009603: 28 ffffffff8100960f: 55 ffffffff81009610: 53 h->sum: 288 <SNIP same annotation> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: David Miller <davem@davemloft.net> Cc: Frédéric Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> LKML-Reference: <1267194194-15670-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
parent
6667661df4
commit
48fb4fdd6b
|
@ -145,21 +145,58 @@ static int process_sample_event(event_t *event, struct perf_session *session)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_line(FILE *file, struct hist_entry *he, u64 len)
|
struct objdump_line {
|
||||||
|
struct list_head node;
|
||||||
|
s64 offset;
|
||||||
|
char *line;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct objdump_line *objdump_line__new(s64 offset, char *line)
|
||||||
|
{
|
||||||
|
struct objdump_line *self = malloc(sizeof(*self));
|
||||||
|
|
||||||
|
if (self != NULL) {
|
||||||
|
self->offset = offset;
|
||||||
|
self->line = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void objdump_line__free(struct objdump_line *self)
|
||||||
|
{
|
||||||
|
free(self->line);
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void objdump__add_line(struct list_head *head, struct objdump_line *line)
|
||||||
|
{
|
||||||
|
list_add_tail(&line->node, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
|
||||||
|
struct objdump_line *pos)
|
||||||
|
{
|
||||||
|
list_for_each_entry_continue(pos, head, node)
|
||||||
|
if (pos->offset >= 0)
|
||||||
|
return pos;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_line(FILE *file, struct hist_entry *he,
|
||||||
|
struct list_head *head)
|
||||||
{
|
{
|
||||||
struct symbol *sym = he->sym;
|
struct symbol *sym = he->sym;
|
||||||
|
struct objdump_line *objdump_line;
|
||||||
char *line = NULL, *tmp, *tmp2;
|
char *line = NULL, *tmp, *tmp2;
|
||||||
static const char *prev_line;
|
|
||||||
static const char *prev_color;
|
|
||||||
unsigned int offset;
|
|
||||||
size_t line_len;
|
size_t line_len;
|
||||||
u64 start;
|
s64 line_ip, offset = -1;
|
||||||
s64 line_ip;
|
|
||||||
int ret;
|
|
||||||
char *c;
|
char *c;
|
||||||
|
|
||||||
if (getline(&line, &line_len, file) < 0)
|
if (getline(&line, &line_len, file) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (!line)
|
if (!line)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -168,8 +205,6 @@ static int parse_line(FILE *file, struct hist_entry *he, u64 len)
|
||||||
*c = 0;
|
*c = 0;
|
||||||
|
|
||||||
line_ip = -1;
|
line_ip = -1;
|
||||||
offset = 0;
|
|
||||||
ret = -2;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Strip leading spaces:
|
* Strip leading spaces:
|
||||||
|
@ -190,9 +225,30 @@ static int parse_line(FILE *file, struct hist_entry *he, u64 len)
|
||||||
line_ip = -1;
|
line_ip = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
start = map__rip_2objdump(he->map, sym->start);
|
|
||||||
|
|
||||||
if (line_ip != -1) {
|
if (line_ip != -1) {
|
||||||
|
u64 start = map__rip_2objdump(he->map, sym->start);
|
||||||
|
offset = line_ip - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
objdump_line = objdump_line__new(offset, line);
|
||||||
|
if (objdump_line == NULL) {
|
||||||
|
free(line);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
objdump__add_line(head, objdump_line);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int objdump_line__print(struct objdump_line *self,
|
||||||
|
struct list_head *head,
|
||||||
|
struct hist_entry *he, u64 len)
|
||||||
|
{
|
||||||
|
struct symbol *sym = he->sym;
|
||||||
|
static const char *prev_line;
|
||||||
|
static const char *prev_color;
|
||||||
|
|
||||||
|
if (self->offset != -1) {
|
||||||
const char *path = NULL;
|
const char *path = NULL;
|
||||||
unsigned int hits = 0;
|
unsigned int hits = 0;
|
||||||
double percent = 0.0;
|
double percent = 0.0;
|
||||||
|
@ -200,15 +256,22 @@ static int parse_line(FILE *file, struct hist_entry *he, u64 len)
|
||||||
struct sym_priv *priv = symbol__priv(sym);
|
struct sym_priv *priv = symbol__priv(sym);
|
||||||
struct sym_ext *sym_ext = priv->ext;
|
struct sym_ext *sym_ext = priv->ext;
|
||||||
struct sym_hist *h = priv->hist;
|
struct sym_hist *h = priv->hist;
|
||||||
|
s64 offset = self->offset;
|
||||||
|
struct objdump_line *next = objdump__get_next_ip_line(head, self);
|
||||||
|
|
||||||
offset = line_ip - start;
|
while (offset < (s64)len &&
|
||||||
if (offset < len)
|
(next == NULL || offset < next->offset)) {
|
||||||
hits = h->ip[offset];
|
if (sym_ext) {
|
||||||
|
if (path == NULL)
|
||||||
if (offset < len && sym_ext) {
|
|
||||||
path = sym_ext[offset].path;
|
path = sym_ext[offset].path;
|
||||||
percent = sym_ext[offset].percent;
|
percent += sym_ext[offset].percent;
|
||||||
} else if (h->sum)
|
} else
|
||||||
|
hits += h->ip[offset];
|
||||||
|
|
||||||
|
++offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sym_ext == NULL && h->sum)
|
||||||
percent = 100.0 * hits / h->sum;
|
percent = 100.0 * hits / h->sum;
|
||||||
|
|
||||||
color = get_percent_color(percent);
|
color = get_percent_color(percent);
|
||||||
|
@ -229,12 +292,12 @@ static int parse_line(FILE *file, struct hist_entry *he, u64 len)
|
||||||
|
|
||||||
color_fprintf(stdout, color, " %7.2f", percent);
|
color_fprintf(stdout, color, " %7.2f", percent);
|
||||||
printf(" : ");
|
printf(" : ");
|
||||||
color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line);
|
color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", self->line);
|
||||||
} else {
|
} else {
|
||||||
if (!*line)
|
if (!*self->line)
|
||||||
printf(" :\n");
|
printf(" :\n");
|
||||||
else
|
else
|
||||||
printf(" : %s\n", line);
|
printf(" : %s\n", self->line);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -360,6 +423,20 @@ static void print_summary(const char *filename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hist_entry__print_hits(struct hist_entry *self)
|
||||||
|
{
|
||||||
|
struct symbol *sym = self->sym;
|
||||||
|
struct sym_priv *priv = symbol__priv(sym);
|
||||||
|
struct sym_hist *h = priv->hist;
|
||||||
|
u64 len = sym->end - sym->start, offset;
|
||||||
|
|
||||||
|
for (offset = 0; offset < len; ++offset)
|
||||||
|
if (h->ip[offset] != 0)
|
||||||
|
printf("%*Lx: %Lu\n", BITS_PER_LONG / 2,
|
||||||
|
sym->start + offset, h->ip[offset]);
|
||||||
|
printf("%*s: %Lu\n", BITS_PER_LONG / 2, "h->sum", h->sum);
|
||||||
|
}
|
||||||
|
|
||||||
static void annotate_sym(struct hist_entry *he)
|
static void annotate_sym(struct hist_entry *he)
|
||||||
{
|
{
|
||||||
struct map *map = he->map;
|
struct map *map = he->map;
|
||||||
|
@ -369,6 +446,8 @@ static void annotate_sym(struct hist_entry *he)
|
||||||
u64 len;
|
u64 len;
|
||||||
char command[PATH_MAX*2];
|
char command[PATH_MAX*2];
|
||||||
FILE *file;
|
FILE *file;
|
||||||
|
LIST_HEAD(head);
|
||||||
|
struct objdump_line *pos, *n;
|
||||||
|
|
||||||
if (!filename)
|
if (!filename)
|
||||||
return;
|
return;
|
||||||
|
@ -410,11 +489,21 @@ static void annotate_sym(struct hist_entry *he)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
while (!feof(file)) {
|
while (!feof(file)) {
|
||||||
if (parse_line(file, he, len) < 0)
|
if (parse_line(file, he, &head) < 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
pclose(file);
|
pclose(file);
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
hist_entry__print_hits(he);
|
||||||
|
|
||||||
|
list_for_each_entry_safe(pos, n, &head, node) {
|
||||||
|
objdump_line__print(pos, &head, he, len);
|
||||||
|
list_del(&pos->node);
|
||||||
|
objdump_line__free(pos);
|
||||||
|
}
|
||||||
|
|
||||||
if (print_line)
|
if (print_line)
|
||||||
free_source_line(he, len);
|
free_source_line(he, len);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue