X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=stat.c;h=765c2b20249b8588d438e357c33f5165e02b2666;hp=f55cb2f5cc8dfdd4fcc3087d6435134e7405c07b;hb=3ce3881b7a301a0f00f199dbead4fa1bef38973a;hpb=ecfd2bb08cc87bc9a1b3d612258f1fdfb4d09698 diff --git a/stat.c b/stat.c index f55cb2f5..765c2b20 100644 --- a/stat.c +++ b/stat.c @@ -16,6 +16,9 @@ #include "lib/pow2.h" #include "lib/output_buffer.h" #include "helper_thread.h" +#include "smalloc.h" + +#define LOG_MSEC_SLACK 10 struct fio_mutex *stat_mutex; @@ -1252,6 +1255,36 @@ static struct json_object *show_thread_status_json(struct thread_stat *ts, } } + /* steady state detection; move this behind json+? */ + if (ts->ss) { + struct json_array *cache; + struct steadystate_data *ss = ts->ss; + int i, x; + char ss_option[64]; + + snprintf(ss_option, sizeof(ss_option), "%s%s:%f%s", + ss->check_iops ? "iops" : "bw", + ss->check_slope ? "_slope" : "", + (float) ss->limit, + ss->pct ? "%" : ""); + + tmp = json_create_object(); + json_object_add_value_object(root, "steadystate", tmp); + json_object_add_value_string(tmp, "ss", ss_option); + json_object_add_value_float(tmp, "limit", (float)ss->limit); + json_object_add_value_int(tmp, "duration", (int)ss->dur); + json_object_add_value_int(tmp, "steadystate_ramptime", ss->ramp_time / 1000000L); + json_object_add_value_int(tmp, "attained", ss->attained); + json_object_add_value_float(tmp, "criterion", ss->criterion); + + cache = json_create_array(); + json_object_add_value_array(tmp, "data", cache); + for (i = 0; i < ss->dur; i++) { + x = (ss->head + i) % ss->dur; + json_array_add_value_int(cache, ss->cache[x]); + } + } + return root; } @@ -1575,6 +1608,11 @@ 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 = &td->ss; + else + ts->ss = NULL; } for (i = 0; i < nr_ts; i++) { @@ -1636,16 +1674,21 @@ void __show_run_stats(void) if (output_format & FIO_OUTPUT_JSON) { struct thread_data *global; char time_buf[32]; - time_t time_p; + struct timeval now; + unsigned long long ms_since_epoch; + + gettimeofday(&now, NULL); + ms_since_epoch = (unsigned long long)(now.tv_sec) * 1000 + + (unsigned long long)(now.tv_usec) / 1000; - time(&time_p); - os_ctime_r((const time_t *) &time_p, time_buf, + os_ctime_r((const time_t *) &now.tv_sec, time_buf, sizeof(time_buf)); time_buf[strlen(time_buf) - 1] = '\0'; root = json_create_object(); json_object_add_value_string(root, "fio version", fio_version_string); - json_object_add_value_int(root, "timestamp", time_p); + json_object_add_value_int(root, "timestamp", now.tv_sec); + json_object_add_value_int(root, "timestamp_ms", ms_since_epoch); json_object_add_value_string(root, "time", time_buf); global = get_global_options(); json_add_job_opts(root, "global options", &global->opt_list, false); @@ -1872,7 +1915,7 @@ static struct io_logs *get_new_log(struct io_log *iolog) new_size = new_samples * log_entry_sz(iolog); - cur_log = malloc(sizeof(*cur_log)); + cur_log = smalloc(sizeof(*cur_log)); if (cur_log) { INIT_FLIST_HEAD(&cur_log->list); cur_log->log = malloc(new_size); @@ -1883,7 +1926,7 @@ static struct io_logs *get_new_log(struct io_log *iolog) iolog->cur_log_max = new_samples; return cur_log; } - free(cur_log); + sfree(cur_log); } return NULL; @@ -1898,9 +1941,15 @@ static struct io_logs *regrow_log(struct io_log *iolog) int i; if (!iolog || iolog->disabled) - return NULL; + goto disable; cur_log = iolog_cur_log(iolog); + if (!cur_log) { + cur_log = get_new_log(iolog); + if (!cur_log) + return NULL; + } + if (cur_log->nr_samples < cur_log->max_samples) return cur_log; @@ -1937,24 +1986,24 @@ static struct io_logs *regrow_log(struct io_log *iolog) dst = get_sample(iolog, cur_log, i); memcpy(dst, src, log_entry_sz(iolog)); } + cur_log->nr_samples = iolog->pending->nr_samples; iolog->pending->nr_samples = 0; return cur_log; +disable: + if (iolog) + iolog->disabled = true; + return NULL; } void regrow_logs(struct thread_data *td) { - if (!regrow_log(td->slat_log)) - td->slat_log->disabled = true; - if (!regrow_log(td->clat_log)) - td->clat_log->disabled = true; - if (!regrow_log(td->lat_log)) - td->lat_log->disabled = true; - if (!regrow_log(td->bw_log)) - td->bw_log->disabled = true; - if (!regrow_log(td->iops_log)) - td->iops_log->disabled = true; - + 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); td->flags &= ~TD_F_REGROW_LOGS; } @@ -1973,11 +2022,14 @@ static struct io_logs *get_cur_log(struct io_log *iolog) return cur_log; /* - * Out of space. If we're in IO offload mode, add a new log chunk - * inline. If we're doing inline submissions, flag 'td' as needing - * a log regrow and we'll take care of it on the submission side. + * Out of space. If we're in IO offload mode, or we're not doing + * per unit logging (hence logging happens outside of the IO thread + * as well), add a new log chunk inline. If we're doing inline + * submissions, flag 'td' as needing a log regrow and we'll take + * care of it on the submission side. */ - if (iolog->td->o.io_submit_mode == IO_MODE_OFFLOAD) + if (iolog->td->o.io_submit_mode == IO_MODE_OFFLOAD || + !per_unit_log(iolog)) return regrow_log(iolog); iolog->td->flags |= TD_F_REGROW_LOGS; @@ -2093,14 +2145,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); @@ -2109,7 +2161,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; } /* @@ -2123,12 +2175,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) @@ -2172,7 +2231,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); @@ -2184,6 +2245,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); } @@ -2250,13 +2348,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; - if (per_unit_log(td->bw_log)) - return 0; + 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); @@ -2284,7 +2382,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]; @@ -2295,9 +2394,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, @@ -2321,13 +2421,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; - if (per_unit_log(td->iops_log)) - return 0; + 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); @@ -2355,7 +2455,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]; @@ -2366,9 +2467,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); } /* @@ -2384,7 +2486,7 @@ int calc_log_samples(void) fio_gettime(&now, NULL); for_each_td(td, i) { - if (!ramp_time_over(td) || + if (in_ramp_time(td) || !(td->runstate == TD_RUNNING || td->runstate == TD_VERIFYING)) { next = min(td->o.iops_avg_time, td->o.bw_avg_time); continue;