xref: /openbmc/linux/tools/perf/ui/browsers/annotate.c (revision 5ef12cb4a3a78ffb331c03a795a15eea4ae35155)
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->rows - 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__show(struct ui_browser *browser, char *title, const char *help)
596 {
597 	struct map_symbol *ms = browser->priv;
598 	struct symbol *sym = ms->sym;
599 	char symbol_dso[SYM_TITLE_MAX_SIZE];
600 
601 	if (ui_browser__show(browser, title, help) < 0)
602 		return -1;
603 
604 	sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso));
605 
606 	ui_browser__gotorc_title(browser, 0, 0);
607 	ui_browser__set_color(browser, HE_COLORSET_ROOT);
608 	ui_browser__write_nstring(browser, symbol_dso, browser->width + 1);
609 	return 0;
610 }
611 
612 static int annotate_browser__run(struct annotate_browser *browser,
613 				 struct perf_evsel *evsel,
614 				 struct hist_browser_timer *hbt)
615 {
616 	struct rb_node *nd = NULL;
617 	struct hists *hists = evsel__hists(evsel);
618 	struct map_symbol *ms = browser->b.priv;
619 	struct symbol *sym = ms->sym;
620 	struct annotation *notes = symbol__annotation(ms->sym);
621 	const char *help = "Press 'h' for help on key bindings";
622 	int delay_secs = hbt ? hbt->refresh : 0;
623 	char title[256];
624 	int key;
625 
626 	annotation__scnprintf_samples_period(notes, title, sizeof(title), evsel);
627 
628 	if (annotate_browser__show(&browser->b, title, help) < 0)
629 		return -1;
630 
631 	annotate_browser__calc_percent(browser, evsel);
632 
633 	if (browser->curr_hot) {
634 		annotate_browser__set_rb_top(browser, browser->curr_hot);
635 		browser->b.navkeypressed = false;
636 	}
637 
638 	nd = browser->curr_hot;
639 
640 	while (1) {
641 		key = ui_browser__run(&browser->b, delay_secs);
642 
643 		if (delay_secs != 0) {
644 			annotate_browser__calc_percent(browser, evsel);
645 			/*
646 			 * Current line focus got out of the list of most active
647 			 * lines, NULL it so that if TAB|UNTAB is pressed, we
648 			 * move to curr_hot (current hottest line).
649 			 */
650 			if (nd != NULL && RB_EMPTY_NODE(nd))
651 				nd = NULL;
652 		}
653 
654 		switch (key) {
655 		case K_TIMER:
656 			if (hbt)
657 				hbt->timer(hbt->arg);
658 
659 			if (delay_secs != 0) {
660 				symbol__annotate_decay_histogram(sym, evsel->idx);
661 				hists__scnprintf_title(hists, title, sizeof(title));
662 				annotate_browser__show(&browser->b, title, help);
663 			}
664 			continue;
665 		case K_TAB:
666 			if (nd != NULL) {
667 				nd = rb_prev(nd);
668 				if (nd == NULL)
669 					nd = rb_last(&browser->entries);
670 			} else
671 				nd = browser->curr_hot;
672 			break;
673 		case K_UNTAB:
674 			if (nd != NULL) {
675 				nd = rb_next(nd);
676 				if (nd == NULL)
677 					nd = rb_first(&browser->entries);
678 			} else
679 				nd = browser->curr_hot;
680 			break;
681 		case K_F1:
682 		case 'h':
683 			ui_browser__help_window(&browser->b,
684 		"UP/DOWN/PGUP\n"
685 		"PGDN/SPACE    Navigate\n"
686 		"q/ESC/CTRL+C  Exit\n\n"
687 		"ENTER         Go to target\n"
688 		"ESC           Exit\n"
689 		"H             Go to hottest instruction\n"
690 		"TAB/shift+TAB Cycle thru hottest instructions\n"
691 		"j             Toggle showing jump to target arrows\n"
692 		"J             Toggle showing number of jump sources on targets\n"
693 		"n             Search next string\n"
694 		"o             Toggle disassembler output/simplified view\n"
695 		"O             Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
696 		"s             Toggle source code view\n"
697 		"t             Circulate percent, total period, samples view\n"
698 		"/             Search string\n"
699 		"k             Toggle line numbers\n"
700 		"P             Print to [symbol_name].annotation file.\n"
701 		"r             Run available scripts\n"
702 		"?             Search string backwards\n");
703 			continue;
704 		case 'r':
705 			{
706 				script_browse(NULL);
707 				continue;
708 			}
709 		case 'k':
710 			notes->options->show_linenr = !notes->options->show_linenr;
711 			break;
712 		case 'H':
713 			nd = browser->curr_hot;
714 			break;
715 		case 's':
716 			if (annotate_browser__toggle_source(browser))
717 				ui_helpline__puts(help);
718 			continue;
719 		case 'o':
720 			notes->options->use_offset = !notes->options->use_offset;
721 			annotation__update_column_widths(notes);
722 			continue;
723 		case 'O':
724 			if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
725 				notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
726 			continue;
727 		case 'j':
728 			notes->options->jump_arrows = !notes->options->jump_arrows;
729 			continue;
730 		case 'J':
731 			notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
732 			annotation__update_column_widths(notes);
733 			continue;
734 		case '/':
735 			if (annotate_browser__search(browser, delay_secs)) {
736 show_help:
737 				ui_helpline__puts(help);
738 			}
739 			continue;
740 		case 'n':
741 			if (browser->searching_backwards ?
742 			    annotate_browser__continue_search_reverse(browser, delay_secs) :
743 			    annotate_browser__continue_search(browser, delay_secs))
744 				goto show_help;
745 			continue;
746 		case '?':
747 			if (annotate_browser__search_reverse(browser, delay_secs))
748 				goto show_help;
749 			continue;
750 		case 'D': {
751 			static int seq;
752 			ui_helpline__pop();
753 			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
754 					   seq++, browser->b.nr_entries,
755 					   browser->b.height,
756 					   browser->b.index,
757 					   browser->b.top_idx,
758 					   notes->nr_asm_entries);
759 		}
760 			continue;
761 		case K_ENTER:
762 		case K_RIGHT:
763 		{
764 			struct disasm_line *dl = disasm_line(browser->selection);
765 
766 			if (browser->selection == NULL)
767 				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
768 			else if (browser->selection->offset == -1)
769 				ui_helpline__puts("Actions are only available for assembly lines.");
770 			else if (!dl->ins.ops)
771 				goto show_sup_ins;
772 			else if (ins__is_ret(&dl->ins))
773 				goto out;
774 			else if (!(annotate_browser__jump(browser, evsel, hbt) ||
775 				     annotate_browser__callq(browser, evsel, hbt))) {
776 show_sup_ins:
777 				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
778 			}
779 			continue;
780 		}
781 		case 'P':
782 			map_symbol__annotation_dump(ms, evsel);
783 			continue;
784 		case 't':
785 			if (notes->options->show_total_period) {
786 				notes->options->show_total_period = false;
787 				notes->options->show_nr_samples = true;
788 			} else if (notes->options->show_nr_samples)
789 				notes->options->show_nr_samples = false;
790 			else
791 				notes->options->show_total_period = true;
792 			annotation__update_column_widths(notes);
793 			continue;
794 		case K_LEFT:
795 		case K_ESC:
796 		case 'q':
797 		case CTRL('c'):
798 			goto out;
799 		default:
800 			continue;
801 		}
802 
803 		if (nd != NULL)
804 			annotate_browser__set_rb_top(browser, nd);
805 	}
806 out:
807 	ui_browser__hide(&browser->b);
808 	return key;
809 }
810 
811 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
812 			     struct hist_browser_timer *hbt)
813 {
814 	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
815 }
816 
817 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
818 			     struct hist_browser_timer *hbt)
819 {
820 	/* reset abort key so that it can get Ctrl-C as a key */
821 	SLang_reset_tty();
822 	SLang_init_tty(0, 0, 0);
823 
824 	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
825 }
826 
827 int symbol__tui_annotate(struct symbol *sym, struct map *map,
828 			 struct perf_evsel *evsel,
829 			 struct hist_browser_timer *hbt)
830 {
831 	struct annotation *notes = symbol__annotation(sym);
832 	struct map_symbol ms = {
833 		.map = map,
834 		.sym = sym,
835 	};
836 	struct annotate_browser browser = {
837 		.b = {
838 			.refresh = annotate_browser__refresh,
839 			.seek	 = ui_browser__list_head_seek,
840 			.write	 = annotate_browser__write,
841 			.filter  = disasm_line__filter,
842 			.extra_title_lines = 1, /* for hists__scnprintf_title() */
843 			.priv	 = &ms,
844 			.use_navkeypressed = true,
845 		},
846 	};
847 	int ret = -1, err;
848 
849 	if (sym == NULL)
850 		return -1;
851 
852 	if (map->dso->annotate_warned)
853 		return -1;
854 
855 	err = symbol__annotate2(sym, map, evsel, &annotation__default_options, &browser.arch);
856 	if (err) {
857 		char msg[BUFSIZ];
858 		symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
859 		ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
860 		goto out_free_offsets;
861 	}
862 
863 	ui_helpline__push("Press ESC to exit");
864 
865 	browser.b.width = notes->max_line_len;
866 	browser.b.nr_entries = notes->nr_entries;
867 	browser.b.entries = &notes->src->source,
868 	browser.b.width += 18; /* Percentage */
869 
870 	if (notes->options->hide_src_code)
871 		ui_browser__init_asm_mode(&browser.b);
872 
873 	ret = annotate_browser__run(&browser, evsel, hbt);
874 
875 	annotated_source__purge(notes->src);
876 
877 out_free_offsets:
878 	zfree(&notes->offsets);
879 	return ret;
880 }
881