cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

annotate.c (27692B)


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