summaryrefslogtreecommitdiffstats
path: root/tools/perf/util
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2009-11-17 10:16:43 +0100
committerIngo Molnar <mingo@elte.hu>2009-11-17 10:17:47 +0100
commita7b63425a41cd6a8d50f76fef0660c5110f97e91 (patch)
treebe17ee121f1c8814d8d39c9f3e0205d9397fab54 /tools/perf/util
parent35039eb6b199749943547c8572be6604edf00229 (diff)
parent3726cc75e581c157202da93bb2333cce25c15c98 (diff)
Merge branch 'perf/core' into perf/probes
Resolved merge conflict in tools/perf/Makefile Merge reason: we want to queue up a dependent patch. Signed-off-by: Ingo Molnar <mingo@elte.hu>
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_o