annotate.c 24.3 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 "../../util/evsel.h"
12
#include <pthread.h>
13
#include <newt.h>
14

15 16 17 18
struct browser_disasm_line {
	struct rb_node	rb_node;
	u32		idx;
	int		idx_asm;
19
	int		jump_sources;
20
	double		percent[1];
21 22
};

23 24 25 26 27 28 29 30 31 32
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,
};

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

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

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

	return false;
}

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

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

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

108
	SLsmg_write_char(' ');
109 110

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

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

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

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

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

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

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

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

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

186 187 188 189 190 191 192 193 194 195
static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
{
	if (!dl || !dl->ins || !ins__is_jump(dl->ins)
	    || !disasm_line__has_offset(dl)
	    || dl->ops.target.offset >= symbol__size(sym))
		return false;

	return true;
}

196
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
197 198
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
199 200
	struct disasm_line *cursor = ab->selection, *target;
	struct browser_disasm_line *btarget, *bcursor;
201
	unsigned int from, to;
202 203 204 205 206 207
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;

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

209
	if (!disasm_line__is_valid_jump(cursor, sym))
210
		return;
211

212 213 214
	target = ab->offsets[cursor->ops.target.offset];
	if (!target)
		return;
215

216 217
	bcursor = disasm_line__browser(cursor);
	btarget = disasm_line__browser(target);
218

219
	if (annotate_browser__opts.hide_src_code) {
220
		from = bcursor->idx_asm;
221 222
		to = btarget->idx_asm;
	} else {
223
		from = (u64)bcursor->idx;
224 225 226 227
		to = (u64)btarget->idx;
	}

	ui_browser__set_color(browser, HE_COLORSET_CODE);
228
	__ui_browser__line_arrow(browser, 9 + ab->addr_width, from, to);
229 230 231 232 233 234
}

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

235
	if (annotate_browser__opts.jump_arrows)
236
		annotate_browser__draw_current_jump(browser);
237

238 239
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
	__ui_browser__vline(browser, 7, 0, browser->height - 1);
240 241 242
	return ret;
}

243
static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
244
{
245
	struct rb_node **p = &root->rb_node;
246
	struct rb_node *parent = NULL;
247
	struct browser_disasm_line *l;
248 249 250

	while (*p != NULL) {
		parent = *p;
251
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
252
		if (bdl->percent[0] < l->percent[0])
253 254 255 256
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
257 258
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
259 260
}

261
static void annotate_browser__set_top(struct annotate_browser *browser,
262
				      struct disasm_line *pos, u32 idx)
263 264 265
{
	unsigned back;

266 267 268
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
269

270
	while (browser->b.top_idx != 0 && back != 0) {
271
		pos = list_entry(pos->node.prev, struct disasm_line, node);
272

273
		if (disasm_line__filter(&browser->b, &pos->node))
274 275
			continue;

276
		--browser->b.top_idx;
277 278 279
		--back;
	}

280 281
	browser->b.top = pos;
	browser->b.navkeypressed = true;
282 283 284 285 286
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
287
	struct browser_disasm_line *bpos;
288
	struct disasm_line *pos;
289
	u32 idx;
290

291 292
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
293
	idx = bpos->idx;
294
	if (annotate_browser__opts.hide_src_code)
295 296
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
297
	browser->curr_hot = nd;
298 299
}

300
static void annotate_browser__calc_percent(struct annotate_browser *browser,
301
					   struct perf_evsel *evsel)
302
{
303 304
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
305
	struct annotation *notes = symbol__annotation(sym);
306 307
	struct disasm_line *pos, *next;
	s64 len = symbol__size(sym);
308 309 310 311 312 313

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
314
		struct browser_disasm_line *bpos = disasm_line__browser(pos);
315 316 317 318 319 320 321 322 323 324 325 326
		const char *path = NULL;

		if (pos->offset == -1) {
			RB_CLEAR_NODE(&bpos->rb_node);
			continue;
		}

		next = disasm__get_next_ip_line(&notes->src->source, pos);
		bpos->percent[0] = disasm__calc_percent(notes, evsel->idx,
					pos->offset, next ? next->offset : len,
				        &path);

327
		if (bpos->percent[0] < 0.01) {
328
			RB_CLEAR_NODE(&bpos->rb_node);
329 330
			continue;
		}
331
		disasm_rb_tree__insert(&browser->entries, bpos);
332 333 334 335 336 337
	}
	pthread_mutex_unlock(&notes->lock);

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

338 339
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
340
	struct disasm_line *dl;
341
	struct browser_disasm_line *bdl;
342 343 344
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
345
	dl = list_entry(browser->b.top, struct disasm_line, node);
346
	bdl = disasm_line__browser(dl);
347

348
	if (annotate_browser__opts.hide_src_code) {
349 350
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
351 352

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

364 365
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
366 367

		browser->b.nr_entries = browser->nr_asm_entries;
368
		annotate_browser__opts.hide_src_code = true;
369
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
370 371
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
372 373 374 375 376
	}

	return true;
}

377 378 379 380 381 382
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;
}

383 384
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
385
				    struct hist_browser_timer *hbt)
386 387
{
	struct map_symbol *ms = browser->b.priv;
388
	struct disasm_line *dl = browser->selection;
389 390 391 392 393
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	u64 ip;

394
	if (!ins__is_call(dl->ins))
395 396
		return false;

397
	ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
	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);
415
	symbol__tui_annotate(target, ms->map, evsel, hbt);
416 417 418 419
	ui_browser__show_title(&browser->b, sym->name);
	return true;
}

420 421 422
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
423 424 425 426
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
427
	struct disasm_line *pos;
428 429 430 431 432

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
433
		if (!disasm_line__filter(&browser->b, &pos->node))
434 435 436 437 438 439 440 441
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
442
	struct disasm_line *dl = browser->selection;
443
	s64 idx;
444

445
	if (!ins__is_jump(dl->ins))
446 447
		return false;

448
	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
449
	if (dl == NULL) {
450 451 452 453
		ui_helpline__puts("Invallid jump offset");
		return true;
	}

454
	annotate_browser__set_top(browser, dl, idx);
455 456 457 458
	
	return true;
}

459 460 461
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
462 463 464 465
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
466
	struct disasm_line *pos = browser->selection;
467 468 469

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
470
		if (disasm_line__filter(&browser->b, &pos->node))
471 472 473 474 475 476 477 478 479 480 481 482 483
			continue;

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
484
	struct disasm_line *dl;
485 486
	s64 idx;

487 488
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
489 490 491 492
		ui_helpline__puts("String not found!");
		return false;
	}

493
	annotate_browser__set_top(browser, dl, idx);
494 495 496 497
	browser->searching_backwards = false;
	return true;
}

498 499 500
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
501 502 503 504
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
505
	struct disasm_line *pos = browser->selection;
506 507 508

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
509
		if (disasm_line__filter(&browser->b, &pos->node))
510 511 512 513 514 515 516 517 518 519 520 521 522
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
523
	struct disasm_line *dl;
524 525
	s64 idx;

526 527
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
528 529 530 531
		ui_helpline__puts("String not found!");
		return false;
	}

532
	annotate_browser__set_top(browser, dl, idx);
533 534 535 536 537 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
	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);
}

585 586 587 588 589 590 591 592 593 594 595 596 597
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;
}

598 599
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
600
				 struct hist_browser_timer *hbt)
601 602
{
	struct rb_node *nd = NULL;
603
	struct map_symbol *ms = browser->b.priv;
604
	struct symbol *sym = ms->sym;
605
	const char *help = "Press 'h' for help on key bindings";
606
	int delay_secs = hbt ? hbt->refresh : 0;
607
	int key;
608

609
	if (ui_browser__show(&browser->b, sym->name, help) < 0)
610
		return -1;
611

612
	annotate_browser__calc_percent(browser, evsel);
613

614 615 616
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
617
	}
618

619
	nd = browser->curr_hot;
620

621
	while (1) {
622
		key = ui_browser__run(&browser->b, delay_secs);
623

624
		if (delay_secs != 0) {
625
			annotate_browser__calc_percent(browser, evsel);
626 627 628 629 630 631 632 633 634
			/*
			 * 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;
		}

635
		switch (key) {
636
		case K_TIMER:
637 638
			if (hbt)
				hbt->timer(hbt->arg);
639 640

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

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

760
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
761
			     struct hist_browser_timer *hbt)
762
{
763
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evsel, hbt);
764 765
}

766 767 768 769
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;
770 771 772 773 774 775
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;

	/* PLT symbols contain external offsets */
	if (strstr(sym->name, "@plt"))
		return;
776 777 778 779 780

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

781
		if (!disasm_line__is_valid_jump(dl, sym))
782 783
			continue;

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

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

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

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

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

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

836 837
	size = symbol__size(sym);

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

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

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

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

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

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

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

880 881
	annotate_browser__mark_jump_targets(&browser, size);

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

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

	annotate_browser__update_addr_width(&browser);

894
	ret = annotate_browser__run(&browser, evsel, hbt);
895
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
896
		list_del(&pos->node);
897
		disasm_line__free(pos);
898
	}
899 900 901

out_free_offsets:
	free(browser.offsets);
902 903
	return ret;
}
904 905 906

#define ANNOTATE_CFG(n) \
	{ .name = #n, .value = &annotate_browser__opts.n, }
907

908 909 910
/*
 * Keep the entries sorted, they are bsearch'ed
 */
911
static struct annotate_config {
912 913 914 915 916 917 918 919 920 921 922 923 924
	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)
{
925
	const struct annotate_config *cfg = cfgp;
926 927 928 929

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

930 931
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
932
{
933
	struct annotate_config *cfg;
934 935 936 937 938 939 940
	const char *name;

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

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
941
		      sizeof(struct annotate_config), annotate_config__cmp);
942 943 944 945 946 947 948 949 950 951 952 953

	if (cfg == NULL)
		return -1;

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

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