annotate.c 27.8 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 910 911 912 913 914 915 916
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

917 918
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
919
			 struct hist_browser_timer *hbt)
920
{
921
	struct annotation *notes = symbol__annotation(sym);
922
	size_t size;
923 924 925 926
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
927 928
	struct annotate_browser browser = {
		.b = {
929
			.refresh = annotate_browser__refresh,
930 931
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
932
			.filter  = disasm_line__filter,
933
			.priv	 = &ms,
934
			.use_navkeypressed = true,
935
		},
936
	};
937
	int ret = -1, err;
938
	int nr_pcnt = 1;
939

940
	if (sym == NULL)
941 942
		return -1;

943 944
	size = symbol__size(sym);

945
	if (map->dso->annotate_warned)
946 947
		return -1;

948 949
	notes->options = &annotate_browser__opts;

950 951
	notes->offsets = zalloc(size * sizeof(struct annotation_line *));
	if (notes->offsets == NULL) {
952 953 954 955
		ui__error("Not enough memory!");
		return -1;
	}

956
	if (perf_evsel__is_group_event(evsel))
957 958
		nr_pcnt = evsel->nr_members;

959
	err = symbol__annotate(sym, map, evsel, 0, &browser.arch);
960 961 962 963
	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);
964
		goto out_free_offsets;
965 966
	}

967 968
	symbol__calc_percent(sym, evsel);

969
	ui_helpline__push("Press ESC to exit");
970

971
	notes->start = map__rip_2objdump(map, sym->start);
972

973 974
	annotation__set_offsets(notes, size);
	browser.b.width = notes->max_line_len;
975
	annotation__mark_jump_targets(notes, sym);
976
	annotation__compute_ipc(notes, size);
977

978 979 980
	notes->widths.addr = notes->widths.target = notes->widths.min_addr = hex_width(size);
	notes->widths.max_addr = hex_width(sym->end);
	notes->widths.jumps = width_jumps(notes->max_jump_sources);
981
	notes->nr_events = nr_pcnt;
982
	browser.b.nr_entries = notes->nr_entries;
983
	browser.b.entries = &notes->src->source,
984
	browser.b.width += 18; /* Percentage */
985

986
	if (notes->options->hide_src_code)
987
		ui_browser__init_asm_mode(&browser.b);
988

989
	annotation__update_column_widths(notes);
990

991
	ret = annotate_browser__run(&browser, evsel, hbt);
992 993

	annotated_source__purge(notes->src);
994 995

out_free_offsets:
996
	zfree(&notes->offsets);
997 998
	return ret;
}
999 1000 1001

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

1003 1004 1005
/*
 * Keep the entries sorted, they are bsearch'ed
 */
1006
static struct annotate_config {
1007 1008 1009 1010 1011
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
1012
	ANNOTATE_CFG(show_linenr),
1013
	ANNOTATE_CFG(show_nr_jumps),
1014
	ANNOTATE_CFG(show_nr_samples),
1015
	ANNOTATE_CFG(show_total_period),
1016
	ANNOTATE_CFG(use_offset),
1017 1018 1019 1020 1021 1022
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1023
	const struct annotate_config *cfg = cfgp;
1024 1025 1026 1027

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

1028 1029
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1030
{
1031
	struct annotate_config *cfg;
1032 1033
	const char *name;

1034
	if (!strstarts(var, "annotate."))
1035 1036 1037 1038
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1039
		      sizeof(struct annotate_config), annotate_config__cmp);
1040 1041

	if (cfg == NULL)
1042 1043 1044
		ui__warning("%s variable unknown, ignoring...", var);
	else
		*cfg->value = perf_config_bool(name, value);
1045 1046 1047 1048 1049 1050 1051
	return 0;
}

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