annotate.c 16.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 <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 disasm_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
struct disasm_line_rb_node {
29 30 31
	struct rb_node	rb_node;
	double		percent;
	u32		idx;
32
	int		idx_asm;
33 34
};

35
static inline struct disasm_line_rb_node *disasm_line__rb(struct disasm_line *dl)
36
{
37
	return (struct disasm_line_rb_node *)(dl + 1);
38 39
}

40
static bool disasm_line__filter(struct ui_browser *browser, void *entry)
41 42 43 44
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);

	if (ab->hide_src_code) {
45 46
		struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
		return dl->offset == -1;
47 48 49 50 51
	}

	return false;
}

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

62 63 64 65
	if (dl->offset != -1) {
		struct disasm_line_rb_node *dlrb = disasm_line__rb(dl);
		ui_browser__set_percent_color(self, dlrb->percent, current_entry);
		slsmg_printf(" %7.2f ", dlrb->percent);
66
	} else {
67
		ui_browser__set_percent_color(self, 0, current_entry);
68 69 70 71 72
		slsmg_write_nstring(" ", 9);
	}

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

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

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

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

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

		printed = scnprintf(bf, sizeof(bf), " %" PRIx64 ":", addr);
94 95 96 97 98
		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);
99
		slsmg_write_nstring(dl->line, width - 18 - printed);
100
	}
101

102
	if (current_entry)
103
		ab->selection = dl;
104 105
}

106
static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
107 108 109
{
	double percent = 0.0;

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

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

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

137 138 139
	return percent;
}

140
static void disasm_rb_tree__insert(struct rb_root *root, struct disasm_line_rb_node *dlrb)
141
{
142
	struct rb_node **p = &root->rb_node;
143
	struct rb_node *parent = NULL;
144
	struct disasm_line_rb_node *l;
145 146 147

	while (*p != NULL) {
		parent = *p;
148 149
		l = rb_entry(parent, struct disasm_line_rb_node, rb_node);
		if (dlrb->percent < l->percent)
150 151 152 153
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
154 155
	rb_link_node(&dlrb->rb_node, parent, p);
	rb_insert_color(&dlrb->rb_node, root);
156 157
}

158
static void annotate_browser__set_top(struct annotate_browser *self,
159
				      struct disasm_line *pos, u32 idx)
160 161 162 163 164
{
	unsigned back;

	ui_browser__refresh_dimensions(&self->b);
	back = self->b.height / 2;
165
	self->b.top_idx = self->b.index = idx;
166 167

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

170
		if (disasm_line__filter(&self->b, &pos->node))
171 172
			continue;

173 174 175 176 177
		--self->b.top_idx;
		--back;
	}

	self->b.top = pos;
178
	self->b.navkeypressed = true;
179 180 181 182 183
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
184 185
	struct disasm_line_rb_node *rbpos;
	struct disasm_line *pos;
186

187 188
	rbpos = rb_entry(nd, struct disasm_line_rb_node, rb_node);
	pos = ((struct disasm_line *)rbpos) - 1;
189 190
	annotate_browser__set_top(browser, pos, rbpos->idx);
	browser->curr_hot = nd;
191 192
}

193 194
static void annotate_browser__calc_percent(struct annotate_browser *browser,
					   int evidx)
195
{
196 197
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
198
	struct annotation *notes = symbol__annotation(sym);
199
	struct disasm_line *pos;
200 201 202 203 204 205

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
206 207
		struct disasm_line_rb_node *rbpos = disasm_line__rb(pos);
		rbpos->percent = disasm_line__calc_percent(pos, sym, evidx);
208 209 210 211
		if (rbpos->percent < 0.01) {
			RB_CLEAR_NODE(&rbpos->rb_node);
			continue;
		}
212
		disasm_rb_tree__insert(&browser->entries, rbpos);
213 214 215 216 217 218
	}
	pthread_mutex_unlock(&notes->lock);

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

219 220
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
221 222
	struct disasm_line *dl;
	struct disasm_line_rb_node *dlrb;
223 224 225
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
226 227
	dl = list_entry(browser->b.top, struct disasm_line, node);
	dlrb = disasm_line__rb(dl);
228 229

	if (browser->hide_src_code) {
230 231
		if (dlrb->idx_asm < offset)
			offset = dlrb->idx;
232 233 234 235

		browser->b.nr_entries = browser->nr_entries;
		browser->hide_src_code = false;
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
236 237
		browser->b.top_idx = dlrb->idx - offset;
		browser->b.index = dlrb->idx;
238
	} else {
239
		if (dlrb->idx_asm < 0) {
240 241 242 243 244
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

245 246
		if (dlrb->idx_asm < offset)
			offset = dlrb->idx_asm;
247 248 249 250

		browser->b.nr_entries = browser->nr_asm_entries;
		browser->hide_src_code = true;
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
251 252
		browser->b.top_idx = dlrb->idx_asm - offset;
		browser->b.index = dlrb->idx_asm;
253 254 255 256 257
	}

	return true;
}

258 259 260 261 262
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;
263
	struct disasm_line *dl = browser->selection;
264 265 266 267 268
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	u64 ip;

269
	if (!ins__is_call(dl->ins))
270 271
		return false;

272
	ip = ms->map->map_ip(ms->map, dl->target);
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
	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;
}

295 296 297
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
298 299 300 301
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
302
	struct disasm_line *pos;
303 304 305 306 307

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
308
		if (!disasm_line__filter(&browser->b, &pos->node))
309 310 311 312 313 314 315 316
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
317
	struct disasm_line *dl = browser->selection;
318
	s64 idx;
319

320
	if (!ins__is_jump(dl->ins))
321 322
		return false;

323
	dl = annotate_browser__find_offset(browser, dl->target, &idx);
324
	if (dl == NULL) {
325 326 327 328
		ui_helpline__puts("Invallid jump offset");
		return true;
	}

329
	annotate_browser__set_top(browser, dl, idx);
330 331 332 333
	
	return true;
}

334 335 336
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
337 338 339 340
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
341
	struct disasm_line *pos = browser->selection;
342 343 344

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
345
		if (disasm_line__filter(&browser->b, &pos->node))
346 347 348 349 350 351 352 353 354 355 356 357 358
			continue;

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
359
	struct disasm_line *dl;
360 361
	s64 idx;

362 363
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
364 365 366 367
		ui_helpline__puts("String not found!");
		return false;
	}

368
	annotate_browser__set_top(browser, dl, idx);
369 370 371 372
	browser->searching_backwards = false;
	return true;
}

373 374 375
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
376 377 378 379
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
380
	struct disasm_line *pos = browser->selection;
381 382 383

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
384
		if (disasm_line__filter(&browser->b, &pos->node))
385 386 387 388 389 390 391 392 393 394 395 396 397
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
398
	struct disasm_line *dl;
399 400
	s64 idx;

401 402
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
403 404 405 406
		ui_helpline__puts("String not found!");
		return false;
	}

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

460
static int annotate_browser__run(struct annotate_browser *self, int evidx,
461
				 void(*timer)(void *arg),
462
				 void *arg, int delay_secs)
463 464
{
	struct rb_node *nd = NULL;
465 466
	struct map_symbol *ms = self->b.priv;
	struct symbol *sym = ms->sym;
467 468
	const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
			   "H: Go to hottest line, ->/ENTER: Line action, "
469
			   "O: Toggle offset view, "
470
			   "S: Toggle source code view";
471
	int key;
472

473
	if (ui_browser__show(&self->b, sym->name, help) < 0)
474
		return -1;
475 476 477

	annotate_browser__calc_percent(self, evidx);

478
	if (self->curr_hot) {
479
		annotate_browser__set_rb_top(self, self->curr_hot);
480 481
		self->b.navkeypressed = false;
	}
482 483

	nd = self->curr_hot;
484

485
	while (1) {
486
		key = ui_browser__run(&self->b, delay_secs);
487

488
		if (delay_secs != 0) {
489 490 491 492 493 494 495 496 497 498
			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;
		}

499
		switch (key) {
500
		case K_TIMER:
501 502 503 504
			if (timer != NULL)
				timer(arg);

			if (delay_secs != 0)
505 506
				symbol__annotate_decay_histogram(sym, evidx);
			continue;
507
		case K_TAB:
508 509 510 511 512 513
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
					nd = rb_last(&self->entries);
			} else
				nd = self->curr_hot;
514
			break;
515
		case K_UNTAB:
516 517 518 519 520 521 522 523
			if (nd != NULL)
				nd = rb_next(nd);
				if (nd == NULL)
					nd = rb_first(&self->entries);
			else
				nd = self->curr_hot;
			break;
		case 'H':
524
		case 'h':
525
			nd = self->curr_hot;
526
			break;
527
		case 'S':
528
		case 's':
529 530 531
			if (annotate_browser__toggle_source(self))
				ui_helpline__puts(help);
			continue;
532 533 534 535
		case 'O':
		case 'o':
			self->use_offset = !self->use_offset;
			continue;
536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
		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;
552 553
		case K_ENTER:
		case K_RIGHT:
554
			if (self->selection == NULL)
555
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
556
			else if (self->selection->offset == -1)
557
				ui_helpline__puts("Actions are only available for assembly lines.");
558 559
			else if (!self->selection->ins ||
				 !(annotate_browser__jump(self) ||
560 561
				   annotate_browser__callq(self, evidx, timer, arg, delay_secs)))
				ui_helpline__puts("Actions are only available for the 'callq' and jump instructions.");
562
			continue;
563 564
		case K_LEFT:
		case K_ESC:
565 566
		case 'q':
		case CTRL('c'):
567
			goto out;
568 569
		default:
			continue;
570
		}
571 572

		if (nd != NULL)
573
			annotate_browser__set_rb_top(self, nd);
574 575
	}
out:
576
	ui_browser__hide(&self->b);
577
	return key;
578 579
}

580
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
581
			     void(*timer)(void *arg), void *arg, int delay_secs)
582
{
583
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
584
				    timer, arg, delay_secs);
585 586
}

587
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
588
			 void(*timer)(void *arg), void *arg,
589
			 int delay_secs)
590
{
591
	struct disasm_line *pos, *n;
592
	struct annotation *notes;
593 594 595 596
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
597 598 599 600 601
	struct annotate_browser browser = {
		.b = {
			.refresh = ui_browser__list_head_refresh,
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
602
			.filter  = disasm_line__filter,
603
			.priv	 = &ms,
604
			.use_navkeypressed = true,
605
		},
606 607 608
	};
	int ret;

609
	if (sym == NULL)
610 611
		return -1;

612
	if (map->dso->annotate_warned)
613 614
		return -1;

615
	if (symbol__annotate(sym, map, sizeof(struct disasm_line_rb_node)) < 0) {
616
		ui__error("%s", ui_helpline__last_msg);
617 618 619 620 621
		return -1;
	}

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

622
	notes = symbol__annotation(sym);
623
	browser.start = map__rip_2objdump(map, sym->start);
624

625
	list_for_each_entry(pos, &notes->src->source, node) {
626
		struct disasm_line_rb_node *rbpos;
627
		size_t line_len = strlen(pos->line);
628

629 630
		if (browser.b.width < line_len)
			browser.b.width = line_len;
631
		rbpos = disasm_line__rb(pos);
632 633 634 635 636
		rbpos->idx = browser.nr_entries++;
		if (pos->offset != -1)
			rbpos->idx_asm = browser.nr_asm_entries++;
		else
			rbpos->idx_asm = -1;
637 638
	}

639
	browser.b.nr_entries = browser.nr_entries;
640
	browser.b.entries = &notes->src->source,
641
	browser.b.width += 18; /* Percentage */
642
	ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
643
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
644
		list_del(&pos->node);
645
		disasm_line__free(pos);
646 647 648
	}
	return ret;
}