annotate.c 20.0 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 120 121 122 123
				SLsmg_write_char(' ');
			} else {
				slsmg_write_nstring(" ", 2);
			}

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

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

137
		slsmg_write_nstring(bf, width - 12 - printed);
138
	}
139

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

144 145 146 147 148 149 150 151 152 153 154 155
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) {
156 157
		if (!pos->ins || !ins__is_jump(pos->ins) ||
		    !disasm_line__has_offset(pos))
158 159
			continue;

160
		target = ab->offsets[pos->ops.target.offset];
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 192 193 194 195 196 197
		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;
}

198
static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
199 200 201
{
	double percent = 0.0;

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

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

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

229 230 231
	return percent;
}

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

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

250
static void annotate_browser__set_top(struct annotate_browser *self,
251
				      struct disasm_line *pos, u32 idx)
252 253 254 255 256
{
	unsigned back;

	ui_browser__refresh_dimensions(&self->b);
	back = self->b.height / 2;
257
	self->b.top_idx = self->b.index = idx;
258 259

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

262
		if (disasm_line__filter(&self->b, &pos->node))
263 264
			continue;

265 266 267 268 269
		--self->b.top_idx;
		--back;
	}

	self->b.top = pos;
270
	self->b.navkeypressed = true;
271 272 273 274 275
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
276
	struct browser_disasm_line *bpos;
277
	struct disasm_line *pos;
278

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

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
298 299 300 301
		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);
302 303
			continue;
		}
304
		disasm_rb_tree__insert(&browser->entries, bpos);
305 306 307 308 309 310
	}
	pthread_mutex_unlock(&notes->lock);

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

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

	browser->b.seek(&browser->b, offset, SEEK_CUR);
318
	dl = list_entry(browser->b.top, struct disasm_line, node);
319
	bdl = disasm_line__browser(dl);
320 321

	if (browser->hide_src_code) {
322 323
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
324 325 326 327

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

337 338
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
339 340 341 342

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

	return true;
}

350 351 352 353 354
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;
355
	struct disasm_line *dl = browser->selection;
356 357 358 359 360
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	u64 ip;

361
	if (!ins__is_call(dl->ins))
362 363
		return false;

364
	ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
	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;
}

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

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

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
409
	struct disasm_line *dl = browser->selection;
410
	s64 idx;
411

412
	if (!ins__is_jump(dl->ins))
413 414
		return false;

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

421
	annotate_browser__set_top(browser, dl, idx);
422 423 424 425
	
	return true;
}

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

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

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
451
	struct disasm_line *dl;
452 453
	s64 idx;

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

460
	annotate_browser__set_top(browser, dl, idx);
461 462 463 464
	browser->searching_backwards = false;
	return true;
}

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

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

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
490
	struct disasm_line *dl;
491 492
	s64 idx;

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

499
	annotate_browser__set_top(browser, dl, idx);
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 542 543 544 545 546 547 548 549 550 551
	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);
}

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

565
	if (ui_browser__show(&self->b, sym->name, help) < 0)
566
		return -1;
567 568 569

	annotate_browser__calc_percent(self, evidx);

570
	if (self->curr_hot) {
571
		annotate_browser__set_rb_top(self, self->curr_hot);
572 573
		self->b.navkeypressed = false;
	}
574 575

	nd = self->curr_hot;
576

577
	while (1) {
578
		key = ui_browser__run(&self->b, delay_secs);
579

580
		if (delay_secs != 0) {
581 582 583 584 585 586 587 588 589 590
			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;
		}

591
		switch (key) {
592
		case K_TIMER:
593 594 595 596
			if (timer != NULL)
				timer(arg);

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

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

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

684 685 686 687 688 689 690 691 692
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;

693 694
		if (!dl || !dl->ins || !ins__is_jump(dl->ins) ||
		    !disasm_line__has_offset(dl))
695 696
			continue;

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

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

712 713 714 715 716 717
		bdlt = disasm_line__browser(dlt);
		bdlt->jump_target = true;
	}
		
}

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

742
	if (sym == NULL)
743 744
		return -1;

745
	if (map->dso->annotate_warned)
746 747
		return -1;

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

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

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

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

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

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

787 788
	annotate_browser__mark_jump_targets(&browser, size);

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

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