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

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

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

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

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

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

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

	return false;
}

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

100
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
101
{
102
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
103
	struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
104
	struct browser_disasm_line *bdl = disasm_line__browser(dl);
105
	bool current_entry = ui_browser__is_current_entry(browser, row);
106
	bool change_color = (!annotate_browser__opts.hide_src_code &&
107 108 109
			     (!current_entry || (browser->use_navkeypressed &&
					         !browser->navkeypressed)));
	int width = browser->width, printed;
110 111
	int i, pcnt_width = 7 * ab->nr_events;
	double percent_max = 0.0;
112
	char bf[256];
113

114
	for (i = 0; i < ab->nr_events; i++) {
115 116
		if (bdl->samples[i].percent > percent_max)
			percent_max = bdl->samples[i].percent;
117 118 119 120
	}

	if (dl->offset != -1 && percent_max != 0.0) {
		for (i = 0; i < ab->nr_events; i++) {
121 122
			ui_browser__set_percent_color(browser,
						      bdl->samples[i].percent,
123
						      current_entry);
124 125 126 127 128
			if (annotate_browser__opts.show_total_period)
				slsmg_printf("%6" PRIu64 " ",
					     bdl->samples[i].nr);
			else
				slsmg_printf("%6.2f ", bdl->samples[i].percent);
129
		}
130
	} else {
131
		ui_browser__set_percent_color(browser, 0, current_entry);
132
		slsmg_write_nstring(" ", pcnt_width);
133 134
	}

135
	SLsmg_write_char(' ');
136 137

	/* The scroll bar isn't being used */
138
	if (!browser->navkeypressed)
139 140
		width += 1;

141
	if (!*dl->line)
142
		slsmg_write_nstring(" ", width - pcnt_width);
143
	else if (dl->offset == -1) {
144 145 146 147 148
		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  ",
149 150
				    ab->addr_width, " ");
		slsmg_write_nstring(bf, printed);
151
		slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1);
152
	} else {
153
		u64 addr = dl->offset;
154
		int color = -1;
155

156
		if (!annotate_browser__opts.use_offset)
157 158
			addr += ab->start;

159
		if (!annotate_browser__opts.use_offset) {
160
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
161
		} else {
162
			if (bdl->jump_sources) {
163
				if (annotate_browser__opts.show_nr_jumps) {
164 165 166 167 168 169 170
					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);
					slsmg_write_nstring(bf, printed);
171
					ui_browser__set_color(browser, prev);
172 173
				}

174
				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
175
						    ab->target_width, addr);
176
			} else {
177 178
				printed = scnprintf(bf, sizeof(bf), "%*s  ",
						    ab->addr_width, " ");
179 180
			}
		}
181

182
		if (change_color)
183
			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
184 185
		slsmg_write_nstring(bf, printed);
		if (change_color)
186
			ui_browser__set_color(browser, color);
187
		if (dl->ins && dl->ins->ops->scnprintf) {
188
			if (ins__is_jump(dl->ins)) {
189
				bool fwd = dl->ops.target.offset > (u64)dl->offset;
190

191
				ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
192
								    SLSMG_UARROW_CHAR);
193
				SLsmg_write_char(' ');
194
			} else if (ins__is_call(dl->ins)) {
195
				ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
196
				SLsmg_write_char(' ');
197 198 199
			} else {
				slsmg_write_nstring(" ", 2);
			}
200 201 202 203
		} else {
			if (strcmp(dl->name, "retq")) {
				slsmg_write_nstring(" ", 2);
			} else {
204
				ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
205 206 207
				SLsmg_write_char(' ');
			}
		}
208

209
		disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
210
		slsmg_write_nstring(bf, width - pcnt_width - 3 - printed);
211
	}
212

213
	if (current_entry)
214
		ab->selection = dl;
215 216
}

217 218 219 220 221 222 223 224 225 226
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;
}

227
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
228 229
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
230 231
	struct disasm_line *cursor = ab->selection, *target;
	struct browser_disasm_line *btarget, *bcursor;
232
	unsigned int from, to;
233 234
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
235
	u8 pcnt_width = 7;
236 237 238 239

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

241
	if (!disasm_line__is_valid_jump(cursor, sym))
242
		return;
243

244 245 246
	target = ab->offsets[cursor->ops.target.offset];
	if (!target)
		return;
247

248 249
	bcursor = disasm_line__browser(cursor);
	btarget = disasm_line__browser(target);
250

251
	if (annotate_browser__opts.hide_src_code) {
252
		from = bcursor->idx_asm;
253 254
		to = btarget->idx_asm;
	} else {
255
		from = (u64)bcursor->idx;
256 257 258
		to = (u64)btarget->idx;
	}

259 260
	pcnt_width *= ab->nr_events;

261
	ui_browser__set_color(browser, HE_COLORSET_CODE);
262 263
	__ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
				 from, to);
264 265 266 267
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
268
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
269
	int ret = ui_browser__list_head_refresh(browser);
270 271 272
	int pcnt_width;

	pcnt_width = 7 * ab->nr_events;
273

274
	if (annotate_browser__opts.jump_arrows)
275
		annotate_browser__draw_current_jump(browser);
276

277
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
278
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
279 280 281
	return ret;
}

282 283 284 285 286 287
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++) {
288
		if (a->samples[i].percent == b->samples[i].percent)
289
			continue;
290
		return a->samples[i].percent < b->samples[i].percent;
291 292 293 294 295 296
	}
	return 0;
}

static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl,
				   int nr_events)
297
{
298
	struct rb_node **p = &root->rb_node;
299
	struct rb_node *parent = NULL;
300
	struct browser_disasm_line *l;
301 302 303

	while (*p != NULL) {
		parent = *p;
304
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
305 306

		if (disasm__cmp(bdl, l, nr_events))
307 308 309 310
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
311 312
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
313 314
}

315
static void annotate_browser__set_top(struct annotate_browser *browser,
316
				      struct disasm_line *pos, u32 idx)
317 318 319
{
	unsigned back;

320 321 322
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
323

324
	while (browser->b.top_idx != 0 && back != 0) {
325
		pos = list_entry(pos->node.prev, struct disasm_line, node);
326

327
		if (disasm_line__filter(&browser->b, &pos->node))
328 329
			continue;

330
		--browser->b.top_idx;
331 332 333
		--back;
	}

334 335
	browser->b.top = pos;
	browser->b.navkeypressed = true;
336 337 338 339 340
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
341
	struct browser_disasm_line *bpos;
342
	struct disasm_line *pos;
343
	u32 idx;
344

345 346
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
347
	idx = bpos->idx;
348
	if (annotate_browser__opts.hide_src_code)
349 350
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
351
	browser->curr_hot = nd;
352 353
}

354
static void annotate_browser__calc_percent(struct annotate_browser *browser,
355
					   struct perf_evsel *evsel)
356
{
357 358
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
359
	struct annotation *notes = symbol__annotation(sym);
360 361
	struct disasm_line *pos, *next;
	s64 len = symbol__size(sym);
362 363 364 365 366 367

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
368
		struct browser_disasm_line *bpos = disasm_line__browser(pos);
369
		const char *path = NULL;
370 371
		double max_percent = 0.0;
		int i;
372 373 374 375 376 377 378 379

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

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

380
		for (i = 0; i < browser->nr_events; i++) {
381 382 383
			u64 nr_samples;

			bpos->samples[i].percent = disasm__calc_percent(notes,
384 385 386
						evsel->idx + i,
						pos->offset,
						next ? next->offset : len,
387 388
						&path, &nr_samples);
			bpos->samples[i].nr = nr_samples;
389

390 391
			if (max_percent < bpos->samples[i].percent)
				max_percent = bpos->samples[i].percent;
392 393
		}

394
		if (max_percent < 0.01 && pos->ipc == 0) {
395
			RB_CLEAR_NODE(&bpos->rb_node);
396 397
			continue;
		}
398 399
		disasm_rb_tree__insert(&browser->entries, bpos,
				       browser->nr_events);
400 401 402 403 404 405
	}
	pthread_mutex_unlock(&notes->lock);

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

406 407
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
408
	struct disasm_line *dl;
409
	struct browser_disasm_line *bdl;
410 411 412
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
413
	dl = list_entry(browser->b.top, struct disasm_line, node);
414
	bdl = disasm_line__browser(dl);
415

416
	if (annotate_browser__opts.hide_src_code) {
417 418
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
419 420

		browser->b.nr_entries = browser->nr_entries;
421
		annotate_browser__opts.hide_src_code = false;
422
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
423 424
		browser->b.top_idx = bdl->idx - offset;
		browser->b.index = bdl->idx;
425
	} else {
426
		if (bdl->idx_asm < 0) {
427 428 429 430 431
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

432 433
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
434 435

		browser->b.nr_entries = browser->nr_asm_entries;
436
		annotate_browser__opts.hide_src_code = true;
437
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
438 439
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
440 441 442 443 444
	}

	return true;
}

445 446 447 448 449 450
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;
}

451 452 453 454 455 456 457 458
#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);
}

459 460
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
461
				    struct hist_browser_timer *hbt)
462 463
{
	struct map_symbol *ms = browser->b.priv;
464
	struct disasm_line *dl = browser->selection;
465
	struct annotation *notes;
466 467
	struct addr_map_symbol target = {
		.map = ms->map,
468
		.addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
469
	};
470
	char title[SYM_TITLE_MAX_SIZE];
471

472
	if (!ins__is_call(dl->ins))
473 474
		return false;

475 476 477 478
	if (map_groups__find_ams(&target, NULL) ||
	    map__rip_2objdump(target.map, target.map->map_ip(target.map,
							     target.addr)) !=
	    dl->ops.target.addr) {
479 480 481 482
		ui_helpline__puts("The called function was not found.");
		return true;
	}

483
	notes = symbol__annotation(target.sym);
484 485
	pthread_mutex_lock(&notes->lock);

486
	if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {
487 488
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
489
			    target.sym->name);
490 491 492 493
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
494 495
	symbol__tui_annotate(target.sym, target.map, evsel, hbt);
	sym_title(ms->sym, ms->map, title, sizeof(title));
496
	ui_browser__show_title(&browser->b, title);
497 498 499
	return true;
}

500 501 502
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
503 504 505 506
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
507
	struct disasm_line *pos;
508 509 510 511 512

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
513
		if (!disasm_line__filter(&browser->b, &pos->node))
514 515 516 517 518 519 520 521
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
522
	struct disasm_line *dl = browser->selection;
523
	s64 idx;
524

525
	if (!ins__is_jump(dl->ins))
526 527
		return false;

528
	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
529
	if (dl == NULL) {
I
Ingo Molnar 已提交
530
		ui_helpline__puts("Invalid jump offset");
531 532 533
		return true;
	}

534
	annotate_browser__set_top(browser, dl, idx);
535

536 537 538
	return true;
}

539 540 541
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
542 543 544 545
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
546
	struct disasm_line *pos = browser->selection;
547 548 549

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
550
		if (disasm_line__filter(&browser->b, &pos->node))
551 552 553 554 555 556 557 558 559 560 561 562 563
			continue;

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
564
	struct disasm_line *dl;
565 566
	s64 idx;

567 568
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
569 570 571 572
		ui_helpline__puts("String not found!");
		return false;
	}

573
	annotate_browser__set_top(browser, dl, idx);
574 575 576 577
	browser->searching_backwards = false;
	return true;
}

578 579 580
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
581 582 583 584
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
585
	struct disasm_line *pos = browser->selection;
586 587 588

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
589
		if (disasm_line__filter(&browser->b, &pos->node))
590 591 592 593 594 595 596 597 598 599 600 601 602
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
603
	struct disasm_line *dl;
604 605
	s64 idx;

606 607
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
608 609 610 611
		ui_helpline__puts("String not found!");
		return false;
	}

612
	annotate_browser__set_top(browser, dl, idx);
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 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
	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);
}

665 666 667 668 669 670 671 672 673 674 675 676 677
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;
}

678 679
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
680
				 struct hist_browser_timer *hbt)
681 682
{
	struct rb_node *nd = NULL;
683
	struct map_symbol *ms = browser->b.priv;
684
	struct symbol *sym = ms->sym;
685
	const char *help = "Press 'h' for help on key bindings";
686
	int delay_secs = hbt ? hbt->refresh : 0;
687
	int key;
688
	char title[SYM_TITLE_MAX_SIZE];
689

690 691
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
692
		return -1;
693

694
	annotate_browser__calc_percent(browser, evsel);
695

696 697 698
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
699
	}
700

701
	nd = browser->curr_hot;
702

703
	while (1) {
704
		key = ui_browser__run(&browser->b, delay_secs);
705

706
		if (delay_secs != 0) {
707
			annotate_browser__calc_percent(browser, evsel);
708 709 710 711 712 713 714 715 716
			/*
			 * 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;
		}

717
		switch (key) {
718
		case K_TIMER:
719 720
			if (hbt)
				hbt->timer(hbt->arg);
721 722

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

		if (nd != NULL)
846
			annotate_browser__set_rb_top(browser, nd);
847 848
	}
out:
849
	ui_browser__hide(&browser->b);
850
	return key;
851 852
}

853 854 855
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
			     struct hist_browser_timer *hbt)
{
856 857 858 859
	/* Set default value for show_total_period.  */
	annotate_browser__opts.show_total_period =
	  symbol_conf.show_total_period;

860 861 862
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

863
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
864
			     struct hist_browser_timer *hbt)
865
{
866 867 868 869
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

870
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
871 872
}

873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 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

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

942 943 944 945
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;
946 947 948 949 950 951
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;

	/* PLT symbols contain external offsets */
	if (strstr(sym->name, "@plt"))
		return;
952 953 954 955 956

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

957
		if (!disasm_line__is_valid_jump(dl, sym))
958 959
			continue;

960
		dlt = browser->offsets[dl->ops.target.offset];
961 962 963 964 965 966 967
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
		if (dlt == NULL)
			continue;

968
		bdlt = disasm_line__browser(dlt);
969 970 971 972
		if (++bdlt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = bdlt->jump_sources;

		++browser->nr_jumps;
973 974 975
	}
}

976 977 978 979 980 981 982 983 984
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

985 986
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
987
			 struct hist_browser_timer *hbt)
988
{
989
	struct disasm_line *pos, *n;
990
	struct annotation *notes;
991
	size_t size;
992 993 994 995
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
996 997
	struct annotate_browser browser = {
		.b = {
998
			.refresh = annotate_browser__refresh,
999 1000
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
1001
			.filter  = disasm_line__filter,
1002
			.priv	 = &ms,
1003
			.use_navkeypressed = true,
1004
		},
1005
	};
1006
	int ret = -1;
1007 1008
	int nr_pcnt = 1;
	size_t sizeof_bdl = sizeof(struct browser_disasm_line);
1009

1010
	if (sym == NULL)
1011 1012
		return -1;

1013 1014
	size = symbol__size(sym);

1015
	if (map->dso->annotate_warned)
1016 1017
		return -1;

1018 1019 1020 1021 1022 1023
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

1024 1025
	if (perf_evsel__is_group_event(evsel)) {
		nr_pcnt = evsel->nr_members;
1026 1027
		sizeof_bdl += sizeof(struct disasm_line_samples) *
		  (nr_pcnt - 1);
1028 1029 1030
	}

	if (symbol__annotate(sym, map, sizeof_bdl) < 0) {
1031
		ui__error("%s", ui_helpline__last_msg);
1032
		goto out_free_offsets;
1033 1034 1035 1036
	}

	ui_helpline__push("Press <- or ESC to exit");

1037
	notes = symbol__annotation(sym);
1038
	browser.start = map__rip_2objdump(map, sym->start);
1039

1040
	list_for_each_entry(pos, &notes->src->source, node) {
1041
		struct browser_disasm_line *bpos;
1042
		size_t line_len = strlen(pos->line);
1043

1044 1045
		if (browser.b.width < line_len)
			browser.b.width = line_len;
1046 1047
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
1048
		if (pos->offset != -1) {
1049
			bpos->idx_asm = browser.nr_asm_entries++;
1050 1051 1052 1053 1054 1055 1056 1057 1058
			/*
			 * 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;
1059
		} else
1060
			bpos->idx_asm = -1;
1061 1062
	}

1063
	annotate_browser__mark_jump_targets(&browser, size);
1064
	annotate__compute_ipc(&browser, size, sym);
1065

1066
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1067
	browser.max_addr_width = hex_width(sym->end);
1068
	browser.jumps_width = width_jumps(browser.max_jump_sources);
1069
	browser.nr_events = nr_pcnt;
1070
	browser.b.nr_entries = browser.nr_entries;
1071
	browser.b.entries = &notes->src->source,
1072
	browser.b.width += 18; /* Percentage */
1073 1074 1075 1076 1077 1078

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

	annotate_browser__update_addr_width(&browser);

1079
	ret = annotate_browser__run(&browser, evsel, hbt);
1080
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
1081
		list_del(&pos->node);
1082
		disasm_line__free(pos);
1083
	}
1084 1085 1086

out_free_offsets:
	free(browser.offsets);
1087 1088
	return ret;
}
1089 1090 1091

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

1093 1094 1095
/*
 * Keep the entries sorted, they are bsearch'ed
 */
1096
static struct annotate_config {
1097 1098 1099 1100 1101
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
1102
	ANNOTATE_CFG(show_linenr),
1103 1104
	ANNOTATE_CFG(show_nr_jumps),
	ANNOTATE_CFG(use_offset),
1105
	ANNOTATE_CFG(show_total_period),
1106 1107 1108 1109 1110 1111
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1112
	const struct annotate_config *cfg = cfgp;
1113 1114 1115 1116

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

1117 1118
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1119
{
1120
	struct annotate_config *cfg;
1121 1122 1123 1124 1125 1126 1127
	const char *name;

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

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1128
		      sizeof(struct annotate_config), annotate_config__cmp);
1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140

	if (cfg == NULL)
		return -1;

	*cfg->value = perf_config_bool(name, value);
	return 0;
}

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