annotate.c 20.2 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
struct browser_disasm_line {
	struct rb_node	rb_node;
	double		percent;
	u32		idx;
	int		idx_asm;
19
	int		jump_sources;
20 21
};

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		    jump_arrows;
34
	bool		    searching_backwards;
35 36 37
	u8		    addr_width;
	u8		    min_addr_width;
	u8		    max_addr_width;
38
	char		    search_bf[128];
39 40
};

41
static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
42
{
43
	return (struct browser_disasm_line *)(dl + 1);
44 45
}

46
static bool disasm_line__filter(struct ui_browser *browser, void *entry)
47 48 49 50
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);

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

	return false;
}

58 59
static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
{
60
	struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
61
	struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
62
	struct browser_disasm_line *bdl = disasm_line__browser(dl);
63
	bool current_entry = ui_browser__is_current_entry(self, row);
64 65 66
	bool change_color = (!ab->hide_src_code &&
			     (!current_entry || (self->use_navkeypressed &&
					         !self->navkeypressed)));
67 68
	int width = self->width, printed;
	char bf[256];
69

70
	if (dl->offset != -1 && bdl->percent != 0.0) {
71
		ui_browser__set_percent_color(self, bdl->percent, current_entry);
72
		slsmg_printf("%6.2f ", bdl->percent);
73
	} else {
74
		ui_browser__set_percent_color(self, 0, current_entry);
75
		slsmg_write_nstring(" ", 7);
76 77
	}

78
	SLsmg_write_char(' ');
79 80 81 82 83

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

84
	if (!*dl->line)
85
		slsmg_write_nstring(" ", width - 7);
86 87 88 89 90 91
	else if (dl->offset == -1) {
		printed = scnprintf(bf, sizeof(bf), "%*s  ",
				    ab->addr_width, " ");
		slsmg_write_nstring(bf, printed);
		slsmg_write_nstring(dl->line, width - printed - 6);
	} else {
92
		u64 addr = dl->offset;
93
		int color = -1;
94

95 96 97
		if (!ab->use_offset)
			addr += ab->start;

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

110 111 112 113 114
		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);
115
		if (dl->ins && dl->ins->ops->scnprintf) {
116
			if (ins__is_jump(dl->ins)) {
117
				bool fwd = dl->ops.target.offset > (u64)dl->offset;
118

119 120
				ui_browser__write_graph(self, fwd ? SLSMG_DARROW_CHAR :
								    SLSMG_UARROW_CHAR);
121
				SLsmg_write_char(' ');
122 123 124
			} else if (ins__is_call(dl->ins)) {
				ui_browser__write_graph(self, SLSMG_RARROW_CHAR);
				SLsmg_write_char(' ');
125 126 127
			} else {
				slsmg_write_nstring(" ", 2);
			}
128 129 130 131
		} else {
			if (strcmp(dl->name, "retq")) {
				slsmg_write_nstring(" ", 2);
			} else {
132
				ui_browser__write_graph(self, SLSMG_LARROW_CHAR);
133 134 135
				SLsmg_write_char(' ');
			}
		}
136

137
		disasm_line__scnprintf(dl, bf, sizeof(bf), !ab->use_offset);
138
		slsmg_write_nstring(bf, width - 10 - printed);
139
	}
140

141
	if (current_entry)
142
		ab->selection = dl;
143 144
}

145
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
146 147
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
148 149
	struct disasm_line *cursor = ab->selection, *target;
	struct browser_disasm_line *btarget, *bcursor;
150
	unsigned int from, to;
151

152 153 154
	if (!cursor->ins || !ins__is_jump(cursor->ins) ||
	    !disasm_line__has_offset(cursor))
		return;
155

156 157 158
	target = ab->offsets[cursor->ops.target.offset];
	if (!target)
		return;
159

160 161
	bcursor = disasm_line__browser(cursor);
	btarget = disasm_line__browser(target);
162 163

	if (ab->hide_src_code) {
164
		from = bcursor->idx_asm;
165 166
		to = btarget->idx_asm;
	} else {
167
		from = (u64)bcursor->idx;
168 169 170 171
		to = (u64)btarget->idx;
	}

	ui_browser__set_color(browser, HE_COLORSET_CODE);
172
	__ui_browser__line_arrow(browser, 9 + ab->addr_width, from, to);
173 174 175 176
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
177
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
178 179
	int ret = ui_browser__list_head_refresh(browser);

180 181
	if (ab->jump_arrows)
		annotate_browser__draw_current_jump(browser);
182

183 184
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
	__ui_browser__vline(browser, 7, 0, browser->height - 1);
185 186 187
	return ret;
}

188
static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
189 190 191
{
	double percent = 0.0;

192
	if (dl->offset != -1) {
193
		int len = sym->end - sym->start;
194
		unsigned int hits = 0;
195
		struct annotation *notes = symbol__annotation(sym);
196
		struct source_line *src_line = notes->src->lines;
197
		struct sym_hist *h = annotation__histogram(notes, evidx);
198 199
		s64 offset = dl->offset;
		struct disasm_line *next;
200

201
		next = disasm__get_next_ip_line(&notes->src->source, dl);
202 203
		while (offset < (s64)len &&
		       (next == NULL || offset < next->offset)) {
204 205
			if (src_line) {
				percent += src_line[offset].percent;
206
			} else
207
				hits += h->addr[offset];
208 209 210

			++offset;
		}
211 212 213 214 215
		/*
 		 * If the percentage wasn't already calculated in
 		 * symbol__get_source_line, do it now:
 		 */
		if (src_line == NULL && h->sum)
216 217 218
			percent = 100.0 * hits / h->sum;
	}

219 220 221
	return percent;
}

222
static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
223
{
224
	struct rb_node **p = &root->rb_node;
225
	struct rb_node *parent = NULL;
226
	struct browser_disasm_line *l;
227 228 229

	while (*p != NULL) {
		parent = *p;
230 231
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
		if (bdl->percent < l->percent)
232 233 234 235
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
236 237
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
238 239
}

240
static void annotate_browser__set_top(struct annotate_browser *self,
241
				      struct disasm_line *pos, u32 idx)
242 243 244 245 246
{
	unsigned back;

	ui_browser__refresh_dimensions(&self->b);
	back = self->b.height / 2;
247
	self->b.top_idx = self->b.index = idx;
248 249

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

252
		if (disasm_line__filter(&self->b, &pos->node))
253 254
			continue;

255 256 257 258 259
		--self->b.top_idx;
		--back;
	}

	self->b.top = pos;
260
	self->b.navkeypressed = true;
261 262 263 264 265
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
266
	struct browser_disasm_line *bpos;
267
	struct disasm_line *pos;
268

269 270 271
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
	annotate_browser__set_top(browser, pos, bpos->idx);
272
	browser->curr_hot = nd;
273 274
}

275 276
static void annotate_browser__calc_percent(struct annotate_browser *browser,
					   int evidx)
277
{
278 279
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
280
	struct annotation *notes = symbol__annotation(sym);
281
	struct disasm_line *pos;
282 283 284 285 286 287

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
288 289 290 291
		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);
292 293
			continue;
		}
294
		disasm_rb_tree__insert(&browser->entries, bpos);
295 296 297 298 299 300
	}
	pthread_mutex_unlock(&notes->lock);

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

301 302
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
303
	struct disasm_line *dl;
304
	struct browser_disasm_line *bdl;
305 306 307
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
308
	dl = list_entry(browser->b.top, struct disasm_line, node);
309
	bdl = disasm_line__browser(dl);
310 311

	if (browser->hide_src_code) {
312 313
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
314 315 316 317

		browser->b.nr_entries = browser->nr_entries;
		browser->hide_src_code = false;
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
318 319
		browser->b.top_idx = bdl->idx - offset;
		browser->b.index = bdl->idx;
320
	} else {
321
		if (bdl->idx_asm < 0) {
322 323 324 325 326
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

327 328
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
329 330 331 332

		browser->b.nr_entries = browser->nr_asm_entries;
		browser->hide_src_code = true;
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
333 334
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
335 336 337 338 339
	}

	return true;
}

340 341 342 343 344
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;
345
	struct disasm_line *dl = browser->selection;
346 347 348 349 350
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	u64 ip;

351
	if (!ins__is_call(dl->ins))
352 353
		return false;

354
	ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
	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;
}

377 378 379
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
380 381 382 383
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
384
	struct disasm_line *pos;
385 386 387 388 389

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
390
		if (!disasm_line__filter(&browser->b, &pos->node))
391 392 393 394 395 396 397 398
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
399
	struct disasm_line *dl = browser->selection;
400
	s64 idx;
401

402
	if (!ins__is_jump(dl->ins))
403 404
		return false;

405
	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
406
	if (dl == NULL) {
407 408 409 410
		ui_helpline__puts("Invallid jump offset");
		return true;
	}

411
	annotate_browser__set_top(browser, dl, idx);
412 413 414 415
	
	return true;
}

416 417 418
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
419 420 421 422
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
423
	struct disasm_line *pos = browser->selection;
424 425 426

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
427
		if (disasm_line__filter(&browser->b, &pos->node))
428 429 430 431 432 433 434 435 436 437 438 439 440
			continue;

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
441
	struct disasm_line *dl;
442 443
	s64 idx;

444 445
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
446 447 448 449
		ui_helpline__puts("String not found!");
		return false;
	}

450
	annotate_browser__set_top(browser, dl, idx);
451 452 453 454
	browser->searching_backwards = false;
	return true;
}

455 456 457
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
458 459 460 461
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
462
	struct disasm_line *pos = browser->selection;
463 464 465

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
466
		if (disasm_line__filter(&browser->b, &pos->node))
467 468 469 470 471 472 473 474 475 476 477 478 479
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
480
	struct disasm_line *dl;
481 482
	s64 idx;

483 484
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
485 486 487 488
		ui_helpline__puts("String not found!");
		return false;
	}

489
	annotate_browser__set_top(browser, dl, idx);
490 491 492 493 494 495 496 497 498 499 500 501 502 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
	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);
}

542
static int annotate_browser__run(struct annotate_browser *self, int evidx,
543
				 void(*timer)(void *arg),
544
				 void *arg, int delay_secs)
545 546
{
	struct rb_node *nd = NULL;
547 548
	struct map_symbol *ms = self->b.priv;
	struct symbol *sym = ms->sym;
549
	const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
550 551 552
			   "H: Hottest line, ->/ENTER: Line action, "
			   "O: Offset view, "
			   "S: Source view";
553
	int key;
554

555
	if (ui_browser__show(&self->b, sym->name, help) < 0)
556
		return -1;
557 558 559

	annotate_browser__calc_percent(self, evidx);

560
	if (self->curr_hot) {
561
		annotate_browser__set_rb_top(self, self->curr_hot);
562 563
		self->b.navkeypressed = false;
	}
564 565

	nd = self->curr_hot;
566

567
	while (1) {
568
		key = ui_browser__run(&self->b, delay_secs);
569

570
		if (delay_secs != 0) {
571 572 573 574 575 576 577 578 579 580
			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;
		}

581
		switch (key) {
582
		case K_TIMER:
583 584 585 586
			if (timer != NULL)
				timer(arg);

			if (delay_secs != 0)
587 588
				symbol__annotate_decay_histogram(sym, evidx);
			continue;
589
		case K_TAB:
590 591 592 593 594 595
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
					nd = rb_last(&self->entries);
			} else
				nd = self->curr_hot;
596
			break;
597
		case K_UNTAB:
598 599 600 601 602 603 604 605
			if (nd != NULL)
				nd = rb_next(nd);
				if (nd == NULL)
					nd = rb_first(&self->entries);
			else
				nd = self->curr_hot;
			break;
		case 'H':
606
		case 'h':
607
			nd = self->curr_hot;
608
			break;
609
		case 'S':
610
		case 's':
611 612 613
			if (annotate_browser__toggle_source(self))
				ui_helpline__puts(help);
			continue;
614 615 616
		case 'O':
		case 'o':
			self->use_offset = !self->use_offset;
617 618 619 620
			if (self->use_offset)
				self->addr_width = self->min_addr_width;
			else
				self->addr_width = self->max_addr_width;
621
			continue;
622 623 624
		case 'j':
			self->jump_arrows = !self->jump_arrows;
			continue;
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
		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;
641 642
		case K_ENTER:
		case K_RIGHT:
643
			if (self->selection == NULL)
644
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
645
			else if (self->selection->offset == -1)
646
				ui_helpline__puts("Actions are only available for assembly lines.");
647 648 649 650 651 652 653 654 655
			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.");
			}
656
			continue;
657 658
		case K_LEFT:
		case K_ESC:
659 660
		case 'q':
		case CTRL('c'):
661
			goto out;
662 663
		default:
			continue;
664
		}
665 666

		if (nd != NULL)
667
			annotate_browser__set_rb_top(self, nd);
668 669
	}
out:
670
	ui_browser__hide(&self->b);
671
	return key;
672 673
}

674
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
675
			     void(*timer)(void *arg), void *arg, int delay_secs)
676
{
677
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
678
				    timer, arg, delay_secs);
679 680
}

681 682 683 684 685 686 687 688 689
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;

690 691
		if (!dl || !dl->ins || !ins__is_jump(dl->ins) ||
		    !disasm_line__has_offset(dl))
692 693
			continue;

694
		if (dl->ops.target.offset >= size) {
695 696
			ui__error("jump to after symbol!\n"
				  "size: %zx, jump target: %" PRIx64,
697
				  size, dl->ops.target.offset);
698 699 700
			continue;
		}

701
		dlt = browser->offsets[dl->ops.target.offset];
702 703 704 705 706 707 708
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
		if (dlt == NULL)
			continue;

709
		bdlt = disasm_line__browser(dlt);
710
		++bdlt->jump_sources;
711 712 713 714
	}
		
}

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

740
	if (sym == NULL)
741 742
		return -1;

743
	if (map->dso->annotate_warned)
744 745
		return -1;

746 747 748 749 750 751
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

752
	if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
753
		ui__error("%s", ui_helpline__last_msg);
754
		goto out_free_offsets;
755 756 757 758
	}

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

759
	notes = symbol__annotation(sym);
760
	browser.start = map__rip_2objdump(map, sym->start);
761

762
	list_for_each_entry(pos, &notes->src->source, node) {
763
		struct browser_disasm_line *bpos;
764
		size_t line_len = strlen(pos->line);
765

766 767
		if (browser.b.width < line_len)
			browser.b.width = line_len;
768 769
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
770
		if (pos->offset != -1) {
771
			bpos->idx_asm = browser.nr_asm_entries++;
772 773 774 775 776 777 778 779 780
			/*
			 * 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;
781
		} else
782
			bpos->idx_asm = -1;
783 784
	}

785 786
	annotate_browser__mark_jump_targets(&browser, size);

787 788
	browser.addr_width = browser.min_addr_width = hex_width(size);
	browser.max_addr_width = hex_width(sym->end);
789
	browser.b.nr_entries = browser.nr_entries;
790
	browser.b.entries = &notes->src->source,
791
	browser.b.width += 18; /* Percentage */
792
	ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
793
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
794
		list_del(&pos->node);
795
		disasm_line__free(pos);
796
	}
797 798 799

out_free_offsets:
	free(browser.offsets);
800 801
	return ret;
}