annotate.c 32.3 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
struct browser_disasm_line {
29 30 31 32
	struct rb_node			rb_node;
	u32				idx;
	int				idx_asm;
	int				jump_sources;
33 34 35 36
	/*
	 * actual length of this array is saved on the nr_events field
	 * of the struct annotate_browser
	 */
37
	struct disasm_line_samples	samples[1];
38 39
};

40 41 42 43
static struct annotate_browser_opt {
	bool hide_src_code,
	     use_offset,
	     jump_arrows,
44
	     show_linenr,
45
	     show_nr_jumps,
46
	     show_nr_samples,
47
	     show_total_period;
48 49 50 51 52
} annotate_browser__opts = {
	.use_offset	= true,
	.jump_arrows	= true,
};

53 54
struct arch;

55 56 57
struct annotate_browser {
	struct ui_browser b;
	struct rb_root	  entries;
58
	struct rb_node	  *curr_hot;
59
	struct disasm_line  *selection;
60
	struct disasm_line  **offsets;
61
	struct arch	    *arch;
62
	int		    nr_events;
63
	u64		    start;
64 65
	int		    nr_asm_entries;
	int		    nr_entries;
66 67
	int		    max_jump_sources;
	int		    nr_jumps;
68
	bool		    searching_backwards;
69
	bool		    have_cycles;
70
	u8		    addr_width;
71 72
	u8		    jumps_width;
	u8		    target_width;
73 74
	u8		    min_addr_width;
	u8		    max_addr_width;
75
	char		    search_bf[128];
76 77
};

78
static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
79
{
80
	return (struct browser_disasm_line *)(dl + 1);
81 82
}

83 84
static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
				void *entry)
85
{
86
	if (annotate_browser__opts.hide_src_code) {
87 88
		struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
		return dl->offset == -1;
89 90 91 92 93
	}

	return false;
}

94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
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);
}

113 114
static int annotate_browser__pcnt_width(struct annotate_browser *ab)
{
115
	return (annotate_browser__opts.show_total_period ? 12 : 7) * ab->nr_events;
116
}
117

118 119 120
static int annotate_browser__cycles_width(struct annotate_browser *ab)
{
	return ab->have_cycles ? IPC_WIDTH + CYCLES_WIDTH : 0;
121 122
}

123
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
124
{
125
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
126
	struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
127
	struct browser_disasm_line *bdl = disasm_line__browser(dl);
128
	bool current_entry = ui_browser__is_current_entry(browser, row);
129
	bool change_color = (!annotate_browser__opts.hide_src_code &&
130 131 132
			     (!current_entry || (browser->use_navkeypressed &&
					         !browser->navkeypressed)));
	int width = browser->width, printed;
133 134
	int i, pcnt_width = annotate_browser__pcnt_width(ab),
	       cycles_width = annotate_browser__cycles_width(ab);
135
	double percent_max = 0.0;
136
	char bf[256];
137
	bool show_title = false;
138

139
	for (i = 0; i < ab->nr_events; i++) {
140 141
		if (bdl->samples[i].percent > percent_max)
			percent_max = bdl->samples[i].percent;
142 143
	}

144 145 146 147 148 149 150 151
	if ((row == 0) && (dl->offset == -1 || percent_max == 0.0)) {
		if (ab->have_cycles) {
			if (dl->ipc == 0.0 && dl->cycles == 0)
				show_title = true;
		} else
			show_title = true;
	}

152
	if (dl->offset != -1 && percent_max != 0.0) {
153 154 155 156 157
		for (i = 0; i < ab->nr_events; i++) {
			ui_browser__set_percent_color(browser,
						bdl->samples[i].percent,
						current_entry);
			if (annotate_browser__opts.show_total_period) {
158
				ui_browser__printf(browser, "%11" PRIu64 " ",
159
						   bdl->samples[i].he.period);
160 161 162
			} else if (annotate_browser__opts.show_nr_samples) {
				ui_browser__printf(browser, "%6" PRIu64 " ",
						   bdl->samples[i].he.nr_samples);
163 164 165
			} else {
				ui_browser__printf(browser, "%6.2f ",
						   bdl->samples[i].percent);
166
			}
167
		}
168
	} else {
169
		ui_browser__set_percent_color(browser, 0, current_entry);
170 171

		if (!show_title)
172
			ui_browser__write_nstring(browser, " ", pcnt_width);
173 174
		else {
			ui_browser__printf(browser, "%*s", pcnt_width,
175 176
					   annotate_browser__opts.show_total_period ? "Period" :
					   annotate_browser__opts.show_nr_samples ? "Samples" : "Percent");
177
		}
178 179 180
	}
	if (ab->have_cycles) {
		if (dl->ipc)
181
			ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, dl->ipc);
182
		else if (!show_title)
183
			ui_browser__write_nstring(browser, " ", IPC_WIDTH);
184 185 186
		else
			ui_browser__printf(browser, "%*s ", IPC_WIDTH - 1, "IPC");

187
		if (dl->cycles)
188 189
			ui_browser__printf(browser, "%*" PRIu64 " ",
					   CYCLES_WIDTH - 1, dl->cycles);
190
		else if (!show_title)
191
			ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
192 193
		else
			ui_browser__printf(browser, "%*s ", CYCLES_WIDTH - 1, "Cycle");
194 195
	}

196
	SLsmg_write_char(' ');
197 198

	/* The scroll bar isn't being used */
199
	if (!browser->navkeypressed)
200 201
		width += 1;

202
	if (!*dl->line)
203
		ui_browser__write_nstring(browser, " ", width - pcnt_width - cycles_width);
204
	else if (dl->offset == -1) {
205 206 207 208 209
		if (dl->line_nr && annotate_browser__opts.show_linenr)
			printed = scnprintf(bf, sizeof(bf), "%-*d ",
					ab->addr_width + 1, dl->line_nr);
		else
			printed = scnprintf(bf, sizeof(bf), "%*s  ",
210
				    ab->addr_width, " ");
211
		ui_browser__write_nstring(browser, bf, printed);
212
		ui_browser__write_nstring(browser, dl->line, width - printed - pcnt_width - cycles_width + 1);
213
	} else {
214
		u64 addr = dl->offset;
215
		int color = -1;
216

217
		if (!annotate_browser__opts.use_offset)
218 219
			addr += ab->start;

220
		if (!annotate_browser__opts.use_offset) {
221
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
222
		} else {
223
			if (bdl->jump_sources) {
224
				if (annotate_browser__opts.show_nr_jumps) {
225 226 227 228 229 230
					int prev;
					printed = scnprintf(bf, sizeof(bf), "%*d ",
							    ab->jumps_width,
							    bdl->jump_sources);
					prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
											 current_entry);
231
					ui_browser__write_nstring(browser, bf, printed);
232
					ui_browser__set_color(browser, prev);
233 234
				}

235
				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
236
						    ab->target_width, addr);
237
			} else {
238 239
				printed = scnprintf(bf, sizeof(bf), "%*s  ",
						    ab->addr_width, " ");
240 241
			}
		}
242

243
		if (change_color)
244
			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
245
		ui_browser__write_nstring(browser, bf, printed);
246
		if (change_color)
247
			ui_browser__set_color(browser, color);
248 249
		if (dl->ins.ops && dl->ins.ops->scnprintf) {
			if (ins__is_jump(&dl->ins)) {
250
				bool fwd = dl->ops.target.offset > dl->offset;
251

252
				ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
253
								    SLSMG_UARROW_CHAR);
254
				SLsmg_write_char(' ');
255
			} else if (ins__is_call(&dl->ins)) {
256
				ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
257
				SLsmg_write_char(' ');
258
			} else if (ins__is_ret(&dl->ins)) {
259 260
				ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
				SLsmg_write_char(' ');
261
			} else {
262
				ui_browser__write_nstring(browser, " ", 2);
263
			}
264
		} else {
265
			ui_browser__write_nstring(browser, " ", 2);
266
		}
267

268
		disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
269
		ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
270
	}
271

272
	if (current_entry)
273
		ab->selection = dl;
274 275
}

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

	return true;
}

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

306
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
307 308
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
309 310
	struct disasm_line *cursor = ab->selection, *target;
	struct browser_disasm_line *btarget, *bcursor;
311
	unsigned int from, to;
312 313
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
314
	u8 pcnt_width = annotate_browser__pcnt_width(ab);
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
	target = ab->offsets[cursor->ops.target.offset];
	if (!target)
		return;
326

327 328
	bcursor = disasm_line__browser(cursor);
	btarget = disasm_line__browser(target);
329

330
	if (annotate_browser__opts.hide_src_code) {
331
		from = bcursor->idx_asm;
332 333
		to = btarget->idx_asm;
	} else {
334
		from = (u64)bcursor->idx;
335 336 337
		to = (u64)btarget->idx;
	}

338
	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
339 340
	__ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
				 from, to);
341 342 343 344 345 346 347

	if (is_fused(ab, cursor)) {
		ui_browser__mark_fused(browser,
				       pcnt_width + 3 + ab->addr_width,
				       from - 1,
				       to > from ? true : false);
	}
348 349 350 351
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
352
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
353
	int ret = ui_browser__list_head_refresh(browser);
354
	int pcnt_width = annotate_browser__pcnt_width(ab);
355

356
	if (annotate_browser__opts.jump_arrows)
357
		annotate_browser__draw_current_jump(browser);
358

359
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
360
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
361 362 363
	return ret;
}

364 365 366 367 368 369
static int disasm__cmp(struct browser_disasm_line *a,
		       struct browser_disasm_line *b, int nr_pcnt)
{
	int i;

	for (i = 0; i < nr_pcnt; i++) {
370
		if (a->samples[i].percent == b->samples[i].percent)
371
			continue;
372
		return a->samples[i].percent < b->samples[i].percent;
373 374 375 376 377 378
	}
	return 0;
}

static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl,
				   int nr_events)
379
{
380
	struct rb_node **p = &root->rb_node;
381
	struct rb_node *parent = NULL;
382
	struct browser_disasm_line *l;
383 384 385

	while (*p != NULL) {
		parent = *p;
386
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
387 388

		if (disasm__cmp(bdl, l, nr_events))
389 390 391 392
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
393 394
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
395 396
}

397
static void annotate_browser__set_top(struct annotate_browser *browser,
398
				      struct disasm_line *pos, u32 idx)
399 400 401
{
	unsigned back;

402 403 404
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
405

406
	while (browser->b.top_idx != 0 && back != 0) {
407
		pos = list_entry(pos->node.prev, struct disasm_line, node);
408

409
		if (disasm_line__filter(&browser->b, &pos->node))
410 411
			continue;

412
		--browser->b.top_idx;
413 414 415
		--back;
	}

416 417
	browser->b.top = pos;
	browser->b.navkeypressed = true;
418 419 420 421 422
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
423
	struct browser_disasm_line *bpos;
424
	struct disasm_line *pos;
425
	u32 idx;
426

427 428
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
429
	idx = bpos->idx;
430
	if (annotate_browser__opts.hide_src_code)
431 432
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
433
	browser->curr_hot = nd;
434 435
}

436
static void annotate_browser__calc_percent(struct annotate_browser *browser,
437
					   struct perf_evsel *evsel)
438
{
439 440
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
441
	struct annotation *notes = symbol__annotation(sym);
442 443
	struct disasm_line *pos, *next;
	s64 len = symbol__size(sym);
444 445 446 447 448 449

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
450
		struct browser_disasm_line *bpos = disasm_line__browser(pos);
451
		const char *path = NULL;
452 453
		double max_percent = 0.0;
		int i;
454 455 456 457 458 459 460 461

		if (pos->offset == -1) {
			RB_CLEAR_NODE(&bpos->rb_node);
			continue;
		}

		next = disasm__get_next_ip_line(&notes->src->source, pos);

462
		for (i = 0; i < browser->nr_events; i++) {
463
			struct sym_hist_entry sample;
464 465

			bpos->samples[i].percent = disasm__calc_percent(notes,
466 467 468
						evsel->idx + i,
						pos->offset,
						next ? next->offset : len,
469
						&path, &sample);
470
			bpos->samples[i].he = sample;
471

472 473
			if (max_percent < bpos->samples[i].percent)
				max_percent = bpos->samples[i].percent;
474 475
		}

476
		if (max_percent < 0.01 && pos->ipc == 0) {
477
			RB_CLEAR_NODE(&bpos->rb_node);
478 479
			continue;
		}
480 481
		disasm_rb_tree__insert(&browser->entries, bpos,
				       browser->nr_events);
482 483 484 485 486 487
	}
	pthread_mutex_unlock(&notes->lock);

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

488 489
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
490
	struct disasm_line *dl;
491
	struct browser_disasm_line *bdl;
492 493 494
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
495
	dl = list_entry(browser->b.top, struct disasm_line, node);
496
	bdl = disasm_line__browser(dl);
497

498
	if (annotate_browser__opts.hide_src_code) {
499 500
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
501 502

		browser->b.nr_entries = browser->nr_entries;
503
		annotate_browser__opts.hide_src_code = false;
504
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
505 506
		browser->b.top_idx = bdl->idx - offset;
		browser->b.index = bdl->idx;
507
	} else {
508
		if (bdl->idx_asm < 0) {
509 510 511 512 513
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

514 515
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
516 517

		browser->b.nr_entries = browser->nr_asm_entries;
518
		annotate_browser__opts.hide_src_code = true;
519
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
520 521
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
522 523 524 525 526
	}

	return true;
}

527 528 529 530 531 532
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;
}

533 534 535 536 537 538 539 540
#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);
}

541 542
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
543
				    struct hist_browser_timer *hbt)
544 545
{
	struct map_symbol *ms = browser->b.priv;
546
	struct disasm_line *dl = browser->selection;
547
	struct annotation *notes;
548 549
	struct addr_map_symbol target = {
		.map = ms->map,
550
		.addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
551
	};
552
	char title[SYM_TITLE_MAX_SIZE];
553

554
	if (!ins__is_call(&dl->ins))
555 556
		return false;

557
	if (map_groups__find_ams(&target) ||
558 559 560
	    map__rip_2objdump(target.map, target.map->map_ip(target.map,
							     target.addr)) !=
	    dl->ops.target.addr) {
561 562 563 564
		ui_helpline__puts("The called function was not found.");
		return true;
	}

565
	notes = symbol__annotation(target.sym);
566 567
	pthread_mutex_lock(&notes->lock);

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

	pthread_mutex_unlock(&notes->lock);
576 577
	symbol__tui_annotate(target.sym, target.map, evsel, hbt);
	sym_title(ms->sym, ms->map, title, sizeof(title));
578
	ui_browser__show_title(&browser->b, title);
579 580 581
	return true;
}

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

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

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
604
	struct disasm_line *dl = browser->selection;
605
	u64 offset;
606
	s64 idx;
607

608
	if (!ins__is_jump(&dl->ins))
609 610
		return false;

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

618
	annotate_browser__set_top(browser, dl, idx);
619

620 621 622
	return true;
}

623 624 625
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
626 627 628 629
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
630
	struct disasm_line *pos = browser->selection;
631 632 633

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
634
		if (disasm_line__filter(&browser->b, &pos->node))
635 636 637 638 639 640 641 642 643 644 645 646 647
			continue;

		++*idx;

		if (pos->line && strstr(pos->line, s) != NULL)
			return pos;
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
648
	struct disasm_line *dl;
649 650
	s64 idx;

651 652
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
653 654 655 656
		ui_helpline__puts("String not found!");
		return false;
	}

657
	annotate_browser__set_top(browser, dl, idx);
658 659 660 661
	browser->searching_backwards = false;
	return true;
}

662 663 664
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
665 666 667 668
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
669
	struct disasm_line *pos = browser->selection;
670 671 672

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
673
		if (disasm_line__filter(&browser->b, &pos->node))
674 675 676 677 678 679 680 681 682 683 684 685 686
			continue;

		--*idx;

		if (pos->line && strstr(pos->line, s) != NULL)
			return pos;
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
687
	struct disasm_line *dl;
688 689
	s64 idx;

690 691
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
692 693 694 695
		ui_helpline__puts("String not found!");
		return false;
	}

696
	annotate_browser__set_top(browser, dl, idx);
697 698 699 700 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
	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);
}

749 750 751 752 753 754 755 756 757 758 759 760 761
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;
}

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

774 775
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
776
		return -1;
777

778
	annotate_browser__calc_percent(browser, evsel);
779

780 781 782
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
783
	}
784

785
	nd = browser->curr_hot;
786

787
	while (1) {
788
		key = ui_browser__run(&browser->b, delay_secs);
789

790
		if (delay_secs != 0) {
791
			annotate_browser__calc_percent(browser, evsel);
792 793 794 795 796 797 798 799 800
			/*
			 * 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;
		}

801
		switch (key) {
802
		case K_TIMER:
803 804
			if (hbt)
				hbt->timer(hbt->arg);
805 806

			if (delay_secs != 0)
807
				symbol__annotate_decay_histogram(sym, evsel->idx);
808
			continue;
809
		case K_TAB:
810 811 812
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
813
					nd = rb_last(&browser->entries);
814
			} else
815
				nd = browser->curr_hot;
816
			break;
817
		case K_UNTAB:
818
			if (nd != NULL) {
819 820
				nd = rb_next(nd);
				if (nd == NULL)
821
					nd = rb_first(&browser->entries);
822
			} else
823
				nd = browser->curr_hot;
824
			break;
825
		case K_F1:
826
		case 'h':
827
			ui_browser__help_window(&browser->b,
828 829 830
		"UP/DOWN/PGUP\n"
		"PGDN/SPACE    Navigate\n"
		"q/ESC/CTRL+C  Exit\n\n"
831 832
		"ENTER         Go to target\n"
		"ESC           Exit\n"
833 834
		"H             Go to hottest instruction\n"
		"TAB/shift+TAB Cycle thru hottest instructions\n"
835 836 837 838 839
		"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"
840
		"t             Circulate percent, total period, samples view\n"
841
		"/             Search string\n"
842
		"k             Toggle line numbers\n"
843
		"r             Run available scripts\n"
844
		"?             Search string backwards\n");
845
			continue;
846 847 848 849 850
		case 'r':
			{
				script_browse(NULL);
				continue;
			}
851 852 853 854
		case 'k':
			annotate_browser__opts.show_linenr =
				!annotate_browser__opts.show_linenr;
			break;
855
		case 'H':
856
			nd = browser->curr_hot;
857
			break;
858
		case 's':
859
			if (annotate_browser__toggle_source(browser))
860 861
				ui_helpline__puts(help);
			continue;
862
		case 'o':
863
			annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
864
			annotate_browser__update_addr_width(browser);
865
			continue;
866
		case 'j':
867
			annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
868
			continue;
869
		case 'J':
870
			annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
871
			annotate_browser__update_addr_width(browser);
872
			continue;
873
		case '/':
874
			if (annotate_browser__search(browser, delay_secs)) {
875 876 877 878 879
show_help:
				ui_helpline__puts(help);
			}
			continue;
		case 'n':
880 881 882
			if (browser->searching_backwards ?
			    annotate_browser__continue_search_reverse(browser, delay_secs) :
			    annotate_browser__continue_search(browser, delay_secs))
883 884 885
				goto show_help;
			continue;
		case '?':
886
			if (annotate_browser__search_reverse(browser, delay_secs))
887 888
				goto show_help;
			continue;
889 890 891 892
		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",
893 894 895 896 897
					   seq++, browser->b.nr_entries,
					   browser->b.height,
					   browser->b.index,
					   browser->b.top_idx,
					   browser->nr_asm_entries);
898 899
		}
			continue;
900 901
		case K_ENTER:
		case K_RIGHT:
902
			if (browser->selection == NULL)
903
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
904
			else if (browser->selection->offset == -1)
905
				ui_helpline__puts("Actions are only available for assembly lines.");
906
			else if (!browser->selection->ins.ops)
907
				goto show_sup_ins;
908
			else if (ins__is_ret(&browser->selection->ins))
909
				goto out;
910
			else if (!(annotate_browser__jump(browser) ||
911
				     annotate_browser__callq(browser, evsel, hbt))) {
912
show_sup_ins:
913
				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
914
			}
915
			continue;
916
		case 't':
917 918 919 920 921 922 923
			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;
924 925
			annotate_browser__update_addr_width(browser);
			continue;
926 927
		case K_LEFT:
		case K_ESC:
928 929
		case 'q':
		case CTRL('c'):
930
			goto out;
931 932
		default:
			continue;
933
		}
934 935

		if (nd != NULL)
936
			annotate_browser__set_rb_top(browser, nd);
937 938
	}
out:
939
	ui_browser__hide(&browser->b);
940
	return key;
941 942
}

943 944 945
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
			     struct hist_browser_timer *hbt)
{
946
	/* Set default value for show_total_period and show_nr_samples  */
947
	annotate_browser__opts.show_total_period =
948 949 950
		symbol_conf.show_total_period;
	annotate_browser__opts.show_nr_samples =
		symbol_conf.show_nr_samples;
951

952 953 954
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

955
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
956
			     struct hist_browser_timer *hbt)
957
{
958 959 960 961
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

962
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
963 964
}

965 966 967 968 969 970 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 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033

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++) {
			struct disasm_line *dl = browser->offsets[offset];

			if (dl)
				dl->ipc = ipc;
		}
	}
}

/*
 * 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) {
			struct disasm_line *dl;

			if (ch->have_start)
				count_and_fill(browser, ch->start, offset, ch);
			dl = browser->offsets[offset];
			if (dl && ch->num_aggr)
				dl->cycles = ch->cycles_aggr / ch->num_aggr;
			browser->have_cycles = true;
		}
	}
	pthread_mutex_unlock(&notes->lock);
}

1034 1035 1036 1037
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;
1038 1039 1040 1041 1042 1043
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;

	/* PLT symbols contain external offsets */
	if (strstr(sym->name, "@plt"))
		return;
1044 1045 1046 1047 1048

	for (offset = 0; offset < size; ++offset) {
		struct disasm_line *dl = browser->offsets[offset], *dlt;
		struct browser_disasm_line *bdlt;

1049
		if (!disasm_line__is_valid_jump(dl, sym))
1050 1051
			continue;

1052
		dlt = browser->offsets[dl->ops.target.offset];
1053 1054 1055 1056 1057 1058 1059
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
		if (dlt == NULL)
			continue;

1060
		bdlt = disasm_line__browser(dlt);
1061 1062 1063 1064
		if (++bdlt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = bdlt->jump_sources;

		++browser->nr_jumps;
1065 1066 1067
	}
}

1068 1069 1070 1071 1072 1073 1074 1075 1076
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

1077 1078
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
1079
			 struct hist_browser_timer *hbt)
1080
{
1081
	struct disasm_line *pos, *n;
1082
	struct annotation *notes;
1083
	size_t size;
1084 1085 1086 1087
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
1088 1089
	struct annotate_browser browser = {
		.b = {
1090
			.refresh = annotate_browser__refresh,
1091 1092
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
1093
			.filter  = disasm_line__filter,
1094
			.priv	 = &ms,
1095
			.use_navkeypressed = true,
1096
		},
1097
	};
1098
	int ret = -1, err;
1099 1100
	int nr_pcnt = 1;
	size_t sizeof_bdl = sizeof(struct browser_disasm_line);
1101

1102
	if (sym == NULL)
1103 1104
		return -1;

1105 1106
	size = symbol__size(sym);

1107
	if (map->dso->annotate_warned)
1108 1109
		return -1;

1110 1111 1112 1113 1114 1115
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

1116 1117
	if (perf_evsel__is_group_event(evsel)) {
		nr_pcnt = evsel->nr_members;
1118 1119
		sizeof_bdl += sizeof(struct disasm_line_samples) *
		  (nr_pcnt - 1);
1120 1121
	}

1122
	err = symbol__disassemble(sym, map, perf_evsel__env_arch(evsel),
1123 1124
				  sizeof_bdl, &browser.arch,
				  perf_evsel__env_cpuid(evsel));
1125 1126 1127 1128
	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);
1129
		goto out_free_offsets;
1130 1131
	}

1132
	ui_helpline__push("Press ESC to exit");
1133

1134
	notes = symbol__annotation(sym);
1135
	browser.start = map__rip_2objdump(map, sym->start);
1136

1137
	list_for_each_entry(pos, &notes->src->source, node) {
1138
		struct browser_disasm_line *bpos;
1139
		size_t line_len = strlen(pos->line);
1140

1141 1142
		if (browser.b.width < line_len)
			browser.b.width = line_len;
1143 1144
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
1145
		if (pos->offset != -1) {
1146
			bpos->idx_asm = browser.nr_asm_entries++;
1147 1148 1149 1150 1151 1152 1153 1154 1155
			/*
			 * 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
 			 */
			if (pos->offset < (s64)size)
				browser.offsets[pos->offset] = pos;
1156
		} else
1157
			bpos->idx_asm = -1;
1158 1159
	}

1160
	annotate_browser__mark_jump_targets(&browser, size);
1161
	annotate__compute_ipc(&browser, size, sym);
1162

1163
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1164
	browser.max_addr_width = hex_width(sym->end);
1165
	browser.jumps_width = width_jumps(browser.max_jump_sources);
1166
	browser.nr_events = nr_pcnt;
1167
	browser.b.nr_entries = browser.nr_entries;
1168
	browser.b.entries = &notes->src->source,
1169
	browser.b.width += 18; /* Percentage */
1170 1171 1172 1173 1174 1175

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

	annotate_browser__update_addr_width(&browser);

1176
	ret = annotate_browser__run(&browser, evsel, hbt);
1177
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
1178
		list_del(&pos->node);
1179
		disasm_line__free(pos);
1180
	}
1181 1182 1183

out_free_offsets:
	free(browser.offsets);
1184 1185
	return ret;
}
1186 1187 1188

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

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

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1210
	const struct annotate_config *cfg = cfgp;
1211 1212 1213 1214

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

1215 1216
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1217
{
1218
	struct annotate_config *cfg;
1219 1220
	const char *name;

1221
	if (!strstarts(var, "annotate."))
1222 1223 1224 1225
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1226
		      sizeof(struct annotate_config), annotate_config__cmp);
1227 1228

	if (cfg == NULL)
1229 1230 1231
		ui__warning("%s variable unknown, ignoring...", var);
	else
		*cfg->value = perf_config_bool(name, value);
1232 1233 1234 1235 1236 1237 1238
	return 0;
}

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