diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2010-02-26 09:23:14 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-02-26 09:42:49 -0500 |
commit | 48fb4fdd6b667ebeccbc6cde0a8a5a148d5c6b68 (patch) | |
tree | d724279780a2a9217f2b2b4c4ba2329bf2d1e30e /tools/perf/builtin-annotate.c | |
parent | 6667661df4bc76083edf1e08831c20f64429709d (diff) |
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>
Diffstat (limited to 'tools/perf/builtin-annotate.c')
-rw-r--r-- | tools/perf/builtin-annotate.c | 135 |
1 files changed, 112 insertions, 23 deletions
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index e47dd1587e39..5ec5de995872 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -145,21 +145,58 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
145 | return 0; | 145 | return 0; |
146 | } | 146 | } |
147 | 147 | ||
148 | static int parse_line(FILE *file, struct hist_entry *he, u64 len) | 148 | struct objdump_line { |
149 | struct list_head node; | ||
150 | s64 offset; | ||
151 | char *line; | ||
152 | }; | ||
153 | |||
154 | static struct objdump_line *objdump_line__new(s64 offset, char *line) | ||
155 | { | ||
156 | struct objdump_line *self = malloc(sizeof(*self)); | ||
157 | |||
158 | if (self != NULL) { | ||
159 | self->offset = offset; | ||
160 | self->line = line; | ||
161 | } | ||
162 | |||
163 | return self; | ||
164 | } | ||
165 | |||
166 | static void objdump_line__free(struct objdump_line *self) | ||
167 | { | ||
168 | free(self->line); | ||
169 | free(self); | ||
170 | } | ||
171 | |||
172 | static void objdump__add_line(struct list_head *head, struct objdump_line *line) | ||
173 | { | ||
174 | list_add_tail(&line->node, head); | ||
175 | } | ||
176 | |||
177 | static struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||
178 | struct objdump_line *pos) | ||
179 | { | ||
180 | list_for_each_entry_continue(pos, head, node) | ||
181 | if (pos->offset >= 0) | ||
182 | return pos; | ||
183 | |||
184 | return NULL; | ||
185 | } | ||
186 | |||
187 | static int parse_line(FILE *file, struct hist_entry *he, | ||
188 | struct list_head *head) | ||
149 | { | 189 | { |
150 | struct symbol *sym = he->sym; | 190 | struct symbol *sym = he->sym; |
191 | struct objdump_line *objdump_line; | ||
151 | char *line = NULL, *tmp, *tmp2; | 192 | char *line = NULL, *tmp, *tmp2; |
152 | static const char *prev_line; | ||
153 | static const char *prev_color; | ||
154 | unsigned int offset; | ||
155 | size_t line_len; | 193 | size_t line_len; |
156 | u64 start; | 194 | s64 line_ip, offset = -1; |
157 | s64 line_ip; | ||
158 | int ret; | ||
159 | char *c; | 195 | char *c; |
160 | 196 | ||
161 | if (getline(&line, &line_len, file) < 0) | 197 | if (getline(&line, &line_len, file) < 0) |
162 | return -1; | 198 | return -1; |
199 | |||
163 | if (!line) | 200 | if (!line) |
164 | return -1; | 201 | return -1; |
165 | 202 | ||
@@ -168,8 +205,6 @@ static int parse_line(FILE *file, struct hist_entry *he, u64 len) | |||
168 | *c = 0; | 205 | *c = 0; |
169 | 206 | ||
170 | line_ip = -1; | 207 | line_ip = -1; |
171 | offset = 0; | ||
172 | ret = -2; | ||
173 | 208 | ||
174 | /* | 209 | /* |
175 | * Strip leading spaces: | 210 | * Strip leading spaces: |
@@ -190,9 +225,30 @@ static int parse_line(FILE *file, struct hist_entry *he, u64 len) | |||
190 | line_ip = -1; | 225 | line_ip = -1; |
191 | } | 226 | } |
192 | 227 | ||
193 | start = map__rip_2objdump(he->map, sym->start); | ||
194 | |||
195 | if (line_ip != -1) { | 228 | if (line_ip != -1) { |
229 | u64 start = map__rip_2objdump(he->map, sym->start); | ||
230 | offset = line_ip - start; | ||
231 | } | ||
232 | |||
233 | objdump_line = objdump_line__new(offset, line); | ||
234 | if (objdump_line == NULL) { | ||
235 | free(line); | ||
236 | return -1; | ||
237 | } | ||
238 | objdump__add_line(head, objdump_line); | ||
239 | |||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | static int objdump_line__print(struct objdump_line *self, | ||
244 | struct list_head *head, | ||
245 | struct hist_entry *he, u64 len) | ||
246 | { | ||
247 | struct symbol *sym = he->sym; | ||
248 | static const char *prev_line; | ||
249 | static const char *prev_color; | ||
250 | |||
251 | if (self->offset != -1) { | ||
196 | const char *path = NULL; | 252 | const char *path = NULL; |
197 | unsigned int hits = 0; | 253 | unsigned int hits = 0; |
198 | double percent = 0.0; | 254 | double percent = 0.0; |
@@ -200,15 +256,22 @@ static int parse_line(FILE *file, struct hist_entry *he, u64 len) | |||
200 | struct sym_priv *priv = symbol__priv(sym); | 256 | struct sym_priv *priv = symbol__priv(sym); |
201 | struct sym_ext *sym_ext = priv->ext; | 257 | struct sym_ext *sym_ext = priv->ext; |
202 | struct sym_hist *h = priv->hist; | 258 | struct sym_hist *h = priv->hist; |
259 | s64 offset = self->offset; | ||
260 | struct objdump_line *next = objdump__get_next_ip_line(head, self); | ||
261 | |||
262 | while (offset < (s64)len && | ||
263 | (next == NULL || offset < next->offset)) { | ||
264 | if (sym_ext) { | ||
265 | if (path == NULL) | ||
266 | path = sym_ext[offset].path; | ||
267 | percent += sym_ext[offset].percent; | ||
268 | } else | ||
269 | hits += h->ip[offset]; | ||
270 | |||
271 | ++offset; | ||
272 | } | ||
203 | 273 | ||
204 | offset = line_ip - start; | 274 | if (sym_ext == NULL && h->sum) |
205 | if (offset < len) | ||
206 | hits = h->ip[offset]; | ||
207 | |||
208 | if (offset < len && sym_ext) { | ||
209 | path = sym_ext[offset].path; | ||
210 | percent = sym_ext[offset].percent; | ||
211 | } else if (h->sum) | ||
212 | percent = 100.0 * hits / h->sum; | 275 | percent = 100.0 * hits / h->sum; |
213 | 276 | ||
214 | color = get_percent_color(percent); | 277 | color = get_percent_color(percent); |
@@ -229,12 +292,12 @@ static int parse_line(FILE *file, struct hist_entry *he, u64 len) | |||
229 | 292 | ||
230 | color_fprintf(stdout, color, " %7.2f", percent); | 293 | color_fprintf(stdout, color, " %7.2f", percent); |
231 | printf(" : "); | 294 | printf(" : "); |
232 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line); | 295 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", self->line); |
233 | } else { | 296 | } else { |
234 | if (!*line) | 297 | if (!*self->line) |
235 | printf(" :\n"); | 298 | printf(" :\n"); |
236 | else | 299 | else |
237 | printf(" : %s\n", line); | 300 | printf(" : %s\n", self->line); |
238 | } | 301 | } |
239 | 302 | ||
240 | return 0; | 303 | return 0; |
@@ -360,6 +423,20 @@ static void print_summary(const char *filename) | |||
360 | } | 423 | } |
361 | } | 424 | } |
362 | 425 | ||
426 | static void hist_entry__print_hits(struct hist_entry *self) | ||
427 | { | ||
428 | struct symbol *sym = self->sym; | ||
429 | struct sym_priv *priv = symbol__priv(sym); | ||
430 | struct sym_hist *h = priv->hist; | ||
431 | u64 len = sym->end - sym->start, offset; | ||
432 | |||
433 | for (offset = 0; offset < len; ++offset) | ||
434 | if (h->ip[offset] != 0) | ||
435 | printf("%*Lx: %Lu\n", BITS_PER_LONG / 2, | ||
436 | sym->start + offset, h->ip[offset]); | ||
437 | printf("%*s: %Lu\n", BITS_PER_LONG / 2, "h->sum", h->sum); | ||
438 | } | ||
439 | |||
363 | static void annotate_sym(struct hist_entry *he) | 440 | static void annotate_sym(struct hist_entry *he) |
364 | { | 441 | { |
365 | struct map *map = he->map; | 442 | struct map *map = he->map; |
@@ -369,6 +446,8 @@ static void annotate_sym(struct hist_entry *he) | |||
369 | u64 len; | 446 | u64 len; |
370 | char command[PATH_MAX*2]; | 447 | char command[PATH_MAX*2]; |
371 | FILE *file; | 448 | FILE *file; |
449 | LIST_HEAD(head); | ||
450 | struct objdump_line *pos, *n; | ||
372 | 451 | ||
373 | if (!filename) | 452 | if (!filename) |
374 | return; | 453 | return; |
@@ -410,11 +489,21 @@ static void annotate_sym(struct hist_entry *he) | |||
410 | return; | 489 | return; |
411 | 490 | ||
412 | while (!feof(file)) { | 491 | while (!feof(file)) { |
413 | if (parse_line(file, he, len) < 0) | 492 | if (parse_line(file, he, &head) < 0) |
414 | break; | 493 | break; |
415 | } | 494 | } |
416 | 495 | ||
417 | pclose(file); | 496 | pclose(file); |
497 | |||
498 | if (verbose) | ||
499 | hist_entry__print_hits(he); | ||
500 | |||
501 | list_for_each_entry_safe(pos, n, &head, node) { | ||
502 | objdump_line__print(pos, &head, he, len); | ||
503 | list_del(&pos->node); | ||
504 | objdump_line__free(pos); | ||
505 | } | ||
506 | |||
418 | if (print_line) | 507 | if (print_line) |
419 | free_source_line(he, len); | 508 | free_source_line(he, len); |
420 | } | 509 | } |