annotate.c 20.1 KB
Newer Older
1
#include "../../util/util.h"
2 3 4
#include "../browser.h"
#include "../helpline.h"
#include "../libslang.h"
5 6
#include "../ui.h"
#include "../util.h"
7 8 9 10
#include "../../util/annotate.h"
#include "../../util/hist.h"
#include "../../util/sort.h"
#include "../../util/symbol.h"
11
#include <pthread.h>
12
#include <newt.h>
13

14 15 16 17 18 19 20 21
struct browser_disasm_line {
	struct rb_node	rb_node;
	double		percent;
	u32		idx;
	int		idx_asm;
	bool		jump_target;
};

22 23 24
struct annotate_browser {
	struct ui_browser b;
	struct rb_root	  entries;
25
	struct rb_node	  *curr_hot;
26
	struct disasm_line	  *selection;
27
	struct disasm_line  **offsets;
28
	u64		    start;
29 30 31
	int		    nr_asm_entries;
	int		    nr_entries;
	bool		    hide_src_code;
32
	bool		    use_offset;
33
	bool		    searching_backwards;
34
	u8		    offset_width;
35
	char		    search_bf[128];
36 37
};

38
static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
39
{
40
	return (struct browser_disasm_line *)(dl + 1);
41 42
}

43
static bool disasm_line__filter(struct ui_browser *browser, void *entry)
44 45 46 47
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);

	if (ab->hide_src_code) {
48 49
		struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
		return dl->offset == -1;
50 51 52 53 54
	}

	return false;
}

55 56
static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
{
57
	struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
58
	struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
59
	struct browser_disasm_line *bdl = disasm_line__browser(dl);
60
	bool current_entry = ui_browser__is_current_entry(self, row);
61 62 63
	bool change_color = (!ab->hide_src_code &&
			     (!current_entry || (self->use_navkeypressed &&
					         !self->navkeypressed)));
64 65
	int width = self->width;

66
	if (dl->offset != -1) {
67 68
		ui_browser__set_percent_color(self, bdl->percent, current_entry);
		slsmg_printf(" %7.2f ", bdl->percent);
69
	} else {
70
		ui_browser__set_percent_color(self, 0, current_entry);
71 72 73
		slsmg_write_nstring(" ", 9);
	}

74
	ui_browser__write_graph(self, SLSMG_VLINE_CHAR);
75
	SLsmg_write_char(' ');
76 77 78 79 80

	/* The scroll bar isn't being used */
	if (!self->navkeypressed)
		width += 1;

81
	if (dl->offset != -1 && change_color)
82
		ui_browser__set_color(self, HE_COLORSET_CODE);
83

84
	if (!*dl->line)
85
		slsmg_write_nstring(" ", width - 10);
86
	else if (dl->offset == -1)
87
		slsmg_write_nstring(dl->line, width - 10);
88
	else {
89
		char bf[256];
90
		u64 addr = dl->offset;
91
		int printed, color = -1;
92

93 94 95
		if (!ab->use_offset)
			addr += ab->start;

96
		if (!ab->use_offset) {
97
			printed = scnprintf(bf, sizeof(bf), "  %" PRIx64 ":", addr);
98 99
		} else {
			if (bdl->jump_target) {
100
				printed = scnprintf(bf, sizeof(bf), "  %*" PRIx64 ":",
101 102
						    ab->offset_width, addr);
			} else {
103
				printed = scnprintf(bf, sizeof(bf), "  %*s ",
104 105 106
						    ab->offset_width, " ");
			}
		}
107

108 109 110 111 112
		if (change_color)
			color = ui_browser__set_color(self, HE_COLORSET_ADDR);
		slsmg_write_nstring(bf, printed);
		if (change_color)
			ui_browser__set_color(self, color);
113
		if (dl->ins && dl->ins->ops->scnprintf) {
114
			if (ins__is_jump(dl->ins)) {
115
				bool fwd = dl->ops.target.offset > (u64)dl->offset;
116

117 118
				ui_browser__write_graph(self, fwd ? SLSMG_DARROW_CHAR :
								    SLSMG_UARROW_CHAR);
119
				SLsmg_write_char(' ');
120 121 122
			} else if (ins__is_call(dl->ins)) {
				ui_browser__write_graph(self, SLSMG_RARROW_CHAR);
				SLsmg_write_char(' ');
123 124 125 126
			} else {
				slsmg_write_nstring(" ", 2);
			}

127 128
			dl->ins->ops->scnprintf(dl->ins, bf, sizeof(bf), &dl->ops,
						!ab->use_offset);
129 130 131 132
		} else {
			if (strcmp(dl->name, "retq")) {
				slsmg_write_nstring(" ", 2);
			} else {
133
				ui_browser__write_graph(self, SLSMG_LARROW_CHAR);
134 135 136 137 138
				SLsmg_write_char(' ');
			}

			scnprintf(bf, sizeof(bf), "%-6.6s %s", dl->name, dl->ops.raw);
		}
139

140
		slsmg_write_nstring(bf, width - 12 - printed);
141
	}
142

143
	if (current_entry)
144
		ab->selection = dl;
145 146
}

147 148 149 150 151 152 153 154 155 156 157 158
static void annotate_browser__draw_current_loop(struct ui_browser *browser)
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
	struct map_symbol *ms = browser->priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
	struct disasm_line *cursor = ab->selection, *pos = cursor, *target;
	struct browser_disasm_line *bcursor = disasm_line__browser(cursor),
				   *btarget, *bpos;
	unsigned int from, to, start_width = 2;

	list_for_each_entry_from(pos, &notes->src->source, node) {
159 160
		if (!pos->ins || !ins__is_jump(pos->ins) ||
		    !disasm_line__has_offset(pos))
161 162
			continue;

163
		target = ab->offsets[pos->ops.target.offset];
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 193 194 195 196 197 198 199 200
		if (!target)
			continue;

		btarget = disasm_line__browser(target);
		if (btarget->idx <= bcursor->idx)
			goto found;
	}

	return;

found:
	bpos = disasm_line__browser(pos);
	if (ab->hide_src_code) {
		from = bpos->idx_asm;
		to = btarget->idx_asm;
	} else {
		from = (u64)bpos->idx;
		to = (u64)btarget->idx;
	}

	ui_browser__set_color(browser, HE_COLORSET_CODE);

	if (!bpos->jump_target)
		start_width += ab->offset_width + 1;

	__ui_browser__line_arrow_up(browser, 10, from, to, start_width);
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
	int ret = ui_browser__list_head_refresh(browser);

	annotate_browser__draw_current_loop(browser);

	return ret;
}

201
static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
202 203 204
{
	double percent = 0.0;

205
	if (dl->offset != -1) {
206
		int len = sym->end - sym->start;
207
		unsigned int hits = 0;
208
		struct annotation *notes = symbol__annotation(sym);
209
		struct source_line *src_line = notes->src->lines;
210
		struct sym_hist *h = annotation__histogram(notes, evidx);
211 212
		s64 offset = dl->offset;
		struct disasm_line *next;
213

214
		next = disasm__get_next_ip_line(&notes->src->source, dl);
215 216
		while (offset < (s64)len &&
		       (next == NULL || offset < next->offset)) {
217 218
			if (src_line) {
				percent += src_line[offset].percent;
219
			} else
220
				hits += h->addr[offset];
221 222 223

			++offset;
		}
224 225 226 227 228
		/*
 		 * If the percentage wasn't already calculated in
 		 * symbol__get_source_line, do it now:
 		 */
		if (src_line == NULL && h->sum)
229 230 231
			percent = 100.0 * hits / h->sum;
	}

232 233 234
	return percent;
}

235
static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
236
{
237
	struct rb_node **p = &root->rb_node;
238
	struct rb_node *parent = NULL;
239
	struct browser_disasm_line *l;
240 241 242

	while (*p != NULL) {
		parent = *p;
243 244
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
		if (bdl->percent < l->percent)
245 246 247 248
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
249 250
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
251 252
}

253
static void annotate_browser__set_top(struct annotate_browser *self,
254
				      struct disasm_line *pos, u32 idx)
255 256 257 258 259
{
	unsigned back;

	ui_browser__refresh_dimensions(&self->b);
	back = self->b.height / 2;
260
	self->b.top_idx = self->b.index = idx;
261 262

	while (self->b.top_idx != 0 && back != 0) {
263
		pos = list_entry(pos->node.prev, struct disasm_line, node);
264

265
		if (disasm_line__filter(&self->b, &pos->node))
266 267
			continue;

268 269 270 271 272
		--self->b.top_idx;
		--back;
	}

	self->b.top = pos;
273
	self->b.navkeypressed = true;
274 275 276 277 278
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
279
	struct browser_disasm_line *bpos;
280
	struct disasm_line *pos;
281

282 283 284
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
	annotate_browser__set_top(browser, pos, bpos->idx);
285
	browser->curr_hot = nd;
286 287
}

288 289
static void annotate_browser__calc_percent(struct annotate_browser *browser,
					   int evidx)
290
{
291 292
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
293
	struct annotation *notes = symbol__annotation(sym);
294
	struct disasm_line *pos;
295 296 297 298 299 300

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
301 302 303 304
		struct browser_disasm_line *bpos = disasm_line__browser(pos);
		bpos->percent = disasm_line__calc_percent(pos, sym, evidx);
		if (bpos->percent < 0.01) {
			RB_CLEAR_NODE(&bpos->rb_node);
305 306
			continue;
		}
307
		disasm_rb_tree__insert(&browser->entries, bpos);
308 309 310 311 312 313
	}
	pthread_mutex_unlock(&notes->lock);

	browser->curr_hot = rb_last(&browser->entries);
}

314 315
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
316
	struct disasm_line *dl;
317
	struct browser_disasm_line *bdl;
318 319 320
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
321
	dl = list_entry(browser->b.top, struct disasm_line, node);
322
	bdl = disasm_line__browser(dl);
323 324

	if (browser->hide_src_code) {
325 326
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
327 328 329 330

		browser->b.nr_entries = browser->nr_entries;
		browser->hide_src_code = false;
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
331 332
		browser->b.top_idx = bdl->idx - offset;
		browser->b.index = bdl->idx;
333
	} else {
334
		if (bdl->idx_asm < 0) {
335 336 337 338 339
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

340 341
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
342 343 344 345

		browser->b.nr_entries = browser->nr_asm_entries;
		browser->hide_src_code = true;
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
346 347
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
348 349 350 351 352
	}

	return true;
}

353 354 355 356 357
static bool annotate_browser__callq(struct annotate_browser *browser,
				    int evidx, void (*timer)(void *arg),
				    void *arg, int delay_secs)
{
	struct map_symbol *ms = browser->b.priv;
358
	struct disasm_line *dl = browser->selection;
359 360 361 362 363
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	u64 ip;

364
	if (!ins__is_call(dl->ins))
365 366
		return false;

367
	ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
	target = map__find_symbol(ms->map, ip, NULL);
	if (target == NULL) {
		ui_helpline__puts("The called function was not found.");
		return true;
	}

	notes = symbol__annotation(target);
	pthread_mutex_lock(&notes->lock);

	if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
			    target->name);
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
	symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs);
	ui_browser__show_title(&browser->b, sym->name);
	return true;
}

390 391 392
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
393 394 395 396
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
397
	struct disasm_line *pos;
398 399 400 401 402

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
403
		if (!disasm_line__filter(&browser->b, &pos->node))
404 405 406 407 408 409 410 411
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
412
	struct disasm_line *dl = browser->selection;
413
	s64 idx;
414

415
	if (!ins__is_jump(dl->ins))
416 417
		return false;

418
	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
419
	if (dl == NULL) {
420 421 422 423
		ui_helpline__puts("Invallid jump offset");
		return true;
	}

424
	annotate_browser__set_top(browser, dl, idx);
425 426 427 428
	
	return true;
}

429 430 431
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
432 433 434 435
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
436
	struct disasm_line *pos = browser->selection;
437 438 439

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
440
		if (disasm_line__filter(&browser->b, &pos->node))
441 442 443 444 445 446 447 448 449 450 451 452 453
			continue;

		++*idx;

		if (pos->line && strstr(pos->line, s) != NULL)
			return pos;
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
454
	struct disasm_line *dl;
455 456
	s64 idx;

457 458
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
459 460 461 462
		ui_helpline__puts("String not found!");
		return false;
	}

463
	annotate_browser__set_top(browser, dl, idx);
464 465 466 467
	browser->searching_backwards = false;
	return true;
}

468 469 470
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
471 472 473 474
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
475
	struct disasm_line *pos = browser->selection;
476 477 478

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
479
		if (disasm_line__filter(&browser->b, &pos->node))
480 481 482 483 484 485 486 487 488 489 490 491 492
			continue;

		--*idx;

		if (pos->line && strstr(pos->line, s) != NULL)
			return pos;
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
493
	struct disasm_line *dl;
494 495
	s64 idx;

496 497
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
498 499 500 501
		ui_helpline__puts("String not found!");
		return false;
	}

502
	annotate_browser__set_top(browser, dl, idx);
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
	browser->searching_backwards = true;
	return true;
}

static bool annotate_browser__search_window(struct annotate_browser *browser,
					    int delay_secs)
{
	if (ui_browser__input_window("Search", "String: ", browser->search_bf,
				     "ENTER: OK, ESC: Cancel",
				     delay_secs * 2) != K_ENTER ||
	    !*browser->search_bf)
		return false;

	return true;
}

static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
{
	if (annotate_browser__search_window(browser, delay_secs))
		return __annotate_browser__search(browser);

	return false;
}

static bool annotate_browser__continue_search(struct annotate_browser *browser,
					      int delay_secs)
{
	if (!*browser->search_bf)
		return annotate_browser__search(browser, delay_secs);

	return __annotate_browser__search(browser);
}

static bool annotate_browser__search_reverse(struct annotate_browser *browser,
					   int delay_secs)
{
	if (annotate_browser__search_window(browser, delay_secs))
		return __annotate_browser__search_reverse(browser);

	return false;
}

static
bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
					       int delay_secs)
{
	if (!*browser->search_bf)
		return annotate_browser__search_reverse(browser, delay_secs);

	return __annotate_browser__search_reverse(browser);
}

555
static int annotate_browser__run(struct annotate_browser *self, int evidx,
556
				 void(*timer)(void *arg),
557
				 void *arg, int delay_secs)
558 559
{
	struct rb_node *nd = NULL;
560 561
	struct map_symbol *ms = self->b.priv;
	struct symbol *sym = ms->sym;
562 563
	const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
			   "H: Go to hottest line, ->/ENTER: Line action, "
564
			   "O: Toggle offset view, "
565
			   "S: Toggle source code view";
566
	int key;
567

568
	if (ui_browser__show(&self->b, sym->name, help) < 0)
569
		return -1;
570 571 572

	annotate_browser__calc_percent(self, evidx);

573
	if (self->curr_hot) {
574
		annotate_browser__set_rb_top(self, self->curr_hot);
575 576
		self->b.navkeypressed = false;
	}
577 578

	nd = self->curr_hot;
579

580
	while (1) {
581
		key = ui_browser__run(&self->b, delay_secs);
582

583
		if (delay_secs != 0) {
584 585 586 587 588 589 590 591 592 593
			annotate_browser__calc_percent(self, evidx);
			/*
			 * Current line focus got out of the list of most active
			 * lines, NULL it so that if TAB|UNTAB is pressed, we
			 * move to curr_hot (current hottest line).
			 */
			if (nd != NULL && RB_EMPTY_NODE(nd))
				nd = NULL;
		}

594
		switch (key) {
595
		case K_TIMER:
596 597 598 599
			if (timer != NULL)
				timer(arg);

			if (delay_secs != 0)
600 601
				symbol__annotate_decay_histogram(sym, evidx);
			continue;
602
		case K_TAB:
603 604 605 606 607 608
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
					nd = rb_last(&self->entries);
			} else
				nd = self->curr_hot;
609
			break;
610
		case K_UNTAB:
611 612 613 614 615 616 617 618
			if (nd != NULL)
				nd = rb_next(nd);
				if (nd == NULL)
					nd = rb_first(&self->entries);
			else
				nd = self->curr_hot;
			break;
		case 'H':
619
		case 'h':
620
			nd = self->curr_hot;
621
			break;
622
		case 'S':
623
		case 's':
624 625 626
			if (annotate_browser__toggle_source(self))
				ui_helpline__puts(help);
			continue;
627 628 629 630
		case 'O':
		case 'o':
			self->use_offset = !self->use_offset;
			continue;
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
		case '/':
			if (annotate_browser__search(self, delay_secs)) {
show_help:
				ui_helpline__puts(help);
			}
			continue;
		case 'n':
			if (self->searching_backwards ?
			    annotate_browser__continue_search_reverse(self, delay_secs) :
			    annotate_browser__continue_search(self, delay_secs))
				goto show_help;
			continue;
		case '?':
			if (annotate_browser__search_reverse(self, delay_secs))
				goto show_help;
			continue;
647 648
		case K_ENTER:
		case K_RIGHT:
649
			if (self->selection == NULL)
650
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
651
			else if (self->selection->offset == -1)
652
				ui_helpline__puts("Actions are only available for assembly lines.");
653 654 655 656 657 658 659 660 661
			else if (!self->selection->ins) {
				if (strcmp(self->selection->name, "retq"))
					goto show_sup_ins;
				goto out;
			} else if (!(annotate_browser__jump(self) ||
				     annotate_browser__callq(self, evidx, timer, arg, delay_secs))) {
show_sup_ins:
				ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
			}
662
			continue;
663 664
		case K_LEFT:
		case K_ESC:
665 666
		case 'q':
		case CTRL('c'):
667
			goto out;
668 669
		default:
			continue;
670
		}
671 672

		if (nd != NULL)
673
			annotate_browser__set_rb_top(self, nd);
674 675
	}
out:
676
	ui_browser__hide(&self->b);
677
	return key;
678 679
}

680
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
681
			     void(*timer)(void *arg), void *arg, int delay_secs)
682
{
683
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
684
				    timer, arg, delay_secs);
685 686
}

687 688 689 690 691 692 693 694 695
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;

	for (offset = 0; offset < size; ++offset) {
		struct disasm_line *dl = browser->offsets[offset], *dlt;
		struct browser_disasm_line *bdlt;

696 697
		if (!dl || !dl->ins || !ins__is_jump(dl->ins) ||
		    !disasm_line__has_offset(dl))
698 699
			continue;

700
		if (dl->ops.target.offset >= size) {
701 702
			ui__error("jump to after symbol!\n"
				  "size: %zx, jump target: %" PRIx64,
703
				  size, dl->ops.target.offset);
704 705 706
			continue;
		}

707
		dlt = browser->offsets[dl->ops.target.offset];
708 709 710 711 712 713 714
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
		if (dlt == NULL)
			continue;

715 716 717 718 719 720
		bdlt = disasm_line__browser(dlt);
		bdlt->jump_target = true;
	}
		
}

721
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
722
			 void(*timer)(void *arg), void *arg,
723
			 int delay_secs)
724
{
725
	struct disasm_line *pos, *n;
726
	struct annotation *notes;
727
	const size_t size = symbol__size(sym);
728 729 730 731
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
732 733
	struct annotate_browser browser = {
		.b = {
734
			.refresh = annotate_browser__refresh,
735 736
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
737
			.filter  = disasm_line__filter,
738
			.priv	 = &ms,
739
			.use_navkeypressed = true,
740
		},
741
		.use_offset = true,
742
	};
743
	int ret = -1;
744

745
	if (sym == NULL)
746 747
		return -1;

748
	if (map->dso->annotate_warned)
749 750
		return -1;

751 752 753 754 755 756
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

757
	if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
758
		ui__error("%s", ui_helpline__last_msg);
759
		goto out_free_offsets;
760 761 762 763
	}

	ui_helpline__push("Press <- or ESC to exit");

764
	notes = symbol__annotation(sym);
765
	browser.start = map__rip_2objdump(map, sym->start);
766

767
	list_for_each_entry(pos, &notes->src->source, node) {
768
		struct browser_disasm_line *bpos;
769
		size_t line_len = strlen(pos->line);
770

771 772
		if (browser.b.width < line_len)
			browser.b.width = line_len;
773 774
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
775
		if (pos->offset != -1) {
776
			bpos->idx_asm = browser.nr_asm_entries++;
777 778 779 780 781 782 783 784 785
			/*
			 * FIXME: short term bandaid to cope with assembly
			 * routines that comes with labels in the same column
			 * as the address in objdump, sigh.
			 *
			 * E.g. copy_user_generic_unrolled
 			 */
			if (pos->offset < (s64)size)
				browser.offsets[pos->offset] = pos;
786
		} else
787
			bpos->idx_asm = -1;
788 789
	}

790 791
	annotate_browser__mark_jump_targets(&browser, size);

792
	browser.offset_width = hex_width(size);
793
	browser.b.nr_entries = browser.nr_entries;
794
	browser.b.entries = &notes->src->source,
795
	browser.b.width += 18; /* Percentage */
796
	ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
797
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
798
		list_del(&pos->node);
799
		disasm_line__free(pos);
800
	}
801 802 803

out_free_offsets:
	free(browser.offsets);
804 805
	return ret;
}