/*
* builtin-report.c
*
* Builtin report command: Analyze the perf.data input file,
* look up and read DSOs and symbol information and display
* a histogram of results, along various sorting keys.
*/
#include "builtin.h"
#include "util/util.h"
#include "util/color.h"
#include "util/list.h"
#include "util/cache.h"
#include "util/rbtree.h"
#include "util/symbol.h"
#include "util/string.h"
#include "perf.h"
#include "util/parse-options.h"
#include "util/parse-events.h"
#define SHOW_KERNEL 1
#define SHOW_USER 2
#define SHOW_HV 4
static char const *input_name = "perf.data";
static char *vmlinux = NULL;
static char default_sort_order[] = "comm,dso";
static char *sort_order = default_sort_order;
static int input;
static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
static int dump_trace = 0;
#define dprintf(x...) do { if (dump_trace) printf(x); } while (0)
#define cdprintf(x...) do { if (dump_trace) color_fprintf(stdout, color, x); } while (0)
static int verbose;
#define eprintf(x...) do { if (verbose) fprintf(stderr, x); } while (0)
static int full_paths;
static unsigned long page_size;
static unsigned long mmap_window = 32;
static char default_parent_pattern[] = "^sys_|^do_page_fault";
static char *parent_pattern = default_parent_pattern;
static regex_t parent_regex;
static int exclude_other = 1;
struct ip_event {
struct perf_event_header header;
u64 ip;
u32 pid, tid;
unsigned char __more_data[];
};
struct ip_callchain {
u64 nr;
u64 ips[0];
};
struct mmap_event {
struct perf_event_header header;
u32 pid, tid;
u64 start;
u64 len;
u64 pgoff;
char filename[PATH_MAX];
};
struct comm_event {
struct perf_event_header header;
u32 pid, tid;
char comm[16];
};
struct fork_event {
struct perf_event_header header;
u32 pid, ppid;
};
struct period_event {
struct perf_event_header header;
u64 time;
u64 id;
u64 sample_period;
};
struct lost_event {
struct perf_event_header header;
u64 id;
u64 lost;
};
typedef union event_union {
struct perf_event_header header;
struct ip_event ip;
struct mmap_event mmap;
struct comm_event comm;
struct fork_event fork;
struct period_event period;
struct lost_event lost;
} event_t;
static LIST_HEAD(dsos);
static struct dso *kernel_dso;
static struct dso *vdso;
static void dsos__add(struct dso *dso)
{
list_add_tail(&dso->node, &dsos);
}
static struct dso *dsos__find(const char *name)
{
struct dso *pos;
list_for_each_entry(pos, &dsos, node)
if (strcmp(pos->name, name) == 0)
return pos;
return NULL;
}
static struct dso *dsos__findnew(const char *name)
{
struct dso *dso = dsos__find(name);
int nr;
if (dso)
return dso;
dso = dso__new(name, 0);
if (!dso)
goto out_delete_dso;
nr = dso__load(dso, NULL, verbose);
if (nr < 0) {
eprintf("Failed to open: %s\n", name);
goto out_delete_dso;
}
if (!nr)
eprintf("No symbols found in: %s, maybe install a debug package?\n", name);
dsos__add(dso);
return dso;
out_delete_dso:
dso__delete(dso);
return NULL;
}
static void dsos__fprintf(FILE *fp)
{
struct dso *pos;
list_for_each_entry(pos, &dsos, node)
dso__fprintf(pos, fp);
}
static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip)
{
return dso__find_symbol(kernel_dso, ip);
}
static int load_kernel(void)
{
int err;
kernel_dso = dso__new("[kernel]", 0);
if (!kernel_dso)
return -1;
err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose);
if (err) {
dso__delete(kernel_dso);
kernel_dso = NULL;
} else
dsos__add(kernel_dso);
vdso = dso__new("[vdso]", 0);
if (!vdso)
return -1;
vdso->find_symbol = vdso__find_symbol;
dsos__add(vdso);
return err;
}
static char __cwd[PATH_MAX];
static char *cwd = __cwd;
static int cwdlen;
static int strcommon(const char *pathname)
{
int n = 0;
while (pathname[n] == cwd[n] && n < cwdlen)
++n;
return n;
}
struct map {
struct list_head node;
u64 start;
u64 end;
u64 pgoff;
u64 (*map_ip)(struct map *, u64);
struct dso *dso;
};
static u64 map__map_ip(struct map *map, u64 ip)
{
return ip - map->start + map->pgoff;
}
static u64 vdso__map_ip(struct map *map, u64 ip)
{
return ip;
}
static inline int is_anon_memory(const char *filename)
{
return strcmp(filename, "//anon") == 0;
}
static struct map *map__new(struct mmap_event *event)
{
struct map *self = malloc(sizeof(*self));
if (self != NULL) {
const char *filename = event->filename;
char newfilename[PATH_MAX];
int anon;
if (cwd) {
int n = strcommon(filename);
if (n == cwdlen) {
snprintf(newfilename, sizeof(newfilename),
".%s", filename + n);
filename = newfilename;
}
}
anon = is_anon_memory(filename);
if (anon) {
snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", event->pid);
filename = newfilename;
}
self->start = event->start;
self->end = event->start + event->len;
self->pgoff = event->pgoff;
self->dso = dsos__findnew(filename);
if (self->dso == NULL)
goto out_delete;
if (self->dso == vdso || anon)
self->map_ip = vdso__map_ip;
else
self->map_ip = map__map_ip;
}
return self;
out_delete:
free(self);
return NULL;
}
static struct map *map__clone(struct map *self)
{
struct map *map = malloc(sizeof(*self));
if (!map)
return NULL;
memcpy(map, self, sizeof(*self));
return map;
}
static int map__overlap(struct map *l, struct map *r)
{
if (l->start > r->start) {
struct map *t = l;
l = r;
r = t;
}
if (l->end > r->start)
return 1;
return 0;
}
static size_t map__fprintf(struct map *self, FILE *fp)
{
return fprintf(fp, " %Lx-%
|