annotate.c 23.3 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2
#include "../../util/util.h"
3 4
#include "../browser.h"
#include "../helpline.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 "../../util/config.h"
13
#include "../../util/evlist.h"
14
#include <inttypes.h>
15
#include <pthread.h>
16
#include <linux/kernel.h>
17
#include <linux/string.h>
18
#include <sys/ttydefaults.h>
19

20
struct disasm_line_samples {
21 22
	double		      percent;
	struct sym_hist_entry he;
23 24
};

25
static struct annotation_options annotate_browser__opts = {
26 27 28 29
	.use_offset	= true,
	.jump_arrows	= true,
};

30 31
struct arch;

32
struct annotate_browser {
33 34 35 36 37 38 39
	struct ui_browser	    b;
	struct rb_root		    entries;
	struct rb_node		   *curr_hot;
	struct annotation_line	   *selection;
	struct arch		   *arch;
	bool			    searching_backwards;
	char			    search_bf[128];
40 41
};

42 43 44 45 46 47
static inline struct annotation *browser__annotation(struct ui_browser *browser)
{
	struct map_symbol *ms = browser->priv;
	return symbol__annotation(ms->sym);
}

48
static bool disasm_line__filter(struct ui_browser *browser, void *entry)
49
{
50
	struct annotation *notes = browser__annotation(browser);
51 52

	if (notes->options->hide_src_code) {
53 54 55
		struct annotation_line *al = list_entry(entry, struct annotation_line, node);

		return al->offset == -1;
56 57 58 59 60
	}

	return false;
}

61
static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
62
{
63
	struct annotation *notes = browser__annotation(browser);
64

65
	if (current && (!browser->use_navkeypressed || browser->navkeypressed))
66
		return HE_COLORSET_SELECTED;
67
	if (nr == notes->max_jump_sources)
68 69 70 71 72 73
		return HE_COLORSET_TOP;
	if (nr > 1)
		return HE_COLORSET_MEDIUM;
	return HE_COLORSET_NORMAL;
}

74
static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current)
75
{
76 77
	 int color = ui_browser__jumps_percent_color(browser, nr, current);
	 return ui_browser__set_color(browser, color);
78 79
}

80
static int annotate_browser__set_color(void *browser, int color)
81
{
82 83
	return ui_browser__set_color(browser, color);
}
84

85 86 87
static void annotate_browser__write_graph(void *browser, int graph)
{
	ui_browser__write_graph(browser, graph);
88 89
}

90 91 92 93 94 95 96 97 98 99 100 101 102 103
static void annotate_browser__set_percent_color(void *browser, double percent, bool current)
{
	ui_browser__set_percent_color(browser, percent, current);
}

static void annotate_browser__printf(void *browser, const char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);
	ui_browser__vprintf(browser, fmt, args);
	va_end(args);
}

104
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
105
{
106
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
107
	struct annotation *notes = browser__annotation(browser);
108
	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
109
	bool current_entry = ui_browser__is_current_entry(browser, row);
110
	bool change_color = (!notes->options->hide_src_code &&
111 112
			     (!current_entry || (browser->use_navkeypressed &&
					         !browser->navkeypressed)));
113
	int width = browser->width;
114 115

	/* The scroll bar isn't being used */
116
	if (!browser->navkeypressed)
117 118
		width += 1;

119 120 121 122 123 124 125
	annotation_line__write(al, notes, row == 0, current_entry, change_color,
			       width, browser,
			       annotate_browser__set_color,
			       annotate_browser__set_percent_color,
			       ui_browser__set_jumps_percent_color,
			       annotate_browser__printf,
			       annotate_browser__write_graph);
126

127
	if (current_entry)
128
		ab->selection = al;
129 130
}

131 132
static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
{
133
	struct disasm_line *pos = list_prev_entry(cursor, al.node);
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
	const char *name;

	if (!pos)
		return false;

	if (ins__is_lock(&pos->ins))
		name = pos->ops.locked.ins.name;
	else
		name = pos->ins.name;

	if (!name || !cursor->ins.name)
		return false;

	return ins__is_fused(ab->arch, name, cursor->ins.name);
}

150
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
151 152
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
153
	struct disasm_line *cursor = disasm_line(ab->selection);
154
	struct annotation_line *target;
155
	unsigned int from, to;
156 157
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
158
	struct annotation *notes = symbol__annotation(sym);
159
	u8 pcnt_width = annotation__pcnt_width(notes);
160
	int width;
161 162 163 164

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

166
	if (!disasm_line__is_valid_jump(cursor, sym))
167
		return;
168

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
	/*
	 * This first was seen with a gcc function, _cpp_lex_token, that
	 * has the usual jumps:
	 *
	 *  │1159e6c: ↓ jne    115aa32 <_cpp_lex_token@@Base+0xf92>
	 *
	 * I.e. jumps to a label inside that function (_cpp_lex_token), and
	 * those works, but also this kind:
	 *
	 *  │1159e8b: ↓ jne    c469be <cpp_named_operator2name@@Base+0xa72>
	 *
	 *  I.e. jumps to another function, outside _cpp_lex_token, which
	 *  are not being correctly handled generating as a side effect references
	 *  to ab->offset[] entries that are set to NULL, so to make this code
	 *  more robust, check that here.
	 *
	 *  A proper fix for will be put in place, looking at the function
	 *  name right after the '<' token and probably treating this like a
	 *  'call' instruction.
	 */
189
	target = notes->offsets[cursor->ops.target.offset];
190
	if (target == NULL) {
191
		ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
192 193 194
				    cursor->ops.target.offset);
		return;
	}
195

196
	if (notes->options->hide_src_code) {
197 198
		from = cursor->al.idx_asm;
		to = target->idx_asm;
199
	} else {
200 201
		from = (u64)cursor->al.idx;
		to = (u64)target->idx;
202 203
	}

204
	width = annotation__cycles_width(notes);
J
Jin Yao 已提交
205

206
	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
J
Jin Yao 已提交
207
	__ui_browser__line_arrow(browser,
208
				 pcnt_width + 2 + notes->widths.addr + width,
209
				 from, to);
210 211 212

	if (is_fused(ab, cursor)) {
		ui_browser__mark_fused(browser,
213
				       pcnt_width + 3 + notes->widths.addr + width,
214 215 216
				       from - 1,
				       to > from ? true : false);
	}
217 218 219 220
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
221
	struct annotation *notes = browser__annotation(browser);
222
	int ret = ui_browser__list_head_refresh(browser);
223
	int pcnt_width = annotation__pcnt_width(notes);
224

225
	if (notes->options->jump_arrows)
226
		annotate_browser__draw_current_jump(browser);
227

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

233
static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
234 235 236
{
	int i;

237
	for (i = 0; i < a->samples_nr; i++) {
238
		if (a->samples[i].percent == b->samples[i].percent)
239
			continue;
240
		return a->samples[i].percent < b->samples[i].percent;
241 242 243 244
	}
	return 0;
}

245
static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
246
{
247
	struct rb_node **p = &root->rb_node;
248
	struct rb_node *parent = NULL;
249
	struct annotation_line *l;
250 251 252

	while (*p != NULL) {
		parent = *p;
253
		l = rb_entry(parent, struct annotation_line, rb_node);
254

255
		if (disasm__cmp(al, l))
256 257 258 259
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
260 261
	rb_link_node(&al->rb_node, parent, p);
	rb_insert_color(&al->rb_node, root);
262 263
}

264
static void annotate_browser__set_top(struct annotate_browser *browser,
265
				      struct annotation_line *pos, u32 idx)
266 267 268
{
	unsigned back;

269 270 271
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
272

273
	while (browser->b.top_idx != 0 && back != 0) {
274
		pos = list_entry(pos->node.prev, struct annotation_line, node);
275

276
		if (disasm_line__filter(&browser->b, &pos->node))
277 278
			continue;

279
		--browser->b.top_idx;
280 281 282
		--back;
	}

283
	browser->b.top = pos;
284
	browser->b.navkeypressed = true;
285 286 287 288 289
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
290
	struct annotation *notes = browser__annotation(&browser->b);
291 292
	struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
	u32 idx = pos->idx;
293

294
	if (notes->options->hide_src_code)
295
		idx = pos->idx_asm;
296
	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
	struct disasm_line *pos;
307 308 309 310 311

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

312 313
	symbol__calc_percent(sym, evsel);

314
	list_for_each_entry(pos, &notes->src->source, al.node) {
315 316
		double max_percent = 0.0;
		int i;
317

318
		if (pos->al.offset == -1) {
319
			RB_CLEAR_NODE(&pos->al.rb_node);
320 321 322
			continue;
		}

323
		for (i = 0; i < pos->al.samples_nr; i++) {
324 325
			struct annotation_data *sample = &pos->al.samples[i];

326 327
			if (max_percent < sample->percent)
				max_percent = sample->percent;
328 329
		}

330
		if (max_percent < 0.01 && pos->al.ipc == 0) {
331
			RB_CLEAR_NODE(&pos->al.rb_node);
332 333
			continue;
		}
334
		disasm_rb_tree__insert(&browser->entries, &pos->al);
335 336 337 338 339 340
	}
	pthread_mutex_unlock(&notes->lock);

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

341 342
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
343
	struct annotation *notes = browser__annotation(&browser->b);
344
	struct annotation_line *al;
345 346 347
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
348
	al = list_entry(browser->b.top, struct annotation_line, node);
349

350
	if (notes->options->hide_src_code) {
351 352
		if (al->idx_asm < offset)
			offset = al->idx;
353

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

366 367
		if (al->idx_asm < offset)
			offset = al->idx_asm;
368

369
		browser->b.nr_entries = notes->nr_asm_entries;
370
		notes->options->hide_src_code = true;
371
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
372 373
		browser->b.top_idx = al->idx_asm - offset;
		browser->b.index = al->idx_asm;
374 375 376 377 378
	}

	return true;
}

379
static void ui_browser__init_asm_mode(struct ui_browser *browser)
380
{
381 382 383
	struct annotation *notes = browser__annotation(browser);
	ui_browser__reset_index(browser);
	browser->nr_entries = notes->nr_asm_entries;
384 385
}

386 387 388 389 390 391 392 393
#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)

static int sym_title(struct symbol *sym, struct map *map, char *title,
		     size_t sz)
{
	return snprintf(title, sz, "%s  %s", sym->name, map->dso->long_name);
}

394 395
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
396
				    struct hist_browser_timer *hbt)
397 398
{
	struct map_symbol *ms = browser->b.priv;
399
	struct disasm_line *dl = disasm_line(browser->selection);
400
	struct annotation *notes;
401
	char title[SYM_TITLE_MAX_SIZE];
402

403
	if (!ins__is_call(&dl->ins))
404 405
		return false;

406
	if (!dl->ops.target.sym) {
407 408 409 410
		ui_helpline__puts("The called function was not found.");
		return true;
	}

411
	notes = symbol__annotation(dl->ops.target.sym);
412 413
	pthread_mutex_lock(&notes->lock);

414
	if (notes->src == NULL && symbol__alloc_hist(dl->ops.target.sym) < 0) {
415 416
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
417
			    dl->ops.target.sym->name);
418 419 420 421
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
422
	symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt);
423
	sym_title(ms->sym, ms->map, title, sizeof(title));
424
	ui_browser__show_title(&browser->b, title);
425 426 427
	return true;
}

428 429 430
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
431
{
432
	struct annotation *notes = browser__annotation(&browser->b);
433
	struct disasm_line *pos;
434 435

	*idx = 0;
436
	list_for_each_entry(pos, &notes->src->source, al.node) {
437
		if (pos->al.offset == offset)
438
			return pos;
439
		if (!disasm_line__filter(&browser->b, &pos->al.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 = disasm_line(browser->selection);
449
	u64 offset;
450
	s64 idx;
451

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

455 456
	offset = dl->ops.target.offset;
	dl = annotate_browser__find_offset(browser, offset, &idx);
457
	if (dl == NULL) {
458
		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
459 460 461
		return true;
	}

462
	annotate_browser__set_top(browser, &dl->al, idx);
463

464 465 466
	return true;
}

467
static
468
struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
469
					  char *s, s64 *idx)
470
{
471
	struct annotation *notes = browser__annotation(&browser->b);
472
	struct annotation_line *al = browser->selection;
473 474

	*idx = browser->b.index;
475 476
	list_for_each_entry_continue(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
477 478 479 480
			continue;

		++*idx;

481 482
		if (al->line && strstr(al->line, s) != NULL)
			return al;
483 484 485 486 487 488 489
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
490
	struct annotation_line *al;
491 492
	s64 idx;

493 494
	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (al == NULL) {
495 496 497 498
		ui_helpline__puts("String not found!");
		return false;
	}

499
	annotate_browser__set_top(browser, al, idx);
500 501 502 503
	browser->searching_backwards = false;
	return true;
}

504
static
505
struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
506
						  char *s, s64 *idx)
507
{
508
	struct annotation *notes = browser__annotation(&browser->b);
509
	struct annotation_line *al = browser->selection;
510 511

	*idx = browser->b.index;
512 513
	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
514 515 516 517
			continue;

		--*idx;

518 519
		if (al->line && strstr(al->line, s) != NULL)
			return al;
520 521 522 523 524 525 526
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
527
	struct annotation_line *al;
528 529
	s64 idx;

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

536
	annotate_browser__set_top(browser, al, idx);
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 585 586 587 588
	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);
}

589 590
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
591
				 struct hist_browser_timer *hbt)
592 593
{
	struct rb_node *nd = NULL;
594
	struct map_symbol *ms = browser->b.priv;
595
	struct symbol *sym = ms->sym;
596
	struct annotation *notes = symbol__annotation(ms->sym);
597
	const char *help = "Press 'h' for help on key bindings";
598
	int delay_secs = hbt ? hbt->refresh : 0;
599
	int key;
600
	char title[SYM_TITLE_MAX_SIZE];
601

602 603
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
604
		return -1;
605

606
	annotate_browser__calc_percent(browser, evsel);
607

608 609 610
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
611
	}
612

613
	nd = browser->curr_hot;
614

615
	while (1) {
616
		key = ui_browser__run(&browser->b, delay_secs);
617

618
		if (delay_secs != 0) {
619
			annotate_browser__calc_percent(browser, evsel);
620 621 622 623 624 625 626 627 628
			/*
			 * 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;
		}

629
		switch (key) {
630
		case K_TIMER:
631 632
			if (hbt)
				hbt->timer(hbt->arg);
633 634

			if (delay_secs != 0)
635
				symbol__annotate_decay_histogram(sym, evsel->idx);
636
			continue;
637
		case K_TAB:
638 639 640
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
641
					nd = rb_last(&browser->entries);
642
			} else
643
				nd = browser->curr_hot;
644
			break;
645
		case K_UNTAB:
646
			if (nd != NULL) {
647 648
				nd = rb_next(nd);
				if (nd == NULL)
649
					nd = rb_first(&browser->entries);
650
			} else
651
				nd = browser->curr_hot;
652
			break;
653
		case K_F1:
654
		case 'h':
655
			ui_browser__help_window(&browser->b,
656 657 658
		"UP/DOWN/PGUP\n"
		"PGDN/SPACE    Navigate\n"
		"q/ESC/CTRL+C  Exit\n\n"
659 660
		"ENTER         Go to target\n"
		"ESC           Exit\n"
661 662
		"H             Go to hottest instruction\n"
		"TAB/shift+TAB Cycle thru hottest instructions\n"
663 664 665 666 667
		"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"
668
		"t             Circulate percent, total period, samples view\n"
669
		"/             Search string\n"
670
		"k             Toggle line numbers\n"
671
		"r             Run available scripts\n"
672
		"?             Search string backwards\n");
673
			continue;
674 675 676 677 678
		case 'r':
			{
				script_browse(NULL);
				continue;
			}
679
		case 'k':
680
			notes->options->show_linenr = !notes->options->show_linenr;
681
			break;
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
			notes->options->use_offset = !notes->options->use_offset;
691
			annotation__update_column_widths(notes);
692
			continue;
693
		case 'j':
694
			notes->options->jump_arrows = !notes->options->jump_arrows;
695
			continue;
696
		case 'J':
697
			notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
698
			annotation__update_column_widths(notes);
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
					   seq++, browser->b.nr_entries,
					   browser->b.height,
					   browser->b.index,
					   browser->b.top_idx,
724
					   notes->nr_asm_entries);
725 726
		}
			continue;
727 728
		case K_ENTER:
		case K_RIGHT:
729 730 731
		{
			struct disasm_line *dl = disasm_line(browser->selection);

732
			if (browser->selection == NULL)
733
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
734
			else if (browser->selection->offset == -1)
735
				ui_helpline__puts("Actions are only available for assembly lines.");
736
			else if (!dl->ins.ops)
737
				goto show_sup_ins;
738
			else if (ins__is_ret(&dl->ins))
739
				goto out;
740
			else if (!(annotate_browser__jump(browser) ||
741
				     annotate_browser__callq(browser, evsel, hbt))) {
742
show_sup_ins:
743
				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
744
			}
745
			continue;
746
		}
747
		case 't':
748 749 750 751 752
			if (notes->options->show_total_period) {
				notes->options->show_total_period = false;
				notes->options->show_nr_samples = true;
			} else if (notes->options->show_nr_samples)
				notes->options->show_nr_samples = false;
753
			else
754
				notes->options->show_total_period = true;
755
			annotation__update_column_widths(notes);
756
			continue;
757 758
		case K_LEFT:
		case K_ESC:
759 760
		case 'q':
		case CTRL('c'):
761
			goto out;
762 763
		default:
			continue;
764
		}
765 766

		if (nd != NULL)
767
			annotate_browser__set_rb_top(browser, nd);
768 769
	}
out:
770
	ui_browser__hide(&browser->b);
771
	return key;
772 773
}

774 775 776
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
			     struct hist_browser_timer *hbt)
{
777
	/* Set default value for show_total_period and show_nr_samples  */
778
	annotate_browser__opts.show_total_period =
779 780 781
		symbol_conf.show_total_period;
	annotate_browser__opts.show_nr_samples =
		symbol_conf.show_nr_samples;
782

783 784 785
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

786
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
787
			     struct hist_browser_timer *hbt)
788
{
789 790 791 792
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

793
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
794 795
}

796 797
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
798
			 struct hist_browser_timer *hbt)
799
{
800
	struct annotation *notes = symbol__annotation(sym);
801 802 803 804
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
805 806
	struct annotate_browser browser = {
		.b = {
807
			.refresh = annotate_browser__refresh,
808 809
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
810
			.filter  = disasm_line__filter,
811
			.priv	 = &ms,
812
			.use_navkeypressed = true,
813
		},
814
	};
815
	int ret = -1, err;
816

817
	if (sym == NULL)
818 819
		return -1;

820
	if (map->dso->annotate_warned)
821 822
		return -1;

823
	err = symbol__annotate2(sym, map, evsel, &annotate_browser__opts, &browser.arch);
824 825 826 827
	if (err) {
		char msg[BUFSIZ];
		symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
		ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
828
		goto out_free_offsets;
829 830
	}

831
	ui_helpline__push("Press ESC to exit");
832

833
	browser.b.width = notes->max_line_len;
834
	browser.b.nr_entries = notes->nr_entries;
835
	browser.b.entries = &notes->src->source,
836
	browser.b.width += 18; /* Percentage */
837

838
	if (notes->options->hide_src_code)
839
		ui_browser__init_asm_mode(&browser.b);
840

841
	ret = annotate_browser__run(&browser, evsel, hbt);
842 843

	annotated_source__purge(notes->src);
844 845

out_free_offsets:
846
	zfree(&notes->offsets);
847 848
	return ret;
}
849 850 851

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

853 854 855
/*
 * Keep the entries sorted, they are bsearch'ed
 */
856
static struct annotate_config {
857 858 859 860 861
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
862
	ANNOTATE_CFG(show_linenr),
863
	ANNOTATE_CFG(show_nr_jumps),
864
	ANNOTATE_CFG(show_nr_samples),
865
	ANNOTATE_CFG(show_total_period),
866
	ANNOTATE_CFG(use_offset),
867 868 869 870 871 872
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
873
	const struct annotate_config *cfg = cfgp;
874 875 876 877

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

878 879
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
880
{
881
	struct annotate_config *cfg;
882 883
	const char *name;

884
	if (!strstarts(var, "annotate."))
885 886 887 888
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
889
		      sizeof(struct annotate_config), annotate_config__cmp);
890 891

	if (cfg == NULL)
892 893 894
		ui__warning("%s variable unknown, ignoring...", var);
	else
		*cfg->value = perf_config_bool(name, value);
895 896 897 898 899 900 901
	return 0;
}

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