struct perf_session *session __unused)
 {
        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);
 
        process(&ev, NULL, session);
 
-       err = read_tracing_data(fd, &evlist->entries);
+       /*
+        * The put function will copy all the tracing data
+        * stored in temp file to the pipe.
+        */
+       tracing_data_put(tdata);
+
        write_padded(fd, NULL, 0, padding);
 
        return aligned_size;
 
                die("Can't read '%s'", file);
 
        /* put in zeros for file size, then fill true size later */
-       write_or_die(&size, hdr_sz);
+       if (hdr_sz)
+               write_or_die(&size, hdr_sz);
 
        do {
                r = read(fd, buf, BUFSIZ);
        if (bigendian())
                sizep += sizeof(u64) - hdr_sz;
 
-       if (pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0)
+       if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0)
                die("writing to %s", output_file);
 }
 
        return nr_tracepoints > 0 ? path.next : NULL;
 }
 
+static void
+put_tracepoints_path(struct tracepoint_path *tps)
+{
+       while (tps) {
+               struct tracepoint_path *t = tps;
+
+               tps = tps->next;
+               free(t->name);
+               free(t->system);
+               free(t);
+       }
+}
+
 bool have_tracepoints(struct list_head *pattrs)
 {
        struct perf_evsel *pos;
        return false;
 }
 
-int read_tracing_data(int fd, struct list_head *pattrs)
+static void tracing_data_header(void)
 {
-       char buf[BUFSIZ];
-       struct tracepoint_path *tps = get_tracepoints_path(pattrs);
-
-       /*
-        * What? No tracepoints? No sense writing anything here, bail out.
-        */
-       if (tps == NULL)
-               return -1;
-
-       output_fd = fd;
+       char buf[20];
 
+       /* just guessing this is someone's birthday.. ;) */
        buf[0] = 23;
        buf[1] = 8;
        buf[2] = 68;
        /* save page_size */
        page_size = sysconf(_SC_PAGESIZE);
        write_or_die(&page_size, 4);
+}
+
+struct tracing_data *tracing_data_get(struct list_head *pattrs,
+                                     int fd, bool temp)
+{
+       struct tracepoint_path *tps;
+       struct tracing_data *tdata;
+
+       output_fd = fd;
+
+       tps = get_tracepoints_path(pattrs);
+       if (!tps)
+               return NULL;
 
+       tdata = malloc_or_die(sizeof(*tdata));
+       tdata->temp = temp;
+       tdata->size = 0;
+
+       if (temp) {
+               int temp_fd;
+
+               snprintf(tdata->temp_file, sizeof(tdata->temp_file),
+                        "/tmp/perf-XXXXXX");
+               if (!mkstemp(tdata->temp_file))
+                       die("Can't make temp file");
+
+               temp_fd = open(tdata->temp_file, O_RDWR);
+               if (temp_fd < 0)
+                       die("Can't read '%s'", tdata->temp_file);
+
+               /*
+                * Set the temp file the default output, so all the
+                * tracing data are stored into it.
+                */
+               output_fd = temp_fd;
+       }
+
+       tracing_data_header();
        read_header_files();
        read_ftrace_files(tps);
        read_event_files(tps);
        read_proc_kallsyms();
        read_ftrace_printk();
 
-       return 0;
+       /*
+        * All tracing data are stored by now, we can restore
+        * the default output file in case we used temp file.
+        */
+       if (temp) {
+               tdata->size = lseek(output_fd, 0, SEEK_CUR);
+               close(output_fd);
+               output_fd = fd;
+       }
+
+       put_tracepoints_path(tps);
+       return tdata;
 }
 
-ssize_t read_tracing_data_size(int fd, struct list_head *pattrs)
+void tracing_data_put(struct tracing_data *tdata)
 {
-       ssize_t size;
-       int err = 0;
+       if (tdata->temp) {
+               record_file(tdata->temp_file, 0);
+               unlink(tdata->temp_file);
+       }
 
-       calc_data_size = 1;
-       err = read_tracing_data(fd, pattrs);
-       size = calc_data_size - 1;
-       calc_data_size = 0;
+       free(tdata);
+}
 
-       if (err < 0)
-               return err;
+int read_tracing_data(int fd, struct list_head *pattrs)
+{
+       struct tracing_data *tdata;
 
-       return size;
+       /*
+        * We work over the real file, so we can write data
+        * directly, no temp file is needed.
+        */
+       tdata = tracing_data_get(pattrs, fd, false);
+       if (!tdata)
+               return -ENOMEM;
+
+       tracing_data_put(tdata);
+       return 0;
 }