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