Update to 3.4-final.
[linux-flexiantxendom0-3.2.10.git] / tools / perf / util / header.c
index 6d58026..c0b70c6 100644 (file)
@@ -291,12 +291,12 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
        if (realname == NULL || filename == NULL || linkname == NULL)
                goto out_free;
 
-       len = snprintf(filename, size, "%s%s%s",
+       len = scnprintf(filename, size, "%s%s%s",
                       debugdir, is_kallsyms ? "/" : "", realname);
        if (mkdir_p(filename, 0755))
                goto out_free;
 
-       snprintf(filename + len, sizeof(filename) - len, "/%s", sbuild_id);
+       snprintf(filename + len, size - len, "/%s", sbuild_id);
 
        if (access(filename, F_OK)) {
                if (is_kallsyms) {
@@ -306,7 +306,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
                        goto out_free;
        }
 
-       len = snprintf(linkname, size, "%s/.build-id/%.2s",
+       len = scnprintf(linkname, size, "%s/.build-id/%.2s",
                       debugdir, sbuild_id);
 
        if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
@@ -1023,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);
@@ -1171,7 +1177,7 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
                goto error;
 
        msz = sizeof(attr);
-       if (sz < (ssize_t)msz)
+       if (sz < msz)
                msz = sz;
 
        for (i = 0 ; i < nre; i++) {
@@ -1315,6 +1321,12 @@ static void print_cpuid(struct perf_header *ph, int fd, FILE *fp)
        free(str);
 }
 
+static void print_branch_stack(struct perf_header *ph __used, int fd __used,
+                              FILE *fp)
+{
+       fprintf(fp, "# contains samples with branch stack\n");
+}
+
 static int __event_process_build_id(struct build_id_event *bev,
                                    char *filename,
                                    struct perf_session *session)
@@ -1519,6 +1531,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
        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),
 };
 
 struct header_print_data {
@@ -1803,35 +1816,101 @@ out_free:
        return err;
 }
 
-static int check_magic_endian(u64 *magic, struct perf_file_header *header,
-                             struct perf_header *ph)
+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)
 {
-       int ret;
+       uint64_t ref_size, attr_size;
+       int i;
 
-       /* check for legacy format */
-       ret = memcmp(magic, __perf_magic1, sizeof(*magic));
-       if (ret == 0) {
-               pr_debug("legacy perf.data format\n");
-               if (!header)
-                       return -1;
+       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;
+
+                       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->attr_size != sizeof(struct perf_file_attr)) {
-                       u64 attr_size = bswap_64(header->attr_size);
+#define PERF_PIPE_HDR_VER0     16
 
-                       if (attr_size != sizeof(struct perf_file_attr))
-                               return -1;
+static const size_t attr_pipe_abi_sizes[] = {
+       [0] = PERF_PIPE_HDR_VER0,
+       0,
+};
+
+/*
+ * 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;
+
+       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;
 
                        ph->needs_swap = true;
                }
+               pr_debug("Pipe ABI%d perf.data file detected\n", i);
                return 0;
        }
+       return -1;
+}
+
+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 same endianness */
-       if (*magic == __perf_magic2)
+       /* check magic number with one endianness */
+       if (magic == __perf_magic2)
                return 0;
 
-       /* check magic number but opposite endianness */
-       if (*magic != __perf_magic2_sw)
+       /* check magic number with opposite endianness */
+       if (magic != __perf_magic2_sw)
                return -1;
 
        ph->needs_swap = true;
@@ -1850,8 +1929,11 @@ int perf_file_header__read(struct perf_file_header *header,
        if (ret <= 0)
                return -1;
 
-       if (check_magic_endian(&header->magic, header, ph) < 0)
+       if (check_magic_endian(header->magic,
+                              header->attr_size, false, ph) < 0) {
+               pr_debug("magic/endian check failed\n");
                return -1;
+       }
 
        if (ph->needs_swap) {
                mem_bswap_64(header, offsetof(struct perf_file_header,
@@ -1938,21 +2020,17 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *header,
        if (ret <= 0)
                return -1;
 
-        if (check_magic_endian(&header->magic, NULL, ph) < 0)
+       if (check_magic_endian(header->magic, header->size, true, ph) < 0) {
+               pr_debug("endian/magic failed\n");
                return -1;
+       }
+
+       if (ph->needs_swap)
+               header->size = bswap_64(header->size);
 
        if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0)
                return -1;
 
-       if (header->size != sizeof(*header)) {
-               u64 size = bswap_64(header->size);
-
-               if (size != sizeof(*header))
-                       return -1;
-
-               ph->needs_swap = true;
-       }
-
        return 0;
 }
 
@@ -1992,6 +2070,7 @@ static int read_attr(int fd, struct perf_header *ph,
 
        /* on file perf_event_attr size */
        sz = attr->size;
+
        if (ph->needs_swap)
                sz = bswap_32(sz);