xref: /openbmc/linux/tools/perf/builtin-annotate.c (revision ef7b93a1)
1 /*
2  * builtin-annotate.c
3  *
4  * Builtin annotate command: Analyze the perf.data input file,
5  * look up and read DSOs and symbol information and display
6  * a histogram of results, along various sorting keys.
7  */
8 #include "builtin.h"
9 
10 #include "util/util.h"
11 
12 #include "util/color.h"
13 #include <linux/list.h>
14 #include "util/cache.h"
15 #include <linux/rbtree.h>
16 #include "util/symbol.h"
17 
18 #include "perf.h"
19 #include "util/debug.h"
20 
21 #include "util/event.h"
22 #include "util/parse-options.h"
23 #include "util/parse-events.h"
24 #include "util/thread.h"
25 #include "util/sort.h"
26 #include "util/hist.h"
27 #include "util/session.h"
28 
29 static char		const *input_name = "perf.data";
30 
31 static bool		force;
32 
33 static bool		full_paths;
34 
35 static bool		print_line;
36 
37 static const char *sym_hist_filter;
38 
39 static int hists__add_entry(struct hists *self, struct addr_location *al)
40 {
41 	struct hist_entry *he;
42 
43 	if (sym_hist_filter != NULL &&
44 	    (al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) {
45 		/* We're only interested in a symbol named sym_hist_filter */
46 		if (al->sym != NULL) {
47 			rb_erase(&al->sym->rb_node,
48 				 &al->map->dso->symbols[al->map->type]);
49 			symbol__delete(al->sym);
50 		}
51 		return 0;
52 	}
53 
54 	he = __hists__add_entry(self, al, NULL, 1);
55 	if (he == NULL)
56 		return -ENOMEM;
57 
58 	return hist_entry__inc_addr_samples(he, al->addr);
59 }
60 
61 static int process_sample_event(event_t *event, struct perf_session *session)
62 {
63 	struct addr_location al;
64 
65 	dump_printf("(IP, %d): %d: %#Lx\n", event->header.misc,
66 		    event->ip.pid, event->ip.ip);
67 
68 	if (event__preprocess_sample(event, session, &al, NULL) < 0) {
69 		pr_warning("problem processing %d event, skipping it.\n",
70 			   event->header.type);
71 		return -1;
72 	}
73 
74 	if (!al.filtered && hists__add_entry(&session->hists, &al)) {
75 		pr_warning("problem incrementing symbol count, "
76 			   "skipping event\n");
77 		return -1;
78 	}
79 
80 	return 0;
81 }
82 
83 static int objdump_line__print(struct objdump_line *self,
84 			       struct list_head *head,
85 			       struct hist_entry *he, u64 len)
86 {
87 	struct symbol *sym = he->ms.sym;
88 	static const char *prev_line;
89 	static const char *prev_color;
90 
91 	if (self->offset != -1) {
92 		const char *path = NULL;
93 		unsigned int hits = 0;
94 		double percent = 0.0;
95 		const char *color;
96 		struct sym_priv *priv = symbol__priv(sym);
97 		struct sym_ext *sym_ext = priv->ext;
98 		struct sym_hist *h = priv->hist;
99 		s64 offset = self->offset;
100 		struct objdump_line *next = objdump__get_next_ip_line(head, self);
101 
102 		while (offset < (s64)len &&
103 		       (next == NULL || offset < next->offset)) {
104 			if (sym_ext) {
105 				if (path == NULL)
106 					path = sym_ext[offset].path;
107 				percent += sym_ext[offset].percent;
108 			} else
109 				hits += h->ip[offset];
110 
111 			++offset;
112 		}
113 
114 		if (sym_ext == NULL && h->sum)
115 			percent = 100.0 * hits / h->sum;
116 
117 		color = get_percent_color(percent);
118 
119 		/*
120 		 * Also color the filename and line if needed, with
121 		 * the same color than the percentage. Don't print it
122 		 * twice for close colored ip with the same filename:line
123 		 */
124 		if (path) {
125 			if (!prev_line || strcmp(prev_line, path)
126 				       || color != prev_color) {
127 				color_fprintf(stdout, color, " %s", path);
128 				prev_line = path;
129 				prev_color = color;
130 			}
131 		}
132 
133 		color_fprintf(stdout, color, " %7.2f", percent);
134 		printf(" :	");
135 		color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", self->line);
136 	} else {
137 		if (!*self->line)
138 			printf("         :\n");
139 		else
140 			printf("         :	%s\n", self->line);
141 	}
142 
143 	return 0;
144 }
145 
146 static struct rb_root root_sym_ext;
147 
148 static void insert_source_line(struct sym_ext *sym_ext)
149 {
150 	struct sym_ext *iter;
151 	struct rb_node **p = &root_sym_ext.rb_node;
152 	struct rb_node *parent = NULL;
153 
154 	while (*p != NULL) {
155 		parent = *p;
156 		iter = rb_entry(parent, struct sym_ext, node);
157 
158 		if (sym_ext->percent > iter->percent)
159 			p = &(*p)->rb_left;
160 		else
161 			p = &(*p)->rb_right;
162 	}
163 
164 	rb_link_node(&sym_ext->node, parent, p);
165 	rb_insert_color(&sym_ext->node, &root_sym_ext);
166 }
167 
168 static void free_source_line(struct hist_entry *he, int len)
169 {
170 	struct sym_priv *priv = symbol__priv(he->ms.sym);
171 	struct sym_ext *sym_ext = priv->ext;
172 	int i;
173 
174 	if (!sym_ext)
175 		return;
176 
177 	for (i = 0; i < len; i++)
178 		free(sym_ext[i].path);
179 	free(sym_ext);
180 
181 	priv->ext = NULL;
182 	root_sym_ext = RB_ROOT;
183 }
184 
185 /* Get the filename:line for the colored entries */
186 static void
187 get_source_line(struct hist_entry *he, int len, const char *filename)
188 {
189 	struct symbol *sym = he->ms.sym;
190 	u64 start;
191 	int i;
192 	char cmd[PATH_MAX * 2];
193 	struct sym_ext *sym_ext;
194 	struct sym_priv *priv = symbol__priv(sym);
195 	struct sym_hist *h = priv->hist;
196 
197 	if (!h->sum)
198 		return;
199 
200 	sym_ext = priv->ext = calloc(len, sizeof(struct sym_ext));
201 	if (!priv->ext)
202 		return;
203 
204 	start = he->ms.map->unmap_ip(he->ms.map, sym->start);
205 
206 	for (i = 0; i < len; i++) {
207 		char *path = NULL;
208 		size_t line_len;
209 		u64 offset;
210 		FILE *fp;
211 
212 		sym_ext[i].percent = 100.0 * h->ip[i] / h->sum;
213 		if (sym_ext[i].percent <= 0.5)
214 			continue;
215 
216 		offset = start + i;
217 		sprintf(cmd, "addr2line -e %s %016llx", filename, offset);
218 		fp = popen(cmd, "r");
219 		if (!fp)
220 			continue;
221 
222 		if (getline(&path, &line_len, fp) < 0 || !line_len)
223 			goto next;
224 
225 		sym_ext[i].path = malloc(sizeof(char) * line_len + 1);
226 		if (!sym_ext[i].path)
227 			goto next;
228 
229 		strcpy(sym_ext[i].path, path);
230 		insert_source_line(&sym_ext[i]);
231 
232 	next:
233 		pclose(fp);
234 	}
235 }
236 
237 static void print_summary(const char *filename)
238 {
239 	struct sym_ext *sym_ext;
240 	struct rb_node *node;
241 
242 	printf("\nSorted summary for file %s\n", filename);
243 	printf("----------------------------------------------\n\n");
244 
245 	if (RB_EMPTY_ROOT(&root_sym_ext)) {
246 		printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);
247 		return;
248 	}
249 
250 	node = rb_first(&root_sym_ext);
251 	while (node) {
252 		double percent;
253 		const char *color;
254 		char *path;
255 
256 		sym_ext = rb_entry(node, struct sym_ext, node);
257 		percent = sym_ext->percent;
258 		color = get_percent_color(percent);
259 		path = sym_ext->path;
260 
261 		color_fprintf(stdout, color, " %7.2f %s", percent, path);
262 		node = rb_next(node);
263 	}
264 }
265 
266 static void hist_entry__print_hits(struct hist_entry *self)
267 {
268 	struct symbol *sym = self->ms.sym;
269 	struct sym_priv *priv = symbol__priv(sym);
270 	struct sym_hist *h = priv->hist;
271 	u64 len = sym->end - sym->start, offset;
272 
273 	for (offset = 0; offset < len; ++offset)
274 		if (h->ip[offset] != 0)
275 			printf("%*Lx: %Lu\n", BITS_PER_LONG / 2,
276 			       sym->start + offset, h->ip[offset]);
277 	printf("%*s: %Lu\n", BITS_PER_LONG / 2, "h->sum", h->sum);
278 }
279 
280 static void annotate_sym(struct hist_entry *he)
281 {
282 	struct map *map = he->ms.map;
283 	struct dso *dso = map->dso;
284 	struct symbol *sym = he->ms.sym;
285 	const char *filename = dso->long_name, *d_filename;
286 	u64 len;
287 	LIST_HEAD(head);
288 	struct objdump_line *pos, *n;
289 
290 	if (hist_entry__annotate(he, &head) < 0)
291 		return;
292 
293 	if (full_paths)
294 		d_filename = filename;
295 	else
296 		d_filename = basename(filename);
297 
298 	len = sym->end - sym->start;
299 
300 	if (print_line) {
301 		get_source_line(he, len, filename);
302 		print_summary(filename);
303 	}
304 
305 	printf("\n\n------------------------------------------------\n");
306 	printf(" Percent |	Source code & Disassembly of %s\n", d_filename);
307 	printf("------------------------------------------------\n");
308 
309 	if (verbose)
310 		hist_entry__print_hits(he);
311 
312 	list_for_each_entry_safe(pos, n, &head, node) {
313 		objdump_line__print(pos, &head, he, len);
314 		list_del(&pos->node);
315 		objdump_line__free(pos);
316 	}
317 
318 	if (print_line)
319 		free_source_line(he, len);
320 }
321 
322 static void hists__find_annotations(struct hists *self)
323 {
324 	struct rb_node *nd;
325 
326 	for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
327 		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
328 		struct sym_priv *priv;
329 
330 		if (he->ms.sym == NULL)
331 			continue;
332 
333 		priv = symbol__priv(he->ms.sym);
334 		if (priv->hist == NULL)
335 			continue;
336 
337 		annotate_sym(he);
338 		/*
339 		 * Since we have a hist_entry per IP for the same symbol, free
340 		 * he->ms.sym->hist to signal we already processed this symbol.
341 		 */
342 		free(priv->hist);
343 		priv->hist = NULL;
344 	}
345 }
346 
347 static struct perf_event_ops event_ops = {
348 	.sample	= process_sample_event,
349 	.mmap	= event__process_mmap,
350 	.comm	= event__process_comm,
351 	.fork	= event__process_task,
352 };
353 
354 static int __cmd_annotate(void)
355 {
356 	int ret;
357 	struct perf_session *session;
358 
359 	session = perf_session__new(input_name, O_RDONLY, force, false);
360 	if (session == NULL)
361 		return -ENOMEM;
362 
363 	ret = perf_session__process_events(session, &event_ops);
364 	if (ret)
365 		goto out_delete;
366 
367 	if (dump_trace) {
368 		event__print_totals();
369 		goto out_delete;
370 	}
371 
372 	if (verbose > 3)
373 		perf_session__fprintf(session, stdout);
374 
375 	if (verbose > 2)
376 		perf_session__fprintf_dsos(session, stdout);
377 
378 	hists__collapse_resort(&session->hists);
379 	hists__output_resort(&session->hists);
380 	hists__find_annotations(&session->hists);
381 out_delete:
382 	perf_session__delete(session);
383 
384 	return ret;
385 }
386 
387 static const char * const annotate_usage[] = {
388 	"perf annotate [<options>] <command>",
389 	NULL
390 };
391 
392 static const struct option options[] = {
393 	OPT_STRING('i', "input", &input_name, "file",
394 		    "input file name"),
395 	OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
396 		   "only consider symbols in these dsos"),
397 	OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
398 		    "symbol to annotate"),
399 	OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
400 	OPT_INCR('v', "verbose", &verbose,
401 		    "be more verbose (show symbol address, etc)"),
402 	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
403 		    "dump raw trace in ASCII"),
404 	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
405 		   "file", "vmlinux pathname"),
406 	OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
407 		    "load module symbols - WARNING: use only with -k and LIVE kernel"),
408 	OPT_BOOLEAN('l', "print-line", &print_line,
409 		    "print matching source lines (may be slow)"),
410 	OPT_BOOLEAN('P', "full-paths", &full_paths,
411 		    "Don't shorten the displayed pathnames"),
412 	OPT_END()
413 };
414 
415 int cmd_annotate(int argc, const char **argv, const char *prefix __used)
416 {
417 	argc = parse_options(argc, argv, options, annotate_usage, 0);
418 
419 	symbol_conf.priv_size = sizeof(struct sym_priv);
420 	symbol_conf.try_vmlinux_path = true;
421 
422 	if (symbol__init() < 0)
423 		return -1;
424 
425 	setup_sorting(annotate_usage, options);
426 
427 	if (argc) {
428 		/*
429 		 * Special case: if there's an argument left then assume tha
430 		 * it's a symbol filter:
431 		 */
432 		if (argc > 1)
433 			usage_with_options(annotate_usage, options);
434 
435 		sym_hist_filter = argv[0];
436 	}
437 
438 	setup_pager();
439 
440 	if (field_sep && *field_sep == '.') {
441 		pr_err("'.' is the only non valid --field-separator argument\n");
442 		return -1;
443 	}
444 
445 	return __cmd_annotate();
446 }
447