提交 439d473b 编写于 作者: A Arnaldo Carvalho de Melo 提交者: Ingo Molnar

perf tools: Rewrite and improve support for kernel modules

Representing modules as struct map entries, backed by a DSO, etc,
using /proc/modules to find where the module is loaded.

DSOs now can have a short and long name, so that in verbose mode we
can show exactly which .ko or vmlinux image was used.

As kernel modules now are a DSO separate from the kernel, we can
ask for just the hits for a particular set of kernel modules, just
like we can do with shared libraries:

[root@doppio linux-2.6-tip]# perf report -n --vmlinux
/home/acme/git/build/tip-recvmmsg/vmlinux --modules --dsos \[drm\] | head -15
    84.58%      13266             Xorg  [k] drm_clflush_pages
     4.02%        630             Xorg  [k] trace_kmalloc.clone.0
     3.95%        619             Xorg  [k] drm_ioctl
     2.07%        324             Xorg  [k] drm_addbufs
     1.68%        263             Xorg  [k] drm_gem_close_ioctl
     0.77%        120             Xorg  [k] drm_setmaster_ioctl
     0.70%        110             Xorg  [k] drm_lastclose
     0.68%        106             Xorg  [k] drm_open
     0.54%         85             Xorg  [k] drm_mm_search_free
[root@doppio linux-2.6-tip]#

Specifying --dsos /lib/modules/2.6.31-tip/kernel/drivers/gpu/drm/drm.ko
would have the same effect. Allowing specifying just 'drm.ko' is left
for another patch.

Processing kallsyms so that per kernel module struct map are
instantiated was also left for another patch. That will allow
removing the module name from each of its symbols.

struct symbol was reduced by removing the ->module backpointer and
moving it (well now the map) to struct symbol_entry in perf top,
that is its only user right now.

The total linecount went down by ~500 lines.
Signed-off-by: NArnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Avi Kivity <avi@redhat.com>
Signed-off-by: NIngo Molnar <mingo@elte.hu>
上级 2ccdc450
......@@ -336,7 +336,6 @@ LIB_H += util/strlist.h
LIB_H += util/run-command.h
LIB_H += util/sigchain.h
LIB_H += util/symbol.h
LIB_H += util/module.h
LIB_H += util/color.h
LIB_H += util/values.h
LIB_H += util/sort.h
......@@ -364,7 +363,6 @@ LIB_OBJS += util/usage.o
LIB_OBJS += util/wrapper.o
LIB_OBJS += util/sigchain.o
LIB_OBJS += util/symbol.o
LIB_OBJS += util/module.o
LIB_OBJS += util/color.o
LIB_OBJS += util/pager.o
LIB_OBJS += util/header.o
......
......@@ -63,6 +63,7 @@ static void hist_hit(struct hist_entry *he, u64 ip)
return;
sym_size = sym->end - sym->start;
ip = he->map->map_ip(he->map, ip);
offset = ip - sym->start;
if (offset >= sym_size)
......@@ -80,7 +81,7 @@ static void hist_hit(struct hist_entry *he, u64 ip)
}
static int
hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
hist_entry__add(struct thread *thread, struct map *map,
struct symbol *sym, u64 ip, char level)
{
struct rb_node **p = &hist.rb_node;
......@@ -89,7 +90,6 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
struct hist_entry entry = {
.thread = thread,
.map = map,
.dso = dso,
.sym = sym,
.ip = ip,
.level = level,
......@@ -130,10 +130,10 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
{
char level;
int show = 0;
struct dso *dso = NULL;
struct thread *thread;
u64 ip = event->ip.ip;
struct map *map = NULL;
struct symbol *sym = NULL;
thread = threads__findnew(event->ip.pid, &threads, &last_match);
......@@ -155,32 +155,35 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
if (event->header.misc & PERF_RECORD_MISC_KERNEL) {
show = SHOW_KERNEL;
level = 'k';
dso = kernel_dso;
dump_printf(" ...... dso: %s\n", dso->name);
sym = kernel_maps__find_symbol(ip, &map);
dump_printf(" ...... dso: %s\n",
map ? map->dso->long_name : "<not found>");
} else if (event->header.misc & PERF_RECORD_MISC_USER) {
show = SHOW_USER;
level = '.';
map = thread__find_map(thread, ip);
if (map != NULL) {
got_map:
ip = map->map_ip(map, ip);
dso = map->dso;
sym = map->dso->find_symbol(map->dso, ip);
} else {
/*
* If this is outside of all known maps,
* and is a negative address, try to look it
* up in the kernel dso, as it might be a
* vsyscall (which executes in user-mode):
* vsyscall or vdso (which executes in user-mode).
*
* XXX This is nasty, we should have a symbol list in
* the "[vdso]" dso, but for now lets use the old
* trick of looking in the whole kernel symbol list.
*/
if ((long long)ip < 0)
dso = kernel_dso;
if ((long long)ip < 0) {
map = kernel_map;
goto got_map;
}
}
dump_printf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
dump_printf(" ...... dso: %s\n",
map ? map->dso->long_name : "<not found>");
} else {
show = SHOW_HV;
level = 'H';
......@@ -188,12 +191,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
}
if (show & show_mask) {
struct symbol *sym = NULL;
if (dso)
sym = dso->find_symbol(dso, ip);
if (hist_entry__add(thread, map, dso, sym, ip, level)) {
if (hist_entry__add(thread, map, sym, ip, level)) {
fprintf(stderr,
"problem incrementing symbol count, skipping event\n");
return -1;
......@@ -313,7 +311,7 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
}
static int
parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)
parse_line(FILE *file, struct symbol *sym, u64 len)
{
char *line = NULL, *tmp, *tmp2;
static const char *prev_line;
......@@ -363,7 +361,7 @@ parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)
const char *color;
struct sym_ext *sym_ext = sym->priv;
offset = line_ip - start;
offset = line_ip - sym->start;
if (offset < len)
hits = sym->hist[offset];
......@@ -442,7 +440,7 @@ static void free_source_line(struct symbol *sym, int len)
/* Get the filename:line for the colored entries */
static void
get_source_line(struct symbol *sym, u64 start, int len, const char *filename)
get_source_line(struct symbol *sym, int len, const char *filename)
{
int i;
char cmd[PATH_MAX * 2];
......@@ -467,7 +465,7 @@ get_source_line(struct symbol *sym, u64 start, int len, const char *filename)
if (sym_ext[i].percent <= 0.5)
continue;
offset = start + i;
offset = sym->start + i;
sprintf(cmd, "addr2line -e %s %016llx", filename, offset);
fp = popen(cmd, "r");
if (!fp)
......@@ -519,31 +517,23 @@ static void print_summary(const char *filename)
static void annotate_sym(struct dso *dso, struct symbol *sym)
{
const char *filename = dso->name, *d_filename;
u64 start, end, len;
const char *filename = dso->long_name, *d_filename;
u64 len;
char command[PATH_MAX*2];
FILE *file;
if (!filename)
return;
if (sym->module)
filename = sym->module->path;
else if (dso == kernel_dso)
filename = vmlinux_name;
start = sym->obj_start;
if (!start)
start = sym->start;
if (full_paths)
d_filename = filename;
else
d_filename = basename(filename);
end = start + sym->end - sym->start + 1;
len = sym->end - sym->start;
if (print_line) {
get_source_line(sym, start, len, filename);
get_source_line(sym, len, filename);
print_summary(filename);
}
......@@ -552,10 +542,11 @@ static void annotate_sym(struct dso *dso, struct symbol *sym)
printf("------------------------------------------------\n");
if (verbose >= 2)
printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name);
printf("annotating [%p] %30s : [%p] %30s\n",
dso, dso->long_name, sym, sym->name);
sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s",
(u64)start, (u64)end, filename, filename);
sym->start, sym->end, filename, filename);
if (verbose >= 3)
printf("doing: %s\n", command);
......@@ -565,7 +556,7 @@ static void annotate_sym(struct dso *dso, struct symbol *sym)
return;
while (!feof(file)) {
if (parse_line(file, sym, start, len) < 0)
if (parse_line(file, sym, len) < 0)
break;
}
......
......@@ -349,22 +349,17 @@ static int thread__set_comm_adjust(struct thread *self, const char *comm)
static struct symbol *
resolve_symbol(struct thread *thread, struct map **mapp,
struct dso **dsop, u64 *ipp)
resolve_symbol(struct thread *thread, struct map **mapp, u64 *ipp)
{
struct dso *dso = dsop ? *dsop : NULL;
struct map *map = mapp ? *mapp : NULL;
u64 ip = *ipp;
if (!thread)
return NULL;
if (dso)
goto got_dso;
if (map)
goto got_map;
if (!thread)
return NULL;
map = thread__find_map(thread, ip);
if (map != NULL) {
/*
......@@ -379,29 +374,29 @@ resolve_symbol(struct thread *thread, struct map **mapp,
*mapp = map;
got_map:
ip = map->map_ip(map, ip);
dso = map->dso;
} else {
/*
* If this is outside of all known maps,
* and is a negative address, try to look it
* up in the kernel dso, as it might be a
* vsyscall (which executes in user-mode):
* vsyscall or vdso (which executes in user-mode).
*
* XXX This is nasty, we should have a symbol list in
* the "[vdso]" dso, but for now lets use the old
* trick of looking in the whole kernel symbol list.
*/
if ((long long)ip < 0)
dso = kernel_dso;
if ((long long)ip < 0) {
map = kernel_map;
if (mapp)
*mapp = map;
}
}
dump_printf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
dump_printf(" ...... dso: %s\n",
map ? map->dso->long_name : "<not found>");
dump_printf(" ...... map: %Lx -> %Lx\n", *ipp, ip);
*ipp = ip;
if (dsop)
*dsop = dso;
if (!dso)
return NULL;
got_dso:
return dso->find_symbol(dso, ip);
return map ? map->dso->find_symbol(map->dso, ip) : NULL;
}
static int call__match(struct symbol *sym)
......@@ -413,7 +408,7 @@ static int call__match(struct symbol *sym)
}
static struct symbol **
resolve_callchain(struct thread *thread, struct map *map __used,
resolve_callchain(struct thread *thread, struct map *map,
struct ip_callchain *chain, struct hist_entry *entry)
{
u64 context = PERF_CONTEXT_MAX;
......@@ -430,8 +425,7 @@ resolve_callchain(struct thread *thread, struct map *map __used,
for (i = 0; i < chain->nr; i++) {
u64 ip = chain->ips[i];
struct dso *dso = NULL;
struct symbol *sym;
struct symbol *sym = NULL;
if (ip >= PERF_CONTEXT_MAX) {
context = ip;
......@@ -440,17 +434,15 @@ resolve_callchain(struct thread *thread, struct map *map __used,
switch (context) {
case PERF_CONTEXT_HV:
dso = hypervisor_dso;
break;
case PERF_CONTEXT_KERNEL:
dso = kernel_dso;
sym = kernel_maps__find_symbol(ip, &map);
break;
default:
sym = resolve_symbol(thread, &map, &ip);
break;
}
sym = resolve_symbol(thread, NULL, &dso, &ip);
if (sym) {
if (sort__has_parent && call__match(sym) &&
!entry->parent)
......@@ -469,7 +461,7 @@ resolve_callchain(struct thread *thread, struct map *map __used,
*/
static int
hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
hist_entry__add(struct thread *thread, struct map *map,
struct symbol *sym, u64 ip, struct ip_callchain *chain,
char level, u64 count)
{
......@@ -480,7 +472,6 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
struct hist_entry entry = {
.thread = thread,
.map = map,
.dso = dso,
.sym = sym,
.ip = ip,
.level = level,
......@@ -641,7 +632,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
{
char level;
int show = 0;
struct dso *dso = NULL;
struct symbol *sym = NULL;
struct thread *thread;
u64 ip = event->ip.ip;
u64 period = 1;
......@@ -700,35 +691,35 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
show = SHOW_KERNEL;
level = 'k';
dso = kernel_dso;
dump_printf(" ...... dso: %s\n", dso->name);
sym = kernel_maps__find_symbol(ip, &map);
dump_printf(" ...... dso: %s\n",
map ? map->dso->long_name : "<not found>");
} else if (cpumode == PERF_RECORD_MISC_USER) {
show = SHOW_USER;
level = '.';
sym = resolve_symbol(thread, &map, &ip);
} else {
show = SHOW_HV;
level = 'H';
dso = hypervisor_dso;
dump_printf(" ...... dso: [hypervisor]\n");
}
if (show & show_mask) {
struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip);
if (dso_list && (!dso || !dso->name ||
!strlist__has_entry(dso_list, dso->name)))
if (dso_list &&
(!map || !map->dso ||
!(strlist__has_entry(dso_list, map->dso->short_name) ||
(map->dso->short_name != map->dso->long_name &&
strlist__has_entry(dso_list, map->dso->long_name)))))
return 0;
if (sym_list && (!sym || !strlist__has_entry(sym_list, sym->name)))
if (sym_list && sym && !strlist__has_entry(sym_list, sym->name))
return 0;
if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) {
if (hist_entry__add(thread, map, sym, ip,
chain, level, period)) {
eprintf("problem incrementing symbol count, skipping event\n");
return -1;
}
......
......@@ -22,6 +22,7 @@
#include "util/symbol.h"
#include "util/color.h"
#include "util/thread.h"
#include "util/util.h"
#include <linux/rbtree.h>
#include "util/parse-options.h"
......@@ -103,6 +104,7 @@ struct sym_entry {
unsigned long snap_count;
double weight;
int skip;
struct map *map;
struct source_line *source;
struct source_line *lines;
struct source_line **lines_tail;
......@@ -116,12 +118,11 @@ struct sym_entry {
static void parse_source(struct sym_entry *syme)
{
struct symbol *sym;
struct module *module;
struct section *section = NULL;
struct map *map;
FILE *file;
char command[PATH_MAX*2];
const char *path = vmlinux_name;
u64 start, end, len;
const char *path;
u64 len;
if (!syme)
return;
......@@ -132,27 +133,15 @@ static void parse_source(struct sym_entry *syme)
}
sym = (struct symbol *)(syme + 1);
module = sym->module;
map = syme->map;
path = map->dso->long_name;
if (module)
path = module->path;
if (!path)
return;
start = sym->obj_start;
if (!start)
start = sym->start;
if (module) {
section = module->sections->find_section(module->sections, ".text");
if (section)
start -= section->vma;
}
end = start + sym->end - sym->start + 1;
len = sym->end - sym->start;
sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", start, end, path);
sprintf(command,
"objdump --start-address=0x%016Lx "
"--stop-address=0x%016Lx -dS %s",
sym->start, sym->end, path);
file = popen(command, "r");
if (!file)
......@@ -184,13 +173,11 @@ static void parse_source(struct sym_entry *syme)
if (strlen(src->line)>8 && src->line[8] == ':') {
src->eip = strtoull(src->line, NULL, 16);
if (section)
src->eip += section->vma;
src->eip += map->start;
}
if (strlen(src->line)>8 && src->line[16] == ':') {
src->eip = strtoull(src->line, NULL, 16);
if (section)
src->eip += section->vma;
src->eip += map->start;
}
}
pclose(file);
......@@ -242,16 +229,9 @@ static void lookup_sym_source(struct sym_entry *syme)
struct symbol *symbol = (struct symbol *)(syme + 1);
struct source_line *line;
char pattern[PATH_MAX];
char *idx;
sprintf(pattern, "<%s>:", symbol->name);
if (symbol->module) {
idx = strstr(pattern, "\t");
if (idx)
*idx = 0;
}
pthread_mutex_lock(&syme->source_lock);
for (line = syme->lines; line; line = line->next) {
if (strstr(line->line, pattern)) {
......@@ -513,8 +493,8 @@ static void print_sym_table(void)
if (verbose)
printf(" - %016llx", sym->start);
printf(" : %s", sym->name);
if (sym->module)
printf("\t[%s]", sym->module->name);
if (syme->map->dso->name[0] == '[')
printf(" \t%s", syme->map->dso->name);
printf("\n");
}
}
......@@ -784,7 +764,7 @@ static const char *skip_symbols[] = {
NULL
};
static int symbol_filter(struct dso *self, struct symbol *sym)
static int symbol_filter(struct map *map, struct symbol *sym)
{
struct sym_entry *syme;
const char *name = sym->name;
......@@ -806,7 +786,8 @@ static int symbol_filter(struct dso *self, struct symbol *sym)
strstr(name, "_text_end"))
return 1;
syme = dso__sym_priv(self, sym);
syme = dso__sym_priv(map->dso, sym);
syme->map = map;
pthread_mutex_init(&syme->source_lock, NULL);
if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter))
sym_filter_entry = syme;
......@@ -825,22 +806,14 @@ static int parse_symbols(void)
{
int use_modules = vmlinux_name ? 1 : 0;
kernel_dso = dso__new("[kernel]", sizeof(struct sym_entry));
if (kernel_dso == NULL)
if (dsos__load_kernel(vmlinux_name, sizeof(struct sym_entry),
symbol_filter, verbose, use_modules) <= 0)
return -1;
if (dso__load_kernel(kernel_dso, vmlinux_name, symbol_filter, verbose, use_modules) <= 0)
goto out_delete_dso;
if (dump_symtab)
dso__fprintf(kernel_dso, stderr);
dsos__fprintf(stderr);
return 0;
out_delete_dso:
dso__delete(kernel_dso);
kernel_dso = NULL;
return -1;
}
/*
......@@ -848,10 +821,11 @@ static int parse_symbols(void)
*/
static void record_ip(u64 ip, int counter)
{
struct symbol *sym = dso__find_symbol(kernel_dso, ip);
struct map *map;
struct symbol *sym = kernel_maps__find_symbol(ip, &map);
if (sym != NULL) {
struct sym_entry *syme = dso__sym_priv(kernel_dso, sym);
struct sym_entry *syme = dso__sym_priv(map->dso, sym);
if (!syme->skip) {
syme->count[counter]++;
......
......@@ -3,6 +3,7 @@
#include "../perf.h"
#include "util.h"
#include <linux/list.h>
#include <linux/rbtree.h>
enum {
......@@ -79,7 +80,10 @@ typedef union event_union {
} event_t;
struct map {
struct rb_node rb_node;
union {
struct rb_node rb_node;
struct list_head node;
};
u64 start;
u64 end;
u64 pgoff;
......
#include "util.h"
#include "../perf.h"
#include "string.h"
#include "module.h"
#include <libelf.h>
#include <libgen.h>
#include <gelf.h>
#include <elf.h>
#include <dirent.h>
#include <sys/utsname.h>
static unsigned int crc32(const char *p, unsigned int len)
{
int i;
unsigned int crc = 0;
while (len--) {
crc ^= *p++;
for (i = 0; i < 8; i++)
crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0);
}
return crc;
}
/* module section methods */
struct sec_dso *sec_dso__new_dso(const char *name)
{
struct sec_dso *self = malloc(sizeof(*self) + strlen(name) + 1);
if (self != NULL) {
strcpy(self->name, name);
self->secs = RB_ROOT;
self->find_section = sec_dso__find_section;
}
return self;
}
static void sec_dso__delete_section(struct section *self)
{
free(((void *)self));
}
void sec_dso__delete_sections(struct sec_dso *self)
{
struct section *pos;
struct rb_node *next = rb_first(&self->secs);
while (next) {
pos = rb_entry(next, struct section, rb_node);
next = rb_next(&pos->rb_node);
rb_erase(&pos->rb_node, &self->secs);
sec_dso__delete_section(pos);
}
}
void sec_dso__delete_self(struct sec_dso *self)
{
sec_dso__delete_sections(self);
free(self);
}
static void sec_dso__insert_section(struct sec_dso *self, struct section *sec)
{
struct rb_node **p = &self->secs.rb_node;
struct rb_node *parent = NULL;
const u64 hash = sec->hash;
struct section *s;
while (*p != NULL) {
parent = *p;
s = rb_entry(parent, struct section, rb_node);
if (hash < s->hash)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
rb_link_node(&sec->rb_node, parent, p);
rb_insert_color(&sec->rb_node, &self->secs);
}
struct section *sec_dso__find_section(struct sec_dso *self, const char *name)
{
struct rb_node *n;
u64 hash;
int len;
if (self == NULL)
return NULL;
len = strlen(name);
hash = crc32(name, len);
n = self->secs.rb_node;
while (n) {
struct section *s = rb_entry(n, struct section, rb_node);
if (hash < s->hash)
n = n->rb_left;
else if (hash > s->hash)
n = n->rb_right;
else {
if (!strcmp(name, s->name))
return s;
else
n = rb_next(&s->rb_node);
}
}
return NULL;
}
static size_t sec_dso__fprintf_section(struct section *self, FILE *fp)
{
return fprintf(fp, "name:%s vma:%llx path:%s\n",
self->name, self->vma, self->path);
}
size_t sec_dso__fprintf(struct sec_dso *self, FILE *fp)
{
size_t ret = fprintf(fp, "dso: %s\n", self->name);
struct rb_node *nd;
for (nd = rb_first(&self->secs); nd; nd = rb_next(nd)) {
struct section *pos = rb_entry(nd, struct section, rb_node);
ret += sec_dso__fprintf_section(pos, fp);
}
return ret;
}
static struct section *section__new(const char *name, const char *path)
{
struct section *self = calloc(1, sizeof(*self));
if (!self)
goto out_failure;
self->name = calloc(1, strlen(name) + 1);
if (!self->name)
goto out_failure;
self->path = calloc(1, strlen(path) + 1);
if (!self->path)
goto out_failure;
strcpy(self->name, name);
strcpy(self->path, path);
self->hash = crc32(self->name, strlen(name));
return self;
out_failure:
if (self) {
if (self->name)
free(self->name);
if (self->path)
free(self->path);
free(self);
}
return NULL;
}
/* module methods */
struct mod_dso *mod_dso__new_dso(const char *name)
{
struct mod_dso *self = malloc(sizeof(*self) + strlen(name) + 1);
if (self != NULL) {
strcpy(self->name, name);
self->mods = RB_ROOT;
self->find_module = mod_dso__find_module;
}
return self;
}
static void mod_dso__delete_module(struct module *self)
{
free(((void *)self));
}
void mod_dso__delete_modules(struct mod_dso *self)
{
struct module *pos;
struct rb_node *next = rb_first(&self->mods);
while (next) {
pos = rb_entry(next, struct module, rb_node);
next = rb_next(&pos->rb_node);
rb_erase(&pos->rb_node, &self->mods);
mod_dso__delete_module(pos);
}
}
void mod_dso__delete_self(struct mod_dso *self)
{
mod_dso__delete_modules(self);
free(self);
}
static void mod_dso__insert_module(struct mod_dso *self, struct module *mod)
{
struct rb_node **p = &self->mods.rb_node;
struct rb_node *parent = NULL;
const u64 hash = mod->hash;
struct module *m;
while (*p != NULL) {
parent = *p;
m = rb_entry(parent, struct module, rb_node);
if (hash < m->hash)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
rb_link_node(&mod->rb_node, parent, p);
rb_insert_color(&mod->rb_node, &self->mods);
}
struct module *mod_dso__find_module(struct mod_dso *self, const char *name)
{
struct rb_node *n;
u64 hash;
int len;
if (self == NULL)
return NULL;
len = strlen(name);
hash = crc32(name, len);
n = self->mods.rb_node;
while (n) {
struct module *m = rb_entry(n, struct module, rb_node);
if (hash < m->hash)
n = n->rb_left;
else if (hash > m->hash)
n = n->rb_right;
else {
if (!strcmp(name, m->name))
return m;
else
n = rb_next(&m->rb_node);
}
}
return NULL;
}
static size_t mod_dso__fprintf_module(struct module *self, FILE *fp)
{
return fprintf(fp, "name:%s path:%s\n", self->name, self->path);
}
size_t mod_dso__fprintf(struct mod_dso *self, FILE *fp)
{
struct rb_node *nd;
size_t ret;
ret = fprintf(fp, "dso: %s\n", self->name);
for (nd = rb_first(&self->mods); nd; nd = rb_next(nd)) {
struct module *pos = rb_entry(nd, struct module, rb_node);
ret += mod_dso__fprintf_module(pos, fp);
}
return ret;
}
static struct module *module__new(const char *name, const char *path)
{
struct module *self = calloc(1, sizeof(*self));
if (!self)
goto out_failure;
self->name = calloc(1, strlen(name) + 1);
if (!self->name)
goto out_failure;
self->path = calloc(1, strlen(path) + 1);
if (!self->path)
goto out_failure;
strcpy(self->name, name);
strcpy(self->path, path);
self->hash = crc32(self->name, strlen(name));
return self;
out_failure:
if (self) {
if (self->name)
free(self->name);
if (self->path)
free(self->path);
free(self);
}
return NULL;
}
static int mod_dso__load_sections(struct module *mod)
{
int count = 0, path_len;
struct dirent *entry;
char *line = NULL;
char *dir_path;
DIR *dir;
size_t n;
path_len = strlen("/sys/module/");
path_len += strlen(mod->name);
path_len += strlen("/sections/");
dir_path = calloc(1, path_len + 1);
if (dir_path == NULL)
goto out_failure;
strcat(dir_path, "/sys/module/");
strcat(dir_path, mod->name);
strcat(dir_path, "/sections/");
dir = opendir(dir_path);
if (dir == NULL)
goto out_free;
while ((entry = readdir(dir))) {
struct section *section;
char *path, *vma;
int line_len;
FILE *file;
if (!strcmp(".", entry->d_name) || !strcmp("..", entry->d_name))
continue;
path = calloc(1, path_len + strlen(entry->d_name) + 1);
if (path == NULL)
break;
strcat(path, dir_path);
strcat(path, entry->d_name);
file = fopen(path, "r");
if (file == NULL) {
free(path);
break;
}
line_len = getline(&line, &n, file);
if (line_len < 0) {
free(path);
fclose(file);
break;
}
if (!line) {
free(path);
fclose(file);
break;
}
line[--line_len] = '\0'; /* \n */
vma = strstr(line, "0x");
if (!vma) {
free(path);
fclose(file);
break;
}
vma += 2;
section = section__new(entry->d_name, path);
if (!section) {
fprintf(stderr, "load_sections: allocation error\n");
free(path);
fclose(file);
break;
}
hex2u64(vma, &section->vma);
sec_dso__insert_section(mod->sections, section);
free(path);
fclose(file);
count++;
}
closedir(dir);
free(line);
free(dir_path);
return count;
out_free:
free(dir_path);
out_failure:
return count;
}
static int mod_dso__load_module_paths(struct mod_dso *self)
{
struct utsname uts;
int count = 0, len, err = -1;
char *line = NULL;
FILE *file;
char *dpath, *dir;
size_t n;
if (uname(&uts) < 0)
return err;
len = strlen("/lib/modules/");
len += strlen(uts.release);
len += strlen("/modules.dep");
dpath = calloc(1, len + 1);
if (dpath == NULL)
return err;
strcat(dpath, "/lib/modules/");
strcat(dpath, uts.release);
strcat(dpath, "/modules.dep");
file = fopen(dpath, "r");
if (file == NULL)
goto out_failure;
dir = dirname(dpath);
if (!dir)
goto out_failure;
strcat(dir, "/");
while (!feof(file)) {
struct module *module;
char *name, *path, *tmp;
FILE *modfile;
int line_len;
line_len = getline(&line, &n, file);
if (line_len < 0)
break;
if (!line)
break;
line[--line_len] = '\0'; /* \n */
path = strchr(line, ':');
if (!path)
break;
*path = '\0';
path = strdup(line);
if (!path)
break;
if (!strstr(path, dir)) {
if (strncmp(path, "kernel/", 7))
break;
free(path);
path = calloc(1, strlen(dir) + strlen(line) + 1);
if (!path)
break;
strcat(path, dir);
strcat(path, line);
}
modfile = fopen(path, "r");
if (modfile == NULL)
break;
fclose(modfile);
name = strdup(path);
if (!name)
break;
name = strtok(name, "/");
tmp = name;
while (tmp) {
tmp = strtok(NULL, "/");
if (tmp)
name = tmp;
}
name = strsep(&name, ".");
if (!name)
break;
/* Quirk: replace '-' with '_' in all modules */
for (len = strlen(name); len; len--) {
if (*(name+len) == '-')
*(name+len) = '_';
}
module = module__new(name, path);
if (!module)
break;
mod_dso__insert_module(self, module);
module->sections = sec_dso__new_dso("sections");
if (!module->sections)
break;
module->active = mod_dso__load_sections(module);
if (module->active > 0)
count++;
}
if (feof(file))
err = count;
else
fprintf(stderr, "load_module_paths: modules.dep parsing failure!\n");
out_failure:
if (dpath)
free(dpath);
if (file)
fclose(file);
if (line)
free(line);
return err;
}
int mod_dso__load_modules(struct mod_dso *dso)
{
int err;
err = mod_dso__load_module_paths(dso);
return err;
}
#ifndef __PERF_MODULE_
#define __PERF_MODULE_ 1
#include <linux/types.h>
#include "../types.h"
#include <linux/list.h>
#include <linux/rbtree.h>
struct section {
struct rb_node rb_node;
u64 hash;
u64 vma;
char *name;
char *path;
};
struct sec_dso {
struct list_head node;
struct rb_root secs;
struct section *(*find_section)(struct sec_dso *, const char *name);
char name[0];
};
struct module {
struct rb_node rb_node;
u64 hash;
char *name;
char *path;
struct sec_dso *sections;
int active;
};
struct mod_dso {
struct list_head node;
struct rb_root mods;
struct module *(*find_module)(struct mod_dso *, const char *name);
char name[0];
};
struct sec_dso *sec_dso__new_dso(const char *name);
void sec_dso__delete_sections(struct sec_dso *self);
void sec_dso__delete_self(struct sec_dso *self);
size_t sec_dso__fprintf(struct sec_dso *self, FILE *fp);
struct section *sec_dso__find_section(struct sec_dso *self, const char *name);
struct mod_dso *mod_dso__new_dso(const char *name);
void mod_dso__delete_modules(struct mod_dso *self);
void mod_dso__delete_self(struct mod_dso *self);
size_t mod_dso__fprintf(struct mod_dso *self, FILE *fp);
struct module *mod_dso__find_module(struct mod_dso *self, const char *name);
int mod_dso__load_modules(struct mod_dso *dso);
#endif /* __PERF_MODULE_ */
......@@ -129,20 +129,32 @@ sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width)
int64_t
sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
{
struct dso *dso_l = left->dso;
struct dso *dso_r = right->dso;
struct dso *dso_l = left->map ? left->map->dso : NULL;
struct dso *dso_r = right->map ? right->map->dso : NULL;
const char *dso_name_l, *dso_name_r;
if (!dso_l || !dso_r)
return cmp_null(dso_l, dso_r);
return strcmp(dso_l->name, dso_r->name);
if (verbose) {
dso_name_l = dso_l->long_name;
dso_name_r = dso_r->long_name;
} else {
dso_name_l = dso_l->short_name;
dso_name_r = dso_r->short_name;
}
return strcmp(dso_name_l, dso_name_r);
}
size_t
sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width)
{
if (self->dso)
return repsep_fprintf(fp, "%-*s", width, self->dso->name);
if (self->map && self->map->dso) {
const char *dso_name = !verbose ? self->map->dso->short_name :
self->map->dso->long_name;
return repsep_fprintf(fp, "%-*s", width, dso_name);
}
return repsep_fprintf(fp, "%*llx", width, (u64)self->ip);
}
......@@ -169,20 +181,16 @@ sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used)
{
size_t ret = 0;
if (verbose)
ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip,
dso__symtab_origin(self->dso));
if (verbose) {
char o = self->map ? dso__symtab_origin(self->map->dso) : '!';
ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, o);
}
ret += repsep_fprintf(fp, "[%c] ", self->level);
if (self->sym) {
if (self->sym)
ret += repsep_fprintf(fp, "%s", self->sym->name);
if (self->sym->module)
ret += repsep_fprintf(fp, "\t[%s]",
self->sym->module->name);
} else {
else
ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip);
}
return ret;
}
......
......@@ -42,18 +42,15 @@ extern unsigned int threads__col_width;
struct hist_entry {
struct rb_node rb_node;
u64 count;
struct thread *thread;
struct map *map;
struct dso *dso;
struct symbol *sym;
struct symbol *parent;
u64 ip;
char level;
struct symbol *parent;
struct callchain_node callchain;
struct rb_root sorted_chain;
u64 count;
};
/*
......
此差异已折叠。
......@@ -5,7 +5,6 @@
#include "types.h"
#include <linux/list.h>
#include <linux/rbtree.h>
#include "module.h"
#include "event.h"
#ifdef HAVE_CPLUS_DEMANGLE
......@@ -36,10 +35,8 @@ struct symbol {
struct rb_node rb_node;
u64 start;
u64 end;
u64 obj_start;
u64 hist_sum;
u64 *hist;
struct module *module;
void *priv;
char name[0];
};
......@@ -52,12 +49,14 @@ struct dso {
unsigned char adjust_symbols;
unsigned char slen_calculated;
unsigned char origin;
const char *short_name;
char *long_name;
char name[0];
};
extern const char *sym_hist_filter;
typedef int (*symbol_filter_t)(struct dso *self, struct symbol *sym);
typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym);
struct dso *dso__new(const char *name, unsigned int sym_priv_size);
void dso__delete(struct dso *self);
......@@ -69,10 +68,12 @@ static inline void *dso__sym_priv(struct dso *self, struct symbol *sym)
struct symbol *dso__find_symbol(struct dso *self, u64 ip);
int dso__load_kernel(struct dso *self, const char *vmlinux,
symbol_filter_t filter, int verbose, int modules);
int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose);
int dso__load(struct dso *self, symbol_filter_t filter, int verbose);
int dsos__load_kernel(const char *vmlinux, unsigned int sym_priv_size,
symbol_filter_t filter, int verbose, int modules);
int dsos__load_modules(unsigned int sym_priv_size, symbol_filter_t filter,
int verbose);
int dso__load(struct dso *self, struct map *map, symbol_filter_t filter,
int verbose);
struct dso *dsos__findnew(const char *name);
void dsos__fprintf(FILE *fp);
......@@ -84,9 +85,8 @@ int load_kernel(void);
void symbol__init(void);
extern struct list_head dsos;
extern struct dso *kernel_dso;
extern struct map *kernel_map;
extern struct dso *vdso;
extern struct dso *hypervisor_dso;
extern const char *vmlinux_name;
extern int modules;
#endif /* __PERF_SYMBOL */
......@@ -16,6 +16,7 @@ static struct thread *thread__new(pid_t pid)
if (self->comm)
snprintf(self->comm, 32, ":%d", self->pid);
self->maps = RB_ROOT;
INIT_LIST_HEAD(&self->removed_maps);
}
return self;
......@@ -32,13 +33,20 @@ int thread__set_comm(struct thread *self, const char *comm)
static size_t thread__fprintf(struct thread *self, FILE *fp)
{
struct rb_node *nd;
size_t ret = fprintf(fp, "Thread %d %s\n", self->pid, self->comm);
struct map *pos;
size_t ret = fprintf(fp, "Thread %d %s\nCurrent maps:\n",
self->pid, self->comm);
for (nd = rb_first(&self->maps); nd; nd = rb_next(nd)) {
struct map *pos = rb_entry(nd, struct map, rb_node);
pos = rb_entry(nd, struct map, rb_node);
ret += map__fprintf(pos, fp);
}
ret = fprintf(fp, "Removed maps:\n");
list_for_each_entry(pos, &self->removed_maps, node)
ret += map__fprintf(pos, fp);
return ret;
}
......@@ -112,21 +120,13 @@ static void thread__remove_overlappings(struct thread *self, struct map *map)
map__fprintf(pos, stdout);
}
if (map->start <= pos->start && map->end > pos->start)
pos->start = map->end;
if (map->end >= pos->end && map->start < pos->end)
pos->end = map->start;
if (verbose >= 2) {
printf("after collision:\n");
map__fprintf(pos, stdout);
}
if (pos->start >= pos->end) {
rb_erase(&pos->rb_node, &self->maps);
free(pos);
}
rb_erase(&pos->rb_node, &self->maps);
/*
* We may have references to this map, for instance in some
* hist_entry instances, so just move them to a separate
* list.
*/
list_add_tail(&pos->node, &self->removed_maps);
}
}
......
......@@ -8,6 +8,7 @@
struct thread {
struct rb_node rb_node;
struct rb_root maps;
struct list_head removed_maps;
pid_t pid;
char shortname[3];
char *comm;
......@@ -25,6 +26,9 @@ size_t threads__fprintf(FILE *fp, struct rb_root *threads);
void maps__insert(struct rb_root *maps, struct map *map);
struct map *maps__find(struct rb_root *maps, u64 ip);
struct symbol *kernel_maps__find_symbol(const u64 ip, struct map **mapp);
struct map *kernel_maps__find_by_dso_name(const char *name);
static inline struct map *thread__find_map(struct thread *self, u64 ip)
{
return self ? maps__find(&self->maps, ip) : NULL;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册