annotate.c 25.5 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/config.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 <sys/ttydefaults.h>
19

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

25
static struct annotation_options annotate_browser__opts = {
26 27 28 29
	.use_offset	= true,
	.jump_arrows	= true,
};

30 31
struct arch;

32
struct annotate_browser {
33 34 35 36 37 38 39
	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];
40 41
};

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

48
static bool disasm_line__filter(struct ui_browser *browser, void *entry)
49
{
50
	struct annotation *notes = browser__annotation(browser);
51 52

	if (notes->options->hide_src_code) {
53 54 55
		struct annotation_line *al = list_entry(entry, struct annotation_line, node);

		return al->offset == -1;
56 57 58 59 60
	}

	return false;
}

61
static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
62
{
63
	struct annotation *notes = browser__annotation(browser);
64

65
	if (current && (!browser->use_navkeypressed || browser->navkeypressed))
66
		return HE_COLORSET_SELECTED;
67
	if (nr == notes->max_jump_sources)
68 69 70 71 72 73
		return HE_COLORSET_TOP;
	if (nr > 1)
		return HE_COLORSET_MEDIUM;
	return HE_COLORSET_NORMAL;
}

74
static int ui_browser__set_jumps_percent_color(struct ui_browser *browser, int nr, bool current)
75
{
76 77
	 int color = ui_browser__jumps_percent_color(browser, nr, current);
	 return ui_browser__set_color(browser, color);
78 79
}

80 81 82
static void disasm_line__write(struct disasm_line *dl, struct ui_browser *browser,
			       char *bf, size_t size)
{
83
	struct annotation *notes = browser__annotation(browser);
84

85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
	if (dl->ins.ops && dl->ins.ops->scnprintf) {
		if (ins__is_jump(&dl->ins)) {
			bool fwd = dl->ops.target.offset > dl->al.offset;

			ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
							    SLSMG_UARROW_CHAR);
			SLsmg_write_char(' ');
		} else if (ins__is_call(&dl->ins)) {
			ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
			SLsmg_write_char(' ');
		} else if (ins__is_ret(&dl->ins)) {
			ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
			SLsmg_write_char(' ');
		} else {
			ui_browser__write_nstring(browser, " ", 2);
		}
	} else {
		ui_browser__write_nstring(browser, " ", 2);
	}

105
	disasm_line__scnprintf(dl, bf, size, !notes->options->use_offset);
106 107
}

108 109 110 111 112 113 114 115 116 117 118 119 120 121
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);
}

122
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
123
{
124
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
125
	struct annotation *notes = browser__annotation(browser);
126
	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
127
	bool current_entry = ui_browser__is_current_entry(browser, row);
128
	bool change_color = (!notes->options->hide_src_code &&
129 130 131
			     (!current_entry || (browser->use_navkeypressed &&
					         !browser->navkeypressed)));
	int width = browser->width, printed;
132 133
	int pcnt_width = annotation__pcnt_width(notes),
	    cycles_width = annotation__cycles_width(notes);
134
	char bf[256];
135

136 137 138
	annotation_line__print_start(al, notes, row == 0, current_entry, browser,
				     annotate_browser__set_percent_color,
				     annotate_browser__printf);
139 140

	/* The scroll bar isn't being used */
141
	if (!browser->navkeypressed)
142 143
		width += 1;

144
	if (!*al->line)
145
		ui_browser__write_nstring(browser, " ", width - pcnt_width - cycles_width);
146
	else if (al->offset == -1) {
147
		if (al->line_nr && notes->options->show_linenr)
148
			printed = scnprintf(bf, sizeof(bf), "%-*d ", notes->widths.addr + 1, al->line_nr);
149
		else
150
			printed = scnprintf(bf, sizeof(bf), "%*s  ", notes->widths.addr, " ");
151
		ui_browser__write_nstring(browser, bf, printed);
152
		ui_browser__write_nstring(browser, al->line, width - printed - pcnt_width - cycles_width + 1);
153
	} else {
154
		u64 addr = al->offset;
155
		int color = -1;
156

157
		if (!notes->options->use_offset)
158
			addr += notes->start;
159

160
		if (!notes->options->use_offset) {
161
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
162
		} else {
163
			if (al->jump_sources) {
164
				if (notes->options->show_nr_jumps) {
165 166
					int prev;
					printed = scnprintf(bf, sizeof(bf), "%*d ",
167
							    notes->widths.jumps,
168 169
							    al->jump_sources);
					prev = ui_browser__set_jumps_percent_color(browser, al->jump_sources,
170
										   current_entry);
171
					ui_browser__write_nstring(browser, bf, printed);
172
					ui_browser__set_color(browser, prev);
173 174
				}

175
				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
176
						    notes->widths.target, addr);
177
			} else {
178
				printed = scnprintf(bf, sizeof(bf), "%*s  ",
179
						    notes->widths.addr, " ");
180 181
			}
		}
182

183
		if (change_color)
184
			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
185
		ui_browser__write_nstring(browser, bf, printed);
186
		if (change_color)
187
			ui_browser__set_color(browser, color);
188

189 190
		disasm_line__write(disasm_line(al), browser, bf, sizeof(bf));

191
		ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
192
	}
193

194
	if (current_entry)
195
		ab->selection = al;
196 197
}

198 199
static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
{
200
	struct disasm_line *pos = list_prev_entry(cursor, al.node);
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
	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);
}

217
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
218 219
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
220
	struct disasm_line *cursor = disasm_line(ab->selection);
221
	struct annotation_line *target;
222
	unsigned int from, to;
223 224
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
225
	struct annotation *notes = symbol__annotation(sym);
226
	u8 pcnt_width = annotation__pcnt_width(notes);
227
	int width;
228 229 230 231

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

233
	if (!disasm_line__is_valid_jump(cursor, sym))
234
		return;
235

236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
	/*
	 * 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.
	 */
256
	target = notes->offsets[cursor->ops.target.offset];
257
	if (target == NULL) {
258
		ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
259 260 261
				    cursor->ops.target.offset);
		return;
	}
262

263
	if (notes->options->hide_src_code) {
264 265
		from = cursor->al.idx_asm;
		to = target->idx_asm;
266
	} else {
267 268
		from = (u64)cursor->al.idx;
		to = (u64)target->idx;
269 270
	}

271
	width = annotation__cycles_width(notes);
J
Jin Yao 已提交
272

273
	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
J
Jin Yao 已提交
274
	__ui_browser__line_arrow(browser,
275
				 pcnt_width + 2 + notes->widths.addr + width,
276
				 from, to);
277 278 279

	if (is_fused(ab, cursor)) {
		ui_browser__mark_fused(browser,
280
				       pcnt_width + 3 + notes->widths.addr + width,
281 282 283
				       from - 1,
				       to > from ? true : false);
	}
284 285 286 287
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
288
	struct annotation *notes = browser__annotation(browser);
289
	int ret = ui_browser__list_head_refresh(browser);
290
	int pcnt_width = annotation__pcnt_width(notes);
291

292
	if (notes->options->jump_arrows)
293
		annotate_browser__draw_current_jump(browser);
294

295
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
296
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
297 298 299
	return ret;
}

300
static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
301 302 303
{
	int i;

304
	for (i = 0; i < a->samples_nr; i++) {
305
		if (a->samples[i].percent == b->samples[i].percent)
306
			continue;
307
		return a->samples[i].percent < b->samples[i].percent;
308 309 310 311
	}
	return 0;
}

312
static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
313
{
314
	struct rb_node **p = &root->rb_node;
315
	struct rb_node *parent = NULL;
316
	struct annotation_line *l;
317 318 319

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

322
		if (disasm__cmp(al, l))
323 324 325 326
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
327 328
	rb_link_node(&al->rb_node, parent, p);
	rb_insert_color(&al->rb_node, root);
329 330
}

331
static void annotate_browser__set_top(struct annotate_browser *browser,
332
				      struct annotation_line *pos, u32 idx)
333 334 335
{
	unsigned back;

336 337 338
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
339

340
	while (browser->b.top_idx != 0 && back != 0) {
341
		pos = list_entry(pos->node.prev, struct annotation_line, node);
342

343
		if (disasm_line__filter(&browser->b, &pos->node))
344 345
			continue;

346
		--browser->b.top_idx;
347 348 349
		--back;
	}

350
	browser->b.top = pos;
351
	browser->b.navkeypressed = true;
352 353 354 355 356
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
357
	struct annotation *notes = browser__annotation(&browser->b);
358 359
	struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
	u32 idx = pos->idx;
360

361
	if (notes->options->hide_src_code)
362
		idx = pos->idx_asm;
363
	annotate_browser__set_top(browser, pos, idx);
364
	browser->curr_hot = nd;
365 366
}

367
static void annotate_browser__calc_percent(struct annotate_browser *browser,
368
					   struct perf_evsel *evsel)
369
{
370 371
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
372
	struct annotation *notes = symbol__annotation(sym);
373
	struct disasm_line *pos;
374 375 376 377 378

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

379 380
	symbol__calc_percent(sym, evsel);

381
	list_for_each_entry(pos, &notes->src->source, al.node) {
382 383
		double max_percent = 0.0;
		int i;
384

385
		if (pos->al.offset == -1) {
386
			RB_CLEAR_NODE(&pos->al.rb_node);
387 388 389
			continue;
		}

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

393 394
			if (max_percent < sample->percent)
				max_percent = sample->percent;
395 396
		}

397
		if (max_percent < 0.01 && pos->al.ipc == 0) {
398
			RB_CLEAR_NODE(&pos->al.rb_node);
399 400
			continue;
		}
401
		disasm_rb_tree__insert(&browser->entries, &pos->al);
402 403 404 405 406 407
	}
	pthread_mutex_unlock(&notes->lock);

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

408 409
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
410
	struct annotation *notes = browser__annotation(&browser->b);
411
	struct annotation_line *al;
412 413 414
	off_t offset = browser->b.index - browser->b.top_idx;

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

417
	if (notes->options->hide_src_code) {
418 419
		if (al->idx_asm < offset)
			offset = al->idx;
420

421
		browser->b.nr_entries = notes->nr_entries;
422
		notes->options->hide_src_code = false;
423
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
424 425
		browser->b.top_idx = al->idx - offset;
		browser->b.index = al->idx;
426
	} else {
427
		if (al->idx_asm < 0) {
428 429 430 431 432
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

433 434
		if (al->idx_asm < offset)
			offset = al->idx_asm;
435

436
		browser->b.nr_entries = notes->nr_asm_entries;
437
		notes->options->hide_src_code = true;
438
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
439 440
		browser->b.top_idx = al->idx_asm - offset;
		browser->b.index = al->idx_asm;
441 442 443 444 445
	}

	return true;
}

446
static void ui_browser__init_asm_mode(struct ui_browser *browser)
447
{
448 449 450
	struct annotation *notes = browser__annotation(browser);
	ui_browser__reset_index(browser);
	browser->nr_entries = notes->nr_asm_entries;
451 452
}

453 454 455 456 457 458 459 460
#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);
}

461 462
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
463
				    struct hist_browser_timer *hbt)
464 465
{
	struct map_symbol *ms = browser->b.priv;
466
	struct disasm_line *dl = disasm_line(browser->selection);
467
	struct annotation *notes;
468
	char title[SYM_TITLE_MAX_SIZE];
469

470
	if (!ins__is_call(&dl->ins))
471 472
		return false;

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

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

481
	if (notes->src == NULL && symbol__alloc_hist(dl->ops.target.sym) < 0) {
482 483
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
484
			    dl->ops.target.sym->name);
485 486 487 488
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
489
	symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt);
490
	sym_title(ms->sym, ms->map, title, sizeof(title));
491
	ui_browser__show_title(&browser->b, title);
492 493 494
	return true;
}

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

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

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
515
	struct disasm_line *dl = disasm_line(browser->selection);
516
	u64 offset;
517
	s64 idx;
518

519
	if (!ins__is_jump(&dl->ins))
520 521
		return false;

522 523
	offset = dl->ops.target.offset;
	dl = annotate_browser__find_offset(browser, offset, &idx);
524
	if (dl == NULL) {
525
		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
526 527 528
		return true;
	}

529
	annotate_browser__set_top(browser, &dl->al, idx);
530

531 532 533
	return true;
}

534
static
535
struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
536
					  char *s, s64 *idx)
537
{
538
	struct annotation *notes = browser__annotation(&browser->b);
539
	struct annotation_line *al = browser->selection;
540 541

	*idx = browser->b.index;
542 543
	list_for_each_entry_continue(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
544 545 546 547
			continue;

		++*idx;

548 549
		if (al->line && strstr(al->line, s) != NULL)
			return al;
550 551 552 553 554 555 556
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
557
	struct annotation_line *al;
558 559
	s64 idx;

560 561
	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (al == NULL) {
562 563 564 565
		ui_helpline__puts("String not found!");
		return false;
	}

566
	annotate_browser__set_top(browser, al, idx);
567 568 569 570
	browser->searching_backwards = false;
	return true;
}

571
static
572
struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
573
						  char *s, s64 *idx)
574
{
575
	struct annotation *notes = browser__annotation(&browser->b);
576
	struct annotation_line *al = browser->selection;
577 578

	*idx = browser->b.index;
579 580
	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
581 582 583 584
			continue;

		--*idx;

585 586
		if (al->line && strstr(al->line, s) != NULL)
			return al;
587 588 589 590 591 592 593
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
594
	struct annotation_line *al;
595 596
	s64 idx;

597 598
	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (al == NULL) {
599 600 601 602
		ui_helpline__puts("String not found!");
		return false;
	}

603
	annotate_browser__set_top(browser, al, idx);
604 605 606 607 608 609 610 611 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
	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);
}

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

669 670
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
671
		return -1;
672

673
	annotate_browser__calc_percent(browser, evsel);
674

675 676 677
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
678
	}
679

680
	nd = browser->curr_hot;
681

682
	while (1) {
683
		key = ui_browser__run(&browser->b, delay_secs);
684

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

696
		switch (key) {
697
		case K_TIMER:
698 699
			if (hbt)
				hbt->timer(hbt->arg);
700 701

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

799
			if (browser->selection == NULL)
800
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
801
			else if (browser->selection->offset == -1)
802
				ui_helpline__puts("Actions are only available for assembly lines.");
803
			else if (!dl->ins.ops)
804
				goto show_sup_ins;
805
			else if (ins__is_ret(&dl->ins))
806
				goto out;
807
			else if (!(annotate_browser__jump(browser) ||
808
				     annotate_browser__callq(browser, evsel, hbt))) {
809
show_sup_ins:
810
				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
811
			}
812
			continue;
813
		}
814
		case 't':
815 816 817 818 819
			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;
820
			else
821
				notes->options->show_total_period = true;
822
			annotation__update_column_widths(notes);
823
			continue;
824 825
		case K_LEFT:
		case K_ESC:
826 827
		case 'q':
		case CTRL('c'):
828
			goto out;
829 830
		default:
			continue;
831
		}
832 833

		if (nd != NULL)
834
			annotate_browser__set_rb_top(browser, nd);
835 836
	}
out:
837
	ui_browser__hide(&browser->b);
838
	return key;
839 840
}

841 842 843
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
			     struct hist_browser_timer *hbt)
{
844
	/* Set default value for show_total_period and show_nr_samples  */
845
	annotate_browser__opts.show_total_period =
846 847 848
		symbol_conf.show_total_period;
	annotate_browser__opts.show_nr_samples =
		symbol_conf.show_nr_samples;
849

850 851 852
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

853
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
854
			     struct hist_browser_timer *hbt)
855
{
856 857 858 859
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

860
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
861 862
}

863 864
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
865
			 struct hist_browser_timer *hbt)
866
{
867
	struct annotation *notes = symbol__annotation(sym);
868 869 870 871
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
872 873
	struct annotate_browser browser = {
		.b = {
874
			.refresh = annotate_browser__refresh,
875 876
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
877
			.filter  = disasm_line__filter,
878
			.priv	 = &ms,
879
			.use_navkeypressed = true,
880
		},
881
	};
882
	int ret = -1, err;
883

884
	if (sym == NULL)
885 886
		return -1;

887
	if (map->dso->annotate_warned)
888 889
		return -1;

890
	err = symbol__annotate2(sym, map, evsel, &annotate_browser__opts, &browser.arch);
891 892 893 894
	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);
895
		goto out_free_offsets;
896 897
	}

898
	ui_helpline__push("Press ESC to exit");
899

900
	browser.b.width = notes->max_line_len;
901
	browser.b.nr_entries = notes->nr_entries;
902
	browser.b.entries = &notes->src->source,
903
	browser.b.width += 18; /* Percentage */
904

905
	if (notes->options->hide_src_code)
906
		ui_browser__init_asm_mode(&browser.b);
907

908
	ret = annotate_browser__run(&browser, evsel, hbt);
909 910

	annotated_source__purge(notes->src);
911 912

out_free_offsets:
913
	zfree(&notes->offsets);
914 915
	return ret;
}
916 917 918

#define ANNOTATE_CFG(n) \
	{ .name = #n, .value = &annotate_browser__opts.n, }
919

920 921 922
/*
 * Keep the entries sorted, they are bsearch'ed
 */
923
static struct annotate_config {
924 925 926 927 928
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
929
	ANNOTATE_CFG(show_linenr),
930
	ANNOTATE_CFG(show_nr_jumps),
931
	ANNOTATE_CFG(show_nr_samples),
932
	ANNOTATE_CFG(show_total_period),
933
	ANNOTATE_CFG(use_offset),
934 935 936 937 938 939
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
940
	const struct annotate_config *cfg = cfgp;
941 942 943 944

	return strcmp(name, cfg->name);
}

945 946
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
947
{
948
	struct annotate_config *cfg;
949 950
	const char *name;

951
	if (!strstarts(var, "annotate."))
952 953 954 955
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
956
		      sizeof(struct annotate_config), annotate_config__cmp);
957 958

	if (cfg == NULL)
959 960 961
		ui__warning("%s variable unknown, ignoring...", var);
	else
		*cfg->value = perf_config_bool(name, value);
962 963 964 965 966 967 968
	return 0;
}

void annotate_browser__init(void)
{
	perf_config(annotate__config, NULL);
}