• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <pthread.h>
18 
symbol__annotate_init(struct map * map __used,struct symbol * sym)19 int symbol__annotate_init(struct map *map __used, struct symbol *sym)
20 {
21 	struct annotation *notes = symbol__annotation(sym);
22 	pthread_mutex_init(&notes->lock, NULL);
23 	return 0;
24 }
25 
symbol__alloc_hist(struct symbol * sym,int nevents)26 int symbol__alloc_hist(struct symbol *sym, int nevents)
27 {
28 	struct annotation *notes = symbol__annotation(sym);
29 	size_t sizeof_sym_hist = (sizeof(struct sym_hist) +
30 				  (sym->end - sym->start) * sizeof(u64));
31 
32 	notes->src = zalloc(sizeof(*notes->src) + nevents * sizeof_sym_hist);
33 	if (notes->src == NULL)
34 		return -1;
35 	notes->src->sizeof_sym_hist = sizeof_sym_hist;
36 	notes->src->nr_histograms   = nevents;
37 	INIT_LIST_HEAD(&notes->src->source);
38 	return 0;
39 }
40 
symbol__annotate_zero_histograms(struct symbol * sym)41 void symbol__annotate_zero_histograms(struct symbol *sym)
42 {
43 	struct annotation *notes = symbol__annotation(sym);
44 
45 	pthread_mutex_lock(&notes->lock);
46 	if (notes->src != NULL)
47 		memset(notes->src->histograms, 0,
48 		       notes->src->nr_histograms * notes->src->sizeof_sym_hist);
49 	pthread_mutex_unlock(&notes->lock);
50 }
51 
symbol__inc_addr_samples(struct symbol * sym,struct map * map,int evidx,u64 addr)52 int symbol__inc_addr_samples(struct symbol *sym, struct map *map,
53 			     int evidx, u64 addr)
54 {
55 	unsigned offset;
56 	struct annotation *notes;
57 	struct sym_hist *h;
58 
59 	notes = symbol__annotation(sym);
60 	if (notes->src == NULL)
61 		return -ENOMEM;
62 
63 	pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr));
64 
65 	if (addr >= sym->end)
66 		return 0;
67 
68 	offset = addr - sym->start;
69 	h = annotation__histogram(notes, evidx);
70 	h->sum++;
71 	h->addr[offset]++;
72 
73 	pr_debug3("%#" PRIx64 " %s: period++ [addr: %#" PRIx64 ", %#" PRIx64
74 		  ", evidx=%d] => %" PRIu64 "\n", sym->start, sym->name,
75 		  addr, addr - sym->start, evidx, h->addr[offset]);
76 	return 0;
77 }
78 
objdump_line__new(s64 offset,char * line,size_t privsize)79 static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize)
80 {
81 	struct objdump_line *self = malloc(sizeof(*self) + privsize);
82 
83 	if (self != NULL) {
84 		self->offset = offset;
85 		self->line = line;
86 	}
87 
88 	return self;
89 }
90 
objdump_line__free(struct objdump_line * self)91 void objdump_line__free(struct objdump_line *self)
92 {
93 	free(self->line);
94 	free(self);
95 }
96 
objdump__add_line(struct list_head * head,struct objdump_line * line)97 static void objdump__add_line(struct list_head *head, struct objdump_line *line)
98 {
99 	list_add_tail(&line->node, head);
100 }
101 
objdump__get_next_ip_line(struct list_head * head,struct objdump_line * pos)102 struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
103 					       struct objdump_line *pos)
104 {
105 	list_for_each_entry_continue(pos, head, node)
106 		if (pos->offset >= 0)
107 			return pos;
108 
109 	return NULL;
110 }
111 
objdump_line__print(struct objdump_line * oline,struct symbol * sym,int evidx,u64 len,int min_pcnt,int printed,int max_lines,struct objdump_line * queue)112 static int objdump_line__print(struct objdump_line *oline, struct symbol *sym,
113 			       int evidx, u64 len, int min_pcnt,
114 			       int printed, int max_lines,
115 			       struct objdump_line *queue)
116 {
117 	static const char *prev_line;
118 	static const char *prev_color;
119 
120 	if (oline->offset != -1) {
121 		const char *path = NULL;
122 		unsigned int hits = 0;
123 		double percent = 0.0;
124 		const char *color;
125 		struct annotation *notes = symbol__annotation(sym);
126 		struct source_line *src_line = notes->src->lines;
127 		struct sym_hist *h = annotation__histogram(notes, evidx);
128 		s64 offset = oline->offset;
129 		struct objdump_line *next;
130 
131 		next = objdump__get_next_ip_line(&notes->src->source, oline);
132 
133 		while (offset < (s64)len &&
134 		       (next == NULL || offset < next->offset)) {
135 			if (src_line) {
136 				if (path == NULL)
137 					path = src_line[offset].path;
138 				percent += src_line[offset].percent;
139 			} else
140 				hits += h->addr[offset];
141 
142 			++offset;
143 		}
144 
145 		if (src_line == NULL && h->sum)
146 			percent = 100.0 * hits / h->sum;
147 
148 		if (percent < min_pcnt)
149 			return -1;
150 
151 		if (max_lines && printed >= max_lines)
152 			return 1;
153 
154 		if (queue != NULL) {
155 			list_for_each_entry_from(queue, &notes->src->source, node) {
156 				if (queue == oline)
157 					break;
158 				objdump_line__print(queue, sym, evidx, len,
159 						    0, 0, 1, NULL);
160 			}
161 		}
162 
163 		color = get_percent_color(percent);
164 
165 		/*
166 		 * Also color the filename and line if needed, with
167 		 * the same color than the percentage. Don't print it
168 		 * twice for close colored addr with the same filename:line
169 		 */
170 		if (path) {
171 			if (!prev_line || strcmp(prev_line, path)
172 				       || color != prev_color) {
173 				color_fprintf(stdout, color, " %s", path);
174 				prev_line = path;
175 				prev_color = color;
176 			}
177 		}
178 
179 		color_fprintf(stdout, color, " %7.2f", percent);
180 		printf(" :	");
181 		color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", oline->line);
182 	} else if (max_lines && printed >= max_lines)
183 		return 1;
184 	else {
185 		if (queue)
186 			return -1;
187 
188 		if (!*oline->line)
189 			printf("         :\n");
190 		else
191 			printf("         :	%s\n", oline->line);
192 	}
193 
194 	return 0;
195 }
196 
symbol__parse_objdump_line(struct symbol * sym,struct map * map,FILE * file,size_t privsize)197 static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
198 				      FILE *file, size_t privsize)
199 {
200 	struct annotation *notes = symbol__annotation(sym);
201 	struct objdump_line *objdump_line;
202 	char *line = NULL, *tmp, *tmp2, *c;
203 	size_t line_len;
204 	s64 line_ip, offset = -1;
205 
206 	if (getline(&line, &line_len, file) < 0)
207 		return -1;
208 
209 	if (!line)
210 		return -1;
211 
212 	while (line_len != 0 && isspace(line[line_len - 1]))
213 		line[--line_len] = '\0';
214 
215 	c = strchr(line, '\n');
216 	if (c)
217 		*c = 0;
218 
219 	line_ip = -1;
220 
221 	/*
222 	 * Strip leading spaces:
223 	 */
224 	tmp = line;
225 	while (*tmp) {
226 		if (*tmp != ' ')
227 			break;
228 		tmp++;
229 	}
230 
231 	if (*tmp) {
232 		/*
233 		 * Parse hexa addresses followed by ':'
234 		 */
235 		line_ip = strtoull(tmp, &tmp2, 16);
236 		if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0')
237 			line_ip = -1;
238 	}
239 
240 	if (line_ip != -1) {
241 		u64 start = map__rip_2objdump(map, sym->start),
242 		    end = map__rip_2objdump(map, sym->end);
243 
244 		offset = line_ip - start;
245 		if (offset < 0 || (u64)line_ip > end)
246 			offset = -1;
247 	}
248 
249 	objdump_line = objdump_line__new(offset, line, privsize);
250 	if (objdump_line == NULL) {
251 		free(line);
252 		return -1;
253 	}
254 	objdump__add_line(&notes->src->source, objdump_line);
255 
256 	return 0;
257 }
258 
259 /* ANDROID_CHANGE_BEGIN */
260 #if 0
261 int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize)
262 #else
symbol__annotate(struct symbol * sym,struct map * map,size_t privsize,bool print_lines)263 int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize,
264                      bool print_lines)
265 #endif
266 /* ANDROID_CHANGE_END */
267 {
268 	struct dso *dso = map->dso;
269 	char *filename = dso__build_id_filename(dso, NULL, 0);
270 	bool free_filename = true;
271 	char command[PATH_MAX * 2];
272 	FILE *file;
273 	int err = 0;
274 	char symfs_filename[PATH_MAX];
275 
276 	if (filename) {
277 		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
278 			 symbol_conf.symfs, filename);
279 	}
280 
281 	if (filename == NULL) {
282 		if (dso->has_build_id) {
283 			pr_err("Can't annotate %s: not enough memory\n",
284 			       sym->name);
285 			return -ENOMEM;
286 		}
287 		goto fallback;
288 	} else if (readlink(symfs_filename, command, sizeof(command)) < 0 ||
289 		   strstr(command, "[kernel.kallsyms]") ||
290 		   access(symfs_filename, R_OK)) {
291 		free(filename);
292 fallback:
293 		/*
294 		 * If we don't have build-ids or the build-id file isn't in the
295 		 * cache, or is just a kallsyms file, well, lets hope that this
296 		 * DSO is the same as when 'perf record' ran.
297 		 */
298 		filename = dso->long_name;
299 		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
300 			 symbol_conf.symfs, filename);
301 		free_filename = false;
302 	}
303 
304 	if (dso->symtab_type == SYMTAB__KALLSYMS) {
305 		char bf[BUILD_ID_SIZE * 2 + 16] = " with build id ";
306 		char *build_id_msg = NULL;
307 
308 		if (dso->annotate_warned)
309 			goto out_free_filename;
310 
311 		if (dso->has_build_id) {
312 			build_id__sprintf(dso->build_id,
313 					  sizeof(dso->build_id), bf + 15);
314 			build_id_msg = bf;
315 		}
316 		err = -ENOENT;
317 		dso->annotate_warned = 1;
318 		pr_err("Can't annotate %s: No vmlinux file%s was found in the "
319 		       "path.\nPlease use 'perf buildid-cache -av vmlinux' or "
320 		       "--vmlinux vmlinux.\n",
321 		       sym->name, build_id_msg ?: "");
322 		goto out_free_filename;
323 	}
324 
325 	pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__,
326 		 filename, sym->name, map->unmap_ip(map, sym->start),
327 		 map->unmap_ip(map, sym->end));
328 
329 	pr_debug("annotating [%p] %30s : [%p] %30s\n",
330 		 dso, dso->long_name, sym, sym->name);
331 
332     /* ANDROID_CHANGE_BEGIN */
333 #if 0
334 	snprintf(command, sizeof(command),
335 		 "objdump --start-address=0x%016" PRIx64
336 		 " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand",
337 		 map__rip_2objdump(map, sym->start),
338 		 map__rip_2objdump(map, sym->end),
339 		 symfs_filename, filename);
340 #else
341 	snprintf(command, sizeof(command),
342 		 "arm-linux-androideabi-objdump --start-address=0x%016" PRIx64
343 		 " --stop-address=0x%016" PRIx64 " -d%c -C %s|grep -v %s|expand",
344 		 map__rip_2objdump(map, sym->start),
345 		 map__rip_2objdump(map, sym->end),
346          print_lines ? 'S' : ' ',
347 		 symfs_filename, filename);
348 #endif
349     /* ANDROID_CHANGE_END */
350 
351 	pr_debug("Executing: %s\n", command);
352 
353 	file = popen(command, "r");
354 	if (!file)
355 		goto out_free_filename;
356 
357 	while (!feof(file))
358 		if (symbol__parse_objdump_line(sym, map, file, privsize) < 0)
359 			break;
360 
361 	pclose(file);
362 out_free_filename:
363 	if (free_filename)
364 		free(filename);
365 	return err;
366 }
367 
insert_source_line(struct rb_root * root,struct source_line * src_line)368 static void insert_source_line(struct rb_root *root, struct source_line *src_line)
369 {
370 	struct source_line *iter;
371 	struct rb_node **p = &root->rb_node;
372 	struct rb_node *parent = NULL;
373 
374 	while (*p != NULL) {
375 		parent = *p;
376 		iter = rb_entry(parent, struct source_line, node);
377 
378 		if (src_line->percent > iter->percent)
379 			p = &(*p)->rb_left;
380 		else
381 			p = &(*p)->rb_right;
382 	}
383 
384 	rb_link_node(&src_line->node, parent, p);
385 	rb_insert_color(&src_line->node, root);
386 }
387 
symbol__free_source_line(struct symbol * sym,int len)388 static void symbol__free_source_line(struct symbol *sym, int len)
389 {
390 	struct annotation *notes = symbol__annotation(sym);
391 	struct source_line *src_line = notes->src->lines;
392 	int i;
393 
394 	for (i = 0; i < len; i++)
395 		free(src_line[i].path);
396 
397 	free(src_line);
398 	notes->src->lines = NULL;
399 }
400 
401 /* Get the filename:line for the colored entries */
symbol__get_source_line(struct symbol * sym,struct map * map,int evidx,struct rb_root * root,int len,const char * filename)402 static int symbol__get_source_line(struct symbol *sym, struct map *map,
403 				   int evidx, struct rb_root *root, int len,
404 				   const char *filename)
405 {
406 	u64 start;
407 	int i;
408 	char cmd[PATH_MAX * 2];
409 	struct source_line *src_line;
410 	struct annotation *notes = symbol__annotation(sym);
411 	struct sym_hist *h = annotation__histogram(notes, evidx);
412 
413 	if (!h->sum)
414 		return 0;
415 
416 	src_line = notes->src->lines = calloc(len, sizeof(struct source_line));
417 	if (!notes->src->lines)
418 		return -1;
419 
420     /* ANDORID_CHANGE_BEGIN */
421 #if 0
422 	start = map->unmap_ip(map, sym->start);
423 #else
424     /* Use relative start address */
425     start = sym->start;
426 #endif
427     /* ANDORID_CHANGE_END */
428 
429 	for (i = 0; i < len; i++) {
430 		char *path = NULL;
431 		size_t line_len;
432 		u64 offset;
433 		FILE *fp;
434 
435 		src_line[i].percent = 100.0 * h->addr[i] / h->sum;
436 		if (src_line[i].percent <= 0.5)
437 			continue;
438 
439 		offset = start + i;
440 		sprintf(cmd, "arm-linux-androideabi-addr2line -e %s%s %016" PRIx64, symbol_conf.symfs,
441                 filename, offset);
442 		fp = popen(cmd, "r");
443 		if (!fp)
444 			continue;
445 
446 		if (getline(&path, &line_len, fp) < 0 || !line_len)
447 			goto next;
448 
449 		src_line[i].path = malloc(sizeof(char) * line_len + 1);
450 		if (!src_line[i].path)
451 			goto next;
452 
453 		strcpy(src_line[i].path, path);
454 		insert_source_line(root, &src_line[i]);
455 
456 	next:
457 		pclose(fp);
458 	}
459 
460 	return 0;
461 }
462 
print_summary(struct rb_root * root,const char * filename)463 static void print_summary(struct rb_root *root, const char *filename)
464 {
465 	struct source_line *src_line;
466 	struct rb_node *node;
467 
468 	printf("\nSorted summary for file %s\n", filename);
469 	printf("----------------------------------------------\n\n");
470 
471 	if (RB_EMPTY_ROOT(root)) {
472 		printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);
473 		return;
474 	}
475 
476 	node = rb_first(root);
477 	while (node) {
478 		double percent;
479 		const char *color;
480 		char *path;
481 
482 		src_line = rb_entry(node, struct source_line, node);
483 		percent = src_line->percent;
484 		color = get_percent_color(percent);
485 		path = src_line->path;
486 
487 		color_fprintf(stdout, color, " %7.2f %s", percent, path);
488 		node = rb_next(node);
489 	}
490 }
491 
symbol__annotate_hits(struct symbol * sym,int evidx)492 static void symbol__annotate_hits(struct symbol *sym, int evidx)
493 {
494 	struct annotation *notes = symbol__annotation(sym);
495 	struct sym_hist *h = annotation__histogram(notes, evidx);
496 	u64 len = sym->end - sym->start, offset;
497 
498 	for (offset = 0; offset < len; ++offset)
499 		if (h->addr[offset] != 0)
500 			printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2,
501 			       sym->start + offset, h->addr[offset]);
502 	printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum);
503 }
504 
symbol__annotate_printf(struct symbol * sym,struct map * map,int evidx,bool full_paths,int min_pcnt,int max_lines,int context)505 int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
506 			    bool full_paths, int min_pcnt, int max_lines,
507 			    int context)
508 {
509 	struct dso *dso = map->dso;
510 	const char *filename = dso->long_name, *d_filename;
511 	struct annotation *notes = symbol__annotation(sym);
512 	struct objdump_line *pos, *queue = NULL;
513 	int printed = 2, queue_len = 0;
514 	int more = 0;
515 	u64 len;
516 
517 	if (full_paths)
518 		d_filename = filename;
519 	else
520 		d_filename = basename(filename);
521 
522 	len = sym->end - sym->start;
523 
524 	printf(" Percent |	Source code & Disassembly of %s\n", d_filename);
525 	printf("------------------------------------------------\n");
526 
527 	if (verbose)
528 		symbol__annotate_hits(sym, evidx);
529 
530 	list_for_each_entry(pos, &notes->src->source, node) {
531 		if (context && queue == NULL) {
532 			queue = pos;
533 			queue_len = 0;
534 		}
535 
536 		switch (objdump_line__print(pos, sym, evidx, len, min_pcnt,
537 					    printed, max_lines, queue)) {
538 		case 0:
539 			++printed;
540 			if (context) {
541 				printed += queue_len;
542 				queue = NULL;
543 				queue_len = 0;
544 			}
545 			break;
546 		case 1:
547 			/* filtered by max_lines */
548 			++more;
549 			break;
550 		case -1:
551 		default:
552 			/*
553 			 * Filtered by min_pcnt or non IP lines when
554 			 * context != 0
555 			 */
556 			if (!context)
557 				break;
558 			if (queue_len == context)
559 				queue = list_entry(queue->node.next, typeof(*queue), node);
560 			else
561 				++queue_len;
562 			break;
563 		}
564 	}
565 
566 	return more;
567 }
568 
symbol__annotate_zero_histogram(struct symbol * sym,int evidx)569 void symbol__annotate_zero_histogram(struct symbol *sym, int evidx)
570 {
571 	struct annotation *notes = symbol__annotation(sym);
572 	struct sym_hist *h = annotation__histogram(notes, evidx);
573 
574 	memset(h, 0, notes->src->sizeof_sym_hist);
575 }
576 
symbol__annotate_decay_histogram(struct symbol * sym,int evidx)577 void symbol__annotate_decay_histogram(struct symbol *sym, int evidx)
578 {
579 	struct annotation *notes = symbol__annotation(sym);
580 	struct sym_hist *h = annotation__histogram(notes, evidx);
581 	struct objdump_line *pos;
582 	int len = sym->end - sym->start;
583 
584 	h->sum = 0;
585 
586 	list_for_each_entry(pos, &notes->src->source, node) {
587 		if (pos->offset != -1 && pos->offset < len) {
588 			h->addr[pos->offset] = h->addr[pos->offset] * 7 / 8;
589 			h->sum += h->addr[pos->offset];
590 		}
591 	}
592 }
593 
objdump_line_list__purge(struct list_head * head)594 void objdump_line_list__purge(struct list_head *head)
595 {
596 	struct objdump_line *pos, *n;
597 
598 	list_for_each_entry_safe(pos, n, head, node) {
599 		list_del(&pos->node);
600 		objdump_line__free(pos);
601 	}
602 }
603 
symbol__tty_annotate(struct symbol * sym,struct map * map,int evidx,bool print_lines,bool full_paths,int min_pcnt,int max_lines)604 int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
605 			 bool print_lines, bool full_paths, int min_pcnt,
606 			 int max_lines)
607 {
608 	struct dso *dso = map->dso;
609 	const char *filename = dso->long_name;
610 	struct rb_root source_line = RB_ROOT;
611 	u64 len;
612 
613     /* ANDROID_CHANGE_BEGIN */
614 #if 0
615 	if (symbol__annotate(sym, map, 0) < 0)
616 		return -1;
617 #else
618 	if (symbol__annotate(sym, map, 0, print_lines) < 0)
619 		return -1;
620 #endif
621     /* ANDROID_CHANGE_END */
622 
623 	len = sym->end - sym->start;
624 
625 	if (print_lines) {
626 		symbol__get_source_line(sym, map, evidx, &source_line,
627 					len, filename);
628 		print_summary(&source_line, filename);
629 	}
630 
631 	symbol__annotate_printf(sym, map, evidx, full_paths,
632 				min_pcnt, max_lines, 0);
633 	if (print_lines)
634 		symbol__free_source_line(sym, len);
635 
636 	objdump_line_list__purge(&symbol__annotation(sym)->src->source);
637 
638 	return 0;
639 }
640