builtin-annotate.c 12.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * builtin-annotate.c
 *
 * Builtin annotate command: Analyze the perf.data input file,
 * look up and read DSOs and symbol information and display
 * a histogram of results, along various sorting keys.
 */
#include "builtin.h"

#include "util/util.h"

#include "util/color.h"
13
#include <linux/list.h>
14
#include "util/cache.h"
15
#include <linux/rbtree.h>
16 17 18 19
#include "util/symbol.h"
#include "util/string.h"

#include "perf.h"
20
#include "util/debug.h"
21

22
#include "util/event.h"
23 24
#include "util/parse-options.h"
#include "util/parse-events.h"
25
#include "util/thread.h"
26
#include "util/sort.h"
27
#include "util/hist.h"
28
#include "util/session.h"
L
Li Zefan 已提交
29
#include "util/data_map.h"
30 31 32

static char		const *input_name = "perf.data";

33
static int		force;
34

35 36
static int		full_paths;

37 38
static int		print_line;

39 40 41 42 43
struct sym_hist {
	u64		sum;
	u64		ip[0];
};

44
struct sym_ext {
45
	struct rb_node	node;
46 47 48 49
	double		percent;
	char		*path;
};

50 51 52 53 54
struct sym_priv {
	struct sym_hist	*hist;
	struct sym_ext	*ext;
};

55 56 57 58 59
static struct symbol_conf symbol_conf = {
	.priv_size	  = sizeof(struct sym_priv),
	.try_vmlinux_path = true,
};

60 61
static const char *sym_hist_filter;

62
static int symbol_filter(struct map *map __used, struct symbol *sym)
63
{
64 65
	if (sym_hist_filter == NULL ||
	    strcmp(sym->name, sym_hist_filter) == 0) {
66
		struct sym_priv *priv = symbol__priv(sym);
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
		const int size = (sizeof(*priv->hist) +
				  (sym->end - sym->start) * sizeof(u64));

		priv->hist = malloc(size);
		if (priv->hist)
			memset(priv->hist, 0, size);
		return 0;
	}
	/*
	 * FIXME: We should really filter it out, as we don't want to go thru symbols
	 * we're not interested, and if a DSO ends up with no symbols, delete it too,
	 * but right now the kernel loading routines in symbol.c bail out if no symbols
	 * are found, fix it later.
	 */
	return 0;
}
83

84 85 86
/*
 * collect histogram counts
 */
87
static void hist_hit(struct hist_entry *he, u64 ip)
88
{
89 90
	unsigned int sym_size, offset;
	struct symbol *sym = he->sym;
91 92
	struct sym_priv *priv;
	struct sym_hist *h;
93

94
	he->count++;
95

96 97 98
	if (!sym || !he->map)
		return;

99
	priv = symbol__priv(sym);
100
	if (!priv->hist)
101
		return;
102

103 104
	sym_size = sym->end - sym->start;
	offset = ip - sym->start;
105

106 107 108 109
	if (verbose)
		fprintf(stderr, "%s: ip=%Lx\n", __func__,
			he->map->unmap_ip(he->map, ip));

110 111
	if (offset >= sym_size)
		return;
112

113 114 115
	h = priv->hist;
	h->sum++;
	h->ip[offset]++;
116

117 118
	if (verbose >= 3)
		printf("%p %s: count++ [ip: %p, %08Lx] => %Ld\n",
119
			(void *)(unsigned long)he->sym->start,
120
			he->sym->name,
121
			(void *)(unsigned long)ip, ip - he->sym->start,
122
			h->ip[offset]);
123 124
}

125
static int hist_entry__add(struct addr_location *al, u64 count)
126
{
127
	bool hit;
128
	struct hist_entry *he = __hist_entry__add(al, NULL, count, &hit);
129
	if (he == NULL)
130
		return -ENOMEM;
131
	hist_hit(he, al->addr);
132 133 134
	return 0;
}

135
static int process_sample_event(event_t *event)
136
{
137
	struct addr_location al;
138

139
	dump_printf("(IP, %d): %d: %p\n", event->header.misc,
140
		    event->ip.pid, (void *)(long)event->ip.ip);
141

142
	if (event__preprocess_sample(event, &al, symbol_filter) < 0) {
143 144 145 146 147
		fprintf(stderr, "problem processing %d event, skipping it.\n",
			event->header.type);
		return -1;
	}

148
	if (hist_entry__add(&al, 1)) {
149 150 151
		fprintf(stderr, "problem incrementing symbol count, "
				"skipping event\n");
		return -1;
152 153 154 155 156
	}

	return 0;
}

157
static int parse_line(FILE *file, struct hist_entry *he, u64 len)
158
{
159
	struct symbol *sym = he->sym;
160
	char *line = NULL, *tmp, *tmp2;
161 162
	static const char *prev_line;
	static const char *prev_color;
163 164
	unsigned int offset;
	size_t line_len;
165
	u64 start;
166
	s64 line_ip;
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
	int ret;
	char *c;

	if (getline(&line, &line_len, file) < 0)
		return -1;
	if (!line)
		return -1;

	c = strchr(line, '\n');
	if (c)
		*c = 0;

	line_ip = -1;
	offset = 0;
	ret = -2;

	/*
	 * Strip leading spaces:
	 */
	tmp = line;
	while (*tmp) {
		if (*tmp != ' ')
			break;
		tmp++;
	}

	if (*tmp) {
		/*
		 * Parse hexa addresses followed by ':'
		 */
		line_ip = strtoull(tmp, &tmp2, 16);
		if (*tmp2 != ':')
			line_ip = -1;
	}

202 203
	start = he->map->unmap_ip(he->map, sym->start);

204
	if (line_ip != -1) {
205
		const char *path = NULL;
206 207
		unsigned int hits = 0;
		double percent = 0.0;
208
		const char *color;
209
		struct sym_priv *priv = symbol__priv(sym);
210 211
		struct sym_ext *sym_ext = priv->ext;
		struct sym_hist *h = priv->hist;
212

213
		offset = line_ip - start;
214
		if (offset < len)
215
			hits = h->ip[offset];
216

217
		if (offset < len && sym_ext) {
218 219
			path = sym_ext[offset].path;
			percent = sym_ext[offset].percent;
220 221
		} else if (h->sum)
			percent = 100.0 * hits / h->sum;
222

223
		color = get_percent_color(percent);
224

225 226 227 228 229 230 231 232 233 234 235 236 237 238
		/*
		 * Also color the filename and line if needed, with
		 * the same color than the percentage. Don't print it
		 * twice for close colored ip with the same filename:line
		 */
		if (path) {
			if (!prev_line || strcmp(prev_line, path)
				       || color != prev_color) {
				color_fprintf(stdout, color, " %s", path);
				prev_line = path;
				prev_color = color;
			}
		}

239 240 241 242 243 244 245 246 247 248 249 250 251
		color_fprintf(stdout, color, " %7.2f", percent);
		printf(" :	");
		color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line);
	} else {
		if (!*line)
			printf("         :\n");
		else
			printf("         :	%s\n", line);
	}

	return 0;
}

252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
static struct rb_root root_sym_ext;

static void insert_source_line(struct sym_ext *sym_ext)
{
	struct sym_ext *iter;
	struct rb_node **p = &root_sym_ext.rb_node;
	struct rb_node *parent = NULL;

	while (*p != NULL) {
		parent = *p;
		iter = rb_entry(parent, struct sym_ext, node);

		if (sym_ext->percent > iter->percent)
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}

	rb_link_node(&sym_ext->node, parent, p);
	rb_insert_color(&sym_ext->node, &root_sym_ext);
}

274
static void free_source_line(struct hist_entry *he, int len)
275
{
276
	struct sym_priv *priv = symbol__priv(he->sym);
277
	struct sym_ext *sym_ext = priv->ext;
278 279 280 281 282 283 284 285 286
	int i;

	if (!sym_ext)
		return;

	for (i = 0; i < len; i++)
		free(sym_ext[i].path);
	free(sym_ext);

287
	priv->ext = NULL;
288
	root_sym_ext = RB_ROOT;
289 290 291
}

/* Get the filename:line for the colored entries */
292
static void
293
get_source_line(struct hist_entry *he, int len, const char *filename)
294
{
295 296
	struct symbol *sym = he->sym;
	u64 start;
297 298 299
	int i;
	char cmd[PATH_MAX * 2];
	struct sym_ext *sym_ext;
300
	struct sym_priv *priv = symbol__priv(sym);
301
	struct sym_hist *h = priv->hist;
302

303
	if (!h->sum)
304 305
		return;

306 307
	sym_ext = priv->ext = calloc(len, sizeof(struct sym_ext));
	if (!priv->ext)
308 309
		return;

310
	start = he->map->unmap_ip(he->map, sym->start);
311 312 313 314

	for (i = 0; i < len; i++) {
		char *path = NULL;
		size_t line_len;
315
		u64 offset;
316 317
		FILE *fp;

318
		sym_ext[i].percent = 100.0 * h->ip[i] / h->sum;
319 320 321
		if (sym_ext[i].percent <= 0.5)
			continue;

322
		offset = start + i;
323
		sprintf(cmd, "addr2line -e %s %016llx", filename, offset);
324 325 326 327 328 329 330
		fp = popen(cmd, "r");
		if (!fp)
			continue;

		if (getline(&path, &line_len, fp) < 0 || !line_len)
			goto next;

331
		sym_ext[i].path = malloc(sizeof(char) * line_len + 1);
332 333 334 335
		if (!sym_ext[i].path)
			goto next;

		strcpy(sym_ext[i].path, path);
336
		insert_source_line(&sym_ext[i]);
337 338 339 340 341 342

	next:
		pclose(fp);
	}
}

343
static void print_summary(const char *filename)
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
{
	struct sym_ext *sym_ext;
	struct rb_node *node;

	printf("\nSorted summary for file %s\n", filename);
	printf("----------------------------------------------\n\n");

	if (RB_EMPTY_ROOT(&root_sym_ext)) {
		printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);
		return;
	}

	node = rb_first(&root_sym_ext);
	while (node) {
		double percent;
359
		const char *color;
360 361 362 363
		char *path;

		sym_ext = rb_entry(node, struct sym_ext, node);
		percent = sym_ext->percent;
364
		color = get_percent_color(percent);
365 366 367 368 369 370 371
		path = sym_ext->path;

		color_fprintf(stdout, color, " %7.2f %s", percent, path);
		node = rb_next(node);
	}
}

372
static void annotate_sym(struct hist_entry *he)
373
{
374 375 376
	struct map *map = he->map;
	struct dso *dso = map->dso;
	struct symbol *sym = he->sym;
377 378
	const char *filename = dso->long_name, *d_filename;
	u64 len;
379 380 381 382 383
	char command[PATH_MAX*2];
	FILE *file;

	if (!filename)
		return;
384

385 386 387 388 389 390
	if (verbose)
		fprintf(stderr, "%s: filename=%s, sym=%s, start=%Lx, end=%Lx\n",
			__func__, filename, sym->name,
			map->unmap_ip(map, sym->start),
			map->unmap_ip(map, sym->end));

391 392 393 394
	if (full_paths)
		d_filename = filename;
	else
		d_filename = basename(filename);
395 396 397

	len = sym->end - sym->start;

398
	if (print_line) {
399
		get_source_line(he, len, filename);
400 401 402 403
		print_summary(filename);
	}

	printf("\n\n------------------------------------------------\n");
404
	printf(" Percent |	Source code & Disassembly of %s\n", d_filename);
405 406 407
	printf("------------------------------------------------\n");

	if (verbose >= 2)
408 409
		printf("annotating [%p] %30s : [%p] %30s\n",
		       dso, dso->long_name, sym, sym->name);
410

411
	sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s",
412 413
		map->unmap_ip(map, sym->start), map->unmap_ip(map, sym->end),
		filename, filename);
414 415 416 417 418 419 420 421 422

	if (verbose >= 3)
		printf("doing: %s\n", command);

	file = popen(command, "r");
	if (!file)
		return;

	while (!feof(file)) {
423
		if (parse_line(file, he, len) < 0)
424 425 426 427
			break;
	}

	pclose(file);
428
	if (print_line)
429
		free_source_line(he, len);
430 431 432 433 434 435
}

static void find_annotations(void)
{
	struct rb_node *nd;

436 437
	for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
438
		struct sym_priv *priv;
439

440 441
		if (he->sym == NULL)
			continue;
442

443
		priv = symbol__priv(he->sym);
444 445 446 447 448 449 450 451 452 453
		if (priv->hist == NULL)
			continue;

		annotate_sym(he);
		/*
		 * Since we have a hist_entry per IP for the same symbol, free
		 * he->sym->hist to signal we already processed this symbol.
		 */
		free(priv->hist);
		priv->hist = NULL;
454 455 456
	}
}

L
Li Zefan 已提交
457 458 459 460 461 462 463
static struct perf_file_handler file_handler = {
	.process_sample_event	= process_sample_event,
	.process_mmap_event	= event__process_mmap,
	.process_comm_event	= event__process_comm,
	.process_fork_event	= event__process_task,
};

464 465
static int __cmd_annotate(void)
{
466
	struct perf_session *session = perf_session__new(input_name, O_RDONLY, force);
L
Li Zefan 已提交
467 468
	struct thread *idle;
	int ret;
469

470 471 472
	if (session == NULL)
		return -ENOMEM;

L
Li Zefan 已提交
473 474
	idle = register_idle_thread();
	register_perf_file_handler(&file_handler);
475

476
	ret = perf_session__process_events(session, 0, &event__cwdlen, &event__cwd);
L
Li Zefan 已提交
477
	if (ret)
478
		goto out_delete;
479

480 481
	if (dump_trace) {
		event__print_totals();
482
		goto out_delete;
483
	}
484

485
	if (verbose > 3)
486
		threads__fprintf(stdout);
487

488
	if (verbose > 2)
489 490 491
		dsos__fprintf(stdout);

	collapse__resort();
492
	output__resort(event__total[0]);
493 494

	find_annotations();
495 496
out_delete:
	perf_session__delete(session);
497

L
Li Zefan 已提交
498
	return ret;
499 500 501 502 503 504 505 506 507 508
}

static const char * const annotate_usage[] = {
	"perf annotate [<options>] <command>",
	NULL
};

static const struct option options[] = {
	OPT_STRING('i', "input", &input_name, "file",
		    "input file name"),
509
	OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
510
		    "symbol to annotate"),
511
	OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
512 513 514 515
	OPT_BOOLEAN('v', "verbose", &verbose,
		    "be more verbose (show symbol address, etc)"),
	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
		    "dump raw trace in ASCII"),
516 517 518
	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
		   "file", "vmlinux pathname"),
	OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
519
		    "load module symbols - WARNING: use only with -k and LIVE kernel"),
520 521
	OPT_BOOLEAN('l', "print-line", &print_line,
		    "print matching source lines (may be slow)"),
522 523
	OPT_BOOLEAN('P', "full-paths", &full_paths,
		    "Don't shorten the displayed pathnames"),
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
	OPT_END()
};

static void setup_sorting(void)
{
	char *tmp, *tok, *str = strdup(sort_order);

	for (tok = strtok_r(str, ", ", &tmp);
			tok; tok = strtok_r(NULL, ", ", &tmp)) {
		if (sort_dimension__add(tok) < 0) {
			error("Unknown --sort key: `%s'", tok);
			usage_with_options(annotate_usage, options);
		}
	}

	free(str);
}

542
int cmd_annotate(int argc, const char **argv, const char *prefix __used)
543
{
544 545
	if (symbol__init(&symbol_conf) < 0)
		return -1;
546 547 548 549 550

	argc = parse_options(argc, argv, options, annotate_usage, 0);

	setup_sorting();

551 552 553 554 555 556 557 558 559 560 561
	if (argc) {
		/*
		 * Special case: if there's an argument left then assume tha
		 * it's a symbol filter:
		 */
		if (argc > 1)
			usage_with_options(annotate_usage, options);

		sym_hist_filter = argv[0];
	}

562 563
	setup_pager();

564 565 566 567 568 569
	if (field_sep && *field_sep == '.') {
		fputs("'.' is the only non valid --field-separator argument\n",
				stderr);
		exit(129);
	}

570 571
	return __cmd_annotate();
}