Update to 3.4-final.
[linux-flexiantxendom0-3.2.10.git] / tools / perf / util / header.c
index 2143a32..c0b70c6 100644 (file)
@@ -1,5 +1,6 @@
 #define _FILE_OFFSET_BITS 64
 
+#include "util.h"
 #include <sys/types.h>
 #include <byteswap.h>
 #include <unistd.h>
@@ -7,11 +8,11 @@
 #include <stdlib.h>
 #include <linux/list.h>
 #include <linux/kernel.h>
+#include <linux/bitops.h>
 #include <sys/utsname.h>
 
 #include "evlist.h"
 #include "evsel.h"
-#include "util.h"
 #include "header.h"
 #include "../perf.h"
 #include "trace-event.h"
@@ -28,9 +29,6 @@ static struct perf_trace_event_type *events;
 static u32 header_argc;
 static const char **header_argv;
 
-static int dsos__write_buildid_table(struct perf_header *header, int fd);
-static int perf_session__cache_build_ids(struct perf_session *session);
-
 int perf_header__push_event(u64 id, const char *name)
 {
        if (strlen(name) > MAX_EVENT_NAME)
@@ -65,9 +63,20 @@ char *perf_header__find_event(u64 id)
        return NULL;
 }
 
-static const char *__perf_magic = "PERFFILE";
+/*
+ * magic2 = "PERFILE2"
+ * must be a numerical value to let the endianness
+ * determine the memory layout. That way we are able
+ * to detect endianness when reading the perf.data file
+ * back.
+ *
+ * we check for legacy (PERFFILE) format.
+ */
+static const char *__perf_magic1 = "PERFFILE";
+static const u64 __perf_magic2    = 0x32454c4946524550ULL;
+static const u64 __perf_magic2_sw = 0x50455246494c4532ULL;
 
-#define PERF_MAGIC     (*(u64 *)__perf_magic)
+#define PERF_MAGIC     __perf_magic2
 
 struct perf_file_attr {
        struct perf_event_attr  attr;
@@ -187,6 +196,252 @@ perf_header__set_cmdline(int argc, const char **argv)
        return 0;
 }
 
+#define dsos__for_each_with_build_id(pos, head)        \
+       list_for_each_entry(pos, head, node)    \
+               if (!pos->has_build_id)         \
+                       continue;               \
+               else
+
+static int __dsos__write_buildid_table(struct list_head *head, pid_t pid,
+                               u16 misc, int fd)
+{
+       struct dso *pos;
+
+       dsos__for_each_with_build_id(pos, head) {
+               int err;
+               struct build_id_event b;
+               size_t len;
+
+               if (!pos->hit)
+                       continue;
+               len = pos->long_name_len + 1;
+               len = ALIGN(len, NAME_ALIGN);
+               memset(&b, 0, sizeof(b));
+               memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id));
+               b.pid = pid;
+               b.header.misc = misc;
+               b.header.size = sizeof(b) + len;
+               err = do_write(fd, &b, sizeof(b));
+               if (err < 0)
+                       return err;
+               err = write_padded(fd, pos->long_name,
+                                  pos->long_name_len + 1, len);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int machine__write_buildid_table(struct machine *machine, int fd)
+{
+       int err;
+       u16 kmisc = PERF_RECORD_MISC_KERNEL,
+           umisc = PERF_RECORD_MISC_USER;
+
+       if (!machine__is_host(machine)) {
+               kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
+               umisc = PERF_RECORD_MISC_GUEST_USER;
+       }
+
+       err = __dsos__write_buildid_table(&machine->kernel_dsos, machine->pid,
+                                         kmisc, fd);
+       if (err == 0)
+               err = __dsos__write_buildid_table(&machine->user_dsos,
+                                                 machine->pid, umisc, fd);
+       return err;
+}
+
+static int dsos__write_buildid_table(struct perf_header *header, int fd)
+{
+       struct perf_session *session = container_of(header,
+                       struct perf_session, header);
+       struct rb_node *nd;
+       int err = machine__write_buildid_table(&session->host_machine, fd);
+
+       if (err)
+               return err;
+
+       for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
+               struct machine *pos = rb_entry(nd, struct machine, rb_node);
+               err = machine__write_buildid_table(pos, fd);
+               if (err)
+                       break;
+       }
+       return err;
+}
+
+int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
+                         const char *name, bool is_kallsyms)
+{
+       const size_t size = PATH_MAX;
+       char *realname, *filename = zalloc(size),
+            *linkname = zalloc(size), *targetname;
+       int len, err = -1;
+
+       if (is_kallsyms) {
+               if (symbol_conf.kptr_restrict) {
+                       pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n");
+                       return 0;
+               }
+               realname = (char *)name;
+       } else
+               realname = realpath(name, NULL);
+
+       if (realname == NULL || filename == NULL || linkname == NULL)
+               goto out_free;
+
+       len = scnprintf(filename, size, "%s%s%s",
+                      debugdir, is_kallsyms ? "/" : "", realname);
+       if (mkdir_p(filename, 0755))
+               goto out_free;
+
+       snprintf(filename + len, size - len, "/%s", sbuild_id);
+
+       if (access(filename, F_OK)) {
+               if (is_kallsyms) {
+                        if (copyfile("/proc/kallsyms", filename))
+                               goto out_free;
+               } else if (link(realname, filename) && copyfile(name, filename))
+                       goto out_free;
+       }
+
+       len = scnprintf(linkname, size, "%s/.build-id/%.2s",
+                      debugdir, sbuild_id);
+
+       if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
+               goto out_free;
+
+       snprintf(linkname + len, size - len, "/%s", sbuild_id + 2);
+       targetname = filename + strlen(debugdir) - 5;
+       memcpy(targetname, "../..", 5);
+
+       if (symlink(targetname, linkname) == 0)
+               err = 0;
+out_free:
+       if (!is_kallsyms)
+               free(realname);
+       free(filename);
+       free(linkname);
+       return err;
+}
+
+static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
+                                const char *name, const char *debugdir,
+                                bool is_kallsyms)
+{
+       char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+
+       build_id__sprintf(build_id, build_id_size, sbuild_id);
+
+       return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms);
+}
+
+int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)
+{
+       const size_t size = PATH_MAX;
+       char *filename = zalloc(size),
+            *linkname = zalloc(size);
+       int err = -1;
+
+       if (filename == NULL || linkname == NULL)
+               goto out_free;
+
+       snprintf(linkname, size, "%s/.build-id/%.2s/%s",
+                debugdir, sbuild_id, sbuild_id + 2);
+
+       if (access(linkname, F_OK))
+               goto out_free;
+
+       if (readlink(linkname, filename, size - 1) < 0)
+               goto out_free;
+
+       if (unlink(linkname))
+               goto out_free;
+
+       /*
+        * Since the link is relative, we must make it absolute:
+        */
+       snprintf(linkname, size, "%s/.build-id/%.2s/%s",
+                debugdir, sbuild_id, filename);
+
+       if (unlink(linkname))
+               goto out_free;
+
+       err = 0;
+out_free:
+       free(filename);
+       free(linkname);
+       return err;
+}
+
+static int dso__cache_build_id(struct dso *dso, const char *debugdir)
+{
+       bool is_kallsyms = dso->kernel && dso->long_name[0] != '/';
+
+       return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id),
+                                    dso->long_name, debugdir, is_kallsyms);
+}
+
+static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)
+{
+       struct dso *pos;
+       int err = 0;
+
+       dsos__for_each_with_build_id(pos, head)
+               if (dso__cache_build_id(pos, debugdir))
+                       err = -1;
+
+       return err;
+}
+
+static int machine__cache_build_ids(struct machine *machine, const char *debugdir)
+{
+       int ret = __dsos__cache_build_ids(&machine->kernel_dsos, debugdir);
+       ret |= __dsos__cache_build_ids(&machine->user_dsos, debugdir);
+       return ret;
+}
+
+static int perf_session__cache_build_ids(struct perf_session *session)
+{
+       struct rb_node *nd;
+       int ret;
+       char debugdir[PATH_MAX];
+
+       snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir);
+
+       if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
+               return -1;
+
+       ret = machine__cache_build_ids(&session->host_machine, debugdir);
+
+       for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
+               struct machine *pos = rb_entry(nd, struct machine, rb_node);
+               ret |= machine__cache_build_ids(pos, debugdir);
+       }
+       return ret ? -1 : 0;
+}
+
+static bool machine__read_build_ids(struct machine *machine, bool with_hits)
+{
+       bool ret = __dsos__read_build_ids(&machine->kernel_dsos, with_hits);
+       ret |= __dsos__read_build_ids(&machine->user_dsos, with_hits);
+       return ret;
+}
+
+static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits)
+{
+       struct rb_node *nd;
+       bool ret = machine__read_build_ids(&session->host_machine, with_hits);
+
+       for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
+               struct machine *pos = rb_entry(nd, struct machine, rb_node);
+               ret |= machine__read_build_ids(pos, with_hits);
+       }
+
+       return ret;
+}
+
 static int write_trace_info(int fd, struct perf_header *h __used,
                            struct perf_evlist *evlist)
 {
@@ -202,6 +457,9 @@ static int write_build_id(int fd, struct perf_header *h,
 
        session = container_of(h, struct perf_session, header);
 
+       if (!perf_session__read_build_ids(session, true))
+               return -1;
+
        err = dsos__write_buildid_table(h, fd);
        if (err < 0) {
                pr_debug("failed to write buildid table\n");
@@ -388,7 +646,7 @@ static int write_event_desc(int fd, struct perf_header *h __used,
                /*
                 * write event string as passed on cmdline
                 */
-               ret = do_write_string(fd, attr->name);
+               ret = do_write_string(fd, event_name(attr));
                if (ret < 0)
                        return ret;
                /*
@@ -765,6 +1023,12 @@ write_it:
        return do_write_string(fd, buffer);
 }
 
+static int write_branch_stack(int fd __used, struct perf_header *h __used,
+                      struct perf_evlist *evlist __used)
+{
+       return 0;
+}
+
 static void print_hostname(struct perf_header *ph, int fd, FILE *fp)
 {
        char *str = do_read_string(fd, ph);
@@ -886,8 +1150,9 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
        uint64_t id;
        void *buf = NULL;
        char *str;
-       u32 nre, sz, nr, i, j, msz;
-       int ret;
+       u32 nre, sz, nr, i, j;
+       ssize_t ret;
+       size_t msz;
 
        /* number of events */
        ret = read(fd, &nre, sizeof(nre));
@@ -904,15 +1169,9 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
        if (ph->needs_swap)
                sz = bswap_32(sz);
 
-       /*
-        * ensure it is at least to our ABI rev
-        */
-       if (sz < (u32)sizeof(attr))
-               goto error;
-
        memset(&attr, 0, sizeof(attr));
 
-       /* read entire region to sync up to next field */
+       /* buffer to hold on file attr struct */
        buf = malloc(sz);
        if (!buf)
                goto error;
@@ -923,6 +1182,10 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
 
        for (i = 0 ; i < nre; i++) {
 
+               /*
+                * must read entire on-file attr struct to
+                * sync up with layout.
+                */
                ret = read(fd, buf, sz);
                if (ret != (ssize_t)sz)
                        goto error;
@@ -1058,324 +1321,262 @@ static void print_cpuid(struct perf_header *ph, int fd, FILE *fp)
        free(str);
 }
 
-struct feature_ops {
-       int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
-       void (*print)(struct perf_header *h, int fd, FILE *fp);
-       const char *name;
-       bool full_only;
-};
-
-#define FEAT_OPA(n, w, p) \
-       [n] = { .name = #n, .write = w, .print = p }
-#define FEAT_OPF(n, w, p) \
-       [n] = { .name = #n, .write = w, .print = p, .full_only = true }
-
-static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
-       FEAT_OPA(HEADER_TRACE_INFO, write_trace_info, NULL),
-       FEAT_OPA(HEADER_BUILD_ID, write_build_id, NULL),
-       FEAT_OPA(HEADER_HOSTNAME, write_hostname, print_hostname),
-       FEAT_OPA(HEADER_OSRELEASE, write_osrelease, print_osrelease),
-       FEAT_OPA(HEADER_VERSION, write_version, print_version),
-       FEAT_OPA(HEADER_ARCH, write_arch, print_arch),
-       FEAT_OPA(HEADER_NRCPUS, write_nrcpus, print_nrcpus),
-       FEAT_OPA(HEADER_CPUDESC, write_cpudesc, print_cpudesc),
-       FEAT_OPA(HEADER_CPUID, write_cpuid, print_cpuid),
-       FEAT_OPA(HEADER_TOTAL_MEM, write_total_mem, print_total_mem),
-       FEAT_OPA(HEADER_EVENT_DESC, write_event_desc, print_event_desc),
-       FEAT_OPA(HEADER_CMDLINE, write_cmdline, print_cmdline),
-       FEAT_OPF(HEADER_CPU_TOPOLOGY, write_cpu_topology, print_cpu_topology),
-       FEAT_OPF(HEADER_NUMA_TOPOLOGY, write_numa_topology, print_numa_topology),
-};
-
-struct header_print_data {
-       FILE *fp;
-       bool full; /* extended list of headers */
-};
-
-static int perf_file_section__fprintf_info(struct perf_file_section *section,
-                                          struct perf_header *ph,
-                                          int feat, int fd, void *data)
+static void print_branch_stack(struct perf_header *ph __used, int fd __used,
+                              FILE *fp)
 {
-       struct header_print_data *hd = data;
-
-       if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {
-               pr_debug("Failed to lseek to %" PRIu64 " offset for feature "
-                               "%d, continuing...\n", section->offset, feat);
-               return 0;
-       }
-       if (feat < HEADER_TRACE_INFO || feat >= HEADER_LAST_FEATURE) {
-               pr_warning("unknown feature %d\n", feat);
-               return -1;
-       }
-       if (!feat_ops[feat].print)
-               return 0;
-
-       if (!feat_ops[feat].full_only || hd->full)
-               feat_ops[feat].print(ph, fd, hd->fp);
-       else
-               fprintf(hd->fp, "# %s info available, use -I to display\n",
-                       feat_ops[feat].name);
-
-       return 0;
+       fprintf(fp, "# contains samples with branch stack\n");
 }
 
-int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full)
+static int __event_process_build_id(struct build_id_event *bev,
+                                   char *filename,
+                                   struct perf_session *session)
 {
-       struct header_print_data hd;
-       struct perf_header *header = &session->header;
-       int fd = session->fd;
-       hd.fp = fp;
-       hd.full = full;
-
-       perf_header__process_sections(header, fd, &hd,
-                                     perf_file_section__fprintf_info);
-       return 0;
-}
-
-#define dsos__for_each_with_build_id(pos, head)        \
-       list_for_each_entry(pos, head, node)    \
-               if (!pos->has_build_id)         \
-                       continue;               \
-               else
+       int err = -1;
+       struct list_head *head;
+       struct machine *machine;
+       u16 misc;
+       struct dso *dso;
+       enum dso_kernel_type dso_type;
 
-static int __dsos__write_buildid_table(struct list_head *head, pid_t pid,
-                               u16 misc, int fd)
-{
-       struct dso *pos;
+       machine = perf_session__findnew_machine(session, bev->pid);
+       if (!machine)
+               goto out;
 
-       dsos__for_each_with_build_id(pos, head) {
-               int err;
-               struct build_id_event b;
-               size_t len;
+       misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
 
-               if (!pos->hit)
-                       continue;
-               len = pos->long_name_len + 1;
-               len = ALIGN(len, NAME_ALIGN);
-               memset(&b, 0, sizeof(b));
-               memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id));
-               b.pid = pid;
-               b.header.misc = misc;
-               b.header.size = sizeof(b) + len;
-               err = do_write(fd, &b, sizeof(b));
-               if (err < 0)
-                       return err;
-               err = write_padded(fd, pos->long_name,
-                                  pos->long_name_len + 1, len);
-               if (err < 0)
-                       return err;
+       switch (misc) {
+       case PERF_RECORD_MISC_KERNEL:
+               dso_type = DSO_TYPE_KERNEL;
+               head = &machine->kernel_dsos;
+               break;
+       case PERF_RECORD_MISC_GUEST_KERNEL:
+               dso_type = DSO_TYPE_GUEST_KERNEL;
+               head = &machine->kernel_dsos;
+               break;
+       case PERF_RECORD_MISC_USER:
+       case PERF_RECORD_MISC_GUEST_USER:
+               dso_type = DSO_TYPE_USER;
+               head = &machine->user_dsos;
+               break;
+       default:
+               goto out;
        }
 
-       return 0;
-}
+       dso = __dsos__findnew(head, filename);
+       if (dso != NULL) {
+               char sbuild_id[BUILD_ID_SIZE * 2 + 1];
 
-static int machine__write_buildid_table(struct machine *machine, int fd)
-{
-       int err;
-       u16 kmisc = PERF_RECORD_MISC_KERNEL,
-           umisc = PERF_RECORD_MISC_USER;
+               dso__set_build_id(dso, &bev->build_id);
 
-       if (!machine__is_host(machine)) {
-               kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
-               umisc = PERF_RECORD_MISC_GUEST_USER;
+               if (filename[0] == '[')
+                       dso->kernel = dso_type;
+
+               build_id__sprintf(dso->build_id, sizeof(dso->build_id),
+                                 sbuild_id);
+               pr_debug("build id event received for %s: %s\n",
+                        dso->long_name, sbuild_id);
        }
 
-       err = __dsos__write_buildid_table(&machine->kernel_dsos, machine->pid,
-                                         kmisc, fd);
-       if (err == 0)
-               err = __dsos__write_buildid_table(&machine->user_dsos,
-                                                 machine->pid, umisc, fd);
+       err = 0;
+out:
        return err;
 }
 
-static int dsos__write_buildid_table(struct perf_header *header, int fd)
+static int perf_header__read_build_ids_abi_quirk(struct perf_header *header,
+                                                int input, u64 offset, u64 size)
 {
-       struct perf_session *session = container_of(header,
-                       struct perf_session, header);
-       struct rb_node *nd;
-       int err = machine__write_buildid_table(&session->host_machine, fd);
+       struct perf_session *session = container_of(header, struct perf_session, header);
+       struct {
+               struct perf_event_header   header;
+               u8                         build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))];
+               char                       filename[0];
+       } old_bev;
+       struct build_id_event bev;
+       char filename[PATH_MAX];
+       u64 limit = offset + size;
 
-       if (err)
-               return err;
+       while (offset < limit) {
+               ssize_t len;
 
-       for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
-               struct machine *pos = rb_entry(nd, struct machine, rb_node);
-               err = machine__write_buildid_table(pos, fd);
-               if (err)
-                       break;
-       }
-       return err;
-}
+               if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev))
+                       return -1;
 
-int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
-                         const char *name, bool is_kallsyms)
-{
-       const size_t size = PATH_MAX;
-       char *realname, *filename = zalloc(size),
-            *linkname = zalloc(size), *targetname;
-       int len, err = -1;
+               if (header->needs_swap)
+                       perf_event_header__bswap(&old_bev.header);
 
-       if (is_kallsyms) {
-               if (symbol_conf.kptr_restrict) {
-                       pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n");
-                       return 0;
-               }
-               realname = (char *)name;
-       } else
-               realname = realpath(name, NULL);
+               len = old_bev.header.size - sizeof(old_bev);
+               if (read(input, filename, len) != len)
+                       return -1;
 
-       if (realname == NULL || filename == NULL || linkname == NULL)
-               goto out_free;
+               bev.header = old_bev.header;
 
-       len = snprintf(filename, size, "%s%s%s",
-                      debugdir, is_kallsyms ? "/" : "", realname);
-       if (mkdir_p(filename, 0755))
-               goto out_free;
+               /*
+                * As the pid is the missing value, we need to fill
+                * it properly. The header.misc value give us nice hint.
+                */
+               bev.pid = HOST_KERNEL_ID;
+               if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER ||
+                   bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL)
+                       bev.pid = DEFAULT_GUEST_KERNEL_ID;
 
-       snprintf(filename + len, sizeof(filename) - len, "/%s", sbuild_id);
+               memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id));
+               __event_process_build_id(&bev, filename, session);
 
-       if (access(filename, F_OK)) {
-               if (is_kallsyms) {
-                        if (copyfile("/proc/kallsyms", filename))
-                               goto out_free;
-               } else if (link(realname, filename) && copyfile(name, filename))
-                       goto out_free;
+               offset += bev.header.size;
        }
 
-       len = snprintf(linkname, size, "%s/.build-id/%.2s",
-                      debugdir, sbuild_id);
-
-       if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
-               goto out_free;
-
-       snprintf(linkname + len, size - len, "/%s", sbuild_id + 2);
-       targetname = filename + strlen(debugdir) - 5;
-       memcpy(targetname, "../..", 5);
-
-       if (symlink(targetname, linkname) == 0)
-               err = 0;
-out_free:
-       if (!is_kallsyms)
-               free(realname);
-       free(filename);
-       free(linkname);
-       return err;
-}
-
-static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
-                                const char *name, const char *debugdir,
-                                bool is_kallsyms)
-{
-       char sbuild_id[BUILD_ID_SIZE * 2 + 1];
-
-       build_id__sprintf(build_id, build_id_size, sbuild_id);
-
-       return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms);
+       return 0;
 }
 
-int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)
+static int perf_header__read_build_ids(struct perf_header *header,
+                                      int input, u64 offset, u64 size)
 {
-       const size_t size = PATH_MAX;
-       char *filename = zalloc(size),
-            *linkname = zalloc(size);
+       struct perf_session *session = container_of(header, struct perf_session, header);
+       struct build_id_event bev;
+       char filename[PATH_MAX];
+       u64 limit = offset + size, orig_offset = offset;
        int err = -1;
 
-       if (filename == NULL || linkname == NULL)
-               goto out_free;
-
-       snprintf(linkname, size, "%s/.build-id/%.2s/%s",
-                debugdir, sbuild_id, sbuild_id + 2);
-
-       if (access(linkname, F_OK))
-               goto out_free;
+       while (offset < limit) {
+               ssize_t len;
 
-       if (readlink(linkname, filename, size - 1) < 0)
-               goto out_free;
+               if (read(input, &bev, sizeof(bev)) != sizeof(bev))
+                       goto out;
 
-       if (unlink(linkname))
-               goto out_free;
+               if (header->needs_swap)
+                       perf_event_header__bswap(&bev.header);
 
-       /*
-        * Since the link is relative, we must make it absolute:
-        */
-       snprintf(linkname, size, "%s/.build-id/%.2s/%s",
-                debugdir, sbuild_id, filename);
+               len = bev.header.size - sizeof(bev);
+               if (read(input, filename, len) != len)
+                       goto out;
+               /*
+                * The a1645ce1 changeset:
+                *
+                * "perf: 'perf kvm' tool for monitoring guest performance from host"
+                *
+                * Added a field to struct build_id_event that broke the file
+                * format.
+                *
+                * Since the kernel build-id is the first entry, process the
+                * table using the old format if the well known
+                * '[kernel.kallsyms]' string for the kernel build-id has the
+                * first 4 characters chopped off (where the pid_t sits).
+                */
+               if (memcmp(filename, "nel.kallsyms]", 13) == 0) {
+                       if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1)
+                               return -1;
+                       return perf_header__read_build_ids_abi_quirk(header, input, offset, size);
+               }
 
-       if (unlink(linkname))
-               goto out_free;
+               __event_process_build_id(&bev, filename, session);
 
+               offset += bev.header.size;
+       }
        err = 0;
-out_free:
-       free(filename);
-       free(linkname);
+out:
        return err;
 }
 
-static int dso__cache_build_id(struct dso *dso, const char *debugdir)
+static int process_trace_info(struct perf_file_section *section __unused,
+                             struct perf_header *ph __unused,
+                             int feat __unused, int fd)
 {
-       bool is_kallsyms = dso->kernel && dso->long_name[0] != '/';
-
-       return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id),
-                                    dso->long_name, debugdir, is_kallsyms);
+       trace_report(fd, false);
+       return 0;
 }
 
-static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)
+static int process_build_id(struct perf_file_section *section,
+                           struct perf_header *ph,
+                           int feat __unused, int fd)
 {
-       struct dso *pos;
-       int err = 0;
-
-       dsos__for_each_with_build_id(pos, head)
-               if (dso__cache_build_id(pos, debugdir))
-                       err = -1;
-
-       return err;
+       if (perf_header__read_build_ids(ph, fd, section->offset, section->size))
+               pr_debug("Failed to read buildids, continuing...\n");
+       return 0;
 }
 
-static int machine__cache_build_ids(struct machine *machine, const char *debugdir)
-{
-       int ret = __dsos__cache_build_ids(&machine->kernel_dsos, debugdir);
-       ret |= __dsos__cache_build_ids(&machine->user_dsos, debugdir);
-       return ret;
-}
+struct feature_ops {
+       int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
+       void (*print)(struct perf_header *h, int fd, FILE *fp);
+       int (*process)(struct perf_file_section *section,
+                      struct perf_header *h, int feat, int fd);
+       const char *name;
+       bool full_only;
+};
 
-static int perf_session__cache_build_ids(struct perf_session *session)
-{
-       struct rb_node *nd;
-       int ret;
-       char debugdir[PATH_MAX];
+#define FEAT_OPA(n, func) \
+       [n] = { .name = #n, .write = write_##func, .print = print_##func }
+#define FEAT_OPP(n, func) \
+       [n] = { .name = #n, .write = write_##func, .print = print_##func, \
+               .process = process_##func }
+#define FEAT_OPF(n, func) \
+       [n] = { .name = #n, .write = write_##func, .print = print_##func, \
+               .full_only = true }
 
-       snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir);
+/* feature_ops not implemented: */
+#define print_trace_info               NULL
+#define print_build_id                 NULL
 
-       if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
-               return -1;
+static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
+       FEAT_OPP(HEADER_TRACE_INFO,     trace_info),
+       FEAT_OPP(HEADER_BUILD_ID,       build_id),
+       FEAT_OPA(HEADER_HOSTNAME,       hostname),
+       FEAT_OPA(HEADER_OSRELEASE,      osrelease),
+       FEAT_OPA(HEADER_VERSION,        version),
+       FEAT_OPA(HEADER_ARCH,           arch),
+       FEAT_OPA(HEADER_NRCPUS,         nrcpus),
+       FEAT_OPA(HEADER_CPUDESC,        cpudesc),
+       FEAT_OPA(HEADER_CPUID,          cpuid),
+       FEAT_OPA(HEADER_TOTAL_MEM,      total_mem),
+       FEAT_OPA(HEADER_EVENT_DESC,     event_desc),
+       FEAT_OPA(HEADER_CMDLINE,        cmdline),
+       FEAT_OPF(HEADER_CPU_TOPOLOGY,   cpu_topology),
+       FEAT_OPF(HEADER_NUMA_TOPOLOGY,  numa_topology),
+       FEAT_OPA(HEADER_BRANCH_STACK,   branch_stack),
+};
 
-       ret = machine__cache_build_ids(&session->host_machine, debugdir);
+struct header_print_data {
+       FILE *fp;
+       bool full; /* extended list of headers */
+};
 
-       for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
-               struct machine *pos = rb_entry(nd, struct machine, rb_node);
-               ret |= machine__cache_build_ids(pos, debugdir);
+static int perf_file_section__fprintf_info(struct perf_file_section *section,
+                                          struct perf_header *ph,
+                                          int feat, int fd, void *data)
+{
+       struct header_print_data *hd = data;
+
+       if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {
+               pr_debug("Failed to lseek to %" PRIu64 " offset for feature "
+                               "%d, continuing...\n", section->offset, feat);
+               return 0;
        }
-       return ret ? -1 : 0;
-}
+       if (feat >= HEADER_LAST_FEATURE) {
+               pr_warning("unknown feature %d\n", feat);
+               return 0;
+       }
+       if (!feat_ops[feat].print)
+               return 0;
 
-static bool machine__read_build_ids(struct machine *machine, bool with_hits)
-{
-       bool ret = __dsos__read_build_ids(&machine->kernel_dsos, with_hits);
-       ret |= __dsos__read_build_ids(&machine->user_dsos, with_hits);
-       return ret;
+       if (!feat_ops[feat].full_only || hd->full)
+               feat_ops[feat].print(ph, fd, hd->fp);
+       else
+               fprintf(hd->fp, "# %s info available, use -I to display\n",
+                       feat_ops[feat].name);
+
+       return 0;
 }
 
-static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits)
+int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full)
 {
-       struct rb_node *nd;
-       bool ret = machine__read_build_ids(&session->host_machine, with_hits);
-
-       for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
-               struct machine *pos = rb_entry(nd, struct machine, rb_node);
-               ret |= machine__read_build_ids(pos, with_hits);
-       }
+       struct header_print_data hd;
+       struct perf_header *header = &session->header;
+       int fd = session->fd;
+       hd.fp = fp;
+       hd.full = full;
 
-       return ret;
+       perf_header__process_sections(header, fd, &hd,
+                                     perf_file_section__fprintf_info);
+       return 0;
 }
 
 static int do_write_feat(int fd, struct perf_header *h, int type,
@@ -1386,6 +1587,8 @@ static int do_write_feat(int fd, struct perf_header *h, int type,
        int ret = 0;
 
        if (perf_header__has_feat(h, type)) {
+               if (!feat_ops[type].write)
+                       return -1;
 
                (*p)->offset = lseek(fd, 0, SEEK_CUR);
 
@@ -1408,18 +1611,12 @@ static int perf_header__adds_write(struct perf_header *header,
                                   struct perf_evlist *evlist, int fd)
 {
        int nr_sections;
-       struct perf_session *session;
        struct perf_file_section *feat_sec, *p;
        int sec_size;
        u64 sec_start;
+       int feat;
        int err;
 
-       session = container_of(header, struct perf_session, header);
-
-       if (perf_header__has_feat(header, HEADER_BUILD_ID &&
-           !perf_session__read_build_ids(session, true)))
-               perf_header__clear_feat(header, HEADER_BUILD_ID);
-
        nr_sections = bitmap_weight(header->adds_features, HEADER_FEAT_BITS);
        if (!nr_sections)
                return 0;
@@ -1433,64 +1630,11 @@ static int perf_header__adds_write(struct perf_header *header,
        sec_start = header->data_offset + header->data_size;
        lseek(fd, sec_start + sec_size, SEEK_SET);
 
-       err = do_write_feat(fd, header, HEADER_TRACE_INFO, &p, evlist);
-       if (err)
-               goto out_free;
-
-       err = do_write_feat(fd, header, HEADER_BUILD_ID, &p, evlist);
-       if (err) {
-               perf_header__clear_feat(header, HEADER_BUILD_ID);
-               goto out_free;
+       for_each_set_bit(feat, header->adds_features, HEADER_FEAT_BITS) {
+               if (do_write_feat(fd, header, feat, &p, evlist))
+                       perf_header__clear_feat(header, feat);
        }
 
-       err = do_write_feat(fd, header, HEADER_HOSTNAME, &p, evlist);
-       if (err)
-               perf_header__clear_feat(header, HEADER_HOSTNAME);
-
-       err = do_write_feat(fd, header, HEADER_OSRELEASE, &p, evlist);
-       if (err)
-               perf_header__clear_feat(header, HEADER_OSRELEASE);
-
-       err = do_write_feat(fd, header, HEADER_VERSION, &p, evlist);
-       if (err)
-               perf_header__clear_feat(header, HEADER_VERSION);
-
-       err = do_write_feat(fd, header, HEADER_ARCH, &p, evlist);
-       if (err)
-               perf_header__clear_feat(header, HEADER_ARCH);
-
-       err = do_write_feat(fd, header, HEADER_NRCPUS, &p, evlist);
-       if (err)
-               perf_header__clear_feat(header, HEADER_NRCPUS);
-
-       err = do_write_feat(fd, header, HEADER_CPUDESC, &p, evlist);
-       if (err)
-               perf_header__clear_feat(header, HEADER_CPUDESC);
-
-       err = do_write_feat(fd, header, HEADER_CPUID, &p, evlist);
-       if (err)
-               perf_header__clear_feat(header, HEADER_CPUID);
-
-       err = do_write_feat(fd, header, HEADER_TOTAL_MEM, &p, evlist);
-       if (err)
-               perf_header__clear_feat(header, HEADER_TOTAL_MEM);
-
-       err = do_write_feat(fd, header, HEADER_CMDLINE, &p, evlist);
-       if (err)
-               perf_header__clear_feat(header, HEADER_CMDLINE);
-
-       err = do_write_feat(fd, header, HEADER_EVENT_DESC, &p, evlist);
-       if (err)
-               perf_header__clear_feat(header, HEADER_EVENT_DESC);
-
-       err = do_write_feat(fd, header, HEADER_CPU_TOPOLOGY, &p, evlist);
-       if (err)
-               perf_header__clear_feat(header, HEADER_CPU_TOPOLOGY);
-
-       err = do_write_feat(fd, header, HEADER_NUMA_TOPOLOGY, &p, evlist);
-       if (err)
-               perf_header__clear_feat(header, HEADER_NUMA_TOPOLOGY);
-
        lseek(fd, sec_start, SEEK_SET);
        /*
         * may write more than needed due to dropped feature, but
@@ -1499,7 +1643,6 @@ static int perf_header__adds_write(struct perf_header *header,
        err = do_write(fd, feat_sec, sec_size);
        if (err < 0)
                pr_debug("failed to write feature section\n");
-out_free:
        free(feat_sec);
        return err;
 }
@@ -1625,255 +1768,225 @@ out_err_write:
 static int perf_header__getbuffer64(struct perf_header *header,
                                    int fd, void *buf, size_t size)
 {
-       if (readn(fd, buf, size) <= 0)
-               return -1;
-
-       if (header->needs_swap)
-               mem_bswap_64(buf, size);
-
-       return 0;
-}
-
-int perf_header__process_sections(struct perf_header *header, int fd,
-                                 void *data,
-                                 int (*process)(struct perf_file_section *section,
-                                 struct perf_header *ph,
-                                 int feat, int fd, void *data))
-{
-       struct perf_file_section *feat_sec;
-       int nr_sections;
-       int sec_size;
-       int idx = 0;
-       int err = -1, feat = 1;
-
-       nr_sections = bitmap_weight(header->adds_features, HEADER_FEAT_BITS);
-       if (!nr_sections)
-               return 0;
-
-       feat_sec = calloc(sizeof(*feat_sec), nr_sections);
-       if (!feat_sec)
-               return -1;
-
-       sec_size = sizeof(*feat_sec) * nr_sections;
-
-       lseek(fd, header->data_offset + header->data_size, SEEK_SET);
-
-       if (perf_header__getbuffer64(header, fd, feat_sec, sec_size))
-               goto out_free;
-
-       err = 0;
-       while (idx < nr_sections && feat < HEADER_LAST_FEATURE) {
-               if (perf_header__has_feat(header, feat)) {
-                       struct perf_file_section *sec = &feat_sec[idx++];
-
-                       err = process(sec, header, feat, fd, data);
-                       if (err < 0)
-                               break;
-               }
-               ++feat;
-       }
-out_free:
-       free(feat_sec);
-       return err;
-}
-
-int perf_file_header__read(struct perf_file_header *header,
-                          struct perf_header *ph, int fd)
-{
-       lseek(fd, 0, SEEK_SET);
-
-       if (readn(fd, header, sizeof(*header)) <= 0 ||
-           memcmp(&header->magic, __perf_magic, sizeof(header->magic)))
-               return -1;
-
-       if (header->attr_size != sizeof(struct perf_file_attr)) {
-               u64 attr_size = bswap_64(header->attr_size);
-
-               if (attr_size != sizeof(struct perf_file_attr))
-                       return -1;
-
-               mem_bswap_64(header, offsetof(struct perf_file_header,
-                                           adds_features));
-               ph->needs_swap = true;
-       }
-
-       if (header->size != sizeof(*header)) {
-               /* Support the previous format */
-               if (header->size == offsetof(typeof(*header), adds_features))
-                       bitmap_zero(header->adds_features, HEADER_FEAT_BITS);
-               else
-                       return -1;
-       }
+       if (readn(fd, buf, size) <= 0)
+               return -1;
 
-       memcpy(&ph->adds_features, &header->adds_features,
-              sizeof(ph->adds_features));
-       /*
-        * FIXME: hack that assumes that if we need swap the perf.data file
-        * may be coming from an arch with a different word-size, ergo different
-        * DEFINE_BITMAP format, investigate more later, but for now its mostly
-        * safe to assume that we have a build-id section. Trace files probably
-        * have several other issues in this realm anyway...
-        */
-       if (ph->needs_swap) {
-               memset(&ph->adds_features, 0, sizeof(ph->adds_features));
-               perf_header__set_feat(ph, HEADER_BUILD_ID);
-       }
+       if (header->needs_swap)
+               mem_bswap_64(buf, size);
 
-       ph->event_offset = header->event_types.offset;
-       ph->event_size   = header->event_types.size;
-       ph->data_offset  = header->data.offset;
-       ph->data_size    = header->data.size;
        return 0;
 }
 
-static int __event_process_build_id(struct build_id_event *bev,
-                                   char *filename,
-                                   struct perf_session *session)
+int perf_header__process_sections(struct perf_header *header, int fd,
+                                 void *data,
+                                 int (*process)(struct perf_file_section *section,
+                                                struct perf_header *ph,
+                                                int feat, int fd, void *data))
 {
-       int err = -1;
-       struct list_head *head;
-       struct machine *machine;
-       u16 misc;
-       struct dso *dso;
-       enum dso_kernel_type dso_type;
-
-       machine = perf_session__findnew_machine(session, bev->pid);
-       if (!machine)
-               goto out;
+       struct perf_file_section *feat_sec, *sec;
+       int nr_sections;
+       int sec_size;
+       int feat;
+       int err;
 
-       misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
+       nr_sections = bitmap_weight(header->adds_features, HEADER_FEAT_BITS);
+       if (!nr_sections)
+               return 0;
 
-       switch (misc) {
-       case PERF_RECORD_MISC_KERNEL:
-               dso_type = DSO_TYPE_KERNEL;
-               head = &machine->kernel_dsos;
-               break;
-       case PERF_RECORD_MISC_GUEST_KERNEL:
-               dso_type = DSO_TYPE_GUEST_KERNEL;
-               head = &machine->kernel_dsos;
-               break;
-       case PERF_RECORD_MISC_USER:
-       case PERF_RECORD_MISC_GUEST_USER:
-               dso_type = DSO_TYPE_USER;
-               head = &machine->user_dsos;
-               break;
-       default:
-               goto out;
-       }
+       feat_sec = sec = calloc(sizeof(*feat_sec), nr_sections);
+       if (!feat_sec)
+               return -1;
 
-       dso = __dsos__findnew(head, filename);
-       if (dso != NULL) {
-               char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+       sec_size = sizeof(*feat_sec) * nr_sections;
 
-               dso__set_build_id(dso, &bev->build_id);
+       lseek(fd, header->data_offset + header->data_size, SEEK_SET);
 
-               if (filename[0] == '[')
-                       dso->kernel = dso_type;
+       err = perf_header__getbuffer64(header, fd, feat_sec, sec_size);
+       if (err < 0)
+               goto out_free;
 
-               build_id__sprintf(dso->build_id, sizeof(dso->build_id),
-                                 sbuild_id);
-               pr_debug("build id event received for %s: %s\n",
-                        dso->long_name, sbuild_id);
+       for_each_set_bit(feat, header->adds_features, HEADER_LAST_FEATURE) {
+               err = process(sec++, header, feat, fd, data);
+               if (err < 0)
+                       goto out_free;
        }
-
        err = 0;
-out:
+out_free:
+       free(feat_sec);
        return err;
 }
 
-static int perf_header__read_build_ids_abi_quirk(struct perf_header *header,
-                                                int input, u64 offset, u64 size)
+static const int attr_file_abi_sizes[] = {
+       [0] = PERF_ATTR_SIZE_VER0,
+       [1] = PERF_ATTR_SIZE_VER1,
+       0,
+};
+
+/*
+ * In the legacy file format, the magic number is not used to encode endianness.
+ * hdr_sz was used to encode endianness. But given that hdr_sz can vary based
+ * on ABI revisions, we need to try all combinations for all endianness to
+ * detect the endianness.
+ */
+static int try_all_file_abis(uint64_t hdr_sz, struct perf_header *ph)
 {
-       struct perf_session *session = container_of(header, struct perf_session, header);
-       struct {
-               struct perf_event_header   header;
-               u8                         build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))];
-               char                       filename[0];
-       } old_bev;
-       struct build_id_event bev;
-       char filename[PATH_MAX];
-       u64 limit = offset + size;
+       uint64_t ref_size, attr_size;
+       int i;
 
-       while (offset < limit) {
-               ssize_t len;
+       for (i = 0 ; attr_file_abi_sizes[i]; i++) {
+               ref_size = attr_file_abi_sizes[i]
+                        + sizeof(struct perf_file_section);
+               if (hdr_sz != ref_size) {
+                       attr_size = bswap_64(hdr_sz);
+                       if (attr_size != ref_size)
+                               continue;
 
-               if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev))
-                       return -1;
+                       ph->needs_swap = true;
+               }
+               pr_debug("ABI%d perf.data file detected, need_swap=%d\n",
+                        i,
+                        ph->needs_swap);
+               return 0;
+       }
+       /* could not determine endianness */
+       return -1;
+}
 
-               if (header->needs_swap)
-                       perf_event_header__bswap(&old_bev.header);
+#define PERF_PIPE_HDR_VER0     16
 
-               len = old_bev.header.size - sizeof(old_bev);
-               if (read(input, filename, len) != len)
-                       return -1;
+static const size_t attr_pipe_abi_sizes[] = {
+       [0] = PERF_PIPE_HDR_VER0,
+       0,
+};
 
-               bev.header = old_bev.header;
+/*
+ * In the legacy pipe format, there is an implicit assumption that endiannesss
+ * between host recording the samples, and host parsing the samples is the
+ * same. This is not always the case given that the pipe output may always be
+ * redirected into a file and analyzed on a different machine with possibly a
+ * different endianness and perf_event ABI revsions in the perf tool itself.
+ */
+static int try_all_pipe_abis(uint64_t hdr_sz, struct perf_header *ph)
+{
+       u64 attr_size;
+       int i;
 
-               /*
-                * As the pid is the missing value, we need to fill
-                * it properly. The header.misc value give us nice hint.
-                */
-               bev.pid = HOST_KERNEL_ID;
-               if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER ||
-                   bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL)
-                       bev.pid = DEFAULT_GUEST_KERNEL_ID;
+       for (i = 0 ; attr_pipe_abi_sizes[i]; i++) {
+               if (hdr_sz != attr_pipe_abi_sizes[i]) {
+                       attr_size = bswap_64(hdr_sz);
+                       if (attr_size != hdr_sz)
+                               continue;
 
-               memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id));
-               __event_process_build_id(&bev, filename, session);
+                       ph->needs_swap = true;
+               }
+               pr_debug("Pipe ABI%d perf.data file detected\n", i);
+               return 0;
+       }
+       return -1;
+}
 
-               offset += bev.header.size;
+static int check_magic_endian(u64 magic, uint64_t hdr_sz,
+                             bool is_pipe, struct perf_header *ph)
+{
+       int ret;
+
+       /* check for legacy format */
+       ret = memcmp(&magic, __perf_magic1, sizeof(magic));
+       if (ret == 0) {
+               pr_debug("legacy perf.data format\n");
+               if (is_pipe)
+                       return try_all_pipe_abis(hdr_sz, ph);
+
+               return try_all_file_abis(hdr_sz, ph);
        }
+       /*
+        * the new magic number serves two purposes:
+        * - unique number to identify actual perf.data files
+        * - encode endianness of file
+        */
+
+       /* check magic number with one endianness */
+       if (magic == __perf_magic2)
+               return 0;
+
+       /* check magic number with opposite endianness */
+       if (magic != __perf_magic2_sw)
+               return -1;
+
+       ph->needs_swap = true;
 
        return 0;
 }
 
-static int perf_header__read_build_ids(struct perf_header *header,
-                                      int input, u64 offset, u64 size)
+int perf_file_header__read(struct perf_file_header *header,
+                          struct perf_header *ph, int fd)
 {
-       struct perf_session *session = container_of(header, struct perf_session, header);
-       struct build_id_event bev;
-       char filename[PATH_MAX];
-       u64 limit = offset + size, orig_offset = offset;
-       int err = -1;
+       int ret;
 
-       while (offset < limit) {
-               ssize_t len;
+       lseek(fd, 0, SEEK_SET);
 
-               if (read(input, &bev, sizeof(bev)) != sizeof(bev))
-                       goto out;
+       ret = readn(fd, header, sizeof(*header));
+       if (ret <= 0)
+               return -1;
 
-               if (header->needs_swap)
-                       perf_event_header__bswap(&bev.header);
+       if (check_magic_endian(header->magic,
+                              header->attr_size, false, ph) < 0) {
+               pr_debug("magic/endian check failed\n");
+               return -1;
+       }
 
-               len = bev.header.size - sizeof(bev);
-               if (read(input, filename, len) != len)
-                       goto out;
+       if (ph->needs_swap) {
+               mem_bswap_64(header, offsetof(struct perf_file_header,
+                            adds_features));
+       }
+
+       if (header->size != sizeof(*header)) {
+               /* Support the previous format */
+               if (header->size == offsetof(typeof(*header), adds_features))
+                       bitmap_zero(header->adds_features, HEADER_FEAT_BITS);
+               else
+                       return -1;
+       } else if (ph->needs_swap) {
+               unsigned int i;
                /*
-                * The a1645ce1 changeset:
-                *
-                * "perf: 'perf kvm' tool for monitoring guest performance from host"
+                * feature bitmap is declared as an array of unsigned longs --
+                * not good since its size can differ between the host that
+                * generated the data file and the host analyzing the file.
                 *
-                * Added a field to struct build_id_event that broke the file
-                * format.
-                *
-                * Since the kernel build-id is the first entry, process the
-                * table using the old format if the well known
-                * '[kernel.kallsyms]' string for the kernel build-id has the
-                * first 4 characters chopped off (where the pid_t sits).
+                * We need to handle endianness, but we don't know the size of
+                * the unsigned long where the file was generated. Take a best
+                * guess at determining it: try 64-bit swap first (ie., file
+                * created on a 64-bit host), and check if the hostname feature
+                * bit is set (this feature bit is forced on as of fbe96f2).
+                * If the bit is not, undo the 64-bit swap and try a 32-bit
+                * swap. If the hostname bit is still not set (e.g., older data
+                * file), punt and fallback to the original behavior --
+                * clearing all feature bits and setting buildid.
                 */
-               if (memcmp(filename, "nel.kallsyms]", 13) == 0) {
-                       if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1)
-                               return -1;
-                       return perf_header__read_build_ids_abi_quirk(header, input, offset, size);
-               }
+               for (i = 0; i < BITS_TO_LONGS(HEADER_FEAT_BITS); ++i)
+                       header->adds_features[i] = bswap_64(header->adds_features[i]);
 
-               __event_process_build_id(&bev, filename, session);
+               if (!test_bit(HEADER_HOSTNAME, header->adds_features)) {
+                       for (i = 0; i < BITS_TO_LONGS(HEADER_FEAT_BITS); ++i) {
+                               header->adds_features[i] = bswap_64(header->adds_features[i]);
+                               header->adds_features[i] = bswap_32(header->adds_features[i]);
+                       }
+               }
 
-               offset += bev.header.size;
+               if (!test_bit(HEADER_HOSTNAME, header->adds_features)) {
+                       bitmap_zero(header->adds_features, HEADER_FEAT_BITS);
+                       set_bit(HEADER_BUILD_ID, header->adds_features);
+               }
        }
-       err = 0;
-out:
-       return err;
+
+       memcpy(&ph->adds_features, &header->adds_features,
+              sizeof(ph->adds_features));
+
+       ph->event_offset = header->event_types.offset;
+       ph->event_size   = header->event_types.size;
+       ph->data_offset  = header->data.offset;
+       ph->data_size    = header->data.size;
+       return 0;
 }
 
 static int perf_file_section__process(struct perf_file_section *section,
@@ -1886,41 +1999,37 @@ static int perf_file_section__process(struct perf_file_section *section,
                return 0;
        }
 
-       switch (feat) {
-       case HEADER_TRACE_INFO:
-               trace_report(fd, false);
-               break;
-
-       case HEADER_BUILD_ID:
-               if (perf_header__read_build_ids(ph, fd, section->offset, section->size))
-                       pr_debug("Failed to read buildids, continuing...\n");
-               break;
-       default:
+       if (feat >= HEADER_LAST_FEATURE) {
                pr_debug("unknown feature %d, continuing...\n", feat);
+               return 0;
        }
 
-       return 0;
+       if (!feat_ops[feat].process)
+               return 0;
+
+       return feat_ops[feat].process(section, ph, feat, fd);
 }
 
 static int perf_file_header__read_pipe(struct perf_pipe_file_header *header,
                                       struct perf_header *ph, int fd,
                                       bool repipe)
 {
-       if (readn(fd, header, sizeof(*header)) <= 0 ||
-           memcmp(&header->magic, __perf_magic, sizeof(header->magic)))
-               return -1;
+       int ret;
 
-       if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0)
+       ret = readn(fd, header, sizeof(*header));
+       if (ret <= 0)
                return -1;
 
-       if (header->size != sizeof(*header)) {
-               u64 size = bswap_64(header->size);
+       if (check_magic_endian(header->magic, header->size, true, ph) < 0) {
+               pr_debug("endian/magic failed\n");
+               return -1;
+       }
 
-               if (size != sizeof(*header))
-                       return -1;
+       if (ph->needs_swap)
+               header->size = bswap_64(header->size);
 
-               ph->needs_swap = true;
-       }
+       if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0)
+               return -1;
 
        return 0;
 }
@@ -1941,6 +2050,52 @@ static int perf_header__read_pipe(struct perf_session *session, int fd)
        return 0;
 }
 
+static int read_attr(int fd, struct perf_header *ph,
+                    struct perf_file_attr *f_attr)
+{
+       struct perf_event_attr *attr = &f_attr->attr;
+       size_t sz, left;
+       size_t our_sz = sizeof(f_attr->attr);
+       int ret;
+
+       memset(f_attr, 0, sizeof(*f_attr));
+
+       /* read minimal guaranteed structure */
+       ret = readn(fd, attr, PERF_ATTR_SIZE_VER0);
+       if (ret <= 0) {
+               pr_debug("cannot read %d bytes of header attr\n",
+                        PERF_ATTR_SIZE_VER0);
+               return -1;
+       }
+
+       /* on file perf_event_attr size */
+       sz = attr->size;
+
+       if (ph->needs_swap)
+               sz = bswap_32(sz);
+
+       if (sz == 0) {
+               /* assume ABI0 */
+               sz =  PERF_ATTR_SIZE_VER0;
+       } else if (sz > our_sz) {
+               pr_debug("file uses a more recent and unsupported ABI"
+                        " (%zu bytes extra)\n", sz - our_sz);
+               return -1;
+       }
+       /* what we have not yet read and that we know about */
+       left = sz - PERF_ATTR_SIZE_VER0;
+       if (left) {
+               void *ptr = attr;
+               ptr += PERF_ATTR_SIZE_VER0;
+
+               ret = readn(fd, ptr, left);
+       }
+       /* read perf_file_section, ids are read in caller */
+       ret = readn(fd, &f_attr->ids, sizeof(f_attr->ids));
+
+       return ret <= 0 ? -1 : 0;
+}
+
 int perf_session__read_header(struct perf_session *session, int fd)
 {
        struct perf_header *header = &session->header;
@@ -1956,19 +2111,17 @@ int perf_session__read_header(struct perf_session *session, int fd)
        if (session->fd_pipe)
                return perf_header__read_pipe(session, fd);
 
-       if (perf_file_header__read(&f_header, header, fd) < 0) {
-               pr_debug("incompatible file format\n");
+       if (perf_file_header__read(&f_header, header, fd) < 0)
                return -EINVAL;
-       }
 
-       nr_attrs = f_header.attrs.size / sizeof(f_attr);
+       nr_attrs = f_header.attrs.size / f_header.attr_size;
        lseek(fd, f_header.attrs.offset, SEEK_SET);
 
        for (i = 0; i < nr_attrs; i++) {
                struct perf_evsel *evsel;
                off_t tmp;
 
-               if (readn(fd, &f_attr, sizeof(f_attr)) <= 0)
+               if (read_attr(fd, header, &f_attr) < 0)
                        goto out_errno;
 
                if (header->needs_swap)
@@ -2006,6 +2159,8 @@ int perf_session__read_header(struct perf_session *session, int fd)
                lseek(fd, tmp, SEEK_SET);
        }
 
+       symbol_conf.nr_events = nr_attrs;
+
        if (f_header.event_types.size) {
                lseek(fd, f_header.event_types.offset, SEEK_SET);
                events = malloc(f_header.event_types.size);
@@ -2033,9 +2188,9 @@ out_delete_evlist:
        return -ENOMEM;
 }
 
-int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id,
-                               perf_event__handler_t process,
-                               struct perf_session *session)
+int perf_event__synthesize_attr(struct perf_tool *tool,
+                               struct perf_event_attr *attr, u16 ids, u64 *id,
+                               perf_event__handler_t process)
 {
        union perf_event *ev;
        size_t size;
@@ -2057,22 +2212,23 @@ int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id,
        ev->attr.header.type = PERF_RECORD_HEADER_ATTR;
        ev->attr.header.size = size;
 
-       err = process(ev, NULL, session);
+       err = process(tool, ev, NULL, NULL);
 
        free(ev);
 
        return err;
 }
 
-int perf_session__synthesize_attrs(struct perf_session *session,
+int perf_event__synthesize_attrs(struct perf_tool *tool,
+                                  struct perf_session *session,
                                   perf_event__handler_t process)
 {
        struct perf_evsel *attr;
        int err = 0;
 
        list_for_each_entry(attr, &session->evlist->entries, node) {
-               err = perf_event__synthesize_attr(&attr->attr, attr->ids,
-                                                 attr->id, process, session);
+               err = perf_event__synthesize_attr(tool, &attr->attr, attr->ids,
+                                                 attr->id, process);
                if (err) {
                        pr_debug("failed to create perf header attribute\n");
                        return err;
@@ -2083,23 +2239,23 @@ int perf_session__synthesize_attrs(struct perf_session *session,
 }
 
 int perf_event__process_attr(union perf_event *event,
-                            struct perf_session *session)
+                            struct perf_evlist **pevlist)
 {
        unsigned int i, ids, n_ids;
        struct perf_evsel *evsel;
+       struct perf_evlist *evlist = *pevlist;
 
-       if (session->evlist == NULL) {
-               session->evlist = perf_evlist__new(NULL, NULL);
-               if (session->evlist == NULL)
+       if (evlist == NULL) {
+               *pevlist = evlist = perf_evlist__new(NULL, NULL);
+               if (evlist == NULL)
                        return -ENOMEM;
        }
 
-       evsel = perf_evsel__new(&event->attr.attr,
-                               session->evlist->nr_entries);
+       evsel = perf_evsel__new(&event->attr.attr, evlist->nr_entries);
        if (evsel == NULL)
                return -ENOMEM;
 
-       perf_evlist__add(session->evlist, evsel);
+       perf_evlist__add(evlist, evsel);
 
        ids = event->header.size;
        ids -= (void *)&event->attr.id - (void *)event;
@@ -2113,18 +2269,16 @@ int perf_event__process_attr(union perf_event *event,
                return -ENOMEM;
 
        for (i = 0; i < n_ids; i++) {
-               perf_evlist__id_add(session->evlist, evsel, 0, i,
-                                   event->attr.id[i]);
+               perf_evlist__id_add(evlist, evsel, 0, i, event->attr.id[i]);
        }
 
-       perf_session__update_sample_type(session);
-
        return 0;
 }
 
-int perf_event__synthesize_event_type(u64 event_id, char *name,
+int perf_event__synthesize_event_type(struct perf_tool *tool,
+                                     u64 event_id, char *name,
                                      perf_event__handler_t process,
-                                     struct perf_session *session)
+                                     struct machine *machine)
 {
        union perf_event ev;
        size_t size = 0;
@@ -2137,18 +2291,19 @@ int perf_event__synthesize_event_type(u64 event_id, char *name,
        strncpy(ev.event_type.event_type.name, name, MAX_EVENT_NAME - 1);
 
        ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE;
-       size = strlen(name);
+       size = strlen(ev.event_type.event_type.name);
        size = ALIGN(size, sizeof(u64));
        ev.event_type.header.size = sizeof(ev.event_type) -
                (sizeof(ev.event_type.event_type.name) - size);
 
-       err = process(&ev, NULL, session);
+       err = process(tool, &ev, NULL, machine);
 
        return err;
 }
 
-int perf_event__synthesize_event_types(perf_event__handler_t process,
-                                      struct perf_session *session)
+int perf_event__synthesize_event_types(struct perf_tool *tool,
+                                      perf_event__handler_t process,
+                                      struct machine *machine)
 {
        struct perf_trace_event_type *type;
        int i, err = 0;
@@ -2156,9 +2311,9 @@ int perf_event__synthesize_event_types(perf_event__handler_t process,
        for (i = 0; i < event_count; i++) {
                type = &events[i];
 
-               err = perf_event__synthesize_event_type(type->event_id,
+               err = perf_event__synthesize_event_type(tool, type->event_id,
                                                        type->name, process,
-                                                       session);
+                                                       machine);
                if (err) {
                        pr_debug("failed to create perf header event type\n");
                        return err;
@@ -2168,8 +2323,8 @@ int perf_event__synthesize_event_types(perf_event__handler_t process,
        return err;
 }
 
-int perf_event__process_event_type(union perf_event *event,
-                                  struct perf_session *session __unused)
+int perf_event__process_event_type(struct perf_tool *tool __unused,
+                                  union perf_event *event)
 {
        if (perf_header__push_event(event->event_type.event_type.event_id,
                                    event->event_type.event_type.name) < 0)
@@ -2178,28 +2333,47 @@ int perf_event__process_event_type(union perf_event *event,
        return 0;
 }
 
-int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist,
-                                        perf_event__handler_t process,
-                                  struct perf_session *session __unused)
+int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd,
+                                       struct perf_evlist *evlist,
+                                       perf_event__handler_t process)
 {
        union perf_event ev;
+       struct tracing_data *tdata;
        ssize_t size = 0, aligned_size = 0, padding;
        int err __used = 0;
 
+       /*
+        * We are going to store the size of the data followed
+        * by the data contents. Since the fd descriptor is a pipe,
+        * we cannot seek back to store the size of the data once
+        * we know it. Instead we:
+        *
+        * - write the tracing data to the temp file
+        * - get/write the data size to pipe
+        * - write the tracing data from the temp file
+        *   to the pipe
+        */
+       tdata = tracing_data_get(&evlist->entries, fd, true);
+       if (!tdata)
+               return -1;
+
        memset(&ev, 0, sizeof(ev));
 
        ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA;
-       size = read_tracing_data_size(fd, &evlist->entries);
-       if (size <= 0)
-               return size;
+       size = tdata->size;
        aligned_size = ALIGN(size, sizeof(u64));
        padding = aligned_size - size;
        ev.tracing_data.header.size = sizeof(ev.tracing_data);
        ev.tracing_data.size = aligned_size;
 
-       process(&ev, NULL, session);
+       process(tool, &ev, NULL, NULL);
+
+       /*
+        * The put function will copy all the tracing data
+        * stored in temp file to the pipe.
+        */
+       tracing_data_put(tdata);
 
-       err = read_tracing_data(fd, &evlist->entries);
        write_padded(fd, NULL, 0, padding);
 
        return aligned_size;
@@ -2234,10 +2408,10 @@ int perf_event__process_tracing_data(union perf_event *event,
        return size_read + padding;
 }
 
-int perf_event__synthesize_build_id(struct dso *pos, u16 misc,
+int perf_event__synthesize_build_id(struct perf_tool *tool,
+                                   struct dso *pos, u16 misc,
                                    perf_event__handler_t process,
-                                   struct machine *machine,
-                                   struct perf_session *session)
+                                   struct machine *machine)
 {
        union perf_event ev;
        size_t len;
@@ -2257,12 +2431,13 @@ int perf_event__synthesize_build_id(struct dso *pos, u16 misc,
        ev.build_id.header.size = sizeof(ev.build_id) + len;
        memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len);
 
-       err = process(&ev, NULL, session);
+       err = process(tool, &ev, NULL, machine);
 
        return err;
 }
 
-int perf_event__process_build_id(union perf_event *event,
+int perf_event__process_build_id(struct perf_tool *tool __used,
+                                union perf_event *event,
                                 struct perf_session *session)
 {
        __event_process_build_id(&event->build_id,