aboutsummaryrefslogtreecommitdiffstats
path: root/tools
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
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')
-rw-r--r--tools/perf/Makefile2
-rw-r--r--tools/perf/builtin-annotate.c255
-rw-r--r--tools/perf/builtin-report.c3
-rw-r--r--tools/perf/util/annotate.c467
-rw-r--r--tools/perf/util/annotate.h65
-rw-r--r--tools/perf/util/hist.c219
-rw-r--r--tools/perf/util/hist.h27
-rw-r--r--tools/perf/util/ui/browsers/annotate.c43
8 files changed, 578 insertions, 503 deletions
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 4c9499cb4398..be3eb1dc9a5a 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -401,6 +401,7 @@ LIB_H += util/include/dwarf-regs.h
401LIB_H += util/include/asm/dwarf2.h 401LIB_H += util/include/asm/dwarf2.h
402LIB_H += util/include/asm/cpufeature.h 402LIB_H += util/include/asm/cpufeature.h
403LIB_H += perf.h 403LIB_H += perf.h
404LIB_H += util/annotate.h
404LIB_H += util/cache.h 405LIB_H += util/cache.h
405LIB_H += util/callchain.h 406LIB_H += util/callchain.h
406LIB_H += util/build-id.h 407LIB_H += util/build-id.h
@@ -444,6 +445,7 @@ LIB_H += $(ARCH_INCLUDE)
444 445
445LIB_OBJS += $(OUTPUT)util/abspath.o 446LIB_OBJS += $(OUTPUT)util/abspath.o
446LIB_OBJS += $(OUTPUT)util/alias.o 447LIB_OBJS += $(OUTPUT)util/alias.o
448LIB_OBJS += $(OUTPUT)util/annotate.o
447LIB_OBJS += $(OUTPUT)util/build-id.o 449LIB_OBJS += $(OUTPUT)util/build-id.o
448LIB_OBJS += $(OUTPUT)util/config.o 450LIB_OBJS += $(OUTPUT)util/config.o
449LIB_OBJS += $(OUTPUT)util/ctype.o 451LIB_OBJS += $(OUTPUT)util/ctype.o
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index cd9dec46c19f..9072ef44cfcb 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -9,6 +9,7 @@
9 9
10#include "util/util.h" 10#include "util/util.h"
11 11
12#include "util/util.h"
12#include "util/color.h" 13#include "util/color.h"
13#include <linux/list.h> 14#include <linux/list.h>
14#include "util/cache.h" 15#include "util/cache.h"
@@ -18,6 +19,7 @@
18#include "perf.h" 19#include "perf.h"
19#include "util/debug.h" 20#include "util/debug.h"
20 21
22#include "util/annotate.h"
21#include "util/event.h" 23#include "util/event.h"
22#include "util/parse-options.h" 24#include "util/parse-options.h"
23#include "util/parse-events.h" 25#include "util/parse-events.h"
@@ -79,245 +81,10 @@ static int process_sample_event(union perf_event *event,
79 return 0; 81 return 0;
80} 82}
81 83
82static int objdump_line__print(struct objdump_line *self,
83 struct list_head *head,
84 struct hist_entry *he, u64 len)
85{
86 struct symbol *sym = he->ms.sym;
87 static const char *prev_line;
88 static const char *prev_color;
89
90 if (self->offset != -1) {
91 const char *path = NULL;
92 unsigned int hits = 0;
93 double percent = 0.0;
94 const char *color;
95 struct sym_priv *priv = symbol__priv(sym);
96 struct sym_ext *sym_ext = priv->ext;
97 struct sym_hist *h = priv->hist;
98 s64 offset = self->offset;
99 struct objdump_line *next = objdump__get_next_ip_line(head, self);
100
101 while (offset < (s64)len &&
102 (next == NULL || offset < next->offset)) {
103 if (sym_ext) {
104 if (path == NULL)
105 path = sym_ext[offset].path;
106 percent += sym_ext[offset].percent;
107 } else
108 hits += h->ip[offset];
109
110 ++offset;
111 }
112
113 if (sym_ext == NULL && h->sum)
114 percent = 100.0 * hits / h->sum;
115
116 color = get_percent_color(percent);
117
118 /*
119 * Also color the filename and line if needed, with
120 * the same color than the percentage. Don't print it
121 * twice for close colored ip with the same filename:line
122 */
123 if (path) {
124 if (!prev_line || strcmp(prev_line, path)
125 || color != prev_color) {
126 color_fprintf(stdout, color, " %s", path);
127 prev_line = path;
128 prev_color = color;
129 }
130 }
131
132 color_fprintf(stdout, color, " %7.2f", percent);
133 printf(" : ");
134 color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", self->line);
135 } else {
136 if (!*self->line)
137 printf(" :\n");
138 else
139 printf(" : %s\n", self->line);
140 }
141
142 return 0;
143}
144
145static struct rb_root root_sym_ext;
146
147static void insert_source_line(struct sym_ext *sym_ext)
148{
149 struct sym_ext *iter;
150 struct rb_node **p = &root_sym_ext.rb_node;
151 struct rb_node *parent = NULL;
152
153 while (*p != NULL) {
154 parent = *p;
155 iter = rb_entry(parent, struct sym_ext, node);
156
157 if (sym_ext->percent > iter->percent)
158 p = &(*p)->rb_left;
159 else
160 p = &(*p)->rb_right;
161 }
162
163 rb_link_node(&sym_ext->node, parent, p);
164 rb_insert_color(&sym_ext->node, &root_sym_ext);
165}
166
167static void free_source_line(struct hist_entry *he, int len)
168{
169 struct sym_priv *priv = symbol__priv(he->ms.sym);
170 struct sym_ext *sym_ext = priv->ext;
171 int i;
172
173 if (!sym_ext)
174 return;
175
176 for (i = 0; i < len; i++)
177 free(sym_ext[i].path);
178 free(sym_ext);
179
180 priv->ext = NULL;
181 root_sym_ext = RB_ROOT;
182}
183
184/* Get the filename:line for the colored entries */
185static void
186get_source_line(struct hist_entry *he, int len, const char *filename)
187{
188 struct symbol *sym = he->ms.sym;
189 u64 start;
190 int i;
191 char cmd[PATH_MAX * 2];
192 struct sym_ext *sym_ext;
193 struct sym_priv *priv = symbol__priv(sym);
194 struct sym_hist *h = priv->hist;
195
196 if (!h->sum)
197 return;
198
199 sym_ext = priv->ext = calloc(len, sizeof(struct sym_ext));
200 if (!priv->ext)
201 return;
202
203 start = he->ms.map->unmap_ip(he->ms.map, sym->start);
204
205 for (i = 0; i < len; i++) {
206 char *path = NULL;
207 size_t line_len;
208 u64 offset;
209 FILE *fp;
210
211 sym_ext[i].percent = 100.0 * h->ip[i] / h->sum;
212 if (sym_ext[i].percent <= 0.5)
213 continue;
214
215 offset = start + i;
216 sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset);
217 fp = popen(cmd, "r");
218 if (!fp)
219 continue;
220
221 if (getline(&path, &line_len, fp) < 0 || !line_len)
222 goto next;
223
224 sym_ext[i].path = malloc(sizeof(char) * line_len + 1);
225 if (!sym_ext[i].path)
226 goto next;
227
228 strcpy(sym_ext[i].path, path);
229 insert_source_line(&sym_ext[i]);
230
231 next:
232 pclose(fp);
233 }
234}
235
236static void print_summary(const char *filename)
237{
238 struct sym_ext *sym_ext;
239 struct rb_node *node;
240
241 printf("\nSorted summary for file %s\n", filename);
242 printf("----------------------------------------------\n\n");
243
244 if (RB_EMPTY_ROOT(&root_sym_ext)) {
245 printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);
246 return;
247 }
248
249 node = rb_first(&root_sym_ext);
250 while (node) {
251 double percent;
252 const char *color;
253 char *path;
254
255 sym_ext = rb_entry(node, struct sym_ext, node);
256 percent = sym_ext->percent;
257 color = get_percent_color(percent);
258 path = sym_ext->path;
259
260 color_fprintf(stdout, color, " %7.2f %s", percent, path);
261 node = rb_next(node);
262 }
263}
264
265static void hist_entry__print_hits(struct hist_entry *self)
266{
267 struct symbol *sym = self->ms.sym;
268 struct sym_priv *priv = symbol__priv(sym);
269 struct sym_hist *h = priv->hist;
270 u64 len = sym->end - sym->start, offset;
271
272 for (offset = 0; offset < len; ++offset)
273 if (h->ip[offset] != 0)
274 printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2,
275 sym->start + offset, h->ip[offset]);
276 printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum);
277}
278
279static int hist_entry__tty_annotate(struct hist_entry *he) 84static int hist_entry__tty_annotate(struct hist_entry *he)
280{ 85{
281 struct map *map = he->ms.map; 86 return symbol__tty_annotate(he->ms.sym, he->ms.map,
282 struct dso *dso = map->dso; 87 print_line, full_paths);
283 struct symbol *sym = he->ms.sym;
284 const char *filename = dso->long_name, *d_filename;
285 u64 len;
286 LIST_HEAD(head);
287 struct objdump_line *pos, *n;
288
289 if (hist_entry__annotate(he, &head, 0) < 0)
290 return -1;
291
292 if (full_paths)
293 d_filename = filename;
294 else
295 d_filename = basename(filename);
296
297 len = sym->end - sym->start;
298
299 if (print_line) {
300 get_source_line(he, len, filename);
301 print_summary(filename);
302 }
303
304 printf("\n\n------------------------------------------------\n");
305 printf(" Percent | Source code & Disassembly of %s\n", d_filename);
306 printf("------------------------------------------------\n");
307
308 if (verbose)
309 hist_entry__print_hits(he);
310
311 list_for_each_entry_safe(pos, n, &head, node) {
312 objdump_line__print(pos, &head, he, len);
313 list_del(&pos->node);
314 objdump_line__free(pos);
315 }
316
317 if (print_line)
318 free_source_line(he, len);
319
320 return 0;
321} 88}
322 89
323static void hists__find_annotations(struct hists *self) 90static void hists__find_annotations(struct hists *self)
@@ -327,13 +94,13 @@ static void hists__find_annotations(struct hists *self)
327 94
328 while (nd) { 95 while (nd) {
329 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); 96 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
330 struct sym_priv *priv; 97 struct annotation *notes;
331 98
332 if (he->ms.sym == NULL || he->ms.map->dso->annotate_warned) 99 if (he->ms.sym == NULL || he->ms.map->dso->annotate_warned)
333 goto find_next; 100 goto find_next;
334 101
335 priv = symbol__priv(he->ms.sym); 102 notes = symbol__annotation(he->ms.sym);
336 if (priv->hist == NULL) { 103 if (notes->histogram == NULL) {
337find_next: 104find_next:
338 if (key == KEY_LEFT) 105 if (key == KEY_LEFT)
339 nd = rb_prev(nd); 106 nd = rb_prev(nd);
@@ -362,11 +129,11 @@ find_next:
362 nd = rb_next(nd); 129 nd = rb_next(nd);
363 /* 130 /*
364 * Since we have a hist_entry per IP for the same 131 * Since we have a hist_entry per IP for the same
365 * symbol, free he->ms.sym->hist to signal we already 132 * symbol, free he->ms.sym->histogram to signal we already
366 * processed this symbol. 133 * processed this symbol.
367 */ 134 */
368 free(priv->hist); 135 free(notes->histogram);
369 priv->hist = NULL; 136 notes->histogram = NULL;
370 } 137 }
371 } 138 }
372} 139}
@@ -454,7 +221,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used)
454 221
455 setup_browser(true); 222 setup_browser(true);
456 223
457 symbol_conf.priv_size = sizeof(struct sym_priv); 224 symbol_conf.priv_size = sizeof(struct annotation);
458 symbol_conf.try_vmlinux_path = true; 225 symbol_conf.try_vmlinux_path = true;
459 226
460 if (symbol__init() < 0) 227 if (symbol__init() < 0)
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 080937c3a656..91e4cdba933b 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -9,6 +9,7 @@
9 9
10#include "util/util.h" 10#include "util/util.h"
11 11
12#include "util/annotate.h"
12#include "util/color.h" 13#include "util/color.h"
13#include <linux/list.h> 14#include <linux/list.h>
14#include "util/cache.h" 15#include "util/cache.h"
@@ -508,7 +509,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
508 * implementation. 509 * implementation.
509 */ 510 */
510 if (use_browser > 0) { 511 if (use_browser > 0) {
511 symbol_conf.priv_size = sizeof(struct sym_priv); 512 symbol_conf.priv_size = sizeof(struct annotation);
512 /* 513 /*
513 * For searching by name on the "Browse map details". 514 * For searching by name on the "Browse map details".
514 * providing it only in verbose mode not to bloat too 515 * providing it only in verbose mode not to bloat too
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}
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
new file mode 100644
index 000000000000..6e2fbc205299
--- /dev/null
+++ b/tools/perf/util/annotate.h
@@ -0,0 +1,65 @@
1#ifndef __PERF_ANNOTATE_H
2#define __PERF_ANNOTATE_H
3
4#include <stdbool.h>
5#include "types.h"
6#include "symbol.h"
7#include <linux/list.h>
8#include <linux/rbtree.h>
9
10struct objdump_line {
11 struct list_head node;
12 s64 offset;
13 char *line;
14};
15
16void objdump_line__free(struct objdump_line *self);
17struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
18 struct objdump_line *pos);
19
20struct sym_hist {
21 u64 sum;
22 u64 addr[0];
23};
24
25struct source_line {
26 struct rb_node node;
27 double percent;
28 char *path;
29};
30
31struct annotation {
32 struct sym_hist *histogram;
33 struct source_line *src_line;
34};
35
36struct sannotation {
37 struct annotation annotation;
38 struct symbol symbol;
39};
40
41static inline struct annotation *symbol__annotation(struct symbol *sym)
42{
43 struct sannotation *a = container_of(sym, struct sannotation, symbol);
44 return &a->annotation;
45}
46
47int symbol__inc_addr_samples(struct symbol *sym, struct map *map, u64 addr);
48
49int symbol__annotate(struct symbol *sym, struct map *map,
50 struct list_head *head, size_t privsize);
51
52int symbol__tty_annotate(struct symbol *sym, struct map *map,
53 bool print_lines, bool full_paths);
54
55#ifdef NO_NEWT_SUPPORT
56static inline int symbol__tui_annotate(symbol *sym __used,
57 struct map *map __used)
58{
59 return 0;
60}
61#else
62int symbol__tui_annotate(struct symbol *sym, struct map *map);
63#endif
64
65#endif /* __PERF_ANNOTATE_H */
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 95887804dc8e..6d9c92c3d7cb 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -1,3 +1,4 @@
1#include "annotate.h"
1#include "util.h" 2#include "util.h"
2#include "build-id.h" 3#include "build-id.h"
3#include "hist.h" 4#include "hist.h"
@@ -949,225 +950,15 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread)
949 } 950 }
950} 951}
951 952
952static int symbol__alloc_hist(struct symbol *self) 953int hist_entry__inc_addr_samples(struct hist_entry *he, u64 ip)
953{ 954{
954 struct sym_priv *priv = symbol__priv(self); 955 return symbol__inc_addr_samples(he->ms.sym, he->ms.map, ip);
955 const int size = (sizeof(*priv->hist) +
956 (self->end - self->start) * sizeof(u64));
957
958 priv->hist = zalloc(size);
959 return priv->hist == NULL ? -1 : 0;
960}
961
962int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip)
963{
964 unsigned int sym_size, offset;
965 struct symbol *sym = self->ms.sym;
966 struct sym_priv *priv;
967 struct sym_hist *h;
968
969 if (!sym || !self->ms.map)
970 return 0;
971
972 priv = symbol__priv(sym);
973 if (priv->hist == NULL && symbol__alloc_hist(sym) < 0)
974 return -ENOMEM;
975
976 sym_size = sym->end - sym->start;
977 offset = ip - sym->start;
978
979 pr_debug3("%s: ip=%#" PRIx64 "\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip));
980
981 if (offset >= sym_size)
982 return 0;
983
984 h = priv->hist;
985 h->sum++;
986 h->ip[offset]++;
987
988 pr_debug3("%#" PRIx64 " %s: period++ [ip: %#" PRIx64 ", %#" PRIx64
989 "] => %" PRIu64 "\n", self->ms.sym->start, self->ms.sym->name,
990 ip, ip - self->ms.sym->start, h->ip[offset]);
991 return 0;
992}
993
994static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize)
995{
996 struct objdump_line *self = malloc(sizeof(*self) + privsize);
997
998 if (self != NULL) {
999 self->offset = offset;
1000 self->line = line;
1001 }
1002
1003 return self;
1004}
1005
1006void objdump_line__free(struct objdump_line *self)
1007{
1008 free(self->line);
1009 free(self);
1010}
1011
1012static void objdump__add_line(struct list_head *head, struct objdump_line *line)
1013{
1014 list_add_tail(&line->node, head);
1015}
1016
1017struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
1018 struct objdump_line *pos)
1019{
1020 list_for_each_entry_continue(pos, head, node)
1021 if (pos->offset >= 0)
1022 return pos;
1023
1024 return NULL;
1025} 956}
1026 957
1027static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, 958int hist_entry__annotate(struct hist_entry *he, struct list_head *head,
1028 struct list_head *head, size_t privsize)
1029{
1030 struct symbol *sym = self->ms.sym;
1031 struct objdump_line *objdump_line;
1032 char *line = NULL, *tmp, *tmp2, *c;
1033 size_t line_len;
1034 s64 line_ip, offset = -1;
1035
1036 if (getline(&line, &line_len, file) < 0)
1037 return -1;
1038
1039 if (!line)
1040 return -1;
1041
1042 while (line_len != 0 && isspace(line[line_len - 1]))
1043 line[--line_len] = '\0';
1044
1045 c = strchr(line, '\n');
1046 if (c)
1047 *c = 0;
1048
1049 line_ip = -1;
1050
1051 /*
1052 * Strip leading spaces:
1053 */
1054 tmp = line;
1055 while (*tmp) {
1056 if (*tmp != ' ')
1057 break;
1058 tmp++;
1059 }
1060
1061 if (*tmp) {
1062 /*
1063 * Parse hexa addresses followed by ':'
1064 */
1065 line_ip = strtoull(tmp, &tmp2, 16);
1066 if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0')
1067 line_ip = -1;
1068 }
1069
1070 if (line_ip != -1) {
1071 u64 start = map__rip_2objdump(self->ms.map, sym->start),
1072 end = map__rip_2objdump(self->ms.map, sym->end);
1073
1074 offset = line_ip - start;
1075 if (offset < 0 || (u64)line_ip > end)
1076 offset = -1;
1077 }
1078
1079 objdump_line = objdump_line__new(offset, line, privsize);
1080 if (objdump_line == NULL) {
1081 free(line);
1082 return -1;
1083 }
1084 objdump__add_line(head, objdump_line);
1085
1086 return 0;
1087}
1088
1089int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
1090 size_t privsize) 959 size_t privsize)
1091{ 960{
1092 struct symbol *sym = self->ms.sym; 961 return symbol__annotate(he->ms.sym, he->ms.map, head, privsize);
1093 struct map *map = self->ms.map;
1094 struct dso *dso = map->dso;
1095 char *filename = dso__build_id_filename(dso, NULL, 0);
1096 bool free_filename = true;
1097 char command[PATH_MAX * 2];
1098 FILE *file;
1099 int err = 0;
1100 u64 len;
1101 char symfs_filename[PATH_MAX];
1102
1103 if (filename) {
1104 snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
1105 symbol_conf.symfs, filename);
1106 }
1107
1108 if (filename == NULL) {
1109 if (dso->has_build_id) {
1110 pr_err("Can't annotate %s: not enough memory\n",
1111 sym->name);
1112 return -ENOMEM;
1113 }
1114 goto fallback;
1115 } else if (readlink(symfs_filename, command, sizeof(command)) < 0 ||
1116 strstr(command, "[kernel.kallsyms]") ||
1117 access(symfs_filename, R_OK)) {
1118 free(filename);
1119fallback:
1120 /*
1121 * If we don't have build-ids or the build-id file isn't in the
1122 * cache, or is just a kallsyms file, well, lets hope that this
1123 * DSO is the same as when 'perf record' ran.
1124 */
1125 filename = dso->long_name;
1126 snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
1127 symbol_conf.symfs, filename);
1128 free_filename = false;
1129 }
1130
1131 if (dso->origin == DSO__ORIG_KERNEL) {
1132 if (dso->annotate_warned)
1133 goto out_free_filename;
1134 err = -ENOENT;
1135 dso->annotate_warned = 1;
1136 pr_err("Can't annotate %s: No vmlinux file was found in the "
1137 "path\n", sym->name);
1138 goto out_free_filename;
1139 }
1140
1141 pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__,
1142 filename, sym->name, map->unmap_ip(map, sym->start),
1143 map->unmap_ip(map, sym->end));
1144
1145 len = sym->end - sym->start;
1146
1147 pr_debug("annotating [%p] %30s : [%p] %30s\n",
1148 dso, dso->long_name, sym, sym->name);
1149
1150 snprintf(command, sizeof(command),
1151 "objdump --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand",
1152 map__rip_2objdump(map, sym->start),
1153 map__rip_2objdump(map, sym->end),
1154 symfs_filename, filename);
1155
1156 pr_debug("Executing: %s\n", command);
1157
1158 file = popen(command, "r");
1159 if (!file)
1160 goto out_free_filename;
1161
1162 while (!feof(file))
1163 if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0)
1164 break;
1165
1166 pclose(file);
1167out_free_filename:
1168 if (free_filename)
1169 free(filename);
1170 return err;
1171} 962}
1172 963
1173void hists__inc_nr_events(struct hists *self, u32 type) 964void hists__inc_nr_events(struct hists *self, u32 type)
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 889559b86492..8a201f755534 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -9,33 +9,6 @@ extern struct callchain_param callchain_param;
9struct hist_entry; 9struct hist_entry;
10struct addr_location; 10struct addr_location;
11struct symbol; 11struct symbol;
12struct rb_root;
13
14struct objdump_line {
15 struct list_head node;
16 s64 offset;
17 char *line;
18};
19
20void objdump_line__free(struct objdump_line *self);
21struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
22 struct objdump_line *pos);
23
24struct sym_hist {
25 u64 sum;
26 u64 ip[0];
27};
28
29struct sym_ext {
30 struct rb_node node;
31 double percent;
32 char *path;
33};
34
35struct sym_priv {
36 struct sym_hist *hist;
37 struct sym_ext *ext;
38};
39 12
40/* 13/*
41 * The kernel collects the number of events it couldn't send in a stretch and 14 * The kernel collects the number of events it couldn't send in a stretch and
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c
index 82b78f99251b..daa7138d8015 100644
--- a/tools/perf/util/ui/browsers/annotate.c
+++ b/tools/perf/util/ui/browsers/annotate.c
@@ -1,9 +1,11 @@
1#include "../browser.h" 1#include "../browser.h"
2#include "../helpline.h" 2#include "../helpline.h"
3#include "../libslang.h" 3#include "../libslang.h"
4#include "../../annotate.h"
4#include "../../hist.h" 5#include "../../hist.h"
5#include "../../sort.h" 6#include "../../sort.h"
6#include "../../symbol.h" 7#include "../../symbol.h"
8#include "../../annotate.h"
7 9
8static void ui__error_window(const char *fmt, ...) 10static void ui__error_window(const char *fmt, ...)
9{ 11{
@@ -66,24 +68,26 @@ static double objdump_line__calc_percent(struct objdump_line *self,
66 if (self->offset != -1) { 68 if (self->offset != -1) {
67 int len = sym->end - sym->start; 69 int len = sym->end - sym->start;
68 unsigned int hits = 0; 70 unsigned int hits = 0;
69 struct sym_priv *priv = symbol__priv(sym); 71 struct annotation *notes = symbol__annotation(sym);
70 struct sym_ext *sym_ext = priv->ext; 72 struct source_line *src_line = notes->src_line;
71 struct sym_hist *h = priv->hist; 73 struct sym_hist *h = notes->histogram;
72 s64 offset = self->offset; 74 s64 offset = self->offset;
73 struct objdump_line *next = objdump__get_next_ip_line(head, self); 75 struct objdump_line *next = objdump__get_next_ip_line(head, self);
74 76
75
76 while (offset < (s64)len && 77 while (offset < (s64)len &&
77 (next == NULL || offset < next->offset)) { 78 (next == NULL || offset < next->offset)) {
78 if (sym_ext) { 79 if (src_line) {
79 percent += sym_ext[offset].percent; 80 percent += src_line[offset].percent;
80 } else 81 } else
81 hits += h->ip[offset]; 82 hits += h->addr[offset];
82 83
83 ++offset; 84 ++offset;
84 } 85 }
85 86 /*
86 if (sym_ext == NULL && h->sum) 87 * If the percentage wasn't already calculated in
88 * symbol__get_source_line, do it now:
89 */
90 if (src_line == NULL && h->sum)
87 percent = 100.0 * hits / h->sum; 91 percent = 100.0 * hits / h->sum;
88 } 92 }
89 93
@@ -136,10 +140,10 @@ static void annotate_browser__set_top(struct annotate_browser *self,
136static int annotate_browser__run(struct annotate_browser *self) 140static int annotate_browser__run(struct annotate_browser *self)
137{ 141{
138 struct rb_node *nd; 142 struct rb_node *nd;
139 struct hist_entry *he = self->b.priv; 143 struct symbol *sym = self->b.priv;
140 int key; 144 int key;
141 145
142 if (ui_browser__show(&self->b, he->ms.sym->name, 146 if (ui_browser__show(&self->b, sym->name,
143 "<-, -> or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0) 147 "<-, -> or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0)
144 return -1; 148 return -1;
145 /* 149 /*
@@ -179,7 +183,12 @@ out:
179 return key; 183 return key;
180} 184}
181 185
182int hist_entry__tui_annotate(struct hist_entry *self) 186int hist_entry__tui_annotate(struct hist_entry *he)
187{
188 return symbol__tui_annotate(he->ms.sym, he->ms.map);
189}
190
191int symbol__tui_annotate(struct symbol *sym, struct map *map)
183{ 192{
184 struct objdump_line *pos, *n; 193 struct objdump_line *pos, *n;
185 struct objdump_line_rb_node *rbpos; 194 struct objdump_line_rb_node *rbpos;
@@ -190,18 +199,18 @@ int hist_entry__tui_annotate(struct hist_entry *self)
190 .refresh = ui_browser__list_head_refresh, 199 .refresh = ui_browser__list_head_refresh,
191 .seek = ui_browser__list_head_seek, 200 .seek = ui_browser__list_head_seek,
192 .write = annotate_browser__write, 201 .write = annotate_browser__write,
193 .priv = self, 202 .priv = sym,
194 }, 203 },
195 }; 204 };
196 int ret; 205 int ret;
197 206
198 if (self->ms.sym == NULL) 207 if (sym == NULL)
199 return -1; 208 return -1;
200 209
201 if (self->ms.map->dso->annotate_warned) 210 if (map->dso->annotate_warned)
202 return -1; 211 return -1;
203 212
204 if (hist_entry__annotate(self, &head, sizeof(*rbpos)) < 0) { 213 if (symbol__annotate(sym, map, &head, sizeof(*rbpos)) < 0) {
205 ui__error_window(ui_helpline__last_msg); 214 ui__error_window(ui_helpline__last_msg);
206 return -1; 215 return -1;
207 } 216 }
@@ -214,7 +223,7 @@ int hist_entry__tui_annotate(struct hist_entry *self)
214 browser.b.width = line_len; 223 browser.b.width = line_len;
215 rbpos = objdump_line__rb(pos); 224 rbpos = objdump_line__rb(pos);
216 rbpos->idx = browser.b.nr_entries++; 225 rbpos->idx = browser.b.nr_entries++;
217 rbpos->percent = objdump_line__calc_percent(pos, &head, self->ms.sym); 226 rbpos->percent = objdump_line__calc_percent(pos, &head, sym);
218 if (rbpos->percent < 0.01) 227 if (rbpos->percent < 0.01)
219 continue; 228 continue;
220 objdump__insert_line(&browser.entries, rbpos); 229 objdump__insert_line(&browser.entries, rbpos);