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

35
static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
36
{
37
	return (struct browser_disasm_line *)(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
	if (dl->offset != -1) {
63 64 65
		struct browser_disasm_line *bdl = disasm_line__browser(dl);
		ui_browser__set_percent_color(self, bdl->percent, current_entry);
		slsmg_printf(" %7.2f ", bdl->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
	else {
86
		char bf[256], *line = dl->line;
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 100 101 102 103 104 105 106 107 108
		if (dl->ins && dl->ins->ops->scnprintf) {
			dl->ins->ops->scnprintf(dl->ins, bf, sizeof(bf),
						!ab->use_offset ? dl->operands : NULL,
						dl->target);
			line = bf;
			slsmg_write_nstring(" ", 7);
			printed += 7;
		}

		slsmg_write_nstring(line, width - 18 - printed);
109
	}
110

111
	if (current_entry)
112
		ab->selection = dl;
113 114
}

115
static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
116 117 118
{
	double percent = 0.0;

119
	if (dl->offset != -1) {
120
		int len = sym->end - sym->start;
121
		unsigned int hits = 0;
122
		struct annotation *notes = symbol__annotation(sym);
123
		struct source_line *src_line = notes->src->lines;
124
		struct sym_hist *h = annotation__histogram(notes, evidx);
125 126
		s64 offset = dl->offset;
		struct disasm_line *next;
127

128
		next = disasm__get_next_ip_line(&notes->src->source, dl);
129 130
		while (offset < (s64)len &&
		       (next == NULL || offset < next->offset)) {
131 132
			if (src_line) {
				percent += src_line[offset].percent;
133
			} else
134
				hits += h->addr[offset];
135 136 137

			++offset;
		}
138 139 140 141 142
		/*
 		 * If the percentage wasn't already calculated in
 		 * symbol__get_source_line, do it now:
 		 */
		if (src_line == NULL && h->sum)
143 144 145
			percent = 100.0 * hits / h->sum;
	}

146 147 148
	return percent;
}

149
static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
150
{
151
	struct rb_node **p = &root->rb_node;
152
	struct rb_node *parent = NULL;
153
	struct browser_disasm_line *l;
154 155 156

	while (*p != NULL) {
		parent = *p;
157 158
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
		if (bdl->percent < l->percent)
159 160 161 162
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
163 164
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
165 166
}

167
static void annotate_browser__set_top(struct annotate_browser *self,
168
				      struct disasm_line *pos, u32 idx)
169 170 171 172 173
{
	unsigned back;

	ui_browser__refresh_dimensions(&self->b);
	back = self->b.height / 2;
174
	self->b.top_idx = self->b.index = idx;
175 176

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

179
		if (disasm_line__filter(&self->b, &pos->node))
180 181
			continue;

182 183 184 185 186
		--self->b.top_idx;
		--back;
	}

	self->b.top = pos;
187
	self->b.navkeypressed = true;
188 189 190 191 192
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
193
	struct browser_disasm_line *bpos;
194
	struct disasm_line *pos;
195

196 197 198
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
	annotate_browser__set_top(browser, pos, bpos->idx);
199
	browser->curr_hot = nd;
200 201
}

202 203
static void annotate_browser__calc_percent(struct annotate_browser *browser,
					   int evidx)
204
{
205 206
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
207
	struct annotation *notes = symbol__annotation(sym);
208
	struct disasm_line *pos;
209 210 211 212 213 214

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
215 216 217 218
		struct browser_disasm_line *bpos = disasm_line__browser(pos);
		bpos->percent = disasm_line__calc_percent(pos, sym, evidx);
		if (bpos->percent < 0.01) {
			RB_CLEAR_NODE(&bpos->rb_node);
219 220
			continue;
		}
221
		disasm_rb_tree__insert(&browser->entries, bpos);
222 223 224 225 226 227
	}
	pthread_mutex_unlock(&notes->lock);

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

228 229
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
230
	struct disasm_line *dl;
231
	struct browser_disasm_line *bdl;
232 233 234
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
235
	dl = list_entry(browser->b.top, struct disasm_line, node);
236
	bdl = disasm_line__browser(dl);
237 238

	if (browser->hide_src_code) {
239 240
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
241 242 243 244

		browser->b.nr_entries = browser->nr_entries;
		browser->hide_src_code = false;
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
245 246
		browser->b.top_idx = bdl->idx - offset;
		browser->b.index = bdl->idx;
247
	} else {
248
		if (bdl->idx_asm < 0) {
249 250 251 252 253
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

254 255
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
256 257 258 259

		browser->b.nr_entries = browser->nr_asm_entries;
		browser->hide_src_code = true;
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
260 261
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
262 263 264 265 266
	}

	return true;
}

267 268 269 270 271
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;
272
	struct disasm_line *dl = browser->selection;
273 274 275 276 277
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	u64 ip;

278
	if (!ins__is_call(dl->ins))
279 280
		return false;

281
	ip = ms->map->map_ip(ms->map, dl->target);
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
	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;
}

304 305 306
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
307 308 309 310
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
311
	struct disasm_line *pos;
312 313 314 315 316

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
317
		if (!disasm_line__filter(&browser->b, &pos->node))
318 319 320 321 322 323 324 325
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
326
	struct disasm_line *dl = browser->selection;
327
	s64 idx;
328

329
	if (!ins__is_jump(dl->ins))
330 331
		return false;

332
	dl = annotate_browser__find_offset(browser, dl->target, &idx);
333
	if (dl == NULL) {
334 335 336 337
		ui_helpline__puts("Invallid jump offset");
		return true;
	}

338
	annotate_browser__set_top(browser, dl, idx);
339 340 341 342
	
	return true;
}

343 344 345
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
346 347 348 349
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
350
	struct disasm_line *pos = browser->selection;
351 352 353

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
354
		if (disasm_line__filter(&browser->b, &pos->node))
355 356 357 358 359 360 361 362 363 364 365 366 367
			continue;

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
368
	struct disasm_line *dl;
369 370
	s64 idx;

371 372
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
373 374 375 376
		ui_helpline__puts("String not found!");
		return false;
	}

377
	annotate_browser__set_top(browser, dl, idx);
378 379 380 381
	browser->searching_backwards = false;
	return true;
}

382 383 384
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
385 386 387 388
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
389
	struct disasm_line *pos = browser->selection;
390 391 392

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
393
		if (disasm_line__filter(&browser->b, &pos->node))
394 395 396 397 398 399 400 401 402 403 404 405 406
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
407
	struct disasm_line *dl;
408 409
	s64 idx;

410 411
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
412 413 414 415
		ui_helpline__puts("String not found!");
		return false;
	}

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

469
static int annotate_browser__run(struct annotate_browser *self, int evidx,
470
				 void(*timer)(void *arg),
471
				 void *arg, int delay_secs)
472 473
{
	struct rb_node *nd = NULL;
474 475
	struct map_symbol *ms = self->b.priv;
	struct symbol *sym = ms->sym;
476 477
	const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
			   "H: Go to hottest line, ->/ENTER: Line action, "
478
			   "O: Toggle offset view, "
479
			   "S: Toggle source code view";
480
	int key;
481

482
	if (ui_browser__show(&self->b, sym->name, help) < 0)
483
		return -1;
484 485 486

	annotate_browser__calc_percent(self, evidx);

487
	if (self->curr_hot) {
488
		annotate_browser__set_rb_top(self, self->curr_hot);
489 490
		self->b.navkeypressed = false;
	}
491 492

	nd = self->curr_hot;
493

494
	while (1) {
495
		key = ui_browser__run(&self->b, delay_secs);
496

497
		if (delay_secs != 0) {
498 499 500 501 502 503 504 505 506 507
			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;
		}

508
		switch (key) {
509
		case K_TIMER:
510 511 512 513
			if (timer != NULL)
				timer(arg);

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

		if (nd != NULL)
582
			annotate_browser__set_rb_top(self, nd);
583 584
	}
out:
585
	ui_browser__hide(&self->b);
586
	return key;
587 588
}

589
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
590
			     void(*timer)(void *arg), void *arg, int delay_secs)
591
{
592
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
593
				    timer, arg, delay_secs);
594 595
}

596
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
597
			 void(*timer)(void *arg), void *arg,
598
			 int delay_secs)
599
{
600
	struct disasm_line *pos, *n;
601
	struct annotation *notes;
602 603 604 605
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
606 607 608 609 610
	struct annotate_browser browser = {
		.b = {
			.refresh = ui_browser__list_head_refresh,
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
611
			.filter  = disasm_line__filter,
612
			.priv	 = &ms,
613
			.use_navkeypressed = true,
614
		},
615 616 617
	};
	int ret;

618
	if (sym == NULL)
619 620
		return -1;

621
	if (map->dso->annotate_warned)
622 623
		return -1;

624
	if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
625
		ui__error("%s", ui_helpline__last_msg);
626 627 628 629 630
		return -1;
	}

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

631
	notes = symbol__annotation(sym);
632
	browser.start = map__rip_2objdump(map, sym->start);
633

634
	list_for_each_entry(pos, &notes->src->source, node) {
635
		struct browser_disasm_line *bpos;
636
		size_t line_len = strlen(pos->line);
637

638 639
		if (browser.b.width < line_len)
			browser.b.width = line_len;
640 641
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
642
		if (pos->offset != -1)
643
			bpos->idx_asm = browser.nr_asm_entries++;
644
		else
645
			bpos->idx_asm = -1;
646 647
	}

648
	browser.b.nr_entries = browser.nr_entries;
649
	browser.b.entries = &notes->src->source,
650
	browser.b.width += 18; /* Percentage */
651
	ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
652
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
653
		list_del(&pos->node);
654
		disasm_line__free(pos);
655 656 657
	}
	return ret;
}