aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util/annotate.c
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2011-02-04 06:45:46 -0500
committerArnaldo Carvalho de Melo <acme@redhat.com>2011-02-05 09:28:21 -0500
commit78f7defedbb4da73b9a07635c357c1afcaa55c8f (patch)
treea4ddcb93682e17e986272b626ce94eb0ed35f8b7 /tools/perf/util/annotate.c
parent764328d3209dd81b02a55722556b07b6f35e3ca0 (diff)
perf annotate: Move annotate functions to util/
They will be used by perf top, so that we have just one set of routines to do annotation. Rename "struct sym_priv" to "struct annotation", etc, to clarify this code a bit. Rename "struct sym_ext" to "struct source_line", to give it a meaningful name, that clarifies that it is a the result of an addr2line call, that is sorted by percentage one particular source code line appeared in the annotation. And since we're moving things around also rename 'sym_hist->ip' to 'sym_hist->addr' as we want to do data structure annotation at some point. Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Cc: Tom Zanussi <tzanussi@gmail.com> LKML-Reference: <new-submission> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/util/annotate.c')
-rw-r--r--tools/perf/util/annotate.c467
1 files changed, 467 insertions, 0 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
new file mode 100644
index 000000000000..9b25575b980c
--- /dev/null
+++ b/tools/perf/util/annotate.c
@@ -0,0 +1,467 @@
1/*
2 * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
3 *
4 * Parts came from builtin-annotate.c, see those files for further
5 * copyright notes.
6 *
7 * Released under the GPL v2. (and only v2, not any later version)
8 */
9
10#include "util.h"
11#include "build-id.h"
12#include "color.h"
13#include "cache.h"
14#include "symbol.h"
15#include "debug.h"
16#include "annotate.h"
17
18static int symbol__alloc_hist(struct symbol *sym)
19{
20 struct annotation *notes = symbol__annotation(sym);
21 const int size = (sizeof(*notes->histogram) +
22 (sym->end - sym->start) * sizeof(u64));
23
24 notes->histogram = zalloc(size);
25 return notes->histogram == NULL ? -1 : 0;
26}
27
28int symbol__inc_addr_samples(struct symbol *sym, struct map *map, u64 addr)
29{
30 unsigned int sym_size, offset;
31 struct annotation *notes;
32 struct sym_hist *h;
33
34 if (!sym || !map)
35 return 0;
36
37 notes = symbol__annotation(sym);
38 if (notes->histogram == NULL && symbol__alloc_hist(sym) < 0)
39 return -ENOMEM;
40
41 sym_size = sym->end - sym->start;
42 offset = addr - sym->start;
43
44 pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr));
45
46 if (offset >= sym_size)
47 return 0;
48
49 h = notes->histogram;
50 h->sum++;
51 h->addr[offset]++;
52
53 pr_debug3("%#" PRIx64 " %s: period++ [addr: %#" PRIx64 ", %#" PRIx64
54 "] => %" PRIu64 "\n", sym->start, sym->name,
55 addr, addr - sym->start, h->addr[offset]);
56 return 0;
57}
58
59static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize)
60{
61 struct objdump_line *self = malloc(sizeof(*self) + privsize);
62
63 if (self != NULL) {
64 self->offset = offset;
65 self->line = line;
66 }
67
68 return self;
69}
70
71void objdump_line__free(struct objdump_line *self)
72{
73 free(self->line);
74 free(self);
75}
76
77static void objdump__add_line(struct list_head *head, struct objdump_line *line)
78{
79 list_add_tail(&line->node, head);
80}
81
82struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
83 struct objdump_line *pos)
84{
85 list_for_each_entry_continue(pos, head, node)
86 if (pos->offset >= 0)
87 return pos;
88
89 return NULL;
90}
91
92static void objdump_line__print(struct objdump_line *oline,
93 struct list_head *head,
94 struct symbol *sym, u64 len)
95{
96 static const char *prev_line;
97 static const char *prev_color;
98
99 if (oline->offset != -1) {
100 const char *path = NULL;
101 unsigned int hits = 0;
102 double percent = 0.0;
103 const char *color;
104 struct annotation *notes = symbol__annotation(sym);
105 struct source_line *src_line = notes->src_line;
106 struct sym_hist *h = notes->histogram;
107 s64 offset = oline->offset;
108 struct objdump_line *next = objdump__get_next_ip_line(head, oline);
109
110 while (offset < (s64)len &&
111 (next == NULL || offset < next->offset)) {
112 if (src_line) {
113 if (path == NULL)
114 path = src_line[offset].path;
115 percent += src_line[offset].percent;
116 } else
117 hits += h->addr[offset];
118
119 ++offset;
120 }
121
122 if (src_line == NULL && h->sum)
123 percent = 100.0 * hits / h->sum;
124
125 color = get_percent_color(percent);
126
127 /*
128 * Also color the filename and line if needed, with
129 * the same color than the percentage. Don't print it
130 * twice for close colored addr with the same filename:line
131 */
132 if (path) {
133 if (!prev_line || strcmp(prev_line, path)
134 || color != prev_color) {
135 color_fprintf(stdout, color, " %s", path);
136 prev_line = path;
137 prev_color = color;
138 }
139 }
140
141 color_fprintf(stdout, color, " %7.2f", percent);
142 printf(" : ");
143 color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", oline->line);
144 } else {
145 if (!*oline->line)
146 printf(" :\n");
147 else
148 printf(" : %s\n", oline->line);
149 }
150}
151
152static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, FILE *file,
153 struct list_head *head, size_t privsize)
154{
155 struct objdump_line *objdump_line;
156 char *line = NULL, *tmp, *tmp2, *c;
157 size_t line_len;
158 s64 line_ip, offset = -1;
159
160 if (getline(&line, &line_len, file) < 0)
161 return -1;
162
163 if (!line)
164 return -1;
165
166 while (line_len != 0 && isspace(line[line_len - 1]))
167 line[--line_len] = '\0';
168
169 c = strchr(line, '\n');
170 if (c)
171 *c = 0;
172
173 line_ip = -1;
174
175 /*
176 * Strip leading spaces:
177 */
178 tmp = line;
179 while (*tmp) {
180 if (*tmp != ' ')
181 break;
182 tmp++;
183 }
184
185 if (*tmp) {
186 /*
187 * Parse hexa addresses followed by ':'
188 */
189 line_ip = strtoull(tmp, &tmp2, 16);
190 if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0')
191 line_ip = -1;
192 }
193
194 if (line_ip != -1) {
195 u64 start = map__rip_2objdump(map, sym->start),
196 end = map__rip_2objdump(map, sym->end);
197
198 offset = line_ip - start;
199 if (offset < 0 || (u64)line_ip > end)
200 offset = -1;
201 }
202
203 objdump_line = objdump_line__new(offset, line, privsize);
204 if (objdump_line == NULL) {
205 free(line);
206 return -1;
207 }
208 objdump__add_line(head, objdump_line);
209
210 return 0;
211}
212
213int symbol__annotate(struct symbol *sym, struct map *map,
214 struct list_head *head, size_t privsize)
215{
216 struct dso *dso = map->dso;
217 char *filename = dso__build_id_filename(dso, NULL, 0);
218 bool free_filename = true;
219 char command[PATH_MAX * 2];
220 FILE *file;
221 int err = 0;
222 u64 len;
223 char symfs_filename[PATH_MAX];
224
225 if (filename) {
226 snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
227 symbol_conf.symfs, filename);
228 }
229
230 if (filename == NULL) {
231 if (dso->has_build_id) {
232 pr_err("Can't annotate %s: not enough memory\n",
233 sym->name);
234 return -ENOMEM;
235 }
236 goto fallback;
237 } else if (readlink(symfs_filename, command, sizeof(command)) < 0 ||
238 strstr(command, "[kernel.kallsyms]") ||
239 access(symfs_filename, R_OK)) {
240 free(filename);
241fallback:
242 /*
243 * If we don't have build-ids or the build-id file isn't in the
244 * cache, or is just a kallsyms file, well, lets hope that this
245 * DSO is the same as when 'perf record' ran.
246 */
247 filename = dso->long_name;
248 snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
249 symbol_conf.symfs, filename);
250 free_filename = false;
251 }
252
253 if (dso->origin == DSO__ORIG_KERNEL) {
254 if (dso->annotate_warned)
255 goto out_free_filename;
256 err = -ENOENT;
257 dso->annotate_warned = 1;
258 pr_err("Can't annotate %s: No vmlinux file was found in the "
259 "path\n", sym->name);
260 goto out_free_filename;
261 }
262
263 pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__,
264 filename, sym->name, map->unmap_ip(map, sym->start),
265 map->unmap_ip(map, sym->end));
266
267 len = sym->end - sym->start;
268
269 pr_debug("annotating [%p] %30s : [%p] %30s\n",
270 dso, dso->long_name, sym, sym->name);
271
272 snprintf(command, sizeof(command),
273 "objdump --start-address=0x%016" PRIx64
274 " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand",
275 map__rip_2objdump(map, sym->start),
276 map__rip_2objdump(map, sym->end),
277 symfs_filename, filename);
278
279 pr_debug("Executing: %s\n", command);
280
281 file = popen(command, "r");
282 if (!file)
283 goto out_free_filename;
284
285 while (!feof(file))
286 if (symbol__parse_objdump_line(sym, map, file, head, privsize) < 0)
287 break;
288
289 pclose(file);
290out_free_filename:
291 if (free_filename)
292 free(filename);
293 return err;
294}
295
296static void insert_source_line(struct rb_root *root, struct source_line *src_line)
297{
298 struct source_line *iter;
299 struct rb_node **p = &root->rb_node;
300 struct rb_node *parent = NULL;
301
302 while (*p != NULL) {
303 parent = *p;
304 iter = rb_entry(parent, struct source_line, node);
305
306 if (src_line->percent > iter->percent)
307 p = &(*p)->rb_left;
308 else
309 p = &(*p)->rb_right;
310 }
311
312 rb_link_node(&src_line->node, parent, p);
313 rb_insert_color(&src_line->node, root);
314}
315
316static void symbol__free_source_line(struct symbol *sym, int len)
317{
318 struct annotation *notes = symbol__annotation(sym);
319 struct source_line *src_line = notes->src_line;
320 int i;
321
322 for (i = 0; i < len; i++)
323 free(src_line[i].path);
324
325 free(src_line);
326 notes->src_line = NULL;
327}
328
329/* Get the filename:line for the colored entries */
330static int symbol__get_source_line(struct symbol *sym, struct map *map,
331 struct rb_root *root, int len,
332 const char *filename)
333{
334 u64 start;
335 int i;
336 char cmd[PATH_MAX * 2];
337 struct source_line *src_line;
338 struct annotation *notes = symbol__annotation(sym);
339 struct sym_hist *h = notes->histogram;
340
341 if (!h->sum)
342 return 0;
343
344 src_line = notes->src_line = calloc(len, sizeof(struct source_line));
345 if (!notes->src_line)
346 return -1;
347
348 start = map->unmap_ip(map, sym->start);
349
350 for (i = 0; i < len; i++) {
351 char *path = NULL;
352 size_t line_len;
353 u64 offset;
354 FILE *fp;
355
356 src_line[i].percent = 100.0 * h->addr[i] / h->sum;
357 if (src_line[i].percent <= 0.5)
358 continue;
359
360 offset = start + i;
361 sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset);
362 fp = popen(cmd, "r");
363 if (!fp)
364 continue;
365
366 if (getline(&path, &line_len, fp) < 0 || !line_len)
367 goto next;
368
369 src_line[i].path = malloc(sizeof(char) * line_len + 1);
370 if (!src_line[i].path)
371 goto next;
372
373 strcpy(src_line[i].path, path);
374 insert_source_line(root, &src_line[i]);
375
376 next:
377 pclose(fp);
378 }
379
380 return 0;
381}
382
383static void print_summary(struct rb_root *root, const char *filename)
384{
385 struct source_line *src_line;
386 struct rb_node *node;
387
388 printf("\nSorted summary for file %s\n", filename);
389 printf("----------------------------------------------\n\n");
390
391 if (RB_EMPTY_ROOT(root)) {
392 printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);
393 return;
394 }
395
396 node = rb_first(root);
397 while (node) {
398 double percent;
399 const char *color;
400 char *path;
401
402 src_line = rb_entry(node, struct source_line, node);
403 percent = src_line->percent;
404 color = get_percent_color(percent);
405 path = src_line->path;
406
407 color_fprintf(stdout, color, " %7.2f %s", percent, path);
408 node = rb_next(node);
409 }
410}
411
412static void symbol__annotate_hits(struct symbol *sym)
413{
414 struct annotation *notes = symbol__annotation(sym);
415 struct sym_hist *h = notes->histogram;
416 u64 len = sym->end - sym->start, offset;
417
418 for (offset = 0; offset < len; ++offset)
419 if (h->addr[offset] != 0)
420 printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2,
421 sym->start + offset, h->addr[offset]);
422 printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum);
423}
424
425int symbol__tty_annotate(struct symbol *sym, struct map *map, bool print_lines,
426 bool full_paths)
427{
428 struct dso *dso = map->dso;
429 const char *filename = dso->long_name, *d_filename;
430 struct rb_root source_line = RB_ROOT;
431 struct objdump_line *pos, *n;
432 LIST_HEAD(head);
433 u64 len;
434
435 if (symbol__annotate(sym, map, &head, 0) < 0)
436 return -1;
437
438 if (full_paths)
439 d_filename = filename;
440 else
441 d_filename = basename(filename);
442
443 len = sym->end - sym->start;
444
445 if (print_lines) {
446 symbol__get_source_line(sym, map, &source_line, len, filename);
447 print_summary(&source_line, filename);
448 }
449
450 printf("\n\n------------------------------------------------\n");
451 printf(" Percent | Source code & Disassembly of %s\n", d_filename);
452 printf("------------------------------------------------\n");
453
454 if (verbose)
455 symbol__annotate_hits(sym);
456
457 list_for_each_entry_safe(pos, n, &head, node) {
458 objdump_line__print(pos, &head, sym, len);
459 list_del(&pos->node);
460 objdump_line__free(pos);
461 }
462
463 if (print_lines)
464 symbol__free_source_line(sym, len);
465
466 return 0;
467}