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