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