annotate.c 24.4 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 25 26 27 28 29 30 31
static struct annotate_browser_opt {
	bool hide_src_code,
	     use_offset,
	     jump_arrows,
	     show_nr_jumps;
} annotate_browser__opts = {
	.use_offset	= true,
	.jump_arrows	= true,
};

32 33 34
struct annotate_browser {
	struct ui_browser b;
	struct rb_root	  entries;
35
	struct rb_node	  *curr_hot;
36
	struct disasm_line	  *selection;
37
	struct disasm_line  **offsets;
38
	u64		    start;
39 40
	int		    nr_asm_entries;
	int		    nr_entries;
41 42
	int		    max_jump_sources;
	int		    nr_jumps;
43
	bool		    searching_backwards;
44
	u8		    addr_width;
45 46
	u8		    jumps_width;
	u8		    target_width;
47 48
	u8		    min_addr_width;
	u8		    max_addr_width;
49
	char		    search_bf[128];
50 51
};

52
static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
53
{
54
	return (struct browser_disasm_line *)(dl + 1);
55 56
}

57
static bool disasm_line__filter(struct ui_browser *browser __used, void *entry)
58
{
59
	if (annotate_browser__opts.hide_src_code) {
60 61
		struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
		return dl->offset == -1;
62 63 64 65 66
	}

	return false;
}

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
						 int nr, bool current)
{
	if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
		return HE_COLORSET_SELECTED;
	if (nr == browser->max_jump_sources)
		return HE_COLORSET_TOP;
	if (nr > 1)
		return HE_COLORSET_MEDIUM;
	return HE_COLORSET_NORMAL;
}

static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
						     int nr, bool current)
{
	 int color = annotate_browser__jumps_percent_color(browser, nr, current);
	 return ui_browser__set_color(&browser->b, color);
}

86
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
87
{
88
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
89
	struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
90
	struct browser_disasm_line *bdl = disasm_line__browser(dl);
91
	bool current_entry = ui_browser__is_current_entry(browser, row);
92
	bool change_color = (!annotate_browser__opts.hide_src_code &&
93 94 95
			     (!current_entry || (browser->use_navkeypressed &&
					         !browser->navkeypressed)));
	int width = browser->width, printed;
96
	char bf[256];
97

98
	if (dl->offset != -1 && bdl->percent != 0.0) {
99
		ui_browser__set_percent_color(browser, bdl->percent, current_entry);
100
		slsmg_printf("%6.2f ", bdl->percent);
101
	} else {
102
		ui_browser__set_percent_color(browser, 0, current_entry);
103
		slsmg_write_nstring(" ", 7);
104 105
	}

106
	SLsmg_write_char(' ');
107 108

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

112
	if (!*dl->line)
113
		slsmg_write_nstring(" ", width - 7);
114 115 116 117 118 119
	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 {
120
		u64 addr = dl->offset;
121
		int color = -1;
122

123
		if (!annotate_browser__opts.use_offset)
124 125
			addr += ab->start;

126
		if (!annotate_browser__opts.use_offset) {
127
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
128
		} else {
129
			if (bdl->jump_sources) {
130
				if (annotate_browser__opts.show_nr_jumps) {
131 132 133 134 135 136 137
					int prev;
					printed = scnprintf(bf, sizeof(bf), "%*d ",
							    ab->jumps_width,
							    bdl->jump_sources);
					prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
											 current_entry);
					slsmg_write_nstring(bf, printed);
138
					ui_browser__set_color(browser, prev);
139 140
				}

141
				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
142
						    ab->target_width, addr);
143
			} else {
144 145
				printed = scnprintf(bf, sizeof(bf), "%*s  ",
						    ab->addr_width, " ");
146 147
			}
		}
148

149
		if (change_color)
150
			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
151 152
		slsmg_write_nstring(bf, printed);
		if (change_color)
153
			ui_browser__set_color(browser, color);
154
		if (dl->ins && dl->ins->ops->scnprintf) {
155
			if (ins__is_jump(dl->ins)) {
156
				bool fwd = dl->ops.target.offset > (u64)dl->offset;
157

158
				ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
159
								    SLSMG_UARROW_CHAR);
160
				SLsmg_write_char(' ');
161
			} else if (ins__is_call(dl->ins)) {
162
				ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
163
				SLsmg_write_char(' ');
164 165 166
			} else {
				slsmg_write_nstring(" ", 2);
			}
167 168 169 170
		} else {
			if (strcmp(dl->name, "retq")) {
				slsmg_write_nstring(" ", 2);
			} else {
171
				ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
172 173 174
				SLsmg_write_char(' ');
			}
		}
175

176
		disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
177
		slsmg_write_nstring(bf, width - 10 - printed);
178
	}
179

180
	if (current_entry)
181
		ab->selection = dl;
182 183
}

184
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
185 186
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
187 188
	struct disasm_line *cursor = ab->selection, *target;
	struct browser_disasm_line *btarget, *bcursor;
189
	unsigned int from, to;
190

191
	if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) ||
192 193
	    !disasm_line__has_offset(cursor))
		return;
194

195 196 197
	target = ab->offsets[cursor->ops.target.offset];
	if (!target)
		return;
198

199 200
	bcursor = disasm_line__browser(cursor);
	btarget = disasm_line__browser(target);
201

202
	if (annotate_browser__opts.hide_src_code) {
203
		from = bcursor->idx_asm;
204 205
		to = btarget->idx_asm;
	} else {
206
		from = (u64)bcursor->idx;
207 208 209 210
		to = (u64)btarget->idx;
	}

	ui_browser__set_color(browser, HE_COLORSET_CODE);
211
	__ui_browser__line_arrow(browser, 9 + ab->addr_width, from, to);
212 213 214 215 216 217
}

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

218
	if (annotate_browser__opts.jump_arrows)
219
		annotate_browser__draw_current_jump(browser);
220

221 222
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
	__ui_browser__vline(browser, 7, 0, browser->height - 1);
223 224 225
	return ret;
}

226
static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
227 228 229
{
	double percent = 0.0;

230
	if (dl->offset != -1) {
231
		int len = sym->end - sym->start;
232
		unsigned int hits = 0;
233
		struct annotation *notes = symbol__annotation(sym);
234
		struct source_line *src_line = notes->src->lines;
235
		struct sym_hist *h = annotation__histogram(notes, evidx);
236 237
		s64 offset = dl->offset;
		struct disasm_line *next;
238

239
		next = disasm__get_next_ip_line(&notes->src->source, dl);
240 241
		while (offset < (s64)len &&
		       (next == NULL || offset < next->offset)) {
242 243
			if (src_line) {
				percent += src_line[offset].percent;
244
			} else
245
				hits += h->addr[offset];
246 247 248

			++offset;
		}
249 250 251 252 253
		/*
 		 * If the percentage wasn't already calculated in
 		 * symbol__get_source_line, do it now:
 		 */
		if (src_line == NULL && h->sum)
254 255 256
			percent = 100.0 * hits / h->sum;
	}

257 258 259
	return percent;
}

260
static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
261
{
262
	struct rb_node **p = &root->rb_node;
263
	struct rb_node *parent = NULL;
264
	struct browser_disasm_line *l;
265 266 267

	while (*p != NULL) {
		parent = *p;
268 269
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
		if (bdl->percent < l->percent)
270 271 272 273
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
274 275
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
276 277
}

278
static void annotate_browser__set_top(struct annotate_browser *browser,
279
				      struct disasm_line *pos, u32 idx)
280 281 282
{
	unsigned back;

283 284 285
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
286

287
	while (browser->b.top_idx != 0 && back != 0) {
288
		pos = list_entry(pos->node.prev, struct disasm_line, node);
289

290
		if (disasm_line__filter(&browser->b, &pos->node))
291 292
			continue;

293
		--browser->b.top_idx;
294 295 296
		--back;
	}

297 298
	browser->b.top = pos;
	browser->b.navkeypressed = true;
299 300 301 302 303
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
304
	struct browser_disasm_line *bpos;
305
	struct disasm_line *pos;
306
	u32 idx;
307

308 309
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
310
	idx = bpos->idx;
311
	if (annotate_browser__opts.hide_src_code)
312 313
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
314
	browser->curr_hot = nd;
315 316
}

317 318
static void annotate_browser__calc_percent(struct annotate_browser *browser,
					   int evidx)
319
{
320 321
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
322
	struct annotation *notes = symbol__annotation(sym);
323
	struct disasm_line *pos;
324 325 326 327 328 329

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
330 331 332 333
		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);
334 335
			continue;
		}
336
		disasm_rb_tree__insert(&browser->entries, bpos);
337 338 339 340 341 342
	}
	pthread_mutex_unlock(&notes->lock);

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

343 344
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
345
	struct disasm_line *dl;
346
	struct browser_disasm_line *bdl;
347 348 349
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
350
	dl = list_entry(browser->b.top, struct disasm_line, node);
351
	bdl = disasm_line__browser(dl);
352

353
	if (annotate_browser__opts.hide_src_code) {
354 355
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
356 357

		browser->b.nr_entries = browser->nr_entries;
358
		annotate_browser__opts.hide_src_code = false;
359
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
360 361
		browser->b.top_idx = bdl->idx - offset;
		browser->b.index = bdl->idx;
362
	} else {
363
		if (bdl->idx_asm < 0) {
364 365 366 367 368
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

369 370
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
371 372

		browser->b.nr_entries = browser->nr_asm_entries;
373
		annotate_browser__opts.hide_src_code = true;
374
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
375 376
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
377 378 379 380 381
	}

	return true;
}

382 383 384 385 386 387
static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
{
	ui_browser__reset_index(&browser->b);
	browser->b.nr_entries = browser->nr_asm_entries;
}

388 389 390 391 392
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;
393
	struct disasm_line *dl = browser->selection;
394 395 396 397 398
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	u64 ip;

399
	if (!ins__is_call(dl->ins))
400 401
		return false;

402
	ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
	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;
}

425 426 427
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, 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;
433 434 435 436 437

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
438
		if (!disasm_line__filter(&browser->b, &pos->node))
439 440 441 442 443 444 445 446
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
447
	struct disasm_line *dl = browser->selection;
448
	s64 idx;
449

450
	if (!ins__is_jump(dl->ins))
451 452
		return false;

453
	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
454
	if (dl == NULL) {
455 456 457 458
		ui_helpline__puts("Invallid jump offset");
		return true;
	}

459
	annotate_browser__set_top(browser, dl, idx);
460 461 462 463
	
	return true;
}

464 465 466
static
struct disasm_line *annotate_browser__find_string(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(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(struct annotate_browser *browser)
{
489
	struct disasm_line *dl;
490 491
	s64 idx;

492 493
	dl = annotate_browser__find_string(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
	browser->searching_backwards = false;
	return true;
}

503 504 505
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
506 507 508 509
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
510
	struct disasm_line *pos = browser->selection;
511 512 513

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
514
		if (disasm_line__filter(&browser->b, &pos->node))
515 516 517 518 519 520 521 522 523 524 525 526 527
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
528
	struct disasm_line *dl;
529 530
	s64 idx;

531 532
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
533 534 535 536
		ui_helpline__puts("String not found!");
		return false;
	}

537
	annotate_browser__set_top(browser, dl, idx);
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
	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);
}

590 591 592 593 594 595 596 597 598 599 600 601 602
static void annotate_browser__update_addr_width(struct annotate_browser *browser)
{
	if (annotate_browser__opts.use_offset)
		browser->target_width = browser->min_addr_width;
	else
		browser->target_width = browser->max_addr_width;

	browser->addr_width = browser->target_width;

	if (annotate_browser__opts.show_nr_jumps)
		browser->addr_width += browser->jumps_width + 1;
}

603
static int annotate_browser__run(struct annotate_browser *browser, int evidx,
604
				 void(*timer)(void *arg),
605
				 void *arg, int delay_secs)
606 607
{
	struct rb_node *nd = NULL;
608
	struct map_symbol *ms = browser->b.priv;
609
	struct symbol *sym = ms->sym;
610
	const char *help = "Press 'h' for help on key bindings";
611
	int key;
612

613
	if (ui_browser__show(&browser->b, sym->name, help) < 0)
614
		return -1;
615

616
	annotate_browser__calc_percent(browser, evidx);
617

618 619 620
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
621
	}
622

623
	nd = browser->curr_hot;
624

625
	while (1) {
626
		key = ui_browser__run(&browser->b, delay_secs);
627

628
		if (delay_secs != 0) {
629
			annotate_browser__calc_percent(browser, evidx);
630 631 632 633 634 635 636 637 638
			/*
			 * 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;
		}

639
		switch (key) {
640
		case K_TIMER:
641 642 643 644
			if (timer != NULL)
				timer(arg);

			if (delay_secs != 0)
645 646
				symbol__annotate_decay_histogram(sym, evidx);
			continue;
647
		case K_TAB:
648 649 650
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
651
					nd = rb_last(&browser->entries);
652
			} else
653
				nd = browser->curr_hot;
654
			break;
655
		case K_UNTAB:
656 657 658
			if (nd != NULL)
				nd = rb_next(nd);
				if (nd == NULL)
659
					nd = rb_first(&browser->entries);
660
			else
661
				nd = browser->curr_hot;
662
			break;
663
		case K_F1:
664
		case 'h':
665
			ui_browser__help_window(&browser->b,
666 667 668 669 670
		"UP/DOWN/PGUP\n"
		"PGDN/SPACE    Navigate\n"
		"q/ESC/CTRL+C  Exit\n\n"
		"->            Go to target\n"
		"<-            Exit\n"
671
		"H             Cycle thru hottest instructions\n"
672 673 674 675 676 677 678 679 680
		"j             Toggle showing jump to target arrows\n"
		"J             Toggle showing number of jump sources on targets\n"
		"n             Search next string\n"
		"o             Toggle disassembler output/simplified view\n"
		"s             Toggle source code view\n"
		"/             Search string\n"
		"?             Search previous string\n");
			continue;
		case 'H':
681
			nd = browser->curr_hot;
682
			break;
683
		case 's':
684
			if (annotate_browser__toggle_source(browser))
685 686
				ui_helpline__puts(help);
			continue;
687
		case 'o':
688
			annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
689
			annotate_browser__update_addr_width(browser);
690
			continue;
691
		case 'j':
692
			annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
693
			continue;
694
		case 'J':
695
			annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
696
			annotate_browser__update_addr_width(browser);
697
			continue;
698
		case '/':
699
			if (annotate_browser__search(browser, delay_secs)) {
700 701 702 703 704
show_help:
				ui_helpline__puts(help);
			}
			continue;
		case 'n':
705 706 707
			if (browser->searching_backwards ?
			    annotate_browser__continue_search_reverse(browser, delay_secs) :
			    annotate_browser__continue_search(browser, delay_secs))
708 709 710
				goto show_help;
			continue;
		case '?':
711
			if (annotate_browser__search_reverse(browser, delay_secs))
712 713
				goto show_help;
			continue;
714 715 716 717
		case 'D': {
			static int seq;
			ui_helpline__pop();
			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
718 719 720 721 722
					   seq++, browser->b.nr_entries,
					   browser->b.height,
					   browser->b.index,
					   browser->b.top_idx,
					   browser->nr_asm_entries);
723 724
		}
			continue;
725 726
		case K_ENTER:
		case K_RIGHT:
727
			if (browser->selection == NULL)
728
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
729
			else if (browser->selection->offset == -1)
730
				ui_helpline__puts("Actions are only available for assembly lines.");
731 732
			else if (!browser->selection->ins) {
				if (strcmp(browser->selection->name, "retq"))
733 734
					goto show_sup_ins;
				goto out;
735 736
			} else if (!(annotate_browser__jump(browser) ||
				     annotate_browser__callq(browser, evidx, timer, arg, delay_secs))) {
737 738 739
show_sup_ins:
				ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
			}
740
			continue;
741 742
		case K_LEFT:
		case K_ESC:
743 744
		case 'q':
		case CTRL('c'):
745
			goto out;
746 747
		default:
			continue;
748
		}
749 750

		if (nd != NULL)
751
			annotate_browser__set_rb_top(browser, nd);
752 753
	}
out:
754
	ui_browser__hide(&browser->b);
755
	return key;
756 757
}

758
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
759
			     void(*timer)(void *arg), void *arg, int delay_secs)
760
{
761
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
762
				    timer, arg, delay_secs);
763 764
}

765 766 767 768 769 770 771 772 773
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;

774 775
		if (!dl || !dl->ins || !ins__is_jump(dl->ins) ||
		    !disasm_line__has_offset(dl))
776 777
			continue;

778
		if (dl->ops.target.offset >= size) {
779 780
			ui__error("jump to after symbol!\n"
				  "size: %zx, jump target: %" PRIx64,
781
				  size, dl->ops.target.offset);
782 783 784
			continue;
		}

785
		dlt = browser->offsets[dl->ops.target.offset];
786 787 788 789 790 791 792
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
		if (dlt == NULL)
			continue;

793
		bdlt = disasm_line__browser(dlt);
794 795 796 797
		if (++bdlt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = bdlt->jump_sources;

		++browser->nr_jumps;
798 799 800 801
	}
		
}

802 803 804 805 806 807 808 809 810
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

811
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
812
			 void(*timer)(void *arg), void *arg,
813
			 int delay_secs)
814
{
815
	struct disasm_line *pos, *n;
816
	struct annotation *notes;
817
	size_t size;
818 819 820 821
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
822 823
	struct annotate_browser browser = {
		.b = {
824
			.refresh = annotate_browser__refresh,
825 826
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
827
			.filter  = disasm_line__filter,
828
			.priv	 = &ms,
829
			.use_navkeypressed = true,
830
		},
831
	};
832
	int ret = -1;
833

834
	if (sym == NULL)
835 836
		return -1;

837 838
	size = symbol__size(sym);

839
	if (map->dso->annotate_warned)
840 841
		return -1;

842 843 844 845 846 847
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

848
	if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
849
		ui__error("%s", ui_helpline__last_msg);
850
		goto out_free_offsets;
851 852 853 854
	}

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

855
	notes = symbol__annotation(sym);
856
	browser.start = map__rip_2objdump(map, sym->start);
857

858
	list_for_each_entry(pos, &notes->src->source, node) {
859
		struct browser_disasm_line *bpos;
860
		size_t line_len = strlen(pos->line);
861

862 863
		if (browser.b.width < line_len)
			browser.b.width = line_len;
864 865
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
866
		if (pos->offset != -1) {
867
			bpos->idx_asm = browser.nr_asm_entries++;
868 869 870 871 872 873 874 875 876
			/*
			 * 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;
877
		} else
878
			bpos->idx_asm = -1;
879 880
	}

881 882
	annotate_browser__mark_jump_targets(&browser, size);

883
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
884
	browser.max_addr_width = hex_width(sym->end);
885
	browser.jumps_width = width_jumps(browser.max_jump_sources);
886
	browser.b.nr_entries = browser.nr_entries;
887
	browser.b.entries = &notes->src->source,
888
	browser.b.width += 18; /* Percentage */
889 890 891 892 893 894

	if (annotate_browser__opts.hide_src_code)
		annotate_browser__init_asm_mode(&browser);

	annotate_browser__update_addr_width(&browser);

895
	ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
896
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
897
		list_del(&pos->node);
898
		disasm_line__free(pos);
899
	}
900 901 902

out_free_offsets:
	free(browser.offsets);
903 904
	return ret;
}
905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953

#define ANNOTATE_CFG(n) \
	{ .name = #n, .value = &annotate_browser__opts.n, }
	
/*
 * Keep the entries sorted, they are bsearch'ed
 */
static struct annotate__config {
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
	ANNOTATE_CFG(show_nr_jumps),
	ANNOTATE_CFG(use_offset),
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
	const struct annotate__config *cfg = cfgp;

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

static int annotate__config(const char *var, const char *value, void *data __used)
{
	struct annotate__config *cfg;
	const char *name;

	if (prefixcmp(var, "annotate.") != 0)
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
		      sizeof(struct annotate__config), annotate_config__cmp);

	if (cfg == NULL)
		return -1;

	*cfg->value = perf_config_bool(name, value);
	return 0;
}

void annotate_browser__init(void)
{
	perf_config(annotate__config, NULL);
}