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