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