annotate.c 30.2 KB
Newer Older
1
#include "../../util/util.h"
2 3
#include "../browser.h"
#include "../helpline.h"
4 5
#include "../ui.h"
#include "../util.h"
6 7 8 9
#include "../../util/annotate.h"
#include "../../util/hist.h"
#include "../../util/sort.h"
#include "../../util/symbol.h"
10
#include "../../util/evsel.h"
11
#include "../../util/config.h"
12
#include <pthread.h>
13

14 15 16 17 18
struct disasm_line_samples {
	double		percent;
	u64		nr;
};

19 20 21
#define IPC_WIDTH 6
#define CYCLES_WIDTH 6

22
struct browser_disasm_line {
23 24 25 26
	struct rb_node			rb_node;
	u32				idx;
	int				idx_asm;
	int				jump_sources;
27 28 29 30
	/*
	 * actual length of this array is saved on the nr_events field
	 * of the struct annotate_browser
	 */
31
	struct disasm_line_samples	samples[1];
32 33
};

34 35 36 37
static struct annotate_browser_opt {
	bool hide_src_code,
	     use_offset,
	     jump_arrows,
38
	     show_linenr,
39 40
	     show_nr_jumps,
	     show_total_period;
41 42 43 44 45
} annotate_browser__opts = {
	.use_offset	= true,
	.jump_arrows	= true,
};

46 47 48
struct annotate_browser {
	struct ui_browser b;
	struct rb_root	  entries;
49
	struct rb_node	  *curr_hot;
50
	struct disasm_line  *selection;
51
	struct disasm_line  **offsets;
52
	int		    nr_events;
53
	u64		    start;
54 55
	int		    nr_asm_entries;
	int		    nr_entries;
56 57
	int		    max_jump_sources;
	int		    nr_jumps;
58
	bool		    searching_backwards;
59
	bool		    have_cycles;
60
	u8		    addr_width;
61 62
	u8		    jumps_width;
	u8		    target_width;
63 64
	u8		    min_addr_width;
	u8		    max_addr_width;
65
	char		    search_bf[128];
66 67
};

68
static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
69
{
70
	return (struct browser_disasm_line *)(dl + 1);
71 72
}

73 74
static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
				void *entry)
75
{
76
	if (annotate_browser__opts.hide_src_code) {
77 78
		struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
		return dl->offset == -1;
79 80 81 82 83
	}

	return false;
}

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
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);
}

103 104 105 106 107 108 109 110 111
static int annotate_browser__pcnt_width(struct annotate_browser *ab)
{
	int w = 7 * ab->nr_events;

	if (ab->have_cycles)
		w += IPC_WIDTH + CYCLES_WIDTH;
	return w;
}

112
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
113
{
114
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
115
	struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
116
	struct browser_disasm_line *bdl = disasm_line__browser(dl);
117
	bool current_entry = ui_browser__is_current_entry(browser, row);
118
	bool change_color = (!annotate_browser__opts.hide_src_code &&
119 120 121
			     (!current_entry || (browser->use_navkeypressed &&
					         !browser->navkeypressed)));
	int width = browser->width, printed;
122
	int i, pcnt_width = annotate_browser__pcnt_width(ab);
123
	double percent_max = 0.0;
124
	char bf[256];
125

126
	for (i = 0; i < ab->nr_events; i++) {
127 128
		if (bdl->samples[i].percent > percent_max)
			percent_max = bdl->samples[i].percent;
129 130 131
	}

	if (dl->offset != -1 && percent_max != 0.0) {
132 133 134 135 136
		if (percent_max != 0.0) {
			for (i = 0; i < ab->nr_events; i++) {
				ui_browser__set_percent_color(browser,
							bdl->samples[i].percent,
							current_entry);
137 138 139 140 141 142 143
				if (annotate_browser__opts.show_total_period) {
					ui_browser__printf(browser, "%6" PRIu64 " ",
							   bdl->samples[i].nr);
				} else {
					ui_browser__printf(browser, "%6.2f ",
							   bdl->samples[i].percent);
				}
144 145
			}
		} else {
146
			ui_browser__write_nstring(browser, " ", 7 * ab->nr_events);
147
		}
148
	} else {
149
		ui_browser__set_percent_color(browser, 0, current_entry);
150
		ui_browser__write_nstring(browser, " ", 7 * ab->nr_events);
151 152 153
	}
	if (ab->have_cycles) {
		if (dl->ipc)
154
			ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, dl->ipc);
155
		else
156
			ui_browser__write_nstring(browser, " ", IPC_WIDTH);
157
		if (dl->cycles)
158 159
			ui_browser__printf(browser, "%*" PRIu64 " ",
					   CYCLES_WIDTH - 1, dl->cycles);
160
		else
161
			ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
162 163
	}

164
	SLsmg_write_char(' ');
165 166

	/* The scroll bar isn't being used */
167
	if (!browser->navkeypressed)
168 169
		width += 1;

170
	if (!*dl->line)
171
		ui_browser__write_nstring(browser, " ", width - pcnt_width);
172
	else if (dl->offset == -1) {
173 174 175 176 177
		if (dl->line_nr && annotate_browser__opts.show_linenr)
			printed = scnprintf(bf, sizeof(bf), "%-*d ",
					ab->addr_width + 1, dl->line_nr);
		else
			printed = scnprintf(bf, sizeof(bf), "%*s  ",
178
				    ab->addr_width, " ");
179 180
		ui_browser__write_nstring(browser, bf, printed);
		ui_browser__write_nstring(browser, dl->line, width - printed - pcnt_width + 1);
181
	} else {
182
		u64 addr = dl->offset;
183
		int color = -1;
184

185
		if (!annotate_browser__opts.use_offset)
186 187
			addr += ab->start;

188
		if (!annotate_browser__opts.use_offset) {
189
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
190
		} else {
191
			if (bdl->jump_sources) {
192
				if (annotate_browser__opts.show_nr_jumps) {
193 194 195 196 197 198
					int prev;
					printed = scnprintf(bf, sizeof(bf), "%*d ",
							    ab->jumps_width,
							    bdl->jump_sources);
					prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
											 current_entry);
199
					ui_browser__write_nstring(browser, bf, printed);
200
					ui_browser__set_color(browser, prev);
201 202
				}

203
				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
204
						    ab->target_width, addr);
205
			} else {
206 207
				printed = scnprintf(bf, sizeof(bf), "%*s  ",
						    ab->addr_width, " ");
208 209
			}
		}
210

211
		if (change_color)
212
			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
213
		ui_browser__write_nstring(browser, bf, printed);
214
		if (change_color)
215
			ui_browser__set_color(browser, color);
216 217
		if (dl->ins.ops && dl->ins.ops->scnprintf) {
			if (ins__is_jump(&dl->ins)) {
218
				bool fwd = dl->ops.target.offset > (u64)dl->offset;
219

220
				ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
221
								    SLSMG_UARROW_CHAR);
222
				SLsmg_write_char(' ');
223
			} else if (ins__is_call(&dl->ins)) {
224
				ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
225
				SLsmg_write_char(' ');
226
			} else if (ins__is_ret(&dl->ins)) {
227 228
				ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
				SLsmg_write_char(' ');
229
			} else {
230
				ui_browser__write_nstring(browser, " ", 2);
231
			}
232
		} else {
233
			ui_browser__write_nstring(browser, " ", 2);
234
		}
235

236
		disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
237
		ui_browser__write_nstring(browser, bf, width - pcnt_width - 3 - printed);
238
	}
239

240
	if (current_entry)
241
		ab->selection = dl;
242 243
}

244 245
static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
{
246
	if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins)
247 248 249 250 251 252 253
	    || !disasm_line__has_offset(dl)
	    || dl->ops.target.offset >= symbol__size(sym))
		return false;

	return true;
}

254
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
255 256
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
257 258
	struct disasm_line *cursor = ab->selection, *target;
	struct browser_disasm_line *btarget, *bcursor;
259
	unsigned int from, to;
260 261
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
262
	u8 pcnt_width = annotate_browser__pcnt_width(ab);
263 264 265 266

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

268
	if (!disasm_line__is_valid_jump(cursor, sym))
269
		return;
270

271 272 273
	target = ab->offsets[cursor->ops.target.offset];
	if (!target)
		return;
274

275 276
	bcursor = disasm_line__browser(cursor);
	btarget = disasm_line__browser(target);
277

278
	if (annotate_browser__opts.hide_src_code) {
279
		from = bcursor->idx_asm;
280 281
		to = btarget->idx_asm;
	} else {
282
		from = (u64)bcursor->idx;
283 284 285
		to = (u64)btarget->idx;
	}

286
	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
287 288
	__ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
				 from, to);
289 290 291 292
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
293
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
294
	int ret = ui_browser__list_head_refresh(browser);
295
	int pcnt_width = annotate_browser__pcnt_width(ab);
296

297
	if (annotate_browser__opts.jump_arrows)
298
		annotate_browser__draw_current_jump(browser);
299

300
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
301
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
302 303 304
	return ret;
}

305 306 307 308 309 310
static int disasm__cmp(struct browser_disasm_line *a,
		       struct browser_disasm_line *b, int nr_pcnt)
{
	int i;

	for (i = 0; i < nr_pcnt; i++) {
311
		if (a->samples[i].percent == b->samples[i].percent)
312
			continue;
313
		return a->samples[i].percent < b->samples[i].percent;
314 315 316 317 318 319
	}
	return 0;
}

static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl,
				   int nr_events)
320
{
321
	struct rb_node **p = &root->rb_node;
322
	struct rb_node *parent = NULL;
323
	struct browser_disasm_line *l;
324 325 326

	while (*p != NULL) {
		parent = *p;
327
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
328 329

		if (disasm__cmp(bdl, l, nr_events))
330 331 332 333
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
334 335
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
336 337
}

338
static void annotate_browser__set_top(struct annotate_browser *browser,
339
				      struct disasm_line *pos, u32 idx)
340 341 342
{
	unsigned back;

343 344 345
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
346

347
	while (browser->b.top_idx != 0 && back != 0) {
348
		pos = list_entry(pos->node.prev, struct disasm_line, node);
349

350
		if (disasm_line__filter(&browser->b, &pos->node))
351 352
			continue;

353
		--browser->b.top_idx;
354 355 356
		--back;
	}

357 358
	browser->b.top = pos;
	browser->b.navkeypressed = true;
359 360 361 362 363
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
364
	struct browser_disasm_line *bpos;
365
	struct disasm_line *pos;
366
	u32 idx;
367

368 369
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
370
	idx = bpos->idx;
371
	if (annotate_browser__opts.hide_src_code)
372 373
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
374
	browser->curr_hot = nd;
375 376
}

377
static void annotate_browser__calc_percent(struct annotate_browser *browser,
378
					   struct perf_evsel *evsel)
379
{
380 381
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
382
	struct annotation *notes = symbol__annotation(sym);
383 384
	struct disasm_line *pos, *next;
	s64 len = symbol__size(sym);
385 386 387 388 389 390

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
391
		struct browser_disasm_line *bpos = disasm_line__browser(pos);
392
		const char *path = NULL;
393 394
		double max_percent = 0.0;
		int i;
395 396 397 398 399 400 401 402

		if (pos->offset == -1) {
			RB_CLEAR_NODE(&bpos->rb_node);
			continue;
		}

		next = disasm__get_next_ip_line(&notes->src->source, pos);

403
		for (i = 0; i < browser->nr_events; i++) {
404 405 406
			u64 nr_samples;

			bpos->samples[i].percent = disasm__calc_percent(notes,
407 408 409
						evsel->idx + i,
						pos->offset,
						next ? next->offset : len,
410 411
						&path, &nr_samples);
			bpos->samples[i].nr = nr_samples;
412

413 414
			if (max_percent < bpos->samples[i].percent)
				max_percent = bpos->samples[i].percent;
415 416
		}

417
		if (max_percent < 0.01 && pos->ipc == 0) {
418
			RB_CLEAR_NODE(&bpos->rb_node);
419 420
			continue;
		}
421 422
		disasm_rb_tree__insert(&browser->entries, bpos,
				       browser->nr_events);
423 424 425 426 427 428
	}
	pthread_mutex_unlock(&notes->lock);

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

429 430
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
431
	struct disasm_line *dl;
432
	struct browser_disasm_line *bdl;
433 434 435
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
436
	dl = list_entry(browser->b.top, struct disasm_line, node);
437
	bdl = disasm_line__browser(dl);
438

439
	if (annotate_browser__opts.hide_src_code) {
440 441
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
442 443

		browser->b.nr_entries = browser->nr_entries;
444
		annotate_browser__opts.hide_src_code = false;
445
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
446 447
		browser->b.top_idx = bdl->idx - offset;
		browser->b.index = bdl->idx;
448
	} else {
449
		if (bdl->idx_asm < 0) {
450 451 452 453 454
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

455 456
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
457 458

		browser->b.nr_entries = browser->nr_asm_entries;
459
		annotate_browser__opts.hide_src_code = true;
460
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
461 462
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
463 464 465 466 467
	}

	return true;
}

468 469 470 471 472 473
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;
}

474 475 476 477 478 479 480 481
#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);
}

482 483
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
484
				    struct hist_browser_timer *hbt)
485 486
{
	struct map_symbol *ms = browser->b.priv;
487
	struct disasm_line *dl = browser->selection;
488
	struct annotation *notes;
489 490
	struct addr_map_symbol target = {
		.map = ms->map,
491
		.addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
492
	};
493
	char title[SYM_TITLE_MAX_SIZE];
494

495
	if (!ins__is_call(&dl->ins))
496 497
		return false;

498
	if (map_groups__find_ams(&target) ||
499 500 501
	    map__rip_2objdump(target.map, target.map->map_ip(target.map,
							     target.addr)) !=
	    dl->ops.target.addr) {
502 503 504 505
		ui_helpline__puts("The called function was not found.");
		return true;
	}

506
	notes = symbol__annotation(target.sym);
507 508
	pthread_mutex_lock(&notes->lock);

509
	if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {
510 511
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
512
			    target.sym->name);
513 514 515 516
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
517 518
	symbol__tui_annotate(target.sym, target.map, evsel, hbt);
	sym_title(ms->sym, ms->map, title, sizeof(title));
519
	ui_browser__show_title(&browser->b, title);
520 521 522
	return true;
}

523 524 525
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
526 527 528 529
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
530
	struct disasm_line *pos;
531 532 533 534 535

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
536
		if (!disasm_line__filter(&browser->b, &pos->node))
537 538 539 540 541 542 543 544
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
545
	struct disasm_line *dl = browser->selection;
546
	u64 offset;
547
	s64 idx;
548

549
	if (!ins__is_jump(&dl->ins))
550 551
		return false;

552 553
	offset = dl->ops.target.offset;
	dl = annotate_browser__find_offset(browser, offset, &idx);
554
	if (dl == NULL) {
555
		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
556 557 558
		return true;
	}

559
	annotate_browser__set_top(browser, dl, idx);
560

561 562 563
	return true;
}

564 565 566
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
567 568 569 570
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
571
	struct disasm_line *pos = browser->selection;
572 573 574

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
575
		if (disasm_line__filter(&browser->b, &pos->node))
576 577 578 579 580 581 582 583 584 585 586 587 588
			continue;

		++*idx;

		if (pos->line && strstr(pos->line, s) != NULL)
			return pos;
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
589
	struct disasm_line *dl;
590 591
	s64 idx;

592 593
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
594 595 596 597
		ui_helpline__puts("String not found!");
		return false;
	}

598
	annotate_browser__set_top(browser, dl, idx);
599 600 601 602
	browser->searching_backwards = false;
	return true;
}

603 604 605
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
606 607 608 609
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
610
	struct disasm_line *pos = browser->selection;
611 612 613

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
614
		if (disasm_line__filter(&browser->b, &pos->node))
615 616 617 618 619 620 621 622 623 624 625 626 627
			continue;

		--*idx;

		if (pos->line && strstr(pos->line, s) != NULL)
			return pos;
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
628
	struct disasm_line *dl;
629 630
	s64 idx;

631 632
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
633 634 635 636
		ui_helpline__puts("String not found!");
		return false;
	}

637
	annotate_browser__set_top(browser, dl, idx);
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
	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);
}

690 691 692 693 694 695 696 697 698 699 700 701 702
static void annotate_browser__update_addr_width(struct annotate_browser *browser)
{
	if (annotate_browser__opts.use_offset)
		browser->target_width = browser->min_addr_width;
	else
		browser->target_width = browser->max_addr_width;

	browser->addr_width = browser->target_width;

	if (annotate_browser__opts.show_nr_jumps)
		browser->addr_width += browser->jumps_width + 1;
}

703 704
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
705
				 struct hist_browser_timer *hbt)
706 707
{
	struct rb_node *nd = NULL;
708
	struct map_symbol *ms = browser->b.priv;
709
	struct symbol *sym = ms->sym;
710
	const char *help = "Press 'h' for help on key bindings";
711
	int delay_secs = hbt ? hbt->refresh : 0;
712
	int key;
713
	char title[SYM_TITLE_MAX_SIZE];
714

715 716
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
717
		return -1;
718

719
	annotate_browser__calc_percent(browser, evsel);
720

721 722 723
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
724
	}
725

726
	nd = browser->curr_hot;
727

728
	while (1) {
729
		key = ui_browser__run(&browser->b, delay_secs);
730

731
		if (delay_secs != 0) {
732
			annotate_browser__calc_percent(browser, evsel);
733 734 735 736 737 738 739 740 741
			/*
			 * 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;
		}

742
		switch (key) {
743
		case K_TIMER:
744 745
			if (hbt)
				hbt->timer(hbt->arg);
746 747

			if (delay_secs != 0)
748
				symbol__annotate_decay_histogram(sym, evsel->idx);
749
			continue;
750
		case K_TAB:
751 752 753
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
754
					nd = rb_last(&browser->entries);
755
			} else
756
				nd = browser->curr_hot;
757
			break;
758
		case K_UNTAB:
759
			if (nd != NULL) {
760 761
				nd = rb_next(nd);
				if (nd == NULL)
762
					nd = rb_first(&browser->entries);
763
			} else
764
				nd = browser->curr_hot;
765
			break;
766
		case K_F1:
767
		case 'h':
768
			ui_browser__help_window(&browser->b,
769 770 771
		"UP/DOWN/PGUP\n"
		"PGDN/SPACE    Navigate\n"
		"q/ESC/CTRL+C  Exit\n\n"
772 773
		"ENTER         Go to target\n"
		"ESC           Exit\n"
774
		"H             Cycle thru hottest instructions\n"
775 776 777 778 779
		"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"
780
		"t             Toggle total period view\n"
781
		"/             Search string\n"
782
		"k             Toggle line numbers\n"
783
		"r             Run available scripts\n"
784
		"?             Search string backwards\n");
785
			continue;
786 787 788 789 790
		case 'r':
			{
				script_browse(NULL);
				continue;
			}
791 792 793 794
		case 'k':
			annotate_browser__opts.show_linenr =
				!annotate_browser__opts.show_linenr;
			break;
795
		case 'H':
796
			nd = browser->curr_hot;
797
			break;
798
		case 's':
799
			if (annotate_browser__toggle_source(browser))
800 801
				ui_helpline__puts(help);
			continue;
802
		case 'o':
803
			annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
804
			annotate_browser__update_addr_width(browser);
805
			continue;
806
		case 'j':
807
			annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
808
			continue;
809
		case 'J':
810
			annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
811
			annotate_browser__update_addr_width(browser);
812
			continue;
813
		case '/':
814
			if (annotate_browser__search(browser, delay_secs)) {
815 816 817 818 819
show_help:
				ui_helpline__puts(help);
			}
			continue;
		case 'n':
820 821 822
			if (browser->searching_backwards ?
			    annotate_browser__continue_search_reverse(browser, delay_secs) :
			    annotate_browser__continue_search(browser, delay_secs))
823 824 825
				goto show_help;
			continue;
		case '?':
826
			if (annotate_browser__search_reverse(browser, delay_secs))
827 828
				goto show_help;
			continue;
829 830 831 832
		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",
833 834 835 836 837
					   seq++, browser->b.nr_entries,
					   browser->b.height,
					   browser->b.index,
					   browser->b.top_idx,
					   browser->nr_asm_entries);
838 839
		}
			continue;
840 841
		case K_ENTER:
		case K_RIGHT:
842
			if (browser->selection == NULL)
843
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
844
			else if (browser->selection->offset == -1)
845
				ui_helpline__puts("Actions are only available for assembly lines.");
846
			else if (!browser->selection->ins.ops)
847
				goto show_sup_ins;
848
			else if (ins__is_ret(&browser->selection->ins))
849
				goto out;
850
			else if (!(annotate_browser__jump(browser) ||
851
				     annotate_browser__callq(browser, evsel, hbt))) {
852
show_sup_ins:
853
				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
854
			}
855
			continue;
856 857 858 859 860
		case 't':
			annotate_browser__opts.show_total_period =
			  !annotate_browser__opts.show_total_period;
			annotate_browser__update_addr_width(browser);
			continue;
861 862
		case K_LEFT:
		case K_ESC:
863 864
		case 'q':
		case CTRL('c'):
865
			goto out;
866 867
		default:
			continue;
868
		}
869 870

		if (nd != NULL)
871
			annotate_browser__set_rb_top(browser, nd);
872 873
	}
out:
874
	ui_browser__hide(&browser->b);
875
	return key;
876 877
}

878 879 880
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
			     struct hist_browser_timer *hbt)
{
881 882 883 884
	/* Set default value for show_total_period.  */
	annotate_browser__opts.show_total_period =
	  symbol_conf.show_total_period;

885 886 887
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

888
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
889
			     struct hist_browser_timer *hbt)
890
{
891 892 893 894
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

895
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
896 897
}

898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966

static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end)
{
	unsigned n_insn = 0;
	u64 offset;

	for (offset = start; offset <= end; offset++) {
		if (browser->offsets[offset])
			n_insn++;
	}
	return n_insn;
}

static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end,
			   struct cyc_hist *ch)
{
	unsigned n_insn;
	u64 offset;

	n_insn = count_insn(browser, start, end);
	if (n_insn && ch->num && ch->cycles) {
		float ipc = n_insn / ((double)ch->cycles / (double)ch->num);

		/* Hide data when there are too many overlaps. */
		if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
			return;

		for (offset = start; offset <= end; offset++) {
			struct disasm_line *dl = browser->offsets[offset];

			if (dl)
				dl->ipc = ipc;
		}
	}
}

/*
 * This should probably be in util/annotate.c to share with the tty
 * annotate, but right now we need the per byte offsets arrays,
 * which are only here.
 */
static void annotate__compute_ipc(struct annotate_browser *browser, size_t size,
			   struct symbol *sym)
{
	u64 offset;
	struct annotation *notes = symbol__annotation(sym);

	if (!notes->src || !notes->src->cycles_hist)
		return;

	pthread_mutex_lock(&notes->lock);
	for (offset = 0; offset < size; ++offset) {
		struct cyc_hist *ch;

		ch = &notes->src->cycles_hist[offset];
		if (ch && ch->cycles) {
			struct disasm_line *dl;

			if (ch->have_start)
				count_and_fill(browser, ch->start, offset, ch);
			dl = browser->offsets[offset];
			if (dl && ch->num_aggr)
				dl->cycles = ch->cycles_aggr / ch->num_aggr;
			browser->have_cycles = true;
		}
	}
	pthread_mutex_unlock(&notes->lock);
}

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

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

	for (offset = 0; offset < size; ++offset) {
		struct disasm_line *dl = browser->offsets[offset], *dlt;
		struct browser_disasm_line *bdlt;

982
		if (!disasm_line__is_valid_jump(dl, sym))
983 984
			continue;

985
		dlt = browser->offsets[dl->ops.target.offset];
986 987 988 989 990 991 992
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
		if (dlt == NULL)
			continue;

993
		bdlt = disasm_line__browser(dlt);
994 995 996 997
		if (++bdlt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = bdlt->jump_sources;

		++browser->nr_jumps;
998 999 1000
	}
}

1001 1002 1003 1004 1005 1006 1007 1008 1009
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

1010 1011
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
1012
			 struct hist_browser_timer *hbt)
1013
{
1014
	struct disasm_line *pos, *n;
1015
	struct annotation *notes;
1016
	size_t size;
1017 1018 1019 1020
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
1021 1022
	struct annotate_browser browser = {
		.b = {
1023
			.refresh = annotate_browser__refresh,
1024 1025
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
1026
			.filter  = disasm_line__filter,
1027
			.priv	 = &ms,
1028
			.use_navkeypressed = true,
1029
		},
1030
	};
1031
	int ret = -1, err;
1032 1033
	int nr_pcnt = 1;
	size_t sizeof_bdl = sizeof(struct browser_disasm_line);
1034

1035
	if (sym == NULL)
1036 1037
		return -1;

1038 1039
	size = symbol__size(sym);

1040
	if (map->dso->annotate_warned)
1041 1042
		return -1;

1043 1044 1045 1046 1047 1048
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

1049 1050
	if (perf_evsel__is_group_event(evsel)) {
		nr_pcnt = evsel->nr_members;
1051 1052
		sizeof_bdl += sizeof(struct disasm_line_samples) *
		  (nr_pcnt - 1);
1053 1054
	}

1055
	err = symbol__disassemble(sym, map, perf_evsel__env_arch(evsel), sizeof_bdl);
1056 1057 1058 1059
	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);
1060
		goto out_free_offsets;
1061 1062
	}

1063
	ui_helpline__push("Press ESC to exit");
1064

1065
	notes = symbol__annotation(sym);
1066
	browser.start = map__rip_2objdump(map, sym->start);
1067

1068
	list_for_each_entry(pos, &notes->src->source, node) {
1069
		struct browser_disasm_line *bpos;
1070
		size_t line_len = strlen(pos->line);
1071

1072 1073
		if (browser.b.width < line_len)
			browser.b.width = line_len;
1074 1075
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
1076
		if (pos->offset != -1) {
1077
			bpos->idx_asm = browser.nr_asm_entries++;
1078 1079 1080 1081 1082 1083 1084 1085 1086
			/*
			 * 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
 			 */
			if (pos->offset < (s64)size)
				browser.offsets[pos->offset] = pos;
1087
		} else
1088
			bpos->idx_asm = -1;
1089 1090
	}

1091
	annotate_browser__mark_jump_targets(&browser, size);
1092
	annotate__compute_ipc(&browser, size, sym);
1093

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

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

	annotate_browser__update_addr_width(&browser);

1107
	ret = annotate_browser__run(&browser, evsel, hbt);
1108
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
1109
		list_del(&pos->node);
1110
		disasm_line__free(pos);
1111
	}
1112 1113 1114

out_free_offsets:
	free(browser.offsets);
1115 1116
	return ret;
}
1117 1118 1119

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

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

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1140
	const struct annotate_config *cfg = cfgp;
1141 1142 1143 1144

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

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

	if (prefixcmp(var, "annotate.") != 0)
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1156
		      sizeof(struct annotate_config), annotate_config__cmp);
1157 1158

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

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