const char *filename)
{
struct io_log *l;
+ int i;
+ struct io_u_plat_entry *entry;
+ struct flist_head *list;
l = scalloc(1, sizeof(*l));
INIT_FLIST_HEAD(&l->io_logs);
l->log_gz = p->log_gz;
l->log_gz_store = p->log_gz_store;
l->avg_msec = p->avg_msec;
+ l->hist_msec = p->hist_msec;
+ l->hist_coarseness = p->hist_coarseness;
l->filename = strdup(filename);
l->td = p->td;
+ /* Initialize histogram lists for each r/w direction,
+ * with initial io_u_plat of all zeros:
+ */
+ for (i = 0; i < DDIR_RWDIR_CNT; i++) {
+ list = &l->hist_window[i].list.list;
+ INIT_FLIST_HEAD(list);
+ entry = calloc(1, sizeof(struct io_u_plat_entry));
+ flist_add(&entry->list, list);
+ }
+
+ if (l->td && l->td->o.io_submit_mode != IO_MODE_OFFLOAD) {
+ struct io_logs *p;
+
+ p = calloc(1, sizeof(*l->pending));
+ p->max_samples = DEF_LOG_ENTRIES;
+ p->log = calloc(p->max_samples, log_entry_sz(l));
+ l->pending = p;
+ }
+
if (l->log_offset)
l->log_ddir_mask = LOG_OFFSET_SAMPLE_BIT;
if (l->log_gz && !p->td)
l->log_gz = 0;
else if (l->log_gz || l->log_gz_store) {
- pthread_mutex_init(&l->chunk_lock, NULL);
+ mutex_init_pshared(&l->chunk_lock);
p->td->flags |= TD_F_COMPRESS_LOG;
}
cur_log = flist_first_entry(&log->io_logs, struct io_logs, list);
flist_del_init(&cur_log->list);
free(cur_log->log);
+ sfree(cur_log);
}
+ if (log->pending) {
+ free(log->pending->log);
+ free(log->pending);
+ log->pending = NULL;
+ }
+
+ free(log->pending);
free(log->filename);
sfree(log);
}
+static inline unsigned long hist_sum(int j, int stride, unsigned int *io_u_plat,
+ unsigned int *io_u_plat_last)
+{
+ unsigned long sum;
+ int k;
+
+ for (k = sum = 0; k < stride; k++)
+ sum += io_u_plat[j + k] - io_u_plat_last[j + k];
+
+ return sum;
+}
+
+void flush_hist_samples(FILE *f, int hist_coarseness, void *samples,
+ uint64_t sample_size)
+{
+ struct io_sample *s;
+ int log_offset;
+ uint64_t i, j, nr_samples;
+ struct io_u_plat_entry *entry, *entry_before;
+ unsigned int *io_u_plat;
+ unsigned int *io_u_plat_before;
+
+ int stride = 1 << hist_coarseness;
+
+ if (!sample_size)
+ return;
+
+ s = __get_sample(samples, 0, 0);
+ log_offset = (s->__ddir & LOG_OFFSET_SAMPLE_BIT) != 0;
+
+ nr_samples = sample_size / __log_entry_sz(log_offset);
+
+ for (i = 0; i < nr_samples; i++) {
+ s = __get_sample(samples, log_offset, i);
+
+ entry = (struct io_u_plat_entry *) s->val;
+ io_u_plat = entry->io_u_plat;
+
+ entry_before = flist_first_entry(&entry->list, struct io_u_plat_entry, list);
+ io_u_plat_before = entry_before->io_u_plat;
+
+ fprintf(f, "%lu, %u, %u, ", (unsigned long)s->time,
+ io_sample_ddir(s), s->bs);
+ for (j = 0; j < FIO_IO_U_PLAT_NR - stride; j += stride) {
+ fprintf(f, "%lu, ", hist_sum(j, stride, io_u_plat, io_u_plat_before));
+ }
+ fprintf(f, "%lu\n", (unsigned long)
+ hist_sum(FIO_IO_U_PLAT_NR - stride, stride, io_u_plat,
+ io_u_plat_before));
+
+ flist_del(&entry_before->list);
+ free(entry_before);
+ }
+}
+
void flush_samples(FILE *f, void *samples, uint64_t sample_size)
{
struct io_sample *s;
{
int wbits = 15;
+ memset(stream, 0, sizeof(*stream));
stream->zalloc = Z_NULL;
stream->zfree = Z_NULL;
stream->opaque = Z_NULL;
ret = inflateEnd(stream);
if (ret != Z_OK)
- log_err("fio: failed to end log inflation (%d)\n", ret);
+ log_err("fio: failed to end log inflation seq %d (%d)\n",
+ iter->seq, ret);
flush_samples(f, iter->buf, iter->buf_used);
free(iter->buf);
{
size_t ret;
- dprint(FD_COMPRESS, "inflate chunk size=%lu, seq=%u",
+ dprint(FD_COMPRESS, "inflate chunk size=%lu, seq=%u\n",
(unsigned long) ic->len, ic->seq);
if (ic->seq != iter->seq) {
ret = (void *) stream->next_in - ic->buf;
- dprint(FD_COMPRESS, "inflated to size=%lu\n", (unsigned long) ret);
+ dprint(FD_COMPRESS, "inflated to size=%lu\n", (unsigned long) iter->buf_size);
return ret;
}
#endif
-void flush_log(struct io_log *log, int do_append)
+void flush_log(struct io_log *log, bool do_append)
{
void *buf;
FILE *f;
cur_log = flist_first_entry(&log->io_logs, struct io_logs, list);
flist_del_init(&cur_log->list);
- flush_samples(f, cur_log->log, cur_log->nr_samples * log_entry_sz(log));
+
+ if (log == log->td->clat_hist_log)
+ flush_hist_samples(f, log->hist_coarseness, cur_log->log,
+ cur_log->nr_samples * log_entry_sz(log));
+ else
+ flush_samples(f, cur_log->log, cur_log->nr_samples * log_entry_sz(log));
+
+ sfree(cur_log);
}
fclose(f);
static int gz_work(struct iolog_flush_data *data)
{
- struct iolog_compress *c;
+ struct iolog_compress *c = NULL;
struct flist_head list;
unsigned int seq;
z_stream stream;
stream.next_in = (void *) data->samples;
stream.avail_in = data->nr_samples * log_entry_sz(data->log);
- dprint(FD_COMPRESS, "deflate input size=%lu, seq=%u\n",
- (unsigned long) stream.avail_in, seq);
+ dprint(FD_COMPRESS, "deflate input size=%lu, seq=%u, log=%s\n",
+ (unsigned long) stream.avail_in, seq,
+ data->log->filename);
do {
+ if (c)
+ dprint(FD_COMPRESS, "seq=%d, chunk=%lu\n", seq, c->len);
c = get_new_chunk(seq);
stream.avail_out = GZ_CHUNK;
stream.next_out = c->buf;
stream.avail_out = GZ_CHUNK - c->len;
ret = deflate(&stream, Z_FINISH);
- if (ret == Z_STREAM_END)
- c->len = GZ_CHUNK - stream.avail_out;
- else {
+ if (ret < 0) {
+ /*
+ * Z_BUF_ERROR is special, it just means we need more
+ * output space. We'll handle that below. Treat any other
+ * error as fatal.
+ */
+ if (ret != Z_BUF_ERROR) {
+ log_err("fio: deflate log (%d)\n", ret);
+ flist_del(&c->list);
+ free_chunk(c);
+ goto err;
+ }
+ }
+
+ total -= c->len;
+ c->len = GZ_CHUNK - stream.avail_out;
+ total += c->len;
+ dprint(FD_COMPRESS, "seq=%d, chunk=%lu\n", seq, c->len);
+
+ if (ret != Z_STREAM_END) {
do {
c = get_new_chunk(seq);
stream.avail_out = GZ_CHUNK;
c->len = GZ_CHUNK - stream.avail_out;
total += c->len;
flist_add_tail(&c->list, &list);
+ dprint(FD_COMPRESS, "seq=%d, chunk=%lu\n", seq, c->len);
} while (ret != Z_STREAM_END);
}
data->samples = cur_log->log;
data->nr_samples = cur_log->nr_samples;
- cur_log->nr_samples = 0;
- cur_log->max_samples = 0;
- cur_log->log = NULL;
+ sfree(cur_log);
gz_work(data);
}
{
struct iolog_flush_data *data;
- io_u_quiesce(log->td);
-
data = malloc(sizeof(*data));
if (!data)
return 1;
return ret;
}
+static int write_clat_hist_log(struct thread_data *td, int try, bool unit_log)
+{
+ int ret;
+
+ if (!unit_log)
+ return 0;
+
+ ret = __write_log(td, td->clat_hist_log, try);
+ if (!ret)
+ td->clat_hist_log = NULL;
+
+ return ret;
+}
+
static int write_lat_log(struct thread_data *td, int try, bool unit_log)
{
int ret;
SLAT_LOG_MASK = 4,
CLAT_LOG_MASK = 8,
IOPS_LOG_MASK = 16,
+ CLAT_HIST_LOG_MASK = 32,
- ALL_LOG_NR = 5,
+ ALL_LOG_NR = 6,
};
struct log_type {
.mask = IOPS_LOG_MASK,
.fn = write_iops_log,
},
+ {
+ .mask = CLAT_HIST_LOG_MASK,
+ .fn = write_clat_hist_log,
+ }
};
void td_writeout_logs(struct thread_data *td, bool unit_logs)