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