+ size_t size = 1048576;
+ void *buf;
+
+ buf = malloc(size);
+ setvbuf(f, buf, _IOFBF, size);
+ return buf;
+}
+
+static void clear_file_buffer(void *buf)
+{
+ free(buf);
+}
+#else
+static void *set_file_buffer(FILE *f)
+{
+ return NULL;
+}
+
+static void clear_file_buffer(void *buf)
+{
+}
+#endif
+
+void free_log(struct io_log *log)
+{
+ free(log->log);
+ free(log->filename);
+ free(log);
+}
+
+static void flush_samples(FILE *f, void *samples, uint64_t nr_samples,
+ int log_offset)
+{
+ uint64_t i;
+
+ for (i = 0; i < nr_samples; i++) {
+ struct io_sample *s = __get_sample(samples, log_offset, i);
+
+ if (!log_offset) {
+ fprintf(f, "%lu, %lu, %u, %u\n",
+ (unsigned long) s->time,
+ (unsigned long) s->val,
+ s->ddir, s->bs);
+ } else {
+ struct io_sample_offset *so = (void *) s;
+
+ fprintf(f, "%lu, %lu, %u, %u, %llu\n",
+ (unsigned long) s->time,
+ (unsigned long) s->val,
+ s->ddir, s->bs,
+ (unsigned long long) so->offset);
+ }
+ }
+}
+
+#ifdef CONFIG_ZLIB
+static int z_stream_init(z_stream *stream)
+{
+ stream->zalloc = Z_NULL;
+ stream->zfree = Z_NULL;
+ stream->opaque = Z_NULL;
+ stream->next_in = Z_NULL;
+
+ if (inflateInit(stream) != Z_OK)
+ return 1;
+
+ return 0;
+}
+
+struct flush_chunk_iter {
+ unsigned int seq;
+ void *buf;
+ size_t buf_size;
+ size_t buf_used;
+ size_t chunk_sz;
+};
+
+static void finish_chunk(z_stream *stream, int log_offset, FILE *f,
+ struct flush_chunk_iter *iter)
+{
+ uint64_t nr_samples;
+ int ret;
+
+ ret = inflateEnd(stream);
+ if (ret != Z_OK)
+ log_err("fio: failed to end log inflation (%d)\n", ret);
+
+ nr_samples = iter->buf_used / __log_entry_sz(log_offset);
+ flush_samples(f, iter->buf, nr_samples, log_offset);
+ free(iter->buf);
+ iter->buf = NULL;
+ iter->buf_size = iter->buf_used = 0;
+}
+
+static int flush_chunk(struct iolog_compress *ic, int log_offset, FILE *f,
+ z_stream *stream, struct flush_chunk_iter *iter)
+{
+ if (ic->seq != iter->seq) {
+ if (iter->seq)
+ finish_chunk(stream, log_offset, f, iter);
+
+ z_stream_init(stream);
+ iter->seq = ic->seq;
+ }
+
+ stream->avail_in = ic->len;
+ stream->next_in = ic->buf;
+
+ if (!iter->buf_size) {
+ iter->buf_size = iter->chunk_sz;
+ iter->buf = malloc(iter->buf_size);
+ }
+
+ while (stream->avail_in) {
+ int err;
+
+ stream->avail_out = iter->buf_size - iter->buf_used;
+ stream->next_out = iter->buf + iter->buf_used;
+
+ err = inflate(stream, Z_NO_FLUSH);
+ if (err < 0) {
+ log_err("fio: failed inflating log: %d\n", err);
+ break;
+ }
+
+ iter->buf_used += iter->buf_size - iter->buf_used - stream->avail_out;
+ }
+
+ free_chunk(ic);
+ return 0;
+}
+
+static void flush_gz_chunks(struct io_log *log, FILE *f)
+{
+ struct flush_chunk_iter iter = { .chunk_sz = log->log_gz, };
+ struct flist_head *node;
+ z_stream stream;
+
+ while (!flist_empty(&log->chunk_list)) {
+ struct iolog_compress *ic;
+
+ node = log->chunk_list.next;
+ ic = flist_entry(node, struct iolog_compress, list);
+ flist_del(&ic->list);
+ flush_chunk(ic, log->log_offset, f, &stream, &iter);
+ }
+
+ if (iter.seq) {
+ finish_chunk(&stream, log->log_offset, f, &iter);
+ free(iter.buf);
+ }
+}
+
+#else
+
+static void flush_gz_chunks(struct io_log *log, FILE *f)
+{
+}
+
+#endif
+
+void flush_log(struct io_log *log)
+{
+ void *buf;