builtin-annotate.c 11.7 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
	start = map__rip_2objdump(he->map, sym->start);
193

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__rip_2objdump(map, sym->start),
		map__rip_2objdump(map, sym->end),
402
		filename, filename);
403 404 405 406 407 408 409 410 411

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

522
	setup_sorting(annotate_usage, options);
523

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

535 536
	setup_pager();

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

542 543
	return __cmd_annotate();
}