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