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
	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;
	u8			    addr_width;
	u8			    jumps_width;
	u8			    target_width;
	u8			    min_addr_width;
	u8			    max_addr_width;
	char			    search_bf[128];
58 59
};

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

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

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

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

	return false;
}

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
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);
}

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

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
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);
}

130
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
131
{
132
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
133 134 135
	struct map_symbol *ms = browser->priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
136 137
	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
	struct browser_line *bl = browser_line(al);
138
	bool current_entry = ui_browser__is_current_entry(browser, row);
139
	bool change_color = (!annotate_browser__opts.hide_src_code &&
140 141 142
			     (!current_entry || (browser->use_navkeypressed &&
					         !browser->navkeypressed)));
	int width = browser->width, printed;
143
	int i, pcnt_width = annotate_browser__pcnt_width(ab),
144
	       cycles_width = annotation__cycles_width(notes);
145
	double percent_max = 0.0;
146
	char bf[256];
147
	bool show_title = false;
148

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

154
	if ((row == 0) && (al->offset == -1 || percent_max == 0.0)) {
155
		if (notes->have_cycles) {
156
			if (al->ipc == 0.0 && al->cycles == 0)
157 158 159 160 161
				show_title = true;
		} else
			show_title = true;
	}

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

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

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

206
	SLsmg_write_char(' ');
207 208

	/* The scroll bar isn't being used */
209
	if (!browser->navkeypressed)
210 211
		width += 1;

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

227
		if (!annotate_browser__opts.use_offset)
228 229
			addr += ab->start;

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

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

253
		if (change_color)
254
			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
255
		ui_browser__write_nstring(browser, bf, printed);
256
		if (change_color)
257
			ui_browser__set_color(browser, color);
258

259 260
		disasm_line__write(disasm_line(al), browser, bf, sizeof(bf));

261
		ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
262
	}
263

264
	if (current_entry)
265
		ab->selection = al;
266 267
}

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

	return true;
}

279 280
static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
{
281
	struct disasm_line *pos = list_prev_entry(cursor, al.node);
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
	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);
}

298
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
299 300
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
301
	struct disasm_line *cursor = disasm_line(ab->selection);
302
	struct annotation_line *target;
303
	struct browser_line *btarget, *bcursor;
304
	unsigned int from, to;
305 306
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
307
	struct annotation *notes = symbol__annotation(sym);
308
	u8 pcnt_width = annotate_browser__pcnt_width(ab);
309
	int width;
310 311 312 313

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

315
	if (!disasm_line__is_valid_jump(cursor, sym))
316
		return;
317

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

345
	bcursor = browser_line(&cursor->al);
346
	btarget = browser_line(target);
347

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

356
	width = annotation__cycles_width(notes);
J
Jin Yao 已提交
357

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

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

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
373
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
374
	int ret = ui_browser__list_head_refresh(browser);
375
	int pcnt_width = annotate_browser__pcnt_width(ab);
376

377
	if (annotate_browser__opts.jump_arrows)
378
		annotate_browser__draw_current_jump(browser);
379

380
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
381
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
382 383 384
	return ret;
}

385
static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
386 387 388
{
	int i;

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

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

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

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

416
static void annotate_browser__set_top(struct annotate_browser *browser,
417
				      struct annotation_line *pos, u32 idx)
418 419 420
{
	unsigned back;

421 422 423
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
424

425
	while (browser->b.top_idx != 0 && back != 0) {
426
		pos = list_entry(pos->node.prev, struct annotation_line, node);
427

428
		if (disasm_line__filter(&browser->b, &pos->node))
429 430
			continue;

431
		--browser->b.top_idx;
432 433 434
		--back;
	}

435
	browser->b.top = pos;
436
	browser->b.navkeypressed = true;
437 438 439 440 441
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
442
	struct browser_line *bpos;
443
	struct annotation_line *pos;
444
	u32 idx;
445

446 447
	pos = rb_entry(nd, struct annotation_line, rb_node);
	bpos = browser_line(pos);
448

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

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

468 469
	symbol__calc_percent(sym, evsel);

470
	list_for_each_entry(pos, &notes->src->source, al.node) {
471 472
		double max_percent = 0.0;
		int i;
473

474
		if (pos->al.offset == -1) {
475
			RB_CLEAR_NODE(&pos->al.rb_node);
476 477 478
			continue;
		}

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

482 483
			if (max_percent < sample->percent)
				max_percent = sample->percent;
484 485
		}

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

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

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

	browser->b.seek(&browser->b, offset, SEEK_CUR);
504 505
	al = list_entry(browser->b.top, struct annotation_line, node);
	bl = browser_line(al);
506

507
	if (annotate_browser__opts.hide_src_code) {
508 509
		if (bl->idx_asm < offset)
			offset = bl->idx;
510 511

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

523 524
		if (bl->idx_asm < offset)
			offset = bl->idx_asm;
525 526

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

	return true;
}

536 537 538 539 540 541
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;
}

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

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

559
	if (!ins__is_call(&dl->ins))
560 561
		return false;

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

567
	notes = symbol__annotation(dl->ops.target.sym);
568 569
	pthread_mutex_lock(&notes->lock);

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

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

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

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

	return NULL;
}

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

610
	if (!ins__is_jump(&dl->ins))
611 612
		return false;

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

620
	annotate_browser__set_top(browser, &dl->al, idx);
621

622 623 624
	return true;
}

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

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

		++*idx;

641 642
		if (al->line && strstr(al->line, s) != NULL)
			return al;
643 644 645 646 647 648 649
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
650
	struct annotation_line *al;
651 652
	s64 idx;

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

659
	annotate_browser__set_top(browser, al, idx);
660 661 662 663
	browser->searching_backwards = false;
	return true;
}

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

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

		--*idx;

680 681
		if (al->line && strstr(al->line, s) != NULL)
			return al;
682 683 684 685 686 687 688
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
689
	struct annotation_line *al;
690 691
	s64 idx;

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

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

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

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

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

780
	annotate_browser__calc_percent(browser, evsel);
781

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

787
	nd = browser->curr_hot;
788

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

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

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

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

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

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

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

958 959 960
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

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

968
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
969 970
}

971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998

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

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

/*
 * 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) {
1027
			struct annotation_line *al;
1028 1029 1030

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

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

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

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

1056 1057
		dl = disasm_line(al);

1058
		if (!disasm_line__is_valid_jump(dl, sym))
1059 1060
			continue;

1061 1062
		al = browser->offsets[dl->ops.target.offset];

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

1070 1071 1072
		blt = browser_line(al);
		if (++blt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = blt->jump_sources;
1073 1074

		++browser->nr_jumps;
1075 1076 1077
	}
}

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

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

1111
	if (sym == NULL)
1112 1113
		return -1;

1114 1115
	size = symbol__size(sym);

1116
	if (map->dso->annotate_warned)
1117 1118
		return -1;

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

1125
	if (perf_evsel__is_group_event(evsel))
1126 1127
		nr_pcnt = evsel->nr_members;

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

1136 1137
	symbol__calc_percent(sym, evsel);

1138
	ui_helpline__push("Press ESC to exit");
1139

1140
	notes = symbol__annotation(sym);
1141
	browser.start = map__rip_2objdump(map, sym->start);
1142

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

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

1166
	annotate_browser__mark_jump_targets(&browser, size);
1167
	annotate__compute_ipc(&browser, size, sym);
1168

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

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

	annotate_browser__update_addr_width(&browser);

1182
	ret = annotate_browser__run(&browser, evsel, hbt);
1183 1184

	annotated_source__purge(notes->src);
1185 1186 1187

out_free_offsets:
	free(browser.offsets);
1188 1189
	return ret;
}
1190 1191 1192

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

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

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1214
	const struct annotate_config *cfg = cfgp;
1215 1216 1217 1218

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

1219 1220
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1221
{
1222
	struct annotate_config *cfg;
1223 1224
	const char *name;

1225
	if (!strstarts(var, "annotate."))
1226 1227 1228 1229
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1230
		      sizeof(struct annotate_config), annotate_config__cmp);
1231 1232

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

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