annotate.c 29.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
struct browser_line {
	u32	idx;
	int	idx_asm;
28 29
};

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

35 36
struct arch;

37
struct annotate_browser {
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
	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;
	bool			    searching_backwards;
	u8			    addr_width;
	u8			    jumps_width;
	u8			    target_width;
	u8			    min_addr_width;
	u8			    max_addr_width;
	char			    search_bf[128];
53 54
};

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

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

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

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

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

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

	return false;
}

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

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

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

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

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

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

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

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

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

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

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

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

202
	SLsmg_write_char(' ');
203 204

	/* The scroll bar isn't being used */
205
	if (!browser->navkeypressed)
206 207
		width += 1;

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

223
		if (!notes->options->use_offset)
224 225
			addr += ab->start;

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

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

249
		if (change_color)
250
			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
251
		ui_browser__write_nstring(browser, bf, printed);
252
		if (change_color)
253
			ui_browser__set_color(browser, color);
254

255 256
		disasm_line__write(disasm_line(al), browser, bf, sizeof(bf));

257
		ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
258
	}
259

260
	if (current_entry)
261
		ab->selection = al;
262 263
}

264 265
static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
{
266
	struct disasm_line *pos = list_prev_entry(cursor, al.node);
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
	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);
}

283
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
284 285
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
286
	struct disasm_line *cursor = disasm_line(ab->selection);
287
	struct annotation_line *target;
288
	struct browser_line *btarget, *bcursor;
289
	unsigned int from, to;
290 291
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
292
	struct annotation *notes = symbol__annotation(sym);
293
	u8 pcnt_width = annotation__pcnt_width(notes);
294
	int width;
295 296 297 298

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

300
	if (!disasm_line__is_valid_jump(cursor, sym))
301
		return;
302

303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
	/*
	 * 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.
	 */
323
	target = notes->offsets[cursor->ops.target.offset];
324
	if (target == NULL) {
325
		ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
326 327 328
				    cursor->ops.target.offset);
		return;
	}
329

330
	bcursor = browser_line(&cursor->al);
331
	btarget = browser_line(target);
332

333
	if (notes->options->hide_src_code) {
334
		from = bcursor->idx_asm;
335 336
		to = btarget->idx_asm;
	} else {
337
		from = (u64)bcursor->idx;
338 339 340
		to = (u64)btarget->idx;
	}

341
	width = annotation__cycles_width(notes);
J
Jin Yao 已提交
342

343
	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
J
Jin Yao 已提交
344 345
	__ui_browser__line_arrow(browser,
				 pcnt_width + 2 + ab->addr_width + width,
346
				 from, to);
347 348 349

	if (is_fused(ab, cursor)) {
		ui_browser__mark_fused(browser,
J
Jin Yao 已提交
350
				       pcnt_width + 3 + ab->addr_width + width,
351 352 353
				       from - 1,
				       to > from ? true : false);
	}
354 355 356 357
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
358
	struct annotation *notes = browser__annotation(browser);
359
	int ret = ui_browser__list_head_refresh(browser);
360
	int pcnt_width = annotation__pcnt_width(notes);
361

362
	if (notes->options->jump_arrows)
363
		annotate_browser__draw_current_jump(browser);
364

365
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
366
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
367 368 369
	return ret;
}

370
static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
371 372 373
{
	int i;

374
	for (i = 0; i < a->samples_nr; i++) {
375
		if (a->samples[i].percent == b->samples[i].percent)
376
			continue;
377
		return a->samples[i].percent < b->samples[i].percent;
378 379 380 381
	}
	return 0;
}

382
static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
383
{
384
	struct rb_node **p = &root->rb_node;
385
	struct rb_node *parent = NULL;
386
	struct annotation_line *l;
387 388 389

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

392
		if (disasm__cmp(al, l))
393 394 395 396
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
397 398
	rb_link_node(&al->rb_node, parent, p);
	rb_insert_color(&al->rb_node, root);
399 400
}

401
static void annotate_browser__set_top(struct annotate_browser *browser,
402
				      struct annotation_line *pos, u32 idx)
403 404 405
{
	unsigned back;

406 407 408
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
409

410
	while (browser->b.top_idx != 0 && back != 0) {
411
		pos = list_entry(pos->node.prev, struct annotation_line, node);
412

413
		if (disasm_line__filter(&browser->b, &pos->node))
414 415
			continue;

416
		--browser->b.top_idx;
417 418 419
		--back;
	}

420
	browser->b.top = pos;
421
	browser->b.navkeypressed = true;
422 423 424 425 426
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
427
	struct annotation *notes = browser__annotation(&browser->b);
428
	struct browser_line *bpos;
429
	struct annotation_line *pos;
430
	u32 idx;
431

432 433
	pos = rb_entry(nd, struct annotation_line, rb_node);
	bpos = browser_line(pos);
434

435
	idx = bpos->idx;
436
	if (notes->options->hide_src_code)
437 438
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
439
	browser->curr_hot = nd;
440 441
}

442
static void annotate_browser__calc_percent(struct annotate_browser *browser,
443
					   struct perf_evsel *evsel)
444
{
445 446
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
447
	struct annotation *notes = symbol__annotation(sym);
448
	struct disasm_line *pos;
449 450 451 452 453

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

454 455
	symbol__calc_percent(sym, evsel);

456
	list_for_each_entry(pos, &notes->src->source, al.node) {
457 458
		double max_percent = 0.0;
		int i;
459

460
		if (pos->al.offset == -1) {
461
			RB_CLEAR_NODE(&pos->al.rb_node);
462 463 464
			continue;
		}

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

468 469
			if (max_percent < sample->percent)
				max_percent = sample->percent;
470 471
		}

472
		if (max_percent < 0.01 && pos->al.ipc == 0) {
473
			RB_CLEAR_NODE(&pos->al.rb_node);
474 475
			continue;
		}
476
		disasm_rb_tree__insert(&browser->entries, &pos->al);
477 478 479 480 481 482
	}
	pthread_mutex_unlock(&notes->lock);

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

483 484
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
485
	struct annotation *notes = browser__annotation(&browser->b);
486
	struct annotation_line *al;
487
	struct browser_line *bl;
488 489 490
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
491 492
	al = list_entry(browser->b.top, struct annotation_line, node);
	bl = browser_line(al);
493

494
	if (notes->options->hide_src_code) {
495 496
		if (bl->idx_asm < offset)
			offset = bl->idx;
497 498

		browser->b.nr_entries = browser->nr_entries;
499
		notes->options->hide_src_code = false;
500
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
501 502
		browser->b.top_idx = bl->idx - offset;
		browser->b.index = bl->idx;
503
	} else {
504
		if (bl->idx_asm < 0) {
505 506 507 508 509
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

510 511
		if (bl->idx_asm < offset)
			offset = bl->idx_asm;
512 513

		browser->b.nr_entries = browser->nr_asm_entries;
514
		notes->options->hide_src_code = true;
515
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
516 517
		browser->b.top_idx = bl->idx_asm - offset;
		browser->b.index = bl->idx_asm;
518 519 520 521 522
	}

	return true;
}

523 524 525 526 527 528
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;
}

529 530 531 532 533 534 535 536
#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);
}

537 538
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
539
				    struct hist_browser_timer *hbt)
540 541
{
	struct map_symbol *ms = browser->b.priv;
542
	struct disasm_line *dl = disasm_line(browser->selection);
543
	struct annotation *notes;
544
	char title[SYM_TITLE_MAX_SIZE];
545

546
	if (!ins__is_call(&dl->ins))
547 548
		return false;

549
	if (!dl->ops.target.sym) {
550 551 552 553
		ui_helpline__puts("The called function was not found.");
		return true;
	}

554
	notes = symbol__annotation(dl->ops.target.sym);
555 556
	pthread_mutex_lock(&notes->lock);

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

	pthread_mutex_unlock(&notes->lock);
565
	symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt);
566
	sym_title(ms->sym, ms->map, title, sizeof(title));
567
	ui_browser__show_title(&browser->b, title);
568 569 570
	return true;
}

571 572 573
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
574
{
575
	struct annotation *notes = browser__annotation(&browser->b);
576
	struct disasm_line *pos;
577 578

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

	return NULL;
}

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

595
	if (!ins__is_jump(&dl->ins))
596 597
		return false;

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

605
	annotate_browser__set_top(browser, &dl->al, idx);
606

607 608 609
	return true;
}

610
static
611
struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
612
					  char *s, s64 *idx)
613
{
614
	struct annotation *notes = browser__annotation(&browser->b);
615
	struct annotation_line *al = browser->selection;
616 617

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

		++*idx;

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

	return NULL;
}

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

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

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

647
static
648
struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
649
						  char *s, s64 *idx)
650
{
651
	struct annotation *notes = browser__annotation(&browser->b);
652
	struct annotation_line *al = browser->selection;
653 654

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

		--*idx;

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

	return NULL;
}

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

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

679
	annotate_browser__set_top(browser, al, idx);
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731
	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);
}

732 733
static void annotate_browser__update_addr_width(struct annotate_browser *browser)
{
734
	struct annotation *notes = browser__annotation(&browser->b);
735 736

	if (notes->options->use_offset)
737 738 739 740 741 742
		browser->target_width = browser->min_addr_width;
	else
		browser->target_width = browser->max_addr_width;

	browser->addr_width = browser->target_width;

743
	if (notes->options->show_nr_jumps)
744 745 746
		browser->addr_width += browser->jumps_width + 1;
}

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

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

764
	annotate_browser__calc_percent(browser, evsel);
765

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

771
	nd = browser->curr_hot;
772

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

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

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

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

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

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

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

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

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

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

954 955 956 957 958 959 960 961 962
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

963 964
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
965
			 struct hist_browser_timer *hbt)
966
{
967
	struct annotation_line *al;
968
	struct annotation *notes = symbol__annotation(sym);
969
	size_t size;
970 971 972 973
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
974 975
	struct annotate_browser browser = {
		.b = {
976
			.refresh = annotate_browser__refresh,
977 978
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
979
			.filter  = disasm_line__filter,
980
			.priv	 = &ms,
981
			.use_navkeypressed = true,
982
		},
983
	};
984
	int ret = -1, err;
985
	int nr_pcnt = 1;
986

987
	if (sym == NULL)
988 989
		return -1;

990 991
	size = symbol__size(sym);

992
	if (map->dso->annotate_warned)
993 994
		return -1;

995 996
	notes->options = &annotate_browser__opts;

997 998
	notes->offsets = zalloc(size * sizeof(struct annotation_line *));
	if (notes->offsets == NULL) {
999 1000 1001 1002
		ui__error("Not enough memory!");
		return -1;
	}

1003
	if (perf_evsel__is_group_event(evsel))
1004 1005
		nr_pcnt = evsel->nr_members;

1006
	err = symbol__annotate(sym, map, evsel, sizeof(struct browser_line), &browser.arch);
1007 1008 1009 1010
	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);
1011
		goto out_free_offsets;
1012 1013
	}

1014 1015
	symbol__calc_percent(sym, evsel);

1016
	ui_helpline__push("Press ESC to exit");
1017

1018
	browser.start = map__rip_2objdump(map, sym->start);
1019

1020
	list_for_each_entry(al, &notes->src->source, node) {
1021
		struct browser_line *bpos;
1022
		size_t line_len = strlen(al->line);
1023

1024 1025
		if (browser.b.width < line_len)
			browser.b.width = line_len;
1026
		bpos = browser_line(al);
1027
		bpos->idx = browser.nr_entries++;
1028
		if (al->offset != -1) {
1029
			bpos->idx_asm = browser.nr_asm_entries++;
1030 1031 1032 1033 1034 1035 1036
			/*
			 * 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
 			 */
1037
			if (al->offset < (s64)size)
1038
				notes->offsets[al->offset] = al;
1039
		} else
1040
			bpos->idx_asm = -1;
1041 1042
	}

1043
	annotation__mark_jump_targets(notes, sym);
1044
	annotation__compute_ipc(notes, size);
1045

1046
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1047
	browser.max_addr_width = hex_width(sym->end);
1048
	browser.jumps_width = width_jumps(notes->max_jump_sources);
1049
	notes->nr_events = nr_pcnt;
1050
	browser.b.nr_entries = browser.nr_entries;
1051
	browser.b.entries = &notes->src->source,
1052
	browser.b.width += 18; /* Percentage */
1053

1054
	if (notes->options->hide_src_code)
1055 1056 1057 1058
		annotate_browser__init_asm_mode(&browser);

	annotate_browser__update_addr_width(&browser);

1059
	ret = annotate_browser__run(&browser, evsel, hbt);
1060 1061

	annotated_source__purge(notes->src);
1062 1063

out_free_offsets:
1064
	zfree(&notes->offsets);
1065 1066
	return ret;
}
1067 1068 1069

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

1071 1072 1073
/*
 * Keep the entries sorted, they are bsearch'ed
 */
1074
static struct annotate_config {
1075 1076 1077 1078 1079
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
1080
	ANNOTATE_CFG(show_linenr),
1081
	ANNOTATE_CFG(show_nr_jumps),
1082
	ANNOTATE_CFG(show_nr_samples),
1083
	ANNOTATE_CFG(show_total_period),
1084
	ANNOTATE_CFG(use_offset),
1085 1086 1087 1088 1089 1090
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1091
	const struct annotate_config *cfg = cfgp;
1092 1093 1094 1095

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

1096 1097
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1098
{
1099
	struct annotate_config *cfg;
1100 1101
	const char *name;

1102
	if (!strstarts(var, "annotate."))
1103 1104 1105 1106
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1107
		      sizeof(struct annotate_config), annotate_config__cmp);
1108 1109

	if (cfg == NULL)
1110 1111 1112
		ui__warning("%s variable unknown, ignoring...", var);
	else
		*cfg->value = perf_config_bool(name, value);
1113 1114 1115 1116 1117 1118 1119
	return 0;
}

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