annotate.c 25.4 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2 3
#include "../browser.h"
#include "../helpline.h"
4
#include "../ui.h"
5
#include "../../util/annotate.h"
6
#include "../../util/debug.h"
7
#include "../../util/dso.h"
8 9
#include "../../util/hist.h"
#include "../../util/sort.h"
10
#include "../../util/map.h"
11
#include "../../util/symbol.h"
12
#include "../../util/evsel.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 <linux/zalloc.h>
19
#include <sys/ttydefaults.h>
20
#include <asm/bug.h>
21

22
struct disasm_line_samples {
23 24
	double		      percent;
	struct sym_hist_entry he;
25 26
};

27 28
struct arch;

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

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

46
static bool disasm_line__filter(struct ui_browser *browser, void *entry)
47
{
48
	struct annotation *notes = browser__annotation(browser);
49 50
	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
	return annotation_line__filter(al, notes);
51 52
}

53
static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
54
{
55
	struct annotation *notes = browser__annotation(browser);
56

57
	if (current && (!browser->use_navkeypressed || browser->navkeypressed))
58
		return HE_COLORSET_SELECTED;
59
	if (nr == notes->max_jump_sources)
60 61 62 63 64 65
		return HE_COLORSET_TOP;
	if (nr > 1)
		return HE_COLORSET_MEDIUM;
	return HE_COLORSET_NORMAL;
}

66
static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current)
67
{
68 69
	 int color = ui_browser__jumps_percent_color(browser, nr, current);
	 return ui_browser__set_color(browser, color);
70 71
}

72
static int annotate_browser__set_color(void *browser, int color)
73
{
74 75
	return ui_browser__set_color(browser, color);
}
76

77 78 79
static void annotate_browser__write_graph(void *browser, int graph)
{
	ui_browser__write_graph(browser, graph);
80 81
}

82 83 84 85 86 87 88 89 90 91 92 93 94 95
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);
}

96
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
97
{
98
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
99
	struct annotation *notes = browser__annotation(browser);
100
	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
101
	const bool is_current_entry = ui_browser__is_current_entry(browser, row);
102 103
	struct annotation_write_ops ops = {
		.first_line		 = row == 0,
104
		.current_entry		 = is_current_entry,
105
		.change_color		 = (!notes->options->hide_src_code &&
106
					    (!is_current_entry ||
107 108 109 110 111 112 113 114 115 116
					     (browser->use_navkeypressed &&
					      !browser->navkeypressed))),
		.width			 = browser->width,
		.obj			 = browser,
		.set_color		 = annotate_browser__set_color,
		.set_percent_color	 = annotate_browser__set_percent_color,
		.set_jumps_percent_color = ui_browser__set_jumps_percent_color,
		.printf			 = annotate_browser__printf,
		.write_graph		 = annotate_browser__write_graph,
	};
117 118

	/* The scroll bar isn't being used */
119
	if (!browser->navkeypressed)
120
		ops.width += 1;
121

122
	annotation_line__write(al, notes, &ops, ab->opts);
123

124
	if (ops.current_entry)
125
		ab->selection = al;
126 127
}

128 129
static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
{
130
	struct disasm_line *pos = list_prev_entry(cursor, al.node);
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
	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);
}

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

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

163
	if (!disasm_line__is_valid_local_jump(cursor, sym))
164
		return;
165

166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
	/*
	 * 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.
	 */
186
	target = notes->offsets[cursor->ops.target.offset];
187
	if (target == NULL) {
188
		ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
189 190 191
				    cursor->ops.target.offset);
		return;
	}
192

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

201
	width = annotation__cycles_width(notes);
J
Jin Yao 已提交
202

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

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

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
218
	struct annotation *notes = browser__annotation(browser);
219
	int ret = ui_browser__list_head_refresh(browser);
220
	int pcnt_width = annotation__pcnt_width(notes);
221

222
	if (notes->options->jump_arrows)
223
		annotate_browser__draw_current_jump(browser);
224

225
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
226
	__ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1);
227 228 229
	return ret;
}

230 231
static double disasm__cmp(struct annotation_line *a, struct annotation_line *b,
						  int percent_type)
232 233 234
{
	int i;

235
	for (i = 0; i < a->data_nr; i++) {
236
		if (a->data[i].percent[percent_type] == b->data[i].percent[percent_type])
237
			continue;
238 239
		return a->data[i].percent[percent_type] -
			   b->data[i].percent[percent_type];
240 241 242 243
	}
	return 0;
}

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

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

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

265
static void annotate_browser__set_top(struct annotate_browser *browser,
266
				      struct annotation_line *pos, u32 idx)
267
{
268
	struct annotation *notes = browser__annotation(&browser->b);
269 270
	unsigned back;

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

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

278
		if (annotation_line__filter(pos, notes))
279 280
			continue;

281
		--browser->b.top_idx;
282 283 284
		--back;
	}

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

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

296
	if (notes->options->hide_src_code)
297
		idx = pos->idx_asm;
298
	annotate_browser__set_top(browser, pos, idx);
299
	browser->curr_hot = nd;
300 301
}

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

314 315
	symbol__calc_percent(sym, evsel);

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

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

325
		for (i = 0; i < pos->al.data_nr; i++) {
326
			double percent;
327

328
			percent = annotation_data__percent(&pos->al.data[i],
329
							   browser->opts->percent_type);
330 331 332

			if (max_percent < percent)
				max_percent = percent;
333 334
		}

335
		if (max_percent < 0.01 && pos->al.ipc == 0) {
336
			RB_CLEAR_NODE(&pos->al.rb_node);
337 338
			continue;
		}
339
		disasm_rb_tree__insert(browser, &pos->al);
340 341 342 343 344 345
	}
	pthread_mutex_unlock(&notes->lock);

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

346 347
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
348
	struct annotation *notes = browser__annotation(&browser->b);
349
	struct annotation_line *al;
350 351 352
	off_t offset = browser->b.index - browser->b.top_idx;

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

355
	if (notes->options->hide_src_code) {
356 357
		if (al->idx_asm < offset)
			offset = al->idx;
358

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

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

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

	return true;
}

384
static void ui_browser__init_asm_mode(struct ui_browser *browser)
385
{
386 387 388
	struct annotation *notes = browser__annotation(browser);
	ui_browser__reset_index(browser);
	browser->nr_entries = notes->nr_asm_entries;
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,
394
		     size_t sz, int percent_type)
395
{
396 397
	return snprintf(title, sz, "%s  %s [Percent: %s]", sym->name, map->dso->long_name,
			percent_type_str(percent_type));
398 399
}

400 401 402 403 404 405 406 407 408
/*
 * This can be called from external jumps, i.e. jumps from one functon
 * to another, like from the kernel's entry_SYSCALL_64 function to the
 * swapgs_restore_regs_and_return_to_usermode() function.
 *
 * So all we check here is that dl->ops.target.sym is set, if it is, just
 * go to that function and when exiting from its disassembly, come back
 * to the calling function.
 */
409
static bool annotate_browser__callq(struct annotate_browser *browser,
410
				    struct evsel *evsel,
411
				    struct hist_browser_timer *hbt)
412
{
413
	struct map_symbol *ms = browser->b.priv, target_ms;
414
	struct disasm_line *dl = disasm_line(browser->selection);
415
	struct annotation *notes;
416
	char title[SYM_TITLE_MAX_SIZE];
417

418
	if (!dl->ops.target.sym) {
419 420 421 422
		ui_helpline__puts("The called function was not found.");
		return true;
	}

423
	notes = symbol__annotation(dl->ops.target.sym);
424 425
	pthread_mutex_lock(&notes->lock);

426
	if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) {
427 428
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
429
			    dl->ops.target.sym->name);
430 431 432
		return true;
	}

433
	target_ms.maps = ms->maps;
434 435
	target_ms.map = ms->map;
	target_ms.sym = dl->ops.target.sym;
436
	pthread_mutex_unlock(&notes->lock);
437
	symbol__tui_annotate(&target_ms, evsel, hbt, browser->opts);
438
	sym_title(ms->sym, ms->map, title, sizeof(title), browser->opts->percent_type);
439
	ui_browser__show_title(&browser->b, title);
440 441 442
	return true;
}

443 444 445
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
446
{
447
	struct annotation *notes = browser__annotation(&browser->b);
448
	struct disasm_line *pos;
449 450

	*idx = 0;
451
	list_for_each_entry(pos, &notes->src->source, al.node) {
452
		if (pos->al.offset == offset)
453
			return pos;
454
		if (!annotation_line__filter(&pos->al, notes))
455 456 457 458 459 460
			++*idx;
	}

	return NULL;
}

461
static bool annotate_browser__jump(struct annotate_browser *browser,
462
				   struct evsel *evsel,
463
				   struct hist_browser_timer *hbt)
464
{
465
	struct disasm_line *dl = disasm_line(browser->selection);
466
	u64 offset;
467
	s64 idx;
468

469
	if (!ins__is_jump(&dl->ins))
470 471
		return false;

472 473 474 475 476
	if (dl->ops.target.outside) {
		annotate_browser__callq(browser, evsel, hbt);
		return true;
	}

477 478
	offset = dl->ops.target.offset;
	dl = annotate_browser__find_offset(browser, offset, &idx);
479
	if (dl == NULL) {
480
		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
481 482 483
		return true;
	}

484
	annotate_browser__set_top(browser, &dl->al, idx);
485

486 487 488
	return true;
}

489
static
490
struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
491
					  char *s, s64 *idx)
492
{
493
	struct annotation *notes = browser__annotation(&browser->b);
494
	struct annotation_line *al = browser->selection;
495 496

	*idx = browser->b.index;
497
	list_for_each_entry_continue(al, &notes->src->source, node) {
498
		if (annotation_line__filter(al, notes))
499 500 501 502
			continue;

		++*idx;

503 504
		if (al->line && strstr(al->line, s) != NULL)
			return al;
505 506 507 508 509 510 511
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
512
	struct annotation_line *al;
513 514
	s64 idx;

515 516
	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (al == NULL) {
517 518 519 520
		ui_helpline__puts("String not found!");
		return false;
	}

521
	annotate_browser__set_top(browser, al, idx);
522 523 524 525
	browser->searching_backwards = false;
	return true;
}

526
static
527
struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
528
						  char *s, s64 *idx)
529
{
530
	struct annotation *notes = browser__annotation(&browser->b);
531
	struct annotation_line *al = browser->selection;
532 533

	*idx = browser->b.index;
534
	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
535
		if (annotation_line__filter(al, notes))
536 537 538 539
			continue;

		--*idx;

540 541
		if (al->line && strstr(al->line, s) != NULL)
			return al;
542 543 544 545 546 547 548
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
549
	struct annotation_line *al;
550 551
	s64 idx;

552 553
	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (al == NULL) {
554 555 556 557
		ui_helpline__puts("String not found!");
		return false;
	}

558
	annotate_browser__set_top(browser, al, idx);
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 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
	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);
}

611 612
static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
{
613
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
614 615 616 617 618 619 620
	struct map_symbol *ms = browser->priv;
	struct symbol *sym = ms->sym;
	char symbol_dso[SYM_TITLE_MAX_SIZE];

	if (ui_browser__show(browser, title, help) < 0)
		return -1;

621
	sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), ab->opts->percent_type);
622 623 624 625 626 627 628

	ui_browser__gotorc_title(browser, 0, 0);
	ui_browser__set_color(browser, HE_COLORSET_ROOT);
	ui_browser__write_nstring(browser, symbol_dso, browser->width + 1);
	return 0;
}

629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
static void
switch_percent_type(struct annotation_options *opts, bool base)
{
	switch (opts->percent_type) {
	case PERCENT_HITS_LOCAL:
		if (base)
			opts->percent_type = PERCENT_PERIOD_LOCAL;
		else
			opts->percent_type = PERCENT_HITS_GLOBAL;
		break;
	case PERCENT_HITS_GLOBAL:
		if (base)
			opts->percent_type = PERCENT_PERIOD_GLOBAL;
		else
			opts->percent_type = PERCENT_HITS_LOCAL;
		break;
	case PERCENT_PERIOD_LOCAL:
		if (base)
			opts->percent_type = PERCENT_HITS_LOCAL;
		else
			opts->percent_type = PERCENT_PERIOD_GLOBAL;
		break;
	case PERCENT_PERIOD_GLOBAL:
		if (base)
			opts->percent_type = PERCENT_HITS_GLOBAL;
		else
			opts->percent_type = PERCENT_PERIOD_LOCAL;
		break;
	default:
		WARN_ON(1);
	}
}

662
static int annotate_browser__run(struct annotate_browser *browser,
663
				 struct evsel *evsel,
664
				 struct hist_browser_timer *hbt)
665 666
{
	struct rb_node *nd = NULL;
667
	struct hists *hists = evsel__hists(evsel);
668
	struct map_symbol *ms = browser->b.priv;
669
	struct symbol *sym = ms->sym;
670
	struct annotation *notes = symbol__annotation(ms->sym);
671
	const char *help = "Press 'h' for help on key bindings";
672
	int delay_secs = hbt ? hbt->refresh : 0;
673
	char title[256];
674
	int key;
675

676
	hists__scnprintf_title(hists, title, sizeof(title));
677
	if (annotate_browser__show(&browser->b, title, help) < 0)
678
		return -1;
679

680
	annotate_browser__calc_percent(browser, evsel);
681

682 683 684
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
685
	}
686

687
	nd = browser->curr_hot;
688

689
	while (1) {
690
		key = ui_browser__run(&browser->b, delay_secs);
691

692
		if (delay_secs != 0) {
693
			annotate_browser__calc_percent(browser, evsel);
694 695 696 697 698 699 700 701 702
			/*
			 * 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;
		}

703
		switch (key) {
704
		case K_TIMER:
705 706
			if (hbt)
				hbt->timer(hbt->arg);
707

708
			if (delay_secs != 0) {
709
				symbol__annotate_decay_histogram(sym, evsel->idx);
710 711 712
				hists__scnprintf_title(hists, title, sizeof(title));
				annotate_browser__show(&browser->b, title, help);
			}
713
			continue;
714
		case K_TAB:
715 716 717
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
718
					nd = rb_last(&browser->entries);
719
			} else
720
				nd = browser->curr_hot;
721
			break;
722
		case K_UNTAB:
723
			if (nd != NULL) {
724 725
				nd = rb_next(nd);
				if (nd == NULL)
726
					nd = rb_first(&browser->entries);
727
			} else
728
				nd = browser->curr_hot;
729
			break;
730
		case K_F1:
731
		case 'h':
732
			ui_browser__help_window(&browser->b,
733 734 735
		"UP/DOWN/PGUP\n"
		"PGDN/SPACE    Navigate\n"
		"q/ESC/CTRL+C  Exit\n\n"
736 737
		"ENTER         Go to target\n"
		"ESC           Exit\n"
738 739
		"H             Go to hottest instruction\n"
		"TAB/shift+TAB Cycle thru hottest instructions\n"
740 741 742 743
		"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"
744
		"O             Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
745
		"s             Toggle source code view\n"
746
		"t             Circulate percent, total period, samples view\n"
747
		"c             Show min/max cycle\n"
748
		"/             Search string\n"
749
		"k             Toggle line numbers\n"
750
		"P             Print to [symbol_name].annotation file.\n"
751
		"r             Run available scripts\n"
752 753
		"p             Toggle percent type [local/global]\n"
		"b             Toggle percent base [period/hits]\n"
754
		"?             Search string backwards\n");
755
			continue;
756
		case 'r':
757 758 759
			script_browse(NULL, NULL);
			annotate_browser__show(&browser->b, title, help);
			continue;
760
		case 'k':
761
			notes->options->show_linenr = !notes->options->show_linenr;
762
			break;
763
		case 'H':
764
			nd = browser->curr_hot;
765
			break;
766
		case 's':
767
			if (annotate_browser__toggle_source(browser))
768 769
				ui_helpline__puts(help);
			continue;
770
		case 'o':
771
			notes->options->use_offset = !notes->options->use_offset;
772
			annotation__update_column_widths(notes);
773
			continue;
774 775 776 777
		case 'O':
			if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
				notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
			continue;
778
		case 'j':
779
			notes->options->jump_arrows = !notes->options->jump_arrows;
780
			continue;
781
		case 'J':
782
			notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
783
			annotation__update_column_widths(notes);
784
			continue;
785
		case '/':
786
			if (annotate_browser__search(browser, delay_secs)) {
787 788 789 790 791
show_help:
				ui_helpline__puts(help);
			}
			continue;
		case 'n':
792 793 794
			if (browser->searching_backwards ?
			    annotate_browser__continue_search_reverse(browser, delay_secs) :
			    annotate_browser__continue_search(browser, delay_secs))
795 796 797
				goto show_help;
			continue;
		case '?':
798
			if (annotate_browser__search_reverse(browser, delay_secs))
799 800
				goto show_help;
			continue;
801 802 803 804
		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",
805 806 807 808
					   seq++, browser->b.nr_entries,
					   browser->b.height,
					   browser->b.index,
					   browser->b.top_idx,
809
					   notes->nr_asm_entries);
810 811
		}
			continue;
812 813
		case K_ENTER:
		case K_RIGHT:
814 815 816
		{
			struct disasm_line *dl = disasm_line(browser->selection);

817
			if (browser->selection == NULL)
818
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
819
			else if (browser->selection->offset == -1)
820
				ui_helpline__puts("Actions are only available for assembly lines.");
821
			else if (!dl->ins.ops)
822
				goto show_sup_ins;
823
			else if (ins__is_ret(&dl->ins))
824
				goto out;
825
			else if (!(annotate_browser__jump(browser, evsel, hbt) ||
826
				     annotate_browser__callq(browser, evsel, hbt))) {
827
show_sup_ins:
828
				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
829
			}
830
			continue;
831
		}
832
		case 'P':
833
			map_symbol__annotation_dump(ms, evsel, browser->opts);
834
			continue;
835
		case 't':
836 837
			if (symbol_conf.show_total_period) {
				symbol_conf.show_total_period = false;
838 839 840
				symbol_conf.show_nr_samples = true;
			} else if (symbol_conf.show_nr_samples)
				symbol_conf.show_nr_samples = false;
841
			else
842
				symbol_conf.show_total_period = true;
843
			annotation__update_column_widths(notes);
844
			continue;
845 846 847 848 849 850 851
		case 'c':
			if (notes->options->show_minmax_cycle)
				notes->options->show_minmax_cycle = false;
			else
				notes->options->show_minmax_cycle = true;
			annotation__update_column_widths(notes);
			continue;
852 853 854 855 856 857
		case 'p':
		case 'b':
			switch_percent_type(browser->opts, key == 'b');
			hists__scnprintf_title(hists, title, sizeof(title));
			annotate_browser__show(&browser->b, title, help);
			continue;
858 859
		case K_LEFT:
		case K_ESC:
860 861
		case 'q':
		case CTRL('c'):
862
			goto out;
863 864
		default:
			continue;
865
		}
866 867

		if (nd != NULL)
868
			annotate_browser__set_rb_top(browser, nd);
869 870
	}
out:
871
	ui_browser__hide(&browser->b);
872
	return key;
873 874
}

875
int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
876 877
			     struct hist_browser_timer *hbt,
			     struct annotation_options *opts)
878
{
879
	return symbol__tui_annotate(ms, evsel, hbt, opts);
880 881
}

882
int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
883 884
			     struct hist_browser_timer *hbt,
			     struct annotation_options *opts)
885
{
886 887 888 889
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

890
	return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts);
891 892
}

893
int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
894 895
			 struct hist_browser_timer *hbt,
			 struct annotation_options *opts)
896
{
897
	struct symbol *sym = ms->sym;
898
	struct annotation *notes = symbol__annotation(sym);
899 900
	struct annotate_browser browser = {
		.b = {
901
			.refresh = annotate_browser__refresh,
902 903
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
904
			.filter  = disasm_line__filter,
905
			.extra_title_lines = 1, /* for hists__scnprintf_title() */
906
			.priv	 = ms,
907
			.use_navkeypressed = true,
908
		},
909
		.opts = opts,
910
	};
911
	int ret = -1, err;
912

913
	if (sym == NULL)
914 915
		return -1;

916
	if (ms->map->dso->annotate_warned)
917 918
		return -1;

919
	err = symbol__annotate2(ms, evsel, opts, &browser.arch);
920 921
	if (err) {
		char msg[BUFSIZ];
922
		symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
923
		ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
924
		goto out_free_offsets;
925 926
	}

927
	ui_helpline__push("Press ESC to exit");
928

929
	browser.b.width = notes->max_line_len;
930
	browser.b.nr_entries = notes->nr_entries;
931
	browser.b.entries = &notes->src->source,
932
	browser.b.width += 18; /* Percentage */
933

934
	if (notes->options->hide_src_code)
935
		ui_browser__init_asm_mode(&browser.b);
936

937
	ret = annotate_browser__run(&browser, evsel, hbt);
938 939

	annotated_source__purge(notes->src);
940 941

out_free_offsets:
942
	zfree(&notes->offsets);
943 944
	return ret;
}