annotate.c 10.7 KB
Newer Older
1 2 3
#include "../browser.h"
#include "../helpline.h"
#include "../libslang.h"
4
#include "../../annotate.h"
5 6 7
#include "../../hist.h"
#include "../../sort.h"
#include "../../symbol.h"
8
#include <pthread.h>
9 10 11 12 13 14 15 16 17 18

static void ui__error_window(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
	va_end(ap);
}

19 20 21
struct annotate_browser {
	struct ui_browser b;
	struct rb_root	  entries;
22
	struct rb_node	  *curr_hot;
23
	struct objdump_line *selection;
24 25 26
	int		    nr_asm_entries;
	int		    nr_entries;
	bool		    hide_src_code;
27 28 29 30 31 32
};

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

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

42 43 44 45 46 47 48 49 50 51 52 53
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;
}

54 55
static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
{
56
	struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
57
	struct objdump_line *ol = list_entry(entry, struct objdump_line, node);
58 59 60 61
	bool current_entry = ui_browser__is_current_entry(self, row);
	int width = self->width;

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

	SLsmg_write_char(':');
	slsmg_write_nstring(" ", 8);
	if (!*ol->line)
		slsmg_write_nstring(" ", width - 18);
	else
		slsmg_write_nstring(ol->line, width - 18);
76 77 78

	if (!current_entry)
		ui_browser__set_color(self, HE_COLORSET_CODE);
79 80
	else
		ab->selection = ol;
81 82 83
}

static double objdump_line__calc_percent(struct objdump_line *self,
84
					 struct symbol *sym, int evidx)
85 86 87 88 89
{
	double percent = 0.0;

	if (self->offset != -1) {
		int len = sym->end - sym->start;
90
		unsigned int hits = 0;
91
		struct annotation *notes = symbol__annotation(sym);
92
		struct source_line *src_line = notes->src->lines;
93
		struct sym_hist *h = annotation__histogram(notes, evidx);
94
		s64 offset = self->offset;
95
		struct objdump_line *next;
96

97
		next = objdump__get_next_ip_line(&notes->src->source, self);
98 99
		while (offset < (s64)len &&
		       (next == NULL || offset < next->offset)) {
100 101
			if (src_line) {
				percent += src_line[offset].percent;
102
			} else
103
				hits += h->addr[offset];
104 105 106

			++offset;
		}
107 108 109 110 111
		/*
 		 * If the percentage wasn't already calculated in
 		 * symbol__get_source_line, do it now:
 		 */
		if (src_line == NULL && h->sum)
112 113 114
			percent = 100.0 * hits / h->sum;
	}

115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
	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);
135 136
}

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
static void annotate_browser__set_top(struct annotate_browser *self,
				      struct rb_node *nd)
{
	struct objdump_line_rb_node *rbpos;
	struct objdump_line *pos;
	unsigned back;

	ui_browser__refresh_dimensions(&self->b);
	back = self->b.height / 2;
	rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
	pos = ((struct objdump_line *)rbpos) - 1;
	self->b.top_idx = self->b.index = rbpos->idx;

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

		--self->b.top_idx;
		--back;
	}

	self->b.top = pos;
	self->curr_hot = nd;
}

161 162
static void annotate_browser__calc_percent(struct annotate_browser *browser,
					   int evidx)
163
{
164 165
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
	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);
}

187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
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;
}

226
static int annotate_browser__run(struct annotate_browser *self, int evidx,
227 228
				 int nr_events, void(*timer)(void *arg),
				 void *arg, int delay_secs)
229 230
{
	struct rb_node *nd = NULL;
231 232
	struct map_symbol *ms = self->b.priv;
	struct symbol *sym = ms->sym;
233 234 235
	const char *help = "<-, ESC: exit, TAB/shift+TAB: cycle hottest lines, "
			   "H: Hottest, -> Line action, S -> Toggle source "
			   "code view";
236
	int key;
237

238
	if (ui_browser__show(&self->b, sym->name, help) < 0)
239
		return -1;
240 241 242 243 244

	annotate_browser__calc_percent(self, evidx);

	if (self->curr_hot)
		annotate_browser__set_top(self, self->curr_hot);
245 246

	nd = self->curr_hot;
247

248
	while (1) {
249
		key = ui_browser__run(&self->b, delay_secs);
250

251
		if (delay_secs != 0) {
252 253 254 255 256 257 258 259 260 261
			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;
		}

262
		switch (key) {
263 264 265 266 267
		case -1:
			/*
 			 * FIXME we need to check if it was
 			 * es.reason == NEWT_EXIT_TIMER
 			 */
268 269 270 271
			if (timer != NULL)
				timer(arg);

			if (delay_secs != 0)
272 273
				symbol__annotate_decay_histogram(sym, evidx);
			continue;
274
		case NEWT_KEY_TAB:
275 276 277 278 279 280
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
					nd = rb_last(&self->entries);
			} else
				nd = self->curr_hot;
281 282
			break;
		case NEWT_KEY_UNTAB:
283 284 285 286 287 288 289 290 291
			if (nd != NULL)
				nd = rb_next(nd);
				if (nd == NULL)
					nd = rb_first(&self->entries);
			else
				nd = self->curr_hot;
			break;
		case 'H':
			nd = self->curr_hot;
292
			break;
293 294 295 296
		case 'S':
			if (annotate_browser__toggle_source(self))
				ui_helpline__puts(help);
			continue;
297
		case NEWT_KEY_ENTER:
298 299 300 301 302 303 304 305 306 307
		case NEWT_KEY_RIGHT:
			if (self->selection == NULL) {
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
				continue;
			}

			if (self->selection->offset == -1) {
				ui_helpline__puts("Actions are only available for assembly lines.");
				continue;
			} else {
308 309 310 311 312
				char *s = strstr(self->selection->line, "callq ");
				struct annotation *notes;
				struct symbol *target;
				u64 ip;

313 314
				if (s == NULL) {
					ui_helpline__puts("Actions are only available for the 'callq' instruction.");
315
					continue;
316
				}
317 318

				s = strchr(s, ' ');
319 320
				if (s++ == NULL) {
					ui_helpline__puts("Invallid callq instruction.");
321
					continue;
322
				}
323 324 325 326

				ip = strtoull(s, NULL, 16);
				ip = ms->map->map_ip(ms->map, ip);
				target = map__find_symbol(ms->map, ip, NULL);
327 328
				if (target == NULL) {
					ui_helpline__puts("The called function was not found.");
329
					continue;
330
				}
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347

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

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

				pthread_mutex_unlock(&notes->lock);
				symbol__tui_annotate(target, ms->map, evidx, nr_events,
						     timer, arg, delay_secs);
			}
			break;
348 349
		case NEWT_KEY_LEFT:
		case NEWT_KEY_ESCAPE:
350 351
		case 'q':
		case CTRL('c'):
352
			goto out;
353 354
		default:
			continue;
355
		}
356 357 358

		if (nd != NULL)
			annotate_browser__set_top(self, nd);
359 360
	}
out:
361
	ui_browser__hide(&self->b);
362
	return key;
363 364
}

365
int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events,
366
			     void(*timer)(void *arg), void *arg, int delay_secs)
367
{
368
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, nr_events,
369
				    timer, arg, delay_secs);
370 371
}

372
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
373 374
			 int nr_events, void(*timer)(void *arg), void *arg,
			 int delay_secs)
375 376
{
	struct objdump_line *pos, *n;
377
	struct annotation *notes;
378 379 380 381
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
382 383 384 385 386
	struct annotate_browser browser = {
		.b = {
			.refresh = ui_browser__list_head_refresh,
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
387
			.filter  = objdump_line__filter,
388
			.priv	 = &ms,
389
		},
390 391 392
	};
	int ret;

393
	if (sym == NULL)
394 395
		return -1;

396
	if (map->dso->annotate_warned)
397 398
		return -1;

399
	if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) {
400
		ui__error_window(ui_helpline__last_msg);
401 402 403 404 405
		return -1;
	}

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

406 407
	notes = symbol__annotation(sym);

408
	list_for_each_entry(pos, &notes->src->source, node) {
409
		struct objdump_line_rb_node *rbpos;
410
		size_t line_len = strlen(pos->line);
411

412 413 414
		if (browser.b.width < line_len)
			browser.b.width = line_len;
		rbpos = objdump_line__rb(pos);
415 416 417 418 419
		rbpos->idx = browser.nr_entries++;
		if (pos->offset != -1)
			rbpos->idx_asm = browser.nr_asm_entries++;
		else
			rbpos->idx_asm = -1;
420 421
	}

422
	browser.b.nr_entries = browser.nr_entries;
423
	browser.b.entries = &notes->src->source,
424
	browser.b.width += 18; /* Percentage */
425 426
	ret = annotate_browser__run(&browser, evidx, nr_events,
				    timer, arg, delay_secs);
427
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
428 429 430 431 432
		list_del(&pos->node);
		objdump_line__free(pos);
	}
	return ret;
}