xref: /linux/tools/perf/ui/browsers/annotate.c (revision 83e5b8f9bf2edfa1aac0d09d994792da594fec56)
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 	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 	browser->b.seek(&browser->b, offset, SEEK_CUR);
453 	al = list_entry(browser->b.top, struct annotation_line, node);
454 
455 	if (!annotate_opts.annotate_src)
456 		annotate_opts.annotate_src = true;
457 
458 	/*
459 	 * It's about to get source code annotation for the first time.
460 	 * Drop the existing annotation_lines and get the new one with source.
461 	 * And then move to the original line at the same asm index.
462 	 */
463 	if (annotate_opts.hide_src_code && !notes->src->tried_source) {
464 		struct map_symbol *ms = browser->b.priv;
465 		int orig_idx_asm = al->idx_asm;
466 
467 		/* annotate again with source code info */
468 		annotate_opts.hide_src_code = false;
469 		annotated_source__purge(notes->src);
470 		symbol__annotate2(ms, evsel, &browser->arch);
471 		annotate_opts.hide_src_code = true;
472 
473 		/* should be after annotated_source__purge() */
474 		notes->src->tried_source = true;
475 
476 		if (!annotation__has_source(notes))
477 			ui__warning("Annotation has no source code.");
478 
479 		browser->b.entries = &notes->src->source;
480 		al = annotate_browser__find_new_asm_line(browser, orig_idx_asm);
481 		if (unlikely(al == NULL)) {
482 			al = list_first_entry(&notes->src->source,
483 					      struct annotation_line, node);
484 		}
485 		browser->b.seek(&browser->b, al->idx_asm, SEEK_SET);
486 	}
487 
488 	if (annotate_opts.hide_src_code) {
489 		if (al->idx_asm < offset)
490 			offset = al->idx;
491 
492 		browser->b.nr_entries = notes->src->nr_entries;
493 		annotate_opts.hide_src_code = false;
494 		browser->b.seek(&browser->b, -offset, SEEK_CUR);
495 		browser->b.top_idx = al->idx - offset;
496 		browser->b.index = al->idx;
497 	} else {
498 		if (al->idx_asm < 0) {
499 			/* move cursor to next asm line */
500 			al = annotate_browser__find_next_asm_line(browser, al);
501 			if (!al) {
502 				browser->b.seek(&browser->b, -offset, SEEK_CUR);
503 				return false;
504 			}
505 		}
506 
507 		if (al->idx_asm < offset)
508 			offset = al->idx_asm;
509 
510 		browser->b.nr_entries = notes->src->nr_asm_entries;
511 		annotate_opts.hide_src_code = true;
512 		browser->b.seek(&browser->b, -offset, SEEK_CUR);
513 		browser->b.top_idx = al->idx_asm - offset;
514 		browser->b.index = al->idx_asm;
515 	}
516 
517 	if (annotate_opts.hide_src_code_on_title)
518 		annotate_opts.hide_src_code_on_title = false;
519 
520 	return true;
521 }
522 
523 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
524 
525 static void annotate_browser__show_full_location(struct ui_browser *browser)
526 {
527 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
528 	struct disasm_line *cursor = disasm_line(ab->selection);
529 	struct annotation_line *al = &cursor->al;
530 
531 	if (al->offset != -1)
532 		ui_helpline__puts("Only available for source code lines.");
533 	else if (al->fileloc == NULL)
534 		ui_helpline__puts("No source file location.");
535 	else {
536 		char help_line[SYM_TITLE_MAX_SIZE];
537 		sprintf (help_line, "Source file location: %s", al->fileloc);
538 		ui_helpline__puts(help_line);
539 	}
540 }
541 
542 static void ui_browser__init_asm_mode(struct ui_browser *browser)
543 {
544 	struct annotation *notes = browser__annotation(browser);
545 	ui_browser__reset_index(browser);
546 	browser->nr_entries = notes->src->nr_asm_entries;
547 }
548 
549 static int sym_title(struct symbol *sym, struct map *map, char *title,
550 		     size_t sz, int percent_type)
551 {
552 	return snprintf(title, sz, "%s  %s [Percent: %s] %s", sym->name,
553 			dso__long_name(map__dso(map)),
554 			percent_type_str(percent_type),
555 			annotate_opts.code_with_type ? "[Type]" : "");
556 }
557 
558 /*
559  * This can be called from external jumps, i.e. jumps from one function
560  * to another, like from the kernel's entry_SYSCALL_64 function to the
561  * swapgs_restore_regs_and_return_to_usermode() function.
562  *
563  * So all we check here is that dl->ops.target.sym is set, if it is, just
564  * go to that function and when exiting from its disassembly, come back
565  * to the calling function.
566  */
567 static bool annotate_browser__callq(struct annotate_browser *browser,
568 				    struct evsel *evsel,
569 				    struct hist_browser_timer *hbt)
570 {
571 	struct map_symbol *ms = browser->b.priv, target_ms;
572 	struct disasm_line *dl = disasm_line(browser->selection);
573 	struct annotation *notes;
574 	char title[SYM_TITLE_MAX_SIZE];
575 
576 	if (!dl->ops.target.sym) {
577 		ui_helpline__puts("The called function was not found.");
578 		return true;
579 	}
580 
581 	notes = symbol__annotation(dl->ops.target.sym);
582 	annotation__lock(notes);
583 
584 	if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) {
585 		annotation__unlock(notes);
586 		ui__warning("Not enough memory for annotating '%s' symbol!\n",
587 			    dl->ops.target.sym->name);
588 		return true;
589 	}
590 
591 	target_ms.maps = ms->maps;
592 	target_ms.map = ms->map;
593 	target_ms.sym = dl->ops.target.sym;
594 	annotation__unlock(notes);
595 	__hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt);
596 	sym_title(ms->sym, ms->map, title, sizeof(title), annotate_opts.percent_type);
597 	ui_browser__show_title(&browser->b, title);
598 	return true;
599 }
600 
601 static
602 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
603 					  s64 offset, s64 *idx)
604 {
605 	struct annotation *notes = browser__annotation(&browser->b);
606 	struct disasm_line *pos;
607 
608 	*idx = 0;
609 	list_for_each_entry(pos, &notes->src->source, al.node) {
610 		if (pos->al.offset == offset)
611 			return pos;
612 		if (!annotation_line__filter(&pos->al))
613 			++*idx;
614 	}
615 
616 	return NULL;
617 }
618 
619 static bool annotate_browser__jump(struct annotate_browser *browser,
620 				   struct evsel *evsel,
621 				   struct hist_browser_timer *hbt)
622 {
623 	struct disasm_line *dl = disasm_line(browser->selection);
624 	u64 offset;
625 	s64 idx;
626 
627 	if (!ins__is_jump(&dl->ins))
628 		return false;
629 
630 	if (dl->ops.target.outside) {
631 		annotate_browser__callq(browser, evsel, hbt);
632 		return true;
633 	}
634 
635 	offset = dl->ops.target.offset;
636 	dl = annotate_browser__find_offset(browser, offset, &idx);
637 	if (dl == NULL) {
638 		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
639 		return true;
640 	}
641 
642 	annotate_browser__set_top(browser, &dl->al, idx);
643 
644 	return true;
645 }
646 
647 static
648 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
649 					  char *s, s64 *idx)
650 {
651 	struct annotation *notes = browser__annotation(&browser->b);
652 	struct annotation_line *al = browser->selection;
653 
654 	*idx = browser->b.index;
655 	list_for_each_entry_continue(al, &notes->src->source, node) {
656 		if (annotation_line__filter(al))
657 			continue;
658 
659 		++*idx;
660 
661 		if (al->line && strstr(al->line, s) != NULL)
662 			return al;
663 	}
664 
665 	return NULL;
666 }
667 
668 static bool __annotate_browser__search(struct annotate_browser *browser)
669 {
670 	struct annotation_line *al;
671 	s64 idx;
672 
673 	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
674 	if (al == NULL) {
675 		ui_helpline__puts("String not found!");
676 		return false;
677 	}
678 
679 	annotate_browser__set_top(browser, al, idx);
680 	browser->searching_backwards = false;
681 	return true;
682 }
683 
684 static
685 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
686 						  char *s, s64 *idx)
687 {
688 	struct annotation *notes = browser__annotation(&browser->b);
689 	struct annotation_line *al = browser->selection;
690 
691 	*idx = browser->b.index;
692 	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
693 		if (annotation_line__filter(al))
694 			continue;
695 
696 		--*idx;
697 
698 		if (al->line && strstr(al->line, s) != NULL)
699 			return al;
700 	}
701 
702 	return NULL;
703 }
704 
705 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
706 {
707 	struct annotation_line *al;
708 	s64 idx;
709 
710 	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
711 	if (al == NULL) {
712 		ui_helpline__puts("String not found!");
713 		return false;
714 	}
715 
716 	annotate_browser__set_top(browser, al, idx);
717 	browser->searching_backwards = true;
718 	return true;
719 }
720 
721 static bool annotate_browser__search_window(struct annotate_browser *browser,
722 					    int delay_secs)
723 {
724 	if (ui_browser__input_window("Search", "String: ", browser->search_bf,
725 				     "ENTER: OK, ESC: Cancel",
726 				     delay_secs * 2) != K_ENTER ||
727 	    !*browser->search_bf)
728 		return false;
729 
730 	return true;
731 }
732 
733 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
734 {
735 	if (annotate_browser__search_window(browser, delay_secs))
736 		return __annotate_browser__search(browser);
737 
738 	return false;
739 }
740 
741 static bool annotate_browser__continue_search(struct annotate_browser *browser,
742 					      int delay_secs)
743 {
744 	if (!*browser->search_bf)
745 		return annotate_browser__search(browser, delay_secs);
746 
747 	return __annotate_browser__search(browser);
748 }
749 
750 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
751 					   int delay_secs)
752 {
753 	if (annotate_browser__search_window(browser, delay_secs))
754 		return __annotate_browser__search_reverse(browser);
755 
756 	return false;
757 }
758 
759 static
760 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
761 					       int delay_secs)
762 {
763 	if (!*browser->search_bf)
764 		return annotate_browser__search_reverse(browser, delay_secs);
765 
766 	return __annotate_browser__search_reverse(browser);
767 }
768 
769 static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
770 {
771 	struct map_symbol *ms = browser->priv;
772 	struct symbol *sym = ms->sym;
773 	char symbol_dso[SYM_TITLE_MAX_SIZE];
774 
775 	if (ui_browser__show(browser, title, help) < 0)
776 		return -1;
777 
778 	sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), annotate_opts.percent_type);
779 
780 	ui_browser__gotorc_title(browser, 0, 0);
781 	ui_browser__set_color(browser, HE_COLORSET_ROOT);
782 	ui_browser__write_nstring(browser, symbol_dso, browser->width + 1);
783 	return 0;
784 }
785 
786 static void
787 switch_percent_type(struct annotation_options *opts, bool base)
788 {
789 	switch (opts->percent_type) {
790 	case PERCENT_HITS_LOCAL:
791 		if (base)
792 			opts->percent_type = PERCENT_PERIOD_LOCAL;
793 		else
794 			opts->percent_type = PERCENT_HITS_GLOBAL;
795 		break;
796 	case PERCENT_HITS_GLOBAL:
797 		if (base)
798 			opts->percent_type = PERCENT_PERIOD_GLOBAL;
799 		else
800 			opts->percent_type = PERCENT_HITS_LOCAL;
801 		break;
802 	case PERCENT_PERIOD_LOCAL:
803 		if (base)
804 			opts->percent_type = PERCENT_HITS_LOCAL;
805 		else
806 			opts->percent_type = PERCENT_PERIOD_GLOBAL;
807 		break;
808 	case PERCENT_PERIOD_GLOBAL:
809 		if (base)
810 			opts->percent_type = PERCENT_HITS_GLOBAL;
811 		else
812 			opts->percent_type = PERCENT_PERIOD_LOCAL;
813 		break;
814 	default:
815 		WARN_ON(1);
816 	}
817 }
818 
819 static int annotate__scnprintf_title(struct hists *hists, char *bf, size_t size)
820 {
821 	int printed = hists__scnprintf_title(hists, bf, size);
822 
823 	if (!annotate_opts.hide_src_code_on_title) {
824 		printed += scnprintf(bf + printed, size - printed, " [source: %s]",
825 				     annotate_opts.hide_src_code ? "OFF" : "On");
826 	}
827 
828 	return printed;
829 }
830 
831 static void annotate_browser__debuginfo_warning(struct annotate_browser *browser)
832 {
833 	struct map_symbol *ms = browser->b.priv;
834 	struct dso *dso = map__dso(ms->map);
835 
836 	if (browser->dbg == NULL && annotate_opts.code_with_type &&
837 	    !dso__debuginfo_warned(dso)) {
838 		ui__warning("DWARF debuginfo not found.\n\n"
839 			    "Data-type in this DSO will not be displayed.\n"
840 			    "Please make sure to have debug information.");
841 		dso__set_debuginfo_warned(dso);
842 	}
843 }
844 
845 static int annotate_browser__run(struct annotate_browser *browser,
846 				 struct evsel *evsel,
847 				 struct hist_browser_timer *hbt)
848 {
849 	struct rb_node *nd = NULL;
850 	struct hists *hists = evsel__hists(evsel);
851 	struct map_symbol *ms = browser->b.priv;
852 	struct symbol *sym = ms->sym;
853 	struct annotation *notes = symbol__annotation(ms->sym);
854 	const char *help = "Press 'h' for help on key bindings";
855 	int delay_secs = hbt ? hbt->refresh : 0;
856 	char *br_cntr_text = NULL;
857 	char title[256];
858 	int key;
859 
860 	annotate__scnprintf_title(hists, title, sizeof(title));
861 	if (annotate_browser__show(&browser->b, title, help) < 0)
862 		return -1;
863 
864 	annotate_browser__calc_percent(browser, evsel);
865 
866 	if (browser->curr_hot) {
867 		annotate_browser__set_rb_top(browser, browser->curr_hot);
868 		browser->b.navkeypressed = false;
869 	}
870 
871 	nd = browser->curr_hot;
872 
873 	annotation_br_cntr_abbr_list(&br_cntr_text, evsel, false);
874 
875 	annotate_browser__debuginfo_warning(browser);
876 
877 	while (1) {
878 		key = ui_browser__run(&browser->b, delay_secs);
879 
880 		if (delay_secs != 0) {
881 			annotate_browser__calc_percent(browser, evsel);
882 			/*
883 			 * Current line focus got out of the list of most active
884 			 * lines, NULL it so that if TAB|UNTAB is pressed, we
885 			 * move to curr_hot (current hottest line).
886 			 */
887 			if (nd != NULL && RB_EMPTY_NODE(nd))
888 				nd = NULL;
889 		}
890 
891 		switch (key) {
892 		case K_TIMER:
893 			if (hbt)
894 				hbt->timer(hbt->arg);
895 
896 			if (delay_secs != 0) {
897 				symbol__annotate_decay_histogram(sym, evsel);
898 				annotate__scnprintf_title(hists, title, sizeof(title));
899 				annotate_browser__show(&browser->b, title, help);
900 			}
901 			continue;
902 		case K_TAB:
903 			if (nd != NULL) {
904 				nd = rb_prev(nd);
905 				if (nd == NULL)
906 					nd = rb_last(&browser->entries);
907 			} else
908 				nd = browser->curr_hot;
909 			break;
910 		case K_UNTAB:
911 			if (nd != NULL) {
912 				nd = rb_next(nd);
913 				if (nd == NULL)
914 					nd = rb_first(&browser->entries);
915 			} else
916 				nd = browser->curr_hot;
917 			break;
918 		case K_F1:
919 		case 'h':
920 			ui_browser__help_window(&browser->b,
921 		"UP/DOWN/PGUP\n"
922 		"PGDN/SPACE    Navigate\n"
923 		"</>           Move to prev/next symbol\n"
924 		"q/ESC/CTRL+C  Exit\n\n"
925 		"ENTER         Go to target\n"
926 		"H             Go to hottest instruction\n"
927 		"TAB/shift+TAB Cycle thru hottest instructions\n"
928 		"j             Toggle showing jump to target arrows\n"
929 		"J             Toggle showing number of jump sources on targets\n"
930 		"n             Search next string\n"
931 		"o             Toggle disassembler output/simplified view\n"
932 		"O             Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
933 		"s             Toggle source code view\n"
934 		"t             Circulate percent, total period, samples view\n"
935 		"c             Show min/max cycle\n"
936 		"/             Search string\n"
937 		"k             Toggle line numbers\n"
938 		"l             Show full source file location\n"
939 		"P             Print to [symbol_name].annotation file.\n"
940 		"r             Run available scripts\n"
941 		"p             Toggle percent type [local/global]\n"
942 		"b             Toggle percent base [period/hits]\n"
943 		"B             Branch counter abbr list (Optional)\n"
944 		"?             Search string backwards\n"
945 		"f             Toggle showing offsets to full address\n"
946 		"T             Toggle data type display\n");
947 			continue;
948 		case 'r':
949 			script_browse(NULL, NULL);
950 			annotate_browser__show(&browser->b, title, help);
951 			continue;
952 		case 'k':
953 			annotate_opts.show_linenr = !annotate_opts.show_linenr;
954 			continue;
955 		case 'l':
956 			annotate_browser__show_full_location (&browser->b);
957 			continue;
958 		case 'H':
959 			nd = browser->curr_hot;
960 			break;
961 		case 's':
962 			if (annotate_browser__toggle_source(browser, evsel))
963 				ui_helpline__puts(help);
964 			annotate__scnprintf_title(hists, title, sizeof(title));
965 			annotate_browser__show(&browser->b, title, help);
966 			continue;
967 		case 'o':
968 			annotate_opts.use_offset = !annotate_opts.use_offset;
969 			annotation__update_column_widths(notes);
970 			continue;
971 		case 'O':
972 			if (++annotate_opts.offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
973 				annotate_opts.offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
974 			continue;
975 		case 'j':
976 			annotate_opts.jump_arrows = !annotate_opts.jump_arrows;
977 			continue;
978 		case 'J':
979 			annotate_opts.show_nr_jumps = !annotate_opts.show_nr_jumps;
980 			annotation__update_column_widths(notes);
981 			continue;
982 		case '/':
983 			if (annotate_browser__search(browser, delay_secs)) {
984 show_help:
985 				ui_helpline__puts(help);
986 			}
987 			continue;
988 		case 'n':
989 			if (browser->searching_backwards ?
990 			    annotate_browser__continue_search_reverse(browser, delay_secs) :
991 			    annotate_browser__continue_search(browser, delay_secs))
992 				goto show_help;
993 			continue;
994 		case '?':
995 			if (annotate_browser__search_reverse(browser, delay_secs))
996 				goto show_help;
997 			continue;
998 		case 'D': {
999 			static int seq;
1000 			ui_helpline__pop();
1001 			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
1002 					   seq++, browser->b.nr_entries,
1003 					   browser->b.height,
1004 					   browser->b.index,
1005 					   browser->b.top_idx,
1006 					   notes->src->nr_asm_entries);
1007 		}
1008 			continue;
1009 		case K_ENTER:
1010 		case K_RIGHT:
1011 		{
1012 			struct disasm_line *dl = disasm_line(browser->selection);
1013 
1014 			if (browser->selection == NULL)
1015 				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
1016 			else if (browser->selection->offset == -1)
1017 				ui_helpline__puts("Actions are only available for assembly lines.");
1018 			else if (!dl->ins.ops)
1019 				goto show_sup_ins;
1020 			else if (ins__is_ret(&dl->ins))
1021 				goto out;
1022 			else if (!(annotate_browser__jump(browser, evsel, hbt) ||
1023 				     annotate_browser__callq(browser, evsel, hbt))) {
1024 show_sup_ins:
1025 				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
1026 			}
1027 			continue;
1028 		}
1029 		case 'P':
1030 			map_symbol__annotation_dump(ms, evsel, browser->he);
1031 			continue;
1032 		case 't':
1033 			if (symbol_conf.show_total_period) {
1034 				symbol_conf.show_total_period = false;
1035 				symbol_conf.show_nr_samples = true;
1036 			} else if (symbol_conf.show_nr_samples)
1037 				symbol_conf.show_nr_samples = false;
1038 			else
1039 				symbol_conf.show_total_period = true;
1040 			annotation__update_column_widths(notes);
1041 			continue;
1042 		case 'c':
1043 			if (annotate_opts.show_minmax_cycle)
1044 				annotate_opts.show_minmax_cycle = false;
1045 			else
1046 				annotate_opts.show_minmax_cycle = true;
1047 			annotation__update_column_widths(notes);
1048 			continue;
1049 		case 'p':
1050 		case 'b':
1051 			switch_percent_type(&annotate_opts, key == 'b');
1052 			annotate__scnprintf_title(hists, title, sizeof(title));
1053 			annotate_browser__show(&browser->b, title, help);
1054 			continue;
1055 		case 'B':
1056 			if (br_cntr_text)
1057 				ui_browser__help_window(&browser->b, br_cntr_text);
1058 			else {
1059 				ui_browser__help_window(&browser->b,
1060 							"\n The branch counter is not available.\n");
1061 			}
1062 			continue;
1063 		case 'f':
1064 			annotation__toggle_full_addr(notes, ms);
1065 			continue;
1066 		case 'T':
1067 			annotate_opts.code_with_type ^= 1;
1068 			if (browser->dbg == NULL)
1069 				browser->dbg = dso__debuginfo(map__dso(ms->map));
1070 			if (browser->type_hash == NULL) {
1071 				browser->type_hash = hashmap__new(type_hash, type_equal,
1072 								  /*ctx=*/NULL);
1073 			}
1074 			annotate_browser__show(&browser->b, title, help);
1075 			annotate_browser__debuginfo_warning(browser);
1076 			continue;
1077 		case K_LEFT:
1078 		case '<':
1079 		case '>':
1080 		case K_ESC:
1081 		case 'q':
1082 		case CTRL('c'):
1083 			goto out;
1084 		default:
1085 			ui_browser__warn_unhandled_hotkey(&browser->b, key, delay_secs, ", use 'h'/F1 to see actions");
1086 			continue;
1087 		}
1088 
1089 		if (nd != NULL)
1090 			annotate_browser__set_rb_top(browser, nd);
1091 	}
1092 out:
1093 	ui_browser__hide(&browser->b);
1094 	free(br_cntr_text);
1095 	return key;
1096 }
1097 
1098 int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
1099 			     struct hist_browser_timer *hbt)
1100 {
1101 	/* reset abort key so that it can get Ctrl-C as a key */
1102 	SLang_reset_tty();
1103 	SLang_init_tty(0, 0, 0);
1104 	SLtty_set_suspend_state(true);
1105 
1106 	return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt);
1107 }
1108 
1109 int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
1110 			       struct evsel *evsel,
1111 			       struct hist_browser_timer *hbt)
1112 {
1113 	struct symbol *sym = ms->sym;
1114 	struct annotation *notes = symbol__annotation(sym);
1115 	struct annotate_browser browser = {
1116 		.b = {
1117 			.refresh = annotate_browser__refresh,
1118 			.seek	 = ui_browser__list_head_seek,
1119 			.write	 = annotate_browser__write,
1120 			.filter  = disasm_line__filter,
1121 			.extra_title_lines = 1, /* for hists__scnprintf_title() */
1122 			.priv	 = ms,
1123 			.use_navkeypressed = true,
1124 		},
1125 		.he = he,
1126 		.evsel = evsel,
1127 	};
1128 	struct dso *dso;
1129 	int ret = -1, err;
1130 	int not_annotated = list_empty(&notes->src->source);
1131 
1132 	if (sym == NULL)
1133 		return -1;
1134 
1135 	dso = map__dso(ms->map);
1136 	if (dso__annotate_warned(dso))
1137 		return -1;
1138 
1139 	if (not_annotated || !sym->annotate2) {
1140 		err = symbol__annotate2(ms, evsel, &browser.arch);
1141 		if (err) {
1142 			char msg[BUFSIZ];
1143 			dso__set_annotate_warned(dso);
1144 			symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
1145 			ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
1146 			return -1;
1147 		}
1148 
1149 		if (!annotate_opts.hide_src_code) {
1150 			notes->src->tried_source = true;
1151 			if (!annotation__has_source(notes))
1152 				ui__warning("Annotation has no source code.");
1153 		}
1154 	}
1155 
1156 	/* Copy necessary information when it's called from perf top */
1157 	if (hbt != NULL && he != &annotate_he) {
1158 		annotate_he.hists = he->hists;
1159 		annotate_he.thread = thread__get(he->thread);
1160 		annotate_he.cpumode = he->cpumode;
1161 		map_symbol__copy(&annotate_he.ms, ms);
1162 
1163 		browser.he = &annotate_he;
1164 	}
1165 
1166 	ui_helpline__push("Press ESC to exit");
1167 
1168 	if (annotate_opts.code_with_type) {
1169 		browser.dbg = dso__debuginfo(dso);
1170 		browser.type_hash = hashmap__new(type_hash, type_equal, /*ctx=*/NULL);
1171 	}
1172 
1173 	browser.b.width = notes->src->widths.max_line_len;
1174 	browser.b.nr_entries = notes->src->nr_entries;
1175 	browser.b.entries = &notes->src->source;
1176 	browser.b.width += 18; /* Percentage */
1177 
1178 	if (annotate_opts.hide_src_code)
1179 		ui_browser__init_asm_mode(&browser.b);
1180 
1181 	ret = annotate_browser__run(&browser, evsel, hbt);
1182 
1183 	debuginfo__delete(browser.dbg);
1184 
1185 	if (!IS_ERR_OR_NULL(browser.type_hash)) {
1186 		struct hashmap_entry *cur;
1187 		size_t bkt;
1188 
1189 		hashmap__for_each_entry(browser.type_hash, cur, bkt)
1190 			zfree(&cur->pvalue);
1191 		hashmap__free(browser.type_hash);
1192 	}
1193 
1194 	if (not_annotated && !notes->src->tried_source)
1195 		annotated_source__purge(notes->src);
1196 
1197 	if (hbt != NULL && he != &annotate_he) {
1198 		thread__zput(annotate_he.thread);
1199 		map_symbol__exit(&annotate_he.ms);
1200 	}
1201 
1202 	return ret;
1203 }
1204