annotate.c 27.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
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
109
{
110
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
111
	struct annotation *notes = browser__annotation(browser);
112
	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
113
	bool current_entry = ui_browser__is_current_entry(browser, row);
114
	bool change_color = (!notes->options->hide_src_code &&
115 116 117
			     (!current_entry || (browser->use_navkeypressed &&
					         !browser->navkeypressed)));
	int width = browser->width, printed;
118
	int i, pcnt_width = annotation__pcnt_width(notes),
119
	       cycles_width = annotation__cycles_width(notes);
120
	double percent_max = 0.0;
121
	char bf[256];
122
	bool show_title = false;
123

124
	for (i = 0; i < notes->nr_events; i++) {
125 126
		if (al->samples[i].percent > percent_max)
			percent_max = al->samples[i].percent;
127 128
	}

129
	if ((row == 0) && (al->offset == -1 || percent_max == 0.0)) {
130
		if (notes->have_cycles) {
131
			if (al->ipc == 0.0 && al->cycles == 0)
132 133 134 135 136
				show_title = true;
		} else
			show_title = true;
	}

137
	if (al->offset != -1 && percent_max != 0.0) {
138
		for (i = 0; i < notes->nr_events; i++) {
139
			ui_browser__set_percent_color(browser,
140
						al->samples[i].percent,
141
						current_entry);
142
			if (notes->options->show_total_period) {
143
				ui_browser__printf(browser, "%11" PRIu64 " ",
144
						   al->samples[i].he.period);
145
			} else if (notes->options->show_nr_samples) {
146
				ui_browser__printf(browser, "%6" PRIu64 " ",
147
						   al->samples[i].he.nr_samples);
148 149
			} else {
				ui_browser__printf(browser, "%6.2f ",
150
						   al->samples[i].percent);
151
			}
152
		}
153
	} else {
154
		ui_browser__set_percent_color(browser, 0, current_entry);
155 156

		if (!show_title)
157
			ui_browser__write_nstring(browser, " ", pcnt_width);
158 159
		else {
			ui_browser__printf(browser, "%*s", pcnt_width,
160 161
					   notes->options->show_total_period ? "Period" :
					   notes->options->show_nr_samples ? "Samples" : "Percent");
162
		}
163
	}
164
	if (notes->have_cycles) {
165
		if (al->ipc)
166
			ui_browser__printf(browser, "%*.2f ", ANNOTATION__IPC_WIDTH - 1, al->ipc);
167
		else if (!show_title)
168
			ui_browser__write_nstring(browser, " ", ANNOTATION__IPC_WIDTH);
169
		else
170
			ui_browser__printf(browser, "%*s ", ANNOTATION__IPC_WIDTH - 1, "IPC");
171

172
		if (al->cycles)
173
			ui_browser__printf(browser, "%*" PRIu64 " ",
174
					   ANNOTATION__CYCLES_WIDTH - 1, al->cycles);
175
		else if (!show_title)
176
			ui_browser__write_nstring(browser, " ", ANNOTATION__CYCLES_WIDTH);
177
		else
178
			ui_browser__printf(browser, "%*s ", ANNOTATION__CYCLES_WIDTH - 1, "Cycle");
179 180
	}

181
	SLsmg_write_char(' ');
182 183

	/* The scroll bar isn't being used */
184
	if (!browser->navkeypressed)
185 186
		width += 1;

187
	if (!*al->line)
188
		ui_browser__write_nstring(browser, " ", width - pcnt_width - cycles_width);
189
	else if (al->offset == -1) {
190
		if (al->line_nr && notes->options->show_linenr)
191
			printed = scnprintf(bf, sizeof(bf), "%-*d ",
192
					notes->widths.addr + 1, al->line_nr);
193 194
		else
			printed = scnprintf(bf, sizeof(bf), "%*s  ",
195
				    notes->widths.addr, " ");
196
		ui_browser__write_nstring(browser, bf, printed);
197
		ui_browser__write_nstring(browser, al->line, width - printed - pcnt_width - cycles_width + 1);
198
	} else {
199
		u64 addr = al->offset;
200
		int color = -1;
201

202
		if (!notes->options->use_offset)
203
			addr += notes->start;
204

205
		if (!notes->options->use_offset) {
206
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
207
		} else {
208
			if (al->jump_sources) {
209
				if (notes->options->show_nr_jumps) {
210 211
					int prev;
					printed = scnprintf(bf, sizeof(bf), "%*d ",
212
							    notes->widths.jumps,
213 214
							    al->jump_sources);
					prev = ui_browser__set_jumps_percent_color(browser, al->jump_sources,
215
										   current_entry);
216
					ui_browser__write_nstring(browser, bf, printed);
217
					ui_browser__set_color(browser, prev);
218 219
				}

220
				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
221
						    notes->widths.target, addr);
222
			} else {
223
				printed = scnprintf(bf, sizeof(bf), "%*s  ",
224
						    notes->widths.addr, " ");
225 226
			}
		}
227

228
		if (change_color)
229
			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
230
		ui_browser__write_nstring(browser, bf, printed);
231
		if (change_color)
232
			ui_browser__set_color(browser, color);
233

234 235
		disasm_line__write(disasm_line(al), browser, bf, sizeof(bf));

236
		ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
237
	}
238

239
	if (current_entry)
240
		ab->selection = al;
241 242
}

243 244
static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
{
245
	struct disasm_line *pos = list_prev_entry(cursor, al.node);
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
	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);
}

262
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
263 264
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
265
	struct disasm_line *cursor = disasm_line(ab->selection);
266
	struct annotation_line *target;
267
	unsigned int from, to;
268 269
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
270
	struct annotation *notes = symbol__annotation(sym);
271
	u8 pcnt_width = annotation__pcnt_width(notes);
272
	int width;
273 274 275 276

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

278
	if (!disasm_line__is_valid_jump(cursor, sym))
279
		return;
280

281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
	/*
	 * 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.
	 */
301
	target = notes->offsets[cursor->ops.target.offset];
302
	if (target == NULL) {
303
		ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
304 305 306
				    cursor->ops.target.offset);
		return;
	}
307

308
	if (notes->options->hide_src_code) {
309 310
		from = cursor->al.idx_asm;
		to = target->idx_asm;
311
	} else {
312 313
		from = (u64)cursor->al.idx;
		to = (u64)target->idx;
314 315
	}

316
	width = annotation__cycles_width(notes);
J
Jin Yao 已提交
317

318
	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
J
Jin Yao 已提交
319
	__ui_browser__line_arrow(browser,
320
				 pcnt_width + 2 + notes->widths.addr + width,
321
				 from, to);
322 323 324

	if (is_fused(ab, cursor)) {
		ui_browser__mark_fused(browser,
325
				       pcnt_width + 3 + notes->widths.addr + width,
326 327 328
				       from - 1,
				       to > from ? true : false);
	}
329 330 331 332
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
333
	struct annotation *notes = browser__annotation(browser);
334
	int ret = ui_browser__list_head_refresh(browser);
335
	int pcnt_width = annotation__pcnt_width(notes);
336

337
	if (notes->options->jump_arrows)
338
		annotate_browser__draw_current_jump(browser);
339

340
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
341
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
342 343 344
	return ret;
}

345
static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
346 347 348
{
	int i;

349
	for (i = 0; i < a->samples_nr; i++) {
350
		if (a->samples[i].percent == b->samples[i].percent)
351
			continue;
352
		return a->samples[i].percent < b->samples[i].percent;
353 354 355 356
	}
	return 0;
}

357
static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
358
{
359
	struct rb_node **p = &root->rb_node;
360
	struct rb_node *parent = NULL;
361
	struct annotation_line *l;
362 363 364

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

367
		if (disasm__cmp(al, l))
368 369 370 371
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
372 373
	rb_link_node(&al->rb_node, parent, p);
	rb_insert_color(&al->rb_node, root);
374 375
}

376
static void annotate_browser__set_top(struct annotate_browser *browser,
377
				      struct annotation_line *pos, u32 idx)
378 379 380
{
	unsigned back;

381 382 383
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
384

385
	while (browser->b.top_idx != 0 && back != 0) {
386
		pos = list_entry(pos->node.prev, struct annotation_line, node);
387

388
		if (disasm_line__filter(&browser->b, &pos->node))
389 390
			continue;

391
		--browser->b.top_idx;
392 393 394
		--back;
	}

395
	browser->b.top = pos;
396
	browser->b.navkeypressed = true;
397 398 399 400 401
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
402
	struct annotation *notes = browser__annotation(&browser->b);
403 404
	struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
	u32 idx = pos->idx;
405

406
	if (notes->options->hide_src_code)
407
		idx = pos->idx_asm;
408
	annotate_browser__set_top(browser, pos, idx);
409
	browser->curr_hot = nd;
410 411
}

412
static void annotate_browser__calc_percent(struct annotate_browser *browser,
413
					   struct perf_evsel *evsel)
414
{
415 416
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
417
	struct annotation *notes = symbol__annotation(sym);
418
	struct disasm_line *pos;
419 420 421 422 423

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

424 425
	symbol__calc_percent(sym, evsel);

426
	list_for_each_entry(pos, &notes->src->source, al.node) {
427 428
		double max_percent = 0.0;
		int i;
429

430
		if (pos->al.offset == -1) {
431
			RB_CLEAR_NODE(&pos->al.rb_node);
432 433 434
			continue;
		}

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

438 439
			if (max_percent < sample->percent)
				max_percent = sample->percent;
440 441
		}

442
		if (max_percent < 0.01 && pos->al.ipc == 0) {
443
			RB_CLEAR_NODE(&pos->al.rb_node);
444 445
			continue;
		}
446
		disasm_rb_tree__insert(&browser->entries, &pos->al);
447 448 449 450 451 452
	}
	pthread_mutex_unlock(&notes->lock);

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

453 454
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
455
	struct annotation *notes = browser__annotation(&browser->b);
456
	struct annotation_line *al;
457 458 459
	off_t offset = browser->b.index - browser->b.top_idx;

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

462
	if (notes->options->hide_src_code) {
463 464
		if (al->idx_asm < offset)
			offset = al->idx;
465

466
		browser->b.nr_entries = notes->nr_entries;
467
		notes->options->hide_src_code = false;
468
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
469 470
		browser->b.top_idx = al->idx - offset;
		browser->b.index = al->idx;
471
	} else {
472
		if (al->idx_asm < 0) {
473 474 475 476 477
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

478 479
		if (al->idx_asm < offset)
			offset = al->idx_asm;
480

481
		browser->b.nr_entries = notes->nr_asm_entries;
482
		notes->options->hide_src_code = true;
483
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
484 485
		browser->b.top_idx = al->idx_asm - offset;
		browser->b.index = al->idx_asm;
486 487 488 489 490
	}

	return true;
}

491
static void ui_browser__init_asm_mode(struct ui_browser *browser)
492
{
493 494 495
	struct annotation *notes = browser__annotation(browser);
	ui_browser__reset_index(browser);
	browser->nr_entries = notes->nr_asm_entries;
496 497
}

498 499 500 501 502 503 504 505
#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);
}

506 507
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
508
				    struct hist_browser_timer *hbt)
509 510
{
	struct map_symbol *ms = browser->b.priv;
511
	struct disasm_line *dl = disasm_line(browser->selection);
512
	struct annotation *notes;
513
	char title[SYM_TITLE_MAX_SIZE];
514

515
	if (!ins__is_call(&dl->ins))
516 517
		return false;

518
	if (!dl->ops.target.sym) {
519 520 521 522
		ui_helpline__puts("The called function was not found.");
		return true;
	}

523
	notes = symbol__annotation(dl->ops.target.sym);
524 525
	pthread_mutex_lock(&notes->lock);

526
	if (notes->src == NULL && symbol__alloc_hist(dl->ops.target.sym) < 0) {
527 528
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
529
			    dl->ops.target.sym->name);
530 531 532 533
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
534
	symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt);
535
	sym_title(ms->sym, ms->map, title, sizeof(title));
536
	ui_browser__show_title(&browser->b, title);
537 538 539
	return true;
}

540 541 542
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
543
{
544
	struct annotation *notes = browser__annotation(&browser->b);
545
	struct disasm_line *pos;
546 547

	*idx = 0;
548
	list_for_each_entry(pos, &notes->src->source, al.node) {
549
		if (pos->al.offset == offset)
550
			return pos;
551
		if (!disasm_line__filter(&browser->b, &pos->al.node))
552 553 554 555 556 557 558 559
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
560
	struct disasm_line *dl = disasm_line(browser->selection);
561
	u64 offset;
562
	s64 idx;
563

564
	if (!ins__is_jump(&dl->ins))
565 566
		return false;

567 568
	offset = dl->ops.target.offset;
	dl = annotate_browser__find_offset(browser, offset, &idx);
569
	if (dl == NULL) {
570
		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
571 572 573
		return true;
	}

574
	annotate_browser__set_top(browser, &dl->al, idx);
575

576 577 578
	return true;
}

579
static
580
struct annotation_line *annotate_browser__find_string(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 588
	list_for_each_entry_continue(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
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(struct annotate_browser *browser)
{
602
	struct annotation_line *al;
603 604
	s64 idx;

605 606
	al = annotate_browser__find_string(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
	browser->searching_backwards = false;
	return true;
}

616
static
617
struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
618
						  char *s, s64 *idx)
619
{
620
	struct annotation *notes = browser__annotation(&browser->b);
621
	struct annotation_line *al = browser->selection;
622 623

	*idx = browser->b.index;
624 625
	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
626 627 628 629
			continue;

		--*idx;

630 631
		if (al->line && strstr(al->line, s) != NULL)
			return al;
632 633 634 635 636 637 638
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
639
	struct annotation_line *al;
640 641
	s64 idx;

642 643
	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (al == NULL) {
644 645 646 647
		ui_helpline__puts("String not found!");
		return false;
	}

648
	annotate_browser__set_top(browser, al, idx);
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
	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);
}

701 702
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
703
				 struct hist_browser_timer *hbt)
704 705
{
	struct rb_node *nd = NULL;
706
	struct map_symbol *ms = browser->b.priv;
707
	struct symbol *sym = ms->sym;
708
	struct annotation *notes = symbol__annotation(ms->sym);
709
	const char *help = "Press 'h' for help on key bindings";
710
	int delay_secs = hbt ? hbt->refresh : 0;
711
	int key;
712
	char title[SYM_TITLE_MAX_SIZE];
713

714 715
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
716
		return -1;
717

718
	annotate_browser__calc_percent(browser, evsel);
719

720 721 722
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
723
	}
724

725
	nd = browser->curr_hot;
726

727
	while (1) {
728
		key = ui_browser__run(&browser->b, delay_secs);
729

730
		if (delay_secs != 0) {
731
			annotate_browser__calc_percent(browser, evsel);
732 733 734 735 736 737 738 739 740
			/*
			 * 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;
		}

741
		switch (key) {
742
		case K_TIMER:
743 744
			if (hbt)
				hbt->timer(hbt->arg);
745 746

			if (delay_secs != 0)
747
				symbol__annotate_decay_histogram(sym, evsel->idx);
748
			continue;
749
		case K_TAB:
750 751 752
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
753
					nd = rb_last(&browser->entries);
754
			} else
755
				nd = browser->curr_hot;
756
			break;
757
		case K_UNTAB:
758
			if (nd != NULL) {
759 760
				nd = rb_next(nd);
				if (nd == NULL)
761
					nd = rb_first(&browser->entries);
762
			} else
763
				nd = browser->curr_hot;
764
			break;
765
		case K_F1:
766
		case 'h':
767
			ui_browser__help_window(&browser->b,
768 769 770
		"UP/DOWN/PGUP\n"
		"PGDN/SPACE    Navigate\n"
		"q/ESC/CTRL+C  Exit\n\n"
771 772
		"ENTER         Go to target\n"
		"ESC           Exit\n"
773 774
		"H             Go to hottest instruction\n"
		"TAB/shift+TAB Cycle thru hottest instructions\n"
775 776 777 778 779
		"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"
780
		"t             Circulate percent, total period, samples view\n"
781
		"/             Search string\n"
782
		"k             Toggle line numbers\n"
783
		"r             Run available scripts\n"
784
		"?             Search string backwards\n");
785
			continue;
786 787 788 789 790
		case 'r':
			{
				script_browse(NULL);
				continue;
			}
791
		case 'k':
792
			notes->options->show_linenr = !notes->options->show_linenr;
793
			break;
794
		case 'H':
795
			nd = browser->curr_hot;
796
			break;
797
		case 's':
798
			if (annotate_browser__toggle_source(browser))
799 800
				ui_helpline__puts(help);
			continue;
801
		case 'o':
802
			notes->options->use_offset = !notes->options->use_offset;
803
			annotation__update_column_widths(notes);
804
			continue;
805
		case 'j':
806
			notes->options->jump_arrows = !notes->options->jump_arrows;
807
			continue;
808
		case 'J':
809
			notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
810
			annotation__update_column_widths(notes);
811
			continue;
812
		case '/':
813
			if (annotate_browser__search(browser, delay_secs)) {
814 815 816 817 818
show_help:
				ui_helpline__puts(help);
			}
			continue;
		case 'n':
819 820 821
			if (browser->searching_backwards ?
			    annotate_browser__continue_search_reverse(browser, delay_secs) :
			    annotate_browser__continue_search(browser, delay_secs))
822 823 824
				goto show_help;
			continue;
		case '?':
825
			if (annotate_browser__search_reverse(browser, delay_secs))
826 827
				goto show_help;
			continue;
828 829 830 831
		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",
832 833 834 835
					   seq++, browser->b.nr_entries,
					   browser->b.height,
					   browser->b.index,
					   browser->b.top_idx,
836
					   notes->nr_asm_entries);
837 838
		}
			continue;
839 840
		case K_ENTER:
		case K_RIGHT:
841 842 843
		{
			struct disasm_line *dl = disasm_line(browser->selection);

844
			if (browser->selection == NULL)
845
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
846
			else if (browser->selection->offset == -1)
847
				ui_helpline__puts("Actions are only available for assembly lines.");
848
			else if (!dl->ins.ops)
849
				goto show_sup_ins;
850
			else if (ins__is_ret(&dl->ins))
851
				goto out;
852
			else if (!(annotate_browser__jump(browser) ||
853
				     annotate_browser__callq(browser, evsel, hbt))) {
854
show_sup_ins:
855
				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
856
			}
857
			continue;
858
		}
859
		case 't':
860 861 862 863 864
			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;
865
			else
866
				notes->options->show_total_period = true;
867
			annotation__update_column_widths(notes);
868
			continue;
869 870
		case K_LEFT:
		case K_ESC:
871 872
		case 'q':
		case CTRL('c'):
873
			goto out;
874 875
		default:
			continue;
876
		}
877 878

		if (nd != NULL)
879
			annotate_browser__set_rb_top(browser, nd);
880 881
	}
out:
882
	ui_browser__hide(&browser->b);
883
	return key;
884 885
}

886 887 888
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
			     struct hist_browser_timer *hbt)
{
889
	/* Set default value for show_total_period and show_nr_samples  */
890
	annotate_browser__opts.show_total_period =
891 892 893
		symbol_conf.show_total_period;
	annotate_browser__opts.show_nr_samples =
		symbol_conf.show_nr_samples;
894

895 896 897
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

898
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
899
			     struct hist_browser_timer *hbt)
900
{
901 902 903 904
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

905
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
906 907
}

908 909
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
910
			 struct hist_browser_timer *hbt)
911
{
912
	struct annotation *notes = symbol__annotation(sym);
913
	size_t size;
914 915 916 917
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
918 919
	struct annotate_browser browser = {
		.b = {
920
			.refresh = annotate_browser__refresh,
921 922
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
923
			.filter  = disasm_line__filter,
924
			.priv	 = &ms,
925
			.use_navkeypressed = true,
926
		},
927
	};
928
	int ret = -1, err;
929
	int nr_pcnt = 1;
930

931
	if (sym == NULL)
932 933
		return -1;

934 935
	size = symbol__size(sym);

936
	if (map->dso->annotate_warned)
937 938
		return -1;

939 940
	notes->options = &annotate_browser__opts;

941 942
	notes->offsets = zalloc(size * sizeof(struct annotation_line *));
	if (notes->offsets == NULL) {
943 944 945 946
		ui__error("Not enough memory!");
		return -1;
	}

947
	if (perf_evsel__is_group_event(evsel))
948 949
		nr_pcnt = evsel->nr_members;

950
	err = symbol__annotate(sym, map, evsel, 0, &browser.arch);
951 952 953 954
	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);
955
		goto out_free_offsets;
956 957
	}

958 959
	symbol__calc_percent(sym, evsel);

960
	ui_helpline__push("Press ESC to exit");
961

962
	notes->start = map__rip_2objdump(map, sym->start);
963

964 965
	annotation__set_offsets(notes, size);
	browser.b.width = notes->max_line_len;
966
	annotation__mark_jump_targets(notes, sym);
967
	annotation__compute_ipc(notes, size);
968
	annotation__init_column_widths(notes, sym);
969
	notes->nr_events = nr_pcnt;
970
	browser.b.nr_entries = notes->nr_entries;
971
	browser.b.entries = &notes->src->source,
972
	browser.b.width += 18; /* Percentage */
973

974
	if (notes->options->hide_src_code)
975
		ui_browser__init_asm_mode(&browser.b);
976

977
	annotation__update_column_widths(notes);
978

979
	ret = annotate_browser__run(&browser, evsel, hbt);
980 981

	annotated_source__purge(notes->src);
982 983

out_free_offsets:
984
	zfree(&notes->offsets);
985 986
	return ret;
}
987 988 989

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

991 992 993
/*
 * Keep the entries sorted, they are bsearch'ed
 */
994
static struct annotate_config {
995 996 997 998 999
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
1000
	ANNOTATE_CFG(show_linenr),
1001
	ANNOTATE_CFG(show_nr_jumps),
1002
	ANNOTATE_CFG(show_nr_samples),
1003
	ANNOTATE_CFG(show_total_period),
1004
	ANNOTATE_CFG(use_offset),
1005 1006 1007 1008 1009 1010
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1011
	const struct annotate_config *cfg = cfgp;
1012 1013 1014 1015

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

1016 1017
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1018
{
1019
	struct annotate_config *cfg;
1020 1021
	const char *name;

1022
	if (!strstarts(var, "annotate."))
1023 1024 1025 1026
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1027
		      sizeof(struct annotate_config), annotate_config__cmp);
1028 1029

	if (cfg == NULL)
1030 1031 1032
		ui__warning("%s variable unknown, ignoring...", var);
	else
		*cfg->value = perf_config_bool(name, value);
1033 1034 1035 1036 1037 1038 1039
	return 0;
}

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