xref: /linux/tools/perf/ui/browsers/annotate.c (revision 4232da23d75d173195c6766729e51947b64f83cd)
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->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 = notes->src->offsets[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->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->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  			map__dso(map)->long_name,
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)
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->annotate_warned = true;
978  			symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
979  			ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
980  			goto out_free_offsets;
981  		}
982  	}
983  
984  	ui_helpline__push("Press ESC to exit");
985  
986  	browser.b.width = notes->src->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  out_free_offsets:
1000  	if(not_annotated)
1001  		zfree(&notes->src->offsets);
1002  	return ret;
1003  }
1004