xref: /linux/tools/perf/ui/browsers/annotate.c (revision 74802634e4a7e556429417963a8cbda27dd8b4b3)
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/debuginfo.h"
8 #include "../../util/dso.h"
9 #include "../../util/hashmap.h"
10 #include "../../util/hist.h"
11 #include "../../util/sort.h"
12 #include "../../util/map.h"
13 #include "../../util/mutex.h"
14 #include "../../util/symbol.h"
15 #include "../../util/evsel.h"
16 #include "../../util/evlist.h"
17 #include "../../util/thread.h"
18 #include <inttypes.h>
19 #include <linux/err.h>
20 #include <linux/kernel.h>
21 #include <linux/string.h>
22 #include <linux/zalloc.h>
23 #include <sys/ttydefaults.h>
24 #include <asm/bug.h>
25 
26 struct arch;
27 
28 struct annotate_browser {
29 	struct ui_browser	    b;
30 	struct rb_root		    entries;
31 	struct rb_node		   *curr_hot;
32 	struct annotation_line	   *selection;
33 	const struct arch	   *arch;
34 	/*
35 	 * perf top can delete hist_entry anytime.  Callers should make sure
36 	 * its lifetime.
37 	 */
38 	struct hist_entry	   *he;
39 	struct debuginfo	   *dbg;
40 	struct evsel		   *evsel;
41 	struct hashmap		   *type_hash;
42 	bool			    searching_backwards;
43 	char			    search_bf[128];
44 };
45 
46 /* A copy of target hist_entry for perf top. */
47 static struct hist_entry annotate_he;
48 
49 static size_t type_hash(long key, void *ctx __maybe_unused)
50 {
51 	return key;
52 }
53 
54 static bool type_equal(long key1, long key2, void *ctx __maybe_unused)
55 {
56 	return key1 == key2;
57 }
58 
59 static inline struct annotation *browser__annotation(struct ui_browser *browser)
60 {
61 	struct map_symbol *ms = browser->priv;
62 	return symbol__annotation(ms->sym);
63 }
64 
65 static bool disasm_line__filter(struct ui_browser *browser __maybe_unused, void *entry)
66 {
67 	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
68 	return annotation_line__filter(al);
69 }
70 
71 static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
72 {
73 	struct annotation *notes = browser__annotation(browser);
74 
75 	if (current && (!browser->use_navkeypressed || browser->navkeypressed))
76 		return HE_COLORSET_SELECTED;
77 	if (nr == notes->src->max_jump_sources)
78 		return HE_COLORSET_TOP;
79 	if (nr > 1)
80 		return HE_COLORSET_MEDIUM;
81 	return HE_COLORSET_NORMAL;
82 }
83 
84 static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current)
85 {
86 	 int color = ui_browser__jumps_percent_color(browser, nr, current);
87 	 return ui_browser__set_color(browser, color);
88 }
89 
90 static int annotate_browser__set_color(void *browser, int color)
91 {
92 	return ui_browser__set_color(browser, color);
93 }
94 
95 static void annotate_browser__write_graph(void *browser, int graph)
96 {
97 	ui_browser__write_graph(browser, graph);
98 }
99 
100 static void annotate_browser__set_percent_color(void *browser, double percent, bool current)
101 {
102 	ui_browser__set_percent_color(browser, percent, current);
103 }
104 
105 static void annotate_browser__printf(void *browser, const char *fmt, ...)
106 {
107 	va_list args;
108 
109 	va_start(args, fmt);
110 	ui_browser__vprintf(browser, fmt, args);
111 	va_end(args);
112 }
113 
114 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
115 {
116 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
117 	struct annotation *notes = browser__annotation(browser);
118 	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
119 	const bool is_current_entry = ui_browser__is_current_entry(browser, row);
120 	struct annotation_write_ops ops = {
121 		.first_line		 = row == 0,
122 		.current_entry		 = is_current_entry,
123 		.change_color		 = (!annotate_opts.hide_src_code &&
124 					    (!is_current_entry ||
125 					     (browser->use_navkeypressed &&
126 					      !browser->navkeypressed))),
127 		.width			 = browser->width,
128 		.obj			 = browser,
129 		.set_color		 = annotate_browser__set_color,
130 		.set_percent_color	 = annotate_browser__set_percent_color,
131 		.set_jumps_percent_color = ui_browser__set_jumps_percent_color,
132 		.printf			 = annotate_browser__printf,
133 		.write_graph		 = annotate_browser__write_graph,
134 	};
135 	struct annotation_print_data apd = {
136 		.he = ab->he,
137 		.arch = ab->arch,
138 		.evsel = ab->evsel,
139 		.dbg = ab->dbg,
140 	};
141 
142 	/* The scroll bar isn't being used */
143 	if (!browser->navkeypressed)
144 		ops.width += 1;
145 
146 	if (!IS_ERR_OR_NULL(ab->type_hash))
147 		apd.type_hash = ab->type_hash;
148 
149 	annotation_line__write(al, notes, &ops, &apd);
150 
151 	if (ops.current_entry)
152 		ab->selection = al;
153 }
154 
155 static int is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
156 {
157 	struct disasm_line *pos = list_prev_entry(cursor, al.node);
158 	const char *name;
159 	int diff = 1;
160 
161 	while (pos && pos->al.offset == -1) {
162 		pos = list_prev_entry(pos, al.node);
163 		if (!annotate_opts.hide_src_code)
164 			diff++;
165 	}
166 
167 	if (!pos)
168 		return 0;
169 
170 	if (ins__is_lock(&pos->ins))
171 		name = pos->ops.locked.ins.name;
172 	else
173 		name = pos->ins.name;
174 
175 	if (!name || !cursor->ins.name)
176 		return 0;
177 
178 	if (ins__is_fused(ab->arch, name, cursor->ins.name))
179 		return diff;
180 	return 0;
181 }
182 
183 static void annotate_browser__draw_current_jump(struct ui_browser *browser)
184 {
185 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
186 	struct disasm_line *cursor = disasm_line(ab->selection);
187 	struct annotation_line *target;
188 	unsigned int from, to;
189 	struct map_symbol *ms = ab->b.priv;
190 	struct symbol *sym = ms->sym;
191 	struct annotation *notes = symbol__annotation(sym);
192 	u8 pcnt_width = annotation__pcnt_width(notes);
193 	u8 cntr_width = annotation__br_cntr_width();
194 	int width;
195 	int diff = 0;
196 
197 	/* PLT symbols contain external offsets */
198 	if (strstr(sym->name, "@plt"))
199 		return;
200 
201 	if (!disasm_line__is_valid_local_jump(cursor, sym))
202 		return;
203 
204 	/*
205 	 * This first was seen with a gcc function, _cpp_lex_token, that
206 	 * has the usual jumps:
207 	 *
208 	 *  │1159e6c: ↓ jne    115aa32 <_cpp_lex_token@@Base+0xf92>
209 	 *
210 	 * I.e. jumps to a label inside that function (_cpp_lex_token), and
211 	 * those works, but also this kind:
212 	 *
213 	 *  │1159e8b: ↓ jne    c469be <cpp_named_operator2name@@Base+0xa72>
214 	 *
215 	 *  I.e. jumps to another function, outside _cpp_lex_token, which
216 	 *  are not being correctly handled generating as a side effect references
217 	 *  to ab->offset[] entries that are set to NULL, so to make this code
218 	 *  more robust, check that here.
219 	 *
220 	 *  A proper fix for will be put in place, looking at the function
221 	 *  name right after the '<' token and probably treating this like a
222 	 *  'call' instruction.
223 	 */
224 	target = annotated_source__get_line(notes->src, cursor->ops.target.offset);
225 	if (target == NULL) {
226 		ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
227 				    cursor->ops.target.offset);
228 		return;
229 	}
230 
231 	if (annotate_opts.hide_src_code) {
232 		from = cursor->al.idx_asm;
233 		to = target->idx_asm;
234 	} else {
235 		from = (u64)cursor->al.idx;
236 		to = (u64)target->idx;
237 	}
238 
239 	width = annotation__cycles_width(notes);
240 
241 	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
242 	__ui_browser__line_arrow(browser,
243 				 pcnt_width + 2 + notes->src->widths.addr + width + cntr_width,
244 				 from, to);
245 
246 	diff = is_fused(ab, cursor);
247 	if (diff > 0) {
248 		ui_browser__mark_fused(browser,
249 				       pcnt_width + 3 + notes->src->widths.addr + width + cntr_width,
250 				       from - diff, diff, to > from);
251 	}
252 }
253 
254 static unsigned int annotate_browser__refresh(struct ui_browser *browser)
255 {
256 	struct annotation *notes = browser__annotation(browser);
257 	int ret = ui_browser__list_head_refresh(browser);
258 	int pcnt_width = annotation__pcnt_width(notes);
259 
260 	if (annotate_opts.jump_arrows)
261 		annotate_browser__draw_current_jump(browser);
262 
263 	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
264 	__ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1);
265 	return ret;
266 }
267 
268 static double disasm__cmp(struct annotation_line *a, struct annotation_line *b,
269 						  int percent_type)
270 {
271 	int i;
272 
273 	for (i = 0; i < a->data_nr; i++) {
274 		if (a->data[i].percent[percent_type] == b->data[i].percent[percent_type])
275 			continue;
276 		return a->data[i].percent[percent_type] -
277 			   b->data[i].percent[percent_type];
278 	}
279 	return 0;
280 }
281 
282 static void disasm_rb_tree__insert(struct annotate_browser *browser,
283 				struct annotation_line *al)
284 {
285 	struct rb_root *root = &browser->entries;
286 	struct rb_node **p = &root->rb_node;
287 	struct rb_node *parent = NULL;
288 	struct annotation_line *l;
289 
290 	while (*p != NULL) {
291 		parent = *p;
292 		l = rb_entry(parent, struct annotation_line, rb_node);
293 
294 		if (disasm__cmp(al, l, annotate_opts.percent_type) < 0)
295 			p = &(*p)->rb_left;
296 		else
297 			p = &(*p)->rb_right;
298 	}
299 	rb_link_node(&al->rb_node, parent, p);
300 	rb_insert_color(&al->rb_node, root);
301 }
302 
303 static void annotate_browser__set_top(struct annotate_browser *browser,
304 				      struct annotation_line *pos, u32 idx)
305 {
306 	unsigned back;
307 
308 	ui_browser__refresh_dimensions(&browser->b);
309 	back = browser->b.height / 2;
310 	browser->b.top_idx = browser->b.index = idx;
311 
312 	while (browser->b.top_idx != 0 && back != 0) {
313 		pos = list_entry(pos->node.prev, struct annotation_line, node);
314 
315 		if (annotation_line__filter(pos))
316 			continue;
317 
318 		--browser->b.top_idx;
319 		--back;
320 	}
321 
322 	browser->b.top = pos;
323 	browser->b.navkeypressed = true;
324 }
325 
326 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
327 					 struct rb_node *nd)
328 {
329 	struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
330 	u32 idx = pos->idx;
331 
332 	if (annotate_opts.hide_src_code)
333 		idx = pos->idx_asm;
334 	annotate_browser__set_top(browser, pos, idx);
335 	browser->curr_hot = nd;
336 }
337 
338 static void annotate_browser__calc_percent(struct annotate_browser *browser,
339 					   struct evsel *evsel)
340 {
341 	struct map_symbol *ms = browser->b.priv;
342 	struct symbol *sym = ms->sym;
343 	struct annotation *notes = symbol__annotation(sym);
344 	struct disasm_line *pos;
345 
346 	browser->entries = RB_ROOT;
347 
348 	annotation__lock(notes);
349 
350 	symbol__calc_percent(sym, evsel);
351 
352 	list_for_each_entry(pos, &notes->src->source, al.node) {
353 		double max_percent = 0.0;
354 		int i;
355 
356 		if (pos->al.offset == -1) {
357 			RB_CLEAR_NODE(&pos->al.rb_node);
358 			continue;
359 		}
360 
361 		for (i = 0; i < pos->al.data_nr; i++) {
362 			double percent;
363 
364 			percent = annotation_data__percent(&pos->al.data[i],
365 							   annotate_opts.percent_type);
366 
367 			if (max_percent < percent)
368 				max_percent = percent;
369 		}
370 
371 		if (max_percent < 0.01 && (!pos->al.cycles || pos->al.cycles->ipc == 0)) {
372 			RB_CLEAR_NODE(&pos->al.rb_node);
373 			continue;
374 		}
375 		disasm_rb_tree__insert(browser, &pos->al);
376 	}
377 	annotation__unlock(notes);
378 
379 	browser->curr_hot = rb_last(&browser->entries);
380 }
381 
382 static struct annotation_line *annotate_browser__find_new_asm_line(
383 					struct annotate_browser *browser,
384 					int idx_asm)
385 {
386 	struct annotation_line *al;
387 	struct list_head *head = browser->b.entries;
388 
389 	/* find an annotation line in the new list with the same idx_asm */
390 	list_for_each_entry(al, head, node) {
391 		if (al->idx_asm == idx_asm)
392 			return al;
393 	}
394 
395 	/* There are no asm lines */
396 	return NULL;
397 }
398 
399 static struct annotation_line *annotate_browser__find_next_asm_line(
400 					struct annotate_browser *browser,
401 					struct annotation_line *al)
402 {
403 	struct annotation_line *it = al;
404 
405 	/* find next asm line */
406 	list_for_each_entry_continue(it, browser->b.entries, node) {
407 		if (it->idx_asm >= 0)
408 			return it;
409 	}
410 
411 	/* no asm line found forwards, try backwards */
412 	it = al;
413 	list_for_each_entry_continue_reverse(it, browser->b.entries, node) {
414 		if (it->idx_asm >= 0)
415 			return it;
416 	}
417 
418 	/* There are no asm lines */
419 	return NULL;
420 }
421 
422 static bool annotation__has_source(struct annotation *notes)
423 {
424 	struct annotation_line *al;
425 	bool found_asm = false;
426 
427 	/* Let's skip the first non-asm lines which present regardless of source. */
428 	list_for_each_entry(al, &notes->src->source, node) {
429 		if (al->offset >= 0) {
430 			found_asm = true;
431 			break;
432 		}
433 	}
434 
435 	if (found_asm) {
436 		/* After assembly lines, any line without offset means source. */
437 		list_for_each_entry_continue(al, &notes->src->source, node) {
438 			if (al->offset == -1)
439 				return true;
440 		}
441 	}
442 	return false;
443 }
444 
445 static bool annotate_browser__toggle_source(struct annotate_browser *browser,
446 					    struct evsel *evsel)
447 {
448 	struct annotation *notes = browser__annotation(&browser->b);
449 	struct annotation_line *al;
450 	off_t offset = browser->b.index - browser->b.top_idx;
451 
452 	if (browser->b.nr_entries == 0)
453 		return false;
454 
455 	browser->b.seek(&browser->b, offset, SEEK_CUR);
456 	al = list_entry(browser->b.top, struct annotation_line, node);
457 
458 	if (!annotate_opts.annotate_src)
459 		annotate_opts.annotate_src = true;
460 
461 	/*
462 	 * It's about to get source code annotation for the first time.
463 	 * Drop the existing annotation_lines and get the new one with source.
464 	 * And then move to the original line at the same asm index.
465 	 */
466 	if (annotate_opts.hide_src_code && !notes->src->tried_source) {
467 		struct map_symbol *ms = browser->b.priv;
468 		int orig_idx_asm = al->idx_asm;
469 
470 		/* annotate again with source code info */
471 		annotate_opts.hide_src_code = false;
472 		annotated_source__purge(notes->src);
473 		symbol__annotate2(ms, evsel, &browser->arch);
474 		annotate_opts.hide_src_code = true;
475 
476 		/* should be after annotated_source__purge() */
477 		notes->src->tried_source = true;
478 
479 		if (!annotation__has_source(notes))
480 			ui__warning("Annotation has no source code.");
481 
482 		browser->b.entries = &notes->src->source;
483 		al = annotate_browser__find_new_asm_line(browser, orig_idx_asm);
484 		if (unlikely(al == NULL)) {
485 			al = list_first_entry(&notes->src->source,
486 					      struct annotation_line, node);
487 		}
488 		browser->b.seek(&browser->b, al->idx_asm, SEEK_SET);
489 	}
490 
491 	if (annotate_opts.hide_src_code) {
492 		if (al->idx_asm < offset)
493 			offset = al->idx;
494 
495 		browser->b.nr_entries = notes->src->nr_entries;
496 		annotate_opts.hide_src_code = false;
497 		browser->b.seek(&browser->b, -offset, SEEK_CUR);
498 		browser->b.top_idx = al->idx - offset;
499 		browser->b.index = al->idx;
500 	} else {
501 		if (al->idx_asm < 0) {
502 			/* move cursor to next asm line */
503 			al = annotate_browser__find_next_asm_line(browser, al);
504 			if (!al) {
505 				browser->b.seek(&browser->b, -offset, SEEK_CUR);
506 				return false;
507 			}
508 		}
509 
510 		if (al->idx_asm < offset)
511 			offset = al->idx_asm;
512 
513 		browser->b.nr_entries = notes->src->nr_asm_entries;
514 		annotate_opts.hide_src_code = true;
515 		browser->b.seek(&browser->b, -offset, SEEK_CUR);
516 		browser->b.top_idx = al->idx_asm - offset;
517 		browser->b.index = al->idx_asm;
518 	}
519 
520 	if (annotate_opts.hide_src_code_on_title)
521 		annotate_opts.hide_src_code_on_title = false;
522 
523 	return true;
524 }
525 
526 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
527 
528 static void annotate_browser__show_full_location(struct ui_browser *browser)
529 {
530 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
531 	struct disasm_line *cursor = disasm_line(ab->selection);
532 	struct annotation_line *al = &cursor->al;
533 
534 	if (al->offset != -1)
535 		ui_helpline__puts("Only available for source code lines.");
536 	else if (al->fileloc == NULL)
537 		ui_helpline__puts("No source file location.");
538 	else {
539 		char help_line[SYM_TITLE_MAX_SIZE];
540 		sprintf (help_line, "Source file location: %s", al->fileloc);
541 		ui_helpline__puts(help_line);
542 	}
543 }
544 
545 static void ui_browser__init_asm_mode(struct ui_browser *browser)
546 {
547 	struct annotation *notes = browser__annotation(browser);
548 	browser->nr_entries = notes->src->nr_asm_entries;
549 	ui_browser__reset_index(browser);
550 }
551 
552 static int sym_title(struct symbol *sym, struct map *map, char *title,
553 		     size_t sz, int percent_type)
554 {
555 	return snprintf(title, sz, "%s  %s [Percent: %s] %s", sym->name,
556 			dso__long_name(map__dso(map)),
557 			percent_type_str(percent_type),
558 			annotate_opts.code_with_type ? "[Type]" : "");
559 }
560 
561 static void annotate_browser__show_function_title(struct annotate_browser *browser)
562 {
563 	struct ui_browser *b = &browser->b;
564 	struct map_symbol *ms = b->priv;
565 	struct symbol *sym = ms->sym;
566 	char title[SYM_TITLE_MAX_SIZE];
567 
568 	sym_title(sym, ms->map, title, sizeof(title), annotate_opts.percent_type);
569 
570 	ui_browser__gotorc_title(b, 0, 0);
571 	ui_browser__set_color(b, HE_COLORSET_ROOT);
572 	ui_browser__write_nstring(b, title, b->width + 1);
573 }
574 
575 /*
576  * This can be called from external jumps, i.e. jumps from one function
577  * to another, like from the kernel's entry_SYSCALL_64 function to the
578  * swapgs_restore_regs_and_return_to_usermode() function.
579  *
580  * So all we check here is that dl->ops.target.sym is set, if it is, just
581  * go to that function and when exiting from its disassembly, come back
582  * to the calling function.
583  */
584 static bool annotate_browser__callq(struct annotate_browser *browser,
585 				    struct evsel *evsel,
586 				    struct hist_browser_timer *hbt)
587 {
588 	struct map_symbol *ms = browser->b.priv, target_ms;
589 	struct disasm_line *dl = disasm_line(browser->selection);
590 	struct annotation *notes;
591 
592 	if (!dl->ops.target.sym) {
593 		ui_helpline__puts("The called function was not found.");
594 		return true;
595 	}
596 
597 	notes = symbol__annotation(dl->ops.target.sym);
598 	annotation__lock(notes);
599 
600 	if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) {
601 		annotation__unlock(notes);
602 		ui__warning("Not enough memory for annotating '%s' symbol!\n",
603 			    dl->ops.target.sym->name);
604 		return true;
605 	}
606 
607 	target_ms.thread = ms->thread;
608 	target_ms.map = ms->map;
609 	target_ms.sym = dl->ops.target.sym;
610 	annotation__unlock(notes);
611 	__hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt, NO_ADDR);
612 
613 	/*
614 	 * The annotate_browser above changed the title with the target function
615 	 * and now it's back to the original function.  Refresh the header line
616 	 * for the original function again.
617 	 */
618 	annotate_browser__show_function_title(browser);
619 	return true;
620 }
621 
622 static
623 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
624 					  s64 offset, s64 *idx)
625 {
626 	struct annotation *notes = browser__annotation(&browser->b);
627 	struct disasm_line *pos;
628 
629 	*idx = 0;
630 	list_for_each_entry(pos, &notes->src->source, al.node) {
631 		if (pos->al.offset == offset)
632 			return pos;
633 		if (!annotation_line__filter(&pos->al))
634 			++*idx;
635 	}
636 
637 	return NULL;
638 }
639 
640 static bool annotate_browser__jump(struct annotate_browser *browser,
641 				   struct evsel *evsel,
642 				   struct hist_browser_timer *hbt)
643 {
644 	struct disasm_line *dl = disasm_line(browser->selection);
645 	u64 offset;
646 	s64 idx;
647 
648 	if (!ins__is_jump(&dl->ins))
649 		return false;
650 
651 	if (dl->ops.target.outside) {
652 		annotate_browser__callq(browser, evsel, hbt);
653 		return true;
654 	}
655 
656 	offset = dl->ops.target.offset;
657 	dl = annotate_browser__find_offset(browser, offset, &idx);
658 	if (dl == NULL) {
659 		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
660 		return true;
661 	}
662 
663 	annotate_browser__set_top(browser, &dl->al, idx);
664 
665 	return true;
666 }
667 
668 static
669 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
670 					  char *s, s64 *idx)
671 {
672 	struct annotation *notes = browser__annotation(&browser->b);
673 	struct annotation_line *al = browser->selection;
674 
675 	*idx = browser->b.index;
676 	list_for_each_entry_continue(al, &notes->src->source, node) {
677 		if (annotation_line__filter(al))
678 			continue;
679 
680 		++*idx;
681 
682 		if (al->line && strstr(al->line, s) != NULL)
683 			return al;
684 	}
685 
686 	return NULL;
687 }
688 
689 static bool __annotate_browser__search(struct annotate_browser *browser)
690 {
691 	struct annotation_line *al;
692 	s64 idx;
693 
694 	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
695 	if (al == NULL) {
696 		ui_helpline__puts("String not found!");
697 		return false;
698 	}
699 
700 	annotate_browser__set_top(browser, al, idx);
701 	browser->searching_backwards = false;
702 	return true;
703 }
704 
705 static
706 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
707 						  char *s, s64 *idx)
708 {
709 	struct annotation *notes = browser__annotation(&browser->b);
710 	struct annotation_line *al = browser->selection;
711 
712 	*idx = browser->b.index;
713 	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
714 		if (annotation_line__filter(al))
715 			continue;
716 
717 		--*idx;
718 
719 		if (al->line && strstr(al->line, s) != NULL)
720 			return al;
721 	}
722 
723 	return NULL;
724 }
725 
726 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
727 {
728 	struct annotation_line *al;
729 	s64 idx;
730 
731 	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
732 	if (al == NULL) {
733 		ui_helpline__puts("String not found!");
734 		return false;
735 	}
736 
737 	annotate_browser__set_top(browser, al, idx);
738 	browser->searching_backwards = true;
739 	return true;
740 }
741 
742 static bool annotate_browser__search_window(struct annotate_browser *browser,
743 					    int delay_secs)
744 {
745 	if (ui_browser__input_window("Search", "String: ", browser->search_bf,
746 				     "ENTER: OK, ESC: Cancel",
747 				     delay_secs * 2) != K_ENTER ||
748 	    !*browser->search_bf)
749 		return false;
750 
751 	return true;
752 }
753 
754 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
755 {
756 	if (annotate_browser__search_window(browser, delay_secs))
757 		return __annotate_browser__search(browser);
758 
759 	return false;
760 }
761 
762 static bool annotate_browser__continue_search(struct annotate_browser *browser,
763 					      int delay_secs)
764 {
765 	if (!*browser->search_bf)
766 		return annotate_browser__search(browser, delay_secs);
767 
768 	return __annotate_browser__search(browser);
769 }
770 
771 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
772 					   int delay_secs)
773 {
774 	if (annotate_browser__search_window(browser, delay_secs))
775 		return __annotate_browser__search_reverse(browser);
776 
777 	return false;
778 }
779 
780 static
781 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
782 					       int delay_secs)
783 {
784 	if (!*browser->search_bf)
785 		return annotate_browser__search_reverse(browser, delay_secs);
786 
787 	return __annotate_browser__search_reverse(browser);
788 }
789 
790 static int annotate_browser__show(struct annotate_browser *browser, char *title, const char *help)
791 {
792 	if (ui_browser__show(&browser->b, title, help) < 0)
793 		return -1;
794 
795 	annotate_browser__show_function_title(browser);
796 	return 0;
797 }
798 
799 static void
800 switch_percent_type(struct annotation_options *opts, bool base)
801 {
802 	switch (opts->percent_type) {
803 	case PERCENT_HITS_LOCAL:
804 		if (base)
805 			opts->percent_type = PERCENT_PERIOD_LOCAL;
806 		else
807 			opts->percent_type = PERCENT_HITS_GLOBAL;
808 		break;
809 	case PERCENT_HITS_GLOBAL:
810 		if (base)
811 			opts->percent_type = PERCENT_PERIOD_GLOBAL;
812 		else
813 			opts->percent_type = PERCENT_HITS_LOCAL;
814 		break;
815 	case PERCENT_PERIOD_LOCAL:
816 		if (base)
817 			opts->percent_type = PERCENT_HITS_LOCAL;
818 		else
819 			opts->percent_type = PERCENT_PERIOD_GLOBAL;
820 		break;
821 	case PERCENT_PERIOD_GLOBAL:
822 		if (base)
823 			opts->percent_type = PERCENT_HITS_GLOBAL;
824 		else
825 			opts->percent_type = PERCENT_PERIOD_LOCAL;
826 		break;
827 	default:
828 		WARN_ON(1);
829 	}
830 }
831 
832 static int annotate__scnprintf_title(struct hists *hists, char *bf, size_t size)
833 {
834 	int printed = hists__scnprintf_title(hists, bf, size);
835 
836 	if (!annotate_opts.hide_src_code_on_title) {
837 		printed += scnprintf(bf + printed, size - printed, " [source: %s]",
838 				     annotate_opts.hide_src_code ? "OFF" : "On");
839 	}
840 
841 	return printed;
842 }
843 
844 static void annotate_browser__debuginfo_warning(struct annotate_browser *browser)
845 {
846 	struct map_symbol *ms = browser->b.priv;
847 	struct dso *dso = map__dso(ms->map);
848 
849 	if (browser->dbg == NULL && annotate_opts.code_with_type &&
850 	    !dso__debuginfo_warned(dso)) {
851 		ui__warning("DWARF debuginfo not found.\n\n"
852 			    "Data-type in this DSO will not be displayed.\n"
853 			    "Please make sure to have debug information.");
854 		dso__set_debuginfo_warned(dso);
855 	}
856 }
857 
858 static s64 annotate_browser__curr_hot_offset(struct annotate_browser *browser)
859 {
860 	struct annotation_line *al = NULL;
861 
862 	if (browser->curr_hot)
863 		al = rb_entry(browser->curr_hot, struct annotation_line, rb_node);
864 
865 	return al ? al->offset : 0;
866 }
867 
868 static void annotate_browser__symbol_annotate_error(struct annotate_browser *browser, int err)
869 {
870 	struct map_symbol *ms = browser->b.priv;
871 	struct symbol *sym = ms->sym;
872 	struct dso *dso = map__dso(ms->map);
873 	char msg[BUFSIZ];
874 
875 	dso__set_annotate_warned(dso);
876 	symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
877 	ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
878 }
879 
880 static int annotate_browser__run(struct annotate_browser *browser,
881 				 struct evsel *evsel,
882 				 struct hist_browser_timer *hbt)
883 {
884 	struct rb_node *nd = NULL;
885 	struct hists *hists = evsel__hists(evsel);
886 	struct map_symbol *ms = browser->b.priv;
887 	struct symbol *sym = ms->sym;
888 	struct annotation *notes = symbol__annotation(ms->sym);
889 	const char *help = "Press 'h' for help on key bindings";
890 	int delay_secs = hbt ? hbt->refresh : 0;
891 	char *br_cntr_text = NULL;
892 	char title[256];
893 	int key;
894 
895 	annotate__scnprintf_title(hists, title, sizeof(title));
896 	if (annotate_browser__show(browser, title, help) < 0)
897 		return -1;
898 
899 	annotate_browser__calc_percent(browser, evsel);
900 
901 	if (browser->selection != NULL) {
902 		browser->curr_hot = &browser->selection->rb_node;
903 		browser->b.use_navkeypressed = false;
904 	}
905 
906 	if (browser->curr_hot) {
907 		annotate_browser__set_rb_top(browser, browser->curr_hot);
908 		browser->b.navkeypressed = false;
909 	}
910 
911 	nd = browser->curr_hot;
912 
913 	annotation_br_cntr_abbr_list(&br_cntr_text, evsel, false);
914 
915 	annotate_browser__debuginfo_warning(browser);
916 
917 	while (1) {
918 		key = ui_browser__run(&browser->b, delay_secs);
919 
920 		if (delay_secs != 0) {
921 			annotate_browser__calc_percent(browser, evsel);
922 			/*
923 			 * Current line focus got out of the list of most active
924 			 * lines, NULL it so that if TAB|UNTAB is pressed, we
925 			 * move to curr_hot (current hottest line).
926 			 */
927 			if (nd != NULL && RB_EMPTY_NODE(nd))
928 				nd = NULL;
929 		}
930 
931 		switch (key) {
932 		case K_TIMER:
933 			if (hbt)
934 				hbt->timer(hbt->arg);
935 
936 			if (delay_secs != 0) {
937 				symbol__annotate_decay_histogram(sym, evsel);
938 				annotate__scnprintf_title(hists, title, sizeof(title));
939 				annotate_browser__show(browser, title, help);
940 			}
941 			continue;
942 		case K_TAB:
943 			if (nd != NULL) {
944 				nd = rb_prev(nd);
945 				if (nd == NULL)
946 					nd = rb_last(&browser->entries);
947 			} else
948 				nd = browser->curr_hot;
949 			break;
950 		case K_UNTAB:
951 			if (nd != NULL) {
952 				nd = rb_next(nd);
953 				if (nd == NULL)
954 					nd = rb_first(&browser->entries);
955 			} else
956 				nd = browser->curr_hot;
957 			break;
958 		case K_F1:
959 		case 'h':
960 			ui_browser__help_window(&browser->b,
961 		"UP/DOWN/PGUP\n"
962 		"PGDN/SPACE    Navigate\n"
963 		"</>           Move to prev/next symbol\n"
964 		"q/ESC/CTRL+C  Exit\n\n"
965 		"ENTER         Go to target\n"
966 		"H             Go to hottest instruction\n"
967 		"TAB/shift+TAB Cycle thru hottest instructions\n"
968 		"j             Toggle showing jump to target arrows\n"
969 		"J             Toggle showing number of jump sources on targets\n"
970 		"n             Search next string\n"
971 		"o             Toggle disassembler output/simplified view\n"
972 		"O             Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
973 		"s             Toggle source code view\n"
974 		"t             Circulate percent, total period, samples view\n"
975 		"c             Show min/max cycle\n"
976 		"/             Search string\n"
977 		"k             Toggle line numbers\n"
978 		"l             Show full source file location\n"
979 		"P             Print to [symbol_name].annotation file.\n"
980 		"r             Run available scripts\n"
981 		"p             Toggle percent type [local/global]\n"
982 		"b             Toggle percent base [period/hits]\n"
983 		"B             Branch counter abbr list (Optional)\n"
984 		"?             Search string backwards\n"
985 		"f             Toggle showing offsets to full address\n"
986 		"T             Toggle data type display\n");
987 			continue;
988 		case 'r':
989 			script_browse(NULL, NULL);
990 			annotate_browser__show(browser, title, help);
991 			continue;
992 		case 'k':
993 			annotate_opts.show_linenr = !annotate_opts.show_linenr;
994 			continue;
995 		case 'l':
996 			annotate_browser__show_full_location (&browser->b);
997 			continue;
998 		case 'H':
999 			nd = browser->curr_hot;
1000 			break;
1001 		case 's': {
1002 			struct annotation_line *al = NULL;
1003 			s64 offset = annotate_browser__curr_hot_offset(browser);
1004 
1005 			if (annotate_browser__toggle_source(browser, evsel))
1006 				ui_helpline__puts(help);
1007 
1008 			/* Update the annotation browser's rb_tree, and reset the nd */
1009 			annotate_browser__calc_percent(browser, evsel);
1010 			/* Try to find the same asm line as before */
1011 			al = annotated_source__get_line(notes->src, offset);
1012 			browser->curr_hot = al ? &al->rb_node : NULL;
1013 			nd = browser->curr_hot;
1014 
1015 			annotate__scnprintf_title(hists, title, sizeof(title));
1016 			annotate_browser__show(browser, title, help);
1017 			continue;
1018 		}
1019 		case 'o':
1020 			annotate_opts.use_offset = !annotate_opts.use_offset;
1021 			annotation__update_column_widths(notes);
1022 			continue;
1023 		case 'O':
1024 			if (++annotate_opts.offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
1025 				annotate_opts.offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
1026 			continue;
1027 		case 'j':
1028 			annotate_opts.jump_arrows = !annotate_opts.jump_arrows;
1029 			continue;
1030 		case 'J':
1031 			annotate_opts.show_nr_jumps = !annotate_opts.show_nr_jumps;
1032 			annotation__update_column_widths(notes);
1033 			continue;
1034 		case '/':
1035 			if (annotate_browser__search(browser, delay_secs)) {
1036 show_help:
1037 				ui_helpline__puts(help);
1038 			}
1039 			continue;
1040 		case 'n':
1041 			if (browser->searching_backwards ?
1042 			    annotate_browser__continue_search_reverse(browser, delay_secs) :
1043 			    annotate_browser__continue_search(browser, delay_secs))
1044 				goto show_help;
1045 			continue;
1046 		case '?':
1047 			if (annotate_browser__search_reverse(browser, delay_secs))
1048 				goto show_help;
1049 			continue;
1050 		case 'D': {
1051 			static int seq;
1052 			ui_helpline__pop();
1053 			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
1054 					   seq++, browser->b.nr_entries,
1055 					   browser->b.height,
1056 					   browser->b.index,
1057 					   browser->b.top_idx,
1058 					   notes->src->nr_asm_entries);
1059 		}
1060 			continue;
1061 		case K_ENTER:
1062 		case K_RIGHT:
1063 		{
1064 			struct disasm_line *dl = disasm_line(browser->selection);
1065 
1066 			if (browser->selection == NULL)
1067 				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
1068 			else if (browser->selection->offset == -1)
1069 				ui_helpline__puts("Actions are only available for assembly lines.");
1070 			else if (!dl->ins.ops)
1071 				goto show_sup_ins;
1072 			else if (ins__is_ret(&dl->ins))
1073 				goto out;
1074 			else if (!(annotate_browser__jump(browser, evsel, hbt) ||
1075 				     annotate_browser__callq(browser, evsel, hbt))) {
1076 show_sup_ins:
1077 				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
1078 			}
1079 			continue;
1080 		}
1081 		case 'P':
1082 			map_symbol__annotation_dump(ms, evsel, browser->he);
1083 			continue;
1084 		case 't':
1085 			if (symbol_conf.show_total_period) {
1086 				symbol_conf.show_total_period = false;
1087 				symbol_conf.show_nr_samples = true;
1088 			} else if (symbol_conf.show_nr_samples)
1089 				symbol_conf.show_nr_samples = false;
1090 			else
1091 				symbol_conf.show_total_period = true;
1092 			annotation__update_column_widths(notes);
1093 			continue;
1094 		case 'c':
1095 			if (annotate_opts.show_minmax_cycle)
1096 				annotate_opts.show_minmax_cycle = false;
1097 			else
1098 				annotate_opts.show_minmax_cycle = true;
1099 			annotation__update_column_widths(notes);
1100 			continue;
1101 		case 'p':
1102 		case 'b':
1103 			switch_percent_type(&annotate_opts, key == 'b');
1104 			annotate__scnprintf_title(hists, title, sizeof(title));
1105 			annotate_browser__show(browser, title, help);
1106 			continue;
1107 		case 'B':
1108 			if (br_cntr_text)
1109 				ui_browser__help_window(&browser->b, br_cntr_text);
1110 			else {
1111 				ui_browser__help_window(&browser->b,
1112 							"\n The branch counter is not available.\n");
1113 			}
1114 			continue;
1115 		case 'f':
1116 			annotation__toggle_full_addr(notes, ms);
1117 			continue;
1118 		case 'T':
1119 			annotate_opts.code_with_type ^= 1;
1120 			if (browser->dbg == NULL)
1121 				browser->dbg = dso__debuginfo(map__dso(ms->map));
1122 			if (browser->type_hash == NULL) {
1123 				browser->type_hash = hashmap__new(type_hash, type_equal,
1124 								  /*ctx=*/NULL);
1125 			}
1126 			annotate_browser__show(browser, title, help);
1127 			annotate_browser__debuginfo_warning(browser);
1128 			continue;
1129 		case K_LEFT:
1130 		case '<':
1131 		case '>':
1132 		case K_ESC:
1133 		case 'q':
1134 		case CTRL('c'):
1135 			goto out;
1136 		default:
1137 			ui_browser__warn_unhandled_hotkey(&browser->b, key, delay_secs, ", use 'h'/F1 to see actions");
1138 			continue;
1139 		}
1140 
1141 		if (nd != NULL)
1142 			annotate_browser__set_rb_top(browser, nd);
1143 	}
1144 out:
1145 	ui_browser__hide(&browser->b);
1146 	free(br_cntr_text);
1147 	return key;
1148 }
1149 
1150 int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
1151 			     struct hist_browser_timer *hbt, u64 al_addr)
1152 {
1153 	/* reset abort key so that it can get Ctrl-C as a key */
1154 	SLang_reset_tty();
1155 	SLang_init_tty(0, 0, 0);
1156 	SLtty_set_suspend_state(true);
1157 
1158 	return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt, al_addr);
1159 }
1160 
1161 int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
1162 			       struct evsel *evsel,
1163 			       struct hist_browser_timer *hbt, u64 al_addr)
1164 {
1165 	struct symbol *sym = ms->sym;
1166 	struct annotation *notes = symbol__annotation(sym);
1167 	struct annotate_browser browser = {
1168 		.b = {
1169 			.refresh = annotate_browser__refresh,
1170 			.seek	 = ui_browser__list_head_seek,
1171 			.write	 = annotate_browser__write,
1172 			.filter  = disasm_line__filter,
1173 			.extra_title_lines = 1, /* for hists__scnprintf_title() */
1174 			.priv	 = ms,
1175 			.use_navkeypressed = true,
1176 		},
1177 		.he = he,
1178 		.evsel = evsel,
1179 	};
1180 	struct dso *dso;
1181 	int ret = -1, err;
1182 	int not_annotated = list_empty(&notes->src->source);
1183 
1184 	if (sym == NULL)
1185 		return -1;
1186 
1187 	dso = map__dso(ms->map);
1188 	if (dso__annotate_warned(dso))
1189 		return -1;
1190 
1191 	if (not_annotated || !symbol__is_annotate2(sym)) {
1192 		err = symbol__annotate2(ms, evsel, &browser.arch);
1193 		if (err) {
1194 			annotate_browser__symbol_annotate_error(&browser, err);
1195 			return -1;
1196 		}
1197 
1198 		if (!annotate_opts.hide_src_code) {
1199 			notes->src->tried_source = true;
1200 			if (!annotation__has_source(notes))
1201 				ui__warning("Annotation has no source code.");
1202 		}
1203 	} else {
1204 		err = thread__get_arch(ms->thread, &browser.arch);
1205 		if (err) {
1206 			annotate_browser__symbol_annotate_error(&browser, err);
1207 			return -1;
1208 		}
1209 	}
1210 
1211 	/* Copy necessary information when it's called from perf top */
1212 	if (hbt != NULL && he != &annotate_he) {
1213 		annotate_he.hists = he->hists;
1214 		annotate_he.thread = thread__get(he->thread);
1215 		annotate_he.cpumode = he->cpumode;
1216 		map_symbol__copy(&annotate_he.ms, ms);
1217 
1218 		browser.he = &annotate_he;
1219 	}
1220 
1221 	ui_helpline__push("Press ESC to exit");
1222 
1223 	if (annotate_opts.code_with_type) {
1224 		browser.dbg = dso__debuginfo(dso);
1225 		browser.type_hash = hashmap__new(type_hash, type_equal, /*ctx=*/NULL);
1226 	}
1227 
1228 	browser.b.width = notes->src->widths.max_line_len;
1229 	browser.b.nr_entries = notes->src->nr_entries;
1230 	browser.b.entries = &notes->src->source;
1231 	browser.b.width += 18; /* Percentage */
1232 
1233 	if (annotate_opts.hide_src_code)
1234 		ui_browser__init_asm_mode(&browser.b);
1235 
1236 	/*
1237 	 * If al_addr is set, it means that there should be a line
1238 	 * intentionally selected, not based on the percentages
1239 	 * which caculated by the event sampling. In this case, we
1240 	 * convey this information into the browser selection, where
1241 	 * the selection in other cases should be empty.
1242 	 */
1243 	if (al_addr != NO_ADDR) {
1244 		struct annotation_line *al = annotated_source__get_line(notes->src,
1245 			al_addr - sym->start);
1246 
1247 		browser.selection = al;
1248 	}
1249 
1250 	ret = annotate_browser__run(&browser, evsel, hbt);
1251 
1252 	debuginfo__delete(browser.dbg);
1253 
1254 	if (!IS_ERR_OR_NULL(browser.type_hash)) {
1255 		struct hashmap_entry *cur;
1256 		size_t bkt;
1257 
1258 		hashmap__for_each_entry(browser.type_hash, cur, bkt)
1259 			zfree(&cur->pvalue);
1260 		hashmap__free(browser.type_hash);
1261 	}
1262 
1263 	if (not_annotated && !notes->src->tried_source)
1264 		annotated_source__purge(notes->src);
1265 
1266 	if (hbt != NULL && he != &annotate_he) {
1267 		thread__zput(annotate_he.thread);
1268 		map_symbol__exit(&annotate_he.ms);
1269 	}
1270 
1271 	return ret;
1272 }
1273