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