builtin-annotate.c 11.6 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 54 55
struct sym_priv {
	struct sym_hist	*hist;
	struct sym_ext	*ext;
};

static const char *sym_hist_filter;

56
static int sym__alloc_hist(struct symbol *self)
57
{
58 59 60 61 62 63
	struct sym_priv *priv = symbol__priv(self);
	const int size = (sizeof(*priv->hist) +
			  (self->end - self->start) * sizeof(u64));

	priv->hist = zalloc(size);
	return priv->hist == NULL ? -1 : 0;
64
}
65

66 67 68
/*
 * collect histogram counts
 */
69
static int annotate__hist_hit(struct hist_entry *he, u64 ip)
70
{
71 72
	unsigned int sym_size, offset;
	struct symbol *sym = he->sym;
73 74
	struct sym_priv *priv;
	struct sym_hist *h;
75

76
	he->count++;
77

78
	if (!sym || !he->map)
79
		return 0;
80

81
	priv = symbol__priv(sym);
82 83
	if (priv->hist == NULL && sym__alloc_hist(sym) < 0)
		return -ENOMEM;
84

85 86
	sym_size = sym->end - sym->start;
	offset = ip - sym->start;
87

88
	pr_debug3("%s: ip=%#Lx\n", __func__, he->map->unmap_ip(he->map, ip));
89

90
	if (offset >= sym_size)
91
		return 0;
92

93 94 95
	h = priv->hist;
	h->sum++;
	h->ip[offset]++;
96

97 98
	pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->sym->start,
		  he->sym->name, ip, ip - he->sym->start, h->ip[offset]);
99
	return 0;
100 101
}

102 103
static int perf_session__add_hist_entry(struct perf_session *self,
					struct addr_location *al, u64 count)
104
{
105
	bool hit;
106 107 108 109 110 111 112 113 114 115 116 117 118 119
	struct hist_entry *he;

	if (sym_hist_filter != NULL &&
	    (al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) {
		/* We're only interested in a symbol named sym_hist_filter */
		if (al->sym != NULL) {
			rb_erase(&al->sym->rb_node,
				 &al->map->dso->symbols[al->map->type]);
			symbol__delete(al->sym);
		}
		return 0;
	}

	he = __perf_session__add_hist_entry(self, al, NULL, count, &hit);
120
	if (he == NULL)
121
		return -ENOMEM;
122 123

	return annotate__hist_hit(he, al->addr);
124 125
}

126
static int process_sample_event(event_t *event, struct perf_session *session)
127
{
128
	struct addr_location al;
129

130 131
	dump_printf("(IP, %d): %d: %#Lx\n", event->header.misc,
		    event->ip.pid, event->ip.ip);
132

133
	if (event__preprocess_sample(event, session, &al, NULL) < 0) {
134 135
		pr_warning("problem processing %d event, skipping it.\n",
			   event->header.type);
136 137 138
		return -1;
	}

139
	if (!al.filtered && perf_session__add_hist_entry(session, &al, 1)) {
140 141
		pr_warning("problem incrementing symbol count, "
			   "skipping event\n");
142
		return -1;
143 144 145 146 147
	}

	return 0;
}

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

193
	start = map__rip_2objdump(he->map, sym->start);
194

195
	if (line_ip != -1) {
196
		const char *path = NULL;
197 198
		unsigned int hits = 0;
		double percent = 0.0;
199
		const char *color;
200
		struct sym_priv *priv = symbol__priv(sym);
201 202
		struct sym_ext *sym_ext = priv->ext;
		struct sym_hist *h = priv->hist;
203

204
		offset = line_ip - start;
205
		if (offset < len)
206
			hits = h->ip[offset];
207

208
		if (offset < len && sym_ext) {
209 210
			path = sym_ext[offset].path;
			percent = sym_ext[offset].percent;
211 212
		} else if (h->sum)
			percent = 100.0 * hits / h->sum;
213

214
		color = get_percent_color(percent);
215

216 217 218 219 220 221 222 223 224 225 226 227 228 229
		/*
		 * 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;
			}
		}

230 231 232 233 234 235 236 237 238 239 240 241 242
		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;
}

243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
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);
}

265
static void free_source_line(struct hist_entry *he, int len)
266
{
267
	struct sym_priv *priv = symbol__priv(he->sym);
268
	struct sym_ext *sym_ext = priv->ext;
269 270 271 272 273 274 275 276 277
	int i;

	if (!sym_ext)
		return;

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

278
	priv->ext = NULL;
279
	root_sym_ext = RB_ROOT;
280 281 282
}

/* Get the filename:line for the colored entries */
283
static void
284
get_source_line(struct hist_entry *he, int len, const char *filename)
285
{
286 287
	struct symbol *sym = he->sym;
	u64 start;
288 289 290
	int i;
	char cmd[PATH_MAX * 2];
	struct sym_ext *sym_ext;
291
	struct sym_priv *priv = symbol__priv(sym);
292
	struct sym_hist *h = priv->hist;
293

294
	if (!h->sum)
295 296
		return;

297 298
	sym_ext = priv->ext = calloc(len, sizeof(struct sym_ext));
	if (!priv->ext)
299 300
		return;

301
	start = he->map->unmap_ip(he->map, sym->start);
302 303 304 305

	for (i = 0; i < len; i++) {
		char *path = NULL;
		size_t line_len;
306
		u64 offset;
307 308
		FILE *fp;

309
		sym_ext[i].percent = 100.0 * h->ip[i] / h->sum;
310 311 312
		if (sym_ext[i].percent <= 0.5)
			continue;

313
		offset = start + i;
314
		sprintf(cmd, "addr2line -e %s %016llx", filename, offset);
315 316 317 318 319 320 321
		fp = popen(cmd, "r");
		if (!fp)
			continue;

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

322
		sym_ext[i].path = malloc(sizeof(char) * line_len + 1);
323 324 325 326
		if (!sym_ext[i].path)
			goto next;

		strcpy(sym_ext[i].path, path);
327
		insert_source_line(&sym_ext[i]);
328 329 330 331 332 333

	next:
		pclose(fp);
	}
}

334
static void print_summary(const char *filename)
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
{
	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;
350
		const char *color;
351 352 353 354
		char *path;

		sym_ext = rb_entry(node, struct sym_ext, node);
		percent = sym_ext->percent;
355
		color = get_percent_color(percent);
356 357 358 359 360 361 362
		path = sym_ext->path;

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

363
static void annotate_sym(struct hist_entry *he)
364
{
365 366 367
	struct map *map = he->map;
	struct dso *dso = map->dso;
	struct symbol *sym = he->sym;
368 369
	const char *filename = dso->long_name, *d_filename;
	u64 len;
370 371 372 373 374
	char command[PATH_MAX*2];
	FILE *file;

	if (!filename)
		return;
375

376 377 378
	pr_debug("%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));
379

380 381 382 383
	if (full_paths)
		d_filename = filename;
	else
		d_filename = basename(filename);
384 385 386

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

387
	if (print_line) {
388
		get_source_line(he, len, filename);
389 390 391 392
		print_summary(filename);
	}

	printf("\n\n------------------------------------------------\n");
393
	printf(" Percent |	Source code & Disassembly of %s\n", d_filename);
394 395 396
	printf("------------------------------------------------\n");

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

400
	sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s",
401 402
		map__rip_2objdump(map, sym->start),
		map__rip_2objdump(map, sym->end),
403
		filename, filename);
404 405 406 407 408 409 410 411 412

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

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

	while (!feof(file)) {
413
		if (parse_line(file, he, len) < 0)
414 415 416 417
			break;
	}

	pclose(file);
418
	if (print_line)
419
		free_source_line(he, len);
420 421
}

422
static void perf_session__find_annotations(struct perf_session *self)
423 424 425
{
	struct rb_node *nd;

426
	for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) {
427
		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
428
		struct sym_priv *priv;
429

430 431
		if (he->sym == NULL)
			continue;
432

433
		priv = symbol__priv(he->sym);
434 435 436 437 438 439 440 441 442 443
		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;
444 445 446
	}
}

447
static struct perf_event_ops event_ops = {
448 449 450 451
	.sample	= process_sample_event,
	.mmap	= event__process_mmap,
	.comm	= event__process_comm,
	.fork	= event__process_task,
L
Li Zefan 已提交
452 453
};

454 455
static int __cmd_annotate(void)
{
L
Li Zefan 已提交
456
	int ret;
457
	struct perf_session *session;
458

459
	session = perf_session__new(input_name, O_RDONLY, force);
460 461 462
	if (session == NULL)
		return -ENOMEM;

463
	ret = perf_session__process_events(session, &event_ops);
L
Li Zefan 已提交
464
	if (ret)
465
		goto out_delete;
466

467 468
	if (dump_trace) {
		event__print_totals();
469
		goto out_delete;
470
	}
471

472
	if (verbose > 3)
473
		perf_session__fprintf(session, stdout);
474

475
	if (verbose > 2)
476 477
		dsos__fprintf(stdout);

478
	perf_session__collapse_resort(session);
479
	perf_session__output_resort(session, session->event_total[0]);
480
	perf_session__find_annotations(session);
481 482
out_delete:
	perf_session__delete(session);
483

L
Li Zefan 已提交
484
	return ret;
485 486 487 488 489 490 491 492 493 494
}

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"),
495
	OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
496
		    "symbol to annotate"),
497
	OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
498 499 500 501
	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"),
502 503 504
	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
		   "file", "vmlinux pathname"),
	OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
505
		    "load module symbols - WARNING: use only with -k and LIVE kernel"),
506 507
	OPT_BOOLEAN('l', "print-line", &print_line,
		    "print matching source lines (may be slow)"),
508 509
	OPT_BOOLEAN('P', "full-paths", &full_paths,
		    "Don't shorten the displayed pathnames"),
510 511 512
	OPT_END()
};

513
int cmd_annotate(int argc, const char **argv, const char *prefix __used)
514
{
515 516
	argc = parse_options(argc, argv, options, annotate_usage, 0);

517 518 519 520
	symbol_conf.priv_size = sizeof(struct sym_priv);
	symbol_conf.try_vmlinux_path = true;

	if (symbol__init() < 0)
521
		return -1;
522

523
	setup_sorting(annotate_usage, options);
524

525 526 527 528 529 530 531 532 533 534 535
	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];
	}

536 537
	setup_pager();

538
	if (field_sep && *field_sep == '.') {
539 540
		pr_err("'.' is the only non valid --field-separator argument\n");
		return -1;
541 542
	}

543 544
	return __cmd_annotate();
}