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