annotate.c 26.9 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
static int is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
129
{
130
	struct disasm_line *pos = list_prev_entry(cursor, al.node);
131
	const char *name;
132 133 134 135 136 137 138
	int diff = 1;

	while (pos && pos->al.offset == -1) {
		pos = list_prev_entry(pos, al.node);
		if (!ab->opts->hide_src_code)
			diff++;
	}
139 140

	if (!pos)
141
		return 0;
142 143 144 145 146 147 148

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

	if (!name || !cursor->ins.name)
149
		return 0;
150

151 152 153
	if (ins__is_fused(ab->arch, name, cursor->ins.name))
		return diff;
	return 0;
154 155
}

156
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
157 158
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
159
	struct disasm_line *cursor = disasm_line(ab->selection);
160
	struct annotation_line *target;
161
	unsigned int from, to;
162 163
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
164
	struct annotation *notes = symbol__annotation(sym);
165
	u8 pcnt_width = annotation__pcnt_width(notes);
166
	int width;
167
	int diff = 0;
168 169 170 171

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

173
	if (!disasm_line__is_valid_local_jump(cursor, sym))
174
		return;
175

176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
	/*
	 * 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.
	 */
196
	target = notes->offsets[cursor->ops.target.offset];
197
	if (target == NULL) {
198
		ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
199 200 201
				    cursor->ops.target.offset);
		return;
	}
202

203
	if (notes->options->hide_src_code) {
204 205
		from = cursor->al.idx_asm;
		to = target->idx_asm;
206
	} else {
207 208
		from = (u64)cursor->al.idx;
		to = (u64)target->idx;
209 210
	}

211
	width = annotation__cycles_width(notes);
J
Jin Yao 已提交
212

213
	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
J
Jin Yao 已提交
214
	__ui_browser__line_arrow(browser,
215
				 pcnt_width + 2 + notes->widths.addr + width,
216
				 from, to);
217

218 219
	diff = is_fused(ab, cursor);
	if (diff > 0) {
220
		ui_browser__mark_fused(browser,
221
				       pcnt_width + 3 + notes->widths.addr + width,
222
				       from - diff, diff, to > from);
223
	}
224 225 226 227
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
228
	struct annotation *notes = browser__annotation(browser);
229
	int ret = ui_browser__list_head_refresh(browser);
230
	int pcnt_width = annotation__pcnt_width(notes);
231

232
	if (notes->options->jump_arrows)
233
		annotate_browser__draw_current_jump(browser);
234

235
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
236
	__ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1);
237 238 239
	return ret;
}

240 241
static double disasm__cmp(struct annotation_line *a, struct annotation_line *b,
						  int percent_type)
242 243 244
{
	int i;

245
	for (i = 0; i < a->data_nr; i++) {
246
		if (a->data[i].percent[percent_type] == b->data[i].percent[percent_type])
247
			continue;
248 249
		return a->data[i].percent[percent_type] -
			   b->data[i].percent[percent_type];
250 251 252 253
	}
	return 0;
}

254 255
static void disasm_rb_tree__insert(struct annotate_browser *browser,
				struct annotation_line *al)
256
{
257
	struct rb_root *root = &browser->entries;
258
	struct rb_node **p = &root->rb_node;
259
	struct rb_node *parent = NULL;
260
	struct annotation_line *l;
261 262 263

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

266
		if (disasm__cmp(al, l, browser->opts->percent_type) < 0)
267 268 269 270
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
271 272
	rb_link_node(&al->rb_node, parent, p);
	rb_insert_color(&al->rb_node, root);
273 274
}

275
static void annotate_browser__set_top(struct annotate_browser *browser,
276
				      struct annotation_line *pos, u32 idx)
277
{
278
	struct annotation *notes = browser__annotation(&browser->b);
279 280
	unsigned back;

281 282 283
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
284

285
	while (browser->b.top_idx != 0 && back != 0) {
286
		pos = list_entry(pos->node.prev, struct annotation_line, node);
287

288
		if (annotation_line__filter(pos, notes))
289 290
			continue;

291
		--browser->b.top_idx;
292 293 294
		--back;
	}

295
	browser->b.top = pos;
296
	browser->b.navkeypressed = true;
297 298 299 300 301
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
302
	struct annotation *notes = browser__annotation(&browser->b);
303 304
	struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
	u32 idx = pos->idx;
305

306
	if (notes->options->hide_src_code)
307
		idx = pos->idx_asm;
308
	annotate_browser__set_top(browser, pos, idx);
309
	browser->curr_hot = nd;
310 311
}

312
static void annotate_browser__calc_percent(struct annotate_browser *browser,
313
					   struct evsel *evsel)
314
{
315 316
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
317
	struct annotation *notes = symbol__annotation(sym);
318
	struct disasm_line *pos;
319 320 321 322 323

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

324 325
	symbol__calc_percent(sym, evsel);

326
	list_for_each_entry(pos, &notes->src->source, al.node) {
327 328
		double max_percent = 0.0;
		int i;
329

330
		if (pos->al.offset == -1) {
331
			RB_CLEAR_NODE(&pos->al.rb_node);
332 333 334
			continue;
		}

335
		for (i = 0; i < pos->al.data_nr; i++) {
336
			double percent;
337

338
			percent = annotation_data__percent(&pos->al.data[i],
339
							   browser->opts->percent_type);
340 341 342

			if (max_percent < percent)
				max_percent = percent;
343 344
		}

345
		if (max_percent < 0.01 && pos->al.ipc == 0) {
346
			RB_CLEAR_NODE(&pos->al.rb_node);
347 348
			continue;
		}
349
		disasm_rb_tree__insert(browser, &pos->al);
350 351 352 353 354 355
	}
	pthread_mutex_unlock(&notes->lock);

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

356 357 358 359 360 361 362
static struct annotation_line *annotate_browser__find_next_asm_line(
					struct annotate_browser *browser,
					struct annotation_line *al)
{
	struct annotation_line *it = al;

	/* find next asm line */
363
	list_for_each_entry_continue(it, browser->b.entries, node) {
364 365 366 367 368 369
		if (it->idx_asm >= 0)
			return it;
	}

	/* no asm line found forwards, try backwards */
	it = al;
370
	list_for_each_entry_continue_reverse(it, browser->b.entries, node) {
371 372 373 374 375 376 377 378
		if (it->idx_asm >= 0)
			return it;
	}

	/* There are no asm lines */
	return NULL;
}

379 380
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
381
	struct annotation *notes = browser__annotation(&browser->b);
382
	struct annotation_line *al;
383 384 385
	off_t offset = browser->b.index - browser->b.top_idx;

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

388
	if (notes->options->hide_src_code) {
389 390
		if (al->idx_asm < offset)
			offset = al->idx;
391

392
		browser->b.nr_entries = notes->nr_entries;
393
		notes->options->hide_src_code = false;
394
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
395 396
		browser->b.top_idx = al->idx - offset;
		browser->b.index = al->idx;
397
	} else {
398
		if (al->idx_asm < 0) {
399 400 401 402 403 404
			/* move cursor to next asm line */
			al = annotate_browser__find_next_asm_line(browser, al);
			if (!al) {
				browser->b.seek(&browser->b, -offset, SEEK_CUR);
				return false;
			}
405 406
		}

407 408
		if (al->idx_asm < offset)
			offset = al->idx_asm;
409

410
		browser->b.nr_entries = notes->nr_asm_entries;
411
		notes->options->hide_src_code = true;
412
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
413 414
		browser->b.top_idx = al->idx_asm - offset;
		browser->b.index = al->idx_asm;
415 416 417 418 419
	}

	return true;
}

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)

static void annotate_browser__show_full_location(struct ui_browser *browser)
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
	struct disasm_line *cursor = disasm_line(ab->selection);
	struct annotation_line *al = &cursor->al;

	if (al->offset != -1)
		ui_helpline__puts("Only available for source code lines.");
	else if (al->fileloc == NULL)
		ui_helpline__puts("No source file location.");
	else {
		char help_line[SYM_TITLE_MAX_SIZE];
		sprintf (help_line, "Source file location: %s", al->fileloc);
		ui_helpline__puts(help_line);
	}
}

439
static void ui_browser__init_asm_mode(struct ui_browser *browser)
440
{
441 442 443
	struct annotation *notes = browser__annotation(browser);
	ui_browser__reset_index(browser);
	browser->nr_entries = notes->nr_asm_entries;
444 445
}

446
static int sym_title(struct symbol *sym, struct map *map, char *title,
447
		     size_t sz, int percent_type)
448
{
449 450
	return snprintf(title, sz, "%s  %s [Percent: %s]", sym->name, map->dso->long_name,
			percent_type_str(percent_type));
451 452
}

453
/*
454
 * This can be called from external jumps, i.e. jumps from one function
455 456 457 458 459 460 461
 * 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.
 */
462
static bool annotate_browser__callq(struct annotate_browser *browser,
463
				    struct evsel *evsel,
464
				    struct hist_browser_timer *hbt)
465
{
466
	struct map_symbol *ms = browser->b.priv, target_ms;
467
	struct disasm_line *dl = disasm_line(browser->selection);
468
	struct annotation *notes;
469
	char title[SYM_TITLE_MAX_SIZE];
470

471
	if (!dl->ops.target.sym) {
472 473 474 475
		ui_helpline__puts("The called function was not found.");
		return true;
	}

476
	notes = symbol__annotation(dl->ops.target.sym);
477 478
	pthread_mutex_lock(&notes->lock);

479
	if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) {
480 481
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
482
			    dl->ops.target.sym->name);
483 484 485
		return true;
	}

486
	target_ms.maps = ms->maps;
487 488
	target_ms.map = ms->map;
	target_ms.sym = dl->ops.target.sym;
489
	pthread_mutex_unlock(&notes->lock);
490
	symbol__tui_annotate(&target_ms, evsel, hbt, browser->opts);
491
	sym_title(ms->sym, ms->map, title, sizeof(title), browser->opts->percent_type);
492
	ui_browser__show_title(&browser->b, title);
493 494 495
	return true;
}

496 497 498
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
499
{
500
	struct annotation *notes = browser__annotation(&browser->b);
501
	struct disasm_line *pos;
502 503

	*idx = 0;
504
	list_for_each_entry(pos, &notes->src->source, al.node) {
505
		if (pos->al.offset == offset)
506
			return pos;
507
		if (!annotation_line__filter(&pos->al, notes))
508 509 510 511 512 513
			++*idx;
	}

	return NULL;
}

514
static bool annotate_browser__jump(struct annotate_browser *browser,
515
				   struct evsel *evsel,
516
				   struct hist_browser_timer *hbt)
517
{
518
	struct disasm_line *dl = disasm_line(browser->selection);
519
	u64 offset;
520
	s64 idx;
521

522
	if (!ins__is_jump(&dl->ins))
523 524
		return false;

525 526 527 528 529
	if (dl->ops.target.outside) {
		annotate_browser__callq(browser, evsel, hbt);
		return true;
	}

530 531
	offset = dl->ops.target.offset;
	dl = annotate_browser__find_offset(browser, offset, &idx);
532
	if (dl == NULL) {
533
		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
534 535 536
		return true;
	}

537
	annotate_browser__set_top(browser, &dl->al, idx);
538

539 540 541
	return true;
}

542
static
543
struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
544
					  char *s, s64 *idx)
545
{
546
	struct annotation *notes = browser__annotation(&browser->b);
547
	struct annotation_line *al = browser->selection;
548 549

	*idx = browser->b.index;
550
	list_for_each_entry_continue(al, &notes->src->source, node) {
551
		if (annotation_line__filter(al, notes))
552 553 554 555
			continue;

		++*idx;

556 557
		if (al->line && strstr(al->line, s) != NULL)
			return al;
558 559 560 561 562 563 564
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
565
	struct annotation_line *al;
566 567
	s64 idx;

568 569
	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (al == NULL) {
570 571 572 573
		ui_helpline__puts("String not found!");
		return false;
	}

574
	annotate_browser__set_top(browser, al, idx);
575 576 577 578
	browser->searching_backwards = false;
	return true;
}

579
static
580
struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
581
						  char *s, s64 *idx)
582
{
583
	struct annotation *notes = browser__annotation(&browser->b);
584
	struct annotation_line *al = browser->selection;
585 586

	*idx = browser->b.index;
587
	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
588
		if (annotation_line__filter(al, notes))
589 590 591 592
			continue;

		--*idx;

593 594
		if (al->line && strstr(al->line, s) != NULL)
			return al;
595 596 597 598 599 600 601
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
602
	struct annotation_line *al;
603 604
	s64 idx;

605 606
	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (al == NULL) {
607 608 609 610
		ui_helpline__puts("String not found!");
		return false;
	}

611
	annotate_browser__set_top(browser, al, idx);
612 613 614 615 616 617 618 619 620 621 622 623 624 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 658 659 660 661 662 663
	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);
}

664 665
static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
{
666
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
667 668 669 670 671 672 673
	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;

674
	sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), ab->opts->percent_type);
675 676 677 678 679 680 681

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

682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
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);
	}
}

715
static int annotate_browser__run(struct annotate_browser *browser,
716
				 struct evsel *evsel,
717
				 struct hist_browser_timer *hbt)
718 719
{
	struct rb_node *nd = NULL;
720
	struct hists *hists = evsel__hists(evsel);
721
	struct map_symbol *ms = browser->b.priv;
722
	struct symbol *sym = ms->sym;
723
	struct annotation *notes = symbol__annotation(ms->sym);
724
	const char *help = "Press 'h' for help on key bindings";
725
	int delay_secs = hbt ? hbt->refresh : 0;
726
	char title[256];
727
	int key;
728

729
	hists__scnprintf_title(hists, title, sizeof(title));
730
	if (annotate_browser__show(&browser->b, title, help) < 0)
731
		return -1;
732

733
	annotate_browser__calc_percent(browser, evsel);
734

735 736 737
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
738
	}
739

740
	nd = browser->curr_hot;
741

742
	while (1) {
743
		key = ui_browser__run(&browser->b, delay_secs);
744

745
		if (delay_secs != 0) {
746
			annotate_browser__calc_percent(browser, evsel);
747 748 749 750 751 752 753 754 755
			/*
			 * 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;
		}

756
		switch (key) {
757
		case K_TIMER:
758 759
			if (hbt)
				hbt->timer(hbt->arg);
760

761
			if (delay_secs != 0) {
762
				symbol__annotate_decay_histogram(sym, evsel->core.idx);
763 764 765
				hists__scnprintf_title(hists, title, sizeof(title));
				annotate_browser__show(&browser->b, title, help);
			}
766
			continue;
767
		case K_TAB:
768 769 770
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
771
					nd = rb_last(&browser->entries);
772
			} else
773
				nd = browser->curr_hot;
774
			break;
775
		case K_UNTAB:
776
			if (nd != NULL) {
777 778
				nd = rb_next(nd);
				if (nd == NULL)
779
					nd = rb_first(&browser->entries);
780
			} else
781
				nd = browser->curr_hot;
782
			break;
783
		case K_F1:
784
		case 'h':
785
			ui_browser__help_window(&browser->b,
786 787 788
		"UP/DOWN/PGUP\n"
		"PGDN/SPACE    Navigate\n"
		"q/ESC/CTRL+C  Exit\n\n"
789 790
		"ENTER         Go to target\n"
		"ESC           Exit\n"
791 792
		"H             Go to hottest instruction\n"
		"TAB/shift+TAB Cycle thru hottest instructions\n"
793 794 795 796
		"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"
797
		"O             Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
798
		"s             Toggle source code view\n"
799
		"t             Circulate percent, total period, samples view\n"
800
		"c             Show min/max cycle\n"
801
		"/             Search string\n"
802
		"k             Toggle line numbers\n"
803
		"l             Show full source file location\n"
804
		"P             Print to [symbol_name].annotation file.\n"
805
		"r             Run available scripts\n"
806 807
		"p             Toggle percent type [local/global]\n"
		"b             Toggle percent base [period/hits]\n"
808
		"?             Search string backwards\n");
809
			continue;
810
		case 'r':
811 812 813
			script_browse(NULL, NULL);
			annotate_browser__show(&browser->b, title, help);
			continue;
814
		case 'k':
815
			notes->options->show_linenr = !notes->options->show_linenr;
816
			continue;
817 818 819
		case 'l':
			annotate_browser__show_full_location (&browser->b);
			continue;
820
		case 'H':
821
			nd = browser->curr_hot;
822
			break;
823
		case 's':
824
			if (annotate_browser__toggle_source(browser))
825 826
				ui_helpline__puts(help);
			continue;
827
		case 'o':
828
			notes->options->use_offset = !notes->options->use_offset;
829
			annotation__update_column_widths(notes);
830
			continue;
831 832 833 834
		case 'O':
			if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
				notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
			continue;
835
		case 'j':
836
			notes->options->jump_arrows = !notes->options->jump_arrows;
837
			continue;
838
		case 'J':
839
			notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
840
			annotation__update_column_widths(notes);
841
			continue;
842
		case '/':
843
			if (annotate_browser__search(browser, delay_secs)) {
844 845 846 847 848
show_help:
				ui_helpline__puts(help);
			}
			continue;
		case 'n':
849 850 851
			if (browser->searching_backwards ?
			    annotate_browser__continue_search_reverse(browser, delay_secs) :
			    annotate_browser__continue_search(browser, delay_secs))
852 853 854
				goto show_help;
			continue;
		case '?':
855
			if (annotate_browser__search_reverse(browser, delay_secs))
856 857
				goto show_help;
			continue;
858 859 860 861
		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",
862 863 864 865
					   seq++, browser->b.nr_entries,
					   browser->b.height,
					   browser->b.index,
					   browser->b.top_idx,
866
					   notes->nr_asm_entries);
867 868
		}
			continue;
869 870
		case K_ENTER:
		case K_RIGHT:
871 872 873
		{
			struct disasm_line *dl = disasm_line(browser->selection);

874
			if (browser->selection == NULL)
875
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
876
			else if (browser->selection->offset == -1)
877
				ui_helpline__puts("Actions are only available for assembly lines.");
878
			else if (!dl->ins.ops)
879
				goto show_sup_ins;
880
			else if (ins__is_ret(&dl->ins))
881
				goto out;
882
			else if (!(annotate_browser__jump(browser, evsel, hbt) ||
883
				     annotate_browser__callq(browser, evsel, hbt))) {
884
show_sup_ins:
885
				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
886
			}
887
			continue;
888
		}
889
		case 'P':
890
			map_symbol__annotation_dump(ms, evsel, browser->opts);
891
			continue;
892
		case 't':
893 894
			if (symbol_conf.show_total_period) {
				symbol_conf.show_total_period = false;
895 896 897
				symbol_conf.show_nr_samples = true;
			} else if (symbol_conf.show_nr_samples)
				symbol_conf.show_nr_samples = false;
898
			else
899
				symbol_conf.show_total_period = true;
900
			annotation__update_column_widths(notes);
901
			continue;
902 903 904 905 906 907 908
		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;
909 910 911 912 913 914
		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;
915 916
		case K_LEFT:
		case K_ESC:
917 918
		case 'q':
		case CTRL('c'):
919
			goto out;
920 921
		default:
			continue;
922
		}
923 924

		if (nd != NULL)
925
			annotate_browser__set_rb_top(browser, nd);
926 927
	}
out:
928
	ui_browser__hide(&browser->b);
929
	return key;
930 931
}

932
int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
933 934
			     struct hist_browser_timer *hbt,
			     struct annotation_options *opts)
935
{
936
	return symbol__tui_annotate(ms, evsel, hbt, opts);
937 938
}

939
int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
940 941
			     struct hist_browser_timer *hbt,
			     struct annotation_options *opts)
942
{
943 944 945 946
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

947
	return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts);
948 949
}

950
int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
951 952
			 struct hist_browser_timer *hbt,
			 struct annotation_options *opts)
953
{
954
	struct symbol *sym = ms->sym;
955
	struct annotation *notes = symbol__annotation(sym);
956 957
	struct annotate_browser browser = {
		.b = {
958
			.refresh = annotate_browser__refresh,
959 960
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
961
			.filter  = disasm_line__filter,
962
			.extra_title_lines = 1, /* for hists__scnprintf_title() */
963
			.priv	 = ms,
964
			.use_navkeypressed = true,
965
		},
966
		.opts = opts,
967
	};
968
	int ret = -1, err;
969

970
	if (sym == NULL)
971 972
		return -1;

973
	if (ms->map->dso->annotate_warned)
974 975
		return -1;

976
	err = symbol__annotate2(ms, evsel, opts, &browser.arch);
977 978
	if (err) {
		char msg[BUFSIZ];
979
		ms->map->dso->annotate_warned = true;
980
		symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
981
		ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
982
		goto out_free_offsets;
983 984
	}

985
	ui_helpline__push("Press ESC to exit");
986

987
	browser.b.width = notes->max_line_len;
988
	browser.b.nr_entries = notes->nr_entries;
989
	browser.b.entries = &notes->src->source,
990
	browser.b.width += 18; /* Percentage */
991

992
	if (notes->options->hide_src_code)
993
		ui_browser__init_asm_mode(&browser.b);
994

995
	ret = annotate_browser__run(&browser, evsel, hbt);
996 997

	annotated_source__purge(notes->src);
998 999

out_free_offsets:
1000
	zfree(&notes->offsets);
1001 1002
	return ret;
}