annotate.c 16.6 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 <pthread.h>
12
#include <newt.h>
13

14 15 16
struct annotate_browser {
	struct ui_browser b;
	struct rb_root	  entries;
17
	struct rb_node	  *curr_hot;
18
	struct objdump_line *selection;
19
	u64		    start;
20 21 22
	int		    nr_asm_entries;
	int		    nr_entries;
	bool		    hide_src_code;
23
	bool		    use_offset;
24 25
	bool		    searching_backwards;
	char		    search_bf[128];
26 27 28 29 30 31
};

struct objdump_line_rb_node {
	struct rb_node	rb_node;
	double		percent;
	u32		idx;
32
	int		idx_asm;
33 34 35 36 37 38 39 40
};

static inline
struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
{
	return (struct objdump_line_rb_node *)(self + 1);
}

41 42 43 44 45 46 47 48 49 50 51 52
static bool objdump_line__filter(struct ui_browser *browser, void *entry)
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);

	if (ab->hide_src_code) {
		struct objdump_line *ol = list_entry(entry, struct objdump_line, node);
		return ol->offset == -1;
	}

	return false;
}

53 54
static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
{
55
	struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
56
	struct objdump_line *ol = list_entry(entry, struct objdump_line, node);
57
	bool current_entry = ui_browser__is_current_entry(self, row);
58 59 60
	bool change_color = (!ab->hide_src_code &&
			     (!current_entry || (self->use_navkeypressed &&
					         !self->navkeypressed)));
61 62 63
	int width = self->width;

	if (ol->offset != -1) {
64
		struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
65
		ui_browser__set_percent_color(self, olrb->percent, current_entry);
66 67
		slsmg_printf(" %7.2f ", olrb->percent);
	} else {
68
		ui_browser__set_percent_color(self, 0, current_entry);
69 70 71 72 73
		slsmg_write_nstring(" ", 9);
	}

	SLsmg_write_char(':');
	slsmg_write_nstring(" ", 8);
74 75 76 77 78

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

79 80
	if (ol->offset != -1 && change_color)
		ui_browser__set_color(self, HE_COLORSET_CODE);
81

82 83
	if (!*ol->line)
		slsmg_write_nstring(" ", width - 18);
84
	else if (ol->offset == -1)
85
		slsmg_write_nstring(ol->line, width - 18);
86 87
	else {
		char bf[64];
88 89
		u64 addr = ol->offset;
		int printed, color = -1;
90

91 92 93 94
		if (!ab->use_offset)
			addr += ab->start;

		printed = scnprintf(bf, sizeof(bf), " %" PRIx64 ":", addr);
95 96 97 98 99 100 101
		if (change_color)
			color = ui_browser__set_color(self, HE_COLORSET_ADDR);
		slsmg_write_nstring(bf, printed);
		if (change_color)
			ui_browser__set_color(self, color);
		slsmg_write_nstring(ol->line, width - 18 - printed);
	}
102

103
	if (current_entry)
104
		ab->selection = ol;
105 106 107
}

static double objdump_line__calc_percent(struct objdump_line *self,
108
					 struct symbol *sym, int evidx)
109 110 111 112 113
{
	double percent = 0.0;

	if (self->offset != -1) {
		int len = sym->end - sym->start;
114
		unsigned int hits = 0;
115
		struct annotation *notes = symbol__annotation(sym);
116
		struct source_line *src_line = notes->src->lines;
117
		struct sym_hist *h = annotation__histogram(notes, evidx);
118
		s64 offset = self->offset;
119
		struct objdump_line *next;
120

121
		next = objdump__get_next_ip_line(&notes->src->source, self);
122 123
		while (offset < (s64)len &&
		       (next == NULL || offset < next->offset)) {
124 125
			if (src_line) {
				percent += src_line[offset].percent;
126
			} else
127
				hits += h->addr[offset];
128 129 130

			++offset;
		}
131 132 133 134 135
		/*
 		 * If the percentage wasn't already calculated in
 		 * symbol__get_source_line, do it now:
 		 */
		if (src_line == NULL && h->sum)
136 137 138
			percent = 100.0 * hits / h->sum;
	}

139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
	return percent;
}

static void objdump__insert_line(struct rb_root *self,
				 struct objdump_line_rb_node *line)
{
	struct rb_node **p = &self->rb_node;
	struct rb_node *parent = NULL;
	struct objdump_line_rb_node *l;

	while (*p != NULL) {
		parent = *p;
		l = rb_entry(parent, struct objdump_line_rb_node, rb_node);
		if (line->percent < l->percent)
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
	rb_link_node(&line->rb_node, parent, p);
	rb_insert_color(&line->rb_node, self);
159 160
}

161
static void annotate_browser__set_top(struct annotate_browser *self,
162
				      struct objdump_line *pos, u32 idx)
163 164 165 166 167
{
	unsigned back;

	ui_browser__refresh_dimensions(&self->b);
	back = self->b.height / 2;
168
	self->b.top_idx = self->b.index = idx;
169 170 171 172

	while (self->b.top_idx != 0 && back != 0) {
		pos = list_entry(pos->node.prev, struct objdump_line, node);

173 174 175
		if (objdump_line__filter(&self->b, &pos->node))
			continue;

176 177 178 179 180
		--self->b.top_idx;
		--back;
	}

	self->b.top = pos;
181
	self->b.navkeypressed = true;
182 183 184 185 186 187 188 189 190 191 192 193
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
	struct objdump_line_rb_node *rbpos;
	struct objdump_line *pos;

	rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
	pos = ((struct objdump_line *)rbpos) - 1;
	annotate_browser__set_top(browser, pos, rbpos->idx);
	browser->curr_hot = nd;
194 195
}

196 197
static void annotate_browser__calc_percent(struct annotate_browser *browser,
					   int evidx)
198
{
199 200
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
	struct annotation *notes = symbol__annotation(sym);
	struct objdump_line *pos;

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
		struct objdump_line_rb_node *rbpos = objdump_line__rb(pos);
		rbpos->percent = objdump_line__calc_percent(pos, sym, evidx);
		if (rbpos->percent < 0.01) {
			RB_CLEAR_NODE(&rbpos->rb_node);
			continue;
		}
		objdump__insert_line(&browser->entries, rbpos);
	}
	pthread_mutex_unlock(&notes->lock);

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

222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
	struct objdump_line *ol;
	struct objdump_line_rb_node *olrb;
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
	ol = list_entry(browser->b.top, struct objdump_line, node);
	olrb = objdump_line__rb(ol);

	if (browser->hide_src_code) {
		if (olrb->idx_asm < offset)
			offset = olrb->idx;

		browser->b.nr_entries = browser->nr_entries;
		browser->hide_src_code = false;
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
		browser->b.top_idx = olrb->idx - offset;
		browser->b.index = olrb->idx;
	} else {
		if (olrb->idx_asm < 0) {
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

		if (olrb->idx_asm < offset)
			offset = olrb->idx_asm;

		browser->b.nr_entries = browser->nr_asm_entries;
		browser->hide_src_code = true;
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
		browser->b.top_idx = olrb->idx_asm - offset;
		browser->b.index = olrb->idx_asm;
	}

	return true;
}

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
static bool annotate_browser__callq(struct annotate_browser *browser,
				    int evidx, void (*timer)(void *arg),
				    void *arg, int delay_secs)
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	char *s = strstr(browser->selection->line, "callq ");
	u64 ip;

	if (s == NULL)
		return false;

	s = strchr(s, ' ');
	if (s++ == NULL) {
		ui_helpline__puts("Invallid callq instruction.");
		return true;
	}

	ip = strtoull(s, NULL, 16);
	ip = ms->map->map_ip(ms->map, ip);
	target = map__find_symbol(ms->map, ip, NULL);
	if (target == NULL) {
		ui_helpline__puts("The called function was not found.");
		return true;
	}

	notes = symbol__annotation(target);
	pthread_mutex_lock(&notes->lock);

	if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
			    target->name);
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
	symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs);
	ui_browser__show_title(&browser->b, sym->name);
	return true;
}

305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
static struct objdump_line *
	annotate_browser__find_offset(struct annotate_browser *browser,
				      s64 offset, s64 *idx)
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
	struct objdump_line *pos;

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
		if (!objdump_line__filter(&browser->b, &pos->node))
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
	const char *jumps[] = { "je ", "jne ", "ja ", "jmpq ", "js ", "jmp ", NULL };
	struct objdump_line *line;
	s64 idx, offset;
	char *s = NULL;
	int i = 0;

	while (jumps[i]) {
		s = strstr(browser->selection->line, jumps[i++]);
		if (s)
			break;
	}

	if (s == NULL)
		return false;

	s = strchr(s, '+');
	if (s++ == NULL) {
		ui_helpline__puts("Invallid jump instruction.");
		return true;
	}

	offset = strtoll(s, NULL, 16);
	line = annotate_browser__find_offset(browser, offset, &idx);
	if (line == NULL) {
		ui_helpline__puts("Invallid jump offset");
		return true;
	}

	annotate_browser__set_top(browser, line, idx);
	
	return true;
}

360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
static struct objdump_line *
	annotate_browser__find_string(struct annotate_browser *browser,
				      char *s, s64 *idx)
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
	struct objdump_line *pos = browser->selection;

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
		if (objdump_line__filter(&browser->b, &pos->node))
			continue;

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
	struct objdump_line *line;
	s64 idx;

	line = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (line == NULL) {
		ui_helpline__puts("String not found!");
		return false;
	}

	annotate_browser__set_top(browser, line, idx);
	browser->searching_backwards = false;
	return true;
}

static struct objdump_line *
	annotate_browser__find_string_reverse(struct annotate_browser *browser,
					      char *s, s64 *idx)
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
	struct objdump_line *pos = browser->selection;

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
		if (objdump_line__filter(&browser->b, &pos->node))
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
	struct objdump_line *line;
	s64 idx;

	line = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (line == NULL) {
		ui_helpline__puts("String not found!");
		return false;
	}

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

486
static int annotate_browser__run(struct annotate_browser *self, int evidx,
487
				 void(*timer)(void *arg),
488
				 void *arg, int delay_secs)
489 490
{
	struct rb_node *nd = NULL;
491 492
	struct map_symbol *ms = self->b.priv;
	struct symbol *sym = ms->sym;
493 494
	const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
			   "H: Go to hottest line, ->/ENTER: Line action, "
495
			   "O: Toggle offset view, "
496
			   "S: Toggle source code view";
497
	int key;
498

499
	if (ui_browser__show(&self->b, sym->name, help) < 0)
500
		return -1;
501 502 503

	annotate_browser__calc_percent(self, evidx);

504
	if (self->curr_hot) {
505
		annotate_browser__set_rb_top(self, self->curr_hot);
506 507
		self->b.navkeypressed = false;
	}
508 509

	nd = self->curr_hot;
510

511
	while (1) {
512
		key = ui_browser__run(&self->b, delay_secs);
513

514
		if (delay_secs != 0) {
515 516 517 518 519 520 521 522 523 524
			annotate_browser__calc_percent(self, evidx);
			/*
			 * 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;
		}

525
		switch (key) {
526
		case K_TIMER:
527 528 529 530
			if (timer != NULL)
				timer(arg);

			if (delay_secs != 0)
531 532
				symbol__annotate_decay_histogram(sym, evidx);
			continue;
533
		case K_TAB:
534 535 536 537 538 539
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
					nd = rb_last(&self->entries);
			} else
				nd = self->curr_hot;
540
			break;
541
		case K_UNTAB:
542 543 544 545 546 547 548 549
			if (nd != NULL)
				nd = rb_next(nd);
				if (nd == NULL)
					nd = rb_first(&self->entries);
			else
				nd = self->curr_hot;
			break;
		case 'H':
550
		case 'h':
551
			nd = self->curr_hot;
552
			break;
553
		case 'S':
554
		case 's':
555 556 557
			if (annotate_browser__toggle_source(self))
				ui_helpline__puts(help);
			continue;
558 559 560 561
		case 'O':
		case 'o':
			self->use_offset = !self->use_offset;
			continue;
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
		case '/':
			if (annotate_browser__search(self, delay_secs)) {
show_help:
				ui_helpline__puts(help);
			}
			continue;
		case 'n':
			if (self->searching_backwards ?
			    annotate_browser__continue_search_reverse(self, delay_secs) :
			    annotate_browser__continue_search(self, delay_secs))
				goto show_help;
			continue;
		case '?':
			if (annotate_browser__search_reverse(self, delay_secs))
				goto show_help;
			continue;
578 579
		case K_ENTER:
		case K_RIGHT:
580
			if (self->selection == NULL)
581
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
582
			else if (self->selection->offset == -1)
583
				ui_helpline__puts("Actions are only available for assembly lines.");
584 585 586
			else if (!(annotate_browser__jump(self) ||
				   annotate_browser__callq(self, evidx, timer, arg, delay_secs)))
				ui_helpline__puts("Actions are only available for the 'callq' and jump instructions.");
587
			continue;
588 589
		case K_LEFT:
		case K_ESC:
590 591
		case 'q':
		case CTRL('c'):
592
			goto out;
593 594
		default:
			continue;
595
		}
596 597

		if (nd != NULL)
598
			annotate_browser__set_rb_top(self, nd);
599 600
	}
out:
601
	ui_browser__hide(&self->b);
602
	return key;
603 604
}

605
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
606
			     void(*timer)(void *arg), void *arg, int delay_secs)
607
{
608
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
609
				    timer, arg, delay_secs);
610 611
}

612
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
613
			 void(*timer)(void *arg), void *arg,
614
			 int delay_secs)
615 616
{
	struct objdump_line *pos, *n;
617
	struct annotation *notes;
618 619 620 621
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
622 623 624 625 626
	struct annotate_browser browser = {
		.b = {
			.refresh = ui_browser__list_head_refresh,
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
627
			.filter  = objdump_line__filter,
628
			.priv	 = &ms,
629
			.use_navkeypressed = true,
630
		},
631 632 633
	};
	int ret;

634
	if (sym == NULL)
635 636
		return -1;

637
	if (map->dso->annotate_warned)
638 639
		return -1;

640
	if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) {
641
		ui__error("%s", ui_helpline__last_msg);
642 643 644 645 646
		return -1;
	}

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

647
	notes = symbol__annotation(sym);
648
	browser.start = map__rip_2objdump(map, sym->start);
649

650
	list_for_each_entry(pos, &notes->src->source, node) {
651
		struct objdump_line_rb_node *rbpos;
652
		size_t line_len = strlen(pos->line);
653

654 655 656
		if (browser.b.width < line_len)
			browser.b.width = line_len;
		rbpos = objdump_line__rb(pos);
657 658 659 660 661
		rbpos->idx = browser.nr_entries++;
		if (pos->offset != -1)
			rbpos->idx_asm = browser.nr_asm_entries++;
		else
			rbpos->idx_asm = -1;
662 663
	}

664
	browser.b.nr_entries = browser.nr_entries;
665
	browser.b.entries = &notes->src->source,
666
	browser.b.width += 18; /* Percentage */
667
	ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
668
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
669 670 671 672 673
		list_del(&pos->node);
		objdump_line__free(pos);
	}
	return ret;
}