xref: /openbmc/linux/tools/perf/ui/browsers/annotate.c (revision 8dda2eac)
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 struct annotation_line *annotate_browser__find_next_asm_line(
347 					struct annotate_browser *browser,
348 					struct annotation_line *al)
349 {
350 	struct annotation_line *it = al;
351 
352 	/* find next asm line */
353 	list_for_each_entry_continue(it, browser->b.entries, node) {
354 		if (it->idx_asm >= 0)
355 			return it;
356 	}
357 
358 	/* no asm line found forwards, try backwards */
359 	it = al;
360 	list_for_each_entry_continue_reverse(it, browser->b.entries, node) {
361 		if (it->idx_asm >= 0)
362 			return it;
363 	}
364 
365 	/* There are no asm lines */
366 	return NULL;
367 }
368 
369 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
370 {
371 	struct annotation *notes = browser__annotation(&browser->b);
372 	struct annotation_line *al;
373 	off_t offset = browser->b.index - browser->b.top_idx;
374 
375 	browser->b.seek(&browser->b, offset, SEEK_CUR);
376 	al = list_entry(browser->b.top, struct annotation_line, node);
377 
378 	if (notes->options->hide_src_code) {
379 		if (al->idx_asm < offset)
380 			offset = al->idx;
381 
382 		browser->b.nr_entries = notes->nr_entries;
383 		notes->options->hide_src_code = false;
384 		browser->b.seek(&browser->b, -offset, SEEK_CUR);
385 		browser->b.top_idx = al->idx - offset;
386 		browser->b.index = al->idx;
387 	} else {
388 		if (al->idx_asm < 0) {
389 			/* move cursor to next asm line */
390 			al = annotate_browser__find_next_asm_line(browser, al);
391 			if (!al) {
392 				browser->b.seek(&browser->b, -offset, SEEK_CUR);
393 				return false;
394 			}
395 		}
396 
397 		if (al->idx_asm < offset)
398 			offset = al->idx_asm;
399 
400 		browser->b.nr_entries = notes->nr_asm_entries;
401 		notes->options->hide_src_code = true;
402 		browser->b.seek(&browser->b, -offset, SEEK_CUR);
403 		browser->b.top_idx = al->idx_asm - offset;
404 		browser->b.index = al->idx_asm;
405 	}
406 
407 	return true;
408 }
409 
410 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
411 
412 static void annotate_browser__show_full_location(struct ui_browser *browser)
413 {
414 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
415 	struct disasm_line *cursor = disasm_line(ab->selection);
416 	struct annotation_line *al = &cursor->al;
417 
418 	if (al->offset != -1)
419 		ui_helpline__puts("Only available for source code lines.");
420 	else if (al->fileloc == NULL)
421 		ui_helpline__puts("No source file location.");
422 	else {
423 		char help_line[SYM_TITLE_MAX_SIZE];
424 		sprintf (help_line, "Source file location: %s", al->fileloc);
425 		ui_helpline__puts(help_line);
426 	}
427 }
428 
429 static void ui_browser__init_asm_mode(struct ui_browser *browser)
430 {
431 	struct annotation *notes = browser__annotation(browser);
432 	ui_browser__reset_index(browser);
433 	browser->nr_entries = notes->nr_asm_entries;
434 }
435 
436 static int sym_title(struct symbol *sym, struct map *map, char *title,
437 		     size_t sz, int percent_type)
438 {
439 	return snprintf(title, sz, "%s  %s [Percent: %s]", sym->name, map->dso->long_name,
440 			percent_type_str(percent_type));
441 }
442 
443 /*
444  * This can be called from external jumps, i.e. jumps from one function
445  * to another, like from the kernel's entry_SYSCALL_64 function to the
446  * swapgs_restore_regs_and_return_to_usermode() function.
447  *
448  * So all we check here is that dl->ops.target.sym is set, if it is, just
449  * go to that function and when exiting from its disassembly, come back
450  * to the calling function.
451  */
452 static bool annotate_browser__callq(struct annotate_browser *browser,
453 				    struct evsel *evsel,
454 				    struct hist_browser_timer *hbt)
455 {
456 	struct map_symbol *ms = browser->b.priv, target_ms;
457 	struct disasm_line *dl = disasm_line(browser->selection);
458 	struct annotation *notes;
459 	char title[SYM_TITLE_MAX_SIZE];
460 
461 	if (!dl->ops.target.sym) {
462 		ui_helpline__puts("The called function was not found.");
463 		return true;
464 	}
465 
466 	notes = symbol__annotation(dl->ops.target.sym);
467 	pthread_mutex_lock(&notes->lock);
468 
469 	if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) {
470 		pthread_mutex_unlock(&notes->lock);
471 		ui__warning("Not enough memory for annotating '%s' symbol!\n",
472 			    dl->ops.target.sym->name);
473 		return true;
474 	}
475 
476 	target_ms.maps = ms->maps;
477 	target_ms.map = ms->map;
478 	target_ms.sym = dl->ops.target.sym;
479 	pthread_mutex_unlock(&notes->lock);
480 	symbol__tui_annotate(&target_ms, evsel, hbt, browser->opts);
481 	sym_title(ms->sym, ms->map, title, sizeof(title), browser->opts->percent_type);
482 	ui_browser__show_title(&browser->b, title);
483 	return true;
484 }
485 
486 static
487 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
488 					  s64 offset, s64 *idx)
489 {
490 	struct annotation *notes = browser__annotation(&browser->b);
491 	struct disasm_line *pos;
492 
493 	*idx = 0;
494 	list_for_each_entry(pos, &notes->src->source, al.node) {
495 		if (pos->al.offset == offset)
496 			return pos;
497 		if (!annotation_line__filter(&pos->al, notes))
498 			++*idx;
499 	}
500 
501 	return NULL;
502 }
503 
504 static bool annotate_browser__jump(struct annotate_browser *browser,
505 				   struct evsel *evsel,
506 				   struct hist_browser_timer *hbt)
507 {
508 	struct disasm_line *dl = disasm_line(browser->selection);
509 	u64 offset;
510 	s64 idx;
511 
512 	if (!ins__is_jump(&dl->ins))
513 		return false;
514 
515 	if (dl->ops.target.outside) {
516 		annotate_browser__callq(browser, evsel, hbt);
517 		return true;
518 	}
519 
520 	offset = dl->ops.target.offset;
521 	dl = annotate_browser__find_offset(browser, offset, &idx);
522 	if (dl == NULL) {
523 		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
524 		return true;
525 	}
526 
527 	annotate_browser__set_top(browser, &dl->al, idx);
528 
529 	return true;
530 }
531 
532 static
533 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
534 					  char *s, s64 *idx)
535 {
536 	struct annotation *notes = browser__annotation(&browser->b);
537 	struct annotation_line *al = browser->selection;
538 
539 	*idx = browser->b.index;
540 	list_for_each_entry_continue(al, &notes->src->source, node) {
541 		if (annotation_line__filter(al, notes))
542 			continue;
543 
544 		++*idx;
545 
546 		if (al->line && strstr(al->line, s) != NULL)
547 			return al;
548 	}
549 
550 	return NULL;
551 }
552 
553 static bool __annotate_browser__search(struct annotate_browser *browser)
554 {
555 	struct annotation_line *al;
556 	s64 idx;
557 
558 	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
559 	if (al == NULL) {
560 		ui_helpline__puts("String not found!");
561 		return false;
562 	}
563 
564 	annotate_browser__set_top(browser, al, idx);
565 	browser->searching_backwards = false;
566 	return true;
567 }
568 
569 static
570 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
571 						  char *s, s64 *idx)
572 {
573 	struct annotation *notes = browser__annotation(&browser->b);
574 	struct annotation_line *al = browser->selection;
575 
576 	*idx = browser->b.index;
577 	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
578 		if (annotation_line__filter(al, notes))
579 			continue;
580 
581 		--*idx;
582 
583 		if (al->line && strstr(al->line, s) != NULL)
584 			return al;
585 	}
586 
587 	return NULL;
588 }
589 
590 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
591 {
592 	struct annotation_line *al;
593 	s64 idx;
594 
595 	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
596 	if (al == NULL) {
597 		ui_helpline__puts("String not found!");
598 		return false;
599 	}
600 
601 	annotate_browser__set_top(browser, al, idx);
602 	browser->searching_backwards = true;
603 	return true;
604 }
605 
606 static bool annotate_browser__search_window(struct annotate_browser *browser,
607 					    int delay_secs)
608 {
609 	if (ui_browser__input_window("Search", "String: ", browser->search_bf,
610 				     "ENTER: OK, ESC: Cancel",
611 				     delay_secs * 2) != K_ENTER ||
612 	    !*browser->search_bf)
613 		return false;
614 
615 	return true;
616 }
617 
618 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
619 {
620 	if (annotate_browser__search_window(browser, delay_secs))
621 		return __annotate_browser__search(browser);
622 
623 	return false;
624 }
625 
626 static bool annotate_browser__continue_search(struct annotate_browser *browser,
627 					      int delay_secs)
628 {
629 	if (!*browser->search_bf)
630 		return annotate_browser__search(browser, delay_secs);
631 
632 	return __annotate_browser__search(browser);
633 }
634 
635 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
636 					   int delay_secs)
637 {
638 	if (annotate_browser__search_window(browser, delay_secs))
639 		return __annotate_browser__search_reverse(browser);
640 
641 	return false;
642 }
643 
644 static
645 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
646 					       int delay_secs)
647 {
648 	if (!*browser->search_bf)
649 		return annotate_browser__search_reverse(browser, delay_secs);
650 
651 	return __annotate_browser__search_reverse(browser);
652 }
653 
654 static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
655 {
656 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
657 	struct map_symbol *ms = browser->priv;
658 	struct symbol *sym = ms->sym;
659 	char symbol_dso[SYM_TITLE_MAX_SIZE];
660 
661 	if (ui_browser__show(browser, title, help) < 0)
662 		return -1;
663 
664 	sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), ab->opts->percent_type);
665 
666 	ui_browser__gotorc_title(browser, 0, 0);
667 	ui_browser__set_color(browser, HE_COLORSET_ROOT);
668 	ui_browser__write_nstring(browser, symbol_dso, browser->width + 1);
669 	return 0;
670 }
671 
672 static void
673 switch_percent_type(struct annotation_options *opts, bool base)
674 {
675 	switch (opts->percent_type) {
676 	case PERCENT_HITS_LOCAL:
677 		if (base)
678 			opts->percent_type = PERCENT_PERIOD_LOCAL;
679 		else
680 			opts->percent_type = PERCENT_HITS_GLOBAL;
681 		break;
682 	case PERCENT_HITS_GLOBAL:
683 		if (base)
684 			opts->percent_type = PERCENT_PERIOD_GLOBAL;
685 		else
686 			opts->percent_type = PERCENT_HITS_LOCAL;
687 		break;
688 	case PERCENT_PERIOD_LOCAL:
689 		if (base)
690 			opts->percent_type = PERCENT_HITS_LOCAL;
691 		else
692 			opts->percent_type = PERCENT_PERIOD_GLOBAL;
693 		break;
694 	case PERCENT_PERIOD_GLOBAL:
695 		if (base)
696 			opts->percent_type = PERCENT_HITS_GLOBAL;
697 		else
698 			opts->percent_type = PERCENT_PERIOD_LOCAL;
699 		break;
700 	default:
701 		WARN_ON(1);
702 	}
703 }
704 
705 static int annotate_browser__run(struct annotate_browser *browser,
706 				 struct evsel *evsel,
707 				 struct hist_browser_timer *hbt)
708 {
709 	struct rb_node *nd = NULL;
710 	struct hists *hists = evsel__hists(evsel);
711 	struct map_symbol *ms = browser->b.priv;
712 	struct symbol *sym = ms->sym;
713 	struct annotation *notes = symbol__annotation(ms->sym);
714 	const char *help = "Press 'h' for help on key bindings";
715 	int delay_secs = hbt ? hbt->refresh : 0;
716 	char title[256];
717 	int key;
718 
719 	hists__scnprintf_title(hists, title, sizeof(title));
720 	if (annotate_browser__show(&browser->b, title, help) < 0)
721 		return -1;
722 
723 	annotate_browser__calc_percent(browser, evsel);
724 
725 	if (browser->curr_hot) {
726 		annotate_browser__set_rb_top(browser, browser->curr_hot);
727 		browser->b.navkeypressed = false;
728 	}
729 
730 	nd = browser->curr_hot;
731 
732 	while (1) {
733 		key = ui_browser__run(&browser->b, delay_secs);
734 
735 		if (delay_secs != 0) {
736 			annotate_browser__calc_percent(browser, evsel);
737 			/*
738 			 * Current line focus got out of the list of most active
739 			 * lines, NULL it so that if TAB|UNTAB is pressed, we
740 			 * move to curr_hot (current hottest line).
741 			 */
742 			if (nd != NULL && RB_EMPTY_NODE(nd))
743 				nd = NULL;
744 		}
745 
746 		switch (key) {
747 		case K_TIMER:
748 			if (hbt)
749 				hbt->timer(hbt->arg);
750 
751 			if (delay_secs != 0) {
752 				symbol__annotate_decay_histogram(sym, evsel->core.idx);
753 				hists__scnprintf_title(hists, title, sizeof(title));
754 				annotate_browser__show(&browser->b, title, help);
755 			}
756 			continue;
757 		case K_TAB:
758 			if (nd != NULL) {
759 				nd = rb_prev(nd);
760 				if (nd == NULL)
761 					nd = rb_last(&browser->entries);
762 			} else
763 				nd = browser->curr_hot;
764 			break;
765 		case K_UNTAB:
766 			if (nd != NULL) {
767 				nd = rb_next(nd);
768 				if (nd == NULL)
769 					nd = rb_first(&browser->entries);
770 			} else
771 				nd = browser->curr_hot;
772 			break;
773 		case K_F1:
774 		case 'h':
775 			ui_browser__help_window(&browser->b,
776 		"UP/DOWN/PGUP\n"
777 		"PGDN/SPACE    Navigate\n"
778 		"q/ESC/CTRL+C  Exit\n\n"
779 		"ENTER         Go to target\n"
780 		"ESC           Exit\n"
781 		"H             Go to hottest instruction\n"
782 		"TAB/shift+TAB Cycle thru hottest instructions\n"
783 		"j             Toggle showing jump to target arrows\n"
784 		"J             Toggle showing number of jump sources on targets\n"
785 		"n             Search next string\n"
786 		"o             Toggle disassembler output/simplified view\n"
787 		"O             Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
788 		"s             Toggle source code view\n"
789 		"t             Circulate percent, total period, samples view\n"
790 		"c             Show min/max cycle\n"
791 		"/             Search string\n"
792 		"k             Toggle line numbers\n"
793 		"l             Show full source file location\n"
794 		"P             Print to [symbol_name].annotation file.\n"
795 		"r             Run available scripts\n"
796 		"p             Toggle percent type [local/global]\n"
797 		"b             Toggle percent base [period/hits]\n"
798 		"?             Search string backwards\n");
799 			continue;
800 		case 'r':
801 			script_browse(NULL, NULL);
802 			annotate_browser__show(&browser->b, title, help);
803 			continue;
804 		case 'k':
805 			notes->options->show_linenr = !notes->options->show_linenr;
806 			continue;
807 		case 'l':
808 			annotate_browser__show_full_location (&browser->b);
809 			continue;
810 		case 'H':
811 			nd = browser->curr_hot;
812 			break;
813 		case 's':
814 			if (annotate_browser__toggle_source(browser))
815 				ui_helpline__puts(help);
816 			continue;
817 		case 'o':
818 			notes->options->use_offset = !notes->options->use_offset;
819 			annotation__update_column_widths(notes);
820 			continue;
821 		case 'O':
822 			if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
823 				notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
824 			continue;
825 		case 'j':
826 			notes->options->jump_arrows = !notes->options->jump_arrows;
827 			continue;
828 		case 'J':
829 			notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
830 			annotation__update_column_widths(notes);
831 			continue;
832 		case '/':
833 			if (annotate_browser__search(browser, delay_secs)) {
834 show_help:
835 				ui_helpline__puts(help);
836 			}
837 			continue;
838 		case 'n':
839 			if (browser->searching_backwards ?
840 			    annotate_browser__continue_search_reverse(browser, delay_secs) :
841 			    annotate_browser__continue_search(browser, delay_secs))
842 				goto show_help;
843 			continue;
844 		case '?':
845 			if (annotate_browser__search_reverse(browser, delay_secs))
846 				goto show_help;
847 			continue;
848 		case 'D': {
849 			static int seq;
850 			ui_helpline__pop();
851 			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
852 					   seq++, browser->b.nr_entries,
853 					   browser->b.height,
854 					   browser->b.index,
855 					   browser->b.top_idx,
856 					   notes->nr_asm_entries);
857 		}
858 			continue;
859 		case K_ENTER:
860 		case K_RIGHT:
861 		{
862 			struct disasm_line *dl = disasm_line(browser->selection);
863 
864 			if (browser->selection == NULL)
865 				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
866 			else if (browser->selection->offset == -1)
867 				ui_helpline__puts("Actions are only available for assembly lines.");
868 			else if (!dl->ins.ops)
869 				goto show_sup_ins;
870 			else if (ins__is_ret(&dl->ins))
871 				goto out;
872 			else if (!(annotate_browser__jump(browser, evsel, hbt) ||
873 				     annotate_browser__callq(browser, evsel, hbt))) {
874 show_sup_ins:
875 				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
876 			}
877 			continue;
878 		}
879 		case 'P':
880 			map_symbol__annotation_dump(ms, evsel, browser->opts);
881 			continue;
882 		case 't':
883 			if (symbol_conf.show_total_period) {
884 				symbol_conf.show_total_period = false;
885 				symbol_conf.show_nr_samples = true;
886 			} else if (symbol_conf.show_nr_samples)
887 				symbol_conf.show_nr_samples = false;
888 			else
889 				symbol_conf.show_total_period = true;
890 			annotation__update_column_widths(notes);
891 			continue;
892 		case 'c':
893 			if (notes->options->show_minmax_cycle)
894 				notes->options->show_minmax_cycle = false;
895 			else
896 				notes->options->show_minmax_cycle = true;
897 			annotation__update_column_widths(notes);
898 			continue;
899 		case 'p':
900 		case 'b':
901 			switch_percent_type(browser->opts, key == 'b');
902 			hists__scnprintf_title(hists, title, sizeof(title));
903 			annotate_browser__show(&browser->b, title, help);
904 			continue;
905 		case K_LEFT:
906 		case K_ESC:
907 		case 'q':
908 		case CTRL('c'):
909 			goto out;
910 		default:
911 			continue;
912 		}
913 
914 		if (nd != NULL)
915 			annotate_browser__set_rb_top(browser, nd);
916 	}
917 out:
918 	ui_browser__hide(&browser->b);
919 	return key;
920 }
921 
922 int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
923 			     struct hist_browser_timer *hbt,
924 			     struct annotation_options *opts)
925 {
926 	return symbol__tui_annotate(ms, evsel, hbt, opts);
927 }
928 
929 int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
930 			     struct hist_browser_timer *hbt,
931 			     struct annotation_options *opts)
932 {
933 	/* reset abort key so that it can get Ctrl-C as a key */
934 	SLang_reset_tty();
935 	SLang_init_tty(0, 0, 0);
936 
937 	return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts);
938 }
939 
940 int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
941 			 struct hist_browser_timer *hbt,
942 			 struct annotation_options *opts)
943 {
944 	struct symbol *sym = ms->sym;
945 	struct annotation *notes = symbol__annotation(sym);
946 	struct annotate_browser browser = {
947 		.b = {
948 			.refresh = annotate_browser__refresh,
949 			.seek	 = ui_browser__list_head_seek,
950 			.write	 = annotate_browser__write,
951 			.filter  = disasm_line__filter,
952 			.extra_title_lines = 1, /* for hists__scnprintf_title() */
953 			.priv	 = ms,
954 			.use_navkeypressed = true,
955 		},
956 		.opts = opts,
957 	};
958 	int ret = -1, err;
959 
960 	if (sym == NULL)
961 		return -1;
962 
963 	if (ms->map->dso->annotate_warned)
964 		return -1;
965 
966 	err = symbol__annotate2(ms, evsel, opts, &browser.arch);
967 	if (err) {
968 		char msg[BUFSIZ];
969 		symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
970 		ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
971 		goto out_free_offsets;
972 	}
973 
974 	ui_helpline__push("Press ESC to exit");
975 
976 	browser.b.width = notes->max_line_len;
977 	browser.b.nr_entries = notes->nr_entries;
978 	browser.b.entries = &notes->src->source,
979 	browser.b.width += 18; /* Percentage */
980 
981 	if (notes->options->hide_src_code)
982 		ui_browser__init_asm_mode(&browser.b);
983 
984 	ret = annotate_browser__run(&browser, evsel, hbt);
985 
986 	annotated_source__purge(notes->src);
987 
988 out_free_offsets:
989 	zfree(&notes->offsets);
990 	return ret;
991 }
992