xref: /openbmc/linux/tools/perf/ui/browsers/annotate.c (revision 82e6fdd6)
1 // SPDX-License-Identifier: GPL-2.0
2 #include "../../util/util.h"
3 #include "../browser.h"
4 #include "../helpline.h"
5 #include "../ui.h"
6 #include "../util.h"
7 #include "../../util/annotate.h"
8 #include "../../util/hist.h"
9 #include "../../util/sort.h"
10 #include "../../util/symbol.h"
11 #include "../../util/evsel.h"
12 #include "../../util/config.h"
13 #include "../../util/evlist.h"
14 #include <inttypes.h>
15 #include <pthread.h>
16 #include <linux/kernel.h>
17 #include <linux/string.h>
18 #include <sys/ttydefaults.h>
19 
20 struct disasm_line_samples {
21 	double		      percent;
22 	struct sym_hist_entry he;
23 };
24 
25 #define IPC_WIDTH 6
26 #define CYCLES_WIDTH 6
27 
28 struct browser_line {
29 	u32	idx;
30 	int	idx_asm;
31 	int	jump_sources;
32 };
33 
34 static struct annotate_browser_opt {
35 	bool hide_src_code,
36 	     use_offset,
37 	     jump_arrows,
38 	     show_linenr,
39 	     show_nr_jumps,
40 	     show_nr_samples,
41 	     show_total_period;
42 } annotate_browser__opts = {
43 	.use_offset	= true,
44 	.jump_arrows	= true,
45 };
46 
47 struct arch;
48 
49 struct annotate_browser {
50 	struct ui_browser	    b;
51 	struct rb_root		    entries;
52 	struct rb_node		   *curr_hot;
53 	struct annotation_line	   *selection;
54 	struct annotation_line	  **offsets;
55 	struct arch		   *arch;
56 	int			    nr_events;
57 	u64			    start;
58 	int			    nr_asm_entries;
59 	int			    nr_entries;
60 	int			    max_jump_sources;
61 	int			    nr_jumps;
62 	bool			    searching_backwards;
63 	bool			    have_cycles;
64 	u8			    addr_width;
65 	u8			    jumps_width;
66 	u8			    target_width;
67 	u8			    min_addr_width;
68 	u8			    max_addr_width;
69 	char			    search_bf[128];
70 };
71 
72 static inline struct browser_line *browser_line(struct annotation_line *al)
73 {
74 	void *ptr = al;
75 
76 	ptr = container_of(al, struct disasm_line, al);
77 	return ptr - sizeof(struct browser_line);
78 }
79 
80 static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
81 				void *entry)
82 {
83 	if (annotate_browser__opts.hide_src_code) {
84 		struct annotation_line *al = list_entry(entry, struct annotation_line, node);
85 
86 		return al->offset == -1;
87 	}
88 
89 	return false;
90 }
91 
92 static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
93 						 int nr, bool current)
94 {
95 	if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
96 		return HE_COLORSET_SELECTED;
97 	if (nr == browser->max_jump_sources)
98 		return HE_COLORSET_TOP;
99 	if (nr > 1)
100 		return HE_COLORSET_MEDIUM;
101 	return HE_COLORSET_NORMAL;
102 }
103 
104 static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
105 						     int nr, bool current)
106 {
107 	 int color = annotate_browser__jumps_percent_color(browser, nr, current);
108 	 return ui_browser__set_color(&browser->b, color);
109 }
110 
111 static int annotate_browser__pcnt_width(struct annotate_browser *ab)
112 {
113 	return (annotate_browser__opts.show_total_period ? 12 : 7) * ab->nr_events;
114 }
115 
116 static int annotate_browser__cycles_width(struct annotate_browser *ab)
117 {
118 	return ab->have_cycles ? IPC_WIDTH + CYCLES_WIDTH : 0;
119 }
120 
121 static void disasm_line__write(struct disasm_line *dl, struct ui_browser *browser,
122 			       char *bf, size_t size)
123 {
124 	if (dl->ins.ops && dl->ins.ops->scnprintf) {
125 		if (ins__is_jump(&dl->ins)) {
126 			bool fwd = dl->ops.target.offset > dl->al.offset;
127 
128 			ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
129 							    SLSMG_UARROW_CHAR);
130 			SLsmg_write_char(' ');
131 		} else if (ins__is_call(&dl->ins)) {
132 			ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
133 			SLsmg_write_char(' ');
134 		} else if (ins__is_ret(&dl->ins)) {
135 			ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
136 			SLsmg_write_char(' ');
137 		} else {
138 			ui_browser__write_nstring(browser, " ", 2);
139 		}
140 	} else {
141 		ui_browser__write_nstring(browser, " ", 2);
142 	}
143 
144 	disasm_line__scnprintf(dl, bf, size, !annotate_browser__opts.use_offset);
145 }
146 
147 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
148 {
149 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
150 	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
151 	struct browser_line *bl = browser_line(al);
152 	bool current_entry = ui_browser__is_current_entry(browser, row);
153 	bool change_color = (!annotate_browser__opts.hide_src_code &&
154 			     (!current_entry || (browser->use_navkeypressed &&
155 					         !browser->navkeypressed)));
156 	int width = browser->width, printed;
157 	int i, pcnt_width = annotate_browser__pcnt_width(ab),
158 	       cycles_width = annotate_browser__cycles_width(ab);
159 	double percent_max = 0.0;
160 	char bf[256];
161 	bool show_title = false;
162 
163 	for (i = 0; i < ab->nr_events; i++) {
164 		if (al->samples[i].percent > percent_max)
165 			percent_max = al->samples[i].percent;
166 	}
167 
168 	if ((row == 0) && (al->offset == -1 || percent_max == 0.0)) {
169 		if (ab->have_cycles) {
170 			if (al->ipc == 0.0 && al->cycles == 0)
171 				show_title = true;
172 		} else
173 			show_title = true;
174 	}
175 
176 	if (al->offset != -1 && percent_max != 0.0) {
177 		for (i = 0; i < ab->nr_events; i++) {
178 			ui_browser__set_percent_color(browser,
179 						al->samples[i].percent,
180 						current_entry);
181 			if (annotate_browser__opts.show_total_period) {
182 				ui_browser__printf(browser, "%11" PRIu64 " ",
183 						   al->samples[i].he.period);
184 			} else if (annotate_browser__opts.show_nr_samples) {
185 				ui_browser__printf(browser, "%6" PRIu64 " ",
186 						   al->samples[i].he.nr_samples);
187 			} else {
188 				ui_browser__printf(browser, "%6.2f ",
189 						   al->samples[i].percent);
190 			}
191 		}
192 	} else {
193 		ui_browser__set_percent_color(browser, 0, current_entry);
194 
195 		if (!show_title)
196 			ui_browser__write_nstring(browser, " ", pcnt_width);
197 		else {
198 			ui_browser__printf(browser, "%*s", pcnt_width,
199 					   annotate_browser__opts.show_total_period ? "Period" :
200 					   annotate_browser__opts.show_nr_samples ? "Samples" : "Percent");
201 		}
202 	}
203 	if (ab->have_cycles) {
204 		if (al->ipc)
205 			ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, al->ipc);
206 		else if (!show_title)
207 			ui_browser__write_nstring(browser, " ", IPC_WIDTH);
208 		else
209 			ui_browser__printf(browser, "%*s ", IPC_WIDTH - 1, "IPC");
210 
211 		if (al->cycles)
212 			ui_browser__printf(browser, "%*" PRIu64 " ",
213 					   CYCLES_WIDTH - 1, al->cycles);
214 		else if (!show_title)
215 			ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
216 		else
217 			ui_browser__printf(browser, "%*s ", CYCLES_WIDTH - 1, "Cycle");
218 	}
219 
220 	SLsmg_write_char(' ');
221 
222 	/* The scroll bar isn't being used */
223 	if (!browser->navkeypressed)
224 		width += 1;
225 
226 	if (!*al->line)
227 		ui_browser__write_nstring(browser, " ", width - pcnt_width - cycles_width);
228 	else if (al->offset == -1) {
229 		if (al->line_nr && annotate_browser__opts.show_linenr)
230 			printed = scnprintf(bf, sizeof(bf), "%-*d ",
231 					ab->addr_width + 1, al->line_nr);
232 		else
233 			printed = scnprintf(bf, sizeof(bf), "%*s  ",
234 				    ab->addr_width, " ");
235 		ui_browser__write_nstring(browser, bf, printed);
236 		ui_browser__write_nstring(browser, al->line, width - printed - pcnt_width - cycles_width + 1);
237 	} else {
238 		u64 addr = al->offset;
239 		int color = -1;
240 
241 		if (!annotate_browser__opts.use_offset)
242 			addr += ab->start;
243 
244 		if (!annotate_browser__opts.use_offset) {
245 			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
246 		} else {
247 			if (bl->jump_sources) {
248 				if (annotate_browser__opts.show_nr_jumps) {
249 					int prev;
250 					printed = scnprintf(bf, sizeof(bf), "%*d ",
251 							    ab->jumps_width,
252 							    bl->jump_sources);
253 					prev = annotate_browser__set_jumps_percent_color(ab, bl->jump_sources,
254 											 current_entry);
255 					ui_browser__write_nstring(browser, bf, printed);
256 					ui_browser__set_color(browser, prev);
257 				}
258 
259 				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
260 						    ab->target_width, addr);
261 			} else {
262 				printed = scnprintf(bf, sizeof(bf), "%*s  ",
263 						    ab->addr_width, " ");
264 			}
265 		}
266 
267 		if (change_color)
268 			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
269 		ui_browser__write_nstring(browser, bf, printed);
270 		if (change_color)
271 			ui_browser__set_color(browser, color);
272 
273 		disasm_line__write(disasm_line(al), browser, bf, sizeof(bf));
274 
275 		ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
276 	}
277 
278 	if (current_entry)
279 		ab->selection = al;
280 }
281 
282 static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
283 {
284 	if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins)
285 	    || !disasm_line__has_offset(dl)
286 	    || dl->ops.target.offset < 0
287 	    || dl->ops.target.offset >= (s64)symbol__size(sym))
288 		return false;
289 
290 	return true;
291 }
292 
293 static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
294 {
295 	struct disasm_line *pos = list_prev_entry(cursor, al.node);
296 	const char *name;
297 
298 	if (!pos)
299 		return false;
300 
301 	if (ins__is_lock(&pos->ins))
302 		name = pos->ops.locked.ins.name;
303 	else
304 		name = pos->ins.name;
305 
306 	if (!name || !cursor->ins.name)
307 		return false;
308 
309 	return ins__is_fused(ab->arch, name, cursor->ins.name);
310 }
311 
312 static void annotate_browser__draw_current_jump(struct ui_browser *browser)
313 {
314 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
315 	struct disasm_line *cursor = disasm_line(ab->selection);
316 	struct annotation_line *target;
317 	struct browser_line *btarget, *bcursor;
318 	unsigned int from, to;
319 	struct map_symbol *ms = ab->b.priv;
320 	struct symbol *sym = ms->sym;
321 	u8 pcnt_width = annotate_browser__pcnt_width(ab);
322 	int width = 0;
323 
324 	/* PLT symbols contain external offsets */
325 	if (strstr(sym->name, "@plt"))
326 		return;
327 
328 	if (!disasm_line__is_valid_jump(cursor, sym))
329 		return;
330 
331 	/*
332 	 * This first was seen with a gcc function, _cpp_lex_token, that
333 	 * has the usual jumps:
334 	 *
335 	 *  │1159e6c: ↓ jne    115aa32 <_cpp_lex_token@@Base+0xf92>
336 	 *
337 	 * I.e. jumps to a label inside that function (_cpp_lex_token), and
338 	 * those works, but also this kind:
339 	 *
340 	 *  │1159e8b: ↓ jne    c469be <cpp_named_operator2name@@Base+0xa72>
341 	 *
342 	 *  I.e. jumps to another function, outside _cpp_lex_token, which
343 	 *  are not being correctly handled generating as a side effect references
344 	 *  to ab->offset[] entries that are set to NULL, so to make this code
345 	 *  more robust, check that here.
346 	 *
347 	 *  A proper fix for will be put in place, looking at the function
348 	 *  name right after the '<' token and probably treating this like a
349 	 *  'call' instruction.
350 	 */
351 	target = ab->offsets[cursor->ops.target.offset];
352 	if (target == NULL) {
353 		ui_helpline__printf("WARN: jump target inconsistency, press 'o', ab->offsets[%#x] = NULL\n",
354 				    cursor->ops.target.offset);
355 		return;
356 	}
357 
358 	bcursor = browser_line(&cursor->al);
359 	btarget = browser_line(target);
360 
361 	if (annotate_browser__opts.hide_src_code) {
362 		from = bcursor->idx_asm;
363 		to = btarget->idx_asm;
364 	} else {
365 		from = (u64)bcursor->idx;
366 		to = (u64)btarget->idx;
367 	}
368 
369 	if (ab->have_cycles)
370 		width = IPC_WIDTH + CYCLES_WIDTH;
371 
372 	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
373 	__ui_browser__line_arrow(browser,
374 				 pcnt_width + 2 + ab->addr_width + width,
375 				 from, to);
376 
377 	if (is_fused(ab, cursor)) {
378 		ui_browser__mark_fused(browser,
379 				       pcnt_width + 3 + ab->addr_width + width,
380 				       from - 1,
381 				       to > from ? true : false);
382 	}
383 }
384 
385 static unsigned int annotate_browser__refresh(struct ui_browser *browser)
386 {
387 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
388 	int ret = ui_browser__list_head_refresh(browser);
389 	int pcnt_width = annotate_browser__pcnt_width(ab);
390 
391 	if (annotate_browser__opts.jump_arrows)
392 		annotate_browser__draw_current_jump(browser);
393 
394 	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
395 	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
396 	return ret;
397 }
398 
399 static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
400 {
401 	int i;
402 
403 	for (i = 0; i < a->samples_nr; i++) {
404 		if (a->samples[i].percent == b->samples[i].percent)
405 			continue;
406 		return a->samples[i].percent < b->samples[i].percent;
407 	}
408 	return 0;
409 }
410 
411 static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
412 {
413 	struct rb_node **p = &root->rb_node;
414 	struct rb_node *parent = NULL;
415 	struct annotation_line *l;
416 
417 	while (*p != NULL) {
418 		parent = *p;
419 		l = rb_entry(parent, struct annotation_line, rb_node);
420 
421 		if (disasm__cmp(al, l))
422 			p = &(*p)->rb_left;
423 		else
424 			p = &(*p)->rb_right;
425 	}
426 	rb_link_node(&al->rb_node, parent, p);
427 	rb_insert_color(&al->rb_node, root);
428 }
429 
430 static void annotate_browser__set_top(struct annotate_browser *browser,
431 				      struct annotation_line *pos, u32 idx)
432 {
433 	unsigned back;
434 
435 	ui_browser__refresh_dimensions(&browser->b);
436 	back = browser->b.height / 2;
437 	browser->b.top_idx = browser->b.index = idx;
438 
439 	while (browser->b.top_idx != 0 && back != 0) {
440 		pos = list_entry(pos->node.prev, struct annotation_line, node);
441 
442 		if (disasm_line__filter(&browser->b, &pos->node))
443 			continue;
444 
445 		--browser->b.top_idx;
446 		--back;
447 	}
448 
449 	browser->b.top = pos;
450 	browser->b.navkeypressed = true;
451 }
452 
453 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
454 					 struct rb_node *nd)
455 {
456 	struct browser_line *bpos;
457 	struct annotation_line *pos;
458 	u32 idx;
459 
460 	pos = rb_entry(nd, struct annotation_line, rb_node);
461 	bpos = browser_line(pos);
462 
463 	idx = bpos->idx;
464 	if (annotate_browser__opts.hide_src_code)
465 		idx = bpos->idx_asm;
466 	annotate_browser__set_top(browser, pos, idx);
467 	browser->curr_hot = nd;
468 }
469 
470 static void annotate_browser__calc_percent(struct annotate_browser *browser,
471 					   struct perf_evsel *evsel)
472 {
473 	struct map_symbol *ms = browser->b.priv;
474 	struct symbol *sym = ms->sym;
475 	struct annotation *notes = symbol__annotation(sym);
476 	struct disasm_line *pos;
477 
478 	browser->entries = RB_ROOT;
479 
480 	pthread_mutex_lock(&notes->lock);
481 
482 	symbol__calc_percent(sym, evsel);
483 
484 	list_for_each_entry(pos, &notes->src->source, al.node) {
485 		double max_percent = 0.0;
486 		int i;
487 
488 		if (pos->al.offset == -1) {
489 			RB_CLEAR_NODE(&pos->al.rb_node);
490 			continue;
491 		}
492 
493 		for (i = 0; i < pos->al.samples_nr; i++) {
494 			struct annotation_data *sample = &pos->al.samples[i];
495 
496 			if (max_percent < sample->percent)
497 				max_percent = sample->percent;
498 		}
499 
500 		if (max_percent < 0.01 && pos->al.ipc == 0) {
501 			RB_CLEAR_NODE(&pos->al.rb_node);
502 			continue;
503 		}
504 		disasm_rb_tree__insert(&browser->entries, &pos->al);
505 	}
506 	pthread_mutex_unlock(&notes->lock);
507 
508 	browser->curr_hot = rb_last(&browser->entries);
509 }
510 
511 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
512 {
513 	struct annotation_line *al;
514 	struct browser_line *bl;
515 	off_t offset = browser->b.index - browser->b.top_idx;
516 
517 	browser->b.seek(&browser->b, offset, SEEK_CUR);
518 	al = list_entry(browser->b.top, struct annotation_line, node);
519 	bl = browser_line(al);
520 
521 	if (annotate_browser__opts.hide_src_code) {
522 		if (bl->idx_asm < offset)
523 			offset = bl->idx;
524 
525 		browser->b.nr_entries = browser->nr_entries;
526 		annotate_browser__opts.hide_src_code = false;
527 		browser->b.seek(&browser->b, -offset, SEEK_CUR);
528 		browser->b.top_idx = bl->idx - offset;
529 		browser->b.index = bl->idx;
530 	} else {
531 		if (bl->idx_asm < 0) {
532 			ui_helpline__puts("Only available for assembly lines.");
533 			browser->b.seek(&browser->b, -offset, SEEK_CUR);
534 			return false;
535 		}
536 
537 		if (bl->idx_asm < offset)
538 			offset = bl->idx_asm;
539 
540 		browser->b.nr_entries = browser->nr_asm_entries;
541 		annotate_browser__opts.hide_src_code = true;
542 		browser->b.seek(&browser->b, -offset, SEEK_CUR);
543 		browser->b.top_idx = bl->idx_asm - offset;
544 		browser->b.index = bl->idx_asm;
545 	}
546 
547 	return true;
548 }
549 
550 static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
551 {
552 	ui_browser__reset_index(&browser->b);
553 	browser->b.nr_entries = browser->nr_asm_entries;
554 }
555 
556 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
557 
558 static int sym_title(struct symbol *sym, struct map *map, char *title,
559 		     size_t sz)
560 {
561 	return snprintf(title, sz, "%s  %s", sym->name, map->dso->long_name);
562 }
563 
564 static bool annotate_browser__callq(struct annotate_browser *browser,
565 				    struct perf_evsel *evsel,
566 				    struct hist_browser_timer *hbt)
567 {
568 	struct map_symbol *ms = browser->b.priv;
569 	struct disasm_line *dl = disasm_line(browser->selection);
570 	struct annotation *notes;
571 	char title[SYM_TITLE_MAX_SIZE];
572 
573 	if (!ins__is_call(&dl->ins))
574 		return false;
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 	pthread_mutex_lock(&notes->lock);
583 
584 	if (notes->src == NULL && symbol__alloc_hist(dl->ops.target.sym) < 0) {
585 		pthread_mutex_unlock(&notes->lock);
586 		ui__warning("Not enough memory for annotating '%s' symbol!\n",
587 			    dl->ops.target.sym->name);
588 		return true;
589 	}
590 
591 	pthread_mutex_unlock(&notes->lock);
592 	symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt);
593 	sym_title(ms->sym, ms->map, title, sizeof(title));
594 	ui_browser__show_title(&browser->b, title);
595 	return true;
596 }
597 
598 static
599 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
600 					  s64 offset, s64 *idx)
601 {
602 	struct map_symbol *ms = browser->b.priv;
603 	struct symbol *sym = ms->sym;
604 	struct annotation *notes = symbol__annotation(sym);
605 	struct disasm_line *pos;
606 
607 	*idx = 0;
608 	list_for_each_entry(pos, &notes->src->source, al.node) {
609 		if (pos->al.offset == offset)
610 			return pos;
611 		if (!disasm_line__filter(&browser->b, &pos->al.node))
612 			++*idx;
613 	}
614 
615 	return NULL;
616 }
617 
618 static bool annotate_browser__jump(struct annotate_browser *browser)
619 {
620 	struct disasm_line *dl = disasm_line(browser->selection);
621 	u64 offset;
622 	s64 idx;
623 
624 	if (!ins__is_jump(&dl->ins))
625 		return false;
626 
627 	offset = dl->ops.target.offset;
628 	dl = annotate_browser__find_offset(browser, offset, &idx);
629 	if (dl == NULL) {
630 		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
631 		return true;
632 	}
633 
634 	annotate_browser__set_top(browser, &dl->al, idx);
635 
636 	return true;
637 }
638 
639 static
640 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
641 					  char *s, s64 *idx)
642 {
643 	struct map_symbol *ms = browser->b.priv;
644 	struct symbol *sym = ms->sym;
645 	struct annotation *notes = symbol__annotation(sym);
646 	struct annotation_line *al = browser->selection;
647 
648 	*idx = browser->b.index;
649 	list_for_each_entry_continue(al, &notes->src->source, node) {
650 		if (disasm_line__filter(&browser->b, &al->node))
651 			continue;
652 
653 		++*idx;
654 
655 		if (al->line && strstr(al->line, s) != NULL)
656 			return al;
657 	}
658 
659 	return NULL;
660 }
661 
662 static bool __annotate_browser__search(struct annotate_browser *browser)
663 {
664 	struct annotation_line *al;
665 	s64 idx;
666 
667 	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
668 	if (al == NULL) {
669 		ui_helpline__puts("String not found!");
670 		return false;
671 	}
672 
673 	annotate_browser__set_top(browser, al, idx);
674 	browser->searching_backwards = false;
675 	return true;
676 }
677 
678 static
679 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
680 						  char *s, s64 *idx)
681 {
682 	struct map_symbol *ms = browser->b.priv;
683 	struct symbol *sym = ms->sym;
684 	struct annotation *notes = symbol__annotation(sym);
685 	struct annotation_line *al = browser->selection;
686 
687 	*idx = browser->b.index;
688 	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
689 		if (disasm_line__filter(&browser->b, &al->node))
690 			continue;
691 
692 		--*idx;
693 
694 		if (al->line && strstr(al->line, s) != NULL)
695 			return al;
696 	}
697 
698 	return NULL;
699 }
700 
701 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
702 {
703 	struct annotation_line *al;
704 	s64 idx;
705 
706 	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
707 	if (al == NULL) {
708 		ui_helpline__puts("String not found!");
709 		return false;
710 	}
711 
712 	annotate_browser__set_top(browser, al, idx);
713 	browser->searching_backwards = true;
714 	return true;
715 }
716 
717 static bool annotate_browser__search_window(struct annotate_browser *browser,
718 					    int delay_secs)
719 {
720 	if (ui_browser__input_window("Search", "String: ", browser->search_bf,
721 				     "ENTER: OK, ESC: Cancel",
722 				     delay_secs * 2) != K_ENTER ||
723 	    !*browser->search_bf)
724 		return false;
725 
726 	return true;
727 }
728 
729 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
730 {
731 	if (annotate_browser__search_window(browser, delay_secs))
732 		return __annotate_browser__search(browser);
733 
734 	return false;
735 }
736 
737 static bool annotate_browser__continue_search(struct annotate_browser *browser,
738 					      int delay_secs)
739 {
740 	if (!*browser->search_bf)
741 		return annotate_browser__search(browser, delay_secs);
742 
743 	return __annotate_browser__search(browser);
744 }
745 
746 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
747 					   int delay_secs)
748 {
749 	if (annotate_browser__search_window(browser, delay_secs))
750 		return __annotate_browser__search_reverse(browser);
751 
752 	return false;
753 }
754 
755 static
756 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
757 					       int delay_secs)
758 {
759 	if (!*browser->search_bf)
760 		return annotate_browser__search_reverse(browser, delay_secs);
761 
762 	return __annotate_browser__search_reverse(browser);
763 }
764 
765 static void annotate_browser__update_addr_width(struct annotate_browser *browser)
766 {
767 	if (annotate_browser__opts.use_offset)
768 		browser->target_width = browser->min_addr_width;
769 	else
770 		browser->target_width = browser->max_addr_width;
771 
772 	browser->addr_width = browser->target_width;
773 
774 	if (annotate_browser__opts.show_nr_jumps)
775 		browser->addr_width += browser->jumps_width + 1;
776 }
777 
778 static int annotate_browser__run(struct annotate_browser *browser,
779 				 struct perf_evsel *evsel,
780 				 struct hist_browser_timer *hbt)
781 {
782 	struct rb_node *nd = NULL;
783 	struct map_symbol *ms = browser->b.priv;
784 	struct symbol *sym = ms->sym;
785 	const char *help = "Press 'h' for help on key bindings";
786 	int delay_secs = hbt ? hbt->refresh : 0;
787 	int key;
788 	char title[SYM_TITLE_MAX_SIZE];
789 
790 	sym_title(sym, ms->map, title, sizeof(title));
791 	if (ui_browser__show(&browser->b, title, help) < 0)
792 		return -1;
793 
794 	annotate_browser__calc_percent(browser, evsel);
795 
796 	if (browser->curr_hot) {
797 		annotate_browser__set_rb_top(browser, browser->curr_hot);
798 		browser->b.navkeypressed = false;
799 	}
800 
801 	nd = browser->curr_hot;
802 
803 	while (1) {
804 		key = ui_browser__run(&browser->b, delay_secs);
805 
806 		if (delay_secs != 0) {
807 			annotate_browser__calc_percent(browser, evsel);
808 			/*
809 			 * Current line focus got out of the list of most active
810 			 * lines, NULL it so that if TAB|UNTAB is pressed, we
811 			 * move to curr_hot (current hottest line).
812 			 */
813 			if (nd != NULL && RB_EMPTY_NODE(nd))
814 				nd = NULL;
815 		}
816 
817 		switch (key) {
818 		case K_TIMER:
819 			if (hbt)
820 				hbt->timer(hbt->arg);
821 
822 			if (delay_secs != 0)
823 				symbol__annotate_decay_histogram(sym, evsel->idx);
824 			continue;
825 		case K_TAB:
826 			if (nd != NULL) {
827 				nd = rb_prev(nd);
828 				if (nd == NULL)
829 					nd = rb_last(&browser->entries);
830 			} else
831 				nd = browser->curr_hot;
832 			break;
833 		case K_UNTAB:
834 			if (nd != NULL) {
835 				nd = rb_next(nd);
836 				if (nd == NULL)
837 					nd = rb_first(&browser->entries);
838 			} else
839 				nd = browser->curr_hot;
840 			break;
841 		case K_F1:
842 		case 'h':
843 			ui_browser__help_window(&browser->b,
844 		"UP/DOWN/PGUP\n"
845 		"PGDN/SPACE    Navigate\n"
846 		"q/ESC/CTRL+C  Exit\n\n"
847 		"ENTER         Go to target\n"
848 		"ESC           Exit\n"
849 		"H             Go to hottest instruction\n"
850 		"TAB/shift+TAB Cycle thru hottest instructions\n"
851 		"j             Toggle showing jump to target arrows\n"
852 		"J             Toggle showing number of jump sources on targets\n"
853 		"n             Search next string\n"
854 		"o             Toggle disassembler output/simplified view\n"
855 		"s             Toggle source code view\n"
856 		"t             Circulate percent, total period, samples view\n"
857 		"/             Search string\n"
858 		"k             Toggle line numbers\n"
859 		"r             Run available scripts\n"
860 		"?             Search string backwards\n");
861 			continue;
862 		case 'r':
863 			{
864 				script_browse(NULL);
865 				continue;
866 			}
867 		case 'k':
868 			annotate_browser__opts.show_linenr =
869 				!annotate_browser__opts.show_linenr;
870 			break;
871 		case 'H':
872 			nd = browser->curr_hot;
873 			break;
874 		case 's':
875 			if (annotate_browser__toggle_source(browser))
876 				ui_helpline__puts(help);
877 			continue;
878 		case 'o':
879 			annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
880 			annotate_browser__update_addr_width(browser);
881 			continue;
882 		case 'j':
883 			annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
884 			continue;
885 		case 'J':
886 			annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
887 			annotate_browser__update_addr_width(browser);
888 			continue;
889 		case '/':
890 			if (annotate_browser__search(browser, delay_secs)) {
891 show_help:
892 				ui_helpline__puts(help);
893 			}
894 			continue;
895 		case 'n':
896 			if (browser->searching_backwards ?
897 			    annotate_browser__continue_search_reverse(browser, delay_secs) :
898 			    annotate_browser__continue_search(browser, delay_secs))
899 				goto show_help;
900 			continue;
901 		case '?':
902 			if (annotate_browser__search_reverse(browser, delay_secs))
903 				goto show_help;
904 			continue;
905 		case 'D': {
906 			static int seq;
907 			ui_helpline__pop();
908 			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
909 					   seq++, browser->b.nr_entries,
910 					   browser->b.height,
911 					   browser->b.index,
912 					   browser->b.top_idx,
913 					   browser->nr_asm_entries);
914 		}
915 			continue;
916 		case K_ENTER:
917 		case K_RIGHT:
918 		{
919 			struct disasm_line *dl = disasm_line(browser->selection);
920 
921 			if (browser->selection == NULL)
922 				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
923 			else if (browser->selection->offset == -1)
924 				ui_helpline__puts("Actions are only available for assembly lines.");
925 			else if (!dl->ins.ops)
926 				goto show_sup_ins;
927 			else if (ins__is_ret(&dl->ins))
928 				goto out;
929 			else if (!(annotate_browser__jump(browser) ||
930 				     annotate_browser__callq(browser, evsel, hbt))) {
931 show_sup_ins:
932 				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
933 			}
934 			continue;
935 		}
936 		case 't':
937 			if (annotate_browser__opts.show_total_period) {
938 				annotate_browser__opts.show_total_period = false;
939 				annotate_browser__opts.show_nr_samples = true;
940 			} else if (annotate_browser__opts.show_nr_samples)
941 				annotate_browser__opts.show_nr_samples = false;
942 			else
943 				annotate_browser__opts.show_total_period = true;
944 			annotate_browser__update_addr_width(browser);
945 			continue;
946 		case K_LEFT:
947 		case K_ESC:
948 		case 'q':
949 		case CTRL('c'):
950 			goto out;
951 		default:
952 			continue;
953 		}
954 
955 		if (nd != NULL)
956 			annotate_browser__set_rb_top(browser, nd);
957 	}
958 out:
959 	ui_browser__hide(&browser->b);
960 	return key;
961 }
962 
963 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
964 			     struct hist_browser_timer *hbt)
965 {
966 	/* Set default value for show_total_period and show_nr_samples  */
967 	annotate_browser__opts.show_total_period =
968 		symbol_conf.show_total_period;
969 	annotate_browser__opts.show_nr_samples =
970 		symbol_conf.show_nr_samples;
971 
972 	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
973 }
974 
975 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
976 			     struct hist_browser_timer *hbt)
977 {
978 	/* reset abort key so that it can get Ctrl-C as a key */
979 	SLang_reset_tty();
980 	SLang_init_tty(0, 0, 0);
981 
982 	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
983 }
984 
985 
986 static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end)
987 {
988 	unsigned n_insn = 0;
989 	u64 offset;
990 
991 	for (offset = start; offset <= end; offset++) {
992 		if (browser->offsets[offset])
993 			n_insn++;
994 	}
995 	return n_insn;
996 }
997 
998 static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end,
999 			   struct cyc_hist *ch)
1000 {
1001 	unsigned n_insn;
1002 	u64 offset;
1003 
1004 	n_insn = count_insn(browser, start, end);
1005 	if (n_insn && ch->num && ch->cycles) {
1006 		float ipc = n_insn / ((double)ch->cycles / (double)ch->num);
1007 
1008 		/* Hide data when there are too many overlaps. */
1009 		if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
1010 			return;
1011 
1012 		for (offset = start; offset <= end; offset++) {
1013 			struct annotation_line *al = browser->offsets[offset];
1014 
1015 			if (al)
1016 				al->ipc = ipc;
1017 		}
1018 	}
1019 }
1020 
1021 /*
1022  * This should probably be in util/annotate.c to share with the tty
1023  * annotate, but right now we need the per byte offsets arrays,
1024  * which are only here.
1025  */
1026 static void annotate__compute_ipc(struct annotate_browser *browser, size_t size,
1027 			   struct symbol *sym)
1028 {
1029 	u64 offset;
1030 	struct annotation *notes = symbol__annotation(sym);
1031 
1032 	if (!notes->src || !notes->src->cycles_hist)
1033 		return;
1034 
1035 	pthread_mutex_lock(&notes->lock);
1036 	for (offset = 0; offset < size; ++offset) {
1037 		struct cyc_hist *ch;
1038 
1039 		ch = &notes->src->cycles_hist[offset];
1040 		if (ch && ch->cycles) {
1041 			struct annotation_line *al;
1042 
1043 			if (ch->have_start)
1044 				count_and_fill(browser, ch->start, offset, ch);
1045 			al = browser->offsets[offset];
1046 			if (al && ch->num_aggr)
1047 				al->cycles = ch->cycles_aggr / ch->num_aggr;
1048 			browser->have_cycles = true;
1049 		}
1050 	}
1051 	pthread_mutex_unlock(&notes->lock);
1052 }
1053 
1054 static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
1055 						size_t size)
1056 {
1057 	u64 offset;
1058 	struct map_symbol *ms = browser->b.priv;
1059 	struct symbol *sym = ms->sym;
1060 
1061 	/* PLT symbols contain external offsets */
1062 	if (strstr(sym->name, "@plt"))
1063 		return;
1064 
1065 	for (offset = 0; offset < size; ++offset) {
1066 		struct annotation_line *al = browser->offsets[offset];
1067 		struct disasm_line *dl;
1068 		struct browser_line *blt;
1069 
1070 		dl = disasm_line(al);
1071 
1072 		if (!disasm_line__is_valid_jump(dl, sym))
1073 			continue;
1074 
1075 		al = browser->offsets[dl->ops.target.offset];
1076 
1077 		/*
1078  		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
1079  		 * have to adjust to the previous offset?
1080  		 */
1081 		if (al == NULL)
1082 			continue;
1083 
1084 		blt = browser_line(al);
1085 		if (++blt->jump_sources > browser->max_jump_sources)
1086 			browser->max_jump_sources = blt->jump_sources;
1087 
1088 		++browser->nr_jumps;
1089 	}
1090 }
1091 
1092 static inline int width_jumps(int n)
1093 {
1094 	if (n >= 100)
1095 		return 5;
1096 	if (n / 10)
1097 		return 2;
1098 	return 1;
1099 }
1100 
1101 int symbol__tui_annotate(struct symbol *sym, struct map *map,
1102 			 struct perf_evsel *evsel,
1103 			 struct hist_browser_timer *hbt)
1104 {
1105 	struct annotation_line *al;
1106 	struct annotation *notes;
1107 	size_t size;
1108 	struct map_symbol ms = {
1109 		.map = map,
1110 		.sym = sym,
1111 	};
1112 	struct annotate_browser browser = {
1113 		.b = {
1114 			.refresh = annotate_browser__refresh,
1115 			.seek	 = ui_browser__list_head_seek,
1116 			.write	 = annotate_browser__write,
1117 			.filter  = disasm_line__filter,
1118 			.priv	 = &ms,
1119 			.use_navkeypressed = true,
1120 		},
1121 	};
1122 	int ret = -1, err;
1123 	int nr_pcnt = 1;
1124 
1125 	if (sym == NULL)
1126 		return -1;
1127 
1128 	size = symbol__size(sym);
1129 
1130 	if (map->dso->annotate_warned)
1131 		return -1;
1132 
1133 	browser.offsets = zalloc(size * sizeof(struct annotation_line *));
1134 	if (browser.offsets == NULL) {
1135 		ui__error("Not enough memory!");
1136 		return -1;
1137 	}
1138 
1139 	if (perf_evsel__is_group_event(evsel))
1140 		nr_pcnt = evsel->nr_members;
1141 
1142 	err = symbol__annotate(sym, map, evsel, sizeof(struct browser_line), &browser.arch);
1143 	if (err) {
1144 		char msg[BUFSIZ];
1145 		symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
1146 		ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
1147 		goto out_free_offsets;
1148 	}
1149 
1150 	symbol__calc_percent(sym, evsel);
1151 
1152 	ui_helpline__push("Press ESC to exit");
1153 
1154 	notes = symbol__annotation(sym);
1155 	browser.start = map__rip_2objdump(map, sym->start);
1156 
1157 	list_for_each_entry(al, &notes->src->source, node) {
1158 		struct browser_line *bpos;
1159 		size_t line_len = strlen(al->line);
1160 
1161 		if (browser.b.width < line_len)
1162 			browser.b.width = line_len;
1163 		bpos = browser_line(al);
1164 		bpos->idx = browser.nr_entries++;
1165 		if (al->offset != -1) {
1166 			bpos->idx_asm = browser.nr_asm_entries++;
1167 			/*
1168 			 * FIXME: short term bandaid to cope with assembly
1169 			 * routines that comes with labels in the same column
1170 			 * as the address in objdump, sigh.
1171 			 *
1172 			 * E.g. copy_user_generic_unrolled
1173  			 */
1174 			if (al->offset < (s64)size)
1175 				browser.offsets[al->offset] = al;
1176 		} else
1177 			bpos->idx_asm = -1;
1178 	}
1179 
1180 	annotate_browser__mark_jump_targets(&browser, size);
1181 	annotate__compute_ipc(&browser, size, sym);
1182 
1183 	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1184 	browser.max_addr_width = hex_width(sym->end);
1185 	browser.jumps_width = width_jumps(browser.max_jump_sources);
1186 	browser.nr_events = nr_pcnt;
1187 	browser.b.nr_entries = browser.nr_entries;
1188 	browser.b.entries = &notes->src->source,
1189 	browser.b.width += 18; /* Percentage */
1190 
1191 	if (annotate_browser__opts.hide_src_code)
1192 		annotate_browser__init_asm_mode(&browser);
1193 
1194 	annotate_browser__update_addr_width(&browser);
1195 
1196 	ret = annotate_browser__run(&browser, evsel, hbt);
1197 
1198 	annotated_source__purge(notes->src);
1199 
1200 out_free_offsets:
1201 	free(browser.offsets);
1202 	return ret;
1203 }
1204 
1205 #define ANNOTATE_CFG(n) \
1206 	{ .name = #n, .value = &annotate_browser__opts.n, }
1207 
1208 /*
1209  * Keep the entries sorted, they are bsearch'ed
1210  */
1211 static struct annotate_config {
1212 	const char *name;
1213 	bool *value;
1214 } annotate__configs[] = {
1215 	ANNOTATE_CFG(hide_src_code),
1216 	ANNOTATE_CFG(jump_arrows),
1217 	ANNOTATE_CFG(show_linenr),
1218 	ANNOTATE_CFG(show_nr_jumps),
1219 	ANNOTATE_CFG(show_nr_samples),
1220 	ANNOTATE_CFG(show_total_period),
1221 	ANNOTATE_CFG(use_offset),
1222 };
1223 
1224 #undef ANNOTATE_CFG
1225 
1226 static int annotate_config__cmp(const void *name, const void *cfgp)
1227 {
1228 	const struct annotate_config *cfg = cfgp;
1229 
1230 	return strcmp(name, cfg->name);
1231 }
1232 
1233 static int annotate__config(const char *var, const char *value,
1234 			    void *data __maybe_unused)
1235 {
1236 	struct annotate_config *cfg;
1237 	const char *name;
1238 
1239 	if (!strstarts(var, "annotate."))
1240 		return 0;
1241 
1242 	name = var + 9;
1243 	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1244 		      sizeof(struct annotate_config), annotate_config__cmp);
1245 
1246 	if (cfg == NULL)
1247 		ui__warning("%s variable unknown, ignoring...", var);
1248 	else
1249 		*cfg->value = perf_config_bool(name, value);
1250 	return 0;
1251 }
1252 
1253 void annotate_browser__init(void)
1254 {
1255 	perf_config(annotate__config, NULL);
1256 }
1257