builtin-annotate.c 11.9 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"
29 30 31

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

32
static int		force;
33

34 35
static int		full_paths;

36 37
static int		print_line;

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

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

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

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

59 60
static const char *sym_hist_filter;

61
static int symbol_filter(struct map *map __used, struct symbol *sym)
62
{
63 64
	if (sym_hist_filter == NULL ||
	    strcmp(sym->name, sym_hist_filter) == 0) {
65
		struct sym_priv *priv = symbol__priv(sym);
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
		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;
}
82

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

93
	he->count++;
94

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

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

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

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

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

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

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

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

136
static int process_sample_event(event_t *event, struct perf_session *session)
137
{
138
	struct addr_location al;
139

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

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

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

	return 0;
}

158
static int parse_line(FILE *file, struct hist_entry *he, u64 len)
159
{
160
	struct symbol *sym = he->sym;
161
	char *line = NULL, *tmp, *tmp2;
162 163
	static const char *prev_line;
	static const char *prev_color;
164 165
	unsigned int offset;
	size_t line_len;
166
	u64 start;
167
	s64 line_ip;
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 202
	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;
	}

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

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

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

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

224
		color = get_percent_color(percent);
225

226 227 228 229 230 231 232 233 234 235 236 237 238 239
		/*
		 * 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;
			}
		}

240 241 242 243 244 245 246 247 248 249 250 251 252
		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;
}

253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
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);
}

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

	if (!sym_ext)
		return;

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

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

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

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

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

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

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

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

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

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

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

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

	next:
		pclose(fp);
	}
}

344
static void print_summary(const char *filename)
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
{
	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;
360
		const char *color;
361 362 363 364
		char *path;

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

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

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

	if (!filename)
		return;
385

386 387 388 389 390 391
	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));

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

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

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

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

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

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

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

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

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

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

433
static void perf_session__find_annotations(struct perf_session *self)
434 435 436
{
	struct rb_node *nd;

437
	for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) {
438
		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
439
		struct sym_priv *priv;
440

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

444
		priv = symbol__priv(he->sym);
445 446 447 448 449 450 451 452 453 454
		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;
455 456 457
	}
}

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

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

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

474
	ret = perf_session__process_events(session, &event_ops);
L
Li Zefan 已提交
475
	if (ret)
476
		goto out_delete;
477

478 479
	if (dump_trace) {
		event__print_totals();
480
		goto out_delete;
481
	}
482

483
	if (verbose > 3)
484
		perf_session__fprintf(session, stdout);
485

486
	if (verbose > 2)
487 488
		dsos__fprintf(stdout);

489
	perf_session__collapse_resort(session);
490
	perf_session__output_resort(session, session->event_total[0]);
491
	perf_session__find_annotations(session);
492 493
out_delete:
	perf_session__delete(session);
494

L
Li Zefan 已提交
495
	return ret;
496 497 498 499 500 501 502 503 504 505
}

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"),
506
	OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
507
		    "symbol to annotate"),
508
	OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
509 510 511 512
	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"),
513 514 515
	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
		   "file", "vmlinux pathname"),
	OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
516
		    "load module symbols - WARNING: use only with -k and LIVE kernel"),
517 518
	OPT_BOOLEAN('l', "print-line", &print_line,
		    "print matching source lines (may be slow)"),
519 520
	OPT_BOOLEAN('P', "full-paths", &full_paths,
		    "Don't shorten the displayed pathnames"),
521 522 523
	OPT_END()
};

524
int cmd_annotate(int argc, const char **argv, const char *prefix __used)
525
{
526 527
	if (symbol__init(&symbol_conf) < 0)
		return -1;
528 529 530

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

531
	setup_sorting(annotate_usage, options);
532

533 534 535 536 537 538 539 540 541 542 543
	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];
	}

544 545
	setup_pager();

546 547 548 549 550 551
	if (field_sep && *field_sep == '.') {
		fputs("'.' is the only non valid --field-separator argument\n",
				stderr);
		exit(129);
	}

552 553
	return __cmd_annotate();
}