Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0c5a0f96 authored by Ingo Molnar's avatar Ingo Molnar
Browse files

Merge tag 'perf-core-for-mingo' of...

Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux

 into perf/core

Arjan & Linus Annotation Edition

 - Fix indirect calls beautifier, reported by Linus.

 - Use the objdump comments to nuke specificities about how access to a well
   know variable is encoded, suggested by Linus.

 - Show the number of places that jump to a target, requested by Arjan.

Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 5dcefda0 54e7a4e8
Loading
Loading
Loading
Loading
+80 −14
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@ struct browser_disasm_line {
	double		percent;
	u32		idx;
	int		idx_asm;
	bool		jump_target;
	int		jump_sources;
};

struct annotate_browser {
@@ -28,11 +28,16 @@ struct annotate_browser {
	u64		    start;
	int		    nr_asm_entries;
	int		    nr_entries;
	int		    max_jump_sources;
	int		    nr_jumps;
	bool		    hide_src_code;
	bool		    use_offset;
	bool		    jump_arrows;
	bool		    show_nr_jumps;
	bool		    searching_backwards;
	u8		    addr_width;
	u8		    jumps_width;
	u8		    target_width;
	u8		    min_addr_width;
	u8		    max_addr_width;
	char		    search_bf[128];
@@ -55,6 +60,25 @@ static bool disasm_line__filter(struct ui_browser *browser, void *entry)
	return false;
}

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

static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
{
	struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
@@ -98,9 +122,20 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro
		if (!ab->use_offset) {
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
		} else {
			if (bdl->jump_target) {
			if (bdl->jump_sources) {
				if (ab->show_nr_jumps) {
					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);
					ui_browser__set_color(self, prev);
				}

				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
						    ab->addr_width, addr);
						    ab->target_width, addr);
			} else {
				printed = scnprintf(bf, sizeof(bf), "%*s  ",
						    ab->addr_width, " ");
@@ -546,10 +581,7 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
	struct rb_node *nd = NULL;
	struct map_symbol *ms = self->b.priv;
	struct symbol *sym = ms->sym;
	const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
			   "H: Hottest line, ->/ENTER: Line action, "
			   "O: Offset view, "
			   "S: Source view";
	const char *help = "Press 'h' for help on key bindings";
	int key;

	if (ui_browser__show(&self->b, sym->name, help) < 0)
@@ -602,26 +634,47 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
			else
				nd = self->curr_hot;
			break;
		case 'H':
		case K_F1:
		case 'h':
			ui_browser__help_window(&self->b,
		"UP/DOWN/PGUP\n"
		"PGDN/SPACE    Navigate\n"
		"q/ESC/CTRL+C  Exit\n\n"
		"->            Go to target\n"
		"<-            Exit\n"
		"h             Cycle thru hottest instructions\n"
		"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"
		"/             Search string\n"
		"?             Search previous string\n");
			continue;
		case 'H':
			nd = self->curr_hot;
			break;
		case 'S':
		case 's':
			if (annotate_browser__toggle_source(self))
				ui_helpline__puts(help);
			continue;
		case 'O':
		case 'o':
			self->use_offset = !self->use_offset;
			if (self->use_offset)
				self->addr_width = self->min_addr_width;
				self->target_width = self->min_addr_width;
			else
				self->addr_width = self->max_addr_width;
				self->target_width = self->max_addr_width;
update_addr_width:
			self->addr_width = self->target_width;
			if (self->show_nr_jumps)
				self->addr_width += self->jumps_width + 1;
			continue;
		case 'j':
			self->jump_arrows = !self->jump_arrows;
			continue;
		case 'J':
			self->show_nr_jumps = !self->show_nr_jumps;
			goto update_addr_width;
		case '/':
			if (annotate_browser__search(self, delay_secs)) {
show_help:
@@ -707,9 +760,21 @@ static void annotate_browser__mark_jump_targets(struct annotate_browser *browser
			continue;

		bdlt = disasm_line__browser(dlt);
		bdlt->jump_target = true;
		if (++bdlt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = bdlt->jump_sources;

		++browser->nr_jumps;
	}
		
}

static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
@@ -784,8 +849,9 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,

	annotate_browser__mark_jump_targets(&browser, size);

	browser.addr_width = browser.min_addr_width = hex_width(size);
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
	browser.max_addr_width = hex_width(sym->end);
	browser.jumps_width = width_jumps(browser.max_jump_sources);
	browser.b.nr_entries = browser.nr_entries;
	browser.b.entries = &notes->src->source,
	browser.b.width += 18; /* Percentage */
+275 −28
Original line number Diff line number Diff line
@@ -18,6 +18,17 @@

const char 	*disassembler_style;

static struct ins *ins__find(const char *name);
static int disasm_line__parse(char *line, char **namep, char **rawp);

static void ins__delete(struct ins_operands *ops)
{
	free(ops->source.raw);
	free(ops->source.name);
	free(ops->target.raw);
	free(ops->target.name);
}

static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size,
			      struct ins_operands *ops)
{
@@ -56,6 +67,12 @@ static int call__parse(struct ins_operands *ops)
	return ops->target.name == NULL ? -1 : 0;

indirect_call:
	tok = strchr(endptr, '(');
	if (tok != NULL) {
		ops->target.addr = 0;
		return 0;
	}

	tok = strchr(endptr, '*');
	if (tok == NULL)
		return -1;
@@ -70,6 +87,9 @@ static int call__scnprintf(struct ins *ins, char *bf, size_t size,
	if (ops->target.name)
		return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->target.name);

	if (ops->target.addr == 0)
		return ins__raw_scnprintf(ins, bf, size, ops);

	return scnprintf(bf, size, "%-6.6s *%" PRIx64, ins->name, ops->target.addr);
}

@@ -113,6 +133,185 @@ bool ins__is_jump(const struct ins *ins)
	return ins->ops == &jump_ops;
}

static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep)
{
	char *endptr, *name, *t;

	if (strstr(raw, "(%rip)") == NULL)
		return 0;

	*addrp = strtoull(comment, &endptr, 16);
	name = strchr(endptr, '<');
	if (name == NULL)
		return -1;

	name++;

	t = strchr(name, '>');
	if (t == NULL)
		return 0;

	*t = '\0';
	*namep = strdup(name);
	*t = '>';

	return 0;
}

static int lock__parse(struct ins_operands *ops)
{
	char *name;

	ops->locked.ops = zalloc(sizeof(*ops->locked.ops));
	if (ops->locked.ops == NULL)
		return 0;

	if (disasm_line__parse(ops->raw, &name, &ops->locked.ops->raw) < 0)
		goto out_free_ops;

        ops->locked.ins = ins__find(name);
        if (ops->locked.ins == NULL)
                goto out_free_ops;

        if (!ops->locked.ins->ops)
                return 0;

        if (ops->locked.ins->ops->parse)
                ops->locked.ins->ops->parse(ops->locked.ops);

	return 0;

out_free_ops:
	free(ops->locked.ops);
	ops->locked.ops = NULL;
	return 0;
}

static int lock__scnprintf(struct ins *ins, char *bf, size_t size,
			   struct ins_operands *ops)
{
	int printed;

	if (ops->locked.ins == NULL)
		return ins__raw_scnprintf(ins, bf, size, ops);

	printed = scnprintf(bf, size, "%-6.6s ", ins->name);
	return printed + ins__scnprintf(ops->locked.ins, bf + printed,
					size - printed, ops->locked.ops);
}

static void lock__delete(struct ins_operands *ops)
{
	free(ops->locked.ops);
	free(ops->target.raw);
	free(ops->target.name);
}

static struct ins_ops lock_ops = {
	.free	   = lock__delete,
	.parse	   = lock__parse,
	.scnprintf = lock__scnprintf,
};

static int mov__parse(struct ins_operands *ops)
{
	char *s = strchr(ops->raw, ','), *target, *comment, prev;

	if (s == NULL)
		return -1;

	*s = '\0';
	ops->source.raw = strdup(ops->raw);
	*s = ',';
	
	if (ops->source.raw == NULL)
		return -1;

	target = ++s;

	while (s[0] != '\0' && !isspace(s[0]))
		++s;
	prev = *s;
	*s = '\0';

	ops->target.raw = strdup(target);
	*s = prev;

	if (ops->target.raw == NULL)
		goto out_free_source;

	comment = strchr(s, '#');
	if (comment == NULL)
		return 0;

	while (comment[0] != '\0' && isspace(comment[0]))
		++comment;

	comment__symbol(ops->source.raw, comment, &ops->source.addr, &ops->source.name);
	comment__symbol(ops->target.raw, comment, &ops->target.addr, &ops->target.name);

	return 0;

out_free_source:
	free(ops->source.raw);
	ops->source.raw = NULL;
	return -1;
}

static int mov__scnprintf(struct ins *ins, char *bf, size_t size,
			   struct ins_operands *ops)
{
	return scnprintf(bf, size, "%-6.6s %s,%s", ins->name,
			 ops->source.name ?: ops->source.raw,
			 ops->target.name ?: ops->target.raw);
}

static struct ins_ops mov_ops = {
	.parse	   = mov__parse,
	.scnprintf = mov__scnprintf,
};

static int dec__parse(struct ins_operands *ops)
{
	char *target, *comment, *s, prev;

	target = s = ops->raw;

	while (s[0] != '\0' && !isspace(s[0]))
		++s;
	prev = *s;
	*s = '\0';

	ops->target.raw = strdup(target);
	*s = prev;

	if (ops->target.raw == NULL)
		return -1;

	comment = strchr(s, '#');
	if (comment == NULL)
		return 0;

	while (comment[0] != '\0' && isspace(comment[0]))
		++comment;

	comment__symbol(ops->target.raw, comment, &ops->target.addr, &ops->target.name);

	return 0;
}

static int dec__scnprintf(struct ins *ins, char *bf, size_t size,
			   struct ins_operands *ops)
{
	return scnprintf(bf, size, "%-6.6s %s", ins->name,
			 ops->target.name ?: ops->target.raw);
}

static struct ins_ops dec_ops = {
	.parse	   = dec__parse,
	.scnprintf = dec__scnprintf,
};

static int nop__scnprintf(struct ins *ins __used, char *bf, size_t size,
			  struct ins_operands *ops __used)
{
@@ -127,8 +326,25 @@ static struct ins_ops nop_ops = {
 * Must be sorted by name!
 */
static struct ins instructions[] = {
	{ .name = "add",   .ops  = &mov_ops, },
	{ .name = "addl",  .ops  = &mov_ops, },
	{ .name = "addq",  .ops  = &mov_ops, },
	{ .name = "addw",  .ops  = &mov_ops, },
	{ .name = "and",   .ops  = &mov_ops, },
	{ .name = "bts",   .ops  = &mov_ops, },
	{ .name = "call",  .ops  = &call_ops, },
	{ .name = "callq", .ops  = &call_ops, },
	{ .name = "cmp",   .ops  = &mov_ops, },
	{ .name = "cmpb",  .ops  = &mov_ops, },
	{ .name = "cmpl",  .ops  = &mov_ops, },
	{ .name = "cmpq",  .ops  = &mov_ops, },
	{ .name = "cmpw",  .ops  = &mov_ops, },
	{ .name = "cmpxch", .ops  = &mov_ops, },
	{ .name = "dec",   .ops  = &dec_ops, },
	{ .name = "decl",  .ops  = &dec_ops, },
	{ .name = "imul",  .ops  = &mov_ops, },
	{ .name = "inc",   .ops  = &dec_ops, },
	{ .name = "incl",  .ops  = &dec_ops, },
	{ .name = "ja",	   .ops  = &jump_ops, },
	{ .name = "jae",   .ops  = &jump_ops, },
	{ .name = "jb",	   .ops  = &jump_ops, },
@@ -164,9 +380,25 @@ static struct ins instructions[] = {
	{ .name = "jrcxz", .ops  = &jump_ops, },
	{ .name = "js",	   .ops  = &jump_ops, },
	{ .name = "jz",	   .ops  = &jump_ops, },
	{ .name = "lea",   .ops  = &mov_ops, },
	{ .name = "lock",  .ops  = &lock_ops, },
	{ .name = "mov",   .ops  = &mov_ops, },
	{ .name = "movb",  .ops  = &mov_ops, },
	{ .name = "movdqa",.ops  = &mov_ops, },
	{ .name = "movl",  .ops  = &mov_ops, },
	{ .name = "movq",  .ops  = &mov_ops, },
	{ .name = "movslq", .ops  = &mov_ops, },
	{ .name = "movzbl", .ops  = &mov_ops, },
	{ .name = "movzwl", .ops  = &mov_ops, },
	{ .name = "nop",   .ops  = &nop_ops, },
	{ .name = "nopl",  .ops  = &nop_ops, },
	{ .name = "nopw",  .ops  = &nop_ops, },
	{ .name = "or",    .ops  = &mov_ops, },
	{ .name = "orl",   .ops  = &mov_ops, },
	{ .name = "test",  .ops  = &mov_ops, },
	{ .name = "testb", .ops  = &mov_ops, },
	{ .name = "testl", .ops  = &mov_ops, },
	{ .name = "xadd",  .ops  = &mov_ops, },
};

static int ins__cmp(const void *name, const void *insp)
@@ -257,46 +489,58 @@ static void disasm_line__init_ins(struct disasm_line *dl)
		dl->ins->ops->parse(&dl->ops);
}

static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privsize)
static int disasm_line__parse(char *line, char **namep, char **rawp)
{
	struct disasm_line *dl = zalloc(sizeof(*dl) + privsize);

	if (dl != NULL) {
		dl->offset = offset;
		dl->line = strdup(line);
		if (dl->line == NULL)
			goto out_delete;

		if (offset != -1) {
			char *name = dl->line, tmp;
	char *name = line, tmp;

	while (isspace(name[0]))
		++name;

	if (name[0] == '\0')
				goto out_delete;
		return -1;

			dl->ops.raw = name + 1;
	*rawp = name + 1;

			while (dl->ops.raw[0] != '\0' &&
			       !isspace(dl->ops.raw[0]))
				++dl->ops.raw;
	while ((*rawp)[0] != '\0' && !isspace((*rawp)[0]))
		++*rawp;

			tmp = dl->ops.raw[0];
			dl->ops.raw[0] = '\0';
			dl->name = strdup(name);
	tmp = (*rawp)[0];
	(*rawp)[0] = '\0';
	*namep = strdup(name);

			if (dl->name == NULL)
				goto out_free_line;
	if (*namep == NULL)
		goto out_free_name;

			dl->ops.raw[0] = tmp;
	(*rawp)[0] = tmp;

			if (dl->ops.raw[0] != '\0') {
				dl->ops.raw++;
				while (isspace(dl->ops.raw[0]))
					++dl->ops.raw;
	if ((*rawp)[0] != '\0') {
		(*rawp)++;
		while (isspace((*rawp)[0]))
			++(*rawp);
	}

	return 0;

out_free_name:
	free(*namep);
	*namep = NULL;
	return -1;
}

static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privsize)
{
	struct disasm_line *dl = zalloc(sizeof(*dl) + privsize);

	if (dl != NULL) {
		dl->offset = offset;
		dl->line = strdup(line);
		if (dl->line == NULL)
			goto out_delete;

		if (offset != -1) {
			if (disasm_line__parse(dl->line, &dl->name, &dl->ops.raw) < 0)
				goto out_free_line;

			disasm_line__init_ins(dl);
		}
	}
@@ -314,7 +558,10 @@ void disasm_line__free(struct disasm_line *dl)
{
	free(dl->line);
	free(dl->name);
	free(dl->ops.target.name);
	if (dl->ins && dl->ins->ops->free)
		dl->ins->ops->free(&dl->ops);
	else
		ins__delete(&dl->ops);
	free(dl);
}

+14 −1
Original line number Diff line number Diff line
@@ -13,13 +13,26 @@ struct ins;
struct ins_operands {
	char	*raw;
	struct {
		char	*raw;
		char	*name;
		u64	offset;
		u64	addr;
		u64	offset;
	} target;
	union {
		struct {
			char	*raw;
			char	*name;
			u64	addr;
		} source;
		struct {
			struct ins *ins;
			struct ins_operands *ops;
		} locked;
	};
};

struct ins_ops {
	void (*free)(struct ins_operands *ops);
	int (*parse)(struct ins_operands *ops);
	int (*scnprintf)(struct ins *ins, char *bf, size_t size,
			 struct ins_operands *ops);