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