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