perf annotate: Move annotate functions to util/
They will be used by perf top, so that we have just one set of routines to do annotation. Rename "struct sym_priv" to "struct annotation", etc, to clarify this code a bit. Rename "struct sym_ext" to "struct source_line", to give it a meaningful name, that clarifies that it is a the result of an addr2line call, that is sorted by percentage one particular source code line appeared in the annotation. And since we're moving things around also rename 'sym_hist->ip' to 'sym_hist->addr' as we want to do data structure annotation at some point. Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Cc: Tom Zanussi <tzanussi@gmail.com> LKML-Reference: <new-submission> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
#include "annotate.h"
|
||||
#include "util.h"
|
||||
#include "build-id.h"
|
||||
#include "hist.h"
|
||||
@@ -949,225 +950,15 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread)
|
||||
}
|
||||
}
|
||||
|
||||
static int symbol__alloc_hist(struct symbol *self)
|
||||
int hist_entry__inc_addr_samples(struct hist_entry *he, u64 ip)
|
||||
{
|
||||
struct sym_priv *priv = symbol__priv(self);
|
||||
const int size = (sizeof(*priv->hist) +
|
||||
(self->end - self->start) * sizeof(u64));
|
||||
|
||||
priv->hist = zalloc(size);
|
||||
return priv->hist == NULL ? -1 : 0;
|
||||
return symbol__inc_addr_samples(he->ms.sym, he->ms.map, ip);
|
||||
}
|
||||
|
||||
int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip)
|
||||
{
|
||||
unsigned int sym_size, offset;
|
||||
struct symbol *sym = self->ms.sym;
|
||||
struct sym_priv *priv;
|
||||
struct sym_hist *h;
|
||||
|
||||
if (!sym || !self->ms.map)
|
||||
return 0;
|
||||
|
||||
priv = symbol__priv(sym);
|
||||
if (priv->hist == NULL && symbol__alloc_hist(sym) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
sym_size = sym->end - sym->start;
|
||||
offset = ip - sym->start;
|
||||
|
||||
pr_debug3("%s: ip=%#" PRIx64 "\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip));
|
||||
|
||||
if (offset >= sym_size)
|
||||
return 0;
|
||||
|
||||
h = priv->hist;
|
||||
h->sum++;
|
||||
h->ip[offset]++;
|
||||
|
||||
pr_debug3("%#" PRIx64 " %s: period++ [ip: %#" PRIx64 ", %#" PRIx64
|
||||
"] => %" PRIu64 "\n", self->ms.sym->start, self->ms.sym->name,
|
||||
ip, ip - self->ms.sym->start, h->ip[offset]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize)
|
||||
{
|
||||
struct objdump_line *self = malloc(sizeof(*self) + privsize);
|
||||
|
||||
if (self != NULL) {
|
||||
self->offset = offset;
|
||||
self->line = line;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
|
||||
struct list_head *head, size_t privsize)
|
||||
{
|
||||
struct symbol *sym = self->ms.sym;
|
||||
struct objdump_line *objdump_line;
|
||||
char *line = NULL, *tmp, *tmp2, *c;
|
||||
size_t line_len;
|
||||
s64 line_ip, offset = -1;
|
||||
|
||||
if (getline(&line, &line_len, file) < 0)
|
||||
return -1;
|
||||
|
||||
if (!line)
|
||||
return -1;
|
||||
|
||||
while (line_len != 0 && isspace(line[line_len - 1]))
|
||||
line[--line_len] = '\0';
|
||||
|
||||
c = strchr(line, '\n');
|
||||
if (c)
|
||||
*c = 0;
|
||||
|
||||
line_ip = -1;
|
||||
|
||||
/*
|
||||
* Strip leading spaces:
|
||||
*/
|
||||
tmp = line;
|
||||
while (*tmp) {
|
||||
if (*tmp != ' ')
|
||||
break;
|
||||
tmp++;
|
||||
}
|
||||
|
||||
if (*tmp) {
|
||||
/*
|
||||
* Parse hexa addresses followed by ':'
|
||||
*/
|
||||
line_ip = strtoull(tmp, &tmp2, 16);
|
||||
if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0')
|
||||
line_ip = -1;
|
||||
}
|
||||
|
||||
if (line_ip != -1) {
|
||||
u64 start = map__rip_2objdump(self->ms.map, sym->start),
|
||||
end = map__rip_2objdump(self->ms.map, sym->end);
|
||||
|
||||
offset = line_ip - start;
|
||||
if (offset < 0 || (u64)line_ip > end)
|
||||
offset = -1;
|
||||
}
|
||||
|
||||
objdump_line = objdump_line__new(offset, line, privsize);
|
||||
if (objdump_line == NULL) {
|
||||
free(line);
|
||||
return -1;
|
||||
}
|
||||
objdump__add_line(head, objdump_line);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
|
||||
int hist_entry__annotate(struct hist_entry *he, struct list_head *head,
|
||||
size_t privsize)
|
||||
{
|
||||
struct symbol *sym = self->ms.sym;
|
||||
struct map *map = self->ms.map;
|
||||
struct dso *dso = map->dso;
|
||||
char *filename = dso__build_id_filename(dso, NULL, 0);
|
||||
bool free_filename = true;
|
||||
char command[PATH_MAX * 2];
|
||||
FILE *file;
|
||||
int err = 0;
|
||||
u64 len;
|
||||
char symfs_filename[PATH_MAX];
|
||||
|
||||
if (filename) {
|
||||
snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
|
||||
symbol_conf.symfs, filename);
|
||||
}
|
||||
|
||||
if (filename == NULL) {
|
||||
if (dso->has_build_id) {
|
||||
pr_err("Can't annotate %s: not enough memory\n",
|
||||
sym->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
goto fallback;
|
||||
} else if (readlink(symfs_filename, command, sizeof(command)) < 0 ||
|
||||
strstr(command, "[kernel.kallsyms]") ||
|
||||
access(symfs_filename, R_OK)) {
|
||||
free(filename);
|
||||
fallback:
|
||||
/*
|
||||
* If we don't have build-ids or the build-id file isn't in the
|
||||
* cache, or is just a kallsyms file, well, lets hope that this
|
||||
* DSO is the same as when 'perf record' ran.
|
||||
*/
|
||||
filename = dso->long_name;
|
||||
snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
|
||||
symbol_conf.symfs, filename);
|
||||
free_filename = false;
|
||||
}
|
||||
|
||||
if (dso->origin == DSO__ORIG_KERNEL) {
|
||||
if (dso->annotate_warned)
|
||||
goto out_free_filename;
|
||||
err = -ENOENT;
|
||||
dso->annotate_warned = 1;
|
||||
pr_err("Can't annotate %s: No vmlinux file was found in the "
|
||||
"path\n", sym->name);
|
||||
goto out_free_filename;
|
||||
}
|
||||
|
||||
pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__,
|
||||
filename, sym->name, map->unmap_ip(map, sym->start),
|
||||
map->unmap_ip(map, sym->end));
|
||||
|
||||
len = sym->end - sym->start;
|
||||
|
||||
pr_debug("annotating [%p] %30s : [%p] %30s\n",
|
||||
dso, dso->long_name, sym, sym->name);
|
||||
|
||||
snprintf(command, sizeof(command),
|
||||
"objdump --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand",
|
||||
map__rip_2objdump(map, sym->start),
|
||||
map__rip_2objdump(map, sym->end),
|
||||
symfs_filename, filename);
|
||||
|
||||
pr_debug("Executing: %s\n", command);
|
||||
|
||||
file = popen(command, "r");
|
||||
if (!file)
|
||||
goto out_free_filename;
|
||||
|
||||
while (!feof(file))
|
||||
if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0)
|
||||
break;
|
||||
|
||||
pclose(file);
|
||||
out_free_filename:
|
||||
if (free_filename)
|
||||
free(filename);
|
||||
return err;
|
||||
return symbol__annotate(he->ms.sym, he->ms.map, head, privsize);
|
||||
}
|
||||
|
||||
void hists__inc_nr_events(struct hists *self, u32 type)
|
||||
|
Reference in New Issue
Block a user