annotate.c 31.4 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 43 44 45 46 47 48 49 50 51 52 53 54 55
	struct ui_browser	    b;
	struct rb_root		    entries;
	struct rb_node		   *curr_hot;
	struct annotation_line	   *selection;
	struct arch		   *arch;
	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];
56 57
};

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

	ptr = container_of(al, struct disasm_line, al);
	return ptr - sizeof(struct browser_line);
64 65
}

66
static bool disasm_line__filter(struct ui_browser *browser, void *entry)
67
{
68 69 70 71
	struct map_symbol *ms = browser->priv;
	struct annotation *notes = symbol__annotation(ms->sym);

	if (notes->options->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 102
	struct map_symbol *ms = ab->b.priv;
	struct annotation *notes = symbol__annotation(ms->sym);
103
	return (notes->options->show_total_period ? 12 : 7) * notes->nr_events;
104
}
105

106 107 108
static void disasm_line__write(struct disasm_line *dl, struct ui_browser *browser,
			       char *bf, size_t size)
{
109 110 111
	struct map_symbol *ms = browser->priv;
	struct annotation *notes = symbol__annotation(ms->sym);

112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
	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);
	}

132
	disasm_line__scnprintf(dl, bf, size, !notes->options->use_offset);
133 134
}

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

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

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

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

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

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

211
	SLsmg_write_char(' ');
212 213

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

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

232
		if (!notes->options->use_offset)
233 234
			addr += ab->start;

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

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

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

264 265
		disasm_line__write(disasm_line(al), browser, bf, sizeof(bf));

266
		ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
267
	}
268

269
	if (current_entry)
270
		ab->selection = al;
271 272
}

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

	return true;
}

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

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

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

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

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

350
	bcursor = browser_line(&cursor->al);
351
	btarget = browser_line(target);
352

353
	if (notes->options->hide_src_code) {
354
		from = bcursor->idx_asm;
355 356
		to = btarget->idx_asm;
	} else {
357
		from = (u64)bcursor->idx;
358 359 360
		to = (u64)btarget->idx;
	}

361
	width = annotation__cycles_width(notes);
J
Jin Yao 已提交
362

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

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

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
378
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
379 380
	struct map_symbol *ms = browser->priv;
	struct annotation *notes = symbol__annotation(ms->sym);
381
	int ret = ui_browser__list_head_refresh(browser);
382
	int pcnt_width = annotate_browser__pcnt_width(ab);
383

384
	if (notes->options->jump_arrows)
385
		annotate_browser__draw_current_jump(browser);
386

387
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
388
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
389 390 391
	return ret;
}

392
static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
393 394 395
{
	int i;

396
	for (i = 0; i < a->samples_nr; i++) {
397
		if (a->samples[i].percent == b->samples[i].percent)
398
			continue;
399
		return a->samples[i].percent < b->samples[i].percent;
400 401 402 403
	}
	return 0;
}

404
static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
405
{
406
	struct rb_node **p = &root->rb_node;
407
	struct rb_node *parent = NULL;
408
	struct annotation_line *l;
409 410 411

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

414
		if (disasm__cmp(al, l))
415 416 417 418
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
419 420
	rb_link_node(&al->rb_node, parent, p);
	rb_insert_color(&al->rb_node, root);
421 422
}

423
static void annotate_browser__set_top(struct annotate_browser *browser,
424
				      struct annotation_line *pos, u32 idx)
425 426 427
{
	unsigned back;

428 429 430
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
431

432
	while (browser->b.top_idx != 0 && back != 0) {
433
		pos = list_entry(pos->node.prev, struct annotation_line, node);
434

435
		if (disasm_line__filter(&browser->b, &pos->node))
436 437
			continue;

438
		--browser->b.top_idx;
439 440 441
		--back;
	}

442
	browser->b.top = pos;
443
	browser->b.navkeypressed = true;
444 445 446 447 448
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
449 450
	struct map_symbol *ms = browser->b.priv;
	struct annotation *notes = symbol__annotation(ms->sym);
451
	struct browser_line *bpos;
452
	struct annotation_line *pos;
453
	u32 idx;
454

455 456
	pos = rb_entry(nd, struct annotation_line, rb_node);
	bpos = browser_line(pos);
457

458
	idx = bpos->idx;
459
	if (notes->options->hide_src_code)
460 461
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
462
	browser->curr_hot = nd;
463 464
}

465
static void annotate_browser__calc_percent(struct annotate_browser *browser,
466
					   struct perf_evsel *evsel)
467
{
468 469
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
470
	struct annotation *notes = symbol__annotation(sym);
471
	struct disasm_line *pos;
472 473 474 475 476

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

477 478
	symbol__calc_percent(sym, evsel);

479
	list_for_each_entry(pos, &notes->src->source, al.node) {
480 481
		double max_percent = 0.0;
		int i;
482

483
		if (pos->al.offset == -1) {
484
			RB_CLEAR_NODE(&pos->al.rb_node);
485 486 487
			continue;
		}

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

491 492
			if (max_percent < sample->percent)
				max_percent = sample->percent;
493 494
		}

495
		if (max_percent < 0.01 && pos->al.ipc == 0) {
496
			RB_CLEAR_NODE(&pos->al.rb_node);
497 498
			continue;
		}
499
		disasm_rb_tree__insert(&browser->entries, &pos->al);
500 501 502 503 504 505
	}
	pthread_mutex_unlock(&notes->lock);

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

506 507
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
508 509
	struct map_symbol *ms = browser->b.priv;
	struct annotation *notes = symbol__annotation(ms->sym);
510
	struct annotation_line *al;
511
	struct browser_line *bl;
512 513 514
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
515 516
	al = list_entry(browser->b.top, struct annotation_line, node);
	bl = browser_line(al);
517

518
	if (notes->options->hide_src_code) {
519 520
		if (bl->idx_asm < offset)
			offset = bl->idx;
521 522

		browser->b.nr_entries = browser->nr_entries;
523
		notes->options->hide_src_code = false;
524
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
525 526
		browser->b.top_idx = bl->idx - offset;
		browser->b.index = bl->idx;
527
	} else {
528
		if (bl->idx_asm < 0) {
529 530 531 532 533
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

534 535
		if (bl->idx_asm < offset)
			offset = bl->idx_asm;
536 537

		browser->b.nr_entries = browser->nr_asm_entries;
538
		notes->options->hide_src_code = true;
539
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
540 541
		browser->b.top_idx = bl->idx_asm - offset;
		browser->b.index = bl->idx_asm;
542 543 544 545 546
	}

	return true;
}

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

553 554 555 556 557 558 559 560
#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);
}

561 562
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
563
				    struct hist_browser_timer *hbt)
564 565
{
	struct map_symbol *ms = browser->b.priv;
566
	struct disasm_line *dl = disasm_line(browser->selection);
567
	struct annotation *notes;
568
	char title[SYM_TITLE_MAX_SIZE];
569

570
	if (!ins__is_call(&dl->ins))
571 572
		return false;

573
	if (!dl->ops.target.sym) {
574 575 576 577
		ui_helpline__puts("The called function was not found.");
		return true;
	}

578
	notes = symbol__annotation(dl->ops.target.sym);
579 580
	pthread_mutex_lock(&notes->lock);

581
	if (notes->src == NULL && symbol__alloc_hist(dl->ops.target.sym) < 0) {
582 583
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
584
			    dl->ops.target.sym->name);
585 586 587 588
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
589
	symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt);
590
	sym_title(ms->sym, ms->map, title, sizeof(title));
591
	ui_browser__show_title(&browser->b, title);
592 593 594
	return true;
}

595 596 597
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
598 599 600 601
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
602
	struct disasm_line *pos;
603 604

	*idx = 0;
605
	list_for_each_entry(pos, &notes->src->source, al.node) {
606
		if (pos->al.offset == offset)
607
			return pos;
608
		if (!disasm_line__filter(&browser->b, &pos->al.node))
609 610 611 612 613 614 615 616
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
617
	struct disasm_line *dl = disasm_line(browser->selection);
618
	u64 offset;
619
	s64 idx;
620

621
	if (!ins__is_jump(&dl->ins))
622 623
		return false;

624 625
	offset = dl->ops.target.offset;
	dl = annotate_browser__find_offset(browser, offset, &idx);
626
	if (dl == NULL) {
627
		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
628 629 630
		return true;
	}

631
	annotate_browser__set_top(browser, &dl->al, idx);
632

633 634 635
	return true;
}

636
static
637
struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
638
					  char *s, s64 *idx)
639 640 641 642
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
643
	struct annotation_line *al = browser->selection;
644 645

	*idx = browser->b.index;
646 647
	list_for_each_entry_continue(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
648 649 650 651
			continue;

		++*idx;

652 653
		if (al->line && strstr(al->line, s) != NULL)
			return al;
654 655 656 657 658 659 660
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
661
	struct annotation_line *al;
662 663
	s64 idx;

664 665
	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (al == NULL) {
666 667 668 669
		ui_helpline__puts("String not found!");
		return false;
	}

670
	annotate_browser__set_top(browser, al, idx);
671 672 673 674
	browser->searching_backwards = false;
	return true;
}

675
static
676
struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
677
						  char *s, s64 *idx)
678 679 680 681
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
682
	struct annotation_line *al = browser->selection;
683 684

	*idx = browser->b.index;
685 686
	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
687 688 689 690
			continue;

		--*idx;

691 692
		if (al->line && strstr(al->line, s) != NULL)
			return al;
693 694 695 696 697 698 699
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
700
	struct annotation_line *al;
701 702
	s64 idx;

703 704
	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (al == NULL) {
705 706 707 708
		ui_helpline__puts("String not found!");
		return false;
	}

709
	annotate_browser__set_top(browser, al, idx);
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 754 755 756 757 758 759 760 761
	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);
}

762 763
static void annotate_browser__update_addr_width(struct annotate_browser *browser)
{
764 765 766 767
	struct map_symbol *ms = browser->b.priv;
	struct annotation *notes = symbol__annotation(ms->sym);

	if (notes->options->use_offset)
768 769 770 771 772 773
		browser->target_width = browser->min_addr_width;
	else
		browser->target_width = browser->max_addr_width;

	browser->addr_width = browser->target_width;

774
	if (notes->options->show_nr_jumps)
775 776 777
		browser->addr_width += browser->jumps_width + 1;
}

778 779
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
780
				 struct hist_browser_timer *hbt)
781 782
{
	struct rb_node *nd = NULL;
783
	struct map_symbol *ms = browser->b.priv;
784
	struct symbol *sym = ms->sym;
785
	struct annotation *notes = symbol__annotation(ms->sym);
786
	const char *help = "Press 'h' for help on key bindings";
787
	int delay_secs = hbt ? hbt->refresh : 0;
788
	int key;
789
	char title[SYM_TITLE_MAX_SIZE];
790

791 792
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
793
		return -1;
794

795
	annotate_browser__calc_percent(browser, evsel);
796

797 798 799
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
800
	}
801

802
	nd = browser->curr_hot;
803

804
	while (1) {
805
		key = ui_browser__run(&browser->b, delay_secs);
806

807
		if (delay_secs != 0) {
808
			annotate_browser__calc_percent(browser, evsel);
809 810 811 812 813 814 815 816 817
			/*
			 * 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;
		}

818
		switch (key) {
819
		case K_TIMER:
820 821
			if (hbt)
				hbt->timer(hbt->arg);
822 823

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

921
			if (browser->selection == NULL)
922
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
923
			else if (browser->selection->offset == -1)
924
				ui_helpline__puts("Actions are only available for assembly lines.");
925
			else if (!dl->ins.ops)
926
				goto show_sup_ins;
927
			else if (ins__is_ret(&dl->ins))
928
				goto out;
929
			else if (!(annotate_browser__jump(browser) ||
930
				     annotate_browser__callq(browser, evsel, hbt))) {
931
show_sup_ins:
932
				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
933
			}
934
			continue;
935
		}
936
		case 't':
937 938 939 940 941
			if (notes->options->show_total_period) {
				notes->options->show_total_period = false;
				notes->options->show_nr_samples = true;
			} else if (notes->options->show_nr_samples)
				notes->options->show_nr_samples = false;
942
			else
943
				notes->options->show_total_period = true;
944 945
			annotate_browser__update_addr_width(browser);
			continue;
946 947
		case K_LEFT:
		case K_ESC:
948 949
		case 'q':
		case CTRL('c'):
950
			goto out;
951 952
		default:
			continue;
953
		}
954 955

		if (nd != NULL)
956
			annotate_browser__set_rb_top(browser, nd);
957 958
	}
out:
959
	ui_browser__hide(&browser->b);
960
	return key;
961 962
}

963 964 965
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
			     struct hist_browser_timer *hbt)
{
966
	/* Set default value for show_total_period and show_nr_samples  */
967
	annotate_browser__opts.show_total_period =
968 969 970
		symbol_conf.show_total_period;
	annotate_browser__opts.show_nr_samples =
		symbol_conf.show_nr_samples;
971

972 973 974
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

975
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
976
			     struct hist_browser_timer *hbt)
977
{
978 979 980 981
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

982
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
983 984
}

985 986 987 988
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;
989 990
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
991
	struct annotation *notes = symbol__annotation(sym);
992 993 994 995

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

	for (offset = 0; offset < size; ++offset) {
998
		struct annotation_line *al = notes->offsets[offset];
999 1000
		struct disasm_line *dl;
		struct browser_line *blt;
1001

1002 1003
		dl = disasm_line(al);

1004
		if (!disasm_line__is_valid_jump(dl, sym))
1005 1006
			continue;

1007
		al = notes->offsets[dl->ops.target.offset];
1008

1009 1010 1011 1012
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
1013
		if (al == NULL)
1014 1015
			continue;

1016 1017 1018
		blt = browser_line(al);
		if (++blt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = blt->jump_sources;
1019 1020

		++browser->nr_jumps;
1021 1022 1023
	}
}

1024 1025 1026 1027 1028 1029 1030 1031 1032
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

1033 1034
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
1035
			 struct hist_browser_timer *hbt)
1036
{
1037
	struct annotation_line *al;
1038
	struct annotation *notes = symbol__annotation(sym);
1039
	size_t size;
1040 1041 1042 1043
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
1044 1045
	struct annotate_browser browser = {
		.b = {
1046
			.refresh = annotate_browser__refresh,
1047 1048
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
1049
			.filter  = disasm_line__filter,
1050
			.priv	 = &ms,
1051
			.use_navkeypressed = true,
1052
		},
1053
	};
1054
	int ret = -1, err;
1055
	int nr_pcnt = 1;
1056

1057
	if (sym == NULL)
1058 1059
		return -1;

1060 1061
	size = symbol__size(sym);

1062
	if (map->dso->annotate_warned)
1063 1064
		return -1;

1065 1066
	notes->options = &annotate_browser__opts;

1067 1068
	notes->offsets = zalloc(size * sizeof(struct annotation_line *));
	if (notes->offsets == NULL) {
1069 1070 1071 1072
		ui__error("Not enough memory!");
		return -1;
	}

1073
	if (perf_evsel__is_group_event(evsel))
1074 1075
		nr_pcnt = evsel->nr_members;

1076
	err = symbol__annotate(sym, map, evsel, sizeof(struct browser_line), &browser.arch);
1077 1078 1079 1080
	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);
1081
		goto out_free_offsets;
1082 1083
	}

1084 1085
	symbol__calc_percent(sym, evsel);

1086
	ui_helpline__push("Press ESC to exit");
1087

1088
	browser.start = map__rip_2objdump(map, sym->start);
1089

1090
	list_for_each_entry(al, &notes->src->source, node) {
1091
		struct browser_line *bpos;
1092
		size_t line_len = strlen(al->line);
1093

1094 1095
		if (browser.b.width < line_len)
			browser.b.width = line_len;
1096
		bpos = browser_line(al);
1097
		bpos->idx = browser.nr_entries++;
1098
		if (al->offset != -1) {
1099
			bpos->idx_asm = browser.nr_asm_entries++;
1100 1101 1102 1103 1104 1105 1106
			/*
			 * 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
 			 */
1107
			if (al->offset < (s64)size)
1108
				notes->offsets[al->offset] = al;
1109
		} else
1110
			bpos->idx_asm = -1;
1111 1112
	}

1113
	annotate_browser__mark_jump_targets(&browser, size);
1114
	annotation__compute_ipc(notes, size);
1115

1116
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1117
	browser.max_addr_width = hex_width(sym->end);
1118
	browser.jumps_width = width_jumps(browser.max_jump_sources);
1119
	notes->nr_events = nr_pcnt;
1120
	browser.b.nr_entries = browser.nr_entries;
1121
	browser.b.entries = &notes->src->source,
1122
	browser.b.width += 18; /* Percentage */
1123

1124
	if (notes->options->hide_src_code)
1125 1126 1127 1128
		annotate_browser__init_asm_mode(&browser);

	annotate_browser__update_addr_width(&browser);

1129
	ret = annotate_browser__run(&browser, evsel, hbt);
1130 1131

	annotated_source__purge(notes->src);
1132 1133

out_free_offsets:
1134
	zfree(&notes->offsets);
1135 1136
	return ret;
}
1137 1138 1139

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

1141 1142 1143
/*
 * Keep the entries sorted, they are bsearch'ed
 */
1144
static struct annotate_config {
1145 1146 1147 1148 1149
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
1150
	ANNOTATE_CFG(show_linenr),
1151
	ANNOTATE_CFG(show_nr_jumps),
1152
	ANNOTATE_CFG(show_nr_samples),
1153
	ANNOTATE_CFG(show_total_period),
1154
	ANNOTATE_CFG(use_offset),
1155 1156 1157 1158 1159 1160
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1161
	const struct annotate_config *cfg = cfgp;
1162 1163 1164 1165

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

1166 1167
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1168
{
1169
	struct annotate_config *cfg;
1170 1171
	const char *name;

1172
	if (!strstarts(var, "annotate."))
1173 1174 1175 1176
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1177
		      sizeof(struct annotate_config), annotate_config__cmp);
1178 1179

	if (cfg == NULL)
1180 1181 1182
		ui__warning("%s variable unknown, ignoring...", var);
	else
		*cfg->value = perf_config_bool(name, value);
1183 1184 1185 1186 1187 1188 1189
	return 0;
}

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