annotate.c 32.5 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2
#include "../../util/util.h"
3 4
#include "../browser.h"
#include "../helpline.h"
5 6
#include "../ui.h"
#include "../util.h"
7 8 9 10
#include "../../util/annotate.h"
#include "../../util/hist.h"
#include "../../util/sort.h"
#include "../../util/symbol.h"
11
#include "../../util/evsel.h"
12
#include "../../util/config.h"
13
#include "../../util/evlist.h"
14
#include <inttypes.h>
15
#include <pthread.h>
16
#include <linux/kernel.h>
17
#include <linux/string.h>
18
#include <sys/ttydefaults.h>
19

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

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

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

36 37
struct arch;

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

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

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

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

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

	return false;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

209
	SLsmg_write_char(' ');
210 211

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

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

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

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

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

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

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

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

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

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

	return true;
}

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

	if (!pos)
		return false;

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

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

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

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

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

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

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

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

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

J
Jin Yao 已提交
358
	if (ab->have_cycles)
359
		width = ANNOTATION__IPC_WIDTH + ANNOTATION__CYCLES_WIDTH;
J
Jin Yao 已提交
360

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

471 472
	symbol__calc_percent(sym, evsel);

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

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

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

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

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

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

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

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

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

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

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

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

	return true;
}

539 540 541 542 543 544
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;
}

545 546 547 548 549 550 551 552
#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);
}

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

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

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

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

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

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

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

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

	return NULL;
}

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

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

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

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

625 626 627
	return true;
}

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

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

		++*idx;

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

	return NULL;
}

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

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

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

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

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

		--*idx;

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

	return NULL;
}

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

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

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

754 755 756 757 758 759 760 761 762 763 764 765 766
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;
}

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

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

783
	annotate_browser__calc_percent(browser, evsel);
784

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

790
	nd = browser->curr_hot;
791

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

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

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

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

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

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

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

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

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

971
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
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

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

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

/*
 * 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) {
1030
			struct annotation_line *al;
1031 1032 1033

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

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

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

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

1059 1060
		dl = disasm_line(al);

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

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

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

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

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

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

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

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

1117 1118
	size = symbol__size(sym);

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

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

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

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

1139 1140
	symbol__calc_percent(sym, evsel);

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

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

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

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

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

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

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

	annotate_browser__update_addr_width(&browser);

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

	annotated_source__purge(notes->src);
1188 1189 1190

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

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

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

#undef ANNOTATE_CFG

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

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

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

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

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

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

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