annotate.c 29.9 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 <pthread.h>
12

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

18 19 20
#define IPC_WIDTH 6
#define CYCLES_WIDTH 6

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

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

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

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

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

	return false;
}

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

102 103 104 105 106 107 108 109 110
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;
}

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

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

	if (dl->offset != -1 && percent_max != 0.0) {
131 132 133 134 135
		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);
136 137 138 139 140 141 142
				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);
				}
143 144
			}
		} else {
145
			ui_browser__write_nstring(browser, " ", 7 * ab->nr_events);
146
		}
147
	} else {
148
		ui_browser__set_percent_color(browser, 0, current_entry);
149
		ui_browser__write_nstring(browser, " ", 7 * ab->nr_events);
150 151 152
	}
	if (ab->have_cycles) {
		if (dl->ipc)
153
			ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, dl->ipc);
154
		else
155
			ui_browser__write_nstring(browser, " ", IPC_WIDTH);
156
		if (dl->cycles)
157 158
			ui_browser__printf(browser, "%*" PRIu64 " ",
					   CYCLES_WIDTH - 1, dl->cycles);
159
		else
160
			ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
161 162
	}

163
	SLsmg_write_char(' ');
164 165

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

169
	if (!*dl->line)
170
		ui_browser__write_nstring(browser, " ", width - pcnt_width);
171
	else if (dl->offset == -1) {
172 173 174 175 176
		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  ",
177
				    ab->addr_width, " ");
178 179
		ui_browser__write_nstring(browser, bf, printed);
		ui_browser__write_nstring(browser, dl->line, width - printed - pcnt_width + 1);
180
	} else {
181
		u64 addr = dl->offset;
182
		int color = -1;
183

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

187
		if (!annotate_browser__opts.use_offset) {
188
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
189
		} else {
190
			if (bdl->jump_sources) {
191
				if (annotate_browser__opts.show_nr_jumps) {
192 193 194 195 196 197
					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);
198
					ui_browser__write_nstring(browser, bf, printed);
199
					ui_browser__set_color(browser, prev);
200 201
				}

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

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

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

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

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

245 246 247 248 249 250 251 252 253 254
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;
}

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

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

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

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

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

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

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

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

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

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

306 307 308 309 310 311
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++) {
312
		if (a->samples[i].percent == b->samples[i].percent)
313
			continue;
314
		return a->samples[i].percent < b->samples[i].percent;
315 316 317 318 319 320
	}
	return 0;
}

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

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

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

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

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

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

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

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

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

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

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

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	return true;
}

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

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

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

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

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

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

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

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

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

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

	return NULL;
}

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

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

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

558
	annotate_browser__set_top(browser, dl, idx);
559

560 561 562
	return true;
}

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

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

		++*idx;

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

	return NULL;
}

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

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

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

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

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

		--*idx;

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

	return NULL;
}

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

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

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

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

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

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

718
	annotate_browser__calc_percent(browser, evsel);
719

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

725
	nd = browser->curr_hot;
726

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

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

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

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

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

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

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

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

894
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
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 965

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

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

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

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

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

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

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

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

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

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

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

1037 1038
	size = symbol__size(sym);

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

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

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

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

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

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

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

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

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

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

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

	annotate_browser__update_addr_width(&browser);

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

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

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

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

#undef ANNOTATE_CFG

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

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

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

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

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

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

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