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

137
	for (i = 0; i < ab->nr_events; i++) {
138 139
		if (dl->al.samples[i].percent > percent_max)
			percent_max = dl->al.samples[i].percent;
140 141
	}

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

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

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

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

194
	SLsmg_write_char(' ');
195 196

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

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

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

218
		if (!annotate_browser__opts.use_offset) {
219
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
220
		} else {
221
			if (bdl->jump_sources) {
222
				if (annotate_browser__opts.show_nr_jumps) {
223 224 225 226 227 228
					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);
229
					ui_browser__write_nstring(browser, bf, printed);
230
					ui_browser__set_color(browser, prev);
231 232
				}

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

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

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

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

270
	if (current_entry)
271
		ab->selection = &dl->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);
314 315 316 317

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

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

322
	target = ab->offsets[cursor->ops.target.offset];
323

324
	bcursor = browser_line(&cursor->al);
325
	btarget = browser_line(target);
326

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

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

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

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
349
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
350
	int ret = ui_browser__list_head_refresh(browser);
351
	int pcnt_width = annotate_browser__pcnt_width(ab);
352

353
	if (annotate_browser__opts.jump_arrows)
354
		annotate_browser__draw_current_jump(browser);
355

356
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
357
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
358 359 360
	return ret;
}

361
static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
362 363 364
{
	int i;

365
	for (i = 0; i < a->samples_nr; i++) {
366
		if (a->samples[i].percent == b->samples[i].percent)
367
			continue;
368
		return a->samples[i].percent < b->samples[i].percent;
369 370 371 372
	}
	return 0;
}

373
static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
374
{
375
	struct rb_node **p = &root->rb_node;
376
	struct rb_node *parent = NULL;
377
	struct annotation_line *l;
378 379 380

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

383
		if (disasm__cmp(al, l))
384 385 386 387
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
388 389
	rb_link_node(&al->rb_node, parent, p);
	rb_insert_color(&al->rb_node, root);
390 391
}

392
static void annotate_browser__set_top(struct annotate_browser *browser,
393
				      struct disasm_line *pos, u32 idx)
394 395 396
{
	unsigned back;

397 398 399
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
400

401
	while (browser->b.top_idx != 0 && back != 0) {
402
		pos = list_entry(pos->al.node.prev, struct disasm_line, al.node);
403

404
		if (disasm_line__filter(&browser->b, &pos->al.node))
405 406
			continue;

407
		--browser->b.top_idx;
408 409 410
		--back;
	}

411
	browser->b.top = &pos->al;
412
	browser->b.navkeypressed = true;
413 414 415 416 417
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
418
	struct browser_line *bpos;
419
	struct disasm_line *pos;
420
	u32 idx;
421

422
	pos = rb_entry(nd, struct disasm_line, al.rb_node);
423
	bpos = browser_line(&pos->al);
424

425
	idx = bpos->idx;
426
	if (annotate_browser__opts.hide_src_code)
427 428
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
429
	browser->curr_hot = nd;
430 431
}

432
static void annotate_browser__calc_percent(struct annotate_browser *browser,
433
					   struct perf_evsel *evsel)
434
{
435 436
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
437
	struct annotation *notes = symbol__annotation(sym);
438
	struct disasm_line *pos;
439 440 441 442 443

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

444 445
	symbol__calc_percent(sym, evsel);

446
	list_for_each_entry(pos, &notes->src->source, al.node) {
447 448
		double max_percent = 0.0;
		int i;
449

450
		if (pos->al.offset == -1) {
451
			RB_CLEAR_NODE(&pos->al.rb_node);
452 453 454
			continue;
		}

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

458 459
			if (max_percent < sample->percent)
				max_percent = sample->percent;
460 461
		}

462
		if (max_percent < 0.01 && pos->al.ipc == 0) {
463
			RB_CLEAR_NODE(&pos->al.rb_node);
464 465
			continue;
		}
466
		disasm_rb_tree__insert(&browser->entries, &pos->al);
467 468 469 470 471 472
	}
	pthread_mutex_unlock(&notes->lock);

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

473 474
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
475
	struct disasm_line *dl;
476
	struct browser_line *bl;
477 478 479
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
480
	dl = list_entry(browser->b.top, struct disasm_line, al.node);
481
	bl = browser_line(&dl->al);
482

483
	if (annotate_browser__opts.hide_src_code) {
484 485
		if (bl->idx_asm < offset)
			offset = bl->idx;
486 487

		browser->b.nr_entries = browser->nr_entries;
488
		annotate_browser__opts.hide_src_code = false;
489
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
490 491
		browser->b.top_idx = bl->idx - offset;
		browser->b.index = bl->idx;
492
	} else {
493
		if (bl->idx_asm < 0) {
494 495 496 497 498
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

499 500
		if (bl->idx_asm < offset)
			offset = bl->idx_asm;
501 502

		browser->b.nr_entries = browser->nr_asm_entries;
503
		annotate_browser__opts.hide_src_code = true;
504
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
505 506
		browser->b.top_idx = bl->idx_asm - offset;
		browser->b.index = bl->idx_asm;
507 508 509 510 511
	}

	return true;
}

512 513 514 515 516 517
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;
}

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

526 527
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
528
				    struct hist_browser_timer *hbt)
529 530
{
	struct map_symbol *ms = browser->b.priv;
531
	struct disasm_line *dl = disasm_line(browser->selection);
532
	struct annotation *notes;
533 534
	struct addr_map_symbol target = {
		.map = ms->map,
535
		.addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
536
	};
537
	char title[SYM_TITLE_MAX_SIZE];
538

539
	if (!ins__is_call(&dl->ins))
540 541
		return false;

542
	if (map_groups__find_ams(&target) ||
543 544 545
	    map__rip_2objdump(target.map, target.map->map_ip(target.map,
							     target.addr)) !=
	    dl->ops.target.addr) {
546 547 548 549
		ui_helpline__puts("The called function was not found.");
		return true;
	}

550
	notes = symbol__annotation(target.sym);
551 552
	pthread_mutex_lock(&notes->lock);

553
	if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {
554 555
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
556
			    target.sym->name);
557 558 559 560
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
561 562
	symbol__tui_annotate(target.sym, target.map, evsel, hbt);
	sym_title(ms->sym, ms->map, title, sizeof(title));
563
	ui_browser__show_title(&browser->b, title);
564 565 566
	return true;
}

567 568 569
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
570 571 572 573
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
574
	struct disasm_line *pos;
575 576

	*idx = 0;
577
	list_for_each_entry(pos, &notes->src->source, al.node) {
578
		if (pos->al.offset == offset)
579
			return pos;
580
		if (!disasm_line__filter(&browser->b, &pos->al.node))
581 582 583 584 585 586 587 588
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
589
	struct disasm_line *dl = disasm_line(browser->selection);
590
	u64 offset;
591
	s64 idx;
592

593
	if (!ins__is_jump(&dl->ins))
594 595
		return false;

596 597
	offset = dl->ops.target.offset;
	dl = annotate_browser__find_offset(browser, offset, &idx);
598
	if (dl == NULL) {
599
		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
600 601 602
		return true;
	}

603
	annotate_browser__set_top(browser, dl, idx);
604

605 606 607
	return true;
}

608
static
609
struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
610
					  char *s, s64 *idx)
611 612 613 614
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
615
	struct annotation_line *al = browser->selection;
616 617

	*idx = browser->b.index;
618 619
	list_for_each_entry_continue(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
620 621 622 623
			continue;

		++*idx;

624 625
		if (al->line && strstr(al->line, s) != NULL)
			return al;
626 627 628 629 630 631 632
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
633
	struct annotation_line *al;
634 635
	s64 idx;

636 637
	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (al == NULL) {
638 639 640 641
		ui_helpline__puts("String not found!");
		return false;
	}

642
	annotate_browser__set_top(browser, disasm_line(al), idx);
643 644 645 646
	browser->searching_backwards = false;
	return true;
}

647
static
648
struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
649
						  char *s, s64 *idx)
650 651 652 653
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
654
	struct annotation_line *al = browser->selection;
655 656

	*idx = browser->b.index;
657 658
	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
659 660 661 662
			continue;

		--*idx;

663 664
		if (al->line && strstr(al->line, s) != NULL)
			return al;
665 666 667 668 669 670 671
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
672
	struct annotation_line *al;
673 674
	s64 idx;

675 676
	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (al == NULL) {
677 678 679 680
		ui_helpline__puts("String not found!");
		return false;
	}

681
	annotate_browser__set_top(browser, disasm_line(al), idx);
682 683 684 685 686 687 688 689 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
	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);
}

734 735 736 737 738 739 740 741 742 743 744 745 746
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;
}

747 748
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
749
				 struct hist_browser_timer *hbt)
750 751
{
	struct rb_node *nd = NULL;
752
	struct map_symbol *ms = browser->b.priv;
753
	struct symbol *sym = ms->sym;
754
	const char *help = "Press 'h' for help on key bindings";
755
	int delay_secs = hbt ? hbt->refresh : 0;
756
	int key;
757
	char title[SYM_TITLE_MAX_SIZE];
758

759 760
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
761
		return -1;
762

763
	annotate_browser__calc_percent(browser, evsel);
764

765 766 767
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
768
	}
769

770
	nd = browser->curr_hot;
771

772
	while (1) {
773
		key = ui_browser__run(&browser->b, delay_secs);
774

775
		if (delay_secs != 0) {
776
			annotate_browser__calc_percent(browser, evsel);
777 778 779 780 781 782 783 784 785
			/*
			 * 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;
		}

786
		switch (key) {
787
		case K_TIMER:
788 789
			if (hbt)
				hbt->timer(hbt->arg);
790 791

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

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

		if (nd != NULL)
925
			annotate_browser__set_rb_top(browser, nd);
926 927
	}
out:
928
	ui_browser__hide(&browser->b);
929
	return key;
930 931
}

932 933 934
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
			     struct hist_browser_timer *hbt)
{
935
	/* Set default value for show_total_period and show_nr_samples  */
936
	annotate_browser__opts.show_total_period =
937 938 939
		symbol_conf.show_total_period;
	annotate_browser__opts.show_nr_samples =
		symbol_conf.show_nr_samples;
940

941 942 943
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

944
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
945
			     struct hist_browser_timer *hbt)
946
{
947 948 949 950
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

951
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
952 953
}

954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981

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

984 985
			if (al)
				al->ipc = ipc;
986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
		}
	}
}

/*
 * 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) {
1010
			struct annotation_line *al;
1011 1012 1013

			if (ch->have_start)
				count_and_fill(browser, ch->start, offset, ch);
1014 1015 1016
			al = browser->offsets[offset];
			if (al && ch->num_aggr)
				al->cycles = ch->cycles_aggr / ch->num_aggr;
1017 1018 1019 1020 1021 1022
			browser->have_cycles = true;
		}
	}
	pthread_mutex_unlock(&notes->lock);
}

1023 1024 1025 1026
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;
1027 1028 1029 1030 1031 1032
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;

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

	for (offset = 0; offset < size; ++offset) {
1035
		struct annotation_line *al = browser->offsets[offset];
1036 1037
		struct disasm_line *dl;
		struct browser_line *blt;
1038

1039 1040
		dl = disasm_line(al);

1041
		if (!disasm_line__is_valid_jump(dl, sym))
1042 1043
			continue;

1044 1045
		al = browser->offsets[dl->ops.target.offset];

1046 1047 1048 1049
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
1050
		if (al == NULL)
1051 1052
			continue;

1053 1054 1055
		blt = browser_line(al);
		if (++blt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = blt->jump_sources;
1056 1057

		++browser->nr_jumps;
1058 1059 1060
	}
}

1061 1062 1063 1064 1065 1066 1067 1068 1069
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

1070 1071
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
1072
			 struct hist_browser_timer *hbt)
1073
{
1074
	struct annotation_line *al;
1075
	struct annotation *notes;
1076
	size_t size;
1077 1078 1079 1080
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
1081 1082
	struct annotate_browser browser = {
		.b = {
1083
			.refresh = annotate_browser__refresh,
1084 1085
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
1086
			.filter  = disasm_line__filter,
1087
			.priv	 = &ms,
1088
			.use_navkeypressed = true,
1089
		},
1090
	};
1091
	int ret = -1, err;
1092
	int nr_pcnt = 1;
1093

1094
	if (sym == NULL)
1095 1096
		return -1;

1097 1098
	size = symbol__size(sym);

1099
	if (map->dso->annotate_warned)
1100 1101
		return -1;

1102
	browser.offsets = zalloc(size * sizeof(struct annotation_line *));
1103 1104 1105 1106 1107
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

1108
	if (perf_evsel__is_group_event(evsel))
1109 1110
		nr_pcnt = evsel->nr_members;

1111
	err = symbol__annotate(sym, map, evsel,
1112
			       sizeof(struct browser_line), &browser.arch,
1113
			       perf_evsel__env_cpuid(evsel));
1114 1115 1116 1117
	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);
1118
		goto out_free_offsets;
1119 1120
	}

1121
	ui_helpline__push("Press ESC to exit");
1122

1123
	notes = symbol__annotation(sym);
1124
	browser.start = map__rip_2objdump(map, sym->start);
1125

1126
	list_for_each_entry(al, &notes->src->source, node) {
1127
		struct browser_line *bpos;
1128
		size_t line_len = strlen(al->line);
1129

1130 1131
		if (browser.b.width < line_len)
			browser.b.width = line_len;
1132
		bpos = browser_line(al);
1133
		bpos->idx = browser.nr_entries++;
1134
		if (al->offset != -1) {
1135
			bpos->idx_asm = browser.nr_asm_entries++;
1136 1137 1138 1139 1140 1141 1142
			/*
			 * 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
 			 */
1143 1144
			if (al->offset < (s64)size)
				browser.offsets[al->offset] = al;
1145
		} else
1146
			bpos->idx_asm = -1;
1147 1148
	}

1149
	annotate_browser__mark_jump_targets(&browser, size);
1150
	annotate__compute_ipc(&browser, size, sym);
1151

1152
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1153
	browser.max_addr_width = hex_width(sym->end);
1154
	browser.jumps_width = width_jumps(browser.max_jump_sources);
1155
	browser.nr_events = nr_pcnt;
1156
	browser.b.nr_entries = browser.nr_entries;
1157
	browser.b.entries = &notes->src->source,
1158
	browser.b.width += 18; /* Percentage */
1159 1160 1161 1162 1163 1164

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

	annotate_browser__update_addr_width(&browser);

1165
	ret = annotate_browser__run(&browser, evsel, hbt);
1166 1167

	annotated_source__purge(notes->src);
1168 1169 1170

out_free_offsets:
	free(browser.offsets);
1171 1172
	return ret;
}
1173 1174 1175

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

1177 1178 1179
/*
 * Keep the entries sorted, they are bsearch'ed
 */
1180
static struct annotate_config {
1181 1182 1183 1184 1185
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
1186
	ANNOTATE_CFG(show_linenr),
1187
	ANNOTATE_CFG(show_nr_jumps),
1188
	ANNOTATE_CFG(show_nr_samples),
1189
	ANNOTATE_CFG(show_total_period),
1190
	ANNOTATE_CFG(use_offset),
1191 1192 1193 1194 1195 1196
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1197
	const struct annotate_config *cfg = cfgp;
1198 1199 1200 1201

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

1202 1203
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1204
{
1205
	struct annotate_config *cfg;
1206 1207
	const char *name;

1208
	if (!strstarts(var, "annotate."))
1209 1210 1211 1212
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1213
		      sizeof(struct annotate_config), annotate_config__cmp);
1214 1215

	if (cfg == NULL)
1216 1217 1218
		ui__warning("%s variable unknown, ignoring...", var);
	else
		*cfg->value = perf_config_bool(name, value);
1219 1220 1221 1222 1223 1224 1225
	return 0;
}

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