Linux Audio

Check our new training course

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