annotate.c 32.5 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2
#include "../../util/util.h"
3 4
#include "../browser.h"
#include "../helpline.h"
5 6
#include "../ui.h"
#include "../util.h"
7 8 9 10
#include "../../util/annotate.h"
#include "../../util/hist.h"
#include "../../util/sort.h"
#include "../../util/symbol.h"
11
#include "../../util/evsel.h"
12
#include "../../util/config.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 <sys/ttydefaults.h>
19

20
struct disasm_line_samples {
21 22
	double		      percent;
	struct sym_hist_entry he;
23 24
};

25 26 27 28
struct browser_line {
	u32	idx;
	int	idx_asm;
	int	jump_sources;
29 30
};

31
static struct annotation_options annotate_browser__opts = {
32 33 34 35
	.use_offset	= true,
	.jump_arrows	= true,
};

36 37
struct arch;

38
struct annotate_browser {
39 40 41 42
	struct ui_browser	    b;
	struct rb_root		    entries;
	struct rb_node		   *curr_hot;
	struct annotation_line	   *selection;
43
	struct annotation_line	  **offsets;
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
	struct arch		   *arch;
	int			    nr_events;
	u64			    start;
	int			    nr_asm_entries;
	int			    nr_entries;
	int			    max_jump_sources;
	int			    nr_jumps;
	bool			    searching_backwards;
	bool			    have_cycles;
	u8			    addr_width;
	u8			    jumps_width;
	u8			    target_width;
	u8			    min_addr_width;
	u8			    max_addr_width;
	char			    search_bf[128];
59 60
};

61
static inline struct browser_line *browser_line(struct annotation_line *al)
62
{
63 64 65 66
	void *ptr = al;

	ptr = container_of(al, struct disasm_line, al);
	return ptr - sizeof(struct browser_line);
67 68
}

69 70
static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
				void *entry)
71
{
72
	if (annotate_browser__opts.hide_src_code) {
73 74 75
		struct annotation_line *al = list_entry(entry, struct annotation_line, node);

		return al->offset == -1;
76 77 78 79 80
	}

	return false;
}

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
						 int nr, bool current)
{
	if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
		return HE_COLORSET_SELECTED;
	if (nr == browser->max_jump_sources)
		return HE_COLORSET_TOP;
	if (nr > 1)
		return HE_COLORSET_MEDIUM;
	return HE_COLORSET_NORMAL;
}

static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
						     int nr, bool current)
{
	 int color = annotate_browser__jumps_percent_color(browser, nr, current);
	 return ui_browser__set_color(&browser->b, color);
}

100 101
static int annotate_browser__pcnt_width(struct annotate_browser *ab)
{
102
	return (annotate_browser__opts.show_total_period ? 12 : 7) * ab->nr_events;
103
}
104

105 106
static int annotate_browser__cycles_width(struct annotate_browser *ab)
{
107
	return ab->have_cycles ? ANNOTATION__IPC_WIDTH + ANNOTATION__CYCLES_WIDTH : 0;
108 109
}

110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
static void disasm_line__write(struct disasm_line *dl, struct ui_browser *browser,
			       char *bf, size_t size)
{
	if (dl->ins.ops && dl->ins.ops->scnprintf) {
		if (ins__is_jump(&dl->ins)) {
			bool fwd = dl->ops.target.offset > dl->al.offset;

			ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
							    SLSMG_UARROW_CHAR);
			SLsmg_write_char(' ');
		} else if (ins__is_call(&dl->ins)) {
			ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
			SLsmg_write_char(' ');
		} else if (ins__is_ret(&dl->ins)) {
			ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
			SLsmg_write_char(' ');
		} else {
			ui_browser__write_nstring(browser, " ", 2);
		}
	} else {
		ui_browser__write_nstring(browser, " ", 2);
	}

	disasm_line__scnprintf(dl, bf, size, !annotate_browser__opts.use_offset);
}

136
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
137
{
138
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
139 140
	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
	struct browser_line *bl = browser_line(al);
141
	bool current_entry = ui_browser__is_current_entry(browser, row);
142
	bool change_color = (!annotate_browser__opts.hide_src_code &&
143 144 145
			     (!current_entry || (browser->use_navkeypressed &&
					         !browser->navkeypressed)));
	int width = browser->width, printed;
146 147
	int i, pcnt_width = annotate_browser__pcnt_width(ab),
	       cycles_width = annotate_browser__cycles_width(ab);
148
	double percent_max = 0.0;
149
	char bf[256];
150
	bool show_title = false;
151

152
	for (i = 0; i < ab->nr_events; i++) {
153 154
		if (al->samples[i].percent > percent_max)
			percent_max = al->samples[i].percent;
155 156
	}

157
	if ((row == 0) && (al->offset == -1 || percent_max == 0.0)) {
158
		if (ab->have_cycles) {
159
			if (al->ipc == 0.0 && al->cycles == 0)
160 161 162 163 164
				show_title = true;
		} else
			show_title = true;
	}

165
	if (al->offset != -1 && percent_max != 0.0) {
166 167
		for (i = 0; i < ab->nr_events; i++) {
			ui_browser__set_percent_color(browser,
168
						al->samples[i].percent,
169 170
						current_entry);
			if (annotate_browser__opts.show_total_period) {
171
				ui_browser__printf(browser, "%11" PRIu64 " ",
172
						   al->samples[i].he.period);
173 174
			} else if (annotate_browser__opts.show_nr_samples) {
				ui_browser__printf(browser, "%6" PRIu64 " ",
175
						   al->samples[i].he.nr_samples);
176 177
			} else {
				ui_browser__printf(browser, "%6.2f ",
178
						   al->samples[i].percent);
179
			}
180
		}
181
	} else {
182
		ui_browser__set_percent_color(browser, 0, current_entry);
183 184

		if (!show_title)
185
			ui_browser__write_nstring(browser, " ", pcnt_width);
186 187
		else {
			ui_browser__printf(browser, "%*s", pcnt_width,
188 189
					   annotate_browser__opts.show_total_period ? "Period" :
					   annotate_browser__opts.show_nr_samples ? "Samples" : "Percent");
190
		}
191 192
	}
	if (ab->have_cycles) {
193
		if (al->ipc)
194
			ui_browser__printf(browser, "%*.2f ", ANNOTATION__IPC_WIDTH - 1, al->ipc);
195
		else if (!show_title)
196
			ui_browser__write_nstring(browser, " ", ANNOTATION__IPC_WIDTH);
197
		else
198
			ui_browser__printf(browser, "%*s ", ANNOTATION__IPC_WIDTH - 1, "IPC");
199

200
		if (al->cycles)
201
			ui_browser__printf(browser, "%*" PRIu64 " ",
202
					   ANNOTATION__CYCLES_WIDTH - 1, al->cycles);
203
		else if (!show_title)
204
			ui_browser__write_nstring(browser, " ", ANNOTATION__CYCLES_WIDTH);
205
		else
206
			ui_browser__printf(browser, "%*s ", ANNOTATION__CYCLES_WIDTH - 1, "Cycle");
207 208
	}

209
	SLsmg_write_char(' ');
210 211

	/* The scroll bar isn't being used */
212
	if (!browser->navkeypressed)
213 214
		width += 1;

215
	if (!*al->line)
216
		ui_browser__write_nstring(browser, " ", width - pcnt_width - cycles_width);
217 218
	else if (al->offset == -1) {
		if (al->line_nr && annotate_browser__opts.show_linenr)
219
			printed = scnprintf(bf, sizeof(bf), "%-*d ",
220
					ab->addr_width + 1, al->line_nr);
221 222
		else
			printed = scnprintf(bf, sizeof(bf), "%*s  ",
223
				    ab->addr_width, " ");
224
		ui_browser__write_nstring(browser, bf, printed);
225
		ui_browser__write_nstring(browser, al->line, width - printed - pcnt_width - cycles_width + 1);
226
	} else {
227
		u64 addr = al->offset;
228
		int color = -1;
229

230
		if (!annotate_browser__opts.use_offset)
231 232
			addr += ab->start;

233
		if (!annotate_browser__opts.use_offset) {
234
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
235
		} else {
236
			if (bl->jump_sources) {
237
				if (annotate_browser__opts.show_nr_jumps) {
238 239 240
					int prev;
					printed = scnprintf(bf, sizeof(bf), "%*d ",
							    ab->jumps_width,
241 242
							    bl->jump_sources);
					prev = annotate_browser__set_jumps_percent_color(ab, bl->jump_sources,
243
											 current_entry);
244
					ui_browser__write_nstring(browser, bf, printed);
245
					ui_browser__set_color(browser, prev);
246 247
				}

248
				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
249
						    ab->target_width, addr);
250
			} else {
251 252
				printed = scnprintf(bf, sizeof(bf), "%*s  ",
						    ab->addr_width, " ");
253 254
			}
		}
255

256
		if (change_color)
257
			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
258
		ui_browser__write_nstring(browser, bf, printed);
259
		if (change_color)
260
			ui_browser__set_color(browser, color);
261

262 263
		disasm_line__write(disasm_line(al), browser, bf, sizeof(bf));

264
		ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
265
	}
266

267
	if (current_entry)
268
		ab->selection = al;
269 270
}

271 272
static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
{
273
	if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins)
274
	    || !disasm_line__has_offset(dl)
275 276
	    || dl->ops.target.offset < 0
	    || dl->ops.target.offset >= (s64)symbol__size(sym))
277 278 279 280 281
		return false;

	return true;
}

282 283
static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
{
284
	struct disasm_line *pos = list_prev_entry(cursor, al.node);
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
	const char *name;

	if (!pos)
		return false;

	if (ins__is_lock(&pos->ins))
		name = pos->ops.locked.ins.name;
	else
		name = pos->ins.name;

	if (!name || !cursor->ins.name)
		return false;

	return ins__is_fused(ab->arch, name, cursor->ins.name);
}

301
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
302 303
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
304
	struct disasm_line *cursor = disasm_line(ab->selection);
305
	struct annotation_line *target;
306
	struct browser_line *btarget, *bcursor;
307
	unsigned int from, to;
308 309
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
310
	u8 pcnt_width = annotate_browser__pcnt_width(ab);
311
	int width;
312 313 314 315

	/* PLT symbols contain external offsets */
	if (strstr(sym->name, "@plt"))
		return;
316

317
	if (!disasm_line__is_valid_jump(cursor, sym))
318
		return;
319

320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
	/*
	 * This first was seen with a gcc function, _cpp_lex_token, that
	 * has the usual jumps:
	 *
	 *  │1159e6c: ↓ jne    115aa32 <_cpp_lex_token@@Base+0xf92>
	 *
	 * I.e. jumps to a label inside that function (_cpp_lex_token), and
	 * those works, but also this kind:
	 *
	 *  │1159e8b: ↓ jne    c469be <cpp_named_operator2name@@Base+0xa72>
	 *
	 *  I.e. jumps to another function, outside _cpp_lex_token, which
	 *  are not being correctly handled generating as a side effect references
	 *  to ab->offset[] entries that are set to NULL, so to make this code
	 *  more robust, check that here.
	 *
	 *  A proper fix for will be put in place, looking at the function
	 *  name right after the '<' token and probably treating this like a
	 *  'call' instruction.
	 */
340
	target = ab->offsets[cursor->ops.target.offset];
341 342 343 344 345
	if (target == NULL) {
		ui_helpline__printf("WARN: jump target inconsistency, press 'o', ab->offsets[%#x] = NULL\n",
				    cursor->ops.target.offset);
		return;
	}
346

347
	bcursor = browser_line(&cursor->al);
348
	btarget = browser_line(target);
349

350
	if (annotate_browser__opts.hide_src_code) {
351
		from = bcursor->idx_asm;
352 353
		to = btarget->idx_asm;
	} else {
354
		from = (u64)bcursor->idx;
355 356 357
		to = (u64)btarget->idx;
	}

358
	width = annotate_browser__cycles_width(ab);
J
Jin Yao 已提交
359

360
	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
J
Jin Yao 已提交
361 362
	__ui_browser__line_arrow(browser,
				 pcnt_width + 2 + ab->addr_width + width,
363
				 from, to);
364 365 366

	if (is_fused(ab, cursor)) {
		ui_browser__mark_fused(browser,
J
Jin Yao 已提交
367
				       pcnt_width + 3 + ab->addr_width + width,
368 369 370
				       from - 1,
				       to > from ? true : false);
	}
371 372 373 374
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
375
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
376
	int ret = ui_browser__list_head_refresh(browser);
377
	int pcnt_width = annotate_browser__pcnt_width(ab);
378

379
	if (annotate_browser__opts.jump_arrows)
380
		annotate_browser__draw_current_jump(browser);
381

382
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
383
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
384 385 386
	return ret;
}

387
static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
388 389 390
{
	int i;

391
	for (i = 0; i < a->samples_nr; i++) {
392
		if (a->samples[i].percent == b->samples[i].percent)
393
			continue;
394
		return a->samples[i].percent < b->samples[i].percent;
395 396 397 398
	}
	return 0;
}

399
static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
400
{
401
	struct rb_node **p = &root->rb_node;
402
	struct rb_node *parent = NULL;
403
	struct annotation_line *l;
404 405 406

	while (*p != NULL) {
		parent = *p;
407
		l = rb_entry(parent, struct annotation_line, rb_node);
408

409
		if (disasm__cmp(al, l))
410 411 412 413
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
414 415
	rb_link_node(&al->rb_node, parent, p);
	rb_insert_color(&al->rb_node, root);
416 417
}

418
static void annotate_browser__set_top(struct annotate_browser *browser,
419
				      struct annotation_line *pos, u32 idx)
420 421 422
{
	unsigned back;

423 424 425
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
426

427
	while (browser->b.top_idx != 0 && back != 0) {
428
		pos = list_entry(pos->node.prev, struct annotation_line, node);
429

430
		if (disasm_line__filter(&browser->b, &pos->node))
431 432
			continue;

433
		--browser->b.top_idx;
434 435 436
		--back;
	}

437
	browser->b.top = pos;
438
	browser->b.navkeypressed = true;
439 440 441 442 443
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
444
	struct browser_line *bpos;
445
	struct annotation_line *pos;
446
	u32 idx;
447

448 449
	pos = rb_entry(nd, struct annotation_line, rb_node);
	bpos = browser_line(pos);
450

451
	idx = bpos->idx;
452
	if (annotate_browser__opts.hide_src_code)
453 454
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
455
	browser->curr_hot = nd;
456 457
}

458
static void annotate_browser__calc_percent(struct annotate_browser *browser,
459
					   struct perf_evsel *evsel)
460
{
461 462
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
463
	struct annotation *notes = symbol__annotation(sym);
464
	struct disasm_line *pos;
465 466 467 468 469

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

470 471
	symbol__calc_percent(sym, evsel);

472
	list_for_each_entry(pos, &notes->src->source, al.node) {
473 474
		double max_percent = 0.0;
		int i;
475

476
		if (pos->al.offset == -1) {
477
			RB_CLEAR_NODE(&pos->al.rb_node);
478 479 480
			continue;
		}

481
		for (i = 0; i < pos->al.samples_nr; i++) {
482 483
			struct annotation_data *sample = &pos->al.samples[i];

484 485
			if (max_percent < sample->percent)
				max_percent = sample->percent;
486 487
		}

488
		if (max_percent < 0.01 && pos->al.ipc == 0) {
489
			RB_CLEAR_NODE(&pos->al.rb_node);
490 491
			continue;
		}
492
		disasm_rb_tree__insert(&browser->entries, &pos->al);
493 494 495 496 497 498
	}
	pthread_mutex_unlock(&notes->lock);

	browser->curr_hot = rb_last(&browser->entries);
}

499 500
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
501
	struct annotation_line *al;
502
	struct browser_line *bl;
503 504 505
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
506 507
	al = list_entry(browser->b.top, struct annotation_line, node);
	bl = browser_line(al);
508

509
	if (annotate_browser__opts.hide_src_code) {
510 511
		if (bl->idx_asm < offset)
			offset = bl->idx;
512 513

		browser->b.nr_entries = browser->nr_entries;
514
		annotate_browser__opts.hide_src_code = false;
515
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
516 517
		browser->b.top_idx = bl->idx - offset;
		browser->b.index = bl->idx;
518
	} else {
519
		if (bl->idx_asm < 0) {
520 521 522 523 524
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

525 526
		if (bl->idx_asm < offset)
			offset = bl->idx_asm;
527 528

		browser->b.nr_entries = browser->nr_asm_entries;
529
		annotate_browser__opts.hide_src_code = true;
530
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
531 532
		browser->b.top_idx = bl->idx_asm - offset;
		browser->b.index = bl->idx_asm;
533 534 535 536 537
	}

	return true;
}

538 539 540 541 542 543
static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
{
	ui_browser__reset_index(&browser->b);
	browser->b.nr_entries = browser->nr_asm_entries;
}

544 545 546 547 548 549 550 551
#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)

static int sym_title(struct symbol *sym, struct map *map, char *title,
		     size_t sz)
{
	return snprintf(title, sz, "%s  %s", sym->name, map->dso->long_name);
}

552 553
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
554
				    struct hist_browser_timer *hbt)
555 556
{
	struct map_symbol *ms = browser->b.priv;
557
	struct disasm_line *dl = disasm_line(browser->selection);
558
	struct annotation *notes;
559
	char title[SYM_TITLE_MAX_SIZE];
560

561
	if (!ins__is_call(&dl->ins))
562 563
		return false;

564
	if (!dl->ops.target.sym) {
565 566 567 568
		ui_helpline__puts("The called function was not found.");
		return true;
	}

569
	notes = symbol__annotation(dl->ops.target.sym);
570 571
	pthread_mutex_lock(&notes->lock);

572
	if (notes->src == NULL && symbol__alloc_hist(dl->ops.target.sym) < 0) {
573 574
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
575
			    dl->ops.target.sym->name);
576 577 578 579
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
580
	symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt);
581
	sym_title(ms->sym, ms->map, title, sizeof(title));
582
	ui_browser__show_title(&browser->b, title);
583 584 585
	return true;
}

586 587 588
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
589 590 591 592
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
593
	struct disasm_line *pos;
594 595

	*idx = 0;
596
	list_for_each_entry(pos, &notes->src->source, al.node) {
597
		if (pos->al.offset == offset)
598
			return pos;
599
		if (!disasm_line__filter(&browser->b, &pos->al.node))
600 601 602 603 604 605 606 607
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
608
	struct disasm_line *dl = disasm_line(browser->selection);
609
	u64 offset;
610
	s64 idx;
611

612
	if (!ins__is_jump(&dl->ins))
613 614
		return false;

615 616
	offset = dl->ops.target.offset;
	dl = annotate_browser__find_offset(browser, offset, &idx);
617
	if (dl == NULL) {
618
		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
619 620 621
		return true;
	}

622
	annotate_browser__set_top(browser, &dl->al, idx);
623

624 625 626
	return true;
}

627
static
628
struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
629
					  char *s, s64 *idx)
630 631 632 633
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
634
	struct annotation_line *al = browser->selection;
635 636

	*idx = browser->b.index;
637 638
	list_for_each_entry_continue(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
639 640 641 642
			continue;

		++*idx;

643 644
		if (al->line && strstr(al->line, s) != NULL)
			return al;
645 646 647 648 649 650 651
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
652
	struct annotation_line *al;
653 654
	s64 idx;

655 656
	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (al == NULL) {
657 658 659 660
		ui_helpline__puts("String not found!");
		return false;
	}

661
	annotate_browser__set_top(browser, al, idx);
662 663 664 665
	browser->searching_backwards = false;
	return true;
}

666
static
667
struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
668
						  char *s, s64 *idx)
669 670 671 672
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
673
	struct annotation_line *al = browser->selection;
674 675

	*idx = browser->b.index;
676 677
	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
678 679 680 681
			continue;

		--*idx;

682 683
		if (al->line && strstr(al->line, s) != NULL)
			return al;
684 685 686 687 688 689 690
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
691
	struct annotation_line *al;
692 693
	s64 idx;

694 695
	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (al == NULL) {
696 697 698 699
		ui_helpline__puts("String not found!");
		return false;
	}

700
	annotate_browser__set_top(browser, al, idx);
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752
	browser->searching_backwards = true;
	return true;
}

static bool annotate_browser__search_window(struct annotate_browser *browser,
					    int delay_secs)
{
	if (ui_browser__input_window("Search", "String: ", browser->search_bf,
				     "ENTER: OK, ESC: Cancel",
				     delay_secs * 2) != K_ENTER ||
	    !*browser->search_bf)
		return false;

	return true;
}

static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
{
	if (annotate_browser__search_window(browser, delay_secs))
		return __annotate_browser__search(browser);

	return false;
}

static bool annotate_browser__continue_search(struct annotate_browser *browser,
					      int delay_secs)
{
	if (!*browser->search_bf)
		return annotate_browser__search(browser, delay_secs);

	return __annotate_browser__search(browser);
}

static bool annotate_browser__search_reverse(struct annotate_browser *browser,
					   int delay_secs)
{
	if (annotate_browser__search_window(browser, delay_secs))
		return __annotate_browser__search_reverse(browser);

	return false;
}

static
bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
					       int delay_secs)
{
	if (!*browser->search_bf)
		return annotate_browser__search_reverse(browser, delay_secs);

	return __annotate_browser__search_reverse(browser);
}

753 754 755 756 757 758 759 760 761 762 763 764 765
static void annotate_browser__update_addr_width(struct annotate_browser *browser)
{
	if (annotate_browser__opts.use_offset)
		browser->target_width = browser->min_addr_width;
	else
		browser->target_width = browser->max_addr_width;

	browser->addr_width = browser->target_width;

	if (annotate_browser__opts.show_nr_jumps)
		browser->addr_width += browser->jumps_width + 1;
}

766 767
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
768
				 struct hist_browser_timer *hbt)
769 770
{
	struct rb_node *nd = NULL;
771
	struct map_symbol *ms = browser->b.priv;
772
	struct symbol *sym = ms->sym;
773
	const char *help = "Press 'h' for help on key bindings";
774
	int delay_secs = hbt ? hbt->refresh : 0;
775
	int key;
776
	char title[SYM_TITLE_MAX_SIZE];
777

778 779
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
780
		return -1;
781

782
	annotate_browser__calc_percent(browser, evsel);
783

784 785 786
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
787
	}
788

789
	nd = browser->curr_hot;
790

791
	while (1) {
792
		key = ui_browser__run(&browser->b, delay_secs);
793

794
		if (delay_secs != 0) {
795
			annotate_browser__calc_percent(browser, evsel);
796 797 798 799 800 801 802 803 804
			/*
			 * Current line focus got out of the list of most active
			 * lines, NULL it so that if TAB|UNTAB is pressed, we
			 * move to curr_hot (current hottest line).
			 */
			if (nd != NULL && RB_EMPTY_NODE(nd))
				nd = NULL;
		}

805
		switch (key) {
806
		case K_TIMER:
807 808
			if (hbt)
				hbt->timer(hbt->arg);
809 810

			if (delay_secs != 0)
811
				symbol__annotate_decay_histogram(sym, evsel->idx);
812
			continue;
813
		case K_TAB:
814 815 816
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
817
					nd = rb_last(&browser->entries);
818
			} else
819
				nd = browser->curr_hot;
820
			break;
821
		case K_UNTAB:
822
			if (nd != NULL) {
823 824
				nd = rb_next(nd);
				if (nd == NULL)
825
					nd = rb_first(&browser->entries);
826
			} else
827
				nd = browser->curr_hot;
828
			break;
829
		case K_F1:
830
		case 'h':
831
			ui_browser__help_window(&browser->b,
832 833 834
		"UP/DOWN/PGUP\n"
		"PGDN/SPACE    Navigate\n"
		"q/ESC/CTRL+C  Exit\n\n"
835 836
		"ENTER         Go to target\n"
		"ESC           Exit\n"
837 838
		"H             Go to hottest instruction\n"
		"TAB/shift+TAB Cycle thru hottest instructions\n"
839 840 841 842 843
		"j             Toggle showing jump to target arrows\n"
		"J             Toggle showing number of jump sources on targets\n"
		"n             Search next string\n"
		"o             Toggle disassembler output/simplified view\n"
		"s             Toggle source code view\n"
844
		"t             Circulate percent, total period, samples view\n"
845
		"/             Search string\n"
846
		"k             Toggle line numbers\n"
847
		"r             Run available scripts\n"
848
		"?             Search string backwards\n");
849
			continue;
850 851 852 853 854
		case 'r':
			{
				script_browse(NULL);
				continue;
			}
855 856 857 858
		case 'k':
			annotate_browser__opts.show_linenr =
				!annotate_browser__opts.show_linenr;
			break;
859
		case 'H':
860
			nd = browser->curr_hot;
861
			break;
862
		case 's':
863
			if (annotate_browser__toggle_source(browser))
864 865
				ui_helpline__puts(help);
			continue;
866
		case 'o':
867
			annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
868
			annotate_browser__update_addr_width(browser);
869
			continue;
870
		case 'j':
871
			annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
872
			continue;
873
		case 'J':
874
			annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
875
			annotate_browser__update_addr_width(browser);
876
			continue;
877
		case '/':
878
			if (annotate_browser__search(browser, delay_secs)) {
879 880 881 882 883
show_help:
				ui_helpline__puts(help);
			}
			continue;
		case 'n':
884 885 886
			if (browser->searching_backwards ?
			    annotate_browser__continue_search_reverse(browser, delay_secs) :
			    annotate_browser__continue_search(browser, delay_secs))
887 888 889
				goto show_help;
			continue;
		case '?':
890
			if (annotate_browser__search_reverse(browser, delay_secs))
891 892
				goto show_help;
			continue;
893 894 895 896
		case 'D': {
			static int seq;
			ui_helpline__pop();
			ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
897 898 899 900 901
					   seq++, browser->b.nr_entries,
					   browser->b.height,
					   browser->b.index,
					   browser->b.top_idx,
					   browser->nr_asm_entries);
902 903
		}
			continue;
904 905
		case K_ENTER:
		case K_RIGHT:
906 907 908
		{
			struct disasm_line *dl = disasm_line(browser->selection);

909
			if (browser->selection == NULL)
910
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
911
			else if (browser->selection->offset == -1)
912
				ui_helpline__puts("Actions are only available for assembly lines.");
913
			else if (!dl->ins.ops)
914
				goto show_sup_ins;
915
			else if (ins__is_ret(&dl->ins))
916
				goto out;
917
			else if (!(annotate_browser__jump(browser) ||
918
				     annotate_browser__callq(browser, evsel, hbt))) {
919
show_sup_ins:
920
				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
921
			}
922
			continue;
923
		}
924
		case 't':
925 926 927 928 929 930 931
			if (annotate_browser__opts.show_total_period) {
				annotate_browser__opts.show_total_period = false;
				annotate_browser__opts.show_nr_samples = true;
			} else if (annotate_browser__opts.show_nr_samples)
				annotate_browser__opts.show_nr_samples = false;
			else
				annotate_browser__opts.show_total_period = true;
932 933
			annotate_browser__update_addr_width(browser);
			continue;
934 935
		case K_LEFT:
		case K_ESC:
936 937
		case 'q':
		case CTRL('c'):
938
			goto out;
939 940
		default:
			continue;
941
		}
942 943

		if (nd != NULL)
944
			annotate_browser__set_rb_top(browser, nd);
945 946
	}
out:
947
	ui_browser__hide(&browser->b);
948
	return key;
949 950
}

951 952 953
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
			     struct hist_browser_timer *hbt)
{
954
	/* Set default value for show_total_period and show_nr_samples  */
955
	annotate_browser__opts.show_total_period =
956 957 958
		symbol_conf.show_total_period;
	annotate_browser__opts.show_nr_samples =
		symbol_conf.show_nr_samples;
959

960 961 962
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

963
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
964
			     struct hist_browser_timer *hbt)
965
{
966 967 968 969
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

970
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
971 972
}

973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000

static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end)
{
	unsigned n_insn = 0;
	u64 offset;

	for (offset = start; offset <= end; offset++) {
		if (browser->offsets[offset])
			n_insn++;
	}
	return n_insn;
}

static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end,
			   struct cyc_hist *ch)
{
	unsigned n_insn;
	u64 offset;

	n_insn = count_insn(browser, start, end);
	if (n_insn && ch->num && ch->cycles) {
		float ipc = n_insn / ((double)ch->cycles / (double)ch->num);

		/* Hide data when there are too many overlaps. */
		if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
			return;

		for (offset = start; offset <= end; offset++) {
1001
			struct annotation_line *al = browser->offsets[offset];
1002

1003 1004
			if (al)
				al->ipc = ipc;
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
		}
	}
}

/*
 * This should probably be in util/annotate.c to share with the tty
 * annotate, but right now we need the per byte offsets arrays,
 * which are only here.
 */
static void annotate__compute_ipc(struct annotate_browser *browser, size_t size,
			   struct symbol *sym)
{
	u64 offset;
	struct annotation *notes = symbol__annotation(sym);

	if (!notes->src || !notes->src->cycles_hist)
		return;

	pthread_mutex_lock(&notes->lock);
	for (offset = 0; offset < size; ++offset) {
		struct cyc_hist *ch;

		ch = &notes->src->cycles_hist[offset];
		if (ch && ch->cycles) {
1029
			struct annotation_line *al;
1030 1031 1032

			if (ch->have_start)
				count_and_fill(browser, ch->start, offset, ch);
1033 1034 1035
			al = browser->offsets[offset];
			if (al && ch->num_aggr)
				al->cycles = ch->cycles_aggr / ch->num_aggr;
1036 1037 1038 1039 1040 1041
			browser->have_cycles = true;
		}
	}
	pthread_mutex_unlock(&notes->lock);
}

1042 1043 1044 1045
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;
1046 1047 1048 1049 1050 1051
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;

	/* PLT symbols contain external offsets */
	if (strstr(sym->name, "@plt"))
		return;
1052 1053

	for (offset = 0; offset < size; ++offset) {
1054
		struct annotation_line *al = browser->offsets[offset];
1055 1056
		struct disasm_line *dl;
		struct browser_line *blt;
1057

1058 1059
		dl = disasm_line(al);

1060
		if (!disasm_line__is_valid_jump(dl, sym))
1061 1062
			continue;

1063 1064
		al = browser->offsets[dl->ops.target.offset];

1065 1066 1067 1068
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
1069
		if (al == NULL)
1070 1071
			continue;

1072 1073 1074
		blt = browser_line(al);
		if (++blt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = blt->jump_sources;
1075 1076

		++browser->nr_jumps;
1077 1078 1079
	}
}

1080 1081 1082 1083 1084 1085 1086 1087 1088
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

1089 1090
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
1091
			 struct hist_browser_timer *hbt)
1092
{
1093
	struct annotation_line *al;
1094
	struct annotation *notes;
1095
	size_t size;
1096 1097 1098 1099
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
1100 1101
	struct annotate_browser browser = {
		.b = {
1102
			.refresh = annotate_browser__refresh,
1103 1104
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
1105
			.filter  = disasm_line__filter,
1106
			.priv	 = &ms,
1107
			.use_navkeypressed = true,
1108
		},
1109
	};
1110
	int ret = -1, err;
1111
	int nr_pcnt = 1;
1112

1113
	if (sym == NULL)
1114 1115
		return -1;

1116 1117
	size = symbol__size(sym);

1118
	if (map->dso->annotate_warned)
1119 1120
		return -1;

1121
	browser.offsets = zalloc(size * sizeof(struct annotation_line *));
1122 1123 1124 1125 1126
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

1127
	if (perf_evsel__is_group_event(evsel))
1128 1129
		nr_pcnt = evsel->nr_members;

1130
	err = symbol__annotate(sym, map, evsel, sizeof(struct browser_line), &browser.arch);
1131 1132 1133 1134
	if (err) {
		char msg[BUFSIZ];
		symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
		ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
1135
		goto out_free_offsets;
1136 1137
	}

1138 1139
	symbol__calc_percent(sym, evsel);

1140
	ui_helpline__push("Press ESC to exit");
1141

1142
	notes = symbol__annotation(sym);
1143
	browser.start = map__rip_2objdump(map, sym->start);
1144

1145
	list_for_each_entry(al, &notes->src->source, node) {
1146
		struct browser_line *bpos;
1147
		size_t line_len = strlen(al->line);
1148

1149 1150
		if (browser.b.width < line_len)
			browser.b.width = line_len;
1151
		bpos = browser_line(al);
1152
		bpos->idx = browser.nr_entries++;
1153
		if (al->offset != -1) {
1154
			bpos->idx_asm = browser.nr_asm_entries++;
1155 1156 1157 1158 1159 1160 1161
			/*
			 * FIXME: short term bandaid to cope with assembly
			 * routines that comes with labels in the same column
			 * as the address in objdump, sigh.
			 *
			 * E.g. copy_user_generic_unrolled
 			 */
1162 1163
			if (al->offset < (s64)size)
				browser.offsets[al->offset] = al;
1164
		} else
1165
			bpos->idx_asm = -1;
1166 1167
	}

1168
	annotate_browser__mark_jump_targets(&browser, size);
1169
	annotate__compute_ipc(&browser, size, sym);
1170

1171
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1172
	browser.max_addr_width = hex_width(sym->end);
1173
	browser.jumps_width = width_jumps(browser.max_jump_sources);
1174
	browser.nr_events = nr_pcnt;
1175
	browser.b.nr_entries = browser.nr_entries;
1176
	browser.b.entries = &notes->src->source,
1177
	browser.b.width += 18; /* Percentage */
1178 1179 1180 1181 1182 1183

	if (annotate_browser__opts.hide_src_code)
		annotate_browser__init_asm_mode(&browser);

	annotate_browser__update_addr_width(&browser);

1184
	ret = annotate_browser__run(&browser, evsel, hbt);
1185 1186

	annotated_source__purge(notes->src);
1187 1188 1189

out_free_offsets:
	free(browser.offsets);
1190 1191
	return ret;
}
1192 1193 1194

#define ANNOTATE_CFG(n) \
	{ .name = #n, .value = &annotate_browser__opts.n, }
1195

1196 1197 1198
/*
 * Keep the entries sorted, they are bsearch'ed
 */
1199
static struct annotate_config {
1200 1201 1202 1203 1204
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
1205
	ANNOTATE_CFG(show_linenr),
1206
	ANNOTATE_CFG(show_nr_jumps),
1207
	ANNOTATE_CFG(show_nr_samples),
1208
	ANNOTATE_CFG(show_total_period),
1209
	ANNOTATE_CFG(use_offset),
1210 1211 1212 1213 1214 1215
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1216
	const struct annotate_config *cfg = cfgp;
1217 1218 1219 1220

	return strcmp(name, cfg->name);
}

1221 1222
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1223
{
1224
	struct annotate_config *cfg;
1225 1226
	const char *name;

1227
	if (!strstarts(var, "annotate."))
1228 1229 1230 1231
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1232
		      sizeof(struct annotate_config), annotate_config__cmp);
1233 1234

	if (cfg == NULL)
1235 1236 1237
		ui__warning("%s variable unknown, ignoring...", var);
	else
		*cfg->value = perf_config_bool(name, value);
1238 1239 1240 1241 1242 1243 1244
	return 0;
}

void annotate_browser__init(void)
{
	perf_config(annotate__config, NULL);
}