X-Git-Url: https://git.kernel.dk/?a=blobdiff_plain;f=stat.c;h=80e3b1681245de82e76f2f763088624335374651;hb=32df42c978ffb0faee36ffb0866f7786ab1209e1;hp=96cd7649b7cf69c4fa82311b97d9532230b86811;hpb=0b2eef4940d9818f91f455d0cdb4f37db4fbb158;p=fio.git diff --git a/stat.c b/stat.c index 96cd7649..80e3b168 100644 --- 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); } /*