annotate.c 23.9 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
	struct ui_browser	    b;
	struct rb_root		    entries;
	struct rb_node		   *curr_hot;
	struct annotation_line	   *selection;
	struct arch		   *arch;
32
	struct annotation_options  *opts;
33 34
	bool			    searching_backwards;
	char			    search_bf[128];
35 36
};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

306 307
	symbol__calc_percent(sym, evsel);

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

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

317
		for (i = 0; i < pos->al.data_nr; i++) {
318
			double percent;
319

320 321 322 323 324
			percent = annotation_data__percent(&pos->al.data[i],
							   PERCENT_HITS_LOCAL);

			if (max_percent < percent)
				max_percent = percent;
325 326
		}

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

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

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

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

347
	if (notes->options->hide_src_code) {
348 349
		if (al->idx_asm < offset)
			offset = al->idx;
350

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

363 364
		if (al->idx_asm < offset)
			offset = al->idx_asm;
365

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

	return true;
}

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

383 384 385 386 387 388 389 390
#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);
}

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

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

414
	notes = symbol__annotation(dl->ops.target.sym);
415 416
	pthread_mutex_lock(&notes->lock);

417
	if (!symbol__hists(dl->ops.target.sym, evsel->evlist->nr_entries)) {
418 419
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
420
			    dl->ops.target.sym->name);
421 422 423 424
		return true;
	}

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

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

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

	return NULL;
}

449 450 451
static bool annotate_browser__jump(struct annotate_browser *browser,
				   struct perf_evsel *evsel,
				   struct hist_browser_timer *hbt)
452
{
453
	struct disasm_line *dl = disasm_line(browser->selection);
454
	u64 offset;
455
	s64 idx;
456

457
	if (!ins__is_jump(&dl->ins))
458 459
		return false;

460 461 462 463 464
	if (dl->ops.target.outside) {
		annotate_browser__callq(browser, evsel, hbt);
		return true;
	}

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

472
	annotate_browser__set_top(browser, &dl->al, idx);
473

474 475 476
	return true;
}

477
static
478
struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
479
					  char *s, s64 *idx)
480
{
481
	struct annotation *notes = browser__annotation(&browser->b);
482
	struct annotation_line *al = browser->selection;
483 484

	*idx = browser->b.index;
485
	list_for_each_entry_continue(al, &notes->src->source, node) {
486
		if (annotation_line__filter(al, notes))
487 488 489 490
			continue;

		++*idx;

491 492
		if (al->line && strstr(al->line, s) != NULL)
			return al;
493 494 495 496 497 498 499
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
500
	struct annotation_line *al;
501 502
	s64 idx;

503 504
	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (al == NULL) {
505 506 507 508
		ui_helpline__puts("String not found!");
		return false;
	}

509
	annotate_browser__set_top(browser, al, idx);
510 511 512 513
	browser->searching_backwards = false;
	return true;
}

514
static
515
struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
516
						  char *s, s64 *idx)
517
{
518
	struct annotation *notes = browser__annotation(&browser->b);
519
	struct annotation_line *al = browser->selection;
520 521

	*idx = browser->b.index;
522
	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
523
		if (annotation_line__filter(al, notes))
524 525 526 527
			continue;

		--*idx;

528 529
		if (al->line && strstr(al->line, s) != NULL)
			return al;
530 531 532 533 534 535 536
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
537
	struct annotation_line *al;
538 539
	s64 idx;

540 541
	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (al == NULL) {
542 543 544 545
		ui_helpline__puts("String not found!");
		return false;
	}

546
	annotate_browser__set_top(browser, al, idx);
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 595 596 597 598
	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);
}

599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615
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;
}

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

630
	hists__scnprintf_title(hists, title, sizeof(title));
631
	if (annotate_browser__show(&browser->b, title, help) < 0)
632
		return -1;
633

634
	annotate_browser__calc_percent(browser, evsel);
635

636 637 638
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
639
	}
640

641
	nd = browser->curr_hot;
642

643
	while (1) {
644
		key = ui_browser__run(&browser->b, delay_secs);
645

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

657
		switch (key) {
658
		case K_TIMER:
659 660
			if (hbt)
				hbt->timer(hbt->arg);
661

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

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

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

822
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
823 824
			     struct hist_browser_timer *hbt,
			     struct annotation_options *opts)
825
{
826
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt, opts);
827 828
}

829
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
830 831
			     struct hist_browser_timer *hbt,
			     struct annotation_options *opts)
832
{
833 834 835 836
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

837
	return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts);
838 839
}

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

864
	if (sym == NULL)
865 866
		return -1;

867
	if (map->dso->annotate_warned)
868 869
		return -1;

870
	err = symbol__annotate2(sym, map, evsel, opts, &browser.arch);
871 872 873 874
	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);
875
		goto out_free_offsets;
876 877
	}

878
	ui_helpline__push("Press ESC to exit");
879

880
	browser.b.width = notes->max_line_len;
881
	browser.b.nr_entries = notes->nr_entries;
882
	browser.b.entries = &notes->src->source,
883
	browser.b.width += 18; /* Percentage */
884

885
	if (notes->options->hide_src_code)
886
		ui_browser__init_asm_mode(&browser.b);
887

888
	ret = annotate_browser__run(&browser, evsel, hbt);
889 890

	annotated_source__purge(notes->src);
891 892

out_free_offsets:
893
	zfree(&notes->offsets);
894 895
	return ret;
}