void show_group_stats(struct group_run_stats *rs)
{
char *p1, *p2, *p3, *p4;
- const char *ddir_str[] = { " READ", " WRITE" };
+ const char *ddir_str[] = { " READ", " WRITE" , " TRIM"};
int i;
log_info("\nRun status group %d (all jobs):\n", rs->groupid);
- for (i = 0; i <= DDIR_WRITE; i++) {
+ for (i = 0; i < DDIR_RWDIR_CNT; i++) {
const int i2p = is_power_of_2(rs->kb_base);
if (!rs->max_run[i])
}
#define ts_total_io_u(ts) \
- ((ts)->total_io_u[0] + (ts)->total_io_u[1])
+ ((ts)->total_io_u[DDIR_READ] + (ts)->total_io_u[DDIR_WRITE] +\
+ (ts)->total_io_u[DDIR_TRIM])
static void stat_calc_dist(unsigned int *map, unsigned long total,
double *io_u_dist)
static void show_ddir_status(struct group_run_stats *rs, struct thread_stat *ts,
int ddir)
{
- const char *ddir_str[] = { "read ", "write" };
+ const char *ddir_str[] = { "read ", "write", "trim" };
unsigned long min, max, runt;
unsigned long long bw, iops;
double mean, dev;
double io_u_dist[FIO_IO_U_MAP_NR];
double io_u_lat_u[FIO_IO_U_LAT_U_NR];
double io_u_lat_m[FIO_IO_U_LAT_M_NR];
+ time_t time_p;
+ char time_buf[64];
- if (!(ts->io_bytes[0] + ts->io_bytes[1]) &&
- !(ts->total_io_u[0] + ts->total_io_u[1]))
+ if (!(ts->io_bytes[DDIR_READ] + ts->io_bytes[DDIR_WRITE] +
+ ts->io_bytes[DDIR_TRIM]) && !(ts->total_io_u[DDIR_READ] +
+ ts->total_io_u[DDIR_WRITE] + ts->total_io_u[DDIR_TRIM]))
return;
+ time(&time_p);
+ ctime_r((const time_t *) &time_p, time_buf);
+
if (!ts->error) {
- log_info("%s: (groupid=%d, jobs=%d): err=%2d: pid=%d\n",
+ log_info("%s: (groupid=%d, jobs=%d): err=%2d: pid=%d: %s",
ts->name, ts->groupid, ts->members,
- ts->error, (int) ts->pid);
+ ts->error, (int) ts->pid, time_buf);
} else {
- log_info("%s: (groupid=%d, jobs=%d): err=%2d (%s): pid=%d\n",
+ log_info("%s: (groupid=%d, jobs=%d): err=%2d (%s): pid=%d: %s",
ts->name, ts->groupid, ts->members,
- ts->error, ts->verror, (int) ts->pid);
+ ts->error, ts->verror, (int) ts->pid,
+ time_buf);
}
if (strlen(ts->description))
show_ddir_status(rs, ts, DDIR_READ);
if (ts->io_bytes[DDIR_WRITE])
show_ddir_status(rs, ts, DDIR_WRITE);
+ if (ts->io_bytes[DDIR_TRIM])
+ show_ddir_status(rs, ts, DDIR_TRIM);
stat_calc_lat_u(ts, io_u_lat_u);
stat_calc_lat_m(ts, io_u_lat_m);
if (ts->runtime[ddir]) {
uint64_t runt = ts->runtime[ddir];
- bw = ts->io_bytes[ddir] / runt;
+ bw = ((1000 * ts->io_bytes[ddir]) / runt) / 1024;
iops = (1000 * (uint64_t) ts->total_io_u[ddir]) / runt;
}
/* General Info */
log_info("2;%s;%d;%d", ts->name, ts->groupid, ts->error);
/* Log Read Status */
- show_ddir_status_terse(ts, rs, 0);
+ show_ddir_status_terse(ts, rs, DDIR_READ);
/* Log Write Status */
- show_ddir_status_terse(ts, rs, 1);
+ show_ddir_status_terse(ts, rs, DDIR_WRITE);
+ /* Log Trim Status */
+ show_ddir_status_terse(ts, rs, DDIR_TRIM);
/* CPU Usage */
if (ts->total_run_time) {
log_info("\n");
}
-#define FIO_TERSE_VERSION "3"
-
-static void show_thread_status_terse_v3(struct thread_stat *ts,
- struct group_run_stats *rs)
+static void show_thread_status_terse_v3_v4(struct thread_stat *ts,
+ struct group_run_stats *rs, int ver)
{
double io_u_dist[FIO_IO_U_MAP_NR];
double io_u_lat_u[FIO_IO_U_LAT_U_NR];
int i;
/* General Info */
- log_info("%s;%s;%s;%d;%d", FIO_TERSE_VERSION, fio_version_string,
+ log_info("%s;%s;%s;%d;%d", ver, fio_version_string,
ts->name, ts->groupid, ts->error);
/* Log Read Status */
- show_ddir_status_terse(ts, rs, 0);
+ show_ddir_status_terse(ts, rs, DDIR_READ);
/* Log Write Status */
- show_ddir_status_terse(ts, rs, 1);
+ show_ddir_status_terse(ts, rs, DDIR_WRITE);
+ /* Log Trim Status */
+ if (ver == 4)
+ show_ddir_status_terse(ts, rs, DDIR_TRIM);
/* CPU Usage */
if (ts->total_run_time) {
/* Additional output if continue_on_error set - default off*/
if (ts->continue_on_error)
log_info(";%lu;%d", ts->total_err_count, ts->first_error);
- log_info("\n");
/* Additional output if description is set */
if (strlen(ts->description))
log_info(";%s", ts->description);
+
+ log_info("\n");
}
static void show_thread_status_terse(struct thread_stat *ts,
{
if (terse_version == 2)
show_thread_status_terse_v2(ts, rs);
- else if (terse_version == 3)
- show_thread_status_terse_v3(ts, rs);
+ else if (terse_version == 3 || terse_version == 4)
+ show_thread_status_terse_v3_v4(ts, rs, terse_version);
else
log_err("fio: bad terse version!? %d\n", terse_version);
}
{
int i;
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < DDIR_RWDIR_CNT; i++) {
if (dst->max_run[i] < src->max_run[i])
dst->max_run[i] = src->max_run[i];
if (dst->min_run[i] && dst->min_run[i] > src->min_run[i])
{
int l, k;
- for (l = 0; l <= DDIR_WRITE; l++) {
+ for (l = 0; l < DDIR_RWDIR_CNT; l++) {
sum_stat(&dst->clat_stat[l], &src->clat_stat[l], nr);
sum_stat(&dst->slat_stat[l], &src->slat_stat[l], nr);
sum_stat(&dst->lat_stat[l], &src->lat_stat[l], nr);
for (k = 0; k < FIO_IO_U_LAT_M_NR; k++)
dst->io_u_lat_m[k] += src->io_u_lat_m[k];
- for (k = 0; k <= 2; k++) {
+ for (k = 0; k < DDIR_RWDIR_CNT; k++) {
dst->total_io_u[k] += src->total_io_u[k];
dst->short_io_u[k] += src->short_io_u[k];
}
- for (k = 0; k <= DDIR_WRITE; k++) {
+ for (k = 0; k < DDIR_RWDIR_CNT; k++) {
int m;
for (m = 0; m < FIO_IO_U_PLAT_NR; m++)
dst->io_u_plat[k][m] += src->io_u_plat[k][m];
void init_group_run_stat(struct group_run_stats *gs)
{
+ int i;
memset(gs, 0, sizeof(*gs));
- gs->min_bw[0] = gs->min_run[0] = ~0UL;
- gs->min_bw[1] = gs->min_run[1] = ~0UL;
+
+ for (i = 0; i < DDIR_RWDIR_CNT; i++)
+ gs->min_bw[i] = gs->min_run[i] = ~0UL;
}
void init_thread_stat(struct thread_stat *ts)
memset(ts, 0, sizeof(*ts));
- for (j = 0; j <= DDIR_WRITE; j++) {
+ for (j = 0; j < DDIR_RWDIR_CNT; j++) {
ts->lat_stat[j].min_val = -1UL;
ts->clat_stat[j].min_val = -1UL;
ts->slat_stat[j].min_val = -1UL;
rs = &runstats[ts->groupid];
rs->kb_base = ts->kb_base;
- for (j = 0; j <= DDIR_WRITE; j++) {
+ for (j = 0; j < DDIR_RWDIR_CNT; j++) {
if (!ts->runtime[j])
continue;
if (ts->runtime[j] < rs->min_run[j] || !rs->min_run[j])
bw = 0;
if (ts->runtime[j]) {
- unsigned long runt;
+ unsigned long runt = ts->runtime[j];
+ unsigned long long kb;
- runt = ts->runtime[j];
- bw = ts->io_bytes[j] / runt;
+ kb = ts->io_bytes[j] / rs->kb_base;
+ bw = kb * 1000 / runt;
}
if (bw < rs->min_bw[j])
rs->min_bw[j] = bw;
}
for (i = 0; i < groupid + 1; i++) {
- unsigned long max_run[2];
+ int ddir;
rs = &runstats[i];
- max_run[0] = rs->max_run[0];
- max_run[1] = rs->max_run[1];
- if (rs->max_run[0])
- rs->agg[0] = (rs->io_kb[0] * 1000) / max_run[0];
- if (rs->max_run[1])
- rs->agg[1] = (rs->io_kb[1] * 1000) / max_run[1];
+ for (ddir = 0; ddir < DDIR_RWDIR_CNT; ddir++) {
+ if (rs->max_run[ddir])
+ rs->agg[ddir] = (rs->io_kb[ddir] * 1000) /
+ rs->max_run[ddir];
+ }
}
/*
else if (!terse_output)
show_disk_util(0);
- free_disk_util();
-
free(runstats);
free(threadstats);
}
+static void *__show_running_run_stats(void *arg)
+{
+ struct thread_data *td;
+ unsigned long long *rt;
+ struct timeval tv;
+ int i;
+
+ rt = malloc(thread_number * sizeof(unsigned long long));
+ fio_gettime(&tv, NULL);
+
+ for_each_td(td, i) {
+ rt[i] = mtime_since(&td->start, &tv);
+ if (td_read(td) && td->io_bytes[DDIR_READ])
+ td->ts.runtime[DDIR_READ] += rt[i];
+ if (td_write(td) && td->io_bytes[DDIR_WRITE])
+ td->ts.runtime[DDIR_WRITE] += rt[i];
+ if (td_trim(td) && td->io_bytes[DDIR_TRIM])
+ td->ts.runtime[DDIR_TRIM] += rt[i];
+
+ update_rusage_stat(td);
+ td->ts.io_bytes[DDIR_READ] = td->io_bytes[DDIR_READ];
+ td->ts.io_bytes[DDIR_WRITE] = td->io_bytes[DDIR_WRITE];
+ td->ts.io_bytes[DDIR_TRIM] = td->io_bytes[DDIR_TRIM];
+ td->ts.total_run_time = mtime_since(&td->epoch, &tv);
+ }
+
+ show_run_stats();
+
+ for_each_td(td, i) {
+ if (td_read(td) && td->io_bytes[DDIR_READ])
+ td->ts.runtime[DDIR_READ] -= rt[i];
+ if (td_write(td) && td->io_bytes[DDIR_WRITE])
+ td->ts.runtime[DDIR_WRITE] -= rt[i];
+ if (td_trim(td) && td->io_bytes[DDIR_TRIM])
+ td->ts.runtime[DDIR_TRIM] -= rt[i];
+ }
+
+ free(rt);
+ return NULL;
+}
+
+/*
+ * Called from signal handler. It _should_ be safe to just run this inline
+ * in the sig handler, but we should be disturbing the system less by just
+ * creating a thread to do it.
+ */
+void show_running_run_stats(void)
+{
+ pthread_t thread;
+
+ pthread_create(&thread, NULL, __show_running_run_stats, NULL);
+ pthread_detach(thread);
+}
+
static inline void add_stat_sample(struct io_stat *is, unsigned long data)
{
double val = data;
mw = iolog->avg_window[DDIR_WRITE].mean.u.f + 0.50;
__add_log_sample(iolog, mw, DDIR_WRITE, 0, elapsed);
}
+ if (iolog->avg_window[DDIR_TRIM].samples) {
+ unsigned long mw;
+
+ mw = iolog->avg_window[DDIR_TRIM].mean.u.f + 0.50;
+ __add_log_sample(iolog, mw, DDIR_TRIM, 0, elapsed);
+ }
+
reset_io_stat(&iolog->avg_window[DDIR_READ]);
reset_io_stat(&iolog->avg_window[DDIR_WRITE]);
+ reset_io_stat(&iolog->avg_window[DDIR_TRIM]);
iolog->avg_last = elapsed;
}
if (spent < td->o.bw_avg_time)
return;
- rate = (td->this_io_bytes[ddir] - td->stat_io_bytes[ddir]) *
- 1000 / spent / 1024;
- add_stat_sample(&ts->bw_stat[ddir], rate);
+ /*
+ * Compute both read and write rates for the interval.
+ */
+ for (ddir = DDIR_READ; ddir < DDIR_RWDIR_CNT; ddir++) {
+ uint64_t delta;
+
+ delta = td->this_io_bytes[ddir] - td->stat_io_bytes[ddir];
+ if (!delta)
+ continue; /* No entries for interval */
- if (td->bw_log)
- add_log_sample(td, td->bw_log, rate, ddir, bs);
+ rate = delta * 1000 / spent / 1024;
+ add_stat_sample(&ts->bw_stat[ddir], rate);
+
+ if (td->bw_log)
+ add_log_sample(td, td->bw_log, rate, ddir, bs);
+
+ td->stat_io_bytes[ddir] = td->this_io_bytes[ddir];
+ }
fio_gettime(&td->bw_sample_time, NULL);
- td->stat_io_bytes[ddir] = td->this_io_bytes[ddir];
}
void add_iops_sample(struct thread_data *td, enum fio_ddir ddir,
if (spent < td->o.iops_avg_time)
return;
- iops = ((td->this_io_blocks[ddir] - td->stat_io_blocks[ddir]) * 1000) / spent;
+ /*
+ * Compute both read and write rates for the interval.
+ */
+ for (ddir = DDIR_READ; ddir < DDIR_RWDIR_CNT; ddir++) {
+ uint64_t delta;
+
+ delta = td->this_io_blocks[ddir] - td->stat_io_blocks[ddir];
+ if (!delta)
+ continue; /* No entries for interval */
- add_stat_sample(&ts->iops_stat[ddir], iops);
+ iops = (delta * 1000) / spent;
+ add_stat_sample(&ts->iops_stat[ddir], iops);
- if (td->iops_log)
- add_log_sample(td, td->iops_log, iops, ddir, 0);
+ if (td->iops_log)
+ add_log_sample(td, td->iops_log, iops, ddir, 0);
+
+ td->stat_io_blocks[ddir] = td->this_io_blocks[ddir];
+ }
fio_gettime(&td->iops_sample_time, NULL);
- td->stat_io_blocks[ddir] = td->this_io_blocks[ddir];
}