annotate.c 19.7 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 115 116
			if (ins__is_jump(dl->ins)) {
				bool fwd = dl->ops.target > (u64)dl->offset;

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 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
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) {
		if (!pos->ins || !ins__is_jump(pos->ins))
			continue;

		target = ab->offsets[pos->ops.target];
		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;
}

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

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

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

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

228 229 230
	return percent;
}

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

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

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

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

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

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

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

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

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

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

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

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

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

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

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

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

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

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

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

	return true;
}

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

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

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

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

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

	return NULL;
}

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

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

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

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

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

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

		++*idx;

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

	return NULL;
}

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

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

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

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

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

		--*idx;

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

	return NULL;
}

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

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

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

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

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

	annotate_browser__calc_percent(self, evidx);

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

	nd = self->curr_hot;
575

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

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

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

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

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

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

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

		if (!dl || !dl->ins || !ins__is_jump(dl->ins))
			continue;

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

702
		dlt = browser->offsets[dl->ops.target];
703 704 705 706 707 708
		bdlt = disasm_line__browser(dlt);
		bdlt->jump_target = true;
	}
		
}

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

733
	if (sym == NULL)
734 735
		return -1;

736
	if (map->dso->annotate_warned)
737 738
		return -1;

739 740 741 742 743 744
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

745
	if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
746
		ui__error("%s", ui_helpline__last_msg);
747
		goto out_free_offsets;
748 749 750 751
	}

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

752
	notes = symbol__annotation(sym);
753
	browser.start = map__rip_2objdump(map, sym->start);
754

755
	list_for_each_entry(pos, &notes->src->source, node) {
756
		struct browser_disasm_line *bpos;
757
		size_t line_len = strlen(pos->line);
758

759 760
		if (browser.b.width < line_len)
			browser.b.width = line_len;
761 762
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
763
		if (pos->offset != -1) {
764
			bpos->idx_asm = browser.nr_asm_entries++;
765 766 767 768 769 770 771 772 773
			/*
			 * 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;
774
		} else
775
			bpos->idx_asm = -1;
776 777
	}

778 779
	annotate_browser__mark_jump_targets(&browser, size);

780
	browser.offset_width = hex_width(size);
781
	browser.b.nr_entries = browser.nr_entries;
782
	browser.b.entries = &notes->src->source,
783
	browser.b.width += 18; /* Percentage */
784
	ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
785
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
786
		list_del(&pos->node);
787
		disasm_line__free(pos);
788
	}
789 790 791

out_free_offsets:
	free(browser.offsets);
792 793
	return ret;
}