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