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
#define IPC_WIDTH 6
#define CYCLES_WIDTH 6

28 29 30 31
struct browser_line {
	u32	idx;
	int	idx_asm;
	int	jump_sources;
32 33
};

34
static struct annotation_options annotate_browser__opts = {
35 36 37 38
	.use_offset	= true,
	.jump_arrows	= true,
};

39 40
struct arch;

41
struct annotate_browser {
42 43 44 45
	struct ui_browser	    b;
	struct rb_root		    entries;
	struct rb_node		   *curr_hot;
	struct annotation_line	   *selection;
46
	struct annotation_line	  **offsets;
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
	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];
62 63
};

64
static inline struct browser_line *browser_line(struct annotation_line *al)
65
{
66 67 68 69
	void *ptr = al;

	ptr = container_of(al, struct disasm_line, al);
	return ptr - sizeof(struct browser_line);
70 71
}

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

		return al->offset == -1;
79 80 81 82 83
	}

	return false;
}

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
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);
}

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

108 109 110
static int annotate_browser__cycles_width(struct annotate_browser *ab)
{
	return ab->have_cycles ? IPC_WIDTH + CYCLES_WIDTH : 0;
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 136 137 138
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);
}

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

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

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

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

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

203
		if (al->cycles)
204
			ui_browser__printf(browser, "%*" PRIu64 " ",
205
					   CYCLES_WIDTH - 1, al->cycles);
206
		else if (!show_title)
207
			ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
208 209
		else
			ui_browser__printf(browser, "%*s ", CYCLES_WIDTH - 1, "Cycle");
210 211
	}

212
	SLsmg_write_char(' ');
213 214

	/* The scroll bar isn't being used */
215
	if (!browser->navkeypressed)
216 217
		width += 1;

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

233
		if (!annotate_browser__opts.use_offset)
234 235
			addr += ab->start;

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

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

259
		if (change_color)
260
			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
261
		ui_browser__write_nstring(browser, bf, printed);
262
		if (change_color)
263
			ui_browser__set_color(browser, color);
264

265 266
		disasm_line__write(disasm_line(al), browser, bf, sizeof(bf));

267
		ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
268
	}
269

270
	if (current_entry)
271
		ab->selection = al;
272 273
}

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

	return true;
}

285 286
static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
{
287
	struct disasm_line *pos = list_prev_entry(cursor, al.node);
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
	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);
}

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

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

320
	if (!disasm_line__is_valid_jump(cursor, sym))
321
		return;
322

323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
	/*
	 * 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.
	 */
343
	target = ab->offsets[cursor->ops.target.offset];
344 345 346 347 348
	if (target == NULL) {
		ui_helpline__printf("WARN: jump target inconsistency, press 'o', ab->offsets[%#x] = NULL\n",
				    cursor->ops.target.offset);
		return;
	}
349

350
	bcursor = browser_line(&cursor->al);
351
	btarget = browser_line(target);
352

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

J
Jin Yao 已提交
361 362 363
	if (ab->have_cycles)
		width = IPC_WIDTH + CYCLES_WIDTH;

364
	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
J
Jin Yao 已提交
365 366
	__ui_browser__line_arrow(browser,
				 pcnt_width + 2 + ab->addr_width + width,
367
				 from, to);
368 369 370

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

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
379
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
380
	int ret = ui_browser__list_head_refresh(browser);
381
	int pcnt_width = annotate_browser__pcnt_width(ab);
382

383
	if (annotate_browser__opts.jump_arrows)
384
		annotate_browser__draw_current_jump(browser);
385

386
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
387
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
388 389 390
	return ret;
}

391
static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
392 393 394
{
	int i;

395
	for (i = 0; i < a->samples_nr; i++) {
396
		if (a->samples[i].percent == b->samples[i].percent)
397
			continue;
398
		return a->samples[i].percent < b->samples[i].percent;
399 400 401 402
	}
	return 0;
}

403
static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
404
{
405
	struct rb_node **p = &root->rb_node;
406
	struct rb_node *parent = NULL;
407
	struct annotation_line *l;
408 409 410

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

413
		if (disasm__cmp(al, l))
414 415 416 417
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
418 419
	rb_link_node(&al->rb_node, parent, p);
	rb_insert_color(&al->rb_node, root);
420 421
}

422
static void annotate_browser__set_top(struct annotate_browser *browser,
423
				      struct annotation_line *pos, u32 idx)
424 425 426
{
	unsigned back;

427 428 429
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
430

431
	while (browser->b.top_idx != 0 && back != 0) {
432
		pos = list_entry(pos->node.prev, struct annotation_line, node);
433

434
		if (disasm_line__filter(&browser->b, &pos->node))
435 436
			continue;

437
		--browser->b.top_idx;
438 439 440
		--back;
	}

441
	browser->b.top = pos;
442
	browser->b.navkeypressed = true;
443 444 445 446 447
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
448
	struct browser_line *bpos;
449
	struct annotation_line *pos;
450
	u32 idx;
451

452 453
	pos = rb_entry(nd, struct annotation_line, rb_node);
	bpos = browser_line(pos);
454

455
	idx = bpos->idx;
456
	if (annotate_browser__opts.hide_src_code)
457 458
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
459
	browser->curr_hot = nd;
460 461
}

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

474 475
	symbol__calc_percent(sym, evsel);

476
	list_for_each_entry(pos, &notes->src->source, al.node) {
477 478
		double max_percent = 0.0;
		int i;
479

480
		if (pos->al.offset == -1) {
481
			RB_CLEAR_NODE(&pos->al.rb_node);
482 483 484
			continue;
		}

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

488 489
			if (max_percent < sample->percent)
				max_percent = sample->percent;
490 491
		}

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

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

503 504
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
505
	struct annotation_line *al;
506
	struct browser_line *bl;
507 508 509
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
510 511
	al = list_entry(browser->b.top, struct annotation_line, node);
	bl = browser_line(al);
512

513
	if (annotate_browser__opts.hide_src_code) {
514 515
		if (bl->idx_asm < offset)
			offset = bl->idx;
516 517

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

529 530
		if (bl->idx_asm < offset)
			offset = bl->idx_asm;
531 532

		browser->b.nr_entries = browser->nr_asm_entries;
533
		annotate_browser__opts.hide_src_code = true;
534
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
535 536
		browser->b.top_idx = bl->idx_asm - offset;
		browser->b.index = bl->idx_asm;
537 538 539 540 541
	}

	return true;
}

542 543 544 545 546 547
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;
}

548 549 550 551 552 553 554 555
#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);
}

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

565
	if (!ins__is_call(&dl->ins))
566 567
		return false;

568
	if (!dl->ops.target.sym) {
569 570 571 572
		ui_helpline__puts("The called function was not found.");
		return true;
	}

573
	notes = symbol__annotation(dl->ops.target.sym);
574 575
	pthread_mutex_lock(&notes->lock);

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

	pthread_mutex_unlock(&notes->lock);
584
	symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt);
585
	sym_title(ms->sym, ms->map, title, sizeof(title));
586
	ui_browser__show_title(&browser->b, title);
587 588 589
	return true;
}

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

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

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
612
	struct disasm_line *dl = disasm_line(browser->selection);
613
	u64 offset;
614
	s64 idx;
615

616
	if (!ins__is_jump(&dl->ins))
617 618
		return false;

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

626
	annotate_browser__set_top(browser, &dl->al, idx);
627

628 629 630
	return true;
}

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

	*idx = browser->b.index;
641 642
	list_for_each_entry_continue(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
643 644 645 646
			continue;

		++*idx;

647 648
		if (al->line && strstr(al->line, s) != NULL)
			return al;
649 650 651 652 653 654 655
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
656
	struct annotation_line *al;
657 658
	s64 idx;

659 660
	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (al == NULL) {
661 662 663 664
		ui_helpline__puts("String not found!");
		return false;
	}

665
	annotate_browser__set_top(browser, al, idx);
666 667 668 669
	browser->searching_backwards = false;
	return true;
}

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

	*idx = browser->b.index;
680 681
	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
682 683 684 685
			continue;

		--*idx;

686 687
		if (al->line && strstr(al->line, s) != NULL)
			return al;
688 689 690 691 692 693 694
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
695
	struct annotation_line *al;
696 697
	s64 idx;

698 699
	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (al == NULL) {
700 701 702 703
		ui_helpline__puts("String not found!");
		return false;
	}

704
	annotate_browser__set_top(browser, al, idx);
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 753 754 755 756
	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);
}

757 758 759 760 761 762 763 764 765 766 767 768 769
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;
}

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

782 783
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
784
		return -1;
785

786
	annotate_browser__calc_percent(browser, evsel);
787

788 789 790
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
791
	}
792

793
	nd = browser->curr_hot;
794

795
	while (1) {
796
		key = ui_browser__run(&browser->b, delay_secs);
797

798
		if (delay_secs != 0) {
799
			annotate_browser__calc_percent(browser, evsel);
800 801 802 803 804 805 806 807 808
			/*
			 * 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;
		}

809
		switch (key) {
810
		case K_TIMER:
811 812
			if (hbt)
				hbt->timer(hbt->arg);
813 814

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

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

		if (nd != NULL)
948
			annotate_browser__set_rb_top(browser, nd);
949 950
	}
out:
951
	ui_browser__hide(&browser->b);
952
	return key;
953 954
}

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

964 965 966
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

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

974
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
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 1001 1002 1003 1004

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++) {
1005
			struct annotation_line *al = browser->offsets[offset];
1006

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

/*
 * 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) {
1033
			struct annotation_line *al;
1034 1035 1036

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

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

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

	for (offset = 0; offset < size; ++offset) {
1058
		struct annotation_line *al = browser->offsets[offset];
1059 1060
		struct disasm_line *dl;
		struct browser_line *blt;
1061

1062 1063
		dl = disasm_line(al);

1064
		if (!disasm_line__is_valid_jump(dl, sym))
1065 1066
			continue;

1067 1068
		al = browser->offsets[dl->ops.target.offset];

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

1076 1077 1078
		blt = browser_line(al);
		if (++blt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = blt->jump_sources;
1079 1080

		++browser->nr_jumps;
1081 1082 1083
	}
}

1084 1085 1086 1087 1088 1089 1090 1091 1092
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

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

1117
	if (sym == NULL)
1118 1119
		return -1;

1120 1121
	size = symbol__size(sym);

1122
	if (map->dso->annotate_warned)
1123 1124
		return -1;

1125
	browser.offsets = zalloc(size * sizeof(struct annotation_line *));
1126 1127 1128 1129 1130
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

1131
	if (perf_evsel__is_group_event(evsel))
1132 1133
		nr_pcnt = evsel->nr_members;

1134
	err = symbol__annotate(sym, map, evsel, sizeof(struct browser_line), &browser.arch);
1135 1136 1137 1138
	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);
1139
		goto out_free_offsets;
1140 1141
	}

1142 1143
	symbol__calc_percent(sym, evsel);

1144
	ui_helpline__push("Press ESC to exit");
1145

1146
	notes = symbol__annotation(sym);
1147
	browser.start = map__rip_2objdump(map, sym->start);
1148

1149
	list_for_each_entry(al, &notes->src->source, node) {
1150
		struct browser_line *bpos;
1151
		size_t line_len = strlen(al->line);
1152

1153 1154
		if (browser.b.width < line_len)
			browser.b.width = line_len;
1155
		bpos = browser_line(al);
1156
		bpos->idx = browser.nr_entries++;
1157
		if (al->offset != -1) {
1158
			bpos->idx_asm = browser.nr_asm_entries++;
1159 1160 1161 1162 1163 1164 1165
			/*
			 * 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
 			 */
1166 1167
			if (al->offset < (s64)size)
				browser.offsets[al->offset] = al;
1168
		} else
1169
			bpos->idx_asm = -1;
1170 1171
	}

1172
	annotate_browser__mark_jump_targets(&browser, size);
1173
	annotate__compute_ipc(&browser, size, sym);
1174

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

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

	annotate_browser__update_addr_width(&browser);

1188
	ret = annotate_browser__run(&browser, evsel, hbt);
1189 1190

	annotated_source__purge(notes->src);
1191 1192 1193

out_free_offsets:
	free(browser.offsets);
1194 1195
	return ret;
}
1196 1197 1198

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

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

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1220
	const struct annotate_config *cfg = cfgp;
1221 1222 1223 1224

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

1225 1226
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1227
{
1228
	struct annotate_config *cfg;
1229 1230
	const char *name;

1231
	if (!strstarts(var, "annotate."))
1232 1233 1234 1235
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1236
		      sizeof(struct annotate_config), annotate_config__cmp);
1237 1238

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

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