File Index
Files and directories
Sign in
FILE

annotate.c

File preview with syntax highlighting.

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