annotate.c 31.8 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 35 36 37
static struct annotate_browser_opt {
	bool hide_src_code,
	     use_offset,
	     jump_arrows,
38
	     show_linenr,
39
	     show_nr_jumps,
40
	     show_nr_samples,
41
	     show_total_period;
42 43 44 45 46
} annotate_browser__opts = {
	.use_offset	= true,
	.jump_arrows	= true,
};

47 48
struct arch;

49
struct annotate_browser {
50 51 52 53
	struct ui_browser	    b;
	struct rb_root		    entries;
	struct rb_node		   *curr_hot;
	struct annotation_line	   *selection;
54
	struct annotation_line	  **offsets;
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
	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];
70 71
};

72
static inline struct browser_line *browser_line(struct annotation_line *al)
73
{
74 75 76 77
	void *ptr = al;

	ptr = container_of(al, struct disasm_line, al);
	return ptr - sizeof(struct browser_line);
78 79
}

80 81
static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
				void *entry)
82
{
83
	if (annotate_browser__opts.hide_src_code) {
84 85 86
		struct annotation_line *al = list_entry(entry, struct annotation_line, node);

		return al->offset == -1;
87 88 89 90 91
	}

	return false;
}

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

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

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

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
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);
}

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

163
	for (i = 0; i < ab->nr_events; i++) {
164 165
		if (al->samples[i].percent > percent_max)
			percent_max = al->samples[i].percent;
166 167
	}

168
	if ((row == 0) && (al->offset == -1 || percent_max == 0.0)) {
169
		if (ab->have_cycles) {
170
			if (al->ipc == 0.0 && al->cycles == 0)
171 172 173 174 175
				show_title = true;
		} else
			show_title = true;
	}

176
	if (al->offset != -1 && percent_max != 0.0) {
177 178
		for (i = 0; i < ab->nr_events; i++) {
			ui_browser__set_percent_color(browser,
179
						al->samples[i].percent,
180 181
						current_entry);
			if (annotate_browser__opts.show_total_period) {
182
				ui_browser__printf(browser, "%11" PRIu64 " ",
183
						   al->samples[i].he.period);
184 185
			} else if (annotate_browser__opts.show_nr_samples) {
				ui_browser__printf(browser, "%6" PRIu64 " ",
186
						   al->samples[i].he.nr_samples);
187 188
			} else {
				ui_browser__printf(browser, "%6.2f ",
189
						   al->samples[i].percent);
190
			}
191
		}
192
	} else {
193
		ui_browser__set_percent_color(browser, 0, current_entry);
194 195

		if (!show_title)
196
			ui_browser__write_nstring(browser, " ", pcnt_width);
197 198
		else {
			ui_browser__printf(browser, "%*s", pcnt_width,
199 200
					   annotate_browser__opts.show_total_period ? "Period" :
					   annotate_browser__opts.show_nr_samples ? "Samples" : "Percent");
201
		}
202 203
	}
	if (ab->have_cycles) {
204 205
		if (al->ipc)
			ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, al->ipc);
206
		else if (!show_title)
207
			ui_browser__write_nstring(browser, " ", IPC_WIDTH);
208 209 210
		else
			ui_browser__printf(browser, "%*s ", IPC_WIDTH - 1, "IPC");

211
		if (al->cycles)
212
			ui_browser__printf(browser, "%*" PRIu64 " ",
213
					   CYCLES_WIDTH - 1, al->cycles);
214
		else if (!show_title)
215
			ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
216 217
		else
			ui_browser__printf(browser, "%*s ", CYCLES_WIDTH - 1, "Cycle");
218 219
	}

220
	SLsmg_write_char(' ');
221 222

	/* The scroll bar isn't being used */
223
	if (!browser->navkeypressed)
224 225
		width += 1;

226
	if (!*al->line)
227
		ui_browser__write_nstring(browser, " ", width - pcnt_width - cycles_width);
228 229
	else if (al->offset == -1) {
		if (al->line_nr && annotate_browser__opts.show_linenr)
230
			printed = scnprintf(bf, sizeof(bf), "%-*d ",
231
					ab->addr_width + 1, al->line_nr);
232 233
		else
			printed = scnprintf(bf, sizeof(bf), "%*s  ",
234
				    ab->addr_width, " ");
235
		ui_browser__write_nstring(browser, bf, printed);
236
		ui_browser__write_nstring(browser, al->line, width - printed - pcnt_width - cycles_width + 1);
237
	} else {
238
		u64 addr = al->offset;
239
		int color = -1;
240

241
		if (!annotate_browser__opts.use_offset)
242 243
			addr += ab->start;

244
		if (!annotate_browser__opts.use_offset) {
245
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
246
		} else {
247
			if (bl->jump_sources) {
248
				if (annotate_browser__opts.show_nr_jumps) {
249 250 251
					int prev;
					printed = scnprintf(bf, sizeof(bf), "%*d ",
							    ab->jumps_width,
252 253
							    bl->jump_sources);
					prev = annotate_browser__set_jumps_percent_color(ab, bl->jump_sources,
254
											 current_entry);
255
					ui_browser__write_nstring(browser, bf, printed);
256
					ui_browser__set_color(browser, prev);
257 258
				}

259
				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
260
						    ab->target_width, addr);
261
			} else {
262 263
				printed = scnprintf(bf, sizeof(bf), "%*s  ",
						    ab->addr_width, " ");
264 265
			}
		}
266

267
		if (change_color)
268
			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
269
		ui_browser__write_nstring(browser, bf, printed);
270
		if (change_color)
271
			ui_browser__set_color(browser, color);
272

273 274
		disasm_line__write(disasm_line(al), browser, bf, sizeof(bf));

275
		ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
276
	}
277

278
	if (current_entry)
279
		ab->selection = al;
280 281
}

282 283
static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
{
284
	if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins)
285
	    || !disasm_line__has_offset(dl)
286 287
	    || dl->ops.target.offset < 0
	    || dl->ops.target.offset >= (s64)symbol__size(sym))
288 289 290 291 292
		return false;

	return true;
}

293 294
static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
{
295
	struct disasm_line *pos = list_prev_entry(cursor, al.node);
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
	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);
}

312
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
313 314
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
315
	struct disasm_line *cursor = disasm_line(ab->selection);
316
	struct annotation_line *target;
317
	struct browser_line *btarget, *bcursor;
318
	unsigned int from, to;
319 320
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
321
	u8 pcnt_width = annotate_browser__pcnt_width(ab);
322 323 324 325

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

327
	if (!disasm_line__is_valid_jump(cursor, sym))
328
		return;
329

330
	target = ab->offsets[cursor->ops.target.offset];
331

332
	bcursor = browser_line(&cursor->al);
333
	btarget = browser_line(target);
334

335
	if (annotate_browser__opts.hide_src_code) {
336
		from = bcursor->idx_asm;
337 338
		to = btarget->idx_asm;
	} else {
339
		from = (u64)bcursor->idx;
340 341 342
		to = (u64)btarget->idx;
	}

343
	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
344 345
	__ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
				 from, to);
346 347 348 349 350 351 352

	if (is_fused(ab, cursor)) {
		ui_browser__mark_fused(browser,
				       pcnt_width + 3 + ab->addr_width,
				       from - 1,
				       to > from ? true : false);
	}
353 354 355 356
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
357
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
358
	int ret = ui_browser__list_head_refresh(browser);
359
	int pcnt_width = annotate_browser__pcnt_width(ab);
360

361
	if (annotate_browser__opts.jump_arrows)
362
		annotate_browser__draw_current_jump(browser);
363

364
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
365
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
366 367 368
	return ret;
}

369
static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
370 371 372
{
	int i;

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

381
static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
382
{
383
	struct rb_node **p = &root->rb_node;
384
	struct rb_node *parent = NULL;
385
	struct annotation_line *l;
386 387 388

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

391
		if (disasm__cmp(al, l))
392 393 394 395
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
396 397
	rb_link_node(&al->rb_node, parent, p);
	rb_insert_color(&al->rb_node, root);
398 399
}

400
static void annotate_browser__set_top(struct annotate_browser *browser,
401
				      struct annotation_line *pos, u32 idx)
402 403 404
{
	unsigned back;

405 406 407
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
408

409
	while (browser->b.top_idx != 0 && back != 0) {
410
		pos = list_entry(pos->node.prev, struct annotation_line, node);
411

412
		if (disasm_line__filter(&browser->b, &pos->node))
413 414
			continue;

415
		--browser->b.top_idx;
416 417 418
		--back;
	}

419
	browser->b.top = pos;
420
	browser->b.navkeypressed = true;
421 422 423 424 425
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
426
	struct browser_line *bpos;
427
	struct annotation_line *pos;
428
	u32 idx;
429

430 431
	pos = rb_entry(nd, struct annotation_line, rb_node);
	bpos = browser_line(pos);
432

433
	idx = bpos->idx;
434
	if (annotate_browser__opts.hide_src_code)
435 436
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
437
	browser->curr_hot = nd;
438 439
}

440
static void annotate_browser__calc_percent(struct annotate_browser *browser,
441
					   struct perf_evsel *evsel)
442
{
443 444
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
445
	struct annotation *notes = symbol__annotation(sym);
446
	struct disasm_line *pos;
447 448 449 450 451

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

452 453
	symbol__calc_percent(sym, evsel);

454
	list_for_each_entry(pos, &notes->src->source, al.node) {
455 456
		double max_percent = 0.0;
		int i;
457

458
		if (pos->al.offset == -1) {
459
			RB_CLEAR_NODE(&pos->al.rb_node);
460 461 462
			continue;
		}

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

466 467
			if (max_percent < sample->percent)
				max_percent = sample->percent;
468 469
		}

470
		if (max_percent < 0.01 && pos->al.ipc == 0) {
471
			RB_CLEAR_NODE(&pos->al.rb_node);
472 473
			continue;
		}
474
		disasm_rb_tree__insert(&browser->entries, &pos->al);
475 476 477 478 479 480
	}
	pthread_mutex_unlock(&notes->lock);

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

481 482
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
483
	struct annotation_line *al;
484
	struct browser_line *bl;
485 486 487
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
488 489
	al = list_entry(browser->b.top, struct annotation_line, node);
	bl = browser_line(al);
490

491
	if (annotate_browser__opts.hide_src_code) {
492 493
		if (bl->idx_asm < offset)
			offset = bl->idx;
494 495

		browser->b.nr_entries = browser->nr_entries;
496
		annotate_browser__opts.hide_src_code = false;
497
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
498 499
		browser->b.top_idx = bl->idx - offset;
		browser->b.index = bl->idx;
500
	} else {
501
		if (bl->idx_asm < 0) {
502 503 504 505 506
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

507 508
		if (bl->idx_asm < offset)
			offset = bl->idx_asm;
509 510

		browser->b.nr_entries = browser->nr_asm_entries;
511
		annotate_browser__opts.hide_src_code = true;
512
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
513 514
		browser->b.top_idx = bl->idx_asm - offset;
		browser->b.index = bl->idx_asm;
515 516 517 518 519
	}

	return true;
}

520 521 522 523 524 525
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;
}

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

534 535
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
536
				    struct hist_browser_timer *hbt)
537 538
{
	struct map_symbol *ms = browser->b.priv;
539
	struct disasm_line *dl = disasm_line(browser->selection);
540
	struct annotation *notes;
541 542
	struct addr_map_symbol target = {
		.map = ms->map,
543
		.addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
544
	};
545
	char title[SYM_TITLE_MAX_SIZE];
546

547
	if (!ins__is_call(&dl->ins))
548 549
		return false;

550
	if (map_groups__find_ams(&target) ||
551 552 553
	    map__rip_2objdump(target.map, target.map->map_ip(target.map,
							     target.addr)) !=
	    dl->ops.target.addr) {
554 555 556 557
		ui_helpline__puts("The called function was not found.");
		return true;
	}

558
	notes = symbol__annotation(target.sym);
559 560
	pthread_mutex_lock(&notes->lock);

561
	if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {
562 563
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
564
			    target.sym->name);
565 566 567 568
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
569 570
	symbol__tui_annotate(target.sym, target.map, evsel, hbt);
	sym_title(ms->sym, ms->map, title, sizeof(title));
571
	ui_browser__show_title(&browser->b, title);
572 573 574
	return true;
}

575 576 577
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
578 579 580 581
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
582
	struct disasm_line *pos;
583 584

	*idx = 0;
585
	list_for_each_entry(pos, &notes->src->source, al.node) {
586
		if (pos->al.offset == offset)
587
			return pos;
588
		if (!disasm_line__filter(&browser->b, &pos->al.node))
589 590 591 592 593 594 595 596
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
597
	struct disasm_line *dl = disasm_line(browser->selection);
598
	u64 offset;
599
	s64 idx;
600

601
	if (!ins__is_jump(&dl->ins))
602 603
		return false;

604 605
	offset = dl->ops.target.offset;
	dl = annotate_browser__find_offset(browser, offset, &idx);
606
	if (dl == NULL) {
607
		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
608 609 610
		return true;
	}

611
	annotate_browser__set_top(browser, &dl->al, idx);
612

613 614 615
	return true;
}

616
static
617
struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
618
					  char *s, s64 *idx)
619 620 621 622
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
623
	struct annotation_line *al = browser->selection;
624 625

	*idx = browser->b.index;
626 627
	list_for_each_entry_continue(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
628 629 630 631
			continue;

		++*idx;

632 633
		if (al->line && strstr(al->line, s) != NULL)
			return al;
634 635 636 637 638 639 640
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
641
	struct annotation_line *al;
642 643
	s64 idx;

644 645
	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (al == NULL) {
646 647 648 649
		ui_helpline__puts("String not found!");
		return false;
	}

650
	annotate_browser__set_top(browser, al, idx);
651 652 653 654
	browser->searching_backwards = false;
	return true;
}

655
static
656
struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
657
						  char *s, s64 *idx)
658 659 660 661
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
662
	struct annotation_line *al = browser->selection;
663 664

	*idx = browser->b.index;
665 666
	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
667 668 669 670
			continue;

		--*idx;

671 672
		if (al->line && strstr(al->line, s) != NULL)
			return al;
673 674 675 676 677 678 679
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
680
	struct annotation_line *al;
681 682
	s64 idx;

683 684
	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (al == NULL) {
685 686 687 688
		ui_helpline__puts("String not found!");
		return false;
	}

689
	annotate_browser__set_top(browser, al, idx);
690 691 692 693 694 695 696 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
	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);
}

742 743 744 745 746 747 748 749 750 751 752 753 754
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;
}

755 756
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
757
				 struct hist_browser_timer *hbt)
758 759
{
	struct rb_node *nd = NULL;
760
	struct map_symbol *ms = browser->b.priv;
761
	struct symbol *sym = ms->sym;
762
	const char *help = "Press 'h' for help on key bindings";
763
	int delay_secs = hbt ? hbt->refresh : 0;
764
	int key;
765
	char title[SYM_TITLE_MAX_SIZE];
766

767 768
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
769
		return -1;
770

771
	annotate_browser__calc_percent(browser, evsel);
772

773 774 775
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
776
	}
777

778
	nd = browser->curr_hot;
779

780
	while (1) {
781
		key = ui_browser__run(&browser->b, delay_secs);
782

783
		if (delay_secs != 0) {
784
			annotate_browser__calc_percent(browser, evsel);
785 786 787 788 789 790 791 792 793
			/*
			 * 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;
		}

794
		switch (key) {
795
		case K_TIMER:
796 797
			if (hbt)
				hbt->timer(hbt->arg);
798 799

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

898
			if (browser->selection == NULL)
899
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
900
			else if (browser->selection->offset == -1)
901
				ui_helpline__puts("Actions are only available for assembly lines.");
902
			else if (!dl->ins.ops)
903
				goto show_sup_ins;
904
			else if (ins__is_ret(&dl->ins))
905
				goto out;
906
			else if (!(annotate_browser__jump(browser) ||
907
				     annotate_browser__callq(browser, evsel, hbt))) {
908
show_sup_ins:
909
				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
910
			}
911
			continue;
912
		}
913
		case 't':
914 915 916 917 918 919 920
			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;
921 922
			annotate_browser__update_addr_width(browser);
			continue;
923 924
		case K_LEFT:
		case K_ESC:
925 926
		case 'q':
		case CTRL('c'):
927
			goto out;
928 929
		default:
			continue;
930
		}
931 932

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

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

949 950 951
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

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

959
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
960 961
}

962 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

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

992 993
			if (al)
				al->ipc = ipc;
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
		}
	}
}

/*
 * 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) {
1018
			struct annotation_line *al;
1019 1020 1021

			if (ch->have_start)
				count_and_fill(browser, ch->start, offset, ch);
1022 1023 1024
			al = browser->offsets[offset];
			if (al && ch->num_aggr)
				al->cycles = ch->cycles_aggr / ch->num_aggr;
1025 1026 1027 1028 1029 1030
			browser->have_cycles = true;
		}
	}
	pthread_mutex_unlock(&notes->lock);
}

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

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

	for (offset = 0; offset < size; ++offset) {
1043
		struct annotation_line *al = browser->offsets[offset];
1044 1045
		struct disasm_line *dl;
		struct browser_line *blt;
1046

1047 1048
		dl = disasm_line(al);

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

1052 1053
		al = browser->offsets[dl->ops.target.offset];

1054 1055 1056 1057
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
1058
		if (al == NULL)
1059 1060
			continue;

1061 1062 1063
		blt = browser_line(al);
		if (++blt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = blt->jump_sources;
1064 1065

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

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

1078 1079
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
1080
			 struct hist_browser_timer *hbt)
1081
{
1082
	struct annotation_line *al;
1083
	struct annotation *notes;
1084
	size_t size;
1085 1086 1087 1088
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
1089 1090
	struct annotate_browser browser = {
		.b = {
1091
			.refresh = annotate_browser__refresh,
1092 1093
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
1094
			.filter  = disasm_line__filter,
1095
			.priv	 = &ms,
1096
			.use_navkeypressed = true,
1097
		},
1098
	};
1099
	int ret = -1, err;
1100
	int nr_pcnt = 1;
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
	browser.offsets = zalloc(size * sizeof(struct annotation_line *));
1111 1112 1113 1114 1115
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

1116
	if (perf_evsel__is_group_event(evsel))
1117 1118
		nr_pcnt = evsel->nr_members;

1119
	err = symbol__annotate(sym, map, evsel,
1120
			       sizeof(struct browser_line), &browser.arch,
1121
			       perf_evsel__env_cpuid(evsel));
1122 1123 1124 1125
	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);
1126
		goto out_free_offsets;
1127 1128
	}

1129 1130
	symbol__calc_percent(sym, evsel);

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

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

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

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

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

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

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

	annotate_browser__update_addr_width(&browser);

1175
	ret = annotate_browser__run(&browser, evsel, hbt);
1176 1177

	annotated_source__purge(notes->src);
1178 1179 1180

out_free_offsets:
	free(browser.offsets);
1181 1182
	return ret;
}
1183 1184 1185

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

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

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1207
	const struct annotate_config *cfg = cfgp;
1208 1209 1210 1211

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

1212 1213
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1214
{
1215
	struct annotate_config *cfg;
1216 1217
	const char *name;

1218
	if (!strstarts(var, "annotate."))
1219 1220 1221 1222
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1223
		      sizeof(struct annotate_config), annotate_config__cmp);
1224 1225

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

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