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
			} else {
227
				ui_browser__write_nstring(browser, " ", 2);
228
			}
229 230
		} else {
			if (strcmp(dl->name, "retq")) {
231
				ui_browser__write_nstring(browser, " ", 2);
232
			} else {
233
				ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
234 235 236
				SLsmg_write_char(' ');
			}
		}
237

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	return true;
}

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

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

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

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

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

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

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

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

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

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

	return NULL;
}

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

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

553
	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
554
	if (dl == NULL) {
I
Ingo Molnar 已提交
555
		ui_helpline__puts("Invalid jump 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 847
			else if (!browser->selection->ins) {
				if (strcmp(browser->selection->name, "retq"))
848 849
					goto show_sup_ins;
				goto out;
850
			} else if (!(annotate_browser__jump(browser) ||
851
				     annotate_browser__callq(browser, evsel, hbt))) {
852 853 854
show_sup_ins:
				ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
			}
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;
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
	}

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

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

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

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

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

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

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

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

	annotate_browser__update_addr_width(&browser);

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

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

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

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

#undef ANNOTATE_CFG

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

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

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

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

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

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

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