+ 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 sample_size)
+{
+ struct io_sample *s;
+ int log_offset;
+ uint64_t i, nr_samples;
+
+ if (!sample_size)
+ return;
+
+ s = __get_sample(samples, 0, 0);
+ if (s->__ddir & 0x80000000)
+ log_offset = 1;
+ else
+ log_offset = 0;
+
+ nr_samples = sample_size / __log_entry_sz(log_offset);
+
+ for (i = 0; i < nr_samples; i++) {
+ 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,
+ io_sample_ddir(s), 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,
+ io_sample_ddir(s), s->bs,
+ (unsigned long long) so->offset);
+ }
+ }
+}
+
+#ifdef CONFIG_ZLIB
+
+struct iolog_flush_data {
+ struct tp_work work;
+ struct io_log *log;
+ void *samples;
+ uint64_t nr_samples;
+};
+
+struct iolog_compress {
+ struct flist_head list;
+ void *buf;
+ size_t len;
+ unsigned int seq;
+};
+
+#define GZ_CHUNK 131072
+
+static struct iolog_compress *get_new_chunk(unsigned int seq)
+{
+ struct iolog_compress *c;
+
+ c = malloc(sizeof(*c));
+ INIT_FLIST_HEAD(&c->list);
+ c->buf = malloc(GZ_CHUNK);
+ c->len = 0;
+ c->seq = seq;
+ return c;
+}
+
+static void free_chunk(struct iolog_compress *ic)
+{
+ free(ic->buf);
+ free(ic);
+}
+
+static int z_stream_init(z_stream *stream, int gz_hdr)
+{
+ int wbits = 15;
+
+ stream->zalloc = Z_NULL;
+ stream->zfree = Z_NULL;
+ stream->opaque = Z_NULL;
+ stream->next_in = Z_NULL;
+
+ if (gz_hdr)
+ wbits += 32;
+
+ if (inflateInit2(stream, wbits) != 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, FILE *f,
+ struct flush_chunk_iter *iter)
+{
+ int ret;
+
+ ret = inflateEnd(stream);
+ if (ret != Z_OK)
+ log_err("fio: failed to end log inflation (%d)\n", ret);
+
+ flush_samples(f, iter->buf, iter->buf_used);
+ free(iter->buf);
+ iter->buf = NULL;
+ iter->buf_size = iter->buf_used = 0;
+}
+
+static int flush_chunk(struct iolog_compress *ic, int gz_hdr, FILE *f,
+ z_stream *stream, struct flush_chunk_iter *iter)
+{
+ if (ic->seq != iter->seq) {
+ if (iter->seq)
+ finish_chunk(stream, f, iter);
+
+ z_stream_init(stream, gz_hdr);
+ 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) {
+ size_t this_out = iter->buf_size - iter->buf_used;
+ int err;
+
+ stream->avail_out = this_out;
+ 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 += this_out - stream->avail_out;
+
+ if (!stream->avail_out) {
+ iter->buf_size += iter->chunk_sz;
+ iter->buf = realloc(iter->buf, iter->buf_size);
+ continue;
+ }
+
+ if (err == Z_STREAM_END)
+ break;
+ }
+
+ 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);
+
+ if (log->log_gz_store)
+ fwrite(ic->buf, ic->len, 1, f);
+ else
+ flush_chunk(ic, log->log_gz_store, f, &stream, &iter);
+
+ free_chunk(ic);
+ }
+
+ if (iter.seq) {
+ finish_chunk(&stream, f, &iter);
+ free(iter.buf);
+ }
+}
+
+int iolog_file_inflate(const char *file)
+{
+ struct flush_chunk_iter iter = { .chunk_sz = 64 * 1024 * 1024, };
+ struct iolog_compress ic;
+ z_stream stream;
+ struct stat sb;
+ ssize_t ret;