steadystate: clean up checks for when steadystate termination is not engaged
[fio.git] / stat.c
diff --git a/stat.c b/stat.c
index 96cd7649b7cf69c4fa82311b97d9532230b86811..80e3b1681245de82e76f2f763088624335374651 100644 (file)
--- a/stat.c
+++ b/stat.c
@@ -18,6 +18,8 @@
 #include "helper_thread.h"
 #include "smalloc.h"
 
+#define LOG_MSEC_SLACK 10
+
 struct fio_mutex *stat_mutex;
 
 void clear_rusage_stat(struct thread_data *td)
@@ -655,6 +657,33 @@ static void show_block_infos(int nr_block_infos, uint32_t *block_infos,
                         i == BLOCK_STATE_COUNT - 1 ? '\n' : ',');
 }
 
+static void show_ss_normal(struct thread_stat *ts, struct buf_output *out)
+{
+       char *p1, *p2;
+       unsigned long long bw_mean, iops_mean;
+       const int i2p = is_power_of_2(ts->kb_base);
+
+       if (!ts->ss_dur)
+               return;
+
+       bw_mean = steadystate_bw_mean(ts);
+       iops_mean = steadystate_iops_mean(ts);
+
+       p1 = num2str(bw_mean / ts->kb_base, 6, ts->kb_base, i2p, ts->unit_base);
+       p2 = num2str(iops_mean, 6, 1, 0, 0);
+
+       log_buf(out, "  steadystate  : attained=%s, bw=%s/s, iops=%s, %s%s=%.3f%s\n",
+               ts->ss_state & __FIO_SS_ATTAINED ? "yes" : "no",
+               p1, p2,
+               ts->ss_state & __FIO_SS_IOPS ? "iops" : "bw",
+               ts->ss_state & __FIO_SS_SLOPE ? " slope": " mean dev",
+               ts->ss_criterion.u.f,
+               ts->ss_state & __FIO_SS_PCT ? "%" : "");
+
+       free(p1);
+       free(p2);
+}
+
 static void show_thread_status_normal(struct thread_stat *ts,
                                      struct group_run_stats *rs,
                                      struct buf_output *out)
@@ -759,6 +788,9 @@ static void show_thread_status_normal(struct thread_stat *ts,
        if (ts->nr_block_infos)
                show_block_infos(ts->nr_block_infos, ts->block_infos,
                                  ts->percentile_list, out);
+
+       if (ts->ss_dur)
+               show_ss_normal(ts, out);
 }
 
 static void show_ddir_status_terse(struct thread_stat *ts,
@@ -1253,6 +1285,56 @@ static struct json_object *show_thread_status_json(struct thread_stat *ts,
                }
        }
 
+       if (ts->ss_dur) {
+               struct json_object *data;
+               struct json_array *iops, *bw;
+               int i, j, k;
+               char ss_buf[64];
+
+               snprintf(ss_buf, sizeof(ss_buf), "%s%s:%f%s",
+                       ts->ss_state & __FIO_SS_IOPS ? "iops" : "bw",
+                       ts->ss_state & __FIO_SS_SLOPE ? "_slope" : "",
+                       (float) ts->ss_limit.u.f,
+                       ts->ss_state & __FIO_SS_PCT ? "%" : "");
+
+               tmp = json_create_object();
+               json_object_add_value_object(root, "steadystate", tmp);
+               json_object_add_value_string(tmp, "ss", ss_buf);
+               json_object_add_value_int(tmp, "duration", (int)ts->ss_dur);
+               json_object_add_value_int(tmp, "attained", (ts->ss_state & __FIO_SS_ATTAINED) > 0);
+
+               snprintf(ss_buf, sizeof(ss_buf), "%f%s", (float) ts->ss_criterion.u.f,
+                       ts->ss_state & __FIO_SS_PCT ? "%" : "");
+               json_object_add_value_string(tmp, "criterion", ss_buf);
+               json_object_add_value_float(tmp, "max_deviation", ts->ss_deviation.u.f);
+               json_object_add_value_float(tmp, "slope", ts->ss_slope.u.f);
+
+               data = json_create_object();
+               json_object_add_value_object(tmp, "data", data);
+               bw = json_create_array();
+               iops = json_create_array();
+
+               /*
+               ** if ss was attained or the buffer is not full,
+               ** ss->head points to the first element in the list.
+               ** otherwise it actually points to the second element
+               ** in the list
+               */
+               if ((ts->ss_state & __FIO_SS_ATTAINED) || !(ts->ss_state & __FIO_SS_BUFFER_FULL))
+                       j = ts->ss_head;
+               else
+                       j = ts->ss_head == 0 ? ts->ss_dur - 1 : ts->ss_head - 1;
+               for (i = 0; i < ts->ss_dur; i++) {
+                       k = (j + i) % ts->ss_dur;
+                       json_array_add_value_int(bw, ts->ss_bw_data[k]);
+                       json_array_add_value_int(iops, ts->ss_iops_data[k]);
+               }
+               json_object_add_value_int(data, "bw_mean", steadystate_bw_mean(ts));
+               json_object_add_value_int(data, "iops_mean", steadystate_iops_mean(ts));
+               json_object_add_value_array(data, "iops", iops);
+               json_object_add_value_array(data, "bw", bw);
+       }
+
        return root;
 }
 
@@ -1576,6 +1658,20 @@ void __show_run_stats(void)
                        ts->block_infos[k] = td->ts.block_infos[k];
 
                sum_thread_stats(ts, &td->ts, idx == 1);
+
+               if (td->o.ss_dur) {
+                       ts->ss_state = td->ss.state;
+                       ts->ss_dur = td->ss.dur;
+                       ts->ss_head = td->ss.head;
+                       ts->ss_bw_data = td->ss.bw_data;
+                       ts->ss_iops_data = td->ss.iops_data;
+                       ts->ss_limit.u.f = td->ss.limit;
+                       ts->ss_slope.u.f = td->ss.slope;
+                       ts->ss_deviation.u.f = td->ss.deviation;
+                       ts->ss_criterion.u.f = td->ss.criterion;
+               }
+               else
+                       ts->ss_dur = ts->ss_state = 0;
        }
 
        for (i = 0; i < nr_ts; i++) {
@@ -1963,6 +2059,7 @@ void regrow_logs(struct thread_data *td)
 {
        regrow_log(td->slat_log);
        regrow_log(td->clat_log);
+       regrow_log(td->clat_hist_log);
        regrow_log(td->lat_log);
        regrow_log(td->bw_log);
        regrow_log(td->iops_log);
@@ -2107,14 +2204,14 @@ static void _add_stat_to_log(struct io_log *iolog, unsigned long elapsed,
                __add_stat_to_log(iolog, ddir, elapsed, log_max);
 }
 
-static void add_log_sample(struct thread_data *td, struct io_log *iolog,
+static long add_log_sample(struct thread_data *td, struct io_log *iolog,
                           unsigned long val, enum fio_ddir ddir,
                           unsigned int bs, uint64_t offset)
 {
        unsigned long elapsed, this_window;
 
        if (!ddir_rw(ddir))
-               return;
+               return 0;
 
        elapsed = mtime_since_now(&td->epoch);
 
@@ -2123,7 +2220,7 @@ static void add_log_sample(struct thread_data *td, struct io_log *iolog,
         */
        if (!iolog->avg_msec) {
                __add_log_sample(iolog, val, ddir, bs, elapsed, offset);
-               return;
+               return 0;
        }
 
        /*
@@ -2137,12 +2234,19 @@ static void add_log_sample(struct thread_data *td, struct io_log *iolog,
         * need to do.
         */
        this_window = elapsed - iolog->avg_last;
-       if (this_window < iolog->avg_msec)
-               return;
+       if (elapsed < iolog->avg_last)
+               return iolog->avg_last - elapsed;
+       else if (this_window < iolog->avg_msec) {
+               int diff = iolog->avg_msec - this_window;
+
+               if (inline_log(iolog) || diff > LOG_MSEC_SLACK)
+                       return diff;
+       }
 
        _add_stat_to_log(iolog, elapsed, td->o.log_max != 0);
 
-       iolog->avg_last = elapsed;
+       iolog->avg_last = elapsed - (this_window - iolog->avg_msec);
+       return iolog->avg_msec;
 }
 
 void finalize_logs(struct thread_data *td, bool unit_logs)
@@ -2186,7 +2290,9 @@ static void add_clat_percentile_sample(struct thread_stat *ts,
 void add_clat_sample(struct thread_data *td, enum fio_ddir ddir,
                     unsigned long usec, unsigned int bs, uint64_t offset)
 {
+       unsigned long elapsed, this_window;
        struct thread_stat *ts = &td->ts;
+       struct io_log *iolog = td->clat_hist_log;
 
        td_io_u_lock(td);
 
@@ -2198,6 +2304,43 @@ void add_clat_sample(struct thread_data *td, enum fio_ddir ddir,
        if (ts->clat_percentiles)
                add_clat_percentile_sample(ts, usec, ddir);
 
+       if (iolog && iolog->hist_msec) {
+               struct io_hist *hw = &iolog->hist_window[ddir];
+
+               hw->samples++;
+               elapsed = mtime_since_now(&td->epoch);
+               if (!hw->hist_last)
+                       hw->hist_last = elapsed;
+               this_window = elapsed - hw->hist_last;
+               
+               if (this_window >= iolog->hist_msec) {
+                       unsigned int *io_u_plat;
+                       unsigned int *dst;
+
+                       /*
+                        * Make a byte-for-byte copy of the latency histogram
+                        * stored in td->ts.io_u_plat[ddir], recording it in a
+                        * log sample. Note that the matching call to free() is
+                        * located in iolog.c after printing this sample to the
+                        * log file.
+                        */
+                       io_u_plat = (unsigned int *) td->ts.io_u_plat[ddir];
+                       dst = malloc(FIO_IO_U_PLAT_NR * sizeof(unsigned int));
+                       memcpy(dst, io_u_plat,
+                               FIO_IO_U_PLAT_NR * sizeof(unsigned int));
+                       __add_log_sample(iolog, (unsigned long )dst, ddir, bs,
+                                               elapsed, offset);
+
+                       /*
+                        * Update the last time we recorded as being now, minus
+                        * any drift in time we encountered before actually
+                        * making the record.
+                        */
+                       hw->hist_last = elapsed - (this_window - iolog->hist_msec);
+                       hw->samples = 0;
+               }
+       }
+
        td_io_u_unlock(td);
 }
 
@@ -2264,10 +2407,13 @@ static int add_bw_samples(struct thread_data *td, struct timeval *t)
        struct thread_stat *ts = &td->ts;
        unsigned long spent, rate;
        enum fio_ddir ddir;
+       unsigned int next, next_log;
+
+       next_log = td->o.bw_avg_time;
 
        spent = mtime_since(&td->bw_sample_time, t);
        if (spent < td->o.bw_avg_time &&
-           td->o.bw_avg_time - spent >= 10)
+           td->o.bw_avg_time - spent >= LOG_MSEC_SLACK)
                return td->o.bw_avg_time - spent;
 
        td_io_u_lock(td);
@@ -2295,7 +2441,8 @@ static int add_bw_samples(struct thread_data *td, struct timeval *t)
                        if (td->o.min_bs[ddir] == td->o.max_bs[ddir])
                                bs = td->o.min_bs[ddir];
 
-                       add_log_sample(td, td->bw_log, rate, ddir, bs, 0);
+                       next = add_log_sample(td, td->bw_log, rate, ddir, bs, 0);
+                       next_log = min(next_log, next);
                }
 
                td->stat_io_bytes[ddir] = td->this_io_bytes[ddir];
@@ -2306,9 +2453,10 @@ static int add_bw_samples(struct thread_data *td, struct timeval *t)
        td_io_u_unlock(td);
 
        if (spent <= td->o.bw_avg_time)
-               return td->o.bw_avg_time;
+               return min(next_log, td->o.bw_avg_time);
 
-       return td->o.bw_avg_time - (1 + spent - td->o.bw_avg_time);
+       next = td->o.bw_avg_time - (1 + spent - td->o.bw_avg_time);
+       return min(next, next_log);
 }
 
 void add_iops_sample(struct thread_data *td, struct io_u *io_u,
@@ -2332,10 +2480,13 @@ static int add_iops_samples(struct thread_data *td, struct timeval *t)
        struct thread_stat *ts = &td->ts;
        unsigned long spent, iops;
        enum fio_ddir ddir;
+       unsigned int next, next_log;
+
+       next_log = td->o.iops_avg_time;
 
        spent = mtime_since(&td->iops_sample_time, t);
        if (spent < td->o.iops_avg_time &&
-           td->o.iops_avg_time - spent >= 10)
+           td->o.iops_avg_time - spent >= LOG_MSEC_SLACK)
                return td->o.iops_avg_time - spent;
 
        td_io_u_lock(td);
@@ -2363,7 +2514,8 @@ static int add_iops_samples(struct thread_data *td, struct timeval *t)
                        if (td->o.min_bs[ddir] == td->o.max_bs[ddir])
                                bs = td->o.min_bs[ddir];
 
-                       add_log_sample(td, td->iops_log, iops, ddir, bs, 0);
+                       next = add_log_sample(td, td->iops_log, iops, ddir, bs, 0);
+                       next_log = min(next_log, next);
                }
 
                td->stat_io_blocks[ddir] = td->this_io_blocks[ddir];
@@ -2374,9 +2526,10 @@ static int add_iops_samples(struct thread_data *td, struct timeval *t)
        td_io_u_unlock(td);
 
        if (spent <= td->o.iops_avg_time)
-               return td->o.iops_avg_time;
+               return min(next_log, td->o.iops_avg_time);
 
-       return td->o.iops_avg_time - (1 + spent - td->o.iops_avg_time);
+       next = td->o.iops_avg_time - (1 + spent - td->o.iops_avg_time);
+       return min(next, next_log);
 }
 
 /*