xref: /linux/tools/perf/ui/browsers/annotate.c (revision 2975489458c59ce2e348b1b3aef5d8d2acb5cc8d)
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.map = ms->map;
434 	target_ms.sym = dl->ops.target.sym;
435 	pthread_mutex_unlock(&notes->lock);
436 	symbol__tui_annotate(&target_ms, evsel, hbt, browser->opts);
437 	sym_title(ms->sym, ms->map, title, sizeof(title), browser->opts->percent_type);
438 	ui_browser__show_title(&browser->b, title);
439 	return true;
440 }
441 
442 static
443 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
444 					  s64 offset, s64 *idx)
445 {
446 	struct annotation *notes = browser__annotation(&browser->b);
447 	struct disasm_line *pos;
448 
449 	*idx = 0;
450 	list_for_each_entry(pos, &notes->src->source, al.node) {
451 		if (pos->al.offset == offset)
452 			return pos;
453 		if (!annotation_line__filter(&pos->al, notes))
454 			++*idx;
455 	}
456 
457 	return NULL;
458 }
459 
460 static bool annotate_browser__jump(struct annotate_browser *browser,
461 				   struct evsel *evsel,
462 				   struct hist_browser_timer *hbt)
463 {
464 	struct disasm_line *dl = disasm_line(browser->selection);
465 	u64 offset;
466 	s64 idx;
467 
468 	if (!ins__is_jump(&dl->ins))
469 		return false;
470 
471 	if (dl->ops.target.outside) {
472 		annotate_browser__callq(browser, evsel, hbt);
473 		return true;
474 	}
475 
476 	offset = dl->ops.target.offset;
477 	dl = annotate_browser__find_offset(browser, offset, &idx);
478 	if (dl == NULL) {
479 		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
480 		return true;
481 	}
482 
483 	annotate_browser__set_top(browser, &dl->al, idx);
484 
485 	return true;
486 }
487 
488 static
489 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
490 					  char *s, s64 *idx)
491 {
492 	struct annotation *notes = browser__annotation(&browser->b);
493 	struct annotation_line *al = browser->selection;
494 
495 	*idx = browser->b.index;
496 	list_for_each_entry_continue(al, &notes->src->source, node) {
497 		if (annotation_line__filter(al, notes))
498 			continue;
499 
500 		++*idx;
501 
502 		if (al->line && strstr(al->line, s) != NULL)
503 			return al;
504 	}
505 
506 	return NULL;
507 }
508 
509 static bool __annotate_browser__search(struct annotate_browser *browser)
510 {
511 	struct annotation_line *al;
512 	s64 idx;
513 
514 	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
515 	if (al == NULL) {
516 		ui_helpline__puts("String not found!");
517 		return false;
518 	}
519 
520 	annotate_browser__set_top(browser, al, idx);
521 	browser->searching_backwards = false;
522 	return true;
523 }
524 
525 static
526 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
527 						  char *s, s64 *idx)
528 {
529 	struct annotation *notes = browser__annotation(&browser->b);
530 	struct annotation_line *al = browser->selection;
531 
532 	*idx = browser->b.index;
533 	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
534 		if (annotation_line__filter(al, notes))
535 			continue;
536 
537 		--*idx;
538 
539 		if (al->line && strstr(al->line, s) != NULL)
540 			return al;
541 	}
542 
543 	return NULL;
544 }
545 
546 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
547 {
548 	struct annotation_line *al;
549 	s64 idx;
550 
551 	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
552 	if (al == NULL) {
553 		ui_helpline__puts("String not found!");
554 		return false;
555 	}
556 
557 	annotate_browser__set_top(browser, al, idx);
558 	browser->searching_backwards = true;
559 	return true;
560 }
561 
562 static bool annotate_browser__search_window(struct annotate_browser *browser,
563 					    int delay_secs)
564 {
565 	if (ui_browser__input_window("Search", "String: ", browser->search_bf,
566 				     "ENTER: OK, ESC: Cancel",
567 				     delay_secs * 2) != K_ENTER ||
568 	    !*browser->search_bf)
569 		return false;
570 
571 	return true;
572 }
573 
574 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
575 {
576 	if (annotate_browser__search_window(browser, delay_secs))
577 		return __annotate_browser__search(browser);
578 
579 	return false;
580 }
581 
582 static bool annotate_browser__continue_search(struct annotate_browser *browser,
583 					      int delay_secs)
584 {
585 	if (!*browser->search_bf)
586 		return annotate_browser__search(browser, delay_secs);
587 
588 	return __annotate_browser__search(browser);
589 }
590 
591 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
592 					   int delay_secs)
593 {
594 	if (annotate_browser__search_window(browser, delay_secs))
595 		return __annotate_browser__search_reverse(browser);
596 
597 	return false;
598 }
599 
600 static
601 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
602 					       int delay_secs)
603 {
604 	if (!*browser->search_bf)
605 		return annotate_browser__search_reverse(browser, delay_secs);
606 
607 	return __annotate_browser__search_reverse(browser);
608 }
609 
610 static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
611 {
612 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
613 	struct map_symbol *ms = browser->priv;
614 	struct symbol *sym = ms->sym;
615 	char symbol_dso[SYM_TITLE_MAX_SIZE];
616 
617 	if (ui_browser__show(browser, title, help) < 0)
618 		return -1;
619 
620 	sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), ab->opts->percent_type);
621 
622 	ui_browser__gotorc_title(browser, 0, 0);
623 	ui_browser__set_color(browser, HE_COLORSET_ROOT);
624 	ui_browser__write_nstring(browser, symbol_dso, browser->width + 1);
625 	return 0;
626 }
627 
628 static void
629 switch_percent_type(struct annotation_options *opts, bool base)
630 {
631 	switch (opts->percent_type) {
632 	case PERCENT_HITS_LOCAL:
633 		if (base)
634 			opts->percent_type = PERCENT_PERIOD_LOCAL;
635 		else
636 			opts->percent_type = PERCENT_HITS_GLOBAL;
637 		break;
638 	case PERCENT_HITS_GLOBAL:
639 		if (base)
640 			opts->percent_type = PERCENT_PERIOD_GLOBAL;
641 		else
642 			opts->percent_type = PERCENT_HITS_LOCAL;
643 		break;
644 	case PERCENT_PERIOD_LOCAL:
645 		if (base)
646 			opts->percent_type = PERCENT_HITS_LOCAL;
647 		else
648 			opts->percent_type = PERCENT_PERIOD_GLOBAL;
649 		break;
650 	case PERCENT_PERIOD_GLOBAL:
651 		if (base)
652 			opts->percent_type = PERCENT_HITS_GLOBAL;
653 		else
654 			opts->percent_type = PERCENT_PERIOD_LOCAL;
655 		break;
656 	default:
657 		WARN_ON(1);
658 	}
659 }
660 
661 static int annotate_browser__run(struct annotate_browser *browser,
662 				 struct evsel *evsel,
663 				 struct hist_browser_timer *hbt)
664 {
665 	struct rb_node *nd = NULL;
666 	struct hists *hists = evsel__hists(evsel);
667 	struct map_symbol *ms = browser->b.priv;
668 	struct symbol *sym = ms->sym;
669 	struct annotation *notes = symbol__annotation(ms->sym);
670 	const char *help = "Press 'h' for help on key bindings";
671 	int delay_secs = hbt ? hbt->refresh : 0;
672 	char title[256];
673 	int key;
674 
675 	hists__scnprintf_title(hists, title, sizeof(title));
676 	if (annotate_browser__show(&browser->b, title, help) < 0)
677 		return -1;
678 
679 	annotate_browser__calc_percent(browser, evsel);
680 
681 	if (browser->curr_hot) {
682 		annotate_browser__set_rb_top(browser, browser->curr_hot);
683 		browser->b.navkeypressed = false;
684 	}
685 
686 	nd = browser->curr_hot;
687 
688 	while (1) {
689 		key = ui_browser__run(&browser->b, delay_secs);
690 
691 		if (delay_secs != 0) {
692 			annotate_browser__calc_percent(browser, evsel);
693 			/*
694 			 * Current line focus got out of the list of most active
695 			 * lines, NULL it so that if TAB|UNTAB is pressed, we
696 			 * move to curr_hot (current hottest line).
697 			 */
698 			if (nd != NULL && RB_EMPTY_NODE(nd))
699 				nd = NULL;
700 		}
701 
702 		switch (key) {
703 		case K_TIMER:
704 			if (hbt)
705 				hbt->timer(hbt->arg);
706 
707 			if (delay_secs != 0) {
708 				symbol__annotate_decay_histogram(sym, evsel->idx);
709 				hists__scnprintf_title(hists, title, sizeof(title));
710 				annotate_browser__show(&browser->b, title, help);
711 			}
712 			continue;
713 		case K_TAB:
714 			if (nd != NULL) {
715 				nd = rb_prev(nd);
716 				if (nd == NULL)
717 					nd = rb_last(&browser->entries);
718 			} else
719 				nd = browser->curr_hot;
720 			break;
721 		case K_UNTAB:
722 			if (nd != NULL) {
723 				nd = rb_next(nd);
724 				if (nd == NULL)
725 					nd = rb_first(&browser->entries);
726 			} else
727 				nd = browser->curr_hot;
728 			break;
729 		case K_F1:
730 		case 'h':
731 			ui_browser__help_window(&browser->b,
732 		"UP/DOWN/PGUP\n"
733 		"PGDN/SPACE    Navigate\n"
734 		"q/ESC/CTRL+C  Exit\n\n"
735 		"ENTER         Go to target\n"
736 		"ESC           Exit\n"
737 		"H             Go to hottest instruction\n"
738 		"TAB/shift+TAB Cycle thru hottest instructions\n"
739 		"j             Toggle showing jump to target arrows\n"
740 		"J             Toggle showing number of jump sources on targets\n"
741 		"n             Search next string\n"
742 		"o             Toggle disassembler output/simplified view\n"
743 		"O             Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
744 		"s             Toggle source code view\n"
745 		"t             Circulate percent, total period, samples view\n"
746 		"c             Show min/max cycle\n"
747 		"/             Search string\n"
748 		"k             Toggle line numbers\n"
749 		"P             Print to [symbol_name].annotation file.\n"
750 		"r             Run available scripts\n"
751 		"p             Toggle percent type [local/global]\n"
752 		"b             Toggle percent base [period/hits]\n"
753 		"?             Search string backwards\n");
754 			continue;
755 		case 'r':
756 			{
757 				script_browse(NULL, NULL);
758 				continue;
759 			}
760 		case 'k':
761 			notes->options->show_linenr = !notes->options->show_linenr;
762 			break;
763 		case 'H':
764 			nd = browser->curr_hot;
765 			break;
766 		case 's':
767 			if (annotate_browser__toggle_source(browser))
768 				ui_helpline__puts(help);
769 			continue;
770 		case 'o':
771 			notes->options->use_offset = !notes->options->use_offset;
772 			annotation__update_column_widths(notes);
773 			continue;
774 		case 'O':
775 			if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
776 				notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
777 			continue;
778 		case 'j':
779 			notes->options->jump_arrows = !notes->options->jump_arrows;
780 			continue;
781 		case 'J':
782 			notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
783 			annotation__update_column_widths(notes);
784 			continue;
785 		case '/':
786 			if (annotate_browser__search(browser, delay_secs)) {
787 show_help:
788 				ui_helpline__puts(help);
789 			}
790 			continue;
791 		case 'n':
792 			if (browser->searching_backwards ?
793 			    annotate_browser__continue_search_reverse(browser, delay_secs) :
794 			    annotate_browser__continue_search(browser, delay_secs))
795 				goto show_help;
796 			continue;
797 		case '?':
798 			if (annotate_browser__search_reverse(browser, delay_secs))
799 				goto show_help;
800 			continue;
801 		case 'D': {
802 			static int seq;
803 			ui_helpline__pop();
804 			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
805 					   seq++, browser->b.nr_entries,
806 					   browser->b.height,
807 					   browser->b.index,
808 					   browser->b.top_idx,
809 					   notes->nr_asm_entries);
810 		}
811 			continue;
812 		case K_ENTER:
813 		case K_RIGHT:
814 		{
815 			struct disasm_line *dl = disasm_line(browser->selection);
816 
817 			if (browser->selection == NULL)
818 				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
819 			else if (browser->selection->offset == -1)
820 				ui_helpline__puts("Actions are only available for assembly lines.");
821 			else if (!dl->ins.ops)
822 				goto show_sup_ins;
823 			else if (ins__is_ret(&dl->ins))
824 				goto out;
825 			else if (!(annotate_browser__jump(browser, evsel, hbt) ||
826 				     annotate_browser__callq(browser, evsel, hbt))) {
827 show_sup_ins:
828 				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
829 			}
830 			continue;
831 		}
832 		case 'P':
833 			map_symbol__annotation_dump(ms, evsel, browser->opts);
834 			continue;
835 		case 't':
836 			if (notes->options->show_total_period) {
837 				notes->options->show_total_period = false;
838 				notes->options->show_nr_samples = true;
839 			} else if (notes->options->show_nr_samples)
840 				notes->options->show_nr_samples = false;
841 			else
842 				notes->options->show_total_period = true;
843 			annotation__update_column_widths(notes);
844 			continue;
845 		case 'c':
846 			if (notes->options->show_minmax_cycle)
847 				notes->options->show_minmax_cycle = false;
848 			else
849 				notes->options->show_minmax_cycle = true;
850 			annotation__update_column_widths(notes);
851 			continue;
852 		case 'p':
853 		case 'b':
854 			switch_percent_type(browser->opts, key == 'b');
855 			hists__scnprintf_title(hists, title, sizeof(title));
856 			annotate_browser__show(&browser->b, title, help);
857 			continue;
858 		case K_LEFT:
859 		case K_ESC:
860 		case 'q':
861 		case CTRL('c'):
862 			goto out;
863 		default:
864 			continue;
865 		}
866 
867 		if (nd != NULL)
868 			annotate_browser__set_rb_top(browser, nd);
869 	}
870 out:
871 	ui_browser__hide(&browser->b);
872 	return key;
873 }
874 
875 int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
876 			     struct hist_browser_timer *hbt,
877 			     struct annotation_options *opts)
878 {
879 	return symbol__tui_annotate(ms, evsel, hbt, opts);
880 }
881 
882 int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
883 			     struct hist_browser_timer *hbt,
884 			     struct annotation_options *opts)
885 {
886 	/* reset abort key so that it can get Ctrl-C as a key */
887 	SLang_reset_tty();
888 	SLang_init_tty(0, 0, 0);
889 
890 	return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts);
891 }
892 
893 int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
894 			 struct hist_browser_timer *hbt,
895 			 struct annotation_options *opts)
896 {
897 	struct symbol *sym = ms->sym;
898 	struct annotation *notes = symbol__annotation(sym);
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 (ms->map->dso->annotate_warned)
917 		return -1;
918 
919 	err = symbol__annotate2(ms, evsel, opts, &browser.arch);
920 	if (err) {
921 		char msg[BUFSIZ];
922 		symbol__strerror_disassemble(ms, 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