annotate.c 23.7 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/evlist.h"
13
#include <inttypes.h>
14
#include <pthread.h>
15
#include <linux/kernel.h>
16
#include <linux/string.h>
17
#include <sys/ttydefaults.h>
18

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

24 25
struct arch;

26
struct annotate_browser {
27 28 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;
	bool			    searching_backwards;
	char			    search_bf[128];
34 35
};

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

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

49
static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
50
{
51
	struct annotation *notes = browser__annotation(browser);
52

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

62
static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current)
63
{
64 65
	 int color = ui_browser__jumps_percent_color(browser, nr, current);
	 return ui_browser__set_color(browser, color);
66 67
}

68
static int annotate_browser__set_color(void *browser, int color)
69
{
70 71
	return ui_browser__set_color(browser, color);
}
72

73 74 75
static void annotate_browser__write_graph(void *browser, int graph)
{
	ui_browser__write_graph(browser, graph);
76 77
}

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

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

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

117
	annotation_line__write(al, notes, &ops);
118

119
	if (ops.current_entry)
120
		ab->selection = al;
121 122
}

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

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

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

158
	if (!disasm_line__is_valid_local_jump(cursor, sym))
159
		return;
160

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

188
	if (notes->options->hide_src_code) {
189 190
		from = cursor->al.idx_asm;
		to = target->idx_asm;
191
	} else {
192 193
		from = (u64)cursor->al.idx;
		to = (u64)target->idx;
194 195
	}

196
	width = annotation__cycles_width(notes);
J
Jin Yao 已提交
197

198
	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
J
Jin Yao 已提交
199
	__ui_browser__line_arrow(browser,
200
				 pcnt_width + 2 + notes->widths.addr + width,
201
				 from, to);
202 203 204

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

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
213
	struct annotation *notes = browser__annotation(browser);
214
	int ret = ui_browser__list_head_refresh(browser);
215
	int pcnt_width = annotation__pcnt_width(notes);
216

217
	if (notes->options->jump_arrows)
218
		annotate_browser__draw_current_jump(browser);
219

220
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
221
	__ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1);
222 223 224
	return ret;
}

225
static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
226 227 228
{
	int i;

229
	for (i = 0; i < a->samples_nr; i++) {
230
		if (a->samples[i].percent == b->samples[i].percent)
231
			continue;
232
		return a->samples[i].percent < b->samples[i].percent;
233 234 235 236
	}
	return 0;
}

237
static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
238
{
239
	struct rb_node **p = &root->rb_node;
240
	struct rb_node *parent = NULL;
241
	struct annotation_line *l;
242 243 244

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

247
		if (disasm__cmp(al, l))
248 249 250 251
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
252 253
	rb_link_node(&al->rb_node, parent, p);
	rb_insert_color(&al->rb_node, root);
254 255
}

256
static void annotate_browser__set_top(struct annotate_browser *browser,
257
				      struct annotation_line *pos, u32 idx)
258
{
259
	struct annotation *notes = browser__annotation(&browser->b);
260 261
	unsigned back;

262 263 264
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
265

266
	while (browser->b.top_idx != 0 && back != 0) {
267
		pos = list_entry(pos->node.prev, struct annotation_line, node);
268

269
		if (annotation_line__filter(pos, notes))
270 271
			continue;

272
		--browser->b.top_idx;
273 274 275
		--back;
	}

276
	browser->b.top = pos;
277
	browser->b.navkeypressed = true;
278 279 280 281 282
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
283
	struct annotation *notes = browser__annotation(&browser->b);
284 285
	struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
	u32 idx = pos->idx;
286

287
	if (notes->options->hide_src_code)
288
		idx = pos->idx_asm;
289
	annotate_browser__set_top(browser, pos, idx);
290
	browser->curr_hot = nd;
291 292
}

293
static void annotate_browser__calc_percent(struct annotate_browser *browser,
294
					   struct perf_evsel *evsel)
295
{
296 297
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
298
	struct annotation *notes = symbol__annotation(sym);
299
	struct disasm_line *pos;
300 301 302 303 304

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

305 306
	symbol__calc_percent(sym, evsel);

307
	list_for_each_entry(pos, &notes->src->source, al.node) {
308 309
		double max_percent = 0.0;
		int i;
310

311
		if (pos->al.offset == -1) {
312
			RB_CLEAR_NODE(&pos->al.rb_node);
313 314 315
			continue;
		}

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

319 320
			if (max_percent < sample->percent)
				max_percent = sample->percent;
321 322
		}

323
		if (max_percent < 0.01 && pos->al.ipc == 0) {
324
			RB_CLEAR_NODE(&pos->al.rb_node);
325 326
			continue;
		}
327
		disasm_rb_tree__insert(&browser->entries, &pos->al);
328 329 330 331 332 333
	}
	pthread_mutex_unlock(&notes->lock);

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

334 335
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
336
	struct annotation *notes = browser__annotation(&browser->b);
337
	struct annotation_line *al;
338 339 340
	off_t offset = browser->b.index - browser->b.top_idx;

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

343
	if (notes->options->hide_src_code) {
344 345
		if (al->idx_asm < offset)
			offset = al->idx;
346

347
		browser->b.nr_entries = notes->nr_entries;
348
		notes->options->hide_src_code = false;
349
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
350 351
		browser->b.top_idx = al->idx - offset;
		browser->b.index = al->idx;
352
	} else {
353
		if (al->idx_asm < 0) {
354 355 356 357 358
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

359 360
		if (al->idx_asm < offset)
			offset = al->idx_asm;
361

362
		browser->b.nr_entries = notes->nr_asm_entries;
363
		notes->options->hide_src_code = true;
364
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
365 366
		browser->b.top_idx = al->idx_asm - offset;
		browser->b.index = al->idx_asm;
367 368 369 370 371
	}

	return true;
}

372
static void ui_browser__init_asm_mode(struct ui_browser *browser)
373
{
374 375 376
	struct annotation *notes = browser__annotation(browser);
	ui_browser__reset_index(browser);
	browser->nr_entries = notes->nr_asm_entries;
377 378
}

379 380 381 382 383 384 385 386
#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);
}

387 388 389 390 391 392 393 394 395
/*
 * 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.
 */
396 397
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
398
				    struct hist_browser_timer *hbt)
399 400
{
	struct map_symbol *ms = browser->b.priv;
401
	struct disasm_line *dl = disasm_line(browser->selection);
402
	struct annotation *notes;
403
	char title[SYM_TITLE_MAX_SIZE];
404

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

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

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

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

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

	*idx = 0;
435
	list_for_each_entry(pos, &notes->src->source, al.node) {
436
		if (pos->al.offset == offset)
437
			return pos;
438
		if (!annotation_line__filter(&pos->al, notes))
439 440 441 442 443 444
			++*idx;
	}

	return NULL;
}

445 446 447
static bool annotate_browser__jump(struct annotate_browser *browser,
				   struct perf_evsel *evsel,
				   struct hist_browser_timer *hbt)
448
{
449
	struct disasm_line *dl = disasm_line(browser->selection);
450
	u64 offset;
451
	s64 idx;
452

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

456 457 458 459 460
	if (dl->ops.target.outside) {
		annotate_browser__callq(browser, evsel, hbt);
		return true;
	}

461 462
	offset = dl->ops.target.offset;
	dl = annotate_browser__find_offset(browser, offset, &idx);
463
	if (dl == NULL) {
464
		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
465 466 467
		return true;
	}

468
	annotate_browser__set_top(browser, &dl->al, idx);
469

470 471 472
	return true;
}

473
static
474
struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
475
					  char *s, s64 *idx)
476
{
477
	struct annotation *notes = browser__annotation(&browser->b);
478
	struct annotation_line *al = browser->selection;
479 480

	*idx = browser->b.index;
481
	list_for_each_entry_continue(al, &notes->src->source, node) {
482
		if (annotation_line__filter(al, notes))
483 484 485 486
			continue;

		++*idx;

487 488
		if (al->line && strstr(al->line, s) != NULL)
			return al;
489 490 491 492 493 494 495
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
496
	struct annotation_line *al;
497 498
	s64 idx;

499 500
	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (al == NULL) {
501 502 503 504
		ui_helpline__puts("String not found!");
		return false;
	}

505
	annotate_browser__set_top(browser, al, idx);
506 507 508 509
	browser->searching_backwards = false;
	return true;
}

510
static
511
struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
512
						  char *s, s64 *idx)
513
{
514
	struct annotation *notes = browser__annotation(&browser->b);
515
	struct annotation_line *al = browser->selection;
516 517

	*idx = browser->b.index;
518
	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
519
		if (annotation_line__filter(al, notes))
520 521 522 523
			continue;

		--*idx;

524 525
		if (al->line && strstr(al->line, s) != NULL)
			return al;
526 527 528 529 530 531 532
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
533
	struct annotation_line *al;
534 535
	s64 idx;

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

542
	annotate_browser__set_top(browser, al, idx);
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 589 590 591 592 593 594
	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);
}

595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
{
	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;

	sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso));

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

612 613
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
614
				 struct hist_browser_timer *hbt)
615 616
{
	struct rb_node *nd = NULL;
617
	struct hists *hists = evsel__hists(evsel);
618
	struct map_symbol *ms = browser->b.priv;
619
	struct symbol *sym = ms->sym;
620
	struct annotation *notes = symbol__annotation(ms->sym);
621
	const char *help = "Press 'h' for help on key bindings";
622
	int delay_secs = hbt ? hbt->refresh : 0;
623
	char title[256];
624
	int key;
625

626 627 628
	annotation__scnprintf_samples_period(notes, title, sizeof(title), evsel);

	if (annotate_browser__show(&browser->b, title, help) < 0)
629
		return -1;
630

631
	annotate_browser__calc_percent(browser, evsel);
632

633 634 635
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
636
	}
637

638
	nd = browser->curr_hot;
639

640
	while (1) {
641
		key = ui_browser__run(&browser->b, delay_secs);
642

643
		if (delay_secs != 0) {
644
			annotate_browser__calc_percent(browser, evsel);
645 646 647 648 649 650 651 652 653
			/*
			 * 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;
		}

654
		switch (key) {
655
		case K_TIMER:
656 657
			if (hbt)
				hbt->timer(hbt->arg);
658

659
			if (delay_secs != 0) {
660
				symbol__annotate_decay_histogram(sym, evsel->idx);
661 662 663
				hists__scnprintf_title(hists, title, sizeof(title));
				annotate_browser__show(&browser->b, title, help);
			}
664
			continue;
665
		case K_TAB:
666 667 668
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
669
					nd = rb_last(&browser->entries);
670
			} else
671
				nd = browser->curr_hot;
672
			break;
673
		case K_UNTAB:
674
			if (nd != NULL) {
675 676
				nd = rb_next(nd);
				if (nd == NULL)
677
					nd = rb_first(&browser->entries);
678
			} else
679
				nd = browser->curr_hot;
680
			break;
681
		case K_F1:
682
		case 'h':
683
			ui_browser__help_window(&browser->b,
684 685 686
		"UP/DOWN/PGUP\n"
		"PGDN/SPACE    Navigate\n"
		"q/ESC/CTRL+C  Exit\n\n"
687 688
		"ENTER         Go to target\n"
		"ESC           Exit\n"
689 690
		"H             Go to hottest instruction\n"
		"TAB/shift+TAB Cycle thru hottest instructions\n"
691 692 693 694
		"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"
695
		"O             Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
696
		"s             Toggle source code view\n"
697
		"t             Circulate percent, total period, samples view\n"
698
		"c             Show min/max cycle\n"
699
		"/             Search string\n"
700
		"k             Toggle line numbers\n"
701
		"P             Print to [symbol_name].annotation file.\n"
702
		"r             Run available scripts\n"
703
		"?             Search string backwards\n");
704
			continue;
705 706 707 708 709
		case 'r':
			{
				script_browse(NULL);
				continue;
			}
710
		case 'k':
711
			notes->options->show_linenr = !notes->options->show_linenr;
712
			break;
713
		case 'H':
714
			nd = browser->curr_hot;
715
			break;
716
		case 's':
717
			if (annotate_browser__toggle_source(browser))
718 719
				ui_helpline__puts(help);
			continue;
720
		case 'o':
721
			notes->options->use_offset = !notes->options->use_offset;
722
			annotation__update_column_widths(notes);
723
			continue;
724 725 726 727
		case 'O':
			if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
				notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
			continue;
728
		case 'j':
729
			notes->options->jump_arrows = !notes->options->jump_arrows;
730
			continue;
731
		case 'J':
732
			notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
733
			annotation__update_column_widths(notes);
734
			continue;
735
		case '/':
736
			if (annotate_browser__search(browser, delay_secs)) {
737 738 739 740 741
show_help:
				ui_helpline__puts(help);
			}
			continue;
		case 'n':
742 743 744
			if (browser->searching_backwards ?
			    annotate_browser__continue_search_reverse(browser, delay_secs) :
			    annotate_browser__continue_search(browser, delay_secs))
745 746 747
				goto show_help;
			continue;
		case '?':
748
			if (annotate_browser__search_reverse(browser, delay_secs))
749 750
				goto show_help;
			continue;
751 752 753 754
		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",
755 756 757 758
					   seq++, browser->b.nr_entries,
					   browser->b.height,
					   browser->b.index,
					   browser->b.top_idx,
759
					   notes->nr_asm_entries);
760 761
		}
			continue;
762 763
		case K_ENTER:
		case K_RIGHT:
764 765 766
		{
			struct disasm_line *dl = disasm_line(browser->selection);

767
			if (browser->selection == NULL)
768
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
769
			else if (browser->selection->offset == -1)
770
				ui_helpline__puts("Actions are only available for assembly lines.");
771
			else if (!dl->ins.ops)
772
				goto show_sup_ins;
773
			else if (ins__is_ret(&dl->ins))
774
				goto out;
775
			else if (!(annotate_browser__jump(browser, evsel, hbt) ||
776
				     annotate_browser__callq(browser, evsel, hbt))) {
777
show_sup_ins:
778
				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
779
			}
780
			continue;
781
		}
782 783 784
		case 'P':
			map_symbol__annotation_dump(ms, evsel);
			continue;
785
		case 't':
786 787 788 789 790
			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;
791
			else
792
				notes->options->show_total_period = true;
793
			annotation__update_column_widths(notes);
794
			continue;
795 796 797 798 799 800 801
		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;
802 803
		case K_LEFT:
		case K_ESC:
804 805
		case 'q':
		case CTRL('c'):
806
			goto out;
807 808
		default:
			continue;
809
		}
810 811

		if (nd != NULL)
812
			annotate_browser__set_rb_top(browser, nd);
813 814
	}
out:
815
	ui_browser__hide(&browser->b);
816
	return key;
817 818
}

819 820 821 822 823 824
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
			     struct hist_browser_timer *hbt)
{
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

825
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
826
			     struct hist_browser_timer *hbt)
827
{
828 829 830 831
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

832
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
833 834
}

835 836
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
837
			 struct hist_browser_timer *hbt)
838
{
839
	struct annotation *notes = symbol__annotation(sym);
840 841 842 843
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
844 845
	struct annotate_browser browser = {
		.b = {
846
			.refresh = annotate_browser__refresh,
847 848
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
849
			.filter  = disasm_line__filter,
850
			.extra_title_lines = 1, /* for hists__scnprintf_title() */
851
			.priv	 = &ms,
852
			.use_navkeypressed = true,
853
		},
854
	};
855
	int ret = -1, err;
856

857
	if (sym == NULL)
858 859
		return -1;

860
	if (map->dso->annotate_warned)
861 862
		return -1;

863
	err = symbol__annotate2(sym, map, evsel, &annotation__default_options, &browser.arch);
864 865 866 867
	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);
868
		goto out_free_offsets;
869 870
	}

871
	ui_helpline__push("Press ESC to exit");
872

873
	browser.b.width = notes->max_line_len;
874
	browser.b.nr_entries = notes->nr_entries;
875
	browser.b.entries = &notes->src->source,
876
	browser.b.width += 18; /* Percentage */
877

878
	if (notes->options->hide_src_code)
879
		ui_browser__init_asm_mode(&browser.b);
880

881
	ret = annotate_browser__run(&browser, evsel, hbt);
882 883

	annotated_source__purge(notes->src);
884 885

out_free_offsets:
886
	zfree(&notes->offsets);
887 888
	return ret;
}