annotate.c 19.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
 *
 * Parts came from builtin-annotate.c, see those files for further
 * copyright notes.
 *
 * Released under the GPL v2. (and only v2, not any later version)
 */

#include "util.h"
#include "build-id.h"
#include "color.h"
#include "cache.h"
#include "symbol.h"
#include "debug.h"
#include "annotate.h"
17
#include <pthread.h>
18

19 20
const char 	*disassembler_style;

21
static int call__parse(struct ins_operands *ops)
22
{
23 24
	char *endptr, *tok, *name;

25
	ops->target.addr = strtoull(ops->raw, &endptr, 16);
26 27 28 29 30 31 32 33 34 35 36 37

	name = strchr(endptr, '<');
	if (name == NULL)
		goto indirect_call;

	name++;

	tok = strchr(name, '>');
	if (tok == NULL)
		return -1;

	*tok = '\0';
38
	ops->target.name = strdup(name);
39 40
	*tok = '>';

41
	return ops->target.name == NULL ? -1 : 0;
42 43 44 45 46 47

indirect_call:
	tok = strchr(endptr, '*');
	if (tok == NULL)
		return -1;

48
	ops->target.addr = strtoull(tok + 1, NULL, 16);
49 50 51
	return 0;
}

52 53 54 55 56 57
static int call__scnprintf(struct ins *ins, char *bf, size_t size,
			   struct ins_operands *ops, bool addrs)
{
	if (addrs)
		return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->raw);

58 59
	if (ops->target.name)
		return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->target.name);
60

61
	return scnprintf(bf, size, "%-6.6s *%" PRIx64, ins->name, ops->target.addr);
62 63
}

64
static struct ins_ops call_ops = {
65 66
	.parse	   = call__parse,
	.scnprintf = call__scnprintf,
67 68 69 70 71 72 73
};

bool ins__is_call(const struct ins *ins)
{
	return ins->ops == &call_ops;
}

74
static int jump__parse(struct ins_operands *ops)
75
{
76
	const char *s = strchr(ops->raw, '+');
77 78 79 80

	if (s++ == NULL)
		return -1;

81
	ops->target.offset = strtoll(s, NULL, 16);
82 83 84
	return 0;
}

85 86
static int jump__scnprintf(struct ins *ins, char *bf, size_t size,
			   struct ins_operands *ops, bool addrs)
87
{
88 89
	if (addrs)
		return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->raw);
90

91
	return scnprintf(bf, size, "%-6.6s %" PRIx64, ins->name, ops->target.offset);
92 93
}

94
static struct ins_ops jump_ops = {
95 96
	.parse	   = jump__parse,
	.scnprintf = jump__scnprintf,
97 98 99 100 101 102 103 104 105 106 107
};

bool ins__is_jump(const struct ins *ins)
{
	return ins->ops == &jump_ops;
}

/*
 * Must be sorted by name!
 */
static struct ins instructions[] = {
108 109
	{ .name = "call",  .ops  = &call_ops, },
	{ .name = "callq", .ops  = &call_ops, },
110
	{ .name = "ja",	   .ops  = &jump_ops, },
111 112 113 114 115
	{ .name = "jae",   .ops  = &jump_ops, },
	{ .name = "jb",	   .ops  = &jump_ops, },
	{ .name = "jbe",   .ops  = &jump_ops, },
	{ .name = "jc",	   .ops  = &jump_ops, },
	{ .name = "jcxz",  .ops  = &jump_ops, },
116
	{ .name = "je",	   .ops  = &jump_ops, },
117 118 119 120 121
	{ .name = "jecxz", .ops  = &jump_ops, },
	{ .name = "jg",	   .ops  = &jump_ops, },
	{ .name = "jge",   .ops  = &jump_ops, },
	{ .name = "jl",    .ops  = &jump_ops, },
	{ .name = "jle",   .ops  = &jump_ops, },
122 123
	{ .name = "jmp",   .ops  = &jump_ops, },
	{ .name = "jmpq",  .ops  = &jump_ops, },
124 125 126 127 128
	{ .name = "jna",   .ops  = &jump_ops, },
	{ .name = "jnae",  .ops  = &jump_ops, },
	{ .name = "jnb",   .ops  = &jump_ops, },
	{ .name = "jnbe",  .ops  = &jump_ops, },
	{ .name = "jnc",   .ops  = &jump_ops, },
129
	{ .name = "jne",   .ops  = &jump_ops, },
130 131 132 133 134 135 136 137 138 139 140 141 142
	{ .name = "jng",   .ops  = &jump_ops, },
	{ .name = "jnge",  .ops  = &jump_ops, },
	{ .name = "jnl",   .ops  = &jump_ops, },
	{ .name = "jnle",  .ops  = &jump_ops, },
	{ .name = "jno",   .ops  = &jump_ops, },
	{ .name = "jnp",   .ops  = &jump_ops, },
	{ .name = "jns",   .ops  = &jump_ops, },
	{ .name = "jnz",   .ops  = &jump_ops, },
	{ .name = "jo",	   .ops  = &jump_ops, },
	{ .name = "jp",	   .ops  = &jump_ops, },
	{ .name = "jpe",   .ops  = &jump_ops, },
	{ .name = "jpo",   .ops  = &jump_ops, },
	{ .name = "jrcxz", .ops  = &jump_ops, },
143
	{ .name = "js",	   .ops  = &jump_ops, },
144
	{ .name = "jz",	   .ops  = &jump_ops, },
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
};

static int ins__cmp(const void *name, const void *insp)
{
	const struct ins *ins = insp;

	return strcmp(name, ins->name);
}

static struct ins *ins__find(const char *name)
{
	const int nmemb = ARRAY_SIZE(instructions);

	return bsearch(name, instructions, nmemb, sizeof(struct ins), ins__cmp);
}

161
int symbol__annotate_init(struct map *map __used, struct symbol *sym)
162 163
{
	struct annotation *notes = symbol__annotation(sym);
164 165 166
	pthread_mutex_init(&notes->lock, NULL);
	return 0;
}
167

168
int symbol__alloc_hist(struct symbol *sym)
169 170
{
	struct annotation *notes = symbol__annotation(sym);
171
	const size_t size = symbol__size(sym);
172
	size_t sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(u64));
173

174
	notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist);
175 176 177
	if (notes->src == NULL)
		return -1;
	notes->src->sizeof_sym_hist = sizeof_sym_hist;
178
	notes->src->nr_histograms   = symbol_conf.nr_events;
179 180
	INIT_LIST_HEAD(&notes->src->source);
	return 0;
181 182
}

183 184 185 186
void symbol__annotate_zero_histograms(struct symbol *sym)
{
	struct annotation *notes = symbol__annotation(sym);

187 188 189 190 191
	pthread_mutex_lock(&notes->lock);
	if (notes->src != NULL)
		memset(notes->src->histograms, 0,
		       notes->src->nr_histograms * notes->src->sizeof_sym_hist);
	pthread_mutex_unlock(&notes->lock);
192 193
}

194 195
int symbol__inc_addr_samples(struct symbol *sym, struct map *map,
			     int evidx, u64 addr)
196
{
197
	unsigned offset;
198 199 200 201
	struct annotation *notes;
	struct sym_hist *h;

	notes = symbol__annotation(sym);
202
	if (notes->src == NULL)
203 204 205 206
		return -ENOMEM;

	pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr));

207 208
	if (addr < sym->start || addr > sym->end)
		return -ERANGE;
209

210 211
	offset = addr - sym->start;
	h = annotation__histogram(notes, evidx);
212 213 214 215
	h->sum++;
	h->addr[offset]++;

	pr_debug3("%#" PRIx64 " %s: period++ [addr: %#" PRIx64 ", %#" PRIx64
216 217
		  ", evidx=%d] => %" PRIu64 "\n", sym->start, sym->name,
		  addr, addr - sym->start, evidx, h->addr[offset]);
218 219 220
	return 0;
}

221 222 223 224 225 226 227 228 229 230
static void disasm_line__init_ins(struct disasm_line *dl)
{
	dl->ins = ins__find(dl->name);

	if (dl->ins == NULL)
		return;

	if (!dl->ins->ops)
		return;

231 232
	if (dl->ins->ops->parse)
		dl->ins->ops->parse(&dl->ops);
233 234
}

235
static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privsize)
236
{
237
	struct disasm_line *dl = zalloc(sizeof(*dl) + privsize);
238

239 240 241 242
	if (dl != NULL) {
		dl->offset = offset;
		dl->line = strdup(line);
		if (dl->line == NULL)
243
			goto out_delete;
244 245 246 247 248 249 250 251 252 253

		if (offset != -1) {
			char *name = dl->line, tmp;

			while (isspace(name[0]))
				++name;

			if (name[0] == '\0')
				goto out_delete;

254
			dl->ops.raw = name + 1;
255

256 257 258
			while (dl->ops.raw[0] != '\0' &&
			       !isspace(dl->ops.raw[0]))
				++dl->ops.raw;
259

260 261
			tmp = dl->ops.raw[0];
			dl->ops.raw[0] = '\0';
262 263 264 265 266
			dl->name = strdup(name);

			if (dl->name == NULL)
				goto out_free_line;

267
			dl->ops.raw[0] = tmp;
268

269 270 271 272
			if (dl->ops.raw[0] != '\0') {
				dl->ops.raw++;
				while (isspace(dl->ops.raw[0]))
					++dl->ops.raw;
273
			}
274 275

			disasm_line__init_ins(dl);
276
		}
277 278
	}

279
	return dl;
280 281 282

out_free_line:
	free(dl->line);
283
out_delete:
284
	free(dl);
285
	return NULL;
286 287
}

288
void disasm_line__free(struct disasm_line *dl)
289
{
290
	free(dl->line);
291
	free(dl->name);
292
	free(dl->ops.target.name);
293
	free(dl);
294 295
}

296
static void disasm__add(struct list_head *head, struct disasm_line *line)
297 298 299 300
{
	list_add_tail(&line->node, head);
}

301
struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos)
302 303 304 305 306 307 308 309
{
	list_for_each_entry_continue(pos, head, node)
		if (pos->offset >= 0)
			return pos;

	return NULL;
}

310 311 312
static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start,
		      int evidx, u64 len, int min_pcnt, int printed,
		      int max_lines, struct disasm_line *queue)
313 314 315 316
{
	static const char *prev_line;
	static const char *prev_color;

317
	if (dl->offset != -1) {
318 319 320 321 322
		const char *path = NULL;
		unsigned int hits = 0;
		double percent = 0.0;
		const char *color;
		struct annotation *notes = symbol__annotation(sym);
323
		struct source_line *src_line = notes->src->lines;
324
		struct sym_hist *h = annotation__histogram(notes, evidx);
325
		s64 offset = dl->offset;
326
		const u64 addr = start + offset;
327
		struct disasm_line *next;
328

329
		next = disasm__get_next_ip_line(&notes->src->source, dl);
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345

		while (offset < (s64)len &&
		       (next == NULL || offset < next->offset)) {
			if (src_line) {
				if (path == NULL)
					path = src_line[offset].path;
				percent += src_line[offset].percent;
			} else
				hits += h->addr[offset];

			++offset;
		}

		if (src_line == NULL && h->sum)
			percent = 100.0 * hits / h->sum;

346
		if (percent < min_pcnt)
347 348
			return -1;

349
		if (max_lines && printed >= max_lines)
350
			return 1;
351

352 353
		if (queue != NULL) {
			list_for_each_entry_from(queue, &notes->src->source, node) {
354
				if (queue == dl)
355
					break;
356
				disasm_line__print(queue, sym, start, evidx, len,
357 358 359 360
						    0, 0, 1, NULL);
			}
		}

361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
		color = get_percent_color(percent);

		/*
		 * Also color the filename and line if needed, with
		 * the same color than the percentage. Don't print it
		 * twice for close colored addr 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;
			}
		}

		color_fprintf(stdout, color, " %7.2f", percent);
		printf(" :	");
379
		color_fprintf(stdout, PERF_COLOR_MAGENTA, "  %" PRIx64 ":", addr);
380
		color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line);
381
	} else if (max_lines && printed >= max_lines)
382 383
		return 1;
	else {
384 385 386
		if (queue)
			return -1;

387
		if (!*dl->line)
388 389
			printf("         :\n");
		else
390
			printf("         :	%s\n", dl->line);
391
	}
392 393

	return 0;
394 395
}

396 397
static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
				      FILE *file, size_t privsize)
398
{
399
	struct annotation *notes = symbol__annotation(sym);
400
	struct disasm_line *dl;
401
	char *line = NULL, *parsed_line, *tmp, *tmp2, *c;
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
	size_t line_len;
	s64 line_ip, offset = -1;

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

	if (!line)
		return -1;

	while (line_len != 0 && isspace(line[line_len - 1]))
		line[--line_len] = '\0';

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

	line_ip = -1;
N
Namhyung Kim 已提交
419
	parsed_line = line;
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446

	/*
	 * 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 != ':' || tmp == tmp2 || tmp2[1] == '\0')
			line_ip = -1;
	}

	if (line_ip != -1) {
		u64 start = map__rip_2objdump(map, sym->start),
		    end = map__rip_2objdump(map, sym->end);

		offset = line_ip - start;
		if (offset < 0 || (u64)line_ip > end)
			offset = -1;
447 448
		else
			parsed_line = tmp2 + 1;
N
Namhyung Kim 已提交
449
	}
450

451
	dl = disasm_line__new(offset, parsed_line, privsize);
452 453
	free(line);

454
	if (dl == NULL)
455
		return -1;
456

457
	disasm__add(&notes->src->source, dl);
458 459 460 461

	return 0;
}

462
int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize)
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
{
	struct dso *dso = map->dso;
	char *filename = dso__build_id_filename(dso, NULL, 0);
	bool free_filename = true;
	char command[PATH_MAX * 2];
	FILE *file;
	int err = 0;
	char symfs_filename[PATH_MAX];

	if (filename) {
		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
			 symbol_conf.symfs, filename);
	}

	if (filename == NULL) {
		if (dso->has_build_id) {
			pr_err("Can't annotate %s: not enough memory\n",
			       sym->name);
			return -ENOMEM;
		}
		goto fallback;
	} else if (readlink(symfs_filename, command, sizeof(command)) < 0 ||
		   strstr(command, "[kernel.kallsyms]") ||
		   access(symfs_filename, R_OK)) {
		free(filename);
fallback:
		/*
		 * If we don't have build-ids or the build-id file isn't in the
		 * cache, or is just a kallsyms file, well, lets hope that this
		 * DSO is the same as when 'perf record' ran.
		 */
		filename = dso->long_name;
		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
			 symbol_conf.symfs, filename);
		free_filename = false;
	}

500
	if (dso->symtab_type == SYMTAB__KALLSYMS) {
501 502 503
		char bf[BUILD_ID_SIZE * 2 + 16] = " with build id ";
		char *build_id_msg = NULL;

504 505
		if (dso->annotate_warned)
			goto out_free_filename;
506 507 508 509 510 511

		if (dso->has_build_id) {
			build_id__sprintf(dso->build_id,
					  sizeof(dso->build_id), bf + 15);
			build_id_msg = bf;
		}
512 513
		err = -ENOENT;
		dso->annotate_warned = 1;
514 515 516 517 518
		pr_err("Can't annotate %s:\n\n"
		       "No vmlinux file%s\nwas found in the path.\n\n"
		       "Please use:\n\n"
		       "  perf buildid-cache -av vmlinux\n\n"
		       "or:\n\n"
519
		       "  --vmlinux vmlinux\n",
520
		       sym->name, build_id_msg ?: "");
521 522 523 524 525 526 527 528 529 530 531
		goto out_free_filename;
	}

	pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__,
		 filename, sym->name, map->unmap_ip(map, sym->start),
		 map->unmap_ip(map, sym->end));

	pr_debug("annotating [%p] %30s : [%p] %30s\n",
		 dso, dso->long_name, sym, sym->name);

	snprintf(command, sizeof(command),
532
		 "objdump %s%s --start-address=0x%016" PRIx64
533 534
		 " --stop-address=0x%016" PRIx64
		 " -d %s %s -C %s|grep -v %s|expand",
535 536
		 disassembler_style ? "-M " : "",
		 disassembler_style ? disassembler_style : "",
537
		 map__rip_2objdump(map, sym->start),
I
Ingo Molnar 已提交
538
		 map__rip_2objdump(map, sym->end+1),
539 540
		 symbol_conf.annotate_asm_raw ? "" : "--no-show-raw",
		 symbol_conf.annotate_src ? "-S" : "",
541 542 543 544 545 546 547 548 549
		 symfs_filename, filename);

	pr_debug("Executing: %s\n", command);

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

	while (!feof(file))
550
		if (symbol__parse_objdump_line(sym, map, file, privsize) < 0)
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
			break;

	pclose(file);
out_free_filename:
	if (free_filename)
		free(filename);
	return err;
}

static void insert_source_line(struct rb_root *root, struct source_line *src_line)
{
	struct source_line *iter;
	struct rb_node **p = &root->rb_node;
	struct rb_node *parent = NULL;

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

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

	rb_link_node(&src_line->node, parent, p);
	rb_insert_color(&src_line->node, root);
}

static void symbol__free_source_line(struct symbol *sym, int len)
{
	struct annotation *notes = symbol__annotation(sym);
583
	struct source_line *src_line = notes->src->lines;
584 585 586 587 588 589
	int i;

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

	free(src_line);
590
	notes->src->lines = NULL;
591 592 593 594
}

/* Get the filename:line for the colored entries */
static int symbol__get_source_line(struct symbol *sym, struct map *map,
595
				   int evidx, struct rb_root *root, int len,
596 597 598 599 600 601 602
				   const char *filename)
{
	u64 start;
	int i;
	char cmd[PATH_MAX * 2];
	struct source_line *src_line;
	struct annotation *notes = symbol__annotation(sym);
603
	struct sym_hist *h = annotation__histogram(notes, evidx);
604 605 606 607

	if (!h->sum)
		return 0;

608 609
	src_line = notes->src->lines = calloc(len, sizeof(struct source_line));
	if (!notes->src->lines)
610 611
		return -1;

612
	start = map__rip_2objdump(map, sym->start);
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675

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

		src_line[i].percent = 100.0 * h->addr[i] / h->sum;
		if (src_line[i].percent <= 0.5)
			continue;

		offset = start + i;
		sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset);
		fp = popen(cmd, "r");
		if (!fp)
			continue;

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

		src_line[i].path = malloc(sizeof(char) * line_len + 1);
		if (!src_line[i].path)
			goto next;

		strcpy(src_line[i].path, path);
		insert_source_line(root, &src_line[i]);

	next:
		pclose(fp);
	}

	return 0;
}

static void print_summary(struct rb_root *root, const char *filename)
{
	struct source_line *src_line;
	struct rb_node *node;

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

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

	node = rb_first(root);
	while (node) {
		double percent;
		const char *color;
		char *path;

		src_line = rb_entry(node, struct source_line, node);
		percent = src_line->percent;
		color = get_percent_color(percent);
		path = src_line->path;

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

676
static void symbol__annotate_hits(struct symbol *sym, int evidx)
677 678
{
	struct annotation *notes = symbol__annotation(sym);
679
	struct sym_hist *h = annotation__histogram(notes, evidx);
680
	u64 len = symbol__size(sym), offset;
681 682 683 684 685 686 687 688

	for (offset = 0; offset < len; ++offset)
		if (h->addr[offset] != 0)
			printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2,
			       sym->start + offset, h->addr[offset]);
	printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum);
}

689
int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
690 691
			    bool full_paths, int min_pcnt, int max_lines,
			    int context)
692 693 694
{
	struct dso *dso = map->dso;
	const char *filename = dso->long_name, *d_filename;
695
	struct annotation *notes = symbol__annotation(sym);
696
	struct disasm_line *pos, *queue = NULL;
697
	u64 start = map__rip_2objdump(map, sym->start);
698
	int printed = 2, queue_len = 0;
699
	int more = 0;
700 701 702 703 704 705 706
	u64 len;

	if (full_paths)
		d_filename = filename;
	else
		d_filename = basename(filename);

707
	len = symbol__size(sym);
708 709 710 711 712

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

	if (verbose)
713
		symbol__annotate_hits(sym, evidx);
714

715
	list_for_each_entry(pos, &notes->src->source, node) {
716 717 718 719 720
		if (context && queue == NULL) {
			queue = pos;
			queue_len = 0;
		}

721
		switch (disasm_line__print(pos, sym, start, evidx, len,
722 723
					    min_pcnt, printed, max_lines,
					    queue)) {
724 725
		case 0:
			++printed;
726 727 728 729 730
			if (context) {
				printed += queue_len;
				queue = NULL;
				queue_len = 0;
			}
731 732 733 734
			break;
		case 1:
			/* filtered by max_lines */
			++more;
735
			break;
736 737
		case -1:
		default:
738 739 740 741 742 743 744 745 746 747
			/*
			 * Filtered by min_pcnt or non IP lines when
			 * context != 0
			 */
			if (!context)
				break;
			if (queue_len == context)
				queue = list_entry(queue->node.next, typeof(*queue), node);
			else
				++queue_len;
748 749 750 751 752 753
			break;
		}
	}

	return more;
}
754

755 756 757 758 759
void symbol__annotate_zero_histogram(struct symbol *sym, int evidx)
{
	struct annotation *notes = symbol__annotation(sym);
	struct sym_hist *h = annotation__histogram(notes, evidx);

760
	memset(h, 0, notes->src->sizeof_sym_hist);
761 762
}

763
void symbol__annotate_decay_histogram(struct symbol *sym, int evidx)
764 765 766
{
	struct annotation *notes = symbol__annotation(sym);
	struct sym_hist *h = annotation__histogram(notes, evidx);
767
	int len = symbol__size(sym), offset;
768 769

	h->sum = 0;
770 771 772
	for (offset = 0; offset < len; ++offset) {
		h->addr[offset] = h->addr[offset] * 7 / 8;
		h->sum += h->addr[offset];
773 774 775
	}
}

776
void disasm__purge(struct list_head *head)
777
{
778
	struct disasm_line *pos, *n;
779 780 781

	list_for_each_entry_safe(pos, n, head, node) {
		list_del(&pos->node);
782
		disasm_line__free(pos);
783 784 785
	}
}

786 787 788 789 790 791 792 793 794
static size_t disasm_line__fprintf(struct disasm_line *dl, FILE *fp)
{
	size_t printed;

	if (dl->offset == -1)
		return fprintf(fp, "%s\n", dl->line);

	printed = fprintf(fp, "%#" PRIx64 " %s", dl->offset, dl->name);

795
	if (dl->ops.raw[0] != '\0') {
796
		printed += fprintf(fp, "%.*s %s\n", 6 - (int)printed, " ",
797
				   dl->ops.raw);
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813
	}

	return printed + fprintf(fp, "\n");
}

size_t disasm__fprintf(struct list_head *head, FILE *fp)
{
	struct disasm_line *pos;
	size_t printed = 0;

	list_for_each_entry(pos, head, node)
		printed += disasm_line__fprintf(pos, fp);

	return printed;
}

814 815 816 817 818 819 820 821 822
int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
			 bool print_lines, bool full_paths, int min_pcnt,
			 int max_lines)
{
	struct dso *dso = map->dso;
	const char *filename = dso->long_name;
	struct rb_root source_line = RB_ROOT;
	u64 len;

823
	if (symbol__annotate(sym, map, 0) < 0)
824 825
		return -1;

826
	len = symbol__size(sym);
827 828 829 830 831

	if (print_lines) {
		symbol__get_source_line(sym, map, evidx, &source_line,
					len, filename);
		print_summary(&source_line, filename);
832 833
	}

834
	symbol__annotate_printf(sym, map, evidx, full_paths,
835
				min_pcnt, max_lines, 0);
836 837 838
	if (print_lines)
		symbol__free_source_line(sym, len);

839
	disasm__purge(&symbol__annotation(sym)->src->source);
840

841 842
	return 0;
}