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