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

78 79 80
/*
 * collect histogram counts
 */
81
static void hist_hit(struct hist_entry *he, u64 ip)
82
{
83 84
	unsigned int sym_size, offset;
	struct symbol *sym = he->sym;
85 86
	struct sym_priv *priv;
	struct sym_hist *h;
87

88
	he->count++;
89

90 91 92
	if (!sym || !he->map)
		return;

93
	priv = symbol__priv(sym);
94
	if (!priv->hist)
95
		return;
96

97 98
	sym_size = sym->end - sym->start;
	offset = ip - sym->start;
99

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

102 103
	if (offset >= sym_size)
		return;
104

105 106 107
	h = priv->hist;
	h->sum++;
	h->ip[offset]++;
108

109 110
	pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->sym->start,
		  he->sym->name, ip, ip - he->sym->start, h->ip[offset]);
111 112
}

113 114
static int perf_session__add_hist_entry(struct perf_session *self,
					struct addr_location *al, u64 count)
115
{
116
	bool hit;
117 118
	struct hist_entry *he = __perf_session__add_hist_entry(self, al, NULL,
							       count, &hit);
119
	if (he == NULL)
120
		return -ENOMEM;
121
	hist_hit(he, al->addr);
122 123 124
	return 0;
}

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

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

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

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

	return 0;
}

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

192 193
	start = he->map->unmap_ip(he->map, sym->start);

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

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

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

213
		color = get_percent_color(percent);
214

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

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

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

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

	if (!sym_ext)
		return;

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

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

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

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

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

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

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

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

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

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

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

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

	next:
		pclose(fp);
	}
}

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

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

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

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

	if (!filename)
		return;
374

375 376 377
	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));
378

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

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

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

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

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

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

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

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

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

	pclose(file);
416
	if (print_line)
417
		free_source_line(he, len);
418 419
}

420
static void perf_session__find_annotations(struct perf_session *self)
421 422 423
{
	struct rb_node *nd;

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

428 429
		if (he->sym == NULL)
			continue;
430

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

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

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

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

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

465 466
	if (dump_trace) {
		event__print_totals();
467
		goto out_delete;
468
	}
469

470
	if (verbose > 3)
471
		perf_session__fprintf(session, stdout);
472

473
	if (verbose > 2)
474 475
		dsos__fprintf(stdout);

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

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

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

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

515 516 517 518
	symbol_conf.priv_size = sizeof(struct sym_priv);
	symbol_conf.try_vmlinux_path = true;

	if (symbol__init() < 0)
519
		return -1;
520

521
	setup_sorting(annotate_usage, options);
522

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

534 535
	setup_pager();

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

541 542
	return __cmd_annotate();
}