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