1378 lines · 32.2 KB · C
RawDownload
/* * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> * * Parts came from builtin-annotate.c, see those files for further * copyright notes. * * Released under the GPL v2. (and only v2, not any later version) */ #include "util.h"#include "build-id.h"#include "color.h"#include "cache.h"#include "symbol.h"#include "debug.h"#include "annotate.h"#include "evsel.h"#include <pthread.h>#include <linux/bitops.h> const char 	*disassembler_style;const char	*objdump_path; 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){	return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->raw);} int ins__scnprintf(struct ins *ins, char *bf, size_t size,		  struct ins_operands *ops){	if (ins->ops->scnprintf)		return ins->ops->scnprintf(ins, bf, size, ops); 	return ins__raw_scnprintf(ins, bf, size, ops);} static int call__parse(struct ins_operands *ops){	char *endptr, *tok, *name; 	ops->target.addr = strtoull(ops->raw, &endptr, 16); 	name = strchr(endptr, '<');	if (name == NULL)		goto indirect_call; 	name++; 	tok = strchr(name, '>');	if (tok == NULL)		return -1; 	*tok = '\0';	ops->target.name = strdup(name);	*tok = '>'; 	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; 	ops->target.addr = strtoull(tok + 1, NULL, 16);	return 0;} static int call__scnprintf(struct ins *ins, char *bf, size_t size,			   struct ins_operands *ops){	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);} static struct ins_ops call_ops = {	.parse	   = call__parse,	.scnprintf = call__scnprintf,}; bool ins__is_call(const struct ins *ins){	return ins->ops == &call_ops;} static int jump__parse(struct ins_operands *ops){	const char *s = strchr(ops->raw, '+'); 	ops->target.addr = strtoull(ops->raw, NULL, 16); 	if (s++ != NULL)		ops->target.offset = strtoull(s, NULL, 16);	else		ops->target.offset = UINT64_MAX; 	return 0;} static int jump__scnprintf(struct ins *ins, char *bf, size_t size,			   struct ins_operands *ops){	return scnprintf(bf, size, "%-6.6s %" PRIx64, ins->name, ops->target.offset);} static struct ins_ops jump_ops = {	.parse	   = jump__parse,	.scnprintf = jump__scnprintf,}; 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 __maybe_unused, char *bf, size_t size,			  struct ins_operands *ops __maybe_unused){	return scnprintf(bf, size, "%-6.6s", "nop");} static struct ins_ops nop_ops = {	.scnprintf = nop__scnprintf,}; /* * 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, },	{ .name = "jbe",   .ops  = &jump_ops, },	{ .name = "jc",	   .ops  = &jump_ops, },	{ .name = "jcxz",  .ops  = &jump_ops, },	{ .name = "je",	   .ops  = &jump_ops, },	{ .name = "jecxz", .ops  = &jump_ops, },	{ .name = "jg",	   .ops  = &jump_ops, },	{ .name = "jge",   .ops  = &jump_ops, },	{ .name = "jl",    .ops  = &jump_ops, },	{ .name = "jle",   .ops  = &jump_ops, },	{ .name = "jmp",   .ops  = &jump_ops, },	{ .name = "jmpq",  .ops  = &jump_ops, },	{ .name = "jna",   .ops  = &jump_ops, },	{ .name = "jnae",  .ops  = &jump_ops, },	{ .name = "jnb",   .ops  = &jump_ops, },	{ .name = "jnbe",  .ops  = &jump_ops, },	{ .name = "jnc",   .ops  = &jump_ops, },	{ .name = "jne",   .ops  = &jump_ops, },	{ .name = "jng",   .ops  = &jump_ops, },	{ .name = "jnge",  .ops  = &jump_ops, },	{ .name = "jnl",   .ops  = &jump_ops, },	{ .name = "jnle",  .ops  = &jump_ops, },	{ .name = "jno",   .ops  = &jump_ops, },	{ .name = "jnp",   .ops  = &jump_ops, },	{ .name = "jns",   .ops  = &jump_ops, },	{ .name = "jnz",   .ops  = &jump_ops, },	{ .name = "jo",	   .ops  = &jump_ops, },	{ .name = "jp",	   .ops  = &jump_ops, },	{ .name = "jpe",   .ops  = &jump_ops, },	{ .name = "jpo",   .ops  = &jump_ops, },	{ .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, },	{ .name = "xbeginl", .ops  = &jump_ops, },	{ .name = "xbeginq", .ops  = &jump_ops, },}; static int ins__cmp(const void *name, const void *insp){	const struct ins *ins = insp; 	return strcmp(name, ins->name);} static struct ins *ins__find(const char *name){	const int nmemb = ARRAY_SIZE(instructions); 	return bsearch(name, instructions, nmemb, sizeof(struct ins), ins__cmp);} int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym){	struct annotation *notes = symbol__annotation(sym);	pthread_mutex_init(&notes->lock, NULL);	return 0;} int symbol__alloc_hist(struct symbol *sym){	struct annotation *notes = symbol__annotation(sym);	const size_t size = symbol__size(sym);	size_t sizeof_sym_hist; 	/* Check for overflow when calculating sizeof_sym_hist */	if (size > (SIZE_MAX - sizeof(struct sym_hist)) / sizeof(u64))		return -1; 	sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(u64)); 	/* Check for overflow in zalloc argument */	if (sizeof_sym_hist > (SIZE_MAX - sizeof(*notes->src))				/ symbol_conf.nr_events)		return -1; 	notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist);	if (notes->src == NULL)		return -1;	notes->src->sizeof_sym_hist = sizeof_sym_hist;	notes->src->nr_histograms   = symbol_conf.nr_events;	INIT_LIST_HEAD(&notes->src->source);	return 0;} void symbol__annotate_zero_histograms(struct symbol *sym){	struct annotation *notes = symbol__annotation(sym); 	pthread_mutex_lock(&notes->lock);	if (notes->src != NULL)		memset(notes->src->histograms, 0,		       notes->src->nr_histograms * notes->src->sizeof_sym_hist);	pthread_mutex_unlock(&notes->lock);} int symbol__inc_addr_samples(struct symbol *sym, struct map *map,			     int evidx, u64 addr){	unsigned offset;	struct annotation *notes;	struct sym_hist *h; 	notes = symbol__annotation(sym);	if (notes->src == NULL)		return -ENOMEM; 	pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); 	if (addr < sym->start || addr > sym->end)		return -ERANGE; 	offset = addr - sym->start;	h = annotation__histogram(notes, evidx);	h->sum++;	h->addr[offset]++; 	pr_debug3("%#" PRIx64 " %s: period++ [addr: %#" PRIx64 ", %#" PRIx64		  ", evidx=%d] => %" PRIu64 "\n", sym->start, sym->name,		  addr, addr - sym->start, evidx, h->addr[offset]);	return 0;} static void disasm_line__init_ins(struct disasm_line *dl){	dl->ins = ins__find(dl->name); 	if (dl->ins == NULL)		return; 

Lines 1–500 of 1378.