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