annotate.c 30.0 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
		if (dl->ins && dl->ins->ops->scnprintf) {
217
			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 227 228
			} else if (ins__is_ret(dl->ins)) {
				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 246 247 248 249 250 251 252 253
static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
{
	if (!dl || !dl->ins || !ins__is_jump(dl->ins)
	    || !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 499 500 501
	if (map_groups__find_ams(&target, NULL) ||
	    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
	s64 idx;
547

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

551
	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
552
	if (dl == NULL) {
I
Ingo Molnar 已提交
553
		ui_helpline__puts("Invalid jump offset");
554 555 556
		return true;
	}

557
	annotate_browser__set_top(browser, dl, idx);
558

559 560 561
	return true;
}

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

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

		++*idx;

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

	return NULL;
}

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

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

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

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

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

		--*idx;

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

	return NULL;
}

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

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

635
	annotate_browser__set_top(browser, dl, idx);
636 637 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
	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);
}

688 689 690 691 692 693 694 695 696 697 698 699 700
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;
}

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

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

717
	annotate_browser__calc_percent(browser, evsel);
718

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

724
	nd = browser->curr_hot;
725

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

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

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

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

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

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

883 884 885
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

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

893
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
894 895
}

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

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);
}

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

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

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

980
		if (!disasm_line__is_valid_jump(dl, sym))
981 982
			continue;

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

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

		++browser->nr_jumps;
996 997 998
	}
}

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

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

1033
	if (sym == NULL)
1034 1035
		return -1;

1036 1037
	size = symbol__size(sym);

1038
	if (map->dso->annotate_warned)
1039 1040
		return -1;

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

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

1053
	if (symbol__disassemble(sym, map, sizeof_bdl) < 0) {
1054
		ui__error("%s", ui_helpline__last_msg);
1055
		goto out_free_offsets;
1056 1057
	}

1058
	ui_helpline__push("Press ESC to exit");
1059

1060
	notes = symbol__annotation(sym);
1061
	browser.start = map__rip_2objdump(map, sym->start);
1062

1063
	list_for_each_entry(pos, &notes->src->source, node) {
1064
		struct browser_disasm_line *bpos;
1065
		size_t line_len = strlen(pos->line);
1066

1067 1068
		if (browser.b.width < line_len)
			browser.b.width = line_len;
1069 1070
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
1071
		if (pos->offset != -1) {
1072
			bpos->idx_asm = browser.nr_asm_entries++;
1073 1074 1075 1076 1077 1078 1079 1080 1081
			/*
			 * 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;
1082
		} else
1083
			bpos->idx_asm = -1;
1084 1085
	}

1086
	annotate_browser__mark_jump_targets(&browser, size);
1087
	annotate__compute_ipc(&browser, size, sym);
1088

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

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

	annotate_browser__update_addr_width(&browser);

1102
	ret = annotate_browser__run(&browser, evsel, hbt);
1103
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
1104
		list_del(&pos->node);
1105
		disasm_line__free(pos);
1106
	}
1107 1108 1109

out_free_offsets:
	free(browser.offsets);
1110 1111
	return ret;
}
1112 1113 1114

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

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

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1135
	const struct annotate_config *cfg = cfgp;
1136 1137 1138 1139

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

1140 1141
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1142
{
1143
	struct annotate_config *cfg;
1144 1145 1146 1147 1148 1149 1150
	const char *name;

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

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1151
		      sizeof(struct annotate_config), annotate_config__cmp);
1152 1153

	if (cfg == NULL)
1154 1155 1156
		ui__warning("%s variable unknown, ignoring...", var);
	else
		*cfg->value = perf_config_bool(name, value);
1157 1158 1159 1160 1161 1162 1163
	return 0;
}

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