annotate.c 25.4 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2 3
#include "../browser.h"
#include "../helpline.h"
4 5
#include "../ui.h"
#include "../util.h"
6 7 8
#include "../../util/annotate.h"
#include "../../util/hist.h"
#include "../../util/sort.h"
9
#include "../../util/map.h"
10
#include "../../util/symbol.h"
11
#include "../../util/evsel.h"
12
#include "../../util/evlist.h"
13
#include <inttypes.h>
14
#include <pthread.h>
15
#include <linux/kernel.h>
16
#include <linux/string.h>
17
#include <linux/zalloc.h>
18
#include <sys/ttydefaults.h>
19
#include <asm/bug.h>
20

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

26 27
struct arch;

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

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

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

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

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

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

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

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

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

95
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
96
{
97
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
98
	struct annotation *notes = browser__annotation(browser);
99
	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
100
	const bool is_current_entry = ui_browser__is_current_entry(browser, row);
101 102
	struct annotation_write_ops ops = {
		.first_line		 = row == 0,
103
		.current_entry		 = is_current_entry,
104
		.change_color		 = (!notes->options->hide_src_code &&
105
					    (!is_current_entry ||
106 107 108 109 110 111 112 113 114 115
					     (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,
	};
116 117

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

243 244
static void disasm_rb_tree__insert(struct annotate_browser *browser,
				struct annotation_line *al)
245
{
246
	struct rb_root *root = &browser->entries;
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, browser->opts->percent_type) < 0)
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
	struct annotation *notes = browser__annotation(&browser->b);
268 269
	unsigned back;

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

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

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

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

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

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

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

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

313 314
	symbol__calc_percent(sym, evsel);

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

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

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

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

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

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

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

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

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

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

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

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

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

	return true;
}

383
static void ui_browser__init_asm_mode(struct ui_browser *browser)
384
{
385 386 387
	struct annotation *notes = browser__annotation(browser);
	ui_browser__reset_index(browser);
	browser->nr_entries = notes->nr_asm_entries;
388 389
}

390 391 392
#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)

static int sym_title(struct symbol *sym, struct map *map, char *title,
393
		     size_t sz, int percent_type)
394
{
395 396
	return snprintf(title, sz, "%s  %s [Percent: %s]", sym->name, map->dso->long_name,
			percent_type_str(percent_type));
397 398
}

399 400 401 402 403 404 405 406 407
/*
 * 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.
 */
408
static bool annotate_browser__callq(struct annotate_browser *browser,
409
				    struct evsel *evsel,
410
				    struct hist_browser_timer *hbt)
411 412
{
	struct map_symbol *ms = browser->b.priv;
413
	struct disasm_line *dl = disasm_line(browser->selection);
414
	struct annotation *notes;
415
	char title[SYM_TITLE_MAX_SIZE];
416

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

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

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

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

439 440 441
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
442
{
443
	struct annotation *notes = browser__annotation(&browser->b);
444
	struct disasm_line *pos;
445 446

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

	return NULL;
}

457
static bool annotate_browser__jump(struct annotate_browser *browser,
458
				   struct evsel *evsel,
459
				   struct hist_browser_timer *hbt)
460
{
461
	struct disasm_line *dl = disasm_line(browser->selection);
462
	u64 offset;
463
	s64 idx;
464

465
	if (!ins__is_jump(&dl->ins))
466 467
		return false;

468 469 470 471 472
	if (dl->ops.target.outside) {
		annotate_browser__callq(browser, evsel, hbt);
		return true;
	}

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

480
	annotate_browser__set_top(browser, &dl->al, idx);
481

482 483 484
	return true;
}

485
static
486
struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
487
					  char *s, s64 *idx)
488
{
489
	struct annotation *notes = browser__annotation(&browser->b);
490
	struct annotation_line *al = browser->selection;
491 492

	*idx = browser->b.index;
493
	list_for_each_entry_continue(al, &notes->src->source, node) {
494
		if (annotation_line__filter(al, notes))
495 496 497 498
			continue;

		++*idx;

499 500
		if (al->line && strstr(al->line, s) != NULL)
			return al;
501 502 503 504 505 506 507
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
508
	struct annotation_line *al;
509 510
	s64 idx;

511 512
	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (al == NULL) {
513 514 515 516
		ui_helpline__puts("String not found!");
		return false;
	}

517
	annotate_browser__set_top(browser, al, idx);
518 519 520 521
	browser->searching_backwards = false;
	return true;
}

522
static
523
struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
524
						  char *s, s64 *idx)
525
{
526
	struct annotation *notes = browser__annotation(&browser->b);
527
	struct annotation_line *al = browser->selection;
528 529

	*idx = browser->b.index;
530
	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
531
		if (annotation_line__filter(al, notes))
532 533 534 535
			continue;

		--*idx;

536 537
		if (al->line && strstr(al->line, s) != NULL)
			return al;
538 539 540 541 542 543 544
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
545
	struct annotation_line *al;
546 547
	s64 idx;

548 549
	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (al == NULL) {
550 551 552 553
		ui_helpline__puts("String not found!");
		return false;
	}

554
	annotate_browser__set_top(browser, al, idx);
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
	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);
}

607 608
static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
{
609
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
610 611 612 613 614 615 616
	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;

617
	sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), ab->opts->percent_type);
618 619 620 621 622 623 624

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

625 626 627 628 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
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);
	}
}

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

672
	hists__scnprintf_title(hists, title, sizeof(title));
673
	if (annotate_browser__show(&browser->b, title, help) < 0)
674
		return -1;
675

676
	annotate_browser__calc_percent(browser, evsel);
677

678 679 680
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
681
	}
682

683
	nd = browser->curr_hot;
684

685
	while (1) {
686
		key = ui_browser__run(&browser->b, delay_secs);
687

688
		if (delay_secs != 0) {
689
			annotate_browser__calc_percent(browser, evsel);
690 691 692 693 694 695 696 697 698
			/*
			 * 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;
		}

699
		switch (key) {
700
		case K_TIMER:
701 702
			if (hbt)
				hbt->timer(hbt->arg);
703

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

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

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

872
int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
873 874
			     struct hist_browser_timer *hbt,
			     struct annotation_options *opts)
875
{
876
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt, opts);
877 878
}

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

887
	return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts);
888 889
}

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

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

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

920
	err = symbol__annotate2(sym, map, evsel, opts, &browser.arch);
921 922 923 924
	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);
925
		goto out_free_offsets;
926 927
	}

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

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

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

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

	annotated_source__purge(notes->src);
941 942

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