xref: /openbmc/linux/tools/perf/ui/browsers/annotate.c (revision 023e4163)
1 // SPDX-License-Identifier: GPL-2.0
2 #include "../../util/util.h"
3 #include "../browser.h"
4 #include "../helpline.h"
5 #include "../ui.h"
6 #include "../util.h"
7 #include "../../util/annotate.h"
8 #include "../../util/hist.h"
9 #include "../../util/sort.h"
10 #include "../../util/map.h"
11 #include "../../util/symbol.h"
12 #include "../../util/evsel.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 #include <asm/bug.h>
20 
21 struct disasm_line_samples {
22 	double		      percent;
23 	struct sym_hist_entry he;
24 };
25 
26 struct arch;
27 
28 struct annotate_browser {
29 	struct ui_browser	    b;
30 	struct rb_root		    entries;
31 	struct rb_node		   *curr_hot;
32 	struct annotation_line	   *selection;
33 	struct arch		   *arch;
34 	struct annotation_options  *opts;
35 	bool			    searching_backwards;
36 	char			    search_bf[128];
37 };
38 
39 static inline struct annotation *browser__annotation(struct ui_browser *browser)
40 {
41 	struct map_symbol *ms = browser->priv;
42 	return symbol__annotation(ms->sym);
43 }
44 
45 static bool disasm_line__filter(struct ui_browser *browser, void *entry)
46 {
47 	struct annotation *notes = browser__annotation(browser);
48 	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
49 	return annotation_line__filter(al, notes);
50 }
51 
52 static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
53 {
54 	struct annotation *notes = browser__annotation(browser);
55 
56 	if (current && (!browser->use_navkeypressed || browser->navkeypressed))
57 		return HE_COLORSET_SELECTED;
58 	if (nr == notes->max_jump_sources)
59 		return HE_COLORSET_TOP;
60 	if (nr > 1)
61 		return HE_COLORSET_MEDIUM;
62 	return HE_COLORSET_NORMAL;
63 }
64 
65 static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current)
66 {
67 	 int color = ui_browser__jumps_percent_color(browser, nr, current);
68 	 return ui_browser__set_color(browser, color);
69 }
70 
71 static int annotate_browser__set_color(void *browser, int color)
72 {
73 	return ui_browser__set_color(browser, color);
74 }
75 
76 static void annotate_browser__write_graph(void *browser, int graph)
77 {
78 	ui_browser__write_graph(browser, graph);
79 }
80 
81 static void annotate_browser__set_percent_color(void *browser, double percent, bool current)
82 {
83 	ui_browser__set_percent_color(browser, percent, current);
84 }
85 
86 static void annotate_browser__printf(void *browser, const char *fmt, ...)
87 {
88 	va_list args;
89 
90 	va_start(args, fmt);
91 	ui_browser__vprintf(browser, fmt, args);
92 	va_end(args);
93 }
94 
95 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
96 {
97 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
98 	struct annotation *notes = browser__annotation(browser);
99 	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
100 	struct annotation_write_ops ops = {
101 		.first_line		 = row == 0,
102 		.current_entry		 = ui_browser__is_current_entry(browser, row),
103 		.change_color		 = (!notes->options->hide_src_code &&
104 					    (!ops.current_entry ||
105 					     (browser->use_navkeypressed &&
106 					      !browser->navkeypressed))),
107 		.width			 = browser->width,
108 		.obj			 = browser,
109 		.set_color		 = annotate_browser__set_color,
110 		.set_percent_color	 = annotate_browser__set_percent_color,
111 		.set_jumps_percent_color = ui_browser__set_jumps_percent_color,
112 		.printf			 = annotate_browser__printf,
113 		.write_graph		 = annotate_browser__write_graph,
114 	};
115 
116 	/* The scroll bar isn't being used */
117 	if (!browser->navkeypressed)
118 		ops.width += 1;
119 
120 	annotation_line__write(al, notes, &ops, ab->opts);
121 
122 	if (ops.current_entry)
123 		ab->selection = al;
124 }
125 
126 static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
127 {
128 	struct disasm_line *pos = list_prev_entry(cursor, al.node);
129 	const char *name;
130 
131 	if (!pos)
132 		return false;
133 
134 	if (ins__is_lock(&pos->ins))
135 		name = pos->ops.locked.ins.name;
136 	else
137 		name = pos->ins.name;
138 
139 	if (!name || !cursor->ins.name)
140 		return false;
141 
142 	return ins__is_fused(ab->arch, name, cursor->ins.name);
143 }
144 
145 static void annotate_browser__draw_current_jump(struct ui_browser *browser)
146 {
147 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
148 	struct disasm_line *cursor = disasm_line(ab->selection);
149 	struct annotation_line *target;
150 	unsigned int from, to;
151 	struct map_symbol *ms = ab->b.priv;
152 	struct symbol *sym = ms->sym;
153 	struct annotation *notes = symbol__annotation(sym);
154 	u8 pcnt_width = annotation__pcnt_width(notes);
155 	int width;
156 
157 	/* PLT symbols contain external offsets */
158 	if (strstr(sym->name, "@plt"))
159 		return;
160 
161 	if (!disasm_line__is_valid_local_jump(cursor, sym))
162 		return;
163 
164 	/*
165 	 * This first was seen with a gcc function, _cpp_lex_token, that
166 	 * has the usual jumps:
167 	 *
168 	 *  │1159e6c: ↓ jne    115aa32 <_cpp_lex_token@@Base+0xf92>
169 	 *
170 	 * I.e. jumps to a label inside that function (_cpp_lex_token), and
171 	 * those works, but also this kind:
172 	 *
173 	 *  │1159e8b: ↓ jne    c469be <cpp_named_operator2name@@Base+0xa72>
174 	 *
175 	 *  I.e. jumps to another function, outside _cpp_lex_token, which
176 	 *  are not being correctly handled generating as a side effect references
177 	 *  to ab->offset[] entries that are set to NULL, so to make this code
178 	 *  more robust, check that here.
179 	 *
180 	 *  A proper fix for will be put in place, looking at the function
181 	 *  name right after the '<' token and probably treating this like a
182 	 *  'call' instruction.
183 	 */
184 	target = notes->offsets[cursor->ops.target.offset];
185 	if (target == NULL) {
186 		ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
187 				    cursor->ops.target.offset);
188 		return;
189 	}
190 
191 	if (notes->options->hide_src_code) {
192 		from = cursor->al.idx_asm;
193 		to = target->idx_asm;
194 	} else {
195 		from = (u64)cursor->al.idx;
196 		to = (u64)target->idx;
197 	}
198 
199 	width = annotation__cycles_width(notes);
200 
201 	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
202 	__ui_browser__line_arrow(browser,
203 				 pcnt_width + 2 + notes->widths.addr + width,
204 				 from, to);
205 
206 	if (is_fused(ab, cursor)) {
207 		ui_browser__mark_fused(browser,
208 				       pcnt_width + 3 + notes->widths.addr + width,
209 				       from - 1,
210 				       to > from ? true : false);
211 	}
212 }
213 
214 static unsigned int annotate_browser__refresh(struct ui_browser *browser)
215 {
216 	struct annotation *notes = browser__annotation(browser);
217 	int ret = ui_browser__list_head_refresh(browser);
218 	int pcnt_width = annotation__pcnt_width(notes);
219 
220 	if (notes->options->jump_arrows)
221 		annotate_browser__draw_current_jump(browser);
222 
223 	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
224 	__ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1);
225 	return ret;
226 }
227 
228 static double disasm__cmp(struct annotation_line *a, struct annotation_line *b,
229 						  int percent_type)
230 {
231 	int i;
232 
233 	for (i = 0; i < a->data_nr; i++) {
234 		if (a->data[i].percent[percent_type] == b->data[i].percent[percent_type])
235 			continue;
236 		return a->data[i].percent[percent_type] -
237 			   b->data[i].percent[percent_type];
238 	}
239 	return 0;
240 }
241 
242 static void disasm_rb_tree__insert(struct annotate_browser *browser,
243 				struct annotation_line *al)
244 {
245 	struct rb_root *root = &browser->entries;
246 	struct rb_node **p = &root->rb_node;
247 	struct rb_node *parent = NULL;
248 	struct annotation_line *l;
249 
250 	while (*p != NULL) {
251 		parent = *p;
252 		l = rb_entry(parent, struct annotation_line, rb_node);
253 
254 		if (disasm__cmp(al, l, browser->opts->percent_type) < 0)
255 			p = &(*p)->rb_left;
256 		else
257 			p = &(*p)->rb_right;
258 	}
259 	rb_link_node(&al->rb_node, parent, p);
260 	rb_insert_color(&al->rb_node, root);
261 }
262 
263 static void annotate_browser__set_top(struct annotate_browser *browser,
264 				      struct annotation_line *pos, u32 idx)
265 {
266 	struct annotation *notes = browser__annotation(&browser->b);
267 	unsigned back;
268 
269 	ui_browser__refresh_dimensions(&browser->b);
270 	back = browser->b.height / 2;
271 	browser->b.top_idx = browser->b.index = idx;
272 
273 	while (browser->b.top_idx != 0 && back != 0) {
274 		pos = list_entry(pos->node.prev, struct annotation_line, node);
275 
276 		if (annotation_line__filter(pos, notes))
277 			continue;
278 
279 		--browser->b.top_idx;
280 		--back;
281 	}
282 
283 	browser->b.top = pos;
284 	browser->b.navkeypressed = true;
285 }
286 
287 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
288 					 struct rb_node *nd)
289 {
290 	struct annotation *notes = browser__annotation(&browser->b);
291 	struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
292 	u32 idx = pos->idx;
293 
294 	if (notes->options->hide_src_code)
295 		idx = pos->idx_asm;
296 	annotate_browser__set_top(browser, pos, idx);
297 	browser->curr_hot = nd;
298 }
299 
300 static void annotate_browser__calc_percent(struct annotate_browser *browser,
301 					   struct perf_evsel *evsel)
302 {
303 	struct map_symbol *ms = browser->b.priv;
304 	struct symbol *sym = ms->sym;
305 	struct annotation *notes = symbol__annotation(sym);
306 	struct disasm_line *pos;
307 
308 	browser->entries = RB_ROOT;
309 
310 	pthread_mutex_lock(&notes->lock);
311 
312 	symbol__calc_percent(sym, evsel);
313 
314 	list_for_each_entry(pos, &notes->src->source, al.node) {
315 		double max_percent = 0.0;
316 		int i;
317 
318 		if (pos->al.offset == -1) {
319 			RB_CLEAR_NODE(&pos->al.rb_node);
320 			continue;
321 		}
322 
323 		for (i = 0; i < pos->al.data_nr; i++) {
324 			double percent;
325 
326 			percent = annotation_data__percent(&pos->al.data[i],
327 							   browser->opts->percent_type);
328 
329 			if (max_percent < percent)
330 				max_percent = percent;
331 		}
332 
333 		if (max_percent < 0.01 && pos->al.ipc == 0) {
334 			RB_CLEAR_NODE(&pos->al.rb_node);
335 			continue;
336 		}
337 		disasm_rb_tree__insert(browser, &pos->al);
338 	}
339 	pthread_mutex_unlock(&notes->lock);
340 
341 	browser->curr_hot = rb_last(&browser->entries);
342 }
343 
344 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
345 {
346 	struct annotation *notes = browser__annotation(&browser->b);
347 	struct annotation_line *al;
348 	off_t offset = browser->b.index - browser->b.top_idx;
349 
350 	browser->b.seek(&browser->b, offset, SEEK_CUR);
351 	al = list_entry(browser->b.top, struct annotation_line, node);
352 
353 	if (notes->options->hide_src_code) {
354 		if (al->idx_asm < offset)
355 			offset = al->idx;
356 
357 		browser->b.nr_entries = notes->nr_entries;
358 		notes->options->hide_src_code = false;
359 		browser->b.seek(&browser->b, -offset, SEEK_CUR);
360 		browser->b.top_idx = al->idx - offset;
361 		browser->b.index = al->idx;
362 	} else {
363 		if (al->idx_asm < 0) {
364 			ui_helpline__puts("Only available for assembly lines.");
365 			browser->b.seek(&browser->b, -offset, SEEK_CUR);
366 			return false;
367 		}
368 
369 		if (al->idx_asm < offset)
370 			offset = al->idx_asm;
371 
372 		browser->b.nr_entries = notes->nr_asm_entries;
373 		notes->options->hide_src_code = true;
374 		browser->b.seek(&browser->b, -offset, SEEK_CUR);
375 		browser->b.top_idx = al->idx_asm - offset;
376 		browser->b.index = al->idx_asm;
377 	}
378 
379 	return true;
380 }
381 
382 static void ui_browser__init_asm_mode(struct ui_browser *browser)
383 {
384 	struct annotation *notes = browser__annotation(browser);
385 	ui_browser__reset_index(browser);
386 	browser->nr_entries = notes->nr_asm_entries;
387 }
388 
389 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
390 
391 static int sym_title(struct symbol *sym, struct map *map, char *title,
392 		     size_t sz, int percent_type)
393 {
394 	return snprintf(title, sz, "%s  %s [Percent: %s]", sym->name, map->dso->long_name,
395 			percent_type_str(percent_type));
396 }
397 
398 /*
399  * This can be called from external jumps, i.e. jumps from one functon
400  * to another, like from the kernel's entry_SYSCALL_64 function to the
401  * swapgs_restore_regs_and_return_to_usermode() function.
402  *
403  * So all we check here is that dl->ops.target.sym is set, if it is, just
404  * go to that function and when exiting from its disassembly, come back
405  * to the calling function.
406  */
407 static bool annotate_browser__callq(struct annotate_browser *browser,
408 				    struct perf_evsel *evsel,
409 				    struct hist_browser_timer *hbt)
410 {
411 	struct map_symbol *ms = browser->b.priv;
412 	struct disasm_line *dl = disasm_line(browser->selection);
413 	struct annotation *notes;
414 	char title[SYM_TITLE_MAX_SIZE];
415 
416 	if (!dl->ops.target.sym) {
417 		ui_helpline__puts("The called function was not found.");
418 		return true;
419 	}
420 
421 	notes = symbol__annotation(dl->ops.target.sym);
422 	pthread_mutex_lock(&notes->lock);
423 
424 	if (!symbol__hists(dl->ops.target.sym, evsel->evlist->nr_entries)) {
425 		pthread_mutex_unlock(&notes->lock);
426 		ui__warning("Not enough memory for annotating '%s' symbol!\n",
427 			    dl->ops.target.sym->name);
428 		return true;
429 	}
430 
431 	pthread_mutex_unlock(&notes->lock);
432 	symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt, browser->opts);
433 	sym_title(ms->sym, ms->map, title, sizeof(title), browser->opts->percent_type);
434 	ui_browser__show_title(&browser->b, title);
435 	return true;
436 }
437 
438 static
439 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
440 					  s64 offset, s64 *idx)
441 {
442 	struct annotation *notes = browser__annotation(&browser->b);
443 	struct disasm_line *pos;
444 
445 	*idx = 0;
446 	list_for_each_entry(pos, &notes->src->source, al.node) {
447 		if (pos->al.offset == offset)
448 			return pos;
449 		if (!annotation_line__filter(&pos->al, notes))
450 			++*idx;
451 	}
452 
453 	return NULL;
454 }
455 
456 static bool annotate_browser__jump(struct annotate_browser *browser,
457 				   struct perf_evsel *evsel,
458 				   struct hist_browser_timer *hbt)
459 {
460 	struct disasm_line *dl = disasm_line(browser->selection);
461 	u64 offset;
462 	s64 idx;
463 
464 	if (!ins__is_jump(&dl->ins))
465 		return false;
466 
467 	if (dl->ops.target.outside) {
468 		annotate_browser__callq(browser, evsel, hbt);
469 		return true;
470 	}
471 
472 	offset = dl->ops.target.offset;
473 	dl = annotate_browser__find_offset(browser, offset, &idx);
474 	if (dl == NULL) {
475 		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
476 		return true;
477 	}
478 
479 	annotate_browser__set_top(browser, &dl->al, idx);
480 
481 	return true;
482 }
483 
484 static
485 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
486 					  char *s, s64 *idx)
487 {
488 	struct annotation *notes = browser__annotation(&browser->b);
489 	struct annotation_line *al = browser->selection;
490 
491 	*idx = browser->b.index;
492 	list_for_each_entry_continue(al, &notes->src->source, node) {
493 		if (annotation_line__filter(al, notes))
494 			continue;
495 
496 		++*idx;
497 
498 		if (al->line && strstr(al->line, s) != NULL)
499 			return al;
500 	}
501 
502 	return NULL;
503 }
504 
505 static bool __annotate_browser__search(struct annotate_browser *browser)
506 {
507 	struct annotation_line *al;
508 	s64 idx;
509 
510 	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
511 	if (al == NULL) {
512 		ui_helpline__puts("String not found!");
513 		return false;
514 	}
515 
516 	annotate_browser__set_top(browser, al, idx);
517 	browser->searching_backwards = false;
518 	return true;
519 }
520 
521 static
522 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
523 						  char *s, s64 *idx)
524 {
525 	struct annotation *notes = browser__annotation(&browser->b);
526 	struct annotation_line *al = browser->selection;
527 
528 	*idx = browser->b.index;
529 	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
530 		if (annotation_line__filter(al, notes))
531 			continue;
532 
533 		--*idx;
534 
535 		if (al->line && strstr(al->line, s) != NULL)
536 			return al;
537 	}
538 
539 	return NULL;
540 }
541 
542 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
543 {
544 	struct annotation_line *al;
545 	s64 idx;
546 
547 	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
548 	if (al == NULL) {
549 		ui_helpline__puts("String not found!");
550 		return false;
551 	}
552 
553 	annotate_browser__set_top(browser, al, idx);
554 	browser->searching_backwards = true;
555 	return true;
556 }
557 
558 static bool annotate_browser__search_window(struct annotate_browser *browser,
559 					    int delay_secs)
560 {
561 	if (ui_browser__input_window("Search", "String: ", browser->search_bf,
562 				     "ENTER: OK, ESC: Cancel",
563 				     delay_secs * 2) != K_ENTER ||
564 	    !*browser->search_bf)
565 		return false;
566 
567 	return true;
568 }
569 
570 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
571 {
572 	if (annotate_browser__search_window(browser, delay_secs))
573 		return __annotate_browser__search(browser);
574 
575 	return false;
576 }
577 
578 static bool annotate_browser__continue_search(struct annotate_browser *browser,
579 					      int delay_secs)
580 {
581 	if (!*browser->search_bf)
582 		return annotate_browser__search(browser, delay_secs);
583 
584 	return __annotate_browser__search(browser);
585 }
586 
587 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
588 					   int delay_secs)
589 {
590 	if (annotate_browser__search_window(browser, delay_secs))
591 		return __annotate_browser__search_reverse(browser);
592 
593 	return false;
594 }
595 
596 static
597 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
598 					       int delay_secs)
599 {
600 	if (!*browser->search_bf)
601 		return annotate_browser__search_reverse(browser, delay_secs);
602 
603 	return __annotate_browser__search_reverse(browser);
604 }
605 
606 static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
607 {
608 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
609 	struct map_symbol *ms = browser->priv;
610 	struct symbol *sym = ms->sym;
611 	char symbol_dso[SYM_TITLE_MAX_SIZE];
612 
613 	if (ui_browser__show(browser, title, help) < 0)
614 		return -1;
615 
616 	sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), ab->opts->percent_type);
617 
618 	ui_browser__gotorc_title(browser, 0, 0);
619 	ui_browser__set_color(browser, HE_COLORSET_ROOT);
620 	ui_browser__write_nstring(browser, symbol_dso, browser->width + 1);
621 	return 0;
622 }
623 
624 static void
625 switch_percent_type(struct annotation_options *opts, bool base)
626 {
627 	switch (opts->percent_type) {
628 	case PERCENT_HITS_LOCAL:
629 		if (base)
630 			opts->percent_type = PERCENT_PERIOD_LOCAL;
631 		else
632 			opts->percent_type = PERCENT_HITS_GLOBAL;
633 		break;
634 	case PERCENT_HITS_GLOBAL:
635 		if (base)
636 			opts->percent_type = PERCENT_PERIOD_GLOBAL;
637 		else
638 			opts->percent_type = PERCENT_HITS_LOCAL;
639 		break;
640 	case PERCENT_PERIOD_LOCAL:
641 		if (base)
642 			opts->percent_type = PERCENT_HITS_LOCAL;
643 		else
644 			opts->percent_type = PERCENT_PERIOD_GLOBAL;
645 		break;
646 	case PERCENT_PERIOD_GLOBAL:
647 		if (base)
648 			opts->percent_type = PERCENT_HITS_GLOBAL;
649 		else
650 			opts->percent_type = PERCENT_PERIOD_LOCAL;
651 		break;
652 	default:
653 		WARN_ON(1);
654 	}
655 }
656 
657 static int annotate_browser__run(struct annotate_browser *browser,
658 				 struct perf_evsel *evsel,
659 				 struct hist_browser_timer *hbt)
660 {
661 	struct rb_node *nd = NULL;
662 	struct hists *hists = evsel__hists(evsel);
663 	struct map_symbol *ms = browser->b.priv;
664 	struct symbol *sym = ms->sym;
665 	struct annotation *notes = symbol__annotation(ms->sym);
666 	const char *help = "Press 'h' for help on key bindings";
667 	int delay_secs = hbt ? hbt->refresh : 0;
668 	char title[256];
669 	int key;
670 
671 	hists__scnprintf_title(hists, title, sizeof(title));
672 	if (annotate_browser__show(&browser->b, title, help) < 0)
673 		return -1;
674 
675 	annotate_browser__calc_percent(browser, evsel);
676 
677 	if (browser->curr_hot) {
678 		annotate_browser__set_rb_top(browser, browser->curr_hot);
679 		browser->b.navkeypressed = false;
680 	}
681 
682 	nd = browser->curr_hot;
683 
684 	while (1) {
685 		key = ui_browser__run(&browser->b, delay_secs);
686 
687 		if (delay_secs != 0) {
688 			annotate_browser__calc_percent(browser, evsel);
689 			/*
690 			 * Current line focus got out of the list of most active
691 			 * lines, NULL it so that if TAB|UNTAB is pressed, we
692 			 * move to curr_hot (current hottest line).
693 			 */
694 			if (nd != NULL && RB_EMPTY_NODE(nd))
695 				nd = NULL;
696 		}
697 
698 		switch (key) {
699 		case K_TIMER:
700 			if (hbt)
701 				hbt->timer(hbt->arg);
702 
703 			if (delay_secs != 0) {
704 				symbol__annotate_decay_histogram(sym, evsel->idx);
705 				hists__scnprintf_title(hists, title, sizeof(title));
706 				annotate_browser__show(&browser->b, title, help);
707 			}
708 			continue;
709 		case K_TAB:
710 			if (nd != NULL) {
711 				nd = rb_prev(nd);
712 				if (nd == NULL)
713 					nd = rb_last(&browser->entries);
714 			} else
715 				nd = browser->curr_hot;
716 			break;
717 		case K_UNTAB:
718 			if (nd != NULL) {
719 				nd = rb_next(nd);
720 				if (nd == NULL)
721 					nd = rb_first(&browser->entries);
722 			} else
723 				nd = browser->curr_hot;
724 			break;
725 		case K_F1:
726 		case 'h':
727 			ui_browser__help_window(&browser->b,
728 		"UP/DOWN/PGUP\n"
729 		"PGDN/SPACE    Navigate\n"
730 		"q/ESC/CTRL+C  Exit\n\n"
731 		"ENTER         Go to target\n"
732 		"ESC           Exit\n"
733 		"H             Go to hottest instruction\n"
734 		"TAB/shift+TAB Cycle thru hottest instructions\n"
735 		"j             Toggle showing jump to target arrows\n"
736 		"J             Toggle showing number of jump sources on targets\n"
737 		"n             Search next string\n"
738 		"o             Toggle disassembler output/simplified view\n"
739 		"O             Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
740 		"s             Toggle source code view\n"
741 		"t             Circulate percent, total period, samples view\n"
742 		"c             Show min/max cycle\n"
743 		"/             Search string\n"
744 		"k             Toggle line numbers\n"
745 		"P             Print to [symbol_name].annotation file.\n"
746 		"r             Run available scripts\n"
747 		"p             Toggle percent type [local/global]\n"
748 		"b             Toggle percent base [period/hits]\n"
749 		"?             Search string backwards\n");
750 			continue;
751 		case 'r':
752 			{
753 				script_browse(NULL, NULL);
754 				continue;
755 			}
756 		case 'k':
757 			notes->options->show_linenr = !notes->options->show_linenr;
758 			break;
759 		case 'H':
760 			nd = browser->curr_hot;
761 			break;
762 		case 's':
763 			if (annotate_browser__toggle_source(browser))
764 				ui_helpline__puts(help);
765 			continue;
766 		case 'o':
767 			notes->options->use_offset = !notes->options->use_offset;
768 			annotation__update_column_widths(notes);
769 			continue;
770 		case 'O':
771 			if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
772 				notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
773 			continue;
774 		case 'j':
775 			notes->options->jump_arrows = !notes->options->jump_arrows;
776 			continue;
777 		case 'J':
778 			notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
779 			annotation__update_column_widths(notes);
780 			continue;
781 		case '/':
782 			if (annotate_browser__search(browser, delay_secs)) {
783 show_help:
784 				ui_helpline__puts(help);
785 			}
786 			continue;
787 		case 'n':
788 			if (browser->searching_backwards ?
789 			    annotate_browser__continue_search_reverse(browser, delay_secs) :
790 			    annotate_browser__continue_search(browser, delay_secs))
791 				goto show_help;
792 			continue;
793 		case '?':
794 			if (annotate_browser__search_reverse(browser, delay_secs))
795 				goto show_help;
796 			continue;
797 		case 'D': {
798 			static int seq;
799 			ui_helpline__pop();
800 			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
801 					   seq++, browser->b.nr_entries,
802 					   browser->b.height,
803 					   browser->b.index,
804 					   browser->b.top_idx,
805 					   notes->nr_asm_entries);
806 		}
807 			continue;
808 		case K_ENTER:
809 		case K_RIGHT:
810 		{
811 			struct disasm_line *dl = disasm_line(browser->selection);
812 
813 			if (browser->selection == NULL)
814 				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
815 			else if (browser->selection->offset == -1)
816 				ui_helpline__puts("Actions are only available for assembly lines.");
817 			else if (!dl->ins.ops)
818 				goto show_sup_ins;
819 			else if (ins__is_ret(&dl->ins))
820 				goto out;
821 			else if (!(annotate_browser__jump(browser, evsel, hbt) ||
822 				     annotate_browser__callq(browser, evsel, hbt))) {
823 show_sup_ins:
824 				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
825 			}
826 			continue;
827 		}
828 		case 'P':
829 			map_symbol__annotation_dump(ms, evsel, browser->opts);
830 			continue;
831 		case 't':
832 			if (notes->options->show_total_period) {
833 				notes->options->show_total_period = false;
834 				notes->options->show_nr_samples = true;
835 			} else if (notes->options->show_nr_samples)
836 				notes->options->show_nr_samples = false;
837 			else
838 				notes->options->show_total_period = true;
839 			annotation__update_column_widths(notes);
840 			continue;
841 		case 'c':
842 			if (notes->options->show_minmax_cycle)
843 				notes->options->show_minmax_cycle = false;
844 			else
845 				notes->options->show_minmax_cycle = true;
846 			annotation__update_column_widths(notes);
847 			continue;
848 		case 'p':
849 		case 'b':
850 			switch_percent_type(browser->opts, key == 'b');
851 			hists__scnprintf_title(hists, title, sizeof(title));
852 			annotate_browser__show(&browser->b, title, help);
853 			continue;
854 		case K_LEFT:
855 		case K_ESC:
856 		case 'q':
857 		case CTRL('c'):
858 			goto out;
859 		default:
860 			continue;
861 		}
862 
863 		if (nd != NULL)
864 			annotate_browser__set_rb_top(browser, nd);
865 	}
866 out:
867 	ui_browser__hide(&browser->b);
868 	return key;
869 }
870 
871 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
872 			     struct hist_browser_timer *hbt,
873 			     struct annotation_options *opts)
874 {
875 	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt, opts);
876 }
877 
878 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
879 			     struct hist_browser_timer *hbt,
880 			     struct annotation_options *opts)
881 {
882 	/* reset abort key so that it can get Ctrl-C as a key */
883 	SLang_reset_tty();
884 	SLang_init_tty(0, 0, 0);
885 
886 	return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts);
887 }
888 
889 int symbol__tui_annotate(struct symbol *sym, struct map *map,
890 			 struct perf_evsel *evsel,
891 			 struct hist_browser_timer *hbt,
892 			 struct annotation_options *opts)
893 {
894 	struct annotation *notes = symbol__annotation(sym);
895 	struct map_symbol ms = {
896 		.map = map,
897 		.sym = sym,
898 	};
899 	struct annotate_browser browser = {
900 		.b = {
901 			.refresh = annotate_browser__refresh,
902 			.seek	 = ui_browser__list_head_seek,
903 			.write	 = annotate_browser__write,
904 			.filter  = disasm_line__filter,
905 			.extra_title_lines = 1, /* for hists__scnprintf_title() */
906 			.priv	 = &ms,
907 			.use_navkeypressed = true,
908 		},
909 		.opts = opts,
910 	};
911 	int ret = -1, err;
912 
913 	if (sym == NULL)
914 		return -1;
915 
916 	if (map->dso->annotate_warned)
917 		return -1;
918 
919 	err = symbol__annotate2(sym, map, evsel, opts, &browser.arch);
920 	if (err) {
921 		char msg[BUFSIZ];
922 		symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
923 		ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
924 		goto out_free_offsets;
925 	}
926 
927 	ui_helpline__push("Press ESC to exit");
928 
929 	browser.b.width = notes->max_line_len;
930 	browser.b.nr_entries = notes->nr_entries;
931 	browser.b.entries = &notes->src->source,
932 	browser.b.width += 18; /* Percentage */
933 
934 	if (notes->options->hide_src_code)
935 		ui_browser__init_asm_mode(&browser.b);
936 
937 	ret = annotate_browser__run(&browser, evsel, hbt);
938 
939 	annotated_source__purge(notes->src);
940 
941 out_free_offsets:
942 	zfree(&notes->offsets);
943 	return ret;
944 }
945