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