annotate.c 25.5 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

14 15 16 17
struct browser_disasm_line {
	struct rb_node	rb_node;
	u32		idx;
	int		idx_asm;
18
	int		jump_sources;
19 20 21 22
	/*
	 * actual length of this array is saved on the nr_events field
	 * of the struct annotate_browser
	 */
23
	double		percent[1];
24 25
};

26 27 28 29 30 31 32 33 34 35
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,
};

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

57
static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
58
{
59
	return (struct browser_disasm_line *)(dl + 1);
60 61
}

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

	return false;
}

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
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);
}

92
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
93
{
94
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
95
	struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
96
	struct browser_disasm_line *bdl = disasm_line__browser(dl);
97
	bool current_entry = ui_browser__is_current_entry(browser, row);
98
	bool change_color = (!annotate_browser__opts.hide_src_code &&
99 100 101
			     (!current_entry || (browser->use_navkeypressed &&
					         !browser->navkeypressed)));
	int width = browser->width, printed;
102 103
	int i, pcnt_width = 7 * ab->nr_events;
	double percent_max = 0.0;
104
	char bf[256];
105

106 107 108 109 110 111 112 113 114 115 116
	for (i = 0; i < ab->nr_events; i++) {
		if (bdl->percent[i] > percent_max)
			percent_max = bdl->percent[i];
	}

	if (dl->offset != -1 && percent_max != 0.0) {
		for (i = 0; i < ab->nr_events; i++) {
			ui_browser__set_percent_color(browser, bdl->percent[i],
						      current_entry);
			slsmg_printf("%6.2f ", bdl->percent[i]);
		}
117
	} else {
118
		ui_browser__set_percent_color(browser, 0, current_entry);
119
		slsmg_write_nstring(" ", pcnt_width);
120 121
	}

122
	SLsmg_write_char(' ');
123 124

	/* The scroll bar isn't being used */
125
	if (!browser->navkeypressed)
126 127
		width += 1;

128
	if (!*dl->line)
129
		slsmg_write_nstring(" ", width - pcnt_width);
130 131 132 133
	else if (dl->offset == -1) {
		printed = scnprintf(bf, sizeof(bf), "%*s  ",
				    ab->addr_width, " ");
		slsmg_write_nstring(bf, printed);
134
		slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1);
135
	} else {
136
		u64 addr = dl->offset;
137
		int color = -1;
138

139
		if (!annotate_browser__opts.use_offset)
140 141
			addr += ab->start;

142
		if (!annotate_browser__opts.use_offset) {
143
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
144
		} else {
145
			if (bdl->jump_sources) {
146
				if (annotate_browser__opts.show_nr_jumps) {
147 148 149 150 151 152 153
					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);
154
					ui_browser__set_color(browser, prev);
155 156
				}

157
				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
158
						    ab->target_width, addr);
159
			} else {
160 161
				printed = scnprintf(bf, sizeof(bf), "%*s  ",
						    ab->addr_width, " ");
162 163
			}
		}
164

165
		if (change_color)
166
			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
167 168
		slsmg_write_nstring(bf, printed);
		if (change_color)
169
			ui_browser__set_color(browser, color);
170
		if (dl->ins && dl->ins->ops->scnprintf) {
171
			if (ins__is_jump(dl->ins)) {
172
				bool fwd = dl->ops.target.offset > (u64)dl->offset;
173

174
				ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
175
								    SLSMG_UARROW_CHAR);
176
				SLsmg_write_char(' ');
177
			} else if (ins__is_call(dl->ins)) {
178
				ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
179
				SLsmg_write_char(' ');
180 181 182
			} else {
				slsmg_write_nstring(" ", 2);
			}
183 184 185 186
		} else {
			if (strcmp(dl->name, "retq")) {
				slsmg_write_nstring(" ", 2);
			} else {
187
				ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
188 189 190
				SLsmg_write_char(' ');
			}
		}
191

192
		disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
193
		slsmg_write_nstring(bf, width - pcnt_width - 3 - printed);
194
	}
195

196
	if (current_entry)
197
		ab->selection = dl;
198 199
}

200 201 202 203 204 205 206 207 208 209
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;
}

210
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
211 212
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
213 214
	struct disasm_line *cursor = ab->selection, *target;
	struct browser_disasm_line *btarget, *bcursor;
215
	unsigned int from, to;
216 217
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
218
	u8 pcnt_width = 7;
219 220 221 222

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

224
	if (!disasm_line__is_valid_jump(cursor, sym))
225
		return;
226

227 228 229
	target = ab->offsets[cursor->ops.target.offset];
	if (!target)
		return;
230

231 232
	bcursor = disasm_line__browser(cursor);
	btarget = disasm_line__browser(target);
233

234
	if (annotate_browser__opts.hide_src_code) {
235
		from = bcursor->idx_asm;
236 237
		to = btarget->idx_asm;
	} else {
238
		from = (u64)bcursor->idx;
239 240 241
		to = (u64)btarget->idx;
	}

242 243
	pcnt_width *= ab->nr_events;

244
	ui_browser__set_color(browser, HE_COLORSET_CODE);
245 246
	__ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
				 from, to);
247 248 249 250
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
251
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
252
	int ret = ui_browser__list_head_refresh(browser);
253 254 255
	int pcnt_width;

	pcnt_width = 7 * ab->nr_events;
256

257
	if (annotate_browser__opts.jump_arrows)
258
		annotate_browser__draw_current_jump(browser);
259

260
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
261
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
262 263 264
	return ret;
}

265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
static int disasm__cmp(struct browser_disasm_line *a,
		       struct browser_disasm_line *b, int nr_pcnt)
{
	int i;

	for (i = 0; i < nr_pcnt; i++) {
		if (a->percent[i] == b->percent[i])
			continue;
		return a->percent[i] < b->percent[i];
	}
	return 0;
}

static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl,
				   int nr_events)
280
{
281
	struct rb_node **p = &root->rb_node;
282
	struct rb_node *parent = NULL;
283
	struct browser_disasm_line *l;
284 285 286

	while (*p != NULL) {
		parent = *p;
287
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
288 289

		if (disasm__cmp(bdl, l, nr_events))
290 291 292 293
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
294 295
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
296 297
}

298
static void annotate_browser__set_top(struct annotate_browser *browser,
299
				      struct disasm_line *pos, u32 idx)
300 301 302
{
	unsigned back;

303 304 305
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
306

307
	while (browser->b.top_idx != 0 && back != 0) {
308
		pos = list_entry(pos->node.prev, struct disasm_line, node);
309

310
		if (disasm_line__filter(&browser->b, &pos->node))
311 312
			continue;

313
		--browser->b.top_idx;
314 315 316
		--back;
	}

317 318
	browser->b.top = pos;
	browser->b.navkeypressed = true;
319 320 321 322 323
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
324
	struct browser_disasm_line *bpos;
325
	struct disasm_line *pos;
326
	u32 idx;
327

328 329
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
330
	idx = bpos->idx;
331
	if (annotate_browser__opts.hide_src_code)
332 333
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
334
	browser->curr_hot = nd;
335 336
}

337
static void annotate_browser__calc_percent(struct annotate_browser *browser,
338
					   struct perf_evsel *evsel)
339
{
340 341
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
342
	struct annotation *notes = symbol__annotation(sym);
343 344
	struct disasm_line *pos, *next;
	s64 len = symbol__size(sym);
345 346 347 348 349 350

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
351
		struct browser_disasm_line *bpos = disasm_line__browser(pos);
352
		const char *path = NULL;
353 354
		double max_percent = 0.0;
		int i;
355 356 357 358 359 360 361 362

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

		next = disasm__get_next_ip_line(&notes->src->source, pos);

363 364 365 366 367 368 369 370 371 372 373 374
		for (i = 0; i < browser->nr_events; i++) {
			bpos->percent[i] = disasm__calc_percent(notes,
						evsel->idx + i,
						pos->offset,
						next ? next->offset : len,
					        &path);

			if (max_percent < bpos->percent[i])
				max_percent = bpos->percent[i];
		}

		if (max_percent < 0.01) {
375
			RB_CLEAR_NODE(&bpos->rb_node);
376 377
			continue;
		}
378 379
		disasm_rb_tree__insert(&browser->entries, bpos,
				       browser->nr_events);
380 381 382 383 384 385
	}
	pthread_mutex_unlock(&notes->lock);

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

386 387
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
388
	struct disasm_line *dl;
389
	struct browser_disasm_line *bdl;
390 391 392
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
393
	dl = list_entry(browser->b.top, struct disasm_line, node);
394
	bdl = disasm_line__browser(dl);
395

396
	if (annotate_browser__opts.hide_src_code) {
397 398
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
399 400

		browser->b.nr_entries = browser->nr_entries;
401
		annotate_browser__opts.hide_src_code = false;
402
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
403 404
		browser->b.top_idx = bdl->idx - offset;
		browser->b.index = bdl->idx;
405
	} else {
406
		if (bdl->idx_asm < 0) {
407 408 409 410 411
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

412 413
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
414 415

		browser->b.nr_entries = browser->nr_asm_entries;
416
		annotate_browser__opts.hide_src_code = true;
417
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
418 419
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
420 421 422 423 424
	}

	return true;
}

425 426 427 428 429 430
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;
}

431 432
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
433
				    struct hist_browser_timer *hbt)
434 435
{
	struct map_symbol *ms = browser->b.priv;
436
	struct disasm_line *dl = browser->selection;
437 438 439 440 441
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	u64 ip;

442
	if (!ins__is_call(dl->ins))
443 444
		return false;

445
	ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
	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);
463
	symbol__tui_annotate(target, ms->map, evsel, hbt);
464 465 466 467
	ui_browser__show_title(&browser->b, sym->name);
	return true;
}

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

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
481
		if (!disasm_line__filter(&browser->b, &pos->node))
482 483 484 485 486 487 488 489
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
490
	struct disasm_line *dl = browser->selection;
491
	s64 idx;
492

493
	if (!ins__is_jump(dl->ins))
494 495
		return false;

496
	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
497
	if (dl == NULL) {
498 499 500 501
		ui_helpline__puts("Invallid jump offset");
		return true;
	}

502
	annotate_browser__set_top(browser, dl, idx);
503 504 505 506
	
	return true;
}

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

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

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
532
	struct disasm_line *dl;
533 534
	s64 idx;

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

541
	annotate_browser__set_top(browser, dl, idx);
542 543 544 545
	browser->searching_backwards = false;
	return true;
}

546 547 548
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
549 550 551 552
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
553
	struct disasm_line *pos = browser->selection;
554 555 556

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
557
		if (disasm_line__filter(&browser->b, &pos->node))
558 559 560 561 562 563 564 565 566 567 568 569 570
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
571
	struct disasm_line *dl;
572 573
	s64 idx;

574 575
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
576 577 578 579
		ui_helpline__puts("String not found!");
		return false;
	}

580
	annotate_browser__set_top(browser, dl, idx);
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
	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);
}

633 634 635 636 637 638 639 640 641 642 643 644 645
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;
}

646 647
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
648
				 struct hist_browser_timer *hbt)
649 650
{
	struct rb_node *nd = NULL;
651
	struct map_symbol *ms = browser->b.priv;
652
	struct symbol *sym = ms->sym;
653
	const char *help = "Press 'h' for help on key bindings";
654
	int delay_secs = hbt ? hbt->refresh : 0;
655
	int key;
656

657
	if (ui_browser__show(&browser->b, sym->name, help) < 0)
658
		return -1;
659

660
	annotate_browser__calc_percent(browser, evsel);
661

662 663 664
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
665
	}
666

667
	nd = browser->curr_hot;
668

669
	while (1) {
670
		key = ui_browser__run(&browser->b, delay_secs);
671

672
		if (delay_secs != 0) {
673
			annotate_browser__calc_percent(browser, evsel);
674 675 676 677 678 679 680 681 682
			/*
			 * 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;
		}

683
		switch (key) {
684
		case K_TIMER:
685 686
			if (hbt)
				hbt->timer(hbt->arg);
687 688

			if (delay_secs != 0)
689
				symbol__annotate_decay_histogram(sym, evsel->idx);
690
			continue;
691
		case K_TAB:
692 693 694
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
695
					nd = rb_last(&browser->entries);
696
			} else
697
				nd = browser->curr_hot;
698
			break;
699
		case K_UNTAB:
700 701 702
			if (nd != NULL)
				nd = rb_next(nd);
				if (nd == NULL)
703
					nd = rb_first(&browser->entries);
704
			else
705
				nd = browser->curr_hot;
706
			break;
707
		case K_F1:
708
		case 'h':
709
			ui_browser__help_window(&browser->b,
710 711 712 713 714
		"UP/DOWN/PGUP\n"
		"PGDN/SPACE    Navigate\n"
		"q/ESC/CTRL+C  Exit\n\n"
		"->            Go to target\n"
		"<-            Exit\n"
715
		"H             Cycle thru hottest instructions\n"
716 717 718 719 720 721
		"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"
722
		"r             Run available scripts\n"
723 724
		"?             Search previous string\n");
			continue;
725 726 727 728 729
		case 'r':
			{
				script_browse(NULL);
				continue;
			}
730
		case 'H':
731
			nd = browser->curr_hot;
732
			break;
733
		case 's':
734
			if (annotate_browser__toggle_source(browser))
735 736
				ui_helpline__puts(help);
			continue;
737
		case 'o':
738
			annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
739
			annotate_browser__update_addr_width(browser);
740
			continue;
741
		case 'j':
742
			annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
743
			continue;
744
		case 'J':
745
			annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
746
			annotate_browser__update_addr_width(browser);
747
			continue;
748
		case '/':
749
			if (annotate_browser__search(browser, delay_secs)) {
750 751 752 753 754
show_help:
				ui_helpline__puts(help);
			}
			continue;
		case 'n':
755 756 757
			if (browser->searching_backwards ?
			    annotate_browser__continue_search_reverse(browser, delay_secs) :
			    annotate_browser__continue_search(browser, delay_secs))
758 759 760
				goto show_help;
			continue;
		case '?':
761
			if (annotate_browser__search_reverse(browser, delay_secs))
762 763
				goto show_help;
			continue;
764 765 766 767
		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",
768 769 770 771 772
					   seq++, browser->b.nr_entries,
					   browser->b.height,
					   browser->b.index,
					   browser->b.top_idx,
					   browser->nr_asm_entries);
773 774
		}
			continue;
775 776
		case K_ENTER:
		case K_RIGHT:
777
			if (browser->selection == NULL)
778
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
779
			else if (browser->selection->offset == -1)
780
				ui_helpline__puts("Actions are only available for assembly lines.");
781 782
			else if (!browser->selection->ins) {
				if (strcmp(browser->selection->name, "retq"))
783 784
					goto show_sup_ins;
				goto out;
785
			} else if (!(annotate_browser__jump(browser) ||
786
				     annotate_browser__callq(browser, evsel, hbt))) {
787 788 789
show_sup_ins:
				ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
			}
790
			continue;
791 792
		case K_LEFT:
		case K_ESC:
793 794
		case 'q':
		case CTRL('c'):
795
			goto out;
796 797
		default:
			continue;
798
		}
799 800

		if (nd != NULL)
801
			annotate_browser__set_rb_top(browser, nd);
802 803
	}
out:
804
	ui_browser__hide(&browser->b);
805
	return key;
806 807
}

808
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
809
			     struct hist_browser_timer *hbt)
810
{
811
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evsel, hbt);
812 813
}

814 815 816 817
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;
818 819 820 821 822 823
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;

	/* PLT symbols contain external offsets */
	if (strstr(sym->name, "@plt"))
		return;
824 825 826 827 828

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

829
		if (!disasm_line__is_valid_jump(dl, sym))
830 831
			continue;

832
		dlt = browser->offsets[dl->ops.target.offset];
833 834 835 836 837 838 839
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
		if (dlt == NULL)
			continue;

840
		bdlt = disasm_line__browser(dlt);
841 842 843 844
		if (++bdlt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = bdlt->jump_sources;

		++browser->nr_jumps;
845 846 847 848
	}
		
}

849 850 851 852 853 854 855 856 857
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

858 859
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
860
			 struct hist_browser_timer *hbt)
861
{
862
	struct disasm_line *pos, *n;
863
	struct annotation *notes;
864
	size_t size;
865 866 867 868
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
869 870
	struct annotate_browser browser = {
		.b = {
871
			.refresh = annotate_browser__refresh,
872 873
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
874
			.filter  = disasm_line__filter,
875
			.priv	 = &ms,
876
			.use_navkeypressed = true,
877
		},
878
	};
879
	int ret = -1;
880 881
	int nr_pcnt = 1;
	size_t sizeof_bdl = sizeof(struct browser_disasm_line);
882

883
	if (sym == NULL)
884 885
		return -1;

886 887
	size = symbol__size(sym);

888
	if (map->dso->annotate_warned)
889 890
		return -1;

891 892 893 894 895 896
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

897 898 899 900 901 902
	if (perf_evsel__is_group_event(evsel)) {
		nr_pcnt = evsel->nr_members;
		sizeof_bdl += sizeof(double) * (nr_pcnt - 1);
	}

	if (symbol__annotate(sym, map, sizeof_bdl) < 0) {
903
		ui__error("%s", ui_helpline__last_msg);
904
		goto out_free_offsets;
905 906 907 908
	}

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

909
	notes = symbol__annotation(sym);
910
	browser.start = map__rip_2objdump(map, sym->start);
911

912
	list_for_each_entry(pos, &notes->src->source, node) {
913
		struct browser_disasm_line *bpos;
914
		size_t line_len = strlen(pos->line);
915

916 917
		if (browser.b.width < line_len)
			browser.b.width = line_len;
918 919
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
920
		if (pos->offset != -1) {
921
			bpos->idx_asm = browser.nr_asm_entries++;
922 923 924 925 926 927 928 929 930
			/*
			 * 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;
931
		} else
932
			bpos->idx_asm = -1;
933 934
	}

935 936
	annotate_browser__mark_jump_targets(&browser, size);

937
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
938
	browser.max_addr_width = hex_width(sym->end);
939
	browser.jumps_width = width_jumps(browser.max_jump_sources);
940
	browser.nr_events = nr_pcnt;
941
	browser.b.nr_entries = browser.nr_entries;
942
	browser.b.entries = &notes->src->source,
943
	browser.b.width += 18; /* Percentage */
944 945 946 947 948 949

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

	annotate_browser__update_addr_width(&browser);

950
	ret = annotate_browser__run(&browser, evsel, hbt);
951
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
952
		list_del(&pos->node);
953
		disasm_line__free(pos);
954
	}
955 956 957

out_free_offsets:
	free(browser.offsets);
958 959
	return ret;
}
960 961 962

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

964 965 966
/*
 * Keep the entries sorted, they are bsearch'ed
 */
967
static struct annotate_config {
968 969 970 971 972 973 974 975 976 977 978 979 980
	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)
{
981
	const struct annotate_config *cfg = cfgp;
982 983 984 985

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

986 987
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
988
{
989
	struct annotate_config *cfg;
990 991 992 993 994 995 996
	const char *name;

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

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
997
		      sizeof(struct annotate_config), annotate_config__cmp);
998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009

	if (cfg == NULL)
		return -1;

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

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