annotate.c 24.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
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 193 194 195 196
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;

	/* PLT symbols contain external offsets */
	if (strstr(sym->name, "@plt"))
		return;
197

198
	if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) ||
199 200
	    !disasm_line__has_offset(cursor))
		return;
201

202 203 204
	target = ab->offsets[cursor->ops.target.offset];
	if (!target)
		return;
205

206 207
	bcursor = disasm_line__browser(cursor);
	btarget = disasm_line__browser(target);
208

209
	if (annotate_browser__opts.hide_src_code) {
210
		from = bcursor->idx_asm;
211 212
		to = btarget->idx_asm;
	} else {
213
		from = (u64)bcursor->idx;
214 215 216 217
		to = (u64)btarget->idx;
	}

	ui_browser__set_color(browser, HE_COLORSET_CODE);
218
	__ui_browser__line_arrow(browser, 9 + ab->addr_width, from, to);
219 220 221 222 223 224
}

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

225
	if (annotate_browser__opts.jump_arrows)
226
		annotate_browser__draw_current_jump(browser);
227

228 229
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
	__ui_browser__vline(browser, 7, 0, browser->height - 1);
230 231 232
	return ret;
}

233
static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
234 235 236
{
	double percent = 0.0;

237
	if (dl->offset != -1) {
238
		int len = sym->end - sym->start;
239
		unsigned int hits = 0;
240
		struct annotation *notes = symbol__annotation(sym);
241
		struct source_line *src_line = notes->src->lines;
242
		struct sym_hist *h = annotation__histogram(notes, evidx);
243 244
		s64 offset = dl->offset;
		struct disasm_line *next;
245

246
		next = disasm__get_next_ip_line(&notes->src->source, dl);
247 248
		while (offset < (s64)len &&
		       (next == NULL || offset < next->offset)) {
249 250
			if (src_line) {
				percent += src_line[offset].percent;
251
			} else
252
				hits += h->addr[offset];
253 254 255

			++offset;
		}
256 257 258 259 260
		/*
 		 * If the percentage wasn't already calculated in
 		 * symbol__get_source_line, do it now:
 		 */
		if (src_line == NULL && h->sum)
261 262 263
			percent = 100.0 * hits / h->sum;
	}

264 265 266
	return percent;
}

267
static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
268
{
269
	struct rb_node **p = &root->rb_node;
270
	struct rb_node *parent = NULL;
271
	struct browser_disasm_line *l;
272 273 274

	while (*p != NULL) {
		parent = *p;
275 276
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
		if (bdl->percent < l->percent)
277 278 279 280
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
281 282
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
283 284
}

285
static void annotate_browser__set_top(struct annotate_browser *browser,
286
				      struct disasm_line *pos, u32 idx)
287 288 289
{
	unsigned back;

290 291 292
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
293

294
	while (browser->b.top_idx != 0 && back != 0) {
295
		pos = list_entry(pos->node.prev, struct disasm_line, node);
296

297
		if (disasm_line__filter(&browser->b, &pos->node))
298 299
			continue;

300
		--browser->b.top_idx;
301 302 303
		--back;
	}

304 305
	browser->b.top = pos;
	browser->b.navkeypressed = true;
306 307 308 309 310
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
311
	struct browser_disasm_line *bpos;
312
	struct disasm_line *pos;
313
	u32 idx;
314

315 316
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
317
	idx = bpos->idx;
318
	if (annotate_browser__opts.hide_src_code)
319 320
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
321
	browser->curr_hot = nd;
322 323
}

324 325
static void annotate_browser__calc_percent(struct annotate_browser *browser,
					   int evidx)
326
{
327 328
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
329
	struct annotation *notes = symbol__annotation(sym);
330
	struct disasm_line *pos;
331 332 333 334 335 336

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
337 338 339 340
		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);
341 342
			continue;
		}
343
		disasm_rb_tree__insert(&browser->entries, bpos);
344 345 346 347 348 349
	}
	pthread_mutex_unlock(&notes->lock);

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

350 351
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
352
	struct disasm_line *dl;
353
	struct browser_disasm_line *bdl;
354 355 356
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
357
	dl = list_entry(browser->b.top, struct disasm_line, node);
358
	bdl = disasm_line__browser(dl);
359

360
	if (annotate_browser__opts.hide_src_code) {
361 362
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
363 364

		browser->b.nr_entries = browser->nr_entries;
365
		annotate_browser__opts.hide_src_code = false;
366
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
367 368
		browser->b.top_idx = bdl->idx - offset;
		browser->b.index = bdl->idx;
369
	} else {
370
		if (bdl->idx_asm < 0) {
371 372 373 374 375
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

376 377
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
378 379

		browser->b.nr_entries = browser->nr_asm_entries;
380
		annotate_browser__opts.hide_src_code = true;
381
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
382 383
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
384 385 386 387 388
	}

	return true;
}

389 390 391 392 393 394
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;
}

395 396
static bool annotate_browser__callq(struct annotate_browser *browser, int evidx,
				    struct hist_browser_timer *hbt)
397 398
{
	struct map_symbol *ms = browser->b.priv;
399
	struct disasm_line *dl = browser->selection;
400 401 402 403 404
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	u64 ip;

405
	if (!ins__is_call(dl->ins))
406 407
		return false;

408
	ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
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);
426
	symbol__tui_annotate(target, ms->map, evidx, hbt);
427 428 429 430
	ui_browser__show_title(&browser->b, sym->name);
	return true;
}

431 432 433
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
434 435 436 437
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
438
	struct disasm_line *pos;
439 440 441 442 443

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
444
		if (!disasm_line__filter(&browser->b, &pos->node))
445 446 447 448 449 450 451 452
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
453
	struct disasm_line *dl = browser->selection;
454
	s64 idx;
455

456
	if (!ins__is_jump(dl->ins))
457 458
		return false;

459
	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
460
	if (dl == NULL) {
461 462 463 464
		ui_helpline__puts("Invallid jump offset");
		return true;
	}

465
	annotate_browser__set_top(browser, dl, idx);
466 467 468 469
	
	return true;
}

470 471 472
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
473 474 475 476
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
477
	struct disasm_line *pos = browser->selection;
478 479 480

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
481
		if (disasm_line__filter(&browser->b, &pos->node))
482 483 484 485 486 487 488 489 490 491 492 493 494
			continue;

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
495
	struct disasm_line *dl;
496 497
	s64 idx;

498 499
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
500 501 502 503
		ui_helpline__puts("String not found!");
		return false;
	}

504
	annotate_browser__set_top(browser, dl, idx);
505 506 507 508
	browser->searching_backwards = false;
	return true;
}

509 510 511
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
512 513 514 515
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
516
	struct disasm_line *pos = browser->selection;
517 518 519

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
520
		if (disasm_line__filter(&browser->b, &pos->node))
521 522 523 524 525 526 527 528 529 530 531 532 533
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
534
	struct disasm_line *dl;
535 536
	s64 idx;

537 538
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
539 540 541 542
		ui_helpline__puts("String not found!");
		return false;
	}

543
	annotate_browser__set_top(browser, dl, idx);
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 591 592 593 594 595
	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);
}

596 597 598 599 600 601 602 603 604 605 606 607 608
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;
}

609
static int annotate_browser__run(struct annotate_browser *browser, int evidx,
610
				 struct hist_browser_timer *hbt)
611 612
{
	struct rb_node *nd = NULL;
613
	struct map_symbol *ms = browser->b.priv;
614
	struct symbol *sym = ms->sym;
615
	const char *help = "Press 'h' for help on key bindings";
616
	int delay_secs = hbt ? hbt->refresh : 0;
617
	int key;
618

619
	if (ui_browser__show(&browser->b, sym->name, help) < 0)
620
		return -1;
621

622
	annotate_browser__calc_percent(browser, evidx);
623

624 625 626
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
627
	}
628

629
	nd = browser->curr_hot;
630

631
	while (1) {
632
		key = ui_browser__run(&browser->b, delay_secs);
633

634
		if (delay_secs != 0) {
635
			annotate_browser__calc_percent(browser, evidx);
636 637 638 639 640 641 642 643 644
			/*
			 * 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;
		}

645
		switch (key) {
646
		case K_TIMER:
647 648
			if (hbt)
				hbt->timer(hbt->arg);
649 650

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

		if (nd != NULL)
763
			annotate_browser__set_rb_top(browser, nd);
764 765
	}
out:
766
	ui_browser__hide(&browser->b);
767
	return key;
768 769
}

770
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
771
			     struct hist_browser_timer *hbt)
772
{
773
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, hbt);
774 775
}

776 777 778 779
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;
780 781 782 783 784 785
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;

	/* PLT symbols contain external offsets */
	if (strstr(sym->name, "@plt"))
		return;
786 787 788 789 790

	for (offset = 0; offset < size; ++offset) {
		struct disasm_line *dl = browser->offsets[offset], *dlt;
		struct browser_disasm_line *bdlt;

791 792
		if (!dl || !dl->ins || !ins__is_jump(dl->ins) ||
		    !disasm_line__has_offset(dl))
793 794
			continue;

795
		if (dl->ops.target.offset >= size) {
796 797
			ui__error("jump to after symbol!\n"
				  "size: %zx, jump target: %" PRIx64,
798
				  size, dl->ops.target.offset);
799 800 801
			continue;
		}

802
		dlt = browser->offsets[dl->ops.target.offset];
803 804 805 806 807 808 809
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
		if (dlt == NULL)
			continue;

810
		bdlt = disasm_line__browser(dlt);
811 812 813 814
		if (++bdlt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = bdlt->jump_sources;

		++browser->nr_jumps;
815 816 817 818
	}
		
}

819 820 821 822 823 824 825 826 827
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

828
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
829
			 struct hist_browser_timer *hbt)
830
{
831
	struct disasm_line *pos, *n;
832
	struct annotation *notes;
833
	size_t size;
834 835 836 837
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
838 839
	struct annotate_browser browser = {
		.b = {
840
			.refresh = annotate_browser__refresh,
841 842
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
843
			.filter  = disasm_line__filter,
844
			.priv	 = &ms,
845
			.use_navkeypressed = true,
846
		},
847
	};
848
	int ret = -1;
849

850
	if (sym == NULL)
851 852
		return -1;

853 854
	size = symbol__size(sym);

855
	if (map->dso->annotate_warned)
856 857
		return -1;

858 859 860 861 862 863
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

864
	if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
865
		ui__error("%s", ui_helpline__last_msg);
866
		goto out_free_offsets;
867 868 869 870
	}

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

871
	notes = symbol__annotation(sym);
872
	browser.start = map__rip_2objdump(map, sym->start);
873

874
	list_for_each_entry(pos, &notes->src->source, node) {
875
		struct browser_disasm_line *bpos;
876
		size_t line_len = strlen(pos->line);
877

878 879
		if (browser.b.width < line_len)
			browser.b.width = line_len;
880 881
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
882
		if (pos->offset != -1) {
883
			bpos->idx_asm = browser.nr_asm_entries++;
884 885 886 887 888 889 890 891 892
			/*
			 * 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;
893
		} else
894
			bpos->idx_asm = -1;
895 896
	}

897 898
	annotate_browser__mark_jump_targets(&browser, size);

899
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
900
	browser.max_addr_width = hex_width(sym->end);
901
	browser.jumps_width = width_jumps(browser.max_jump_sources);
902
	browser.b.nr_entries = browser.nr_entries;
903
	browser.b.entries = &notes->src->source,
904
	browser.b.width += 18; /* Percentage */
905 906 907 908 909 910

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

	annotate_browser__update_addr_width(&browser);

911
	ret = annotate_browser__run(&browser, evidx, hbt);
912
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
913
		list_del(&pos->node);
914
		disasm_line__free(pos);
915
	}
916 917 918

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

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

947 948
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970
{
	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);
}