xref: /linux/tools/perf/ui/browsers/annotate.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
1 // SPDX-License-Identifier: GPL-2.0
2 #include "../browser.h"
3 #include "../helpline.h"
4 #include "../ui.h"
5 #include "../../util/annotate.h"
6 #include "../../util/debug.h"
7 #include "../../util/dso.h"
8 #include "../../util/hist.h"
9 #include "../../util/sort.h"
10 #include "../../util/map.h"
11 #include "../../util/mutex.h"
12 #include "../../util/symbol.h"
13 #include "../../util/evsel.h"
14 #include "../../util/evlist.h"
15 #include <inttypes.h>
16 #include <linux/kernel.h>
17 #include <linux/string.h>
18 #include <linux/zalloc.h>
19 #include <sys/ttydefaults.h>
20 #include <asm/bug.h>
21 
22 struct arch;
23 
24 struct annotate_browser {
25 	struct ui_browser	    b;
26 	struct rb_root		    entries;
27 	struct rb_node		   *curr_hot;
28 	struct annotation_line	   *selection;
29 	struct arch		   *arch;
30 	bool			    searching_backwards;
31 	char			    search_bf[128];
32 };
33 
browser__annotation(struct ui_browser * browser)34 static inline struct annotation *browser__annotation(struct ui_browser *browser)
35 {
36 	struct map_symbol *ms = browser->priv;
37 	return symbol__annotation(ms->sym);
38 }
39 
disasm_line__filter(struct ui_browser * browser __maybe_unused,void * entry)40 static bool disasm_line__filter(struct ui_browser *browser __maybe_unused, void *entry)
41 {
42 	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
43 	return annotation_line__filter(al);
44 }
45 
ui_browser__jumps_percent_color(struct ui_browser * browser,int nr,bool current)46 static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
47 {
48 	struct annotation *notes = browser__annotation(browser);
49 
50 	if (current && (!browser->use_navkeypressed || browser->navkeypressed))
51 		return HE_COLORSET_SELECTED;
52 	if (nr == notes->src->max_jump_sources)
53 		return HE_COLORSET_TOP;
54 	if (nr > 1)
55 		return HE_COLORSET_MEDIUM;
56 	return HE_COLORSET_NORMAL;
57 }
58 
ui_browser__set_jumps_percent_color(void * browser,int nr,bool current)59 static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current)
60 {
61 	 int color = ui_browser__jumps_percent_color(browser, nr, current);
62 	 return ui_browser__set_color(browser, color);
63 }
64 
annotate_browser__set_color(void * browser,int color)65 static int annotate_browser__set_color(void *browser, int color)
66 {
67 	return ui_browser__set_color(browser, color);
68 }
69 
annotate_browser__write_graph(void * browser,int graph)70 static void annotate_browser__write_graph(void *browser, int graph)
71 {
72 	ui_browser__write_graph(browser, graph);
73 }
74 
annotate_browser__set_percent_color(void * browser,double percent,bool current)75 static void annotate_browser__set_percent_color(void *browser, double percent, bool current)
76 {
77 	ui_browser__set_percent_color(browser, percent, current);
78 }
79 
annotate_browser__printf(void * browser,const char * fmt,...)80 static void annotate_browser__printf(void *browser, const char *fmt, ...)
81 {
82 	va_list args;
83 
84 	va_start(args, fmt);
85 	ui_browser__vprintf(browser, fmt, args);
86 	va_end(args);
87 }
88 
annotate_browser__write(struct ui_browser * browser,void * entry,int row)89 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
90 {
91 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
92 	struct annotation *notes = browser__annotation(browser);
93 	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
94 	const bool is_current_entry = ui_browser__is_current_entry(browser, row);
95 	struct annotation_write_ops ops = {
96 		.first_line		 = row == 0,
97 		.current_entry		 = is_current_entry,
98 		.change_color		 = (!annotate_opts.hide_src_code &&
99 					    (!is_current_entry ||
100 					     (browser->use_navkeypressed &&
101 					      !browser->navkeypressed))),
102 		.width			 = browser->width,
103 		.obj			 = browser,
104 		.set_color		 = annotate_browser__set_color,
105 		.set_percent_color	 = annotate_browser__set_percent_color,
106 		.set_jumps_percent_color = ui_browser__set_jumps_percent_color,
107 		.printf			 = annotate_browser__printf,
108 		.write_graph		 = annotate_browser__write_graph,
109 	};
110 
111 	/* The scroll bar isn't being used */
112 	if (!browser->navkeypressed)
113 		ops.width += 1;
114 
115 	annotation_line__write(al, notes, &ops);
116 
117 	if (ops.current_entry)
118 		ab->selection = al;
119 }
120 
is_fused(struct annotate_browser * ab,struct disasm_line * cursor)121 static int is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
122 {
123 	struct disasm_line *pos = list_prev_entry(cursor, al.node);
124 	const char *name;
125 	int diff = 1;
126 
127 	while (pos && pos->al.offset == -1) {
128 		pos = list_prev_entry(pos, al.node);
129 		if (!annotate_opts.hide_src_code)
130 			diff++;
131 	}
132 
133 	if (!pos)
134 		return 0;
135 
136 	if (ins__is_lock(&pos->ins))
137 		name = pos->ops.locked.ins.name;
138 	else
139 		name = pos->ins.name;
140 
141 	if (!name || !cursor->ins.name)
142 		return 0;
143 
144 	if (ins__is_fused(ab->arch, name, cursor->ins.name))
145 		return diff;
146 	return 0;
147 }
148 
annotate_browser__draw_current_jump(struct ui_browser * browser)149 static void annotate_browser__draw_current_jump(struct ui_browser *browser)
150 {
151 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
152 	struct disasm_line *cursor = disasm_line(ab->selection);
153 	struct annotation_line *target;
154 	unsigned int from, to;
155 	struct map_symbol *ms = ab->b.priv;
156 	struct symbol *sym = ms->sym;
157 	struct annotation *notes = symbol__annotation(sym);
158 	u8 pcnt_width = annotation__pcnt_width(notes);
159 	u8 cntr_width = annotation__br_cntr_width();
160 	int width;
161 	int diff = 0;
162 
163 	/* PLT symbols contain external offsets */
164 	if (strstr(sym->name, "@plt"))
165 		return;
166 
167 	if (!disasm_line__is_valid_local_jump(cursor, sym))
168 		return;
169 
170 	/*
171 	 * This first was seen with a gcc function, _cpp_lex_token, that
172 	 * has the usual jumps:
173 	 *
174 	 *  │1159e6c: ↓ jne    115aa32 <_cpp_lex_token@@Base+0xf92>
175 	 *
176 	 * I.e. jumps to a label inside that function (_cpp_lex_token), and
177 	 * those works, but also this kind:
178 	 *
179 	 *  │1159e8b: ↓ jne    c469be <cpp_named_operator2name@@Base+0xa72>
180 	 *
181 	 *  I.e. jumps to another function, outside _cpp_lex_token, which
182 	 *  are not being correctly handled generating as a side effect references
183 	 *  to ab->offset[] entries that are set to NULL, so to make this code
184 	 *  more robust, check that here.
185 	 *
186 	 *  A proper fix for will be put in place, looking at the function
187 	 *  name right after the '<' token and probably treating this like a
188 	 *  'call' instruction.
189 	 */
190 	target = annotated_source__get_line(notes->src, cursor->ops.target.offset);
191 	if (target == NULL) {
192 		ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
193 				    cursor->ops.target.offset);
194 		return;
195 	}
196 
197 	if (annotate_opts.hide_src_code) {
198 		from = cursor->al.idx_asm;
199 		to = target->idx_asm;
200 	} else {
201 		from = (u64)cursor->al.idx;
202 		to = (u64)target->idx;
203 	}
204 
205 	width = annotation__cycles_width(notes);
206 
207 	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
208 	__ui_browser__line_arrow(browser,
209 				 pcnt_width + 2 + notes->src->widths.addr + width + cntr_width,
210 				 from, to);
211 
212 	diff = is_fused(ab, cursor);
213 	if (diff > 0) {
214 		ui_browser__mark_fused(browser,
215 				       pcnt_width + 3 + notes->src->widths.addr + width + cntr_width,
216 				       from - diff, diff, to > from);
217 	}
218 }
219 
annotate_browser__refresh(struct ui_browser * browser)220 static unsigned int annotate_browser__refresh(struct ui_browser *browser)
221 {
222 	struct annotation *notes = browser__annotation(browser);
223 	int ret = ui_browser__list_head_refresh(browser);
224 	int pcnt_width = annotation__pcnt_width(notes);
225 
226 	if (annotate_opts.jump_arrows)
227 		annotate_browser__draw_current_jump(browser);
228 
229 	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
230 	__ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1);
231 	return ret;
232 }
233 
disasm__cmp(struct annotation_line * a,struct annotation_line * b,int percent_type)234 static double disasm__cmp(struct annotation_line *a, struct annotation_line *b,
235 						  int percent_type)
236 {
237 	int i;
238 
239 	for (i = 0; i < a->data_nr; i++) {
240 		if (a->data[i].percent[percent_type] == b->data[i].percent[percent_type])
241 			continue;
242 		return a->data[i].percent[percent_type] -
243 			   b->data[i].percent[percent_type];
244 	}
245 	return 0;
246 }
247 
disasm_rb_tree__insert(struct annotate_browser * browser,struct annotation_line * al)248 static void disasm_rb_tree__insert(struct annotate_browser *browser,
249 				struct annotation_line *al)
250 {
251 	struct rb_root *root = &browser->entries;
252 	struct rb_node **p = &root->rb_node;
253 	struct rb_node *parent = NULL;
254 	struct annotation_line *l;
255 
256 	while (*p != NULL) {
257 		parent = *p;
258 		l = rb_entry(parent, struct annotation_line, rb_node);
259 
260 		if (disasm__cmp(al, l, annotate_opts.percent_type) < 0)
261 			p = &(*p)->rb_left;
262 		else
263 			p = &(*p)->rb_right;
264 	}
265 	rb_link_node(&al->rb_node, parent, p);
266 	rb_insert_color(&al->rb_node, root);
267 }
268 
annotate_browser__set_top(struct annotate_browser * browser,struct annotation_line * pos,u32 idx)269 static void annotate_browser__set_top(struct annotate_browser *browser,
270 				      struct annotation_line *pos, u32 idx)
271 {
272 	unsigned back;
273 
274 	ui_browser__refresh_dimensions(&browser->b);
275 	back = browser->b.height / 2;
276 	browser->b.top_idx = browser->b.index = idx;
277 
278 	while (browser->b.top_idx != 0 && back != 0) {
279 		pos = list_entry(pos->node.prev, struct annotation_line, node);
280 
281 		if (annotation_line__filter(pos))
282 			continue;
283 
284 		--browser->b.top_idx;
285 		--back;
286 	}
287 
288 	browser->b.top = pos;
289 	browser->b.navkeypressed = true;
290 }
291 
annotate_browser__set_rb_top(struct annotate_browser * browser,struct rb_node * nd)292 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
293 					 struct rb_node *nd)
294 {
295 	struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
296 	u32 idx = pos->idx;
297 
298 	if (annotate_opts.hide_src_code)
299 		idx = pos->idx_asm;
300 	annotate_browser__set_top(browser, pos, idx);
301 	browser->curr_hot = nd;
302 }
303 
annotate_browser__calc_percent(struct annotate_browser * browser,struct evsel * evsel)304 static void annotate_browser__calc_percent(struct annotate_browser *browser,
305 					   struct evsel *evsel)
306 {
307 	struct map_symbol *ms = browser->b.priv;
308 	struct symbol *sym = ms->sym;
309 	struct annotation *notes = symbol__annotation(sym);
310 	struct disasm_line *pos;
311 
312 	browser->entries = RB_ROOT;
313 
314 	annotation__lock(notes);
315 
316 	symbol__calc_percent(sym, evsel);
317 
318 	list_for_each_entry(pos, &notes->src->source, al.node) {
319 		double max_percent = 0.0;
320 		int i;
321 
322 		if (pos->al.offset == -1) {
323 			RB_CLEAR_NODE(&pos->al.rb_node);
324 			continue;
325 		}
326 
327 		for (i = 0; i < pos->al.data_nr; i++) {
328 			double percent;
329 
330 			percent = annotation_data__percent(&pos->al.data[i],
331 							   annotate_opts.percent_type);
332 
333 			if (max_percent < percent)
334 				max_percent = percent;
335 		}
336 
337 		if (max_percent < 0.01 && (!pos->al.cycles || pos->al.cycles->ipc == 0)) {
338 			RB_CLEAR_NODE(&pos->al.rb_node);
339 			continue;
340 		}
341 		disasm_rb_tree__insert(browser, &pos->al);
342 	}
343 	annotation__unlock(notes);
344 
345 	browser->curr_hot = rb_last(&browser->entries);
346 }
347 
annotate_browser__find_next_asm_line(struct annotate_browser * browser,struct annotation_line * al)348 static struct annotation_line *annotate_browser__find_next_asm_line(
349 					struct annotate_browser *browser,
350 					struct annotation_line *al)
351 {
352 	struct annotation_line *it = al;
353 
354 	/* find next asm line */
355 	list_for_each_entry_continue(it, browser->b.entries, node) {
356 		if (it->idx_asm >= 0)
357 			return it;
358 	}
359 
360 	/* no asm line found forwards, try backwards */
361 	it = al;
362 	list_for_each_entry_continue_reverse(it, browser->b.entries, node) {
363 		if (it->idx_asm >= 0)
364 			return it;
365 	}
366 
367 	/* There are no asm lines */
368 	return NULL;
369 }
370 
annotate_browser__toggle_source(struct annotate_browser * browser)371 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
372 {
373 	struct annotation *notes = browser__annotation(&browser->b);
374 	struct annotation_line *al;
375 	off_t offset = browser->b.index - browser->b.top_idx;
376 
377 	browser->b.seek(&browser->b, offset, SEEK_CUR);
378 	al = list_entry(browser->b.top, struct annotation_line, node);
379 
380 	if (annotate_opts.hide_src_code) {
381 		if (al->idx_asm < offset)
382 			offset = al->idx;
383 
384 		browser->b.nr_entries = notes->src->nr_entries;
385 		annotate_opts.hide_src_code = false;
386 		browser->b.seek(&browser->b, -offset, SEEK_CUR);
387 		browser->b.top_idx = al->idx - offset;
388 		browser->b.index = al->idx;
389 	} else {
390 		if (al->idx_asm < 0) {
391 			/* move cursor to next asm line */
392 			al = annotate_browser__find_next_asm_line(browser, al);
393 			if (!al) {
394 				browser->b.seek(&browser->b, -offset, SEEK_CUR);
395 				return false;
396 			}
397 		}
398 
399 		if (al->idx_asm < offset)
400 			offset = al->idx_asm;
401 
402 		browser->b.nr_entries = notes->src->nr_asm_entries;
403 		annotate_opts.hide_src_code = true;
404 		browser->b.seek(&browser->b, -offset, SEEK_CUR);
405 		browser->b.top_idx = al->idx_asm - offset;
406 		browser->b.index = al->idx_asm;
407 	}
408 
409 	return true;
410 }
411 
412 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
413 
annotate_browser__show_full_location(struct ui_browser * browser)414 static void annotate_browser__show_full_location(struct ui_browser *browser)
415 {
416 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
417 	struct disasm_line *cursor = disasm_line(ab->selection);
418 	struct annotation_line *al = &cursor->al;
419 
420 	if (al->offset != -1)
421 		ui_helpline__puts("Only available for source code lines.");
422 	else if (al->fileloc == NULL)
423 		ui_helpline__puts("No source file location.");
424 	else {
425 		char help_line[SYM_TITLE_MAX_SIZE];
426 		sprintf (help_line, "Source file location: %s", al->fileloc);
427 		ui_helpline__puts(help_line);
428 	}
429 }
430 
ui_browser__init_asm_mode(struct ui_browser * browser)431 static void ui_browser__init_asm_mode(struct ui_browser *browser)
432 {
433 	struct annotation *notes = browser__annotation(browser);
434 	ui_browser__reset_index(browser);
435 	browser->nr_entries = notes->src->nr_asm_entries;
436 }
437 
sym_title(struct symbol * sym,struct map * map,char * title,size_t sz,int percent_type)438 static int sym_title(struct symbol *sym, struct map *map, char *title,
439 		     size_t sz, int percent_type)
440 {
441 	return snprintf(title, sz, "%s  %s [Percent: %s]", sym->name,
442 			dso__long_name(map__dso(map)),
443 			percent_type_str(percent_type));
444 }
445 
446 /*
447  * This can be called from external jumps, i.e. jumps from one function
448  * to another, like from the kernel's entry_SYSCALL_64 function to the
449  * swapgs_restore_regs_and_return_to_usermode() function.
450  *
451  * So all we check here is that dl->ops.target.sym is set, if it is, just
452  * go to that function and when exiting from its disassembly, come back
453  * to the calling function.
454  */
annotate_browser__callq(struct annotate_browser * browser,struct evsel * evsel,struct hist_browser_timer * hbt)455 static bool annotate_browser__callq(struct annotate_browser *browser,
456 				    struct evsel *evsel,
457 				    struct hist_browser_timer *hbt)
458 {
459 	struct map_symbol *ms = browser->b.priv, target_ms;
460 	struct disasm_line *dl = disasm_line(browser->selection);
461 	struct annotation *notes;
462 	char title[SYM_TITLE_MAX_SIZE];
463 
464 	if (!dl->ops.target.sym) {
465 		ui_helpline__puts("The called function was not found.");
466 		return true;
467 	}
468 
469 	notes = symbol__annotation(dl->ops.target.sym);
470 	annotation__lock(notes);
471 
472 	if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) {
473 		annotation__unlock(notes);
474 		ui__warning("Not enough memory for annotating '%s' symbol!\n",
475 			    dl->ops.target.sym->name);
476 		return true;
477 	}
478 
479 	target_ms.maps = ms->maps;
480 	target_ms.map = ms->map;
481 	target_ms.sym = dl->ops.target.sym;
482 	annotation__unlock(notes);
483 	symbol__tui_annotate(&target_ms, evsel, hbt);
484 	sym_title(ms->sym, ms->map, title, sizeof(title), annotate_opts.percent_type);
485 	ui_browser__show_title(&browser->b, title);
486 	return true;
487 }
488 
489 static
annotate_browser__find_offset(struct annotate_browser * browser,s64 offset,s64 * idx)490 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
491 					  s64 offset, s64 *idx)
492 {
493 	struct annotation *notes = browser__annotation(&browser->b);
494 	struct disasm_line *pos;
495 
496 	*idx = 0;
497 	list_for_each_entry(pos, &notes->src->source, al.node) {
498 		if (pos->al.offset == offset)
499 			return pos;
500 		if (!annotation_line__filter(&pos->al))
501 			++*idx;
502 	}
503 
504 	return NULL;
505 }
506 
annotate_browser__jump(struct annotate_browser * browser,struct evsel * evsel,struct hist_browser_timer * hbt)507 static bool annotate_browser__jump(struct annotate_browser *browser,
508 				   struct evsel *evsel,
509 				   struct hist_browser_timer *hbt)
510 {
511 	struct disasm_line *dl = disasm_line(browser->selection);
512 	u64 offset;
513 	s64 idx;
514 
515 	if (!ins__is_jump(&dl->ins))
516 		return false;
517 
518 	if (dl->ops.target.outside) {
519 		annotate_browser__callq(browser, evsel, hbt);
520 		return true;
521 	}
522 
523 	offset = dl->ops.target.offset;
524 	dl = annotate_browser__find_offset(browser, offset, &idx);
525 	if (dl == NULL) {
526 		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
527 		return true;
528 	}
529 
530 	annotate_browser__set_top(browser, &dl->al, idx);
531 
532 	return true;
533 }
534 
535 static
annotate_browser__find_string(struct annotate_browser * browser,char * s,s64 * idx)536 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
537 					  char *s, s64 *idx)
538 {
539 	struct annotation *notes = browser__annotation(&browser->b);
540 	struct annotation_line *al = browser->selection;
541 
542 	*idx = browser->b.index;
543 	list_for_each_entry_continue(al, &notes->src->source, node) {
544 		if (annotation_line__filter(al))
545 			continue;
546 
547 		++*idx;
548 
549 		if (al->line && strstr(al->line, s) != NULL)
550 			return al;
551 	}
552 
553 	return NULL;
554 }
555 
__annotate_browser__search(struct annotate_browser * browser)556 static bool __annotate_browser__search(struct annotate_browser *browser)
557 {
558 	struct annotation_line *al;
559 	s64 idx;
560 
561 	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
562 	if (al == NULL) {
563 		ui_helpline__puts("String not found!");
564 		return false;
565 	}
566 
567 	annotate_browser__set_top(browser, al, idx);
568 	browser->searching_backwards = false;
569 	return true;
570 }
571 
572 static
annotate_browser__find_string_reverse(struct annotate_browser * browser,char * s,s64 * idx)573 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
574 						  char *s, s64 *idx)
575 {
576 	struct annotation *notes = browser__annotation(&browser->b);
577 	struct annotation_line *al = browser->selection;
578 
579 	*idx = browser->b.index;
580 	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
581 		if (annotation_line__filter(al))
582 			continue;
583 
584 		--*idx;
585 
586 		if (al->line && strstr(al->line, s) != NULL)
587 			return al;
588 	}
589 
590 	return NULL;
591 }
592 
__annotate_browser__search_reverse(struct annotate_browser * browser)593 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
594 {
595 	struct annotation_line *al;
596 	s64 idx;
597 
598 	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
599 	if (al == NULL) {
600 		ui_helpline__puts("String not found!");
601 		return false;
602 	}
603 
604 	annotate_browser__set_top(browser, al, idx);
605 	browser->searching_backwards = true;
606 	return true;
607 }
608 
annotate_browser__search_window(struct annotate_browser * browser,int delay_secs)609 static bool annotate_browser__search_window(struct annotate_browser *browser,
610 					    int delay_secs)
611 {
612 	if (ui_browser__input_window("Search", "String: ", browser->search_bf,
613 				     "ENTER: OK, ESC: Cancel",
614 				     delay_secs * 2) != K_ENTER ||
615 	    !*browser->search_bf)
616 		return false;
617 
618 	return true;
619 }
620 
annotate_browser__search(struct annotate_browser * browser,int delay_secs)621 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
622 {
623 	if (annotate_browser__search_window(browser, delay_secs))
624 		return __annotate_browser__search(browser);
625 
626 	return false;
627 }
628 
annotate_browser__continue_search(struct annotate_browser * browser,int delay_secs)629 static bool annotate_browser__continue_search(struct annotate_browser *browser,
630 					      int delay_secs)
631 {
632 	if (!*browser->search_bf)
633 		return annotate_browser__search(browser, delay_secs);
634 
635 	return __annotate_browser__search(browser);
636 }
637 
annotate_browser__search_reverse(struct annotate_browser * browser,int delay_secs)638 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
639 					   int delay_secs)
640 {
641 	if (annotate_browser__search_window(browser, delay_secs))
642 		return __annotate_browser__search_reverse(browser);
643 
644 	return false;
645 }
646 
647 static
annotate_browser__continue_search_reverse(struct annotate_browser * browser,int delay_secs)648 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
649 					       int delay_secs)
650 {
651 	if (!*browser->search_bf)
652 		return annotate_browser__search_reverse(browser, delay_secs);
653 
654 	return __annotate_browser__search_reverse(browser);
655 }
656 
annotate_browser__show(struct ui_browser * browser,char * title,const char * help)657 static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
658 {
659 	struct map_symbol *ms = browser->priv;
660 	struct symbol *sym = ms->sym;
661 	char symbol_dso[SYM_TITLE_MAX_SIZE];
662 
663 	if (ui_browser__show(browser, title, help) < 0)
664 		return -1;
665 
666 	sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), annotate_opts.percent_type);
667 
668 	ui_browser__gotorc_title(browser, 0, 0);
669 	ui_browser__set_color(browser, HE_COLORSET_ROOT);
670 	ui_browser__write_nstring(browser, symbol_dso, browser->width + 1);
671 	return 0;
672 }
673 
674 static void
switch_percent_type(struct annotation_options * opts,bool base)675 switch_percent_type(struct annotation_options *opts, bool base)
676 {
677 	switch (opts->percent_type) {
678 	case PERCENT_HITS_LOCAL:
679 		if (base)
680 			opts->percent_type = PERCENT_PERIOD_LOCAL;
681 		else
682 			opts->percent_type = PERCENT_HITS_GLOBAL;
683 		break;
684 	case PERCENT_HITS_GLOBAL:
685 		if (base)
686 			opts->percent_type = PERCENT_PERIOD_GLOBAL;
687 		else
688 			opts->percent_type = PERCENT_HITS_LOCAL;
689 		break;
690 	case PERCENT_PERIOD_LOCAL:
691 		if (base)
692 			opts->percent_type = PERCENT_HITS_LOCAL;
693 		else
694 			opts->percent_type = PERCENT_PERIOD_GLOBAL;
695 		break;
696 	case PERCENT_PERIOD_GLOBAL:
697 		if (base)
698 			opts->percent_type = PERCENT_HITS_GLOBAL;
699 		else
700 			opts->percent_type = PERCENT_PERIOD_LOCAL;
701 		break;
702 	default:
703 		WARN_ON(1);
704 	}
705 }
706 
annotate_browser__run(struct annotate_browser * browser,struct evsel * evsel,struct hist_browser_timer * hbt)707 static int annotate_browser__run(struct annotate_browser *browser,
708 				 struct evsel *evsel,
709 				 struct hist_browser_timer *hbt)
710 {
711 	struct rb_node *nd = NULL;
712 	struct hists *hists = evsel__hists(evsel);
713 	struct map_symbol *ms = browser->b.priv;
714 	struct symbol *sym = ms->sym;
715 	struct annotation *notes = symbol__annotation(ms->sym);
716 	const char *help = "Press 'h' for help on key bindings";
717 	int delay_secs = hbt ? hbt->refresh : 0;
718 	char *br_cntr_text = NULL;
719 	char title[256];
720 	int key;
721 
722 	hists__scnprintf_title(hists, title, sizeof(title));
723 	if (annotate_browser__show(&browser->b, title, help) < 0)
724 		return -1;
725 
726 	annotate_browser__calc_percent(browser, evsel);
727 
728 	if (browser->curr_hot) {
729 		annotate_browser__set_rb_top(browser, browser->curr_hot);
730 		browser->b.navkeypressed = false;
731 	}
732 
733 	nd = browser->curr_hot;
734 
735 	annotation_br_cntr_abbr_list(&br_cntr_text, evsel, false);
736 
737 	while (1) {
738 		key = ui_browser__run(&browser->b, delay_secs);
739 
740 		if (delay_secs != 0) {
741 			annotate_browser__calc_percent(browser, evsel);
742 			/*
743 			 * Current line focus got out of the list of most active
744 			 * lines, NULL it so that if TAB|UNTAB is pressed, we
745 			 * move to curr_hot (current hottest line).
746 			 */
747 			if (nd != NULL && RB_EMPTY_NODE(nd))
748 				nd = NULL;
749 		}
750 
751 		switch (key) {
752 		case K_TIMER:
753 			if (hbt)
754 				hbt->timer(hbt->arg);
755 
756 			if (delay_secs != 0) {
757 				symbol__annotate_decay_histogram(sym, evsel->core.idx);
758 				hists__scnprintf_title(hists, title, sizeof(title));
759 				annotate_browser__show(&browser->b, title, help);
760 			}
761 			continue;
762 		case K_TAB:
763 			if (nd != NULL) {
764 				nd = rb_prev(nd);
765 				if (nd == NULL)
766 					nd = rb_last(&browser->entries);
767 			} else
768 				nd = browser->curr_hot;
769 			break;
770 		case K_UNTAB:
771 			if (nd != NULL) {
772 				nd = rb_next(nd);
773 				if (nd == NULL)
774 					nd = rb_first(&browser->entries);
775 			} else
776 				nd = browser->curr_hot;
777 			break;
778 		case K_F1:
779 		case 'h':
780 			ui_browser__help_window(&browser->b,
781 		"UP/DOWN/PGUP\n"
782 		"PGDN/SPACE    Navigate\n"
783 		"</>           Move to prev/next symbol\n"
784 		"q/ESC/CTRL+C  Exit\n\n"
785 		"ENTER         Go to target\n"
786 		"H             Go to hottest instruction\n"
787 		"TAB/shift+TAB Cycle thru hottest instructions\n"
788 		"j             Toggle showing jump to target arrows\n"
789 		"J             Toggle showing number of jump sources on targets\n"
790 		"n             Search next string\n"
791 		"o             Toggle disassembler output/simplified view\n"
792 		"O             Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
793 		"s             Toggle source code view\n"
794 		"t             Circulate percent, total period, samples view\n"
795 		"c             Show min/max cycle\n"
796 		"/             Search string\n"
797 		"k             Toggle line numbers\n"
798 		"l             Show full source file location\n"
799 		"P             Print to [symbol_name].annotation file.\n"
800 		"r             Run available scripts\n"
801 		"p             Toggle percent type [local/global]\n"
802 		"b             Toggle percent base [period/hits]\n"
803 		"B             Branch counter abbr list (Optional)\n"
804 		"?             Search string backwards\n"
805 		"f             Toggle showing offsets to full address\n");
806 			continue;
807 		case 'r':
808 			script_browse(NULL, NULL);
809 			annotate_browser__show(&browser->b, title, help);
810 			continue;
811 		case 'k':
812 			annotate_opts.show_linenr = !annotate_opts.show_linenr;
813 			continue;
814 		case 'l':
815 			annotate_browser__show_full_location (&browser->b);
816 			continue;
817 		case 'H':
818 			nd = browser->curr_hot;
819 			break;
820 		case 's':
821 			if (annotate_browser__toggle_source(browser))
822 				ui_helpline__puts(help);
823 			continue;
824 		case 'o':
825 			annotate_opts.use_offset = !annotate_opts.use_offset;
826 			annotation__update_column_widths(notes);
827 			continue;
828 		case 'O':
829 			if (++annotate_opts.offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
830 				annotate_opts.offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
831 			continue;
832 		case 'j':
833 			annotate_opts.jump_arrows = !annotate_opts.jump_arrows;
834 			continue;
835 		case 'J':
836 			annotate_opts.show_nr_jumps = !annotate_opts.show_nr_jumps;
837 			annotation__update_column_widths(notes);
838 			continue;
839 		case '/':
840 			if (annotate_browser__search(browser, delay_secs)) {
841 show_help:
842 				ui_helpline__puts(help);
843 			}
844 			continue;
845 		case 'n':
846 			if (browser->searching_backwards ?
847 			    annotate_browser__continue_search_reverse(browser, delay_secs) :
848 			    annotate_browser__continue_search(browser, delay_secs))
849 				goto show_help;
850 			continue;
851 		case '?':
852 			if (annotate_browser__search_reverse(browser, delay_secs))
853 				goto show_help;
854 			continue;
855 		case 'D': {
856 			static int seq;
857 			ui_helpline__pop();
858 			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
859 					   seq++, browser->b.nr_entries,
860 					   browser->b.height,
861 					   browser->b.index,
862 					   browser->b.top_idx,
863 					   notes->src->nr_asm_entries);
864 		}
865 			continue;
866 		case K_ENTER:
867 		case K_RIGHT:
868 		{
869 			struct disasm_line *dl = disasm_line(browser->selection);
870 
871 			if (browser->selection == NULL)
872 				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
873 			else if (browser->selection->offset == -1)
874 				ui_helpline__puts("Actions are only available for assembly lines.");
875 			else if (!dl->ins.ops)
876 				goto show_sup_ins;
877 			else if (ins__is_ret(&dl->ins))
878 				goto out;
879 			else if (!(annotate_browser__jump(browser, evsel, hbt) ||
880 				     annotate_browser__callq(browser, evsel, hbt))) {
881 show_sup_ins:
882 				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
883 			}
884 			continue;
885 		}
886 		case 'P':
887 			map_symbol__annotation_dump(ms, evsel);
888 			continue;
889 		case 't':
890 			if (symbol_conf.show_total_period) {
891 				symbol_conf.show_total_period = false;
892 				symbol_conf.show_nr_samples = true;
893 			} else if (symbol_conf.show_nr_samples)
894 				symbol_conf.show_nr_samples = false;
895 			else
896 				symbol_conf.show_total_period = true;
897 			annotation__update_column_widths(notes);
898 			continue;
899 		case 'c':
900 			if (annotate_opts.show_minmax_cycle)
901 				annotate_opts.show_minmax_cycle = false;
902 			else
903 				annotate_opts.show_minmax_cycle = true;
904 			annotation__update_column_widths(notes);
905 			continue;
906 		case 'p':
907 		case 'b':
908 			switch_percent_type(&annotate_opts, key == 'b');
909 			hists__scnprintf_title(hists, title, sizeof(title));
910 			annotate_browser__show(&browser->b, title, help);
911 			continue;
912 		case 'B':
913 			if (br_cntr_text)
914 				ui_browser__help_window(&browser->b, br_cntr_text);
915 			else {
916 				ui_browser__help_window(&browser->b,
917 							"\n The branch counter is not available.\n");
918 			}
919 			continue;
920 		case 'f':
921 			annotation__toggle_full_addr(notes, ms);
922 			continue;
923 		case K_LEFT:
924 		case '<':
925 		case '>':
926 		case K_ESC:
927 		case 'q':
928 		case CTRL('c'):
929 			goto out;
930 		default:
931 			continue;
932 		}
933 
934 		if (nd != NULL)
935 			annotate_browser__set_rb_top(browser, nd);
936 	}
937 out:
938 	ui_browser__hide(&browser->b);
939 	free(br_cntr_text);
940 	return key;
941 }
942 
map_symbol__tui_annotate(struct map_symbol * ms,struct evsel * evsel,struct hist_browser_timer * hbt)943 int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
944 			     struct hist_browser_timer *hbt)
945 {
946 	return symbol__tui_annotate(ms, evsel, hbt);
947 }
948 
hist_entry__tui_annotate(struct hist_entry * he,struct evsel * evsel,struct hist_browser_timer * hbt)949 int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
950 			     struct hist_browser_timer *hbt)
951 {
952 	/* reset abort key so that it can get Ctrl-C as a key */
953 	SLang_reset_tty();
954 	SLang_init_tty(0, 0, 0);
955 	SLtty_set_suspend_state(true);
956 
957 	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
958 }
959 
symbol__tui_annotate(struct map_symbol * ms,struct evsel * evsel,struct hist_browser_timer * hbt)960 int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
961 			 struct hist_browser_timer *hbt)
962 {
963 	struct symbol *sym = ms->sym;
964 	struct annotation *notes = symbol__annotation(sym);
965 	struct annotate_browser browser = {
966 		.b = {
967 			.refresh = annotate_browser__refresh,
968 			.seek	 = ui_browser__list_head_seek,
969 			.write	 = annotate_browser__write,
970 			.filter  = disasm_line__filter,
971 			.extra_title_lines = 1, /* for hists__scnprintf_title() */
972 			.priv	 = ms,
973 			.use_navkeypressed = true,
974 		},
975 	};
976 	struct dso *dso;
977 	int ret = -1, err;
978 	int not_annotated = list_empty(&notes->src->source);
979 
980 	if (sym == NULL)
981 		return -1;
982 
983 	dso = map__dso(ms->map);
984 	if (dso__annotate_warned(dso))
985 		return -1;
986 
987 	if (not_annotated || !sym->annotate2) {
988 		err = symbol__annotate2(ms, evsel, &browser.arch);
989 		if (err) {
990 			char msg[BUFSIZ];
991 			dso__set_annotate_warned(dso);
992 			symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
993 			ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
994 			return -1;
995 		}
996 	}
997 
998 	ui_helpline__push("Press ESC to exit");
999 
1000 	browser.b.width = notes->src->widths.max_line_len;
1001 	browser.b.nr_entries = notes->src->nr_entries;
1002 	browser.b.entries = &notes->src->source;
1003 	browser.b.width += 18; /* Percentage */
1004 
1005 	if (annotate_opts.hide_src_code)
1006 		ui_browser__init_asm_mode(&browser.b);
1007 
1008 	ret = annotate_browser__run(&browser, evsel, hbt);
1009 
1010 	if(not_annotated)
1011 		annotated_source__purge(notes->src);
1012 
1013 	return ret;
1014 }
1015