xref: /linux/tools/perf/ui/browsers/annotate.c (revision f4f346c3465949ebba80c6cc52cd8d2eeaa545fd)
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_new_asm_line(struct annotate_browser * browser,int idx_asm)348 static struct annotation_line *annotate_browser__find_new_asm_line(
349 					struct annotate_browser *browser,
350 					int idx_asm)
351 {
352 	struct annotation_line *al;
353 	struct list_head *head = browser->b.entries;
354 
355 	/* find an annotation line in the new list with the same idx_asm */
356 	list_for_each_entry(al, head, node) {
357 		if (al->idx_asm == idx_asm)
358 			return al;
359 	}
360 
361 	/* There are no asm lines */
362 	return NULL;
363 }
364 
annotate_browser__find_next_asm_line(struct annotate_browser * browser,struct annotation_line * al)365 static struct annotation_line *annotate_browser__find_next_asm_line(
366 					struct annotate_browser *browser,
367 					struct annotation_line *al)
368 {
369 	struct annotation_line *it = al;
370 
371 	/* find next asm line */
372 	list_for_each_entry_continue(it, browser->b.entries, node) {
373 		if (it->idx_asm >= 0)
374 			return it;
375 	}
376 
377 	/* no asm line found forwards, try backwards */
378 	it = al;
379 	list_for_each_entry_continue_reverse(it, browser->b.entries, node) {
380 		if (it->idx_asm >= 0)
381 			return it;
382 	}
383 
384 	/* There are no asm lines */
385 	return NULL;
386 }
387 
annotation__has_source(struct annotation * notes)388 static bool annotation__has_source(struct annotation *notes)
389 {
390 	struct annotation_line *al;
391 	bool found_asm = false;
392 
393 	/* Let's skip the first non-asm lines which present regardless of source. */
394 	list_for_each_entry(al, &notes->src->source, node) {
395 		if (al->offset >= 0) {
396 			found_asm = true;
397 			break;
398 		}
399 	}
400 
401 	if (found_asm) {
402 		/* After assembly lines, any line without offset means source. */
403 		list_for_each_entry_continue(al, &notes->src->source, node) {
404 			if (al->offset == -1)
405 				return true;
406 		}
407 	}
408 	return false;
409 }
410 
annotate_browser__toggle_source(struct annotate_browser * browser,struct evsel * evsel)411 static bool annotate_browser__toggle_source(struct annotate_browser *browser,
412 					    struct evsel *evsel)
413 {
414 	struct annotation *notes = browser__annotation(&browser->b);
415 	struct annotation_line *al;
416 	off_t offset = browser->b.index - browser->b.top_idx;
417 
418 	browser->b.seek(&browser->b, offset, SEEK_CUR);
419 	al = list_entry(browser->b.top, struct annotation_line, node);
420 
421 	if (!annotate_opts.annotate_src)
422 		annotate_opts.annotate_src = true;
423 
424 	/*
425 	 * It's about to get source code annotation for the first time.
426 	 * Drop the existing annotation_lines and get the new one with source.
427 	 * And then move to the original line at the same asm index.
428 	 */
429 	if (annotate_opts.hide_src_code && !notes->src->tried_source) {
430 		struct map_symbol *ms = browser->b.priv;
431 		int orig_idx_asm = al->idx_asm;
432 
433 		/* annotate again with source code info */
434 		annotate_opts.hide_src_code = false;
435 		annotated_source__purge(notes->src);
436 		symbol__annotate2(ms, evsel, &browser->arch);
437 		annotate_opts.hide_src_code = true;
438 
439 		/* should be after annotated_source__purge() */
440 		notes->src->tried_source = true;
441 
442 		if (!annotation__has_source(notes))
443 			ui__warning("Annotation has no source code.");
444 
445 		browser->b.entries = &notes->src->source;
446 		al = annotate_browser__find_new_asm_line(browser, orig_idx_asm);
447 		if (unlikely(al == NULL)) {
448 			al = list_first_entry(&notes->src->source,
449 					      struct annotation_line, node);
450 		}
451 		browser->b.seek(&browser->b, al->idx_asm, SEEK_SET);
452 	}
453 
454 	if (annotate_opts.hide_src_code) {
455 		if (al->idx_asm < offset)
456 			offset = al->idx;
457 
458 		browser->b.nr_entries = notes->src->nr_entries;
459 		annotate_opts.hide_src_code = false;
460 		browser->b.seek(&browser->b, -offset, SEEK_CUR);
461 		browser->b.top_idx = al->idx - offset;
462 		browser->b.index = al->idx;
463 	} else {
464 		if (al->idx_asm < 0) {
465 			/* move cursor to next asm line */
466 			al = annotate_browser__find_next_asm_line(browser, al);
467 			if (!al) {
468 				browser->b.seek(&browser->b, -offset, SEEK_CUR);
469 				return false;
470 			}
471 		}
472 
473 		if (al->idx_asm < offset)
474 			offset = al->idx_asm;
475 
476 		browser->b.nr_entries = notes->src->nr_asm_entries;
477 		annotate_opts.hide_src_code = true;
478 		browser->b.seek(&browser->b, -offset, SEEK_CUR);
479 		browser->b.top_idx = al->idx_asm - offset;
480 		browser->b.index = al->idx_asm;
481 	}
482 
483 	if (annotate_opts.hide_src_code_on_title)
484 		annotate_opts.hide_src_code_on_title = false;
485 
486 	return true;
487 }
488 
489 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
490 
annotate_browser__show_full_location(struct ui_browser * browser)491 static void annotate_browser__show_full_location(struct ui_browser *browser)
492 {
493 	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
494 	struct disasm_line *cursor = disasm_line(ab->selection);
495 	struct annotation_line *al = &cursor->al;
496 
497 	if (al->offset != -1)
498 		ui_helpline__puts("Only available for source code lines.");
499 	else if (al->fileloc == NULL)
500 		ui_helpline__puts("No source file location.");
501 	else {
502 		char help_line[SYM_TITLE_MAX_SIZE];
503 		sprintf (help_line, "Source file location: %s", al->fileloc);
504 		ui_helpline__puts(help_line);
505 	}
506 }
507 
ui_browser__init_asm_mode(struct ui_browser * browser)508 static void ui_browser__init_asm_mode(struct ui_browser *browser)
509 {
510 	struct annotation *notes = browser__annotation(browser);
511 	ui_browser__reset_index(browser);
512 	browser->nr_entries = notes->src->nr_asm_entries;
513 }
514 
sym_title(struct symbol * sym,struct map * map,char * title,size_t sz,int percent_type)515 static int sym_title(struct symbol *sym, struct map *map, char *title,
516 		     size_t sz, int percent_type)
517 {
518 	return snprintf(title, sz, "%s  %s [Percent: %s]", sym->name,
519 			dso__long_name(map__dso(map)),
520 			percent_type_str(percent_type));
521 }
522 
523 /*
524  * This can be called from external jumps, i.e. jumps from one function
525  * to another, like from the kernel's entry_SYSCALL_64 function to the
526  * swapgs_restore_regs_and_return_to_usermode() function.
527  *
528  * So all we check here is that dl->ops.target.sym is set, if it is, just
529  * go to that function and when exiting from its disassembly, come back
530  * to the calling function.
531  */
annotate_browser__callq(struct annotate_browser * browser,struct evsel * evsel,struct hist_browser_timer * hbt)532 static bool annotate_browser__callq(struct annotate_browser *browser,
533 				    struct evsel *evsel,
534 				    struct hist_browser_timer *hbt)
535 {
536 	struct map_symbol *ms = browser->b.priv, target_ms;
537 	struct disasm_line *dl = disasm_line(browser->selection);
538 	struct annotation *notes;
539 	char title[SYM_TITLE_MAX_SIZE];
540 
541 	if (!dl->ops.target.sym) {
542 		ui_helpline__puts("The called function was not found.");
543 		return true;
544 	}
545 
546 	notes = symbol__annotation(dl->ops.target.sym);
547 	annotation__lock(notes);
548 
549 	if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) {
550 		annotation__unlock(notes);
551 		ui__warning("Not enough memory for annotating '%s' symbol!\n",
552 			    dl->ops.target.sym->name);
553 		return true;
554 	}
555 
556 	target_ms.maps = ms->maps;
557 	target_ms.map = ms->map;
558 	target_ms.sym = dl->ops.target.sym;
559 	annotation__unlock(notes);
560 	symbol__tui_annotate(&target_ms, evsel, hbt);
561 	sym_title(ms->sym, ms->map, title, sizeof(title), annotate_opts.percent_type);
562 	ui_browser__show_title(&browser->b, title);
563 	return true;
564 }
565 
566 static
annotate_browser__find_offset(struct annotate_browser * browser,s64 offset,s64 * idx)567 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
568 					  s64 offset, s64 *idx)
569 {
570 	struct annotation *notes = browser__annotation(&browser->b);
571 	struct disasm_line *pos;
572 
573 	*idx = 0;
574 	list_for_each_entry(pos, &notes->src->source, al.node) {
575 		if (pos->al.offset == offset)
576 			return pos;
577 		if (!annotation_line__filter(&pos->al))
578 			++*idx;
579 	}
580 
581 	return NULL;
582 }
583 
annotate_browser__jump(struct annotate_browser * browser,struct evsel * evsel,struct hist_browser_timer * hbt)584 static bool annotate_browser__jump(struct annotate_browser *browser,
585 				   struct evsel *evsel,
586 				   struct hist_browser_timer *hbt)
587 {
588 	struct disasm_line *dl = disasm_line(browser->selection);
589 	u64 offset;
590 	s64 idx;
591 
592 	if (!ins__is_jump(&dl->ins))
593 		return false;
594 
595 	if (dl->ops.target.outside) {
596 		annotate_browser__callq(browser, evsel, hbt);
597 		return true;
598 	}
599 
600 	offset = dl->ops.target.offset;
601 	dl = annotate_browser__find_offset(browser, offset, &idx);
602 	if (dl == NULL) {
603 		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
604 		return true;
605 	}
606 
607 	annotate_browser__set_top(browser, &dl->al, idx);
608 
609 	return true;
610 }
611 
612 static
annotate_browser__find_string(struct annotate_browser * browser,char * s,s64 * idx)613 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
614 					  char *s, s64 *idx)
615 {
616 	struct annotation *notes = browser__annotation(&browser->b);
617 	struct annotation_line *al = browser->selection;
618 
619 	*idx = browser->b.index;
620 	list_for_each_entry_continue(al, &notes->src->source, node) {
621 		if (annotation_line__filter(al))
622 			continue;
623 
624 		++*idx;
625 
626 		if (al->line && strstr(al->line, s) != NULL)
627 			return al;
628 	}
629 
630 	return NULL;
631 }
632 
__annotate_browser__search(struct annotate_browser * browser)633 static bool __annotate_browser__search(struct annotate_browser *browser)
634 {
635 	struct annotation_line *al;
636 	s64 idx;
637 
638 	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
639 	if (al == NULL) {
640 		ui_helpline__puts("String not found!");
641 		return false;
642 	}
643 
644 	annotate_browser__set_top(browser, al, idx);
645 	browser->searching_backwards = false;
646 	return true;
647 }
648 
649 static
annotate_browser__find_string_reverse(struct annotate_browser * browser,char * s,s64 * idx)650 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
651 						  char *s, s64 *idx)
652 {
653 	struct annotation *notes = browser__annotation(&browser->b);
654 	struct annotation_line *al = browser->selection;
655 
656 	*idx = browser->b.index;
657 	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
658 		if (annotation_line__filter(al))
659 			continue;
660 
661 		--*idx;
662 
663 		if (al->line && strstr(al->line, s) != NULL)
664 			return al;
665 	}
666 
667 	return NULL;
668 }
669 
__annotate_browser__search_reverse(struct annotate_browser * browser)670 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
671 {
672 	struct annotation_line *al;
673 	s64 idx;
674 
675 	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
676 	if (al == NULL) {
677 		ui_helpline__puts("String not found!");
678 		return false;
679 	}
680 
681 	annotate_browser__set_top(browser, al, idx);
682 	browser->searching_backwards = true;
683 	return true;
684 }
685 
annotate_browser__search_window(struct annotate_browser * browser,int delay_secs)686 static bool annotate_browser__search_window(struct annotate_browser *browser,
687 					    int delay_secs)
688 {
689 	if (ui_browser__input_window("Search", "String: ", browser->search_bf,
690 				     "ENTER: OK, ESC: Cancel",
691 				     delay_secs * 2) != K_ENTER ||
692 	    !*browser->search_bf)
693 		return false;
694 
695 	return true;
696 }
697 
annotate_browser__search(struct annotate_browser * browser,int delay_secs)698 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
699 {
700 	if (annotate_browser__search_window(browser, delay_secs))
701 		return __annotate_browser__search(browser);
702 
703 	return false;
704 }
705 
annotate_browser__continue_search(struct annotate_browser * browser,int delay_secs)706 static bool annotate_browser__continue_search(struct annotate_browser *browser,
707 					      int delay_secs)
708 {
709 	if (!*browser->search_bf)
710 		return annotate_browser__search(browser, delay_secs);
711 
712 	return __annotate_browser__search(browser);
713 }
714 
annotate_browser__search_reverse(struct annotate_browser * browser,int delay_secs)715 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
716 					   int delay_secs)
717 {
718 	if (annotate_browser__search_window(browser, delay_secs))
719 		return __annotate_browser__search_reverse(browser);
720 
721 	return false;
722 }
723 
724 static
annotate_browser__continue_search_reverse(struct annotate_browser * browser,int delay_secs)725 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
726 					       int delay_secs)
727 {
728 	if (!*browser->search_bf)
729 		return annotate_browser__search_reverse(browser, delay_secs);
730 
731 	return __annotate_browser__search_reverse(browser);
732 }
733 
annotate_browser__show(struct ui_browser * browser,char * title,const char * help)734 static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
735 {
736 	struct map_symbol *ms = browser->priv;
737 	struct symbol *sym = ms->sym;
738 	char symbol_dso[SYM_TITLE_MAX_SIZE];
739 
740 	if (ui_browser__show(browser, title, help) < 0)
741 		return -1;
742 
743 	sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), annotate_opts.percent_type);
744 
745 	ui_browser__gotorc_title(browser, 0, 0);
746 	ui_browser__set_color(browser, HE_COLORSET_ROOT);
747 	ui_browser__write_nstring(browser, symbol_dso, browser->width + 1);
748 	return 0;
749 }
750 
751 static void
switch_percent_type(struct annotation_options * opts,bool base)752 switch_percent_type(struct annotation_options *opts, bool base)
753 {
754 	switch (opts->percent_type) {
755 	case PERCENT_HITS_LOCAL:
756 		if (base)
757 			opts->percent_type = PERCENT_PERIOD_LOCAL;
758 		else
759 			opts->percent_type = PERCENT_HITS_GLOBAL;
760 		break;
761 	case PERCENT_HITS_GLOBAL:
762 		if (base)
763 			opts->percent_type = PERCENT_PERIOD_GLOBAL;
764 		else
765 			opts->percent_type = PERCENT_HITS_LOCAL;
766 		break;
767 	case PERCENT_PERIOD_LOCAL:
768 		if (base)
769 			opts->percent_type = PERCENT_HITS_LOCAL;
770 		else
771 			opts->percent_type = PERCENT_PERIOD_GLOBAL;
772 		break;
773 	case PERCENT_PERIOD_GLOBAL:
774 		if (base)
775 			opts->percent_type = PERCENT_HITS_GLOBAL;
776 		else
777 			opts->percent_type = PERCENT_PERIOD_LOCAL;
778 		break;
779 	default:
780 		WARN_ON(1);
781 	}
782 }
783 
annotate__scnprintf_title(struct hists * hists,char * bf,size_t size)784 static int annotate__scnprintf_title(struct hists *hists, char *bf, size_t size)
785 {
786 	int printed = hists__scnprintf_title(hists, bf, size);
787 
788 	if (!annotate_opts.hide_src_code_on_title) {
789 		printed += scnprintf(bf + printed, size - printed, " [source: %s]",
790 				     annotate_opts.hide_src_code ? "OFF" : "On");
791 	}
792 
793 	return printed;
794 }
795 
annotate_browser__run(struct annotate_browser * browser,struct evsel * evsel,struct hist_browser_timer * hbt)796 static int annotate_browser__run(struct annotate_browser *browser,
797 				 struct evsel *evsel,
798 				 struct hist_browser_timer *hbt)
799 {
800 	struct rb_node *nd = NULL;
801 	struct hists *hists = evsel__hists(evsel);
802 	struct map_symbol *ms = browser->b.priv;
803 	struct symbol *sym = ms->sym;
804 	struct annotation *notes = symbol__annotation(ms->sym);
805 	const char *help = "Press 'h' for help on key bindings";
806 	int delay_secs = hbt ? hbt->refresh : 0;
807 	char *br_cntr_text = NULL;
808 	char title[256];
809 	int key;
810 
811 	annotate__scnprintf_title(hists, title, sizeof(title));
812 	if (annotate_browser__show(&browser->b, title, help) < 0)
813 		return -1;
814 
815 	annotate_browser__calc_percent(browser, evsel);
816 
817 	if (browser->curr_hot) {
818 		annotate_browser__set_rb_top(browser, browser->curr_hot);
819 		browser->b.navkeypressed = false;
820 	}
821 
822 	nd = browser->curr_hot;
823 
824 	annotation_br_cntr_abbr_list(&br_cntr_text, evsel, false);
825 
826 	while (1) {
827 		key = ui_browser__run(&browser->b, delay_secs);
828 
829 		if (delay_secs != 0) {
830 			annotate_browser__calc_percent(browser, evsel);
831 			/*
832 			 * Current line focus got out of the list of most active
833 			 * lines, NULL it so that if TAB|UNTAB is pressed, we
834 			 * move to curr_hot (current hottest line).
835 			 */
836 			if (nd != NULL && RB_EMPTY_NODE(nd))
837 				nd = NULL;
838 		}
839 
840 		switch (key) {
841 		case K_TIMER:
842 			if (hbt)
843 				hbt->timer(hbt->arg);
844 
845 			if (delay_secs != 0) {
846 				symbol__annotate_decay_histogram(sym, evsel);
847 				annotate__scnprintf_title(hists, title, sizeof(title));
848 				annotate_browser__show(&browser->b, title, help);
849 			}
850 			continue;
851 		case K_TAB:
852 			if (nd != NULL) {
853 				nd = rb_prev(nd);
854 				if (nd == NULL)
855 					nd = rb_last(&browser->entries);
856 			} else
857 				nd = browser->curr_hot;
858 			break;
859 		case K_UNTAB:
860 			if (nd != NULL) {
861 				nd = rb_next(nd);
862 				if (nd == NULL)
863 					nd = rb_first(&browser->entries);
864 			} else
865 				nd = browser->curr_hot;
866 			break;
867 		case K_F1:
868 		case 'h':
869 			ui_browser__help_window(&browser->b,
870 		"UP/DOWN/PGUP\n"
871 		"PGDN/SPACE    Navigate\n"
872 		"</>           Move to prev/next symbol\n"
873 		"q/ESC/CTRL+C  Exit\n\n"
874 		"ENTER         Go to target\n"
875 		"H             Go to hottest instruction\n"
876 		"TAB/shift+TAB Cycle thru hottest instructions\n"
877 		"j             Toggle showing jump to target arrows\n"
878 		"J             Toggle showing number of jump sources on targets\n"
879 		"n             Search next string\n"
880 		"o             Toggle disassembler output/simplified view\n"
881 		"O             Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
882 		"s             Toggle source code view\n"
883 		"t             Circulate percent, total period, samples view\n"
884 		"c             Show min/max cycle\n"
885 		"/             Search string\n"
886 		"k             Toggle line numbers\n"
887 		"l             Show full source file location\n"
888 		"P             Print to [symbol_name].annotation file.\n"
889 		"r             Run available scripts\n"
890 		"p             Toggle percent type [local/global]\n"
891 		"b             Toggle percent base [period/hits]\n"
892 		"B             Branch counter abbr list (Optional)\n"
893 		"?             Search string backwards\n"
894 		"f             Toggle showing offsets to full address\n");
895 			continue;
896 		case 'r':
897 			script_browse(NULL, NULL);
898 			annotate_browser__show(&browser->b, title, help);
899 			continue;
900 		case 'k':
901 			annotate_opts.show_linenr = !annotate_opts.show_linenr;
902 			continue;
903 		case 'l':
904 			annotate_browser__show_full_location (&browser->b);
905 			continue;
906 		case 'H':
907 			nd = browser->curr_hot;
908 			break;
909 		case 's':
910 			if (annotate_browser__toggle_source(browser, evsel))
911 				ui_helpline__puts(help);
912 			annotate__scnprintf_title(hists, title, sizeof(title));
913 			annotate_browser__show(&browser->b, title, help);
914 			continue;
915 		case 'o':
916 			annotate_opts.use_offset = !annotate_opts.use_offset;
917 			annotation__update_column_widths(notes);
918 			continue;
919 		case 'O':
920 			if (++annotate_opts.offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
921 				annotate_opts.offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
922 			continue;
923 		case 'j':
924 			annotate_opts.jump_arrows = !annotate_opts.jump_arrows;
925 			continue;
926 		case 'J':
927 			annotate_opts.show_nr_jumps = !annotate_opts.show_nr_jumps;
928 			annotation__update_column_widths(notes);
929 			continue;
930 		case '/':
931 			if (annotate_browser__search(browser, delay_secs)) {
932 show_help:
933 				ui_helpline__puts(help);
934 			}
935 			continue;
936 		case 'n':
937 			if (browser->searching_backwards ?
938 			    annotate_browser__continue_search_reverse(browser, delay_secs) :
939 			    annotate_browser__continue_search(browser, delay_secs))
940 				goto show_help;
941 			continue;
942 		case '?':
943 			if (annotate_browser__search_reverse(browser, delay_secs))
944 				goto show_help;
945 			continue;
946 		case 'D': {
947 			static int seq;
948 			ui_helpline__pop();
949 			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
950 					   seq++, browser->b.nr_entries,
951 					   browser->b.height,
952 					   browser->b.index,
953 					   browser->b.top_idx,
954 					   notes->src->nr_asm_entries);
955 		}
956 			continue;
957 		case K_ENTER:
958 		case K_RIGHT:
959 		{
960 			struct disasm_line *dl = disasm_line(browser->selection);
961 
962 			if (browser->selection == NULL)
963 				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
964 			else if (browser->selection->offset == -1)
965 				ui_helpline__puts("Actions are only available for assembly lines.");
966 			else if (!dl->ins.ops)
967 				goto show_sup_ins;
968 			else if (ins__is_ret(&dl->ins))
969 				goto out;
970 			else if (!(annotate_browser__jump(browser, evsel, hbt) ||
971 				     annotate_browser__callq(browser, evsel, hbt))) {
972 show_sup_ins:
973 				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
974 			}
975 			continue;
976 		}
977 		case 'P':
978 			map_symbol__annotation_dump(ms, evsel);
979 			continue;
980 		case 't':
981 			if (symbol_conf.show_total_period) {
982 				symbol_conf.show_total_period = false;
983 				symbol_conf.show_nr_samples = true;
984 			} else if (symbol_conf.show_nr_samples)
985 				symbol_conf.show_nr_samples = false;
986 			else
987 				symbol_conf.show_total_period = true;
988 			annotation__update_column_widths(notes);
989 			continue;
990 		case 'c':
991 			if (annotate_opts.show_minmax_cycle)
992 				annotate_opts.show_minmax_cycle = false;
993 			else
994 				annotate_opts.show_minmax_cycle = true;
995 			annotation__update_column_widths(notes);
996 			continue;
997 		case 'p':
998 		case 'b':
999 			switch_percent_type(&annotate_opts, key == 'b');
1000 			annotate__scnprintf_title(hists, title, sizeof(title));
1001 			annotate_browser__show(&browser->b, title, help);
1002 			continue;
1003 		case 'B':
1004 			if (br_cntr_text)
1005 				ui_browser__help_window(&browser->b, br_cntr_text);
1006 			else {
1007 				ui_browser__help_window(&browser->b,
1008 							"\n The branch counter is not available.\n");
1009 			}
1010 			continue;
1011 		case 'f':
1012 			annotation__toggle_full_addr(notes, ms);
1013 			continue;
1014 		case K_LEFT:
1015 		case '<':
1016 		case '>':
1017 		case K_ESC:
1018 		case 'q':
1019 		case CTRL('c'):
1020 			goto out;
1021 		default:
1022 			ui_browser__warn_unhandled_hotkey(&browser->b, key, delay_secs, ", use 'h'/F1 to see actions");
1023 			continue;
1024 		}
1025 
1026 		if (nd != NULL)
1027 			annotate_browser__set_rb_top(browser, nd);
1028 	}
1029 out:
1030 	ui_browser__hide(&browser->b);
1031 	free(br_cntr_text);
1032 	return key;
1033 }
1034 
map_symbol__tui_annotate(struct map_symbol * ms,struct evsel * evsel,struct hist_browser_timer * hbt)1035 int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
1036 			     struct hist_browser_timer *hbt)
1037 {
1038 	return symbol__tui_annotate(ms, evsel, hbt);
1039 }
1040 
hist_entry__tui_annotate(struct hist_entry * he,struct evsel * evsel,struct hist_browser_timer * hbt)1041 int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
1042 			     struct hist_browser_timer *hbt)
1043 {
1044 	/* reset abort key so that it can get Ctrl-C as a key */
1045 	SLang_reset_tty();
1046 	SLang_init_tty(0, 0, 0);
1047 	SLtty_set_suspend_state(true);
1048 
1049 	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
1050 }
1051 
symbol__tui_annotate(struct map_symbol * ms,struct evsel * evsel,struct hist_browser_timer * hbt)1052 int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
1053 			 struct hist_browser_timer *hbt)
1054 {
1055 	struct symbol *sym = ms->sym;
1056 	struct annotation *notes = symbol__annotation(sym);
1057 	struct annotate_browser browser = {
1058 		.b = {
1059 			.refresh = annotate_browser__refresh,
1060 			.seek	 = ui_browser__list_head_seek,
1061 			.write	 = annotate_browser__write,
1062 			.filter  = disasm_line__filter,
1063 			.extra_title_lines = 1, /* for hists__scnprintf_title() */
1064 			.priv	 = ms,
1065 			.use_navkeypressed = true,
1066 		},
1067 	};
1068 	struct dso *dso;
1069 	int ret = -1, err;
1070 	int not_annotated = list_empty(&notes->src->source);
1071 
1072 	if (sym == NULL)
1073 		return -1;
1074 
1075 	dso = map__dso(ms->map);
1076 	if (dso__annotate_warned(dso))
1077 		return -1;
1078 
1079 	if (not_annotated || !sym->annotate2) {
1080 		err = symbol__annotate2(ms, evsel, &browser.arch);
1081 		if (err) {
1082 			char msg[BUFSIZ];
1083 			dso__set_annotate_warned(dso);
1084 			symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
1085 			ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
1086 			return -1;
1087 		}
1088 
1089 		if (!annotate_opts.hide_src_code) {
1090 			notes->src->tried_source = true;
1091 			if (!annotation__has_source(notes))
1092 				ui__warning("Annotation has no source code.");
1093 		}
1094 	}
1095 
1096 	ui_helpline__push("Press ESC to exit");
1097 
1098 	browser.b.width = notes->src->widths.max_line_len;
1099 	browser.b.nr_entries = notes->src->nr_entries;
1100 	browser.b.entries = &notes->src->source;
1101 	browser.b.width += 18; /* Percentage */
1102 
1103 	if (annotate_opts.hide_src_code)
1104 		ui_browser__init_asm_mode(&browser.b);
1105 
1106 	ret = annotate_browser__run(&browser, evsel, hbt);
1107 
1108 	if (not_annotated && !notes->src->tried_source)
1109 		annotated_source__purge(notes->src);
1110 
1111 	return ret;
1112 }
1113