annotate.c 24.6 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 58
static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
				void *entry)
59
{
60
	if (annotate_browser__opts.hide_src_code) {
61 62
		struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
		return dl->offset == -1;
63 64 65 66 67
	}

	return false;
}

68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
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);
}

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

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

107
	SLsmg_write_char(' ');
108 109

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

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

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

127
		if (!annotate_browser__opts.use_offset) {
128
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
129
		} else {
130
			if (bdl->jump_sources) {
131
				if (annotate_browser__opts.show_nr_jumps) {
132 133 134 135 136 137 138
					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);
139
					ui_browser__set_color(browser, prev);
140 141
				}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

258 259 260
	return percent;
}

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

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

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

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

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

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

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

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

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

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

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

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

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

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

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

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

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

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

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

	return true;
}

383 384 385 386 387 388
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;
}

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

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

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

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

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

	return NULL;
}

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

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

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

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

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

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

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

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

		--*idx;

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

	return NULL;
}

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

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

538
	annotate_browser__set_top(browser, dl, idx);
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 590
	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);
}

591 592 593 594 595 596 597 598 599 600 601 602 603
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;
}

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

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

617
	annotate_browser__calc_percent(browser, evidx);
618

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

624
	nd = browser->curr_hot;
625

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

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

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

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

		if (nd != NULL)
758
			annotate_browser__set_rb_top(browser, nd);
759 760
	}
out:
761
	ui_browser__hide(&browser->b);
762
	return key;
763 764
}

765
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
766
			     void(*timer)(void *arg), void *arg, int delay_secs)
767
{
768
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
769
				    timer, arg, delay_secs);
770 771
}

772 773 774 775 776 777 778 779 780
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;

781 782
		if (!dl || !dl->ins || !ins__is_jump(dl->ins) ||
		    !disasm_line__has_offset(dl))
783 784
			continue;

785
		if (dl->ops.target.offset >= size) {
786 787
			ui__error("jump to after symbol!\n"
				  "size: %zx, jump target: %" PRIx64,
788
				  size, dl->ops.target.offset);
789 790 791
			continue;
		}

792
		dlt = browser->offsets[dl->ops.target.offset];
793 794 795 796 797 798 799
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
		if (dlt == NULL)
			continue;

800
		bdlt = disasm_line__browser(dlt);
801 802 803 804
		if (++bdlt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = bdlt->jump_sources;

		++browser->nr_jumps;
805 806 807 808
	}
		
}

809 810 811 812 813 814 815 816 817
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

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

841
	if (sym == NULL)
842 843
		return -1;

844 845
	size = symbol__size(sym);

846
	if (map->dso->annotate_warned)
847 848
		return -1;

849 850 851 852 853 854
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

855
	if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
856
		ui__error("%s", ui_helpline__last_msg);
857
		goto out_free_offsets;
858 859 860 861
	}

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

862
	notes = symbol__annotation(sym);
863
	browser.start = map__rip_2objdump(map, sym->start);
864

865
	list_for_each_entry(pos, &notes->src->source, node) {
866
		struct browser_disasm_line *bpos;
867
		size_t line_len = strlen(pos->line);
868

869 870
		if (browser.b.width < line_len)
			browser.b.width = line_len;
871 872
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
873
		if (pos->offset != -1) {
874
			bpos->idx_asm = browser.nr_asm_entries++;
875 876 877 878 879 880 881 882 883
			/*
			 * 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;
884
		} else
885
			bpos->idx_asm = -1;
886 887
	}

888 889
	annotate_browser__mark_jump_targets(&browser, size);

890
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
891
	browser.max_addr_width = hex_width(sym->end);
892
	browser.jumps_width = width_jumps(browser.max_jump_sources);
893
	browser.b.nr_entries = browser.nr_entries;
894
	browser.b.entries = &notes->src->source,
895
	browser.b.width += 18; /* Percentage */
896 897 898 899 900 901

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

	annotate_browser__update_addr_width(&browser);

902
	ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
903
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
904
		list_del(&pos->node);
905
		disasm_line__free(pos);
906
	}
907 908 909

out_free_offsets:
	free(browser.offsets);
910 911
	return ret;
}
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

#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);
}

938 939
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961
{
	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);
}