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