stat.c revision 2e33101f218a1603eeecca969f5b7a0e98696a01
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#include "lib/ieee754.h"
13
14void update_rusage_stat(struct thread_data *td)
15{
16	struct thread_stat *ts = &td->ts;
17
18	getrusage(RUSAGE_SELF, &td->ru_end);
19
20	ts->usr_time += mtime_since(&td->ru_start.ru_utime,
21					&td->ru_end.ru_utime);
22	ts->sys_time += mtime_since(&td->ru_start.ru_stime,
23					&td->ru_end.ru_stime);
24	ts->ctx += td->ru_end.ru_nvcsw + td->ru_end.ru_nivcsw
25			- (td->ru_start.ru_nvcsw + td->ru_start.ru_nivcsw);
26	ts->minf += td->ru_end.ru_minflt - td->ru_start.ru_minflt;
27	ts->majf += td->ru_end.ru_majflt - td->ru_start.ru_majflt;
28
29	memcpy(&td->ru_start, &td->ru_end, sizeof(td->ru_end));
30}
31
32/*
33 * Given a latency, return the index of the corresponding bucket in
34 * the structure tracking percentiles.
35 *
36 * (1) find the group (and error bits) that the value (latency)
37 * belongs to by looking at its MSB. (2) find the bucket number in the
38 * group by looking at the index bits.
39 *
40 */
41static unsigned int plat_val_to_idx(unsigned int val)
42{
43	unsigned int msb, error_bits, base, offset, idx;
44
45	/* Find MSB starting from bit 0 */
46	if (val == 0)
47		msb = 0;
48	else
49		msb = (sizeof(val)*8) - __builtin_clz(val) - 1;
50
51	/*
52	 * MSB <= (FIO_IO_U_PLAT_BITS-1), cannot be rounded off. Use
53	 * all bits of the sample as index
54	 */
55	if (msb <= FIO_IO_U_PLAT_BITS)
56		return val;
57
58	/* Compute the number of error bits to discard*/
59	error_bits = msb - FIO_IO_U_PLAT_BITS;
60
61	/* Compute the number of buckets before the group */
62	base = (error_bits + 1) << FIO_IO_U_PLAT_BITS;
63
64	/*
65	 * Discard the error bits and apply the mask to find the
66         * index for the buckets in the group
67	 */
68	offset = (FIO_IO_U_PLAT_VAL - 1) & (val >> error_bits);
69
70	/* Make sure the index does not exceed (array size - 1) */
71	idx = (base + offset) < (FIO_IO_U_PLAT_NR - 1)?
72		(base + offset) : (FIO_IO_U_PLAT_NR - 1);
73
74	return idx;
75}
76
77/*
78 * Convert the given index of the bucket array to the value
79 * represented by the bucket
80 */
81static unsigned int plat_idx_to_val(unsigned int idx)
82{
83	unsigned int error_bits, k, base;
84
85	assert(idx < FIO_IO_U_PLAT_NR);
86
87	/* MSB <= (FIO_IO_U_PLAT_BITS-1), cannot be rounded off. Use
88	 * all bits of the sample as index */
89	if (idx < (FIO_IO_U_PLAT_VAL << 1) )
90		return idx;
91
92	/* Find the group and compute the minimum value of that group */
93	error_bits = (idx >> FIO_IO_U_PLAT_BITS) -1;
94	base = 1 << (error_bits + FIO_IO_U_PLAT_BITS);
95
96	/* Find its bucket number of the group */
97	k = idx % FIO_IO_U_PLAT_VAL;
98
99	/* Return the mean of the range of the bucket */
100	return base + ((k + 0.5) * (1 << error_bits));
101}
102
103static int double_cmp(const void *a, const void *b)
104{
105	const fio_fp64_t fa = *(const fio_fp64_t *) a;
106	const fio_fp64_t fb = *(const fio_fp64_t *) b;
107	int cmp = 0;
108
109	if (fa.u.f > fb.u.f)
110		cmp = 1;
111	else if (fa.u.f < fb.u.f)
112		cmp = -1;
113
114	return cmp;
115}
116
117unsigned int calc_clat_percentiles(unsigned int *io_u_plat, unsigned long nr,
118				   fio_fp64_t *plist, unsigned int **output,
119				   unsigned int *maxv, unsigned int *minv)
120{
121	unsigned long sum = 0;
122	unsigned int len, i, j = 0;
123	unsigned int oval_len = 0;
124	unsigned int *ovals = NULL;
125	int is_last;
126
127	*minv = -1U;
128	*maxv = 0;
129
130	len = 0;
131	while (len < FIO_IO_U_LIST_MAX_LEN && plist[len].u.f != 0.0)
132		len++;
133
134	if (!len)
135		return 0;
136
137	/*
138	 * Sort the percentile list. Note that it may already be sorted if
139	 * we are using the default values, but since it's a short list this
140	 * isn't a worry. Also note that this does not work for NaN values.
141	 */
142	if (len > 1)
143		qsort((void*)plist, len, sizeof(plist[0]), double_cmp);
144
145	/*
146	 * Calculate bucket values, note down max and min values
147	 */
148	is_last = 0;
149	for (i = 0; i < FIO_IO_U_PLAT_NR && !is_last; i++) {
150		sum += io_u_plat[i];
151		while (sum >= (plist[j].u.f / 100.0 * nr)) {
152			assert(plist[j].u.f <= 100.0);
153
154			if (j == oval_len) {
155				oval_len += 100;
156				ovals = realloc(ovals, oval_len * sizeof(unsigned int));
157			}
158
159			ovals[j] = plat_idx_to_val(i);
160			if (ovals[j] < *minv)
161				*minv = ovals[j];
162			if (ovals[j] > *maxv)
163				*maxv = ovals[j];
164
165			is_last = (j == len - 1);
166			if (is_last)
167				break;
168
169			j++;
170		}
171	}
172
173	*output = ovals;
174	return len;
175}
176
177/*
178 * Find and display the p-th percentile of clat
179 */
180static void show_clat_percentiles(unsigned int *io_u_plat, unsigned long nr,
181				  fio_fp64_t *plist)
182{
183	unsigned int len, j = 0, minv, maxv;
184	unsigned int *ovals;
185	int is_last, scale_down;
186
187	len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
188	if (!len)
189		goto out;
190
191	/*
192	 * We default to usecs, but if the value range is such that we
193	 * should scale down to msecs, do that.
194	 */
195	if (minv > 2000 && maxv > 99999) {
196		scale_down = 1;
197		log_info("    clat percentiles (msec):\n     |");
198	} else {
199		scale_down = 0;
200		log_info("    clat percentiles (usec):\n     |");
201	}
202
203	for (j = 0; j < len; j++) {
204		char fbuf[8];
205
206		/* for formatting */
207		if (j != 0 && (j % 4) == 0)
208			log_info("     |");
209
210		/* end of the list */
211		is_last = (j == len - 1);
212
213		if (plist[j].u.f < 10.0)
214			sprintf(fbuf, " %2.2f", plist[j].u.f);
215		else
216			sprintf(fbuf, "%2.2f", plist[j].u.f);
217
218		if (scale_down)
219			ovals[j] = (ovals[j] + 999) / 1000;
220
221		log_info(" %sth=[%5u]%c", fbuf, ovals[j], is_last ? '\n' : ',');
222
223		if (is_last)
224			break;
225
226		if (j % 4 == 3)	/* for formatting */
227			log_info("\n");
228	}
229
230out:
231	if (ovals)
232		free(ovals);
233}
234
235int calc_lat(struct io_stat *is, unsigned long *min, unsigned long *max,
236	     double *mean, double *dev)
237{
238	double n = is->samples;
239
240	if (is->samples == 0)
241		return 0;
242
243	*min = is->min_val;
244	*max = is->max_val;
245
246	n = (double) is->samples;
247	*mean = is->mean.u.f;
248
249	if (n > 1.0)
250		*dev = sqrt(is->S.u.f / (n - 1.0));
251	else
252		*dev = 0;
253
254	return 1;
255}
256
257void show_group_stats(struct group_run_stats *rs)
258{
259	char *p1, *p2, *p3, *p4;
260	const char *ddir_str[] = { "   READ", "  WRITE" };
261	int i;
262
263	log_info("\nRun status group %d (all jobs):\n", rs->groupid);
264
265	for (i = 0; i <= DDIR_WRITE; i++) {
266		const int i2p = is_power_of_2(rs->kb_base);
267
268		if (!rs->max_run[i])
269			continue;
270
271		p1 = num2str(rs->io_kb[i], 6, rs->kb_base, i2p);
272		p2 = num2str(rs->agg[i], 6, rs->kb_base, i2p);
273		p3 = num2str(rs->min_bw[i], 6, rs->kb_base, i2p);
274		p4 = num2str(rs->max_bw[i], 6, rs->kb_base, i2p);
275
276		log_info("%s: io=%sB, aggrb=%sB/s, minb=%sB/s, maxb=%sB/s,"
277			 " mint=%llumsec, maxt=%llumsec\n", ddir_str[i], p1, p2,
278						p3, p4, rs->min_run[i],
279						rs->max_run[i]);
280
281		free(p1);
282		free(p2);
283		free(p3);
284		free(p4);
285	}
286}
287
288void stat_calc_dist(unsigned int *map, unsigned long total, double *io_u_dist)
289{
290	int i;
291
292	/*
293	 * Do depth distribution calculations
294	 */
295	for (i = 0; i < FIO_IO_U_MAP_NR; i++) {
296		if (total) {
297			io_u_dist[i] = (double) map[i] / (double) total;
298			io_u_dist[i] *= 100.0;
299			if (io_u_dist[i] < 0.1 && map[i])
300				io_u_dist[i] = 0.1;
301		} else
302			io_u_dist[i] = 0.0;
303	}
304}
305
306static void stat_calc_lat(struct thread_stat *ts, double *dst,
307			  unsigned int *src, int nr)
308{
309	unsigned long total = ts_total_io_u(ts);
310	int i;
311
312	/*
313	 * Do latency distribution calculations
314	 */
315	for (i = 0; i < nr; i++) {
316		if (total) {
317			dst[i] = (double) src[i] / (double) total;
318			dst[i] *= 100.0;
319			if (dst[i] < 0.01 && src[i])
320				dst[i] = 0.01;
321		} else
322			dst[i] = 0.0;
323	}
324}
325
326void stat_calc_lat_u(struct thread_stat *ts, double *io_u_lat)
327{
328	stat_calc_lat(ts, io_u_lat, ts->io_u_lat_u, FIO_IO_U_LAT_U_NR);
329}
330
331void stat_calc_lat_m(struct thread_stat *ts, double *io_u_lat)
332{
333	stat_calc_lat(ts, io_u_lat, ts->io_u_lat_m, FIO_IO_U_LAT_M_NR);
334}
335
336static void display_lat(const char *name, unsigned long min, unsigned long max,
337			double mean, double dev)
338{
339	const char *base = "(usec)";
340	char *minp, *maxp;
341
342	if (!usec_to_msec(&min, &max, &mean, &dev))
343		base = "(msec)";
344
345	minp = num2str(min, 6, 1, 0);
346	maxp = num2str(max, 6, 1, 0);
347
348	log_info("    %s %s: min=%s, max=%s, avg=%5.02f,"
349		 " stdev=%5.02f\n", name, base, minp, maxp, mean, dev);
350
351	free(minp);
352	free(maxp);
353}
354
355static void show_ddir_status(struct group_run_stats *rs, struct thread_stat *ts,
356			     int ddir)
357{
358	const char *ddir_str[] = { "read ", "write" };
359	unsigned long min, max, runt;
360	unsigned long long bw, iops;
361	double mean, dev;
362	char *io_p, *bw_p, *iops_p;
363	int i2p;
364
365	assert(ddir_rw(ddir));
366
367	if (!ts->runtime[ddir])
368		return;
369
370	i2p = is_power_of_2(rs->kb_base);
371	runt = ts->runtime[ddir];
372
373	bw = (1000 * ts->io_bytes[ddir]) / runt;
374	io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
375	bw_p = num2str(bw, 6, 1, i2p);
376
377	iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
378	iops_p = num2str(iops, 6, 1, 0);
379
380	log_info("  %s: io=%sB, bw=%sB/s, iops=%s, runt=%6llumsec\n",
381					ddir_str[ddir], io_p, bw_p, iops_p,
382					ts->runtime[ddir]);
383
384	free(io_p);
385	free(bw_p);
386	free(iops_p);
387
388	if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev))
389		display_lat("slat", min, max, mean, dev);
390	if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev))
391		display_lat("clat", min, max, mean, dev);
392	if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
393		display_lat(" lat", min, max, mean, dev);
394
395	if (ts->clat_percentiles) {
396		show_clat_percentiles(ts->io_u_plat[ddir],
397					ts->clat_stat[ddir].samples,
398					ts->percentile_list);
399	}
400	if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) {
401		double p_of_agg = 100.0;
402		const char *bw_str = "KB";
403
404		if (rs->agg[ddir]) {
405			p_of_agg = mean * 100 / (double) rs->agg[ddir];
406			if (p_of_agg > 100.0)
407				p_of_agg = 100.0;
408		}
409
410		if (mean > 999999.9) {
411			min /= 1000.0;
412			max /= 1000.0;
413			mean /= 1000.0;
414			dev /= 1000.0;
415			bw_str = "MB";
416		}
417
418		log_info("    bw (%s/s)  : min=%5lu, max=%5lu, per=%3.2f%%,"
419			 " avg=%5.02f, stdev=%5.02f\n", bw_str, min, max,
420							p_of_agg, mean, dev);
421	}
422}
423
424static int show_lat(double *io_u_lat, int nr, const char **ranges,
425		    const char *msg)
426{
427	int new_line = 1, i, line = 0, shown = 0;
428
429	for (i = 0; i < nr; i++) {
430		if (io_u_lat[i] <= 0.0)
431			continue;
432		shown = 1;
433		if (new_line) {
434			if (line)
435				log_info("\n");
436			log_info("    lat (%s) : ", msg);
437			new_line = 0;
438			line = 0;
439		}
440		if (line)
441			log_info(", ");
442		log_info("%s%3.2f%%", ranges[i], io_u_lat[i]);
443		line++;
444		if (line == 5)
445			new_line = 1;
446	}
447
448	if (shown)
449		log_info("\n");
450
451	return shown;
452}
453
454static void show_lat_u(double *io_u_lat_u)
455{
456	const char *ranges[] = { "2=", "4=", "10=", "20=", "50=", "100=",
457				 "250=", "500=", "750=", "1000=", };
458
459	show_lat(io_u_lat_u, FIO_IO_U_LAT_U_NR, ranges, "usec");
460}
461
462static void show_lat_m(double *io_u_lat_m)
463{
464	const char *ranges[] = { "2=", "4=", "10=", "20=", "50=", "100=",
465				 "250=", "500=", "750=", "1000=", "2000=",
466				 ">=2000=", };
467
468	show_lat(io_u_lat_m, FIO_IO_U_LAT_M_NR, ranges, "msec");
469}
470
471static void show_latencies(struct thread_stat *ts)
472{
473	double io_u_lat_u[FIO_IO_U_LAT_U_NR];
474	double io_u_lat_m[FIO_IO_U_LAT_M_NR];
475
476	stat_calc_lat_u(ts, io_u_lat_u);
477	stat_calc_lat_m(ts, io_u_lat_m);
478
479	show_lat_u(io_u_lat_u);
480	show_lat_m(io_u_lat_m);
481}
482
483void show_thread_status(struct thread_stat *ts, struct group_run_stats *rs)
484{
485	double usr_cpu, sys_cpu;
486	unsigned long runtime;
487	double io_u_dist[FIO_IO_U_MAP_NR];
488
489	if (!(ts->io_bytes[0] + ts->io_bytes[1]) &&
490	    !(ts->total_io_u[0] + ts->total_io_u[1]))
491		return;
492
493	if (!ts->error) {
494		log_info("%s: (groupid=%d, jobs=%d): err=%2d: pid=%d\n",
495					ts->name, ts->groupid, ts->members,
496					ts->error, (int) ts->pid);
497	} else {
498		log_info("%s: (groupid=%d, jobs=%d): err=%2d (%s): pid=%d\n",
499					ts->name, ts->groupid, ts->members,
500					ts->error, ts->verror, (int) ts->pid);
501	}
502
503	if (strlen(ts->description))
504		log_info("  Description  : [%s]\n", ts->description);
505
506	if (ts->io_bytes[DDIR_READ])
507		show_ddir_status(rs, ts, DDIR_READ);
508	if (ts->io_bytes[DDIR_WRITE])
509		show_ddir_status(rs, ts, DDIR_WRITE);
510
511	show_latencies(ts);
512
513	runtime = ts->total_run_time;
514	if (runtime) {
515		double runt = (double) runtime;
516
517		usr_cpu = (double) ts->usr_time * 100 / runt;
518		sys_cpu = (double) ts->sys_time * 100 / runt;
519	} else {
520		usr_cpu = 0;
521		sys_cpu = 0;
522	}
523
524	log_info("  cpu          : usr=%3.2f%%, sys=%3.2f%%, ctx=%lu, majf=%lu,"
525		 " minf=%lu\n", usr_cpu, sys_cpu, ts->ctx, ts->majf, ts->minf);
526
527	stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
528	log_info("  IO depths    : 1=%3.1f%%, 2=%3.1f%%, 4=%3.1f%%, 8=%3.1f%%,"
529		 " 16=%3.1f%%, 32=%3.1f%%, >=64=%3.1f%%\n", io_u_dist[0],
530					io_u_dist[1], io_u_dist[2],
531					io_u_dist[3], io_u_dist[4],
532					io_u_dist[5], io_u_dist[6]);
533
534	stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
535	log_info("     submit    : 0=%3.1f%%, 4=%3.1f%%, 8=%3.1f%%, 16=%3.1f%%,"
536		 " 32=%3.1f%%, 64=%3.1f%%, >=64=%3.1f%%\n", io_u_dist[0],
537					io_u_dist[1], io_u_dist[2],
538					io_u_dist[3], io_u_dist[4],
539					io_u_dist[5], io_u_dist[6]);
540	stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
541	log_info("     complete  : 0=%3.1f%%, 4=%3.1f%%, 8=%3.1f%%, 16=%3.1f%%,"
542		 " 32=%3.1f%%, 64=%3.1f%%, >=64=%3.1f%%\n", io_u_dist[0],
543					io_u_dist[1], io_u_dist[2],
544					io_u_dist[3], io_u_dist[4],
545					io_u_dist[5], io_u_dist[6]);
546	log_info("     issued    : total=r=%lu/w=%lu/d=%lu,"
547				 " short=r=%lu/w=%lu/d=%lu\n",
548					ts->total_io_u[0], ts->total_io_u[1],
549					ts->total_io_u[2],
550					ts->short_io_u[0], ts->short_io_u[1],
551					ts->short_io_u[2]);
552	if (ts->continue_on_error) {
553		log_info("     errors    : total=%lu, first_error=%d/<%s>\n",
554					ts->total_err_count,
555					ts->first_error,
556					strerror(ts->first_error));
557	}
558}
559
560static void show_ddir_status_terse(struct thread_stat *ts,
561				   struct group_run_stats *rs, int ddir)
562{
563	unsigned long min, max;
564	unsigned long long bw, iops;
565	unsigned int *ovals = NULL;
566	double mean, dev;
567	unsigned int len, minv, maxv;
568	int i;
569
570	assert(ddir_rw(ddir));
571
572	iops = bw = 0;
573	if (ts->runtime[ddir]) {
574		uint64_t runt = ts->runtime[ddir];
575
576		bw = ts->io_bytes[ddir] / runt;
577		iops = (1000 * (uint64_t) ts->total_io_u[ddir]) / runt;
578	}
579
580	log_info(";%llu;%llu;%llu;%llu", ts->io_bytes[ddir] >> 10, bw, iops,
581							ts->runtime[ddir]);
582
583	if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev))
584		log_info(";%lu;%lu;%f;%f", min, max, mean, dev);
585	else
586		log_info(";%lu;%lu;%f;%f", 0UL, 0UL, 0.0, 0.0);
587
588	if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev))
589		log_info(";%lu;%lu;%f;%f", min, max, mean, dev);
590	else
591		log_info(";%lu;%lu;%f;%f", 0UL, 0UL, 0.0, 0.0);
592
593	if (ts->clat_percentiles) {
594		len = calc_clat_percentiles(ts->io_u_plat[ddir],
595					ts->clat_stat[ddir].samples,
596					ts->percentile_list, &ovals, &maxv,
597					&minv);
598	} else
599		len = 0;
600
601	for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++) {
602		if (i >= len) {
603			log_info(";0%%=0");
604			continue;
605		}
606		log_info(";%2.2f%%=%u", ts->percentile_list[i].u.f, ovals[i]);
607	}
608
609	if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
610		log_info(";%lu;%lu;%f;%f", min, max, mean, dev);
611	else
612		log_info(";%lu;%lu;%f;%f", 0UL, 0UL, 0.0, 0.0);
613
614	if (ovals)
615		free(ovals);
616
617	if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) {
618		double p_of_agg = 100.0;
619
620		if (rs->agg[ddir]) {
621			p_of_agg = mean * 100 / (double) rs->agg[ddir];
622			if (p_of_agg > 100.0)
623				p_of_agg = 100.0;
624		}
625
626		log_info(";%lu;%lu;%f%%;%f;%f", min, max, p_of_agg, mean, dev);
627	} else
628		log_info(";%lu;%lu;%f%%;%f;%f", 0UL, 0UL, 0.0, 0.0, 0.0);
629}
630
631static void show_thread_status_terse_v2(struct thread_stat *ts,
632				        struct group_run_stats *rs)
633{
634	double io_u_dist[FIO_IO_U_MAP_NR];
635	double io_u_lat_u[FIO_IO_U_LAT_U_NR];
636	double io_u_lat_m[FIO_IO_U_LAT_M_NR];
637	double usr_cpu, sys_cpu;
638	int i;
639
640	/* General Info */
641	log_info("2;%s;%d;%d", ts->name, ts->groupid, ts->error);
642	/* Log Read Status */
643	show_ddir_status_terse(ts, rs, 0);
644	/* Log Write Status */
645	show_ddir_status_terse(ts, rs, 1);
646
647	/* CPU Usage */
648	if (ts->total_run_time) {
649		double runt = (double) ts->total_run_time;
650
651		usr_cpu = (double) ts->usr_time * 100 / runt;
652		sys_cpu = (double) ts->sys_time * 100 / runt;
653	} else {
654		usr_cpu = 0;
655		sys_cpu = 0;
656	}
657
658	log_info(";%f%%;%f%%;%lu;%lu;%lu", usr_cpu, sys_cpu, ts->ctx, ts->majf,
659								ts->minf);
660
661	/* Calc % distribution of IO depths, usecond, msecond latency */
662	stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
663	stat_calc_lat_u(ts, io_u_lat_u);
664	stat_calc_lat_m(ts, io_u_lat_m);
665
666	/* Only show fixed 7 I/O depth levels*/
667	log_info(";%3.1f%%;%3.1f%%;%3.1f%%;%3.1f%%;%3.1f%%;%3.1f%%;%3.1f%%",
668			io_u_dist[0], io_u_dist[1], io_u_dist[2], io_u_dist[3],
669			io_u_dist[4], io_u_dist[5], io_u_dist[6]);
670
671	/* Microsecond latency */
672	for (i = 0; i < FIO_IO_U_LAT_U_NR; i++)
673		log_info(";%3.2f%%", io_u_lat_u[i]);
674	/* Millisecond latency */
675	for (i = 0; i < FIO_IO_U_LAT_M_NR; i++)
676		log_info(";%3.2f%%", io_u_lat_m[i]);
677	/* Additional output if continue_on_error set - default off*/
678	if (ts->continue_on_error)
679		log_info(";%lu;%d", ts->total_err_count, ts->first_error);
680	log_info("\n");
681
682	/* Additional output if description is set */
683	if (ts->description)
684		log_info(";%s", ts->description);
685
686	log_info("\n");
687}
688
689#define FIO_TERSE_VERSION	"3"
690
691static void show_thread_status_terse_v3(struct thread_stat *ts,
692					struct group_run_stats *rs)
693{
694	double io_u_dist[FIO_IO_U_MAP_NR];
695	double io_u_lat_u[FIO_IO_U_LAT_U_NR];
696	double io_u_lat_m[FIO_IO_U_LAT_M_NR];
697	double usr_cpu, sys_cpu;
698	int i;
699
700	/* General Info */
701	log_info("%s;%s;%s;%d;%d", FIO_TERSE_VERSION, fio_version_string,
702					ts->name, ts->groupid, ts->error);
703	/* Log Read Status */
704	show_ddir_status_terse(ts, rs, 0);
705	/* Log Write Status */
706	show_ddir_status_terse(ts, rs, 1);
707
708	/* CPU Usage */
709	if (ts->total_run_time) {
710		double runt = (double) ts->total_run_time;
711
712		usr_cpu = (double) ts->usr_time * 100 / runt;
713		sys_cpu = (double) ts->sys_time * 100 / runt;
714	} else {
715		usr_cpu = 0;
716		sys_cpu = 0;
717	}
718
719	log_info(";%f%%;%f%%;%lu;%lu;%lu", usr_cpu, sys_cpu, ts->ctx, ts->majf,
720								ts->minf);
721
722	/* Calc % distribution of IO depths, usecond, msecond latency */
723	stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
724	stat_calc_lat_u(ts, io_u_lat_u);
725	stat_calc_lat_m(ts, io_u_lat_m);
726
727	/* Only show fixed 7 I/O depth levels*/
728	log_info(";%3.1f%%;%3.1f%%;%3.1f%%;%3.1f%%;%3.1f%%;%3.1f%%;%3.1f%%",
729			io_u_dist[0], io_u_dist[1], io_u_dist[2], io_u_dist[3],
730			io_u_dist[4], io_u_dist[5], io_u_dist[6]);
731
732	/* Microsecond latency */
733	for (i = 0; i < FIO_IO_U_LAT_U_NR; i++)
734		log_info(";%3.2f%%", io_u_lat_u[i]);
735	/* Millisecond latency */
736	for (i = 0; i < FIO_IO_U_LAT_M_NR; i++)
737		log_info(";%3.2f%%", io_u_lat_m[i]);
738
739	/* disk util stats, if any */
740	show_disk_util(1);
741
742	/* Additional output if continue_on_error set - default off*/
743	if (ts->continue_on_error)
744		log_info(";%lu;%d", ts->total_err_count, ts->first_error);
745	log_info("\n");
746
747	/* Additional output if description is set */
748	if (strlen(ts->description))
749		log_info(";%s", ts->description);
750}
751
752static void show_thread_status_terse(struct thread_stat *ts,
753				     struct group_run_stats *rs)
754{
755	if (terse_version == 2)
756		show_thread_status_terse_v2(ts, rs);
757	else if (terse_version == 3)
758		show_thread_status_terse_v3(ts, rs);
759	else
760		log_err("fio: bad terse version!? %d\n", terse_version);
761}
762
763static void sum_stat(struct io_stat *dst, struct io_stat *src, int nr)
764{
765	double mean, S;
766
767	if (src->samples == 0)
768		return;
769
770	dst->min_val = min(dst->min_val, src->min_val);
771	dst->max_val = max(dst->max_val, src->max_val);
772
773	/*
774	 * Compute new mean and S after the merge
775	 * <http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
776	 *  #Parallel_algorithm>
777	 */
778	if (nr == 1) {
779		mean = src->mean.u.f;
780		S = src->S.u.f;
781	} else {
782		double delta = src->mean.u.f - dst->mean.u.f;
783
784		mean = ((src->mean.u.f * src->samples) +
785			(dst->mean.u.f * dst->samples)) /
786			(dst->samples + src->samples);
787
788		S =  src->S.u.f + dst->S.u.f + pow(delta, 2.0) *
789			(dst->samples * src->samples) /
790			(dst->samples + src->samples);
791	}
792
793	dst->samples += src->samples;
794	dst->mean.u.f = mean;
795	dst->S.u.f = S;
796}
797
798void sum_group_stats(struct group_run_stats *dst, struct group_run_stats *src)
799{
800	int i;
801
802	for (i = 0; i < 2; i++) {
803		if (dst->max_run[i] < src->max_run[i])
804			dst->max_run[i] = src->max_run[i];
805		if (dst->min_run[i] && dst->min_run[i] > src->min_run[i])
806			dst->min_run[i] = src->min_run[i];
807		if (dst->max_bw[i] < src->max_bw[i])
808			dst->max_bw[i] = src->max_bw[i];
809		if (dst->min_bw[i] && dst->min_bw[i] > src->min_bw[i])
810			dst->min_bw[i] = src->min_bw[i];
811
812		dst->io_kb[i] += src->io_kb[i];
813		dst->agg[i] += src->agg[i];
814	}
815
816}
817
818void sum_thread_stats(struct thread_stat *dst, struct thread_stat *src, int nr)
819{
820	int l, k;
821
822	for (l = 0; l <= DDIR_WRITE; l++) {
823		sum_stat(&dst->clat_stat[l], &src->clat_stat[l], nr);
824		sum_stat(&dst->slat_stat[l], &src->slat_stat[l], nr);
825		sum_stat(&dst->lat_stat[l], &src->lat_stat[l], nr);
826		sum_stat(&dst->bw_stat[l], &src->bw_stat[l], nr);
827
828		dst->io_bytes[l] += src->io_bytes[l];
829
830		if (dst->runtime[l] < src->runtime[l])
831			dst->runtime[l] = src->runtime[l];
832	}
833
834	dst->usr_time += src->usr_time;
835	dst->sys_time += src->sys_time;
836	dst->ctx += src->ctx;
837	dst->majf += src->majf;
838	dst->minf += src->minf;
839
840	for (k = 0; k < FIO_IO_U_MAP_NR; k++)
841		dst->io_u_map[k] += src->io_u_map[k];
842	for (k = 0; k < FIO_IO_U_MAP_NR; k++)
843		dst->io_u_submit[k] += src->io_u_submit[k];
844	for (k = 0; k < FIO_IO_U_MAP_NR; k++)
845		dst->io_u_complete[k] += src->io_u_complete[k];
846	for (k = 0; k < FIO_IO_U_LAT_U_NR; k++)
847		dst->io_u_lat_u[k] += src->io_u_lat_u[k];
848	for (k = 0; k < FIO_IO_U_LAT_M_NR; k++)
849		dst->io_u_lat_m[k] += src->io_u_lat_m[k];
850
851	for (k = 0; k <= 2; k++) {
852		dst->total_io_u[k] += src->total_io_u[k];
853		dst->short_io_u[k] += src->short_io_u[k];
854	}
855
856	for (k = 0; k <= DDIR_WRITE; k++) {
857		int m;
858		for (m = 0; m < FIO_IO_U_PLAT_NR; m++)
859			dst->io_u_plat[k][m] += src->io_u_plat[k][m];
860	}
861
862	dst->total_run_time += src->total_run_time;
863	dst->total_submit += src->total_submit;
864	dst->total_complete += src->total_complete;
865}
866
867void init_group_run_stat(struct group_run_stats *gs)
868{
869	memset(gs, 0, sizeof(*gs));
870	gs->min_bw[0] = gs->min_run[0] = ~0UL;
871	gs->min_bw[1] = gs->min_run[1] = ~0UL;
872}
873
874void init_thread_stat(struct thread_stat *ts)
875{
876	int j;
877
878	memset(ts, 0, sizeof(*ts));
879
880	for (j = 0; j <= DDIR_WRITE; j++) {
881		ts->lat_stat[j].min_val = -1UL;
882		ts->clat_stat[j].min_val = -1UL;
883		ts->slat_stat[j].min_val = -1UL;
884		ts->bw_stat[j].min_val = -1UL;
885	}
886	ts->groupid = -1;
887}
888
889void show_run_stats(void)
890{
891	struct group_run_stats *runstats, *rs;
892	struct thread_data *td;
893	struct thread_stat *threadstats, *ts;
894	int i, j, nr_ts, last_ts, idx;
895	int kb_base_warned = 0;
896
897	runstats = malloc(sizeof(struct group_run_stats) * (groupid + 1));
898
899	for (i = 0; i < groupid + 1; i++)
900		init_group_run_stat(&runstats[i]);
901
902	/*
903	 * find out how many threads stats we need. if group reporting isn't
904	 * enabled, it's one-per-td.
905	 */
906	nr_ts = 0;
907	last_ts = -1;
908	for_each_td(td, i) {
909		if (!td->o.group_reporting) {
910			nr_ts++;
911			continue;
912		}
913		if (last_ts == td->groupid)
914			continue;
915
916		last_ts = td->groupid;
917		nr_ts++;
918	}
919
920	threadstats = malloc(nr_ts * sizeof(struct thread_stat));
921
922	for (i = 0; i < nr_ts; i++)
923		init_thread_stat(&threadstats[i]);
924
925	j = 0;
926	last_ts = -1;
927	idx = 0;
928	for_each_td(td, i) {
929		if (idx && (!td->o.group_reporting ||
930		    (td->o.group_reporting && last_ts != td->groupid))) {
931			idx = 0;
932			j++;
933		}
934
935		last_ts = td->groupid;
936
937		ts = &threadstats[j];
938
939		ts->clat_percentiles = td->o.clat_percentiles;
940		if (td->o.overwrite_plist)
941			memcpy(ts->percentile_list, td->o.percentile_list, sizeof(td->o.percentile_list));
942		else
943			memcpy(ts->percentile_list, def_percentile_list, sizeof(def_percentile_list));
944
945		idx++;
946		ts->members++;
947
948		if (ts->groupid == -1) {
949			/*
950			 * These are per-group shared already
951			 */
952			strncpy(ts->name, td->o.name, FIO_JOBNAME_SIZE);
953			if (td->o.description)
954				strncpy(ts->description, td->o.description,
955						FIO_JOBNAME_SIZE);
956			else
957				memset(ts->description, 0, FIO_JOBNAME_SIZE);
958
959			ts->groupid = td->groupid;
960
961			/*
962			 * first pid in group, not very useful...
963			 */
964			ts->pid = td->pid;
965
966			ts->kb_base = td->o.kb_base;
967		} else if (ts->kb_base != td->o.kb_base && !kb_base_warned) {
968			log_info("fio: kb_base differs for jobs in group, using"
969				 " %u as the base\n", ts->kb_base);
970			kb_base_warned = 1;
971		}
972
973		ts->continue_on_error = td->o.continue_on_error;
974		ts->total_err_count += td->total_err_count;
975		ts->first_error = td->first_error;
976		if (!ts->error) {
977			if (!td->error && td->o.continue_on_error &&
978			    td->first_error) {
979				ts->error = td->first_error;
980				strcpy(ts->verror, td->verror);
981			} else  if (td->error) {
982				ts->error = td->error;
983				strcpy(ts->verror, td->verror);
984			}
985		}
986
987		sum_thread_stats(ts, &td->ts, idx);
988	}
989
990	for (i = 0; i < nr_ts; i++) {
991		unsigned long long bw;
992
993		ts = &threadstats[i];
994		rs = &runstats[ts->groupid];
995		rs->kb_base = ts->kb_base;
996
997		for (j = 0; j <= DDIR_WRITE; j++) {
998			if (!ts->runtime[j])
999				continue;
1000			if (ts->runtime[j] < rs->min_run[j] || !rs->min_run[j])
1001				rs->min_run[j] = ts->runtime[j];
1002			if (ts->runtime[j] > rs->max_run[j])
1003				rs->max_run[j] = ts->runtime[j];
1004
1005			bw = 0;
1006			if (ts->runtime[j]) {
1007				unsigned long runt;
1008
1009				runt = ts->runtime[j];
1010				bw = ts->io_bytes[j] / runt;
1011			}
1012			if (bw < rs->min_bw[j])
1013				rs->min_bw[j] = bw;
1014			if (bw > rs->max_bw[j])
1015				rs->max_bw[j] = bw;
1016
1017			rs->io_kb[j] += ts->io_bytes[j] / rs->kb_base;
1018		}
1019	}
1020
1021	for (i = 0; i < groupid + 1; i++) {
1022		unsigned long max_run[2];
1023
1024		rs = &runstats[i];
1025		max_run[0] = rs->max_run[0];
1026		max_run[1] = rs->max_run[1];
1027
1028		if (rs->max_run[0])
1029			rs->agg[0] = (rs->io_kb[0] * 1000) / max_run[0];
1030		if (rs->max_run[1])
1031			rs->agg[1] = (rs->io_kb[1] * 1000) / max_run[1];
1032	}
1033
1034	/*
1035	 * don't overwrite last signal output
1036	 */
1037	if (!terse_output)
1038		log_info("\n");
1039
1040	for (i = 0; i < nr_ts; i++) {
1041		ts = &threadstats[i];
1042		rs = &runstats[ts->groupid];
1043
1044		if (is_backend)
1045			fio_server_send_ts(ts, rs);
1046		else if (terse_output)
1047			show_thread_status_terse(ts, rs);
1048		else
1049			show_thread_status(ts, rs);
1050	}
1051
1052	for (i = 0; i < groupid + 1; i++) {
1053		rs = &runstats[i];
1054
1055		rs->groupid = i;
1056		if (is_backend)
1057			fio_server_send_gs(rs);
1058		else if (!terse_output)
1059			show_group_stats(rs);
1060	}
1061
1062	if (is_backend)
1063		fio_server_send_du();
1064	else if (!terse_output)
1065		show_disk_util(0);
1066
1067	free_disk_util();
1068
1069	free(runstats);
1070	free(threadstats);
1071}
1072
1073static inline void add_stat_sample(struct io_stat *is, unsigned long data)
1074{
1075	double val = data;
1076	double delta;
1077
1078	if (data > is->max_val)
1079		is->max_val = data;
1080	if (data < is->min_val)
1081		is->min_val = data;
1082
1083	delta = val - is->mean.u.f;
1084	if (delta) {
1085		is->mean.u.f += delta / (is->samples + 1.0);
1086		is->S.u.f += delta * (val - is->mean.u.f);
1087	}
1088
1089	is->samples++;
1090}
1091
1092static void __add_log_sample(struct io_log *iolog, unsigned long val,
1093			     enum fio_ddir ddir, unsigned int bs,
1094			     unsigned long t)
1095{
1096	const int nr_samples = iolog->nr_samples;
1097
1098	if (!iolog->nr_samples)
1099		iolog->avg_last = t;
1100
1101	if (iolog->nr_samples == iolog->max_samples) {
1102		int new_size = sizeof(struct io_sample) * iolog->max_samples*2;
1103
1104		iolog->log = realloc(iolog->log, new_size);
1105		iolog->max_samples <<= 1;
1106	}
1107
1108	iolog->log[nr_samples].val = val;
1109	iolog->log[nr_samples].time = t;
1110	iolog->log[nr_samples].ddir = ddir;
1111	iolog->log[nr_samples].bs = bs;
1112	iolog->nr_samples++;
1113}
1114
1115static inline void reset_io_stat(struct io_stat *ios)
1116{
1117	ios->max_val = ios->min_val = ios->samples = 0;
1118	ios->mean.u.f = ios->S.u.f = 0;
1119}
1120
1121static void add_log_sample(struct thread_data *td, struct io_log *iolog,
1122			   unsigned long val, enum fio_ddir ddir,
1123			   unsigned int bs)
1124{
1125	unsigned long elapsed, this_window;
1126
1127	if (!ddir_rw(ddir))
1128		return;
1129
1130	elapsed = mtime_since_now(&td->epoch);
1131
1132	/*
1133	 * If no time averaging, just add the log sample.
1134	 */
1135	if (!iolog->avg_msec) {
1136		__add_log_sample(iolog, val, ddir, bs, elapsed);
1137		return;
1138	}
1139
1140	/*
1141	 * Add the sample. If the time period has passed, then
1142	 * add that entry to the log and clear.
1143	 */
1144	add_stat_sample(&iolog->avg_window[ddir], val);
1145
1146	/*
1147	 * If period hasn't passed, adding the above sample is all we
1148	 * need to do.
1149	 */
1150	this_window = elapsed - iolog->avg_last;
1151	if (this_window < iolog->avg_msec)
1152		return;
1153
1154	/*
1155	 * Note an entry in the log. Use the mean from the logged samples,
1156	 * making sure to properly round up. Only write a log entry if we
1157	 * had actual samples done.
1158	 */
1159	if (iolog->avg_window[DDIR_READ].samples) {
1160		unsigned long mr;
1161
1162		mr = iolog->avg_window[DDIR_READ].mean.u.f + 0.50;
1163		__add_log_sample(iolog, mr, DDIR_READ, 0, elapsed);
1164	}
1165	if (iolog->avg_window[DDIR_WRITE].samples) {
1166		unsigned long mw;
1167
1168		mw = iolog->avg_window[DDIR_WRITE].mean.u.f + 0.50;
1169		__add_log_sample(iolog, mw, DDIR_WRITE, 0, elapsed);
1170	}
1171
1172	reset_io_stat(&iolog->avg_window[DDIR_READ]);
1173	reset_io_stat(&iolog->avg_window[DDIR_WRITE]);
1174	iolog->avg_last = elapsed;
1175}
1176
1177void add_agg_sample(unsigned long val, enum fio_ddir ddir, unsigned int bs)
1178{
1179	struct io_log *iolog;
1180
1181	if (!ddir_rw(ddir))
1182		return;
1183
1184	iolog = agg_io_log[ddir];
1185	__add_log_sample(iolog, val, ddir, bs, mtime_since_genesis());
1186}
1187
1188static void add_clat_percentile_sample(struct thread_stat *ts,
1189				unsigned long usec, enum fio_ddir ddir)
1190{
1191	unsigned int idx = plat_val_to_idx(usec);
1192	assert(idx < FIO_IO_U_PLAT_NR);
1193
1194	ts->io_u_plat[ddir][idx]++;
1195}
1196
1197void add_clat_sample(struct thread_data *td, enum fio_ddir ddir,
1198		     unsigned long usec, unsigned int bs)
1199{
1200	struct thread_stat *ts = &td->ts;
1201
1202	if (!ddir_rw(ddir))
1203		return;
1204
1205	add_stat_sample(&ts->clat_stat[ddir], usec);
1206
1207	if (td->clat_log)
1208		add_log_sample(td, td->clat_log, usec, ddir, bs);
1209
1210	if (ts->clat_percentiles)
1211		add_clat_percentile_sample(ts, usec, ddir);
1212}
1213
1214void add_slat_sample(struct thread_data *td, enum fio_ddir ddir,
1215		     unsigned long usec, unsigned int bs)
1216{
1217	struct thread_stat *ts = &td->ts;
1218
1219	if (!ddir_rw(ddir))
1220		return;
1221
1222	add_stat_sample(&ts->slat_stat[ddir], usec);
1223
1224	if (td->slat_log)
1225		add_log_sample(td, td->slat_log, usec, ddir, bs);
1226}
1227
1228void add_lat_sample(struct thread_data *td, enum fio_ddir ddir,
1229		    unsigned long usec, unsigned int bs)
1230{
1231	struct thread_stat *ts = &td->ts;
1232
1233	if (!ddir_rw(ddir))
1234		return;
1235
1236	add_stat_sample(&ts->lat_stat[ddir], usec);
1237
1238	if (td->lat_log)
1239		add_log_sample(td, td->lat_log, usec, ddir, bs);
1240}
1241
1242void add_bw_sample(struct thread_data *td, enum fio_ddir ddir, unsigned int bs,
1243		   struct timeval *t)
1244{
1245	struct thread_stat *ts = &td->ts;
1246	unsigned long spent, rate;
1247
1248	if (!ddir_rw(ddir))
1249		return;
1250
1251	spent = mtime_since(&td->bw_sample_time, t);
1252	if (spent < td->o.bw_avg_time)
1253		return;
1254
1255	/*
1256	 * Compute both read and write rates for the interval.
1257	 */
1258	for (ddir = DDIR_READ; ddir <= DDIR_WRITE; ddir++) {
1259		uint64_t delta;
1260
1261		delta = td->this_io_bytes[ddir] - td->stat_io_bytes[ddir];
1262		if (!delta)
1263			continue; /* No entries for interval */
1264
1265		rate = delta * 1000 / spent / 1024;
1266		add_stat_sample(&ts->bw_stat[ddir], rate);
1267
1268		if (td->bw_log)
1269			add_log_sample(td, td->bw_log, rate, ddir, bs);
1270
1271		td->stat_io_bytes[ddir] = td->this_io_bytes[ddir];
1272	}
1273
1274	fio_gettime(&td->bw_sample_time, NULL);
1275}
1276
1277void add_iops_sample(struct thread_data *td, enum fio_ddir ddir,
1278		     struct timeval *t)
1279{
1280	struct thread_stat *ts = &td->ts;
1281	unsigned long spent, iops;
1282
1283	if (!ddir_rw(ddir))
1284		return;
1285
1286	spent = mtime_since(&td->iops_sample_time, t);
1287	if (spent < td->o.iops_avg_time)
1288		return;
1289
1290	/*
1291	 * Compute both read and write rates for the interval.
1292	 */
1293	for (ddir = DDIR_READ; ddir <= DDIR_WRITE; ddir++) {
1294		uint64_t delta;
1295
1296		delta = td->this_io_blocks[ddir] - td->stat_io_blocks[ddir];
1297		if (!delta)
1298			continue; /* No entries for interval */
1299
1300		iops = (delta * 1000) / spent;
1301		add_stat_sample(&ts->iops_stat[ddir], iops);
1302
1303		if (td->iops_log)
1304			add_log_sample(td, td->iops_log, iops, ddir, 0);
1305
1306		td->stat_io_blocks[ddir] = td->this_io_blocks[ddir];
1307	}
1308
1309	fio_gettime(&td->iops_sample_time, NULL);
1310}
1311