summaryrefslogtreecommitdiff
path: root/tools/perf/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/data_map.c31
-rw-r--r--tools/perf/util/data_map.h1
-rw-r--r--tools/perf/util/debug.h2
-rw-r--r--tools/perf/util/debugfs.c241
-rw-r--r--tools/perf/util/debugfs.h25
-rw-r--r--tools/perf/util/event.c177
-rw-r--r--tools/perf/util/event.h23
-rw-r--r--tools/perf/util/header.c342
-rw-r--r--tools/perf/util/header.h46
-rw-r--r--tools/perf/util/include/linux/bitmap.h1
-rw-r--r--tools/perf/util/include/linux/ctype.h2
-rw-r--r--tools/perf/util/map.c84
-rw-r--r--tools/perf/util/parse-events.c43
-rw-r--r--tools/perf/util/string.c84
-rw-r--r--tools/perf/util/string.h1
-rw-r--r--tools/perf/util/symbol.c286
-rw-r--r--tools/perf/util/symbol.h49
-rw-r--r--tools/perf/util/util.h3
18 files changed, 1173 insertions, 268 deletions
diff --git a/tools/perf/util/data_map.c b/tools/perf/util/data_map.c
index 18accb8fee4d..14cb8465eb08 100644
--- a/tools/perf/util/data_map.c
+++ b/tools/perf/util/data_map.c
@@ -70,6 +70,35 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
}
}
+int perf_header__read_build_ids(int input, off_t offset, off_t size)
+{
+ struct build_id_event bev;
+ char filename[PATH_MAX];
+ off_t limit = offset + size;
+ int err = -1;
+
+ while (offset < limit) {
+ struct dso *dso;
+ ssize_t len;
+
+ if (read(input, &bev, sizeof(bev)) != sizeof(bev))
+ goto out;
+
+ len = bev.header.size - sizeof(bev);
+ if (read(input, filename, len) != len)
+ goto out;
+
+ dso = dsos__findnew(filename);
+ if (dso != NULL)
+ dso__set_build_id(dso, &bev.build_id);
+
+ offset += bev.header.size;
+ }
+ err = 0;
+out:
+ return err;
+}
+
int mmap_dispatch_perf_file(struct perf_header **pheader,
const char *input_name,
int force,
@@ -130,7 +159,7 @@ int mmap_dispatch_perf_file(struct perf_header **pheader,
if (curr_handler->sample_type_check(sample_type) < 0)
exit(-1);
- if (load_kernel(0, NULL) < 0) {
+ if (load_kernel(NULL) < 0) {
perror("failed to load kernel symbols");
return EXIT_FAILURE;
}
diff --git a/tools/perf/util/data_map.h b/tools/perf/util/data_map.h
index 716d1053b074..ae036ecd7625 100644
--- a/tools/perf/util/data_map.h
+++ b/tools/perf/util/data_map.h
@@ -27,5 +27,6 @@ int mmap_dispatch_perf_file(struct perf_header **pheader,
int full_paths,
int *cwdlen,
char **cwd);
+int perf_header__read_build_ids(int input, off_t offset, off_t file_size);
#endif
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index e8b18a1f87a4..c6c24c522dea 100644
--- a/tools/perf/util/debug.h
+++ b/tools/perf/util/debug.h
@@ -2,6 +2,8 @@
#ifndef __PERF_DEBUG_H
#define __PERF_DEBUG_H
+#include "event.h"
+
extern int verbose;
extern int dump_trace;
diff --git a/tools/perf/util/debugfs.c b/tools/perf/util/debugfs.c
new file mode 100644
index 000000000000..06b73ee02c49
--- /dev/null
+++ b/tools/perf/util/debugfs.c
@@ -0,0 +1,241 @@
+#include "util.h"
+#include "debugfs.h"
+#include "cache.h"
+
+static int debugfs_premounted;
+static char debugfs_mountpoint[MAX_PATH+1];
+
+static const char *debugfs_known_mountpoints[] = {
+ "/sys/kernel/debug/",
+ "/debug/",
+ 0,
+};
+
+/* use this to force a umount */
+void debugfs_force_cleanup(void)
+{
+ debugfs_find_mountpoint();
+ debugfs_premounted = 0;
+ debugfs_umount();
+}
+
+/* construct a full path to a debugfs element */
+int debugfs_make_path(const char *element, char *buffer, int size)
+{
+ int len;
+
+ if (strlen(debugfs_mountpoint) == 0) {
+ buffer[0] = '\0';
+ return -1;
+ }
+
+ len = strlen(debugfs_mountpoint) + strlen(element) + 1;
+ if (len >= size)
+ return len+1;
+
+ snprintf(buffer, size-1, "%s/%s", debugfs_mountpoint, element);
+ return 0;
+}
+
+static int debugfs_found;
+
+/* find the path to the mounted debugfs */
+const char *debugfs_find_mountpoint(void)
+{
+ const char **ptr;
+ char type[100];
+ FILE *fp;
+
+ if (debugfs_found)
+ return (const char *) debugfs_mountpoint;
+
+ ptr = debugfs_known_mountpoints;
+ while (*ptr) {
+ if (debugfs_valid_mountpoint(*ptr) == 0) {
+ debugfs_found = 1;
+ strcpy(debugfs_mountpoint, *ptr);
+ return debugfs_mountpoint;
+ }
+ ptr++;
+ }
+
+ /* give up and parse /proc/mounts */
+ fp = fopen("/proc/mounts", "r");
+ if (fp == NULL)
+ die("Can't open /proc/mounts for read");
+
+ while (fscanf(fp, "%*s %"
+ STR(MAX_PATH)
+ "s %99s %*s %*d %*d\n",
+ debugfs_mountpoint, type) == 2) {
+ if (strcmp(type, "debugfs") == 0)
+ break;
+ }
+ fclose(fp);
+
+ if (strcmp(type, "debugfs") != 0)
+ return NULL;
+
+ debugfs_found = 1;
+
+ return debugfs_mountpoint;
+}
+
+/* verify that a mountpoint is actually a debugfs instance */
+
+int debugfs_valid_mountpoint(const char *debugfs)
+{
+ struct statfs st_fs;
+
+ if (statfs(debugfs, &st_fs) < 0)
+ return -ENOENT;
+ else if (st_fs.f_type != (long) DEBUGFS_MAGIC)
+ return -ENOENT;
+
+ return 0;
+}
+
+
+int debugfs_valid_entry(const char *path)
+{
+ struct stat st;
+
+ if (stat(path, &st))
+ return -errno;
+
+ return 0;
+}
+
+/* mount the debugfs somewhere */
+
+int debugfs_mount(const char *mountpoint)
+{
+ char mountcmd[128];
+
+ /* see if it's already mounted */
+ if (debugfs_find_mountpoint()) {
+ debugfs_premounted = 1;
+ return 0;
+ }
+
+ /* if not mounted and no argument */
+ if (mountpoint == NULL) {
+ /* see if environment variable set */
+ mountpoint = getenv(PERF_DEBUGFS_ENVIRONMENT);
+ /* if no environment variable, use default */
+ if (mountpoint == NULL)
+ mountpoint = "/sys/kernel/debug";
+ }
+
+ /* save the mountpoint */
+ strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint));
+
+ /* mount it */
+ snprintf(mountcmd, sizeof(mountcmd),
+ "/bin/mount -t debugfs debugfs %s", mountpoint);
+ return system(mountcmd);
+}
+
+/* umount the debugfs */
+
+int debugfs_umount(void)
+{
+ char umountcmd[128];
+ int ret;
+
+ /* if it was already mounted, leave it */
+ if (debugfs_premounted)
+ return 0;
+
+ /* make sure it's a valid mount point */
+ ret = debugfs_valid_mountpoint(debugfs_mountpoint);
+ if (ret)
+ return ret;
+
+ snprintf(umountcmd, sizeof(umountcmd),
+ "/bin/umount %s", debugfs_mountpoint);
+ return system(umountcmd);
+}
+
+int debugfs_write(const char *entry, const char *value)
+{
+ char path[MAX_PATH+1];
+ int ret, count;
+ int fd;
+
+ /* construct the path */
+ snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry);
+
+ /* verify that it exists */
+ ret = debugfs_valid_entry(path);
+ if (ret)
+ return ret;
+
+ /* get how many chars we're going to write */
+ count = strlen(value);
+
+ /* open the debugfs entry */
+ fd = open(path, O_RDWR);
+ if (fd < 0)
+ return -errno;
+
+ while (count > 0) {
+ /* write it */
+ ret = write(fd, value, count);
+ if (ret <= 0) {
+ if (ret == EAGAIN)
+ continue;
+ close(fd);
+ return -errno;
+ }
+ count -= ret;
+ }
+
+ /* close it */
+ close(fd);
+
+ /* return success */
+ return 0;
+}
+
+/*
+ * read a debugfs entry
+ * returns the number of chars read or a negative errno
+ */
+int debugfs_read(const char *entry, char *buffer, size_t size)
+{
+ char path[MAX_PATH+1];
+ int ret;
+ int fd;
+
+ /* construct the path */
+ snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry);
+
+ /* verify that it exists */
+ ret = debugfs_valid_entry(path);
+ if (ret)
+ return ret;
+
+ /* open the debugfs entry */
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -errno;
+
+ do {
+ /* read it */
+ ret = read(fd, buffer, size);
+ if (ret == 0) {
+ close(fd);
+ return EOF;
+ }
+ } while (ret < 0 && errno == EAGAIN);
+
+ /* close it */
+ close(fd);
+
+ /* make *sure* there's a null character at the end */
+ buffer[ret] = '\0';
+
+ /* return the number of chars read */
+ return ret;
+}
diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h
new file mode 100644
index 000000000000..3cd14f9ae784
--- /dev/null
+++ b/tools/perf/util/debugfs.h
@@ -0,0 +1,25 @@
+#ifndef __DEBUGFS_H__
+#define __DEBUGFS_H__
+
+#include <sys/mount.h>
+
+#ifndef MAX_PATH
+# define MAX_PATH 256
+#endif
+
+#ifndef STR
+# define _STR(x) #x
+# define STR(x) _STR(x)
+#endif
+
+extern const char *debugfs_find_mountpoint(void);
+extern int debugfs_valid_mountpoint(const char *debugfs);
+extern int debugfs_valid_entry(const char *path);
+extern int debugfs_mount(const char *mountpoint);
+extern int debugfs_umount(void);
+extern int debugfs_write(const char *entry, const char *value);
+extern int debugfs_read(const char *entry, char *buffer, size_t size);
+extern void debugfs_force_cleanup(void);
+extern int debugfs_make_path(const char *element, char *buffer, int size);
+
+#endif /* __DEBUGFS_H__ */
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
new file mode 100644
index 000000000000..1dae7e3b400d
--- /dev/null
+++ b/tools/perf/util/event.c
@@ -0,0 +1,177 @@
+#include <linux/types.h>
+#include "event.h"
+#include "debug.h"
+#include "string.h"
+
+static pid_t event__synthesize_comm(pid_t pid, int full,
+ int (*process)(event_t *event))
+{
+ event_t ev;
+ char filename[PATH_MAX];
+ char bf[BUFSIZ];
+ FILE *fp;
+ size_t size = 0;
+ DIR *tasks;
+ struct dirent dirent, *next;
+ pid_t tgid = 0;
+
+ snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+out_race:
+ /*
+ * We raced with a task exiting - just return:
+ */
+ pr_debug("couldn't open %s\n", filename);
+ return 0;
+ }
+
+ memset(&ev.comm, 0, sizeof(ev.comm));
+ while (!ev.comm.comm[0] || !ev.comm.pid) {
+ if (fgets(bf, sizeof(bf), fp) == NULL)
+ goto out_failure;
+
+ if (memcmp(bf, "Name:", 5) == 0) {
+ char *name = bf + 5;
+ while (*name && isspace(*name))
+ ++name;
+ size = strlen(name) - 1;
+ memcpy(ev.comm.comm, name, size++);
+ } else if (memcmp(bf, "Tgid:", 5) == 0) {
+ char *tgids = bf + 5;
+ while (*tgids && isspace(*tgids))
+ ++tgids;
+ tgid = ev.comm.pid = atoi(tgids);
+ }
+ }
+
+ ev.comm.header.type = PERF_RECORD_COMM;
+ size = ALIGN(size, sizeof(u64));
+ ev.comm.header.size = sizeof(ev.comm) - (sizeof(ev.comm.comm) - size);
+
+ if (!full) {
+ ev.comm.tid = pid;
+
+ process(&ev);
+ goto out_fclose;
+ }
+
+ snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
+
+ tasks = opendir(filename);
+ if (tasks == NULL)
+ goto out_race;
+
+ while (!readdir_r(tasks, &dirent, &next) && next) {
+ char *end;
+ pid = strtol(dirent.d_name, &end, 10);
+ if (*end)
+ continue;
+
+ ev.comm.tid = pid;
+
+ process(&ev);
+ }
+ closedir(tasks);
+
+out_fclose:
+ fclose(fp);
+ return tgid;
+
+out_failure:
+ pr_warning("couldn't get COMM and pgid, malformed %s\n", filename);
+ return -1;
+}
+
+static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
+ int (*process)(event_t *event))
+{
+ char filename[PATH_MAX];
+ FILE *fp;
+
+ snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ /*
+ * We raced with a task exiting - just return:
+ */
+ pr_debug("couldn't open %s\n", filename);
+ return -1;
+ }
+
+ while (1) {
+ char bf[BUFSIZ], *pbf = bf;
+ event_t ev = {
+ .header = { .type = PERF_RECORD_MMAP },
+ };
+ int n;
+ size_t size;
+ if (fgets(bf, sizeof(bf), fp) == NULL)
+ break;
+
+ /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
+ n = hex2u64(pbf, &ev.mmap.start);
+ if (n < 0)
+ continue;
+ pbf += n + 1;
+ n = hex2u64(pbf, &ev.mmap.len);
+ if (n < 0)
+ continue;
+ pbf += n + 3;
+ if (*pbf == 'x') { /* vm_exec */
+ char *execname = strchr(bf, '/');
+
+ /* Catch VDSO */
+ if (execname == NULL)
+ execname = strstr(bf, "[vdso]");
+
+ if (execname == NULL)
+ continue;
+
+ size = strlen(execname);
+ execname[size - 1] = '\0'; /* Remove \n */
+ memcpy(ev.mmap.filename, execname, size);
+ size = ALIGN(size, sizeof(u64));
+ ev.mmap.len -= ev.mmap.start;
+ ev.mmap.header.size = (sizeof(ev.mmap) -
+ (sizeof(ev.mmap.filename) - size));
+ ev.mmap.pid = tgid;
+ ev.mmap.tid = pid;
+
+ process(&ev);
+ }
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+int event__synthesize_thread(pid_t pid, int (*process)(event_t *event))
+{
+ pid_t tgid = event__synthesize_comm(pid, 1, process);
+ if (tgid == -1)
+ return -1;
+ return event__synthesize_mmap_events(pid, tgid, process);
+}
+
+void event__synthesize_threads(int (*process)(event_t *event))
+{
+ DIR *proc;
+ struct dirent dirent, *next;
+
+ proc = opendir("/proc");
+
+ while (!readdir_r(proc, &dirent, &next) && next) {
+ char *end;
+ pid_t pid = strtol(dirent.d_name, &end, 10);
+
+ if (*end) /* only interested in proper numerical dirents */
+ continue;
+
+ event__synthesize_thread(pid, process);
+ }
+
+ closedir(proc);
+}
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index d972b4b0d38c..1f771ce3a957 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -61,6 +61,20 @@ struct sample_event{
u64 array[];
};
+#define BUILD_ID_SIZE 20
+
+struct build_id_event {
+ struct perf_event_header header;
+ u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))];
+ char filename[];
+};
+
+struct build_id_list {
+ struct build_id_event event;
+ struct list_head list;
+ const char *dso_name;
+ int len;
+};
typedef union event_union {
struct perf_event_header header;
@@ -105,10 +119,15 @@ struct symbol;
typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym);
-struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen,
- unsigned int sym_priv_size, symbol_filter_t filter);
+void map__init(struct map *self, u64 start, u64 end, u64 pgoff,
+ struct dso *dso);
+struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen);
struct map *map__clone(struct map *self);
int map__overlap(struct map *l, struct map *r);
size_t map__fprintf(struct map *self, FILE *fp);
+struct symbol *map__find_symbol(struct map *self, u64 ip, symbol_filter_t filter);
+
+int event__synthesize_thread(pid_t pid, int (*process)(event_t *event));
+void event__synthesize_threads(int (*process)(event_t *event));
#endif /* __PERF_RECORD_H */
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 7d26659b806c..b01a9537977f 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -2,11 +2,15 @@
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
+#include <linux/list.h>
#include "util.h"
#include "header.h"
#include "../perf.h"
#include "trace-event.h"
+#include "symbol.h"
+#include "data_map.h"
+#include "debug.h"
/*
* Create new perf.data header attribute:
@@ -15,32 +19,43 @@ struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr)
{
struct perf_header_attr *self = malloc(sizeof(*self));
- if (!self)
- die("nomem");
-
- self->attr = *attr;
- self->ids = 0;
- self->size = 1;
- self->id = malloc(sizeof(u64));
-
- if (!self->id)
- die("nomem");
+ if (self != NULL) {
+ self->attr = *attr;
+ self->ids = 0;
+ self->size = 1;
+ self->id = malloc(sizeof(u64));
+ if (self->id == NULL) {
+ free(self);
+ self = NULL;
+ }
+ }
return self;
}
-void perf_header_attr__add_id(struct perf_header_attr *self, u64 id)
+void perf_header_attr__delete(struct perf_header_attr *self)
+{
+ free(self->id);
+ free(self);
+}
+
+int perf_header_attr__add_id(struct perf_header_attr *self, u64 id)
{
int pos = self->ids;
self->ids++;
if (self->ids > self->size) {
- self->size *= 2;
- self->id = realloc(self->id, self->size * sizeof(u64));
- if (!self->id)
- die("nomem");
+ int nsize = self->size * 2;
+ u64 *nid = realloc(self->id, nsize * sizeof(u64));
+
+ if (nid == NULL)
+ return -1;
+
+ self->size = nsize;
+ self->id = nid;
}
self->id[pos] = id;
+ return 0;
}
/*
@@ -50,34 +65,41 @@ struct perf_header *perf_header__new(void)
{
struct perf_header *self = calloc(sizeof(*self), 1);
- if (!self)
- die("nomem");
-
- self->size = 1;
- self->attr = malloc(sizeof(void *));
+ if (self != NULL) {
+ self->size = 1;
+ self->attr = malloc(sizeof(void *));
- if (!self->attr)
- die("nomem");
+ if (self->attr == NULL) {
+ free(self);
+ self = NULL;
+ }
+ }
return self;
}
-void perf_header__add_attr(struct perf_header *self,
- struct perf_header_attr *attr)
+int perf_header__add_attr(struct perf_header *self,
+ struct perf_header_attr *attr)
{
int pos = self->attrs;
if (self->frozen)
- die("frozen");
+ return -1;
self->attrs++;
if (self->attrs > self->size) {
- self->size *= 2;
- self->attr = realloc(self->attr, self->size * sizeof(void *));
- if (!self->attr)
- die("nomem");
+ int nsize = self->size * 2;
+ struct perf_header_attr **nattr;
+
+ nattr = realloc(self->attr, nsize * sizeof(void *));
+ if (nattr == NULL)
+ return -1;
+
+ self->size = nsize;
+ self->attr = nattr;
}
self->attr[pos] = attr;
+ return 0;
}
#define MAX_EVENT_NAME 64
@@ -124,71 +146,110 @@ static const char *__perf_magic = "PERFFILE";
#define PERF_MAGIC (*(u64 *)__perf_magic)
-struct perf_file_section {
- u64 offset;
- u64 size;
-};
-
struct perf_file_attr {
struct perf_event_attr attr;
struct perf_file_section ids;
};
-struct perf_file_header {
- u64 magic;
- u64 size;
- u64 attr_size;
- struct perf_file_section attrs;
- struct perf_file_section data;
- struct perf_file_section event_types;
- DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
-};
+void perf_header__set_feat(struct perf_header *self, int feat)
+{
+ set_bit(feat, self->adds_features);
+}
-void perf_header__feat_trace_info(struct perf_header *header)
+bool perf_header__has_feat(const struct perf_header *self, int feat)
{
- set_bit(HEADER_TRACE_INFO, header->adds_features);
+ return test_bit(feat, self->adds_features);
}
-static void do_write(int fd, void *buf, size_t size)
+static int do_write(int fd, const void *buf, size_t size)
{
while (size) {
int ret = write(fd, buf, size);
if (ret < 0)
- die("failed to write");
+ return -1;
size -= ret;
buf += ret;
}
+
+ return 0;
+}
+
+static int write_buildid_table(int fd, struct list_head *id_head)
+{
+ struct build_id_list *iter, *next;
+
+ list_for_each_entry_safe(iter, next, id_head, list) {
+ struct build_id_event *b = &iter->event;
+
+ if (do_write(fd, b, sizeof(*b)) < 0 ||
+ do_write(fd, iter->dso_name, iter->len) < 0)
+ return -1;
+ list_del(&iter->list);
+ free(iter);
+ }
+
+ return 0;
}
-static void perf_header__adds_write(struct perf_header *self, int fd)
+static void
+perf_header__adds_write(struct perf_header *self, int fd)
{
- struct perf_file_section trace_sec;
- u64 cur_offset = lseek(fd, 0, SEEK_CUR);
- unsigned long *feat_mask = self->adds_features;
+ LIST_HEAD(id_list);
+ int nr_sections;
+ struct perf_file_section *feat_sec;
+ int sec_size;
+ u64 sec_start;
+ int idx = 0;
+
+ if (fetch_build_id_table(&id_list))
+ perf_header__set_feat(self, HEADER_BUILD_ID);
+
+ nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);
+ if (!nr_sections)
+ return;
+
+ feat_sec = calloc(sizeof(*feat_sec), nr_sections);
+ if (!feat_sec)
+ die("No memory");
+
+ sec_size = sizeof(*feat_sec) * nr_sections;
+
+ sec_start = self->data_offset + self->data_size;
+ lseek(fd, sec_start + sec_size, SEEK_SET);
+
+ if (perf_header__has_feat(self, HEADER_TRACE_INFO)) {
+ struct perf_file_section *trace_sec;
+
+ trace_sec = &feat_sec[idx++];
- if (test_bit(HEADER_TRACE_INFO, feat_mask)) {
/* Write trace info */
- trace_sec.offset = lseek(fd, sizeof(trace_sec), SEEK_CUR);
+ trace_sec->offset = lseek(fd, 0, SEEK_CUR);
read_tracing_data(fd, attrs, nr_counters);
- trace_sec.size = lseek(fd, 0, SEEK_CUR) - trace_sec.offset;
-
- /* Write trace info headers */
- lseek(fd, cur_offset, SEEK_SET);
- do_write(fd, &trace_sec, sizeof(trace_sec));
-
- /*
- * Update cur_offset. So that other (future)
- * features can set their own infos in this place. But if we are
- * the only feature, at least that seeks to the place the data
- * should begin.
- */
- cur_offset = lseek(fd, trace_sec.offset + trace_sec.size, SEEK_SET);
+ trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset;
}
-};
-void perf_header__write(struct perf_header *self, int fd)
+
+ if (perf_header__has_feat(self, HEADER_BUILD_ID)) {
+ struct perf_file_section *buildid_sec;
+
+ buildid_sec = &feat_sec[idx++];
+
+ /* Write build-ids */
+ buildid_sec->offset = lseek(fd, 0, SEEK_CUR);
+ if (write_buildid_table(fd, &id_list) < 0)
+ die("failed to write buildid table");
+ buildid_sec->size = lseek(fd, 0, SEEK_CUR) - buildid_sec->offset;
+ }
+
+ lseek(fd, sec_start, SEEK_SET);
+ if (do_write(fd, feat_sec, sec_size) < 0)
+ die("failed to write feature section");
+ free(feat_sec);
+}
+
+void perf_header__write(struct perf_header *self, int fd, bool at_exit)
{
struct perf_file_header f_header;
struct perf_file_attr f_attr;
@@ -202,7 +263,8 @@ void perf_header__write(struct perf_header *self, int fd)
attr = self->attr[i];
attr->id_offset = lseek(fd, 0, SEEK_CUR);
- do_write(fd, attr->id, attr->ids * sizeof(u64));
+ if (do_write(fd, attr->id, attr->ids * sizeof(u64)) < 0)
+ die("failed to write perf header");
}
@@ -218,18 +280,21 @@ void perf_header__write(struct perf_header *self, int fd)
.size = attr->ids * sizeof(u64),
}
};
- do_write(fd, &f_attr, sizeof(f_attr));
+ if (do_write(fd, &f_attr, sizeof(f_attr)) < 0)
+ die("failed to write perf header attribute");
}
self->event_offset = lseek(fd, 0, SEEK_CUR);
self->event_size = event_count * sizeof(struct perf_trace_event_type);
if (events)
- do_write(fd, events, self->event_size);
-
- perf_header__adds_write(self, fd);
+ if (do_write(fd, events, self->event_size) < 0)
+ die("failed to write perf header events");
self->data_offset = lseek(fd, 0, SEEK_CUR);
+ if (at_exit)
+ perf_header__adds_write(self, fd);
+
f_header = (struct perf_file_header){
.magic = PERF_MAGIC,
.size = sizeof(f_header),
@@ -251,7 +316,8 @@ void perf_header__write(struct perf_header *self, int fd)
memcpy(&f_header.adds_features, &self->adds_features, sizeof(self->adds_features));
lseek(fd, 0, SEEK_SET);
- do_write(fd, &f_header, sizeof(f_header));
+ if (do_write(fd, &f_header, sizeof(f_header)) < 0)
+ die("failed to write perf header");
lseek(fd, self->data_offset + self->data_size, SEEK_SET);
self->frozen = 1;
@@ -272,43 +338,112 @@ static void do_read(int fd, void *buf, size_t size)
}
}
-static void perf_header__adds_read(struct perf_header *self, int fd)
+int perf_header__process_sections(struct perf_header *self, int fd,
+ int (*process)(struct perf_file_section *self,
+ int feat, int fd))
{
- const unsigned long *feat_mask = self->adds_features;
+ struct perf_file_section *feat_sec;
+ int nr_sections;
+ int sec_size;
+ int idx = 0;
+ int err = 0, feat = 1;
- if (test_bit(HEADER_TRACE_INFO, feat_mask)) {
- struct perf_file_section trace_sec;
+ nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);
+ if (!nr_sections)
+ return 0;
- do_read(fd, &trace_sec, sizeof(trace_sec));
- lseek(fd, trace_sec.offset, SEEK_SET);
- trace_report(fd);
- lseek(fd, trace_sec.offset + trace_sec.size, SEEK_SET);
+ feat_sec = calloc(sizeof(*feat_sec), nr_sections);
+ if (!feat_sec)
+ return -1;
+
+ sec_size = sizeof(*feat_sec) * nr_sections;
+
+ lseek(fd, self->data_offset + self->data_size, SEEK_SET);
+
+ do_read(fd, feat_sec, sec_size);
+
+ while (idx < nr_sections && feat < HEADER_LAST_FEATURE) {
+ if (perf_header__has_feat(self, feat)) {
+ struct perf_file_section *sec = &feat_sec[idx++];
+
+ err = process(sec, feat, fd);
+ if (err < 0)
+ break;
+ }
+ ++feat;
}
+
+ free(feat_sec);
+ return err;
};
+int perf_file_header__read(struct perf_file_header *self,
+ struct perf_header *ph, int fd)
+{
+ lseek(fd, 0, SEEK_SET);
+ do_read(fd, self, sizeof(*self));
+
+ if (self->magic != PERF_MAGIC ||
+ self->attr_size != sizeof(struct perf_file_attr))
+ return -1;
+
+ if (self->size != sizeof(*self)) {
+ /* Support the previous format */
+ if (self->size == offsetof(typeof(*self), adds_features))
+ bitmap_zero(self->adds_features, HEADER_FEAT_BITS);
+ else
+ return -1;
+ }
+
+ memcpy(&ph->adds_features, &self->adds_features,
+ sizeof(self->adds_features));
+
+ ph->event_offset = self->event_types.offset;
+ ph->event_size = self->event_types.size;
+ ph->data_offset = self->data.offset;
+ ph->data_size = self->data.size;
+ return 0;
+}
+
+static int perf_file_section__process(struct perf_file_section *self,
+ int feat, int fd)
+{
+ if (lseek(fd, self->offset, SEEK_SET) < 0) {
+ pr_debug("Failed to lseek to %Ld offset for feature %d, "
+ "continuing...\n", self->offset, feat);
+ return 0;
+ }
+
+ switch (feat) {
+ case HEADER_TRACE_INFO:
+ trace_report(fd);
+ break;
+
+ case HEADER_BUILD_ID:
+ if (perf_header__read_build_ids(fd, self->offset, self->size))
+ pr_debug("Failed to read buildids, continuing...\n");
+ break;
+ default:
+ pr_debug("unknown feature %d, continuing...\n", feat);
+ }
+
+ return 0;
+}
+
struct perf_header *perf_header__read(int fd)
{
struct perf_header *self = perf_header__new();
struct perf_file_header f_header;
struct perf_file_attr f_attr;
u64 f_id;
-
int nr_attrs, nr_ids, i, j;
- lseek(fd, 0, SEEK_SET);
- do_read(fd, &f_header, sizeof(f_header));
+ if (self == NULL)
+ die("nomem");
- if (f_header.magic != PERF_MAGIC ||
- f_header.attr_size != sizeof(f_attr))
+ if (perf_file_header__read(&f_header, self, fd) < 0)
die("incompatible file format");
- if (f_header.size != sizeof(f_header)) {
- /* Support the previous format */
- if (f_header.size == offsetof(typeof(f_header), adds_features))
- bitmap_zero(f_header.adds_features, HEADER_FEAT_BITS);
- else
- die("incompatible file format");
- }
nr_attrs = f_header.attrs.size / sizeof(f_attr);
lseek(fd, f_header.attrs.offset, SEEK_SET);
@@ -320,6 +455,8 @@ struct perf_header *perf_header__read(int fd)
tmp = lseek(fd, 0, SEEK_CUR);
attr = perf_header_attr__new(&f_attr.attr);
+ if (attr == NULL)
+ die("nomem");
nr_ids = f_attr.ids.size / sizeof(u64);
lseek(fd, f_attr.ids.offset, SEEK_SET);
@@ -327,9 +464,12 @@ struct perf_header *perf_header__read(int fd)
for (j = 0; j < nr_ids; j++) {
do_read(fd, &f_id, sizeof(f_id));
- perf_header_attr__add_id(attr, f_id);
+ if (perf_header_attr__add_id(attr, f_id) < 0)
+ die("nomem");
}
- perf_header__add_attr(self, attr);
+ if (perf_header__add_attr(self, attr) < 0)
+ die("nomem");
+
lseek(fd, tmp, SEEK_SET);
}
@@ -342,15 +482,7 @@ struct perf_header *perf_header__read(int fd)
event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type);
}
- memcpy(&self->adds_features, &f_header.adds_features, sizeof(f_header.adds_features));
-
- perf_header__adds_read(self, fd);
-
- self->event_offset = f_header.event_types.offset;
- self->event_size = f_header.event_types.size;
-
- self->data_offset = f_header.data.offset;
- self->data_size = f_header.data.size;
+ perf_header__process_sections(self, fd, perf_file_section__process);
lseek(fd, self->data_offset, SEEK_SET);
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 2ea9dfb1236a..f46a94e09eea 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -3,6 +3,7 @@
#include "../../../include/linux/perf_event.h"
#include <sys/types.h>
+#include <stdbool.h>
#include "types.h"
#include <linux/bitmap.h>
@@ -14,10 +15,34 @@ struct perf_header_attr {
off_t id_offset;
};
-#define HEADER_TRACE_INFO 1
+enum {
+ HEADER_TRACE_INFO = 1,
+ HEADER_BUILD_ID,
+ HEADER_LAST_FEATURE,
+};
#define HEADER_FEAT_BITS 256
+struct perf_file_section {
+ u64 offset;
+ u64 size;
+};
+
+struct perf_file_header {
+ u64 magic;
+ u64 size;
+ u64 attr_size;
+ struct perf_file_section attrs;
+ struct perf_file_section data;
+ struct perf_file_section event_types;
+ DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
+};
+
+struct perf_header;
+
+int perf_file_header__read(struct perf_file_header *self,
+ struct perf_header *ph, int fd);
+
struct perf_header {
int frozen;
int attrs, size;
@@ -31,24 +56,29 @@ struct perf_header {
};
struct perf_header *perf_header__read(int fd);
-void perf_header__write(struct perf_header *self, int fd);
+void perf_header__write(struct perf_header *self, int fd, bool at_exit);
-void perf_header__add_attr(struct perf_header *self,
- struct perf_header_attr *attr);
+int perf_header__add_attr(struct perf_header *self,
+ struct perf_header_attr *attr);
void perf_header__push_event(u64 id, const char *name);
char *perf_header__find_event(u64 id);
+struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr);
+void perf_header_attr__delete(struct perf_header_attr *self);
-struct perf_header_attr *
-perf_header_attr__new(struct perf_event_attr *attr);
-void perf_header_attr__add_id(struct perf_header_attr *self, u64 id);
+int perf_header_attr__add_id(struct perf_header_attr *self, u64 id);
u64 perf_header__sample_type(struct perf_header *header);
struct perf_event_attr *
perf_header__find_attr(u64 id, struct perf_header *header);
-void perf_header__feat_trace_info(struct perf_header *header);
+void perf_header__set_feat(struct perf_header *self, int feat);
+bool perf_header__has_feat(const struct perf_header *self, int feat);
struct perf_header *perf_header__new(void);
+int perf_header__process_sections(struct perf_header *self, int fd,
+ int (*process)(struct perf_file_section *self,
+ int feat, int fd));
+
#endif /* __PERF_HEADER_H */
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h
index 821c1033bccf..94507639a8c4 100644
--- a/tools/perf/util/include/linux/bitmap.h
+++ b/tools/perf/util/include/linux/bitmap.h
@@ -1,2 +1,3 @@
#include "../../../../include/linux/bitmap.h"
#include "../../../../include/asm-generic/bitops/find.h"
+#include <linux/errno.h>
diff --git a/tools/perf/util/include/linux/ctype.h b/tools/perf/util/include/linux/ctype.h
index bae5783282ef..a53d4ee1e0b7 100644
--- a/tools/perf/util/include/linux/ctype.h
+++ b/tools/perf/util/include/linux/ctype.h
@@ -1 +1 @@
-#include "../../../../include/linux/ctype.h"
+#include "../util.h"
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index c1c556825343..94ca95073c40 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -20,16 +20,27 @@ static int strcommon(const char *pathname, char *cwd, int cwdlen)
return n;
}
-struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen,
- unsigned int sym_priv_size, symbol_filter_t filter)
+void map__init(struct map *self, u64 start, u64 end, u64 pgoff,
+ struct dso *dso)
+{
+ self->start = start;
+ self->end = end;
+ self->pgoff = pgoff;
+ self->dso = dso;
+ self->map_ip = map__map_ip;
+ self->unmap_ip = map__unmap_ip;
+ RB_CLEAR_NODE(&self->rb_node);
+}
+
+struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen)
{
struct map *self = malloc(sizeof(*self));
if (self != NULL) {
const char *filename = event->filename;
char newfilename[PATH_MAX];
+ struct dso *dso;
int anon;
- bool new_dso;
if (cwd) {
int n = strcommon(filename, cwd, cwdlen);
@@ -48,33 +59,15 @@ struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen,
filename = newfilename;
}
- self->start = event->start;
- self->end = event->start + event->len;
- self->pgoff = event->pgoff;
-
- self->dso = dsos__findnew(filename, sym_priv_size, &new_dso);
- if (self->dso == NULL)
+ dso = dsos__findnew(filename);
+ if (dso == NULL)
goto out_delete;
- if (new_dso) {
- int nr = dso__load(self->dso, self, filter);
-
- if (nr < 0)
- pr_warning("Failed to open %s, continuing "
- "without symbols\n",
- self->dso->long_name);
- else if (nr == 0)
- pr_warning("No symbols found in %s, maybe "
- "install a debug package?\n",
- self->dso->long_name);
- }
+ map__init(self, event->start, event->start + event->len,
+ event->pgoff, dso);
if (self->dso == vdso || anon)
self->map_ip = self->unmap_ip = identity__map_ip;
- else {
- self->map_ip = map__map_ip;
- self->unmap_ip = map__unmap_ip;
- }
}
return self;
out_delete:
@@ -82,6 +75,47 @@ out_delete:
return NULL;
}
+#define DSO__DELETED "(deleted)"
+
+struct symbol *
+map__find_symbol(struct map *self, u64 ip, symbol_filter_t filter)
+{
+ if (!self->dso->loaded) {
+ int nr = dso__load(self->dso, self, filter);
+
+ if (nr < 0) {
+ if (self->dso->has_build_id) {
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+
+ build_id__sprintf(self->dso->build_id,
+ sizeof(self->dso->build_id),
+ sbuild_id);
+ pr_warning("%s with build id %s not found",
+ self->dso->long_name, sbuild_id);
+ } else
+ pr_warning("Failed to open %s",
+ self->dso->long_name);
+ pr_warning(", continuing without symbols\n");
+ return NULL;
+ } else if (nr == 0) {
+ const char *name = self->dso->long_name;
+ const size_t len = strlen(name);
+ const size_t real_len = len - sizeof(DSO__DELETED);
+
+ if (len > sizeof(DSO__DELETED) &&
+ strcmp(name + real_len + 1, DSO__DELETED) == 0) {
+ pr_warning("%.*s was updated, restart the long running apps that use it!\n",
+ (int)real_len, name);
+ } else {
+ pr_warning("no symbols found in %s, maybe install a debug package?\n", name);
+ }
+ return NULL;
+ }
+ }
+
+ return self->dso->find_symbol(self->dso, ip);
+}
+
struct map *map__clone(struct map *self)
{
struct map *map = malloc(sizeof(*self));
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index b097570e9623..0faf4f2bb5ca 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -7,6 +7,7 @@
#include "string.h"
#include "cache.h"
#include "header.h"
+#include "debugfs.h"
int nr_counters;
@@ -47,6 +48,8 @@ static struct event_symbol event_symbols[] = {
{ CSW(PAGE_FAULTS_MAJ), "major-faults", "" },
{ CSW(CONTEXT_SWITCHES), "context-switches", "cs" },
{ CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" },
+ { CSW(ALIGNMENT_FAULTS), "alignment-faults", "" },
+ { CSW(EMULATION_FAULTS), "emulation-faults", "" },
};
#define __PERF_EVENT_FIELD(config, name) \
@@ -75,6 +78,8 @@ static const char *sw_event_names[] = {
"CPU-migrations",
"minor-faults",
"major-faults",
+ "alignment-faults",
+ "emulation-faults",
};
#define MAX_ALIASES 8
@@ -149,16 +154,6 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir)
#define MAX_EVENT_LENGTH 512
-int valid_debugfs_mount(const char *debugfs)
-{
- struct statfs st_fs;
-
- if (statfs(debugfs, &st_fs) < 0)
- return -ENOENT;
- else if (st_fs.f_type != (long) DEBUGFS_MAGIC)
- return -ENOENT;
- return 0;
-}
struct tracepoint_path *tracepoint_id_to_path(u64 config)
{
@@ -171,7 +166,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)
char evt_path[MAXPATHLEN];
char dir_path[MAXPATHLEN];
- if (valid_debugfs_mount(debugfs_path))
+ if (debugfs_valid_mountpoint(debugfs_path))
return NULL;
sys_dir = opendir(debugfs_path);
@@ -510,7 +505,7 @@ static enum event_result parse_tracepoint_event(const char **strp,
char sys_name[MAX_EVENT_LENGTH];
unsigned int sys_length, evt_length;
- if (valid_debugfs_mount(debugfs_path))
+ if (debugfs_valid_mountpoint(debugfs_path))
return 0;
evt_name = strchr(*strp, ':');
@@ -678,6 +673,8 @@ parse_event_symbols(const char **str, struct perf_event_attr *attr)
if (ret != EVT_FAILED)
goto modifier;
+ fprintf(stderr, "invalid or unsupported event: '%s'\n", *str);
+ fprintf(stderr, "Run 'perf list' for a list of valid events\n");
return EVT_FAILED;
modifier:
@@ -786,7 +783,7 @@ static void print_tracepoint_events(void)
char evt_path[MAXPATHLEN];
char dir_path[MAXPATHLEN];
- if (valid_debugfs_mount(debugfs_path))
+ if (debugfs_valid_mountpoint(debugfs_path))
return;
sys_dir = opendir(debugfs_path);
@@ -804,7 +801,7 @@ static void print_tracepoint_events(void)
for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) {
snprintf(evt_path, MAXPATHLEN, "%s:%s",
sys_dirent.d_name, evt_dirent.d_name);
- fprintf(stderr, " %-42s [%s]\n", evt_path,
+ printf(" %-42s [%s]\n", evt_path,
event_type_descriptors[PERF_TYPE_TRACEPOINT+1]);
}
closedir(evt_dir);
@@ -821,8 +818,8 @@ void print_events(void)
unsigned int i, type, op, prev_type = -1;
char name[40];
- fprintf(stderr, "\n");
- fprintf(stderr, "List of pre-defined events (to be used in -e):\n");
+ printf("\n");
+ printf("List of pre-defined events (to be used in -e):\n");
for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) {
type = syms->type + 1;
@@ -830,19 +827,19 @@ void print_events(void)
type = 0;
if (type != prev_type)
- fprintf(stderr, "\n");
+ printf("\n");
if (strlen(syms->alias))
sprintf(name, "%s OR %s", syms->symbol, syms->alias);
else
strcpy(name, syms->symbol);
- fprintf(stderr, " %-42s [%s]\n", name,
+ printf(" %-42s [%s]\n", name,
event_type_descriptors[type]);
prev_type = type;
}
- fprintf(stderr, "\n");
+ printf("\n");
for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
/* skip invalid cache type */
@@ -850,17 +847,17 @@ void print_events(void)
continue;
for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
- fprintf(stderr, " %-42s [%s]\n",
+ printf(" %-42s [%s]\n",
event_cache_name(type, op, i),
event_type_descriptors[4]);
}
}
}
- fprintf(stderr, "\n");
- fprintf(stderr, " %-42s [raw hardware event descriptor]\n",
+ printf("\n");
+ printf(" %-42s [raw hardware event descriptor]\n",
"rNNN");
- fprintf(stderr, "\n");
+ printf("\n");
print_tracepoint_events();
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
index 04743d3e9039..227043577e06 100644
--- a/tools/perf/util/string.c
+++ b/tools/perf/util/string.c
@@ -1,5 +1,7 @@
#include <string.h>
+#include <stdlib.h>
#include "string.h"
+#include "util.h"
static int hex(char ch)
{
@@ -43,3 +45,85 @@ char *strxfrchar(char *s, char from, char to)
return s;
}
+
+#define K 1024LL
+/*
+ * perf_atoll()
+ * Parse (\d+)(b|B|kb|KB|mb|MB|gb|GB|tb|TB) (e.g. "256MB")
+ * and return its numeric value
+ */
+s64 perf_atoll(const char *str)
+{
+ unsigned int i;
+ s64 length = -1, unit = 1;
+
+ if (!isdigit(str[0]))
+ goto out_err;
+
+ for (i = 1; i < strlen(str); i++) {
+ switch (str[i]) {
+ case 'B':
+ case 'b':
+ break;
+ case 'K':
+ if (str[i + 1] != 'B')
+ goto out_err;
+ else
+ goto kilo;
+ case 'k':
+ if (str[i + 1] != 'b')
+ goto out_err;
+kilo:
+ unit = K;
+ break;
+ case 'M':
+ if (str[i + 1] != 'B')
+ goto out_err;
+ else
+ goto mega;
+ case 'm':
+ if (str[i + 1] != 'b')
+ goto out_err;
+mega:
+ unit = K * K;
+ break;
+ case 'G':
+ if (str[i + 1] != 'B')
+ goto out_err;
+ else
+ goto giga;
+ case 'g':
+ if (str[i + 1] != 'b')
+ goto out_err;
+giga:
+ unit = K * K * K;
+ break;
+ case 'T':
+ if (str[i + 1] != 'B')
+ goto out_err;
+ else
+ goto tera;
+ case 't':
+ if (str[i + 1] != 'b')
+ goto out_err;
+tera:
+ unit = K * K * K * K;
+ break;
+ case '\0': /* only specified figures */
+ unit = 1;
+ break;
+ default:
+ if (!isdigit(str[i]))
+ goto out_err;
+ break;
+ }
+ }
+
+ length = atoll(str) * unit;
+ goto out;
+
+out_err:
+ length = -1;
+out:
+ return length;
+}
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h
index 2c84bf65ba0f..e50b07f80827 100644
--- a/tools/perf/util/string.h
+++ b/tools/perf/util/string.h
@@ -5,6 +5,7 @@
int hex2u64(const char *ptr, u64 *val);
char *strxfrchar(char *s, char from, char to);
+s64 perf_atoll(const char *str);
#define _STR(x) #x
#define STR(x) _STR(x)
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 8f0208ce237a..1b77e81b38de 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -26,6 +26,7 @@ static void dsos__add(struct dso *dso);
static struct dso *dsos__find(const char *name);
static struct map *map__new2(u64 start, struct dso *dso);
static void kernel_maps__insert(struct map *map);
+unsigned int symbol__priv_size;
static struct rb_root kernel_maps;
@@ -75,18 +76,17 @@ static void kernel_maps__fixup_end(void)
}
}
-static struct symbol *symbol__new(u64 start, u64 len, const char *name,
- unsigned int priv_size)
+static struct symbol *symbol__new(u64 start, u64 len, const char *name)
{
size_t namelen = strlen(name) + 1;
- struct symbol *self = calloc(1, priv_size + sizeof(*self) + namelen);
-
+ struct symbol *self = calloc(1, (symbol__priv_size +
+ sizeof(*self) + namelen));
if (!self)
return NULL;
- if (priv_size) {
- memset(self, 0, priv_size);
- self = ((void *)self) + priv_size;
+ if (symbol__priv_size) {
+ memset(self, 0, symbol__priv_size);
+ self = ((void *)self) + symbol__priv_size;
}
self->start = start;
self->end = len ? start + len - 1 : start;
@@ -98,9 +98,9 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name,
return self;
}
-static void symbol__delete(struct symbol *self, unsigned int priv_size)
+static void symbol__delete(struct symbol *self)
{
- free(((void *)self) - priv_size);
+ free(((void *)self) - symbol__priv_size);
}
static size_t symbol__fprintf(struct symbol *self, FILE *fp)
@@ -109,7 +109,7 @@ static size_t symbol__fprintf(struct symbol *self, FILE *fp)
self->start, self->end, self->name);
}
-struct dso *dso__new(const char *name, unsigned int sym_priv_size)
+struct dso *dso__new(const char *name)
{
struct dso *self = malloc(sizeof(*self) + strlen(name) + 1);
@@ -118,10 +118,11 @@ struct dso *dso__new(const char *name, unsigned int sym_priv_size)
self->long_name = self->name;
self->short_name = self->name;
self->syms = RB_ROOT;
- self->sym_priv_size = sym_priv_size;
self->find_symbol = dso__find_symbol;
self->slen_calculated = 0;
self->origin = DSO__ORIG_NOT_FOUND;
+ self->loaded = 0;
+ self->has_build_id = 0;
}
return self;
@@ -136,7 +137,7 @@ static void dso__delete_symbols(struct dso *self)
pos = rb_entry(next, struct symbol, rb_node);
next = rb_next(&pos->rb_node);
rb_erase(&pos->rb_node, &self->syms);
- symbol__delete(pos, self->sym_priv_size);
+ symbol__delete(pos);
}
}
@@ -148,6 +149,12 @@ void dso__delete(struct dso *self)
free(self);
}
+void dso__set_build_id(struct dso *self, void *build_id)
+{
+ memcpy(self->build_id, build_id, sizeof(self->build_id));
+ self->has_build_id = 1;
+}
+
static void dso__insert_symbol(struct dso *self, struct symbol *sym)
{
struct rb_node **p = &self->syms.rb_node;
@@ -190,11 +197,37 @@ struct symbol *dso__find_symbol(struct dso *self, u64 ip)
return NULL;
}
-size_t dso__fprintf(struct dso *self, FILE *fp)
+int build_id__sprintf(u8 *self, int len, char *bf)
{
- size_t ret = fprintf(fp, "dso: %s\n", self->short_name);
+ char *bid = bf;
+ u8 *raw = self;
+ int i;
+ for (i = 0; i < len; ++i) {
+ sprintf(bid, "%02x", *raw);
+ ++raw;
+ bid += 2;
+ }
+
+ return raw - self;
+}
+
+size_t dso__fprintf_buildid(struct dso *self, FILE *fp)
+{
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+
+ build_id__sprintf(self->build_id, sizeof(self->build_id), sbuild_id);
+ return fprintf(fp, "%s", sbuild_id);
+}
+
+size_t dso__fprintf(struct dso *self, FILE *fp)
+{
struct rb_node *nd;
+ size_t ret = fprintf(fp, "dso: %s (", self->short_name);
+
+ ret += dso__fprintf_buildid(self, fp);
+ ret += fprintf(fp, ")\n");
+
for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) {
struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
ret += symbol__fprintf(pos, fp);
@@ -250,12 +283,16 @@ static int kernel_maps__load_all_kallsyms(void)
/*
* Will fix up the end later, when we have all symbols sorted.
*/
- sym = symbol__new(start, 0, symbol_name,
- kernel_map->dso->sym_priv_size);
+ sym = symbol__new(start, 0, symbol_name);
if (sym == NULL)
goto out_delete_line;
+ /*
+ * We will pass the symbols to the filter later, in
+ * kernel_maps__split_kallsyms, when we have split the
+ * maps per module
+ */
dso__insert_symbol(kernel_map->dso, sym);
}
@@ -317,8 +354,7 @@ static int kernel_maps__split_kallsyms(symbol_filter_t filter, int use_modules)
snprintf(dso_name, sizeof(dso_name), "[kernel].%d",
kernel_range++);
- dso = dso__new(dso_name,
- kernel_map->dso->sym_priv_size);
+ dso = dso__new(dso_name);
if (dso == NULL)
return -1;
@@ -336,7 +372,7 @@ static int kernel_maps__split_kallsyms(symbol_filter_t filter, int use_modules)
if (filter && filter(map, pos)) {
delete_symbol:
rb_erase(&pos->rb_node, &kernel_map->dso->syms);
- symbol__delete(pos, kernel_map->dso->sym_priv_size);
+ symbol__delete(pos);
} else {
if (map != kernel_map) {
rb_erase(&pos->rb_node, &kernel_map->dso->syms);
@@ -417,14 +453,13 @@ static int dso__load_perf_map(struct dso *self, struct map *map,
if (len + 2 >= line_len)
continue;
- sym = symbol__new(start, size, line + len,
- self->sym_priv_size);
+ sym = symbol__new(start, size, line + len);
if (sym == NULL)
goto out_delete_line;
if (filter && filter(map, sym))
- symbol__delete(sym, self->sym_priv_size);
+ symbol__delete(sym);
else {
dso__insert_symbol(self, sym);
nr_syms++;
@@ -532,7 +567,8 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
* And always look at the original dso, not at debuginfo packages, that
* have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS).
*/
-static int dso__synthesize_plt_symbols(struct dso *self)
+static int dso__synthesize_plt_symbols(struct dso *self, struct map *map,
+ symbol_filter_t filter)
{
uint32_t nr_rel_entries, idx;
GElf_Sym sym;
@@ -552,7 +588,7 @@ static int dso__synthesize_plt_symbols(struct dso *self)
if (fd < 0)
goto out;
- elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
if (elf == NULL)
goto out_close;
@@ -616,12 +652,16 @@ static int dso__synthesize_plt_symbols(struct dso *self)
"%s@plt", elf_sym__name(&sym, symstrs));
f = symbol__new(plt_offset, shdr_plt.sh_entsize,
- sympltname, self->sym_priv_size);
+ sympltname);
if (!f)
goto out_elf_end;
- dso__insert_symbol(self, f);
- ++nr;
+ if (filter && filter(map, f))
+ symbol__delete(f);
+ else {
+ dso__insert_symbol(self, f);
+ ++nr;
+ }
}
} else if (shdr_rel_plt.sh_type == SHT_REL) {
GElf_Rel pos_mem, *pos;
@@ -634,12 +674,16 @@ static int dso__synthesize_plt_symbols(struct dso *self)
"%s@plt", elf_sym__name(&sym, symstrs));
f = symbol__new(plt_offset, shdr_plt.sh_entsize,
- sympltname, self->sym_priv_size);
+ sympltname);
if (!f)
goto out_elf_end;
- dso__insert_symbol(self, f);
- ++nr;
+ if (filter && filter(map, f))
+ symbol__delete(f);
+ else {
+ dso__insert_symbol(self, f);
+ ++nr;
+ }
}
}
@@ -676,7 +720,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
Elf *elf;
int nr = 0;
- elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
if (elf == NULL) {
pr_err("%s: cannot read %s ELF file.\n", __func__, name);
goto out_close;
@@ -769,7 +813,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
if (kmodule)
start += map->start + shdr.sh_offset;
- curr_dso = dso__new(dso_name, self->sym_priv_size);
+ curr_dso = dso__new(dso_name);
if (curr_dso == NULL)
goto out_elf_end;
curr_map = map__new2(start, curr_dso);
@@ -803,14 +847,13 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
if (demangled != NULL)
elf_name = demangled;
new_symbol:
- f = symbol__new(sym.st_value, sym.st_size, elf_name,
- curr_dso->sym_priv_size);
+ f = symbol__new(sym.st_value, sym.st_size, elf_name);
free(demangled);
if (!f)
goto out_elf_end;
if (filter && filter(curr_map, f))
- symbol__delete(f, curr_dso->sym_priv_size);
+ symbol__delete(f);
else {
dso__insert_symbol(curr_dso, f);
nr++;
@@ -829,27 +872,59 @@ out_close:
return err;
}
-#define BUILD_ID_SIZE 128
+bool fetch_build_id_table(struct list_head *head)
+{
+ bool have_buildid = false;
+ struct dso *pos;
-static char *dso__read_build_id(struct dso *self)
+ list_for_each_entry(pos, &dsos, node) {
+ struct build_id_list *new;
+ struct build_id_event b;
+ size_t len;
+
+ if (filename__read_build_id(pos->long_name,
+ &b.build_id,
+ sizeof(b.build_id)) < 0)
+ continue;
+ have_buildid = true;
+ memset(&b.header, 0, sizeof(b.header));
+ len = strlen(pos->long_name) + 1;
+ len = ALIGN(len, 64);
+ b.header.size = sizeof(b) + len;
+
+ new = malloc(sizeof(*new));
+ if (!new)
+ die("No memory\n");
+
+ memcpy(&new->event, &b, sizeof(b));
+ new->dso_name = pos->long_name;
+ new->len = len;
+
+ list_add_tail(&new->list, head);
+ }
+
+ return have_buildid;
+}
+
+int filename__read_build_id(const char *filename, void *bf, size_t size)
{
- int i;
+ int fd, err = -1;
GElf_Ehdr ehdr;
GElf_Shdr shdr;
Elf_Data *build_id_data;
Elf_Scn *sec;
- char *build_id = NULL, *bid;
- unsigned char *raw;
Elf *elf;
- int fd = open(self->long_name, O_RDONLY);
+ if (size < BUILD_ID_SIZE)
+ goto out;
+
+ fd = open(filename, O_RDONLY);
if (fd < 0)
goto out;
- elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
if (elf == NULL) {
- pr_err("%s: cannot read %s ELF file.\n", __func__,
- self->long_name);
+ pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
goto out_close;
}
@@ -858,30 +933,40 @@ static char *dso__read_build_id(struct dso *self)
goto out_elf_end;
}
- sec = elf_section_by_name(elf, &ehdr, &shdr, ".note.gnu.build-id", NULL);
+ sec = elf_section_by_name(elf, &ehdr, &shdr,
+ ".note.gnu.build-id", NULL);
if (sec == NULL)
goto out_elf_end;
build_id_data = elf_getdata(sec, NULL);
if (build_id_data == NULL)
goto out_elf_end;
- build_id = malloc(BUILD_ID_SIZE);
- if (build_id == NULL)
- goto out_elf_end;
- raw = build_id_data->d_buf + 16;
- bid = build_id;
-
- for (i = 0; i < 20; ++i) {
- sprintf(bid, "%02x", *raw);
- ++raw;
- bid += 2;
- }
- pr_debug2("%s(%s): %s\n", __func__, self->long_name, build_id);
+ memcpy(bf, build_id_data->d_buf + 16, BUILD_ID_SIZE);
+ err = BUILD_ID_SIZE;
out_elf_end:
elf_end(elf);
out_close:
close(fd);
out:
+ return err;
+}
+
+static char *dso__read_build_id(struct dso *self)
+{
+ int len;
+ char *build_id = NULL;
+ unsigned char rawbf[BUILD_ID_SIZE];
+
+ len = filename__read_build_id(self->long_name, rawbf, sizeof(rawbf));
+ if (len < 0)
+ goto out;
+
+ build_id = malloc(len * 2 + 1);
+ if (build_id == NULL)
+ goto out;
+
+ build_id__sprintf(rawbf, len, build_id);
+out:
return build_id;
}
@@ -909,6 +994,8 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
int ret = -1;
int fd;
+ self->loaded = 1;
+
if (!name)
return -1;
@@ -925,6 +1012,8 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
more:
do {
+ int berr = 0;
+
self->origin++;
switch (self->origin) {
case DSO__ORIG_FEDORA:
@@ -941,8 +1030,7 @@ more:
snprintf(name, size,
"/usr/lib/debug/.build-id/%.2s/%s.debug",
build_id, build_id + 2);
- free(build_id);
- break;
+ goto compare_build_id;
}
self->origin++;
/* Fall thru */
@@ -954,6 +1042,22 @@ more:
goto out;
}
+ if (self->has_build_id) {
+ bool match;
+ build_id = malloc(BUILD_ID_SIZE);
+ if (build_id == NULL)
+ goto more;
+ berr = filename__read_build_id(name, build_id,
+ BUILD_ID_SIZE);
+compare_build_id:
+ match = berr > 0 && memcmp(build_id, self->build_id,
+ sizeof(self->build_id)) == 0;
+ free(build_id);
+ build_id = NULL;
+ if (!match)
+ goto more;
+ }
+
fd = open(name, O_RDONLY);
} while (fd < 0);
@@ -967,7 +1071,7 @@ more:
goto more;
if (ret > 0) {
- int nr_plt = dso__synthesize_plt_symbols(self);
+ int nr_plt = dso__synthesize_plt_symbols(self, map, filter);
if (nr_plt > 0)
ret += nr_plt;
}
@@ -1019,6 +1123,8 @@ static int dso__load_module_sym(struct dso *self, struct map *map,
{
int err = 0, fd = open(self->long_name, O_RDONLY);
+ self->loaded = 1;
+
if (fd < 0) {
pr_err("%s: cannot open %s\n", __func__, self->long_name);
return err;
@@ -1128,22 +1234,16 @@ static struct map *map__new2(u64 start, struct dso *dso)
struct map *self = malloc(sizeof(*self));
if (self != NULL) {
- self->start = start;
/*
- * Will be filled after we load all the symbols
+ * ->end will be filled after we load all the symbols
*/
- self->end = 0;
-
- self->pgoff = 0;
- self->dso = dso;
- self->map_ip = map__map_ip;
- self->unmap_ip = map__unmap_ip;
- RB_CLEAR_NODE(&self->rb_node);
+ map__init(self, start, 0, 0, dso);
}
+
return self;
}
-static int dsos__load_modules(unsigned int sym_priv_size)
+static int dsos__load_modules(void)
{
char *line = NULL;
size_t n;
@@ -1182,7 +1282,7 @@ static int dsos__load_modules(unsigned int sym_priv_size)
*sep = '\0';
snprintf(name, sizeof(name), "[%s]", line);
- dso = dso__new(name, sym_priv_size);
+ dso = dso__new(name);
if (dso == NULL)
goto out_delete_line;
@@ -1214,6 +1314,8 @@ static int dso__load_vmlinux(struct dso *self, struct map *map,
{
int err, fd = open(vmlinux, O_RDONLY);
+ self->loaded = 1;
+
if (fd < 0)
return -1;
@@ -1224,11 +1326,11 @@ static int dso__load_vmlinux(struct dso *self, struct map *map,
return err;
}
-int dsos__load_kernel(const char *vmlinux, unsigned int sym_priv_size,
- symbol_filter_t filter, int use_modules)
+int dsos__load_kernel(const char *vmlinux, symbol_filter_t filter,
+ int use_modules)
{
int err = -1;
- struct dso *dso = dso__new(vmlinux, sym_priv_size);
+ struct dso *dso = dso__new(vmlinux);
if (dso == NULL)
return -1;
@@ -1240,7 +1342,7 @@ int dsos__load_kernel(const char *vmlinux, unsigned int sym_priv_size,
kernel_map->map_ip = kernel_map->unmap_ip = identity__map_ip;
- if (use_modules && dsos__load_modules(sym_priv_size) < 0) {
+ if (use_modules && dsos__load_modules() < 0) {
pr_warning("Failed to load list of modules in use! "
"Continuing...\n");
use_modules = 0;
@@ -1312,19 +1414,15 @@ static struct dso *dsos__find(const char *name)
return NULL;
}
-struct dso *dsos__findnew(const char *name, unsigned int sym_priv_size,
- bool *is_new)
+struct dso *dsos__findnew(const char *name)
{
struct dso *dso = dsos__find(name);
if (!dso) {
- dso = dso__new(name, sym_priv_size);
- if (dso) {
+ dso = dso__new(name);
+ if (dso != NULL)
dsos__add(dso);
- *is_new = true;
- }
- } else
- *is_new = false;
+ }
return dso;
}
@@ -1337,13 +1435,24 @@ void dsos__fprintf(FILE *fp)
dso__fprintf(pos, fp);
}
-int load_kernel(unsigned int sym_priv_size, symbol_filter_t filter)
+size_t dsos__fprintf_buildid(FILE *fp)
+{
+ struct dso *pos;
+ size_t ret = 0;
+
+ list_for_each_entry(pos, &dsos, node) {
+ ret += dso__fprintf_buildid(pos, fp);
+ ret += fprintf(fp, " %s\n", pos->long_name);
+ }
+ return ret;
+}
+
+int load_kernel(symbol_filter_t filter)
{
- if (dsos__load_kernel(vmlinux_name, sym_priv_size, filter,
- modules) <= 0)
+ if (dsos__load_kernel(vmlinux_name, filter, modules) <= 0)
return -1;
- vdso = dso__new("[vdso]", 0);
+ vdso = dso__new("[vdso]");
if (!vdso)
return -1;
@@ -1352,7 +1461,8 @@ int load_kernel(unsigned int sym_priv_size, symbol_filter_t filter)
return 0;
}
-void symbol__init(void)
+void symbol__init(unsigned int priv_size)
{
elf_version(EV_CURRENT);
+ symbol__priv_size = priv_size;
}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 77b7b3e42417..51c5a4a08133 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -27,6 +27,16 @@ static inline char *bfd_demangle(void __used *v, const char __used *c,
#endif
#endif
+/*
+ * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP;
+ * for newer versions we can use mmap to reduce memory usage:
+ */
+#ifdef LIBELF_NO_MMAP
+# define PERF_ELF_C_READ_MMAP ELF_C_READ
+#else
+# define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP
+#endif
+
#ifndef DMGL_PARAMS
#define DMGL_PARAMS (1 << 0) /* Include function args */
#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
@@ -39,42 +49,51 @@ struct symbol {
char name[0];
};
+extern unsigned int symbol__priv_size;
+
+static inline void *symbol__priv(struct symbol *self)
+{
+ return ((void *)self) - symbol__priv_size;
+}
+
struct dso {
struct list_head node;
struct rb_root syms;
struct symbol *(*find_symbol)(struct dso *, u64 ip);
- unsigned int sym_priv_size;
- unsigned char adjust_symbols;
- unsigned char slen_calculated;
+ u8 adjust_symbols:1;
+ u8 slen_calculated:1;
+ u8 loaded:1;
+ u8 has_build_id:1;
unsigned char origin;
+ u8 build_id[BUILD_ID_SIZE];
const char *short_name;
char *long_name;
char name[0];
};
-struct dso *dso__new(const char *name, unsigned int sym_priv_size);
+struct dso *dso__new(const char *name);
void dso__delete(struct dso *self);
-static inline void *dso__sym_priv(struct dso *self, struct symbol *sym)
-{
- return ((void *)sym) - self->sym_priv_size;
-}
-
struct symbol *dso__find_symbol(struct dso *self, u64 ip);
-int dsos__load_kernel(const char *vmlinux, unsigned int sym_priv_size,
- symbol_filter_t filter, int modules);
-struct dso *dsos__findnew(const char *name, unsigned int sym_priv_size,
- bool *is_new);
+int dsos__load_kernel(const char *vmlinux, symbol_filter_t filter, int modules);
+struct dso *dsos__findnew(const char *name);
int dso__load(struct dso *self, struct map *map, symbol_filter_t filter);
void dsos__fprintf(FILE *fp);
+size_t dsos__fprintf_buildid(FILE *fp);
+size_t dso__fprintf_buildid(struct dso *self, FILE *fp);
size_t dso__fprintf(struct dso *self, FILE *fp);
char dso__symtab_origin(const struct dso *self);
+void dso__set_build_id(struct dso *self, void *build_id);
+
+int filename__read_build_id(const char *filename, void *bf, size_t size);
+bool fetch_build_id_table(struct list_head *head);
+int build_id__sprintf(u8 *self, int len, char *bf);
-int load_kernel(unsigned int sym_priv_size, symbol_filter_t filter);
+int load_kernel(symbol_filter_t filter);
-void symbol__init(void);
+void symbol__init(unsigned int priv_size);
extern struct list_head dsos;
extern struct map *kernel_map;
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 0daa341734f9..f2203a0946bc 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -315,6 +315,7 @@ static inline int has_extension(const char *filename, const char *ext)
#undef isascii
#undef isspace
#undef isdigit
+#undef isxdigit
#undef isalpha
#undef isprint
#undef isalnum
@@ -332,6 +333,8 @@ extern unsigned char sane_ctype[256];
#define isascii(x) (((x) & ~0x7f) == 0)
#define isspace(x) sane_istest(x,GIT_SPACE)
#define isdigit(x) sane_istest(x,GIT_DIGIT)
+#define isxdigit(x) \
+ (sane_istest(toupper(x), GIT_ALPHA | GIT_DIGIT) && toupper(x) < 'G')
#define isalpha(x) sane_istest(x,GIT_ALPHA)
#define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
#define isprint(x) sane_istest(x,GIT_PRINT)