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

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

25 26 27 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
	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			    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];
55 56
};

57 58 59 60 61 62
static inline struct annotation *browser__annotation(struct ui_browser *browser)
{
	struct map_symbol *ms = browser->priv;
	return symbol__annotation(ms->sym);
}

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

	ptr = container_of(al, struct disasm_line, al);
	return ptr - sizeof(struct browser_line);
69 70
}

71
static bool disasm_line__filter(struct ui_browser *browser, void *entry)
72
{
73
	struct annotation *notes = browser__annotation(browser);
74 75

	if (notes->options->hide_src_code) {
76 77 78
		struct annotation_line *al = list_entry(entry, struct annotation_line, node);

		return al->offset == -1;
79 80 81 82 83
	}

	return false;
}

84
static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
85
{
86
	struct annotation *notes = browser__annotation(browser);
87

88
	if (current && (!browser->use_navkeypressed || browser->navkeypressed))
89
		return HE_COLORSET_SELECTED;
90
	if (nr == notes->max_jump_sources)
91 92 93 94 95 96
		return HE_COLORSET_TOP;
	if (nr > 1)
		return HE_COLORSET_MEDIUM;
	return HE_COLORSET_NORMAL;
}

97
static int ui_browser__set_jumps_percent_color(struct ui_browser *browser, int nr, bool current)
98
{
99 100
	 int color = ui_browser__jumps_percent_color(browser, nr, current);
	 return ui_browser__set_color(browser, color);
101 102
}

103 104 105
static void disasm_line__write(struct disasm_line *dl, struct ui_browser *browser,
			       char *bf, size_t size)
{
106
	struct annotation *notes = browser__annotation(browser);
107

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

128
	disasm_line__scnprintf(dl, bf, size, !notes->options->use_offset);
129 130
}

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

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

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

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

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

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

205
	SLsmg_write_char(' ');
206 207

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

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

226
		if (!notes->options->use_offset)
227 228
			addr += ab->start;

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

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

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

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

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

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

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

	return true;
}

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

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

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

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

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

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

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

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

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

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

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
372
	struct annotation *notes = browser__annotation(browser);
373
	int ret = ui_browser__list_head_refresh(browser);
374
	int pcnt_width = annotation__pcnt_width(notes);
375

376
	if (notes->options->jump_arrows)
377
		annotate_browser__draw_current_jump(browser);
378

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

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

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

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

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

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

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

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

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

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

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

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

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
441
	struct annotation *notes = browser__annotation(&browser->b);
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 (notes->options->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 *notes = browser__annotation(&browser->b);
500
	struct annotation_line *al;
501
	struct browser_line *bl;
502 503 504
	off_t offset = browser->b.index - browser->b.top_idx;

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

508
	if (notes->options->hide_src_code) {
509 510
		if (bl->idx_asm < offset)
			offset = bl->idx;
511 512

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

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

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

	return true;
}

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

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

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

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

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

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

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

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

585 586 587
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
588
{
589
	struct annotation *notes = browser__annotation(&browser->b);
590
	struct disasm_line *pos;
591 592

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

	return NULL;
}

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

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

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

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

621 622 623
	return true;
}

624
static
625
struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
626
					  char *s, s64 *idx)
627
{
628
	struct annotation *notes = browser__annotation(&browser->b);
629
	struct annotation_line *al = browser->selection;
630 631

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

		++*idx;

638 639
		if (al->line && strstr(al->line, s) != NULL)
			return al;
640 641 642 643 644 645 646
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
647
	struct annotation_line *al;
648 649
	s64 idx;

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

656
	annotate_browser__set_top(browser, al, idx);
657 658 659 660
	browser->searching_backwards = false;
	return true;
}

661
static
662
struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
663
						  char *s, s64 *idx)
664
{
665
	struct annotation *notes = browser__annotation(&browser->b);
666
	struct annotation_line *al = browser->selection;
667 668

	*idx = browser->b.index;
669 670
	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
671 672 673 674
			continue;

		--*idx;

675 676
		if (al->line && strstr(al->line, s) != NULL)
			return al;
677 678 679 680 681 682 683
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
684
	struct annotation_line *al;
685 686
	s64 idx;

687 688
	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (al == NULL) {
689 690 691 692
		ui_helpline__puts("String not found!");
		return false;
	}

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

746 747
static void annotate_browser__update_addr_width(struct annotate_browser *browser)
{
748
	struct annotation *notes = browser__annotation(&browser->b);
749 750

	if (notes->options->use_offset)
751 752 753 754 755 756
		browser->target_width = browser->min_addr_width;
	else
		browser->target_width = browser->max_addr_width;

	browser->addr_width = browser->target_width;

757
	if (notes->options->show_nr_jumps)
758 759 760
		browser->addr_width += browser->jumps_width + 1;
}

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

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

778
	annotate_browser__calc_percent(browser, evsel);
779

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

785
	nd = browser->curr_hot;
786

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

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

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

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

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

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

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

955 956 957
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

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

965
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
966 967
}

968 969 970 971
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;
972 973
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
974
	struct annotation *notes = symbol__annotation(sym);
975 976 977 978

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

	for (offset = 0; offset < size; ++offset) {
981
		struct annotation_line *al = notes->offsets[offset];
982 983
		struct disasm_line *dl;
		struct browser_line *blt;
984

985 986
		dl = disasm_line(al);

987
		if (!disasm_line__is_valid_jump(dl, sym))
988 989
			continue;

990
		al = notes->offsets[dl->ops.target.offset];
991

992 993 994 995
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
996
		if (al == NULL)
997 998
			continue;

999
		blt = browser_line(al);
1000 1001
		if (++blt->jump_sources > notes->max_jump_sources)
			notes->max_jump_sources = blt->jump_sources;
1002 1003

		++browser->nr_jumps;
1004 1005 1006
	}
}

1007 1008 1009 1010 1011 1012 1013 1014 1015
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

1016 1017
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
1018
			 struct hist_browser_timer *hbt)
1019
{
1020
	struct annotation_line *al;
1021
	struct annotation *notes = symbol__annotation(sym);
1022
	size_t size;
1023 1024 1025 1026
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
1027 1028
	struct annotate_browser browser = {
		.b = {
1029
			.refresh = annotate_browser__refresh,
1030 1031
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
1032
			.filter  = disasm_line__filter,
1033
			.priv	 = &ms,
1034
			.use_navkeypressed = true,
1035
		},
1036
	};
1037
	int ret = -1, err;
1038
	int nr_pcnt = 1;
1039

1040
	if (sym == NULL)
1041 1042
		return -1;

1043 1044
	size = symbol__size(sym);

1045
	if (map->dso->annotate_warned)
1046 1047
		return -1;

1048 1049
	notes->options = &annotate_browser__opts;

1050 1051
	notes->offsets = zalloc(size * sizeof(struct annotation_line *));
	if (notes->offsets == NULL) {
1052 1053 1054 1055
		ui__error("Not enough memory!");
		return -1;
	}

1056
	if (perf_evsel__is_group_event(evsel))
1057 1058
		nr_pcnt = evsel->nr_members;

1059
	err = symbol__annotate(sym, map, evsel, sizeof(struct browser_line), &browser.arch);
1060 1061 1062 1063
	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);
1064
		goto out_free_offsets;
1065 1066
	}

1067 1068
	symbol__calc_percent(sym, evsel);

1069
	ui_helpline__push("Press ESC to exit");
1070

1071
	browser.start = map__rip_2objdump(map, sym->start);
1072

1073
	list_for_each_entry(al, &notes->src->source, node) {
1074
		struct browser_line *bpos;
1075
		size_t line_len = strlen(al->line);
1076

1077 1078
		if (browser.b.width < line_len)
			browser.b.width = line_len;
1079
		bpos = browser_line(al);
1080
		bpos->idx = browser.nr_entries++;
1081
		if (al->offset != -1) {
1082
			bpos->idx_asm = browser.nr_asm_entries++;
1083 1084 1085 1086 1087 1088 1089
			/*
			 * 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
 			 */
1090
			if (al->offset < (s64)size)
1091
				notes->offsets[al->offset] = al;
1092
		} else
1093
			bpos->idx_asm = -1;
1094 1095
	}

1096
	annotate_browser__mark_jump_targets(&browser, size);
1097
	annotation__compute_ipc(notes, size);
1098

1099
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1100
	browser.max_addr_width = hex_width(sym->end);
1101
	browser.jumps_width = width_jumps(notes->max_jump_sources);
1102
	notes->nr_events = nr_pcnt;
1103
	browser.b.nr_entries = browser.nr_entries;
1104
	browser.b.entries = &notes->src->source,
1105
	browser.b.width += 18; /* Percentage */
1106

1107
	if (notes->options->hide_src_code)
1108 1109 1110 1111
		annotate_browser__init_asm_mode(&browser);

	annotate_browser__update_addr_width(&browser);

1112
	ret = annotate_browser__run(&browser, evsel, hbt);
1113 1114

	annotated_source__purge(notes->src);
1115 1116

out_free_offsets:
1117
	zfree(&notes->offsets);
1118 1119
	return ret;
}
1120 1121 1122

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

1124 1125 1126
/*
 * Keep the entries sorted, they are bsearch'ed
 */
1127
static struct annotate_config {
1128 1129 1130 1131 1132
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
1133
	ANNOTATE_CFG(show_linenr),
1134
	ANNOTATE_CFG(show_nr_jumps),
1135
	ANNOTATE_CFG(show_nr_samples),
1136
	ANNOTATE_CFG(show_total_period),
1137
	ANNOTATE_CFG(use_offset),
1138 1139 1140 1141 1142 1143
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1144
	const struct annotate_config *cfg = cfgp;
1145 1146 1147 1148

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

1149 1150
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1151
{
1152
	struct annotate_config *cfg;
1153 1154
	const char *name;

1155
	if (!strstarts(var, "annotate."))
1156 1157 1158 1159
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1160
		      sizeof(struct annotate_config), annotate_config__cmp);
1161 1162

	if (cfg == NULL)
1163 1164 1165
		ui__warning("%s variable unknown, ignoring...", var);
	else
		*cfg->value = perf_config_bool(name, value);
1166 1167 1168 1169 1170 1171 1172
	return 0;
}

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