xref: /openbmc/linux/tools/perf/ui/browsers/annotate.c (revision a48c7709)
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 		"s             Toggle source code view\n"
696 		"t             Circulate percent, total period, samples view\n"
697 		"/             Search string\n"
698 		"k             Toggle line numbers\n"
699 		"P             Print to [symbol_name].annotation file.\n"
700 		"r             Run available scripts\n"
701 		"?             Search string backwards\n");
702 			continue;
703 		case 'r':
704 			{
705 				script_browse(NULL);
706 				continue;
707 			}
708 		case 'k':
709 			notes->options->show_linenr = !notes->options->show_linenr;
710 			break;
711 		case 'H':
712 			nd = browser->curr_hot;
713 			break;
714 		case 's':
715 			if (annotate_browser__toggle_source(browser))
716 				ui_helpline__puts(help);
717 			continue;
718 		case 'o':
719 			notes->options->use_offset = !notes->options->use_offset;
720 			annotation__update_column_widths(notes);
721 			continue;
722 		case 'j':
723 			notes->options->jump_arrows = !notes->options->jump_arrows;
724 			continue;
725 		case 'J':
726 			notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
727 			annotation__update_column_widths(notes);
728 			continue;
729 		case '/':
730 			if (annotate_browser__search(browser, delay_secs)) {
731 show_help:
732 				ui_helpline__puts(help);
733 			}
734 			continue;
735 		case 'n':
736 			if (browser->searching_backwards ?
737 			    annotate_browser__continue_search_reverse(browser, delay_secs) :
738 			    annotate_browser__continue_search(browser, delay_secs))
739 				goto show_help;
740 			continue;
741 		case '?':
742 			if (annotate_browser__search_reverse(browser, delay_secs))
743 				goto show_help;
744 			continue;
745 		case 'D': {
746 			static int seq;
747 			ui_helpline__pop();
748 			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
749 					   seq++, browser->b.nr_entries,
750 					   browser->b.height,
751 					   browser->b.index,
752 					   browser->b.top_idx,
753 					   notes->nr_asm_entries);
754 		}
755 			continue;
756 		case K_ENTER:
757 		case K_RIGHT:
758 		{
759 			struct disasm_line *dl = disasm_line(browser->selection);
760 
761 			if (browser->selection == NULL)
762 				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
763 			else if (browser->selection->offset == -1)
764 				ui_helpline__puts("Actions are only available for assembly lines.");
765 			else if (!dl->ins.ops)
766 				goto show_sup_ins;
767 			else if (ins__is_ret(&dl->ins))
768 				goto out;
769 			else if (!(annotate_browser__jump(browser, evsel, hbt) ||
770 				     annotate_browser__callq(browser, evsel, hbt))) {
771 show_sup_ins:
772 				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
773 			}
774 			continue;
775 		}
776 		case 'P':
777 			map_symbol__annotation_dump(ms, evsel);
778 			continue;
779 		case 't':
780 			if (notes->options->show_total_period) {
781 				notes->options->show_total_period = false;
782 				notes->options->show_nr_samples = true;
783 			} else if (notes->options->show_nr_samples)
784 				notes->options->show_nr_samples = false;
785 			else
786 				notes->options->show_total_period = true;
787 			annotation__update_column_widths(notes);
788 			continue;
789 		case K_LEFT:
790 		case K_ESC:
791 		case 'q':
792 		case CTRL('c'):
793 			goto out;
794 		default:
795 			continue;
796 		}
797 
798 		if (nd != NULL)
799 			annotate_browser__set_rb_top(browser, nd);
800 	}
801 out:
802 	ui_browser__hide(&browser->b);
803 	return key;
804 }
805 
806 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
807 			     struct hist_browser_timer *hbt)
808 {
809 	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
810 }
811 
812 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
813 			     struct hist_browser_timer *hbt)
814 {
815 	/* reset abort key so that it can get Ctrl-C as a key */
816 	SLang_reset_tty();
817 	SLang_init_tty(0, 0, 0);
818 
819 	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
820 }
821 
822 int symbol__tui_annotate(struct symbol *sym, struct map *map,
823 			 struct perf_evsel *evsel,
824 			 struct hist_browser_timer *hbt)
825 {
826 	struct annotation *notes = symbol__annotation(sym);
827 	struct map_symbol ms = {
828 		.map = map,
829 		.sym = sym,
830 	};
831 	struct annotate_browser browser = {
832 		.b = {
833 			.refresh = annotate_browser__refresh,
834 			.seek	 = ui_browser__list_head_seek,
835 			.write	 = annotate_browser__write,
836 			.filter  = disasm_line__filter,
837 			.extra_title_lines = 1, /* for hists__scnprintf_title() */
838 			.priv	 = &ms,
839 			.use_navkeypressed = true,
840 		},
841 	};
842 	int ret = -1, err;
843 
844 	if (sym == NULL)
845 		return -1;
846 
847 	if (map->dso->annotate_warned)
848 		return -1;
849 
850 	err = symbol__annotate2(sym, map, evsel, &annotation__default_options, &browser.arch);
851 	if (err) {
852 		char msg[BUFSIZ];
853 		symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
854 		ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
855 		goto out_free_offsets;
856 	}
857 
858 	ui_helpline__push("Press ESC to exit");
859 
860 	browser.b.width = notes->max_line_len;
861 	browser.b.nr_entries = notes->nr_entries;
862 	browser.b.entries = &notes->src->source,
863 	browser.b.width += 18; /* Percentage */
864 
865 	if (notes->options->hide_src_code)
866 		ui_browser__init_asm_mode(&browser.b);
867 
868 	ret = annotate_browser__run(&browser, evsel, hbt);
869 
870 	annotated_source__purge(notes->src);
871 
872 out_free_offsets:
873 	zfree(&notes->offsets);
874 	return ret;
875 }
876