stat.c revision 0d29de831183dfd049c97a03008d425ce21e2fa4
1#include <stdio.h>
2#include <string.h>
3#include <sys/time.h>
4#include <sys/types.h>
5#include <sys/stat.h>
6#include <dirent.h>
7#include <libgen.h>
8#include <math.h>
9
10#include "fio.h"
11#include "diskutil.h"
12
13void update_rusage_stat(struct thread_data *td)
14{
15	struct thread_stat *ts = &td->ts;
16
17	getrusage(RUSAGE_SELF, &ts->ru_end);
18
19	ts->usr_time += mtime_since(&ts->ru_start.ru_utime,
20					&ts->ru_end.ru_utime);
21	ts->sys_time += mtime_since(&ts->ru_start.ru_stime,
22					&ts->ru_end.ru_stime);
23	ts->ctx += ts->ru_end.ru_nvcsw + ts->ru_end.ru_nivcsw
24			- (ts->ru_start.ru_nvcsw + ts->ru_start.ru_nivcsw);
25	ts->minf += ts->ru_end.ru_minflt - ts->ru_start.ru_minflt;
26	ts->majf += ts->ru_end.ru_majflt - ts->ru_start.ru_majflt;
27
28	memcpy(&ts->ru_start, &ts->ru_end, sizeof(ts->ru_end));
29}
30
31static int calc_lat(struct io_stat *is, unsigned long *min, unsigned long *max,
32		    double *mean, double *dev)
33{
34	double n = is->samples;
35
36	if (is->samples == 0)
37		return 0;
38
39	*min = is->min_val;
40	*max = is->max_val;
41
42	n = (double) is->samples;
43	*mean = is->mean;
44
45	if (n > 1.0)
46		*dev = sqrt(is->S / (n - 1.0));
47	else
48		*dev = 0;
49
50	return 1;
51}
52
53static void show_group_stats(struct group_run_stats *rs, int id)
54{
55	char *p1, *p2, *p3, *p4;
56	const char *ddir_str[] = { "   READ", "  WRITE" };
57	int i;
58
59	log_info("\nRun status group %d (all jobs):\n", id);
60
61	for (i = 0; i <= DDIR_WRITE; i++) {
62		const int i2p = is_power_of_2(rs->kb_base);
63
64		if (!rs->max_run[i])
65			continue;
66
67		p1 = num2str(rs->io_kb[i], 6, rs->kb_base, i2p);
68		p2 = num2str(rs->agg[i], 6, rs->kb_base, i2p);
69		p3 = num2str(rs->min_bw[i], 6, rs->kb_base, i2p);
70		p4 = num2str(rs->max_bw[i], 6, rs->kb_base, i2p);
71
72		log_info("%s: io=%sB, aggrb=%sB/s, minb=%sB/s, maxb=%sB/s,"
73			 " mint=%llumsec, maxt=%llumsec\n", ddir_str[i], p1, p2,
74						p3, p4, rs->min_run[i],
75						rs->max_run[i]);
76
77		free(p1);
78		free(p2);
79		free(p3);
80		free(p4);
81	}
82}
83
84#define ts_total_io_u(ts)	\
85	((ts)->total_io_u[0] + (ts)->total_io_u[1])
86
87static void stat_calc_dist(unsigned int *map, unsigned long total,
88			   double *io_u_dist)
89{
90	int i;
91
92	/*
93	 * Do depth distribution calculations
94	 */
95	for (i = 0; i < FIO_IO_U_MAP_NR; i++) {
96		if (total) {
97			io_u_dist[i] = (double) map[i] / (double) total;
98			io_u_dist[i] *= 100.0;
99			if (io_u_dist[i] < 0.1 && map[i])
100				io_u_dist[i] = 0.1;
101		} else
102			io_u_dist[i] = 0.0;
103	}
104}
105
106static void stat_calc_lat(struct thread_stat *ts, double *dst,
107			  unsigned int *src, int nr)
108{
109	unsigned long total = ts_total_io_u(ts);
110	int i;
111
112	/*
113	 * Do latency distribution calculations
114	 */
115	for (i = 0; i < nr; i++) {
116		if (total) {
117			dst[i] = (double) src[i] / (double) total;
118			dst[i] *= 100.0;
119			if (dst[i] < 0.01 && src[i])
120				dst[i] = 0.01;
121		} else
122			dst[i] = 0.0;
123	}
124}
125
126static void stat_calc_lat_u(struct thread_stat *ts, double *io_u_lat)
127{
128	stat_calc_lat(ts, io_u_lat, ts->io_u_lat_u, FIO_IO_U_LAT_U_NR);
129}
130
131static void stat_calc_lat_m(struct thread_stat *ts, double *io_u_lat)
132{
133	stat_calc_lat(ts, io_u_lat, ts->io_u_lat_m, FIO_IO_U_LAT_M_NR);
134}
135
136static int usec_to_msec(unsigned long *min, unsigned long *max, double *mean,
137			double *dev)
138{
139	if (*min > 1000 && *max > 1000 && *mean > 1000.0 && *dev > 1000.0) {
140		*min /= 1000;
141		*max /= 1000;
142		*mean /= 1000.0;
143		*dev /= 1000.0;
144		return 0;
145	}
146
147	return 1;
148}
149
150static void show_ddir_status(struct group_run_stats *rs, struct thread_stat *ts,
151			     int ddir)
152{
153	const char *ddir_str[] = { "read ", "write" };
154	unsigned long min, max, runt;
155	unsigned long long bw, iops;
156	double mean, dev;
157	char *io_p, *bw_p, *iops_p;
158	int i2p;
159
160	assert(ddir_rw(ddir));
161
162	if (!ts->runtime[ddir])
163		return;
164
165	i2p = is_power_of_2(rs->kb_base);
166	runt = ts->runtime[ddir];
167
168	bw = (1000 * ts->io_bytes[ddir]) / runt;
169	io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
170	bw_p = num2str(bw, 6, 1, i2p);
171
172	iops = (1000 * ts->total_io_u[ddir]) / runt;
173	iops_p = num2str(iops, 6, 1, 0);
174
175	log_info("  %s: io=%sB, bw=%sB/s, iops=%s, runt=%6lumsec\n",
176					ddir_str[ddir], io_p, bw_p, iops_p,
177					ts->runtime[ddir]);
178
179	free(io_p);
180	free(bw_p);
181	free(iops_p);
182
183	if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev)) {
184		const char *base = "(usec)";
185		char *minp, *maxp;
186
187		if (!usec_to_msec(&min, &max, &mean, &dev))
188			base = "(msec)";
189
190		minp = num2str(min, 6, 1, 0);
191		maxp = num2str(max, 6, 1, 0);
192
193		log_info("    slat %s: min=%s, max=%s, avg=%5.02f,"
194			 " stdev=%5.02f\n", base, minp, maxp, mean, dev);
195
196		free(minp);
197		free(maxp);
198	}
199	if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev)) {
200		const char *base = "(usec)";
201		char *minp, *maxp;
202
203		if (!usec_to_msec(&min, &max, &mean, &dev))
204			base = "(msec)";
205
206		minp = num2str(min, 6, 1, 0);
207		maxp = num2str(max, 6, 1, 0);
208
209		log_info("    clat %s: min=%s, max=%s, avg=%5.02f,"
210			 " stdev=%5.02f\n", base, minp, maxp, mean, dev);
211
212		free(minp);
213		free(maxp);
214	}
215	if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev)) {
216		const char *base = "(usec)";
217		char *minp, *maxp;
218
219		if (!usec_to_msec(&min, &max, &mean, &dev))
220			base = "(msec)";
221
222		minp = num2str(min, 6, 1, 0);
223		maxp = num2str(max, 6, 1, 0);
224
225		log_info("     lat %s: min=%s, max=%s, avg=%5.02f,"
226			 " stdev=%5.02f\n", base, minp, maxp, mean, dev);
227
228		free(minp);
229		free(maxp);
230	}
231	if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) {
232		double p_of_agg;
233
234		p_of_agg = mean * 100 / (double) rs->agg[ddir];
235		log_info("    bw (KB/s) : min=%5lu, max=%5lu, per=%3.2f%%,"
236			 " avg=%5.02f, stdev=%5.02f\n", min, max, p_of_agg,
237							mean, dev);
238	}
239}
240
241static void show_lat(double *io_u_lat, int nr, const char **ranges,
242		     const char *msg)
243{
244	int new_line = 1, i, line = 0;
245
246	for (i = 0; i < nr; i++) {
247		if (io_u_lat[i] <= 0.0)
248			continue;
249		if (new_line) {
250			if (line)
251				log_info("\n");
252			log_info("     lat (%s): ", msg);
253			new_line = 0;
254			line = 0;
255		}
256		if (line)
257			log_info(", ");
258		log_info("%s%3.2f%%", ranges[i], io_u_lat[i]);
259		line++;
260		if (line == 5)
261			new_line = 1;
262	}
263}
264
265static void show_lat_u(double *io_u_lat_u)
266{
267	const char *ranges[] = { "2=", "4=", "10=", "20=", "50=", "100=",
268				 "250=", "500=", "750=", "1000=", };
269
270	show_lat(io_u_lat_u, FIO_IO_U_LAT_U_NR, ranges, "usec");
271}
272
273static void show_lat_m(double *io_u_lat_m)
274{
275	const char *ranges[] = { "2=", "4=", "10=", "20=", "50=", "100=",
276				 "250=", "500=", "750=", "1000=", "2000=",
277				 ">=2000=", };
278
279	show_lat(io_u_lat_m, FIO_IO_U_LAT_M_NR, ranges, "msec");
280}
281
282static void show_latencies(double *io_u_lat_u, double *io_u_lat_m)
283{
284	show_lat_u(io_u_lat_u);
285	log_info("\n");
286	show_lat_m(io_u_lat_m);
287	log_info("\n");
288}
289
290static void show_thread_status(struct thread_stat *ts,
291			       struct group_run_stats *rs)
292{
293	double usr_cpu, sys_cpu;
294	unsigned long runtime;
295	double io_u_dist[FIO_IO_U_MAP_NR];
296	double io_u_lat_u[FIO_IO_U_LAT_U_NR];
297	double io_u_lat_m[FIO_IO_U_LAT_M_NR];
298
299	if (!(ts->io_bytes[0] + ts->io_bytes[1]) &&
300	    !(ts->total_io_u[0] + ts->total_io_u[1]))
301		return;
302
303	if (!ts->error) {
304		log_info("%s: (groupid=%d, jobs=%d): err=%2d: pid=%d\n",
305					ts->name, ts->groupid, ts->members,
306					ts->error, (int) ts->pid);
307	} else {
308		log_info("%s: (groupid=%d, jobs=%d): err=%2d (%s): pid=%d\n",
309					ts->name, ts->groupid, ts->members,
310					ts->error, ts->verror, (int) ts->pid);
311	}
312
313	if (ts->description)
314		log_info("  Description  : [%s]\n", ts->description);
315
316	if (ts->io_bytes[DDIR_READ])
317		show_ddir_status(rs, ts, DDIR_READ);
318	if (ts->io_bytes[DDIR_WRITE])
319		show_ddir_status(rs, ts, DDIR_WRITE);
320
321	runtime = ts->total_run_time;
322	if (runtime) {
323		double runt = (double) runtime;
324
325		usr_cpu = (double) ts->usr_time * 100 / runt;
326		sys_cpu = (double) ts->sys_time * 100 / runt;
327	} else {
328		usr_cpu = 0;
329		sys_cpu = 0;
330	}
331
332	log_info("  cpu          : usr=%3.2f%%, sys=%3.2f%%, ctx=%lu, majf=%lu,"
333		 " minf=%lu\n", usr_cpu, sys_cpu, ts->ctx, ts->majf, ts->minf);
334
335	stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
336	log_info("  IO depths    : 1=%3.1f%%, 2=%3.1f%%, 4=%3.1f%%, 8=%3.1f%%,"
337		 " 16=%3.1f%%, 32=%3.1f%%, >=64=%3.1f%%\n", io_u_dist[0],
338					io_u_dist[1], io_u_dist[2],
339					io_u_dist[3], io_u_dist[4],
340					io_u_dist[5], io_u_dist[6]);
341
342	stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
343	log_info("     submit    : 0=%3.1f%%, 4=%3.1f%%, 8=%3.1f%%, 16=%3.1f%%,"
344		 " 32=%3.1f%%, 64=%3.1f%%, >=64=%3.1f%%\n", io_u_dist[0],
345					io_u_dist[1], io_u_dist[2],
346					io_u_dist[3], io_u_dist[4],
347					io_u_dist[5], io_u_dist[6]);
348	stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
349	log_info("     complete  : 0=%3.1f%%, 4=%3.1f%%, 8=%3.1f%%, 16=%3.1f%%,"
350		 " 32=%3.1f%%, 64=%3.1f%%, >=64=%3.1f%%\n", io_u_dist[0],
351					io_u_dist[1], io_u_dist[2],
352					io_u_dist[3], io_u_dist[4],
353					io_u_dist[5], io_u_dist[6]);
354	log_info("     issued r/w/d: total=%lu/%lu/%lu, short=%lu/%lu/%lu\n",
355					ts->total_io_u[0], ts->total_io_u[1],
356					ts->total_io_u[2],
357					ts->short_io_u[0], ts->short_io_u[1],
358					ts->short_io_u[2]);
359	stat_calc_lat_u(ts, io_u_lat_u);
360	stat_calc_lat_m(ts, io_u_lat_m);
361	show_latencies(io_u_lat_u, io_u_lat_m);
362	if (ts->continue_on_error) {
363		log_info("     errors    : total=%lu, first_error=%d/<%s>\n",
364					ts->total_err_count,
365					ts->first_error,
366					strerror(ts->first_error));
367	}
368}
369
370static void show_ddir_status_terse(struct thread_stat *ts,
371				   struct group_run_stats *rs, int ddir)
372{
373	unsigned long min, max;
374	unsigned long long bw;
375	double mean, dev;
376
377	assert(ddir_rw(ddir));
378
379	bw = 0;
380	if (ts->runtime[ddir])
381		bw = ts->io_bytes[ddir] / ts->runtime[ddir];
382
383	log_info(";%llu;%llu;%lu", ts->io_bytes[ddir] >> 10, bw,
384							ts->runtime[ddir]);
385
386	if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev))
387		log_info(";%lu;%lu;%f;%f", min, max, mean, dev);
388	else
389		log_info(";%lu;%lu;%f;%f", 0UL, 0UL, 0.0, 0.0);
390
391	if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev))
392		log_info(";%lu;%lu;%f;%f", min, max, mean, dev);
393	else
394		log_info(";%lu;%lu;%f;%f", 0UL, 0UL, 0.0, 0.0);
395
396	if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
397		log_info(";%lu;%lu;%f;%f", min, max, mean, dev);
398	else
399		log_info(";%lu;%lu;%f;%f", 0UL, 0UL, 0.0, 0.0);
400
401	if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) {
402		double p_of_agg;
403
404		p_of_agg = mean * 100 / (double) rs->agg[ddir];
405		log_info(";%lu;%lu;%f%%;%f;%f", min, max, p_of_agg, mean, dev);
406	} else
407		log_info(";%lu;%lu;%f%%;%f;%f", 0UL, 0UL, 0.0, 0.0, 0.0);
408}
409
410#define FIO_TERSE_VERSION	"2"
411
412static void show_thread_status_terse(struct thread_stat *ts,
413				     struct group_run_stats *rs)
414{
415	double io_u_dist[FIO_IO_U_MAP_NR];
416	double io_u_lat_u[FIO_IO_U_LAT_U_NR];
417	double io_u_lat_m[FIO_IO_U_LAT_M_NR];
418	double usr_cpu, sys_cpu;
419	int i;
420
421	log_info("%s;%s;%d;%d", FIO_TERSE_VERSION, ts->name, ts->groupid,
422				ts->error);
423
424	show_ddir_status_terse(ts, rs, 0);
425	show_ddir_status_terse(ts, rs, 1);
426
427	if (ts->total_run_time) {
428		double runt = (double) ts->total_run_time;
429
430		usr_cpu = (double) ts->usr_time * 100 / runt;
431		sys_cpu = (double) ts->sys_time * 100 / runt;
432	} else {
433		usr_cpu = 0;
434		sys_cpu = 0;
435	}
436
437	log_info(";%f%%;%f%%;%lu;%lu;%lu", usr_cpu, sys_cpu, ts->ctx, ts->majf,
438								ts->minf);
439
440	stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
441	stat_calc_lat_u(ts, io_u_lat_u);
442	stat_calc_lat_m(ts, io_u_lat_m);
443
444	log_info(";%3.1f%%;%3.1f%%;%3.1f%%;%3.1f%%;%3.1f%%;%3.1f%%;%3.1f%%",
445			io_u_dist[0], io_u_dist[1], io_u_dist[2], io_u_dist[3],
446			io_u_dist[4], io_u_dist[5], io_u_dist[6]);
447
448	for (i = 0; i < FIO_IO_U_LAT_U_NR; i++)
449		log_info(";%3.2f%%", io_u_lat_u[i]);
450	for (i = 0; i < FIO_IO_U_LAT_M_NR; i++)
451		log_info(";%3.2f%%", io_u_lat_m[i]);
452	if (ts->continue_on_error)
453		log_info(";%lu;%d", ts->total_err_count, ts->first_error);
454	log_info("\n");
455
456	if (ts->description)
457		log_info(";%s", ts->description);
458
459	log_info("\n");
460}
461
462static void sum_stat(struct io_stat *dst, struct io_stat *src, int nr)
463{
464	double mean, S;
465
466	dst->min_val = min(dst->min_val, src->min_val);
467	dst->max_val = max(dst->max_val, src->max_val);
468	dst->samples += src->samples;
469
470	/*
471	 * Needs a new method for calculating stddev, we cannot just
472	 * average them we do below for nr > 1
473	 */
474	if (nr == 1) {
475		mean = src->mean;
476		S = src->S;
477	} else {
478		mean = ((src->mean * (double) (nr - 1))
479				+ dst->mean) / ((double) nr);
480		S = ((src->S * (double) (nr - 1)) + dst->S) / ((double) nr);
481	}
482
483	dst->mean = mean;
484	dst->S = S;
485}
486
487void show_run_stats(void)
488{
489	struct group_run_stats *runstats, *rs;
490	struct thread_data *td;
491	struct thread_stat *threadstats, *ts;
492	int i, j, k, l, nr_ts, last_ts, idx;
493	int kb_base_warned = 0;
494
495	runstats = malloc(sizeof(struct group_run_stats) * (groupid + 1));
496
497	for (i = 0; i < groupid + 1; i++) {
498		rs = &runstats[i];
499
500		memset(rs, 0, sizeof(*rs));
501		rs->min_bw[0] = rs->min_run[0] = ~0UL;
502		rs->min_bw[1] = rs->min_run[1] = ~0UL;
503	}
504
505	/*
506	 * find out how many threads stats we need. if group reporting isn't
507	 * enabled, it's one-per-td.
508	 */
509	nr_ts = 0;
510	last_ts = -1;
511	for_each_td(td, i) {
512		if (!td->o.group_reporting) {
513			nr_ts++;
514			continue;
515		}
516		if (last_ts == td->groupid)
517			continue;
518
519		last_ts = td->groupid;
520		nr_ts++;
521	}
522
523	threadstats = malloc(nr_ts * sizeof(struct thread_stat));
524
525	for (i = 0; i < nr_ts; i++) {
526		ts = &threadstats[i];
527
528		memset(ts, 0, sizeof(*ts));
529		for (j = 0; j <= DDIR_WRITE; j++) {
530			ts->lat_stat[j].min_val = -1UL;
531			ts->clat_stat[j].min_val = -1UL;
532			ts->slat_stat[j].min_val = -1UL;
533			ts->bw_stat[j].min_val = -1UL;
534		}
535		ts->groupid = -1;
536	}
537
538	j = 0;
539	last_ts = -1;
540	idx = 0;
541	for_each_td(td, i) {
542		if (idx && (!td->o.group_reporting ||
543		    (td->o.group_reporting && last_ts != td->groupid))) {
544			idx = 0;
545			j++;
546		}
547
548		last_ts = td->groupid;
549
550		ts = &threadstats[j];
551
552		idx++;
553		ts->members++;
554
555		if (ts->groupid == -1) {
556			/*
557			 * These are per-group shared already
558			 */
559			ts->name = td->o.name;
560			ts->description = td->o.description;
561			ts->groupid = td->groupid;
562
563			/*
564			 * first pid in group, not very useful...
565			 */
566			ts->pid = td->pid;
567
568			ts->kb_base = td->o.kb_base;
569		} else if (ts->kb_base != td->o.kb_base && !kb_base_warned) {
570			log_info("fio: kb_base differs for jobs in group, using"
571				 " %u as the base\n", ts->kb_base);
572			kb_base_warned = 1;
573		}
574
575		ts->continue_on_error = td->o.continue_on_error;
576		ts->total_err_count += td->total_err_count;
577		ts->first_error = td->first_error;
578		if (!ts->error) {
579			if (!td->error && td->o.continue_on_error &&
580			    td->first_error) {
581				ts->error = td->first_error;
582				ts->verror = td->verror;
583			} else  if (td->error) {
584				ts->error = td->error;
585				ts->verror = td->verror;
586			}
587		}
588
589		for (l = 0; l <= DDIR_WRITE; l++) {
590			sum_stat(&ts->clat_stat[l], &td->ts.clat_stat[l], idx);
591			sum_stat(&ts->slat_stat[l], &td->ts.slat_stat[l], idx);
592			sum_stat(&ts->lat_stat[l], &td->ts.lat_stat[l], idx);
593			sum_stat(&ts->bw_stat[l], &td->ts.bw_stat[l], idx);
594
595			ts->stat_io_bytes[l] += td->ts.stat_io_bytes[l];
596			ts->io_bytes[l] += td->ts.io_bytes[l];
597
598			if (ts->runtime[l] < td->ts.runtime[l])
599				ts->runtime[l] = td->ts.runtime[l];
600		}
601
602		ts->usr_time += td->ts.usr_time;
603		ts->sys_time += td->ts.sys_time;
604		ts->ctx += td->ts.ctx;
605		ts->majf += td->ts.majf;
606		ts->minf += td->ts.minf;
607
608		for (k = 0; k < FIO_IO_U_MAP_NR; k++)
609			ts->io_u_map[k] += td->ts.io_u_map[k];
610		for (k = 0; k < FIO_IO_U_MAP_NR; k++)
611			ts->io_u_submit[k] += td->ts.io_u_submit[k];
612		for (k = 0; k < FIO_IO_U_MAP_NR; k++)
613			ts->io_u_complete[k] += td->ts.io_u_complete[k];
614		for (k = 0; k < FIO_IO_U_LAT_U_NR; k++)
615			ts->io_u_lat_u[k] += td->ts.io_u_lat_u[k];
616		for (k = 0; k < FIO_IO_U_LAT_M_NR; k++)
617			ts->io_u_lat_m[k] += td->ts.io_u_lat_m[k];
618
619
620		for (k = 0; k <= 2; k++) {
621			ts->total_io_u[k] += td->ts.total_io_u[k];
622			ts->short_io_u[k] += td->ts.short_io_u[k];
623		}
624
625		ts->total_run_time += td->ts.total_run_time;
626		ts->total_submit += td->ts.total_submit;
627		ts->total_complete += td->ts.total_complete;
628	}
629
630	for (i = 0; i < nr_ts; i++) {
631		unsigned long long bw;
632
633		ts = &threadstats[i];
634		rs = &runstats[ts->groupid];
635		rs->kb_base = ts->kb_base;
636
637		for (j = 0; j <= DDIR_WRITE; j++) {
638			if (!ts->runtime[j])
639				continue;
640			if (ts->runtime[j] < rs->min_run[j] || !rs->min_run[j])
641				rs->min_run[j] = ts->runtime[j];
642			if (ts->runtime[j] > rs->max_run[j])
643				rs->max_run[j] = ts->runtime[j];
644
645			bw = 0;
646			if (ts->runtime[j]) {
647				unsigned long runt;
648
649				runt = ts->runtime[j];
650				bw = ts->io_bytes[j] / runt;
651			}
652			if (bw < rs->min_bw[j])
653				rs->min_bw[j] = bw;
654			if (bw > rs->max_bw[j])
655				rs->max_bw[j] = bw;
656
657			rs->io_kb[j] += ts->io_bytes[j] / rs->kb_base;
658		}
659	}
660
661	for (i = 0; i < groupid + 1; i++) {
662		unsigned long max_run[2];
663
664		rs = &runstats[i];
665		max_run[0] = rs->max_run[0];
666		max_run[1] = rs->max_run[1];
667
668		if (rs->max_run[0])
669			rs->agg[0] = (rs->io_kb[0] * 1000) / max_run[0];
670		if (rs->max_run[1])
671			rs->agg[1] = (rs->io_kb[1] * 1000) / max_run[1];
672	}
673
674	/*
675	 * don't overwrite last signal output
676	 */
677	if (!terse_output)
678		log_info("\n");
679
680	for (i = 0; i < nr_ts; i++) {
681		ts = &threadstats[i];
682		rs = &runstats[ts->groupid];
683
684		if (terse_output)
685			show_thread_status_terse(ts, rs);
686		else
687			show_thread_status(ts, rs);
688	}
689
690	if (!terse_output) {
691		for (i = 0; i < groupid + 1; i++)
692			show_group_stats(&runstats[i], i);
693
694		show_disk_util();
695	}
696
697	free(runstats);
698	free(threadstats);
699}
700
701static inline void add_stat_sample(struct io_stat *is, unsigned long data)
702{
703	double val = data;
704	double delta;
705
706	if (data > is->max_val)
707		is->max_val = data;
708	if (data < is->min_val)
709		is->min_val = data;
710
711	delta = val - is->mean;
712	if (delta) {
713		is->mean += delta / (is->samples + 1.0);
714		is->S += delta * (val - is->mean);
715	}
716
717	is->samples++;
718}
719
720static void __add_log_sample(struct io_log *iolog, unsigned long val,
721			     enum fio_ddir ddir, unsigned int bs,
722			     unsigned long time)
723{
724	const int nr_samples = iolog->nr_samples;
725
726	if (iolog->nr_samples == iolog->max_samples) {
727		int new_size = sizeof(struct io_sample) * iolog->max_samples*2;
728
729		iolog->log = realloc(iolog->log, new_size);
730		iolog->max_samples <<= 1;
731	}
732
733	iolog->log[nr_samples].val = val;
734	iolog->log[nr_samples].time = time;
735	iolog->log[nr_samples].ddir = ddir;
736	iolog->log[nr_samples].bs = bs;
737	iolog->nr_samples++;
738}
739
740static void add_log_sample(struct thread_data *td, struct io_log *iolog,
741			   unsigned long val, enum fio_ddir ddir,
742			   unsigned int bs)
743{
744	if (!ddir_rw(ddir))
745		return;
746
747	__add_log_sample(iolog, val, ddir, bs, mtime_since_now(&td->epoch));
748}
749
750void add_agg_sample(unsigned long val, enum fio_ddir ddir, unsigned int bs)
751{
752	struct io_log *iolog;
753
754	if (!ddir_rw(ddir))
755		return;
756
757	iolog = agg_io_log[ddir];
758	__add_log_sample(iolog, val, ddir, bs, mtime_since_genesis());
759}
760
761void add_clat_sample(struct thread_data *td, enum fio_ddir ddir,
762		     unsigned long usec, unsigned int bs)
763{
764	struct thread_stat *ts = &td->ts;
765
766	if (!ddir_rw(ddir))
767		return;
768
769	add_stat_sample(&ts->clat_stat[ddir], usec);
770
771	if (ts->clat_log)
772		add_log_sample(td, ts->clat_log, usec, ddir, bs);
773}
774
775void add_slat_sample(struct thread_data *td, enum fio_ddir ddir,
776		     unsigned long usec, unsigned int bs)
777{
778	struct thread_stat *ts = &td->ts;
779
780	if (!ddir_rw(ddir))
781		return;
782
783	add_stat_sample(&ts->slat_stat[ddir], usec);
784
785	if (ts->slat_log)
786		add_log_sample(td, ts->slat_log, usec, ddir, bs);
787}
788
789void add_lat_sample(struct thread_data *td, enum fio_ddir ddir,
790		    unsigned long usec, unsigned int bs)
791{
792	struct thread_stat *ts = &td->ts;
793
794	if (!ddir_rw(ddir))
795		return;
796
797	add_stat_sample(&ts->lat_stat[ddir], usec);
798
799	if (ts->lat_log)
800		add_log_sample(td, ts->lat_log, usec, ddir, bs);
801}
802
803void add_bw_sample(struct thread_data *td, enum fio_ddir ddir, unsigned int bs,
804		   struct timeval *t)
805{
806	struct thread_stat *ts = &td->ts;
807	unsigned long spent, rate;
808
809	if (!ddir_rw(ddir))
810		return;
811
812	spent = mtime_since(&ts->stat_sample_time[ddir], t);
813	if (spent < td->o.bw_avg_time)
814		return;
815
816	rate = (td->this_io_bytes[ddir] - ts->stat_io_bytes[ddir]) *
817			1000 / spent / 1024;
818	add_stat_sample(&ts->bw_stat[ddir], rate);
819
820	if (ts->bw_log)
821		add_log_sample(td, ts->bw_log, rate, ddir, bs);
822
823	fio_gettime(&ts->stat_sample_time[ddir], NULL);
824	ts->stat_io_bytes[ddir] = td->this_io_bytes[ddir];
825}
826