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