summaryrefslogtreecommitdiffstats
path: root/tools/perf/builtin-annotate.c
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2010-03-08 20:21:04 +0000
committerRussell King <rmk+kernel@arm.linux.org.uk>2010-03-08 20:21:04 +0000
commit988addf82e4c03739375279de73929580a2d4a6a (patch)
tree989ae1cd4e264bbad80c65f04480486246e7b9f3 /tools/perf/builtin-annotate.c
parent004c1c7096659d352b83047a7593e91d8a30e3c5 (diff)
parent25cf84cf377c0aae5dbcf937ea89bc7893db5176 (diff)
Merge branch 'origin' into devel-stable
Conflicts: arch/arm/mach-mx2/devices.c arch/arm/mach-mx2/devices.h sound/soc/pxa/pxa-ssp.c
Diffstat (limited to 'tools/perf/builtin-annotate.c')
-rw-r--r--tools/perf/builtin-annotate.c240
1 files changed, 161 insertions, 79 deletions
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 593ff25006d..5ec5de99587 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -53,32 +53,20 @@ struct sym_priv {
static const char *sym_hist_filter;
-static int symbol_filter(struct map *map __used, struct symbol *sym)
+static int sym__alloc_hist(struct symbol *self)
{
- if (sym_hist_filter == NULL ||
- strcmp(sym->name, sym_hist_filter) == 0) {
- struct sym_priv *priv = symbol__priv(sym);
- const int size = (sizeof(*priv->hist) +
- (sym->end - sym->start) * sizeof(u64));
+ struct sym_priv *priv = symbol__priv(self);
+ const int size = (sizeof(*priv->hist) +
+ (self->end - self->start) * sizeof(u64));
- priv->hist = malloc(size);
- if (priv->hist)
- memset(priv->hist, 0, size);
- return 0;
- }
- /*
- * FIXME: We should really filter it out, as we don't want to go thru symbols
- * we're not interested, and if a DSO ends up with no symbols, delete it too,
- * but right now the kernel loading routines in symbol.c bail out if no symbols
- * are found, fix it later.
- */
- return 0;
+ priv->hist = zalloc(size);
+ return priv->hist == NULL ? -1 : 0;
}
/*
* collect histogram counts
*/
-static void hist_hit(struct hist_entry *he, u64 ip)
+static int annotate__hist_hit(struct hist_entry *he, u64 ip)
{
unsigned int sym_size, offset;
struct symbol *sym = he->sym;
@@ -88,83 +76,127 @@ static void hist_hit(struct hist_entry *he, u64 ip)
he->count++;
if (!sym || !he->map)
- return;
+ return 0;
priv = symbol__priv(sym);
- if (!priv->hist)
- return;
+ if (priv->hist == NULL && sym__alloc_hist(sym) < 0)
+ return -ENOMEM;
sym_size = sym->end - sym->start;
offset = ip - sym->start;
- if (verbose)
- fprintf(stderr, "%s: ip=%Lx\n", __func__,
- he->map->unmap_ip(he->map, ip));
+ pr_debug3("%s: ip=%#Lx\n", __func__, he->map->unmap_ip(he->map, ip));
if (offset >= sym_size)
- return;
+ return 0;
h = priv->hist;
h->sum++;
h->ip[offset]++;
- if (verbose >= 3)
- printf("%p %s: count++ [ip: %p, %08Lx] => %Ld\n",
- (void *)(unsigned long)he->sym->start,
- he->sym->name,
- (void *)(unsigned long)ip, ip - he->sym->start,
- h->ip[offset]);
+ pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->sym->start,
+ he->sym->name, ip, ip - he->sym->start, h->ip[offset]);
+ return 0;
}
static int perf_session__add_hist_entry(struct perf_session *self,
struct addr_location *al, u64 count)
{
bool hit;
- struct hist_entry *he = __perf_session__add_hist_entry(self, al, NULL,
- count, &hit);
+ struct hist_entry *he;
+
+ if (sym_hist_filter != NULL &&
+ (al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) {
+ /* We're only interested in a symbol named sym_hist_filter */
+ if (al->sym != NULL) {
+ rb_erase(&al->sym->rb_node,
+ &al->map->dso->symbols[al->map->type]);
+ symbol__delete(al->sym);
+ }
+ return 0;
+ }
+
+ he = __perf_session__add_hist_entry(self, al, NULL, count, &hit);
if (he == NULL)
return -ENOMEM;
- hist_hit(he, al->addr);
- return 0;
+
+ return annotate__hist_hit(he, al->addr);
}
static int process_sample_event(event_t *event, struct perf_session *session)
{
struct addr_location al;
- dump_printf("(IP, %d): %d: %p\n", event->header.misc,
- event->ip.pid, (void *)(long)event->ip.ip);
+ dump_printf("(IP, %d): %d: %#Lx\n", event->header.misc,
+ event->ip.pid, event->ip.ip);
- if (event__preprocess_sample(event, session, &al, symbol_filter) < 0) {
- fprintf(stderr, "problem processing %d event, skipping it.\n",
- event->header.type);
+ if (event__preprocess_sample(event, session, &al, NULL) < 0) {
+ pr_warning("problem processing %d event, skipping it.\n",
+ event->header.type);
return -1;
}
if (!al.filtered && perf_session__add_hist_entry(session, &al, 1)) {
- fprintf(stderr, "problem incrementing symbol count, "
- "skipping event\n");
+ pr_warning("problem incrementing symbol count, "
+ "skipping event\n");
return -1;
}
return 0;
}
-static int parse_line(FILE *file, struct hist_entry *he, u64 len)
+struct objdump_line {
+ struct list_head node;
+ s64 offset;
+ char *line;
+};
+
+static struct objdump_line *objdump_line__new(s64 offset, char *line)
+{
+ struct objdump_line *self = malloc(sizeof(*self));
+
+ if (self != NULL) {
+ self->offset = offset;
+ self->line = line;
+ }
+
+ return self;
+}
+
+static void objdump_line__free(struct objdump_line *self)
+{
+ free(self->line);
+ free(self);
+}
+
+static void objdump__add_line(struct list_head *head, struct objdump_line *line)
+{
+ list_add_tail(&line->node, head);
+}
+
+static struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
+ struct objdump_line *pos)
+{
+ list_for_each_entry_continue(pos, head, node)
+ if (pos->offset >= 0)
+ return pos;
+
+ return NULL;
+}
+
+static int parse_line(FILE *file, struct hist_entry *he,
+ struct list_head *head)
{
struct symbol *sym = he->sym;
+ struct objdump_line *objdump_line;
char *line = NULL, *tmp, *tmp2;
- static const char *prev_line;
- static const char *prev_color;
- unsigned int offset;
size_t line_len;
- u64 start;
- s64 line_ip;
- int ret;
+ s64 line_ip, offset = -1;
char *c;
if (getline(&line, &line_len, file) < 0)
return -1;
+
if (!line)
return -1;
@@ -173,8 +205,6 @@ static int parse_line(FILE *file, struct hist_entry *he, u64 len)
*c = 0;
line_ip = -1;
- offset = 0;
- ret = -2;
/*
* Strip leading spaces:
@@ -195,9 +225,30 @@ static int parse_line(FILE *file, struct hist_entry *he, u64 len)
line_ip = -1;
}
- start = he->map->unmap_ip(he->map, sym->start);
-
if (line_ip != -1) {
+ u64 start = map__rip_2objdump(he->map, sym->start);
+ offset = line_ip - start;
+ }
+
+ objdump_line = objdump_line__new(offset, line);
+ if (objdump_line == NULL) {
+ free(line);
+ return -1;
+ }
+ objdump__add_line(head, objdump_line);
+
+ return 0;
+}
+
+static int objdump_line__print(struct objdump_line *self,
+ struct list_head *head,
+ struct hist_entry *he, u64 len)
+{
+ struct symbol *sym = he->sym;
+ static const char *prev_line;
+ static const char *prev_color;
+
+ if (self->offset != -1) {
const char *path = NULL;
unsigned int hits = 0;
double percent = 0.0;
@@ -205,15 +256,22 @@ static int parse_line(FILE *file, struct hist_entry *he, u64 len)
struct sym_priv *priv = symbol__priv(sym);
struct sym_ext *sym_ext = priv->ext;
struct sym_hist *h = priv->hist;
+ s64 offset = self->offset;
+ struct objdump_line *next = objdump__get_next_ip_line(head, self);
+
+ while (offset < (s64)len &&
+ (next == NULL || offset < next->offset)) {
+ if (sym_ext) {
+ if (path == NULL)
+ path = sym_ext[offset].path;
+ percent += sym_ext[offset].percent;
+ } else
+ hits += h->ip[offset];
+
+ ++offset;
+ }
- offset = line_ip - start;
- if (offset < len)
- hits = h->ip[offset];
-
- if (offset < len && sym_ext) {
- path = sym_ext[offset].path;
- percent = sym_ext[offset].percent;
- } else if (h->sum)
+ if (sym_ext == NULL && h->sum)
percent = 100.0 * hits / h->sum;
color = get_percent_color(percent);
@@ -234,12 +292,12 @@ static int parse_line(FILE *file, struct hist_entry *he, u64 len)
color_fprintf(stdout, color, " %7.2f", percent);
printf(" : ");
- color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line);
+ color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", self->line);
} else {
- if (!*line)
+ if (!*self->line)
printf(" :\n");
else
- printf(" : %s\n", line);
+ printf(" : %s\n", self->line);
}
return 0;
@@ -365,6 +423,20 @@ static void print_summary(const char *filename)
}
}
+static void hist_entry__print_hits(struct hist_entry *self)
+{
+ struct symbol *sym = self->sym;
+ struct sym_priv *priv = symbol__priv(sym);
+ struct sym_hist *h = priv->hist;
+ u64 len = sym->end - sym->start, offset;
+
+ for (offset = 0; offset < len; ++offset)
+ if (h->ip[offset] != 0)
+ printf("%*Lx: %Lu\n", BITS_PER_LONG / 2,
+ sym->start + offset, h->ip[offset]);
+ printf("%*s: %Lu\n", BITS_PER_LONG / 2, "h->sum", h->sum);
+}
+
static void annotate_sym(struct hist_entry *he)
{
struct map *map = he->map;
@@ -374,15 +446,15 @@ static void annotate_sym(struct hist_entry *he)
u64 len;
char command[PATH_MAX*2];
FILE *file;
+ LIST_HEAD(head);
+ struct objdump_line *pos, *n;
if (!filename)
return;
- if (verbose)
- fprintf(stderr, "%s: filename=%s, sym=%s, start=%Lx, end=%Lx\n",
- __func__, filename, sym->name,
- map->unmap_ip(map, sym->start),
- map->unmap_ip(map, sym->end));
+ pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__,
+ filename, sym->name, map->unmap_ip(map, sym->start),
+ map->unmap_ip(map, sym->end));
if (full_paths)
d_filename = filename;
@@ -405,7 +477,8 @@ static void annotate_sym(struct hist_entry *he)
dso, dso->long_name, sym, sym->name);
sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s",
- map->unmap_ip(map, sym->start), map->unmap_ip(map, sym->end),
+ map__rip_2objdump(map, sym->start),
+ map__rip_2objdump(map, sym->end),
filename, filename);
if (verbose >= 3)
@@ -416,11 +489,21 @@ static void annotate_sym(struct hist_entry *he)
return;
while (!feof(file)) {
- if (parse_line(file, he, len) < 0)
+ if (parse_line(file, he, &head) < 0)
break;
}
pclose(file);
+
+ if (verbose)
+ hist_entry__print_hits(he);
+
+ list_for_each_entry_safe(pos, n, &head, node) {
+ objdump_line__print(pos, &head, he, len);
+ list_del(&pos->node);
+ objdump_line__free(pos);
+ }
+
if (print_line)
free_source_line(he, len);
}
@@ -451,10 +534,10 @@ static void perf_session__find_annotations(struct perf_session *self)
}
static struct perf_event_ops event_ops = {
- .process_sample_event = process_sample_event,
- .process_mmap_event = event__process_mmap,
- .process_comm_event = event__process_comm,
- .process_fork_event = event__process_task,
+ .sample = process_sample_event,
+ .mmap = event__process_mmap,
+ .comm = event__process_comm,
+ .fork = event__process_task,
};
static int __cmd_annotate(void)
@@ -542,9 +625,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used)
setup_pager();
if (field_sep && *field_sep == '.') {
- fputs("'.' is the only non valid --field-separator argument\n",
- stderr);
- exit(129);
+ pr_err("'.' is the only non valid --field-separator argument\n");
+ return -1;
}
return __cmd_annotate();