xref: /onnv-gate/usr/src/cmd/filebench/common/stats.c (revision 5184:da60d2b4a9e2)
1*5184Sek110237 /*
2*5184Sek110237  * CDDL HEADER START
3*5184Sek110237  *
4*5184Sek110237  * The contents of this file are subject to the terms of the
5*5184Sek110237  * Common Development and Distribution License (the "License").
6*5184Sek110237  * You may not use this file except in compliance with the License.
7*5184Sek110237  *
8*5184Sek110237  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*5184Sek110237  * or http://www.opensolaris.org/os/licensing.
10*5184Sek110237  * See the License for the specific language governing permissions
11*5184Sek110237  * and limitations under the License.
12*5184Sek110237  *
13*5184Sek110237  * When distributing Covered Code, include this CDDL HEADER in each
14*5184Sek110237  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*5184Sek110237  * If applicable, add the following below this CDDL HEADER, with the
16*5184Sek110237  * fields enclosed by brackets "[]" replaced with your own identifying
17*5184Sek110237  * information: Portions Copyright [yyyy] [name of copyright owner]
18*5184Sek110237  *
19*5184Sek110237  * CDDL HEADER END
20*5184Sek110237  */
21*5184Sek110237 /*
22*5184Sek110237  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23*5184Sek110237  * Use is subject to license terms.
24*5184Sek110237  */
25*5184Sek110237 
26*5184Sek110237 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*5184Sek110237 
28*5184Sek110237 #include "config.h"
29*5184Sek110237 
30*5184Sek110237 #include <stdio.h>
31*5184Sek110237 #include <fcntl.h>
32*5184Sek110237 #include <sys/types.h>
33*5184Sek110237 
34*5184Sek110237 #ifdef HAVE_SYSINFO
35*5184Sek110237 #include <sys/sysinfo.h>
36*5184Sek110237 #endif
37*5184Sek110237 
38*5184Sek110237 #ifdef HAVE_LIBKSTAT
39*5184Sek110237 #include <kstat.h>
40*5184Sek110237 #include <sys/cpuvar.h>
41*5184Sek110237 #endif /* HAVE_LIBKSTAT */
42*5184Sek110237 
43*5184Sek110237 #include <stdarg.h>
44*5184Sek110237 
45*5184Sek110237 #include "filebench.h"
46*5184Sek110237 #include "flowop.h"
47*5184Sek110237 #include "vars.h"
48*5184Sek110237 #include "stats.h"
49*5184Sek110237 
50*5184Sek110237 /*
51*5184Sek110237  * A set of routines for collecting and dumping various filebench
52*5184Sek110237  * run statistics.
53*5184Sek110237  */
54*5184Sek110237 
55*5184Sek110237 /* Global statistics */
56*5184Sek110237 static flowstat_t *globalstats = NULL;
57*5184Sek110237 
58*5184Sek110237 static hrtime_t stats_cputime = 0;
59*5184Sek110237 
60*5184Sek110237 #ifdef HAVE_LIBKSTAT
61*5184Sek110237 static kstat_ctl_t *kstatp = NULL;
62*5184Sek110237 static kstat_t *sysinfo_ksp = NULL;
63*5184Sek110237 
64*5184Sek110237 /*
65*5184Sek110237  * Uses the kstat library or, if it is not available, the /proc/stat file
66*5184Sek110237  * to obtain cpu statistics. Collects statistics for each cpu, initializes
67*5184Sek110237  * a local pointer to the sysinfo kstat, and returns the sum of user and
68*5184Sek110237  * kernel time for all the cpus.
69*5184Sek110237  */
70*5184Sek110237 static vinteger_t
71*5184Sek110237 kstats_read_cpu(void)
72*5184Sek110237 {
73*5184Sek110237 	int ncpus;
74*5184Sek110237 	kstat_t	**cpu_stat_list = NULL;
75*5184Sek110237 	ulong_t cputime_states[CPU_STATES];
76*5184Sek110237 	hrtime_t cputime;
77*5184Sek110237 	int i;
78*5184Sek110237 
79*5184Sek110237 	kstat_t *ksp;
80*5184Sek110237 
81*5184Sek110237 	if (kstatp == NULL) {
82*5184Sek110237 		if ((kstatp = kstat_open()) == (kstat_ctl_t *)NULL) {
83*5184Sek110237 			filebench_log(LOG_ERROR, "Cannot read kstats");
84*5184Sek110237 			return (-1);
85*5184Sek110237 		}
86*5184Sek110237 	}
87*5184Sek110237 
88*5184Sek110237 	/*
89*5184Sek110237 	 * Per-CPU statistics
90*5184Sek110237 	 */
91*5184Sek110237 
92*5184Sek110237 	ncpus = 0;
93*5184Sek110237 	for (ksp = kstatp->kc_chain; ksp; ksp = ksp->ks_next)
94*5184Sek110237 		if (strncmp(ksp->ks_name, "cpu_stat", 8) == 0)
95*5184Sek110237 			ncpus++;
96*5184Sek110237 
97*5184Sek110237 	if ((cpu_stat_list =
98*5184Sek110237 	    (kstat_t **)malloc(ncpus * sizeof (kstat_t *))) == NULL) {
99*5184Sek110237 		filebench_log(LOG_ERROR, "malloc failed");
100*5184Sek110237 		return (-1);
101*5184Sek110237 	}
102*5184Sek110237 
103*5184Sek110237 	ncpus = 0;
104*5184Sek110237 	for (ksp = kstatp->kc_chain; ksp; ksp = ksp->ks_next)
105*5184Sek110237 		if (strncmp(ksp->ks_name, "cpu_stat", 8) == 0 &&
106*5184Sek110237 		    kstat_read(kstatp, ksp, NULL) != -1)
107*5184Sek110237 			cpu_stat_list[ncpus++] = ksp;
108*5184Sek110237 
109*5184Sek110237 	if (ncpus == 0) {
110*5184Sek110237 		filebench_log(LOG_ERROR,
111*5184Sek110237 		    "kstats can't find any cpu statistics");
112*5184Sek110237 		return (0);
113*5184Sek110237 	}
114*5184Sek110237 
115*5184Sek110237 	if (sysinfo_ksp == NULL)
116*5184Sek110237 		sysinfo_ksp = kstat_lookup(kstatp, "unix", 0, "sysinfo");
117*5184Sek110237 
118*5184Sek110237 	/* Sum across all CPUs */
119*5184Sek110237 	(void) memset(&cputime_states, 0, sizeof (cputime_states));
120*5184Sek110237 	for (i = 0; i < ncpus; i++) {
121*5184Sek110237 		cpu_stat_t cpu_stats;
122*5184Sek110237 		int j;
123*5184Sek110237 
124*5184Sek110237 		(void) kstat_read(kstatp, cpu_stat_list[i],
125*5184Sek110237 		    (void *) &cpu_stats);
126*5184Sek110237 		for (j = 0; j < CPU_STATES; j++)
127*5184Sek110237 			cputime_states[j] += cpu_stats.cpu_sysinfo.cpu[j];
128*5184Sek110237 	}
129*5184Sek110237 
130*5184Sek110237 	cputime = cputime_states[CPU_KERNEL] + cputime_states[CPU_USER];
131*5184Sek110237 
132*5184Sek110237 	return (10000000LL * cputime);
133*5184Sek110237 }
134*5184Sek110237 #else /* HAVE_LIBKSTAT */
135*5184Sek110237 #ifdef HAVE_PROC_STAT
136*5184Sek110237 static FILE *statfd = 0;
137*5184Sek110237 vinteger_t
138*5184Sek110237 kstats_read_cpu(void)
139*5184Sek110237 {
140*5184Sek110237 	/*
141*5184Sek110237 	 * Linux provides system wide statistics in /proc/stat
142*5184Sek110237 	 * The entry for cpu is
143*5184Sek110237 	 * cpu  1636 67 1392 208671 5407 20 12
144*5184Sek110237 	 * cpu0 626 8 997 104476 2499 7 7
145*5184Sek110237 	 * cpu1 1010 58 395 104195 2907 13 5
146*5184Sek110237 	 *
147*5184Sek110237 	 * The number of jiffies (1/100ths of  a  second)  that  the
148*5184Sek110237 	 * system  spent  in  user mode, user mode with low priority
149*5184Sek110237 	 * (nice), system mode, and  the  idle  task,  respectively.
150*5184Sek110237 	 */
151*5184Sek110237 	unsigned int user, nice, system;
152*5184Sek110237 	char cpu[128]; /* placeholder to read "cpu" */
153*5184Sek110237 	if (statfd == 0) {
154*5184Sek110237 		statfd = fopen("/proc/stat", "r");
155*5184Sek110237 		if (statfd < 0) {
156*5184Sek110237 			filebench_log(LOG_ERROR, "Cannot open /proc/stat");
157*5184Sek110237 			return (-1);
158*5184Sek110237 		}
159*5184Sek110237 	}
160*5184Sek110237 	if (fscanf(statfd, "%s %u %u %u", cpu, &user, &nice, &system) < 0) {
161*5184Sek110237 		filebench_log(LOG_ERROR, "Cannot read /proc/stat");
162*5184Sek110237 		return (-1);
163*5184Sek110237 	}
164*5184Sek110237 	/* convert jiffies to nanosecs */
165*5184Sek110237 	return ((user+nice+system)*1000000);
166*5184Sek110237 }
167*5184Sek110237 
168*5184Sek110237 #else /* HAVE_PROC_STAT */
169*5184Sek110237 vinteger_t
170*5184Sek110237 kstats_read_cpu(void)
171*5184Sek110237 {
172*5184Sek110237 	return (0);
173*5184Sek110237 }
174*5184Sek110237 #endif
175*5184Sek110237 #endif /* HAVE_LIBKSTAT */
176*5184Sek110237 
177*5184Sek110237 /*
178*5184Sek110237  * Returns the net cpu time used since the beginning of the run.
179*5184Sek110237  * Just calls kstat_read_cpu() and subtracts stats_cputime which
180*5184Sek110237  * is set at the beginning of the filebench run.
181*5184Sek110237  */
182*5184Sek110237 static hrtime_t
183*5184Sek110237 kstats_read_cpu_relative(void)
184*5184Sek110237 {
185*5184Sek110237 	hrtime_t cputime;
186*5184Sek110237 
187*5184Sek110237 	cputime = kstats_read_cpu();
188*5184Sek110237 	return (cputime - stats_cputime);
189*5184Sek110237 }
190*5184Sek110237 
191*5184Sek110237 /*
192*5184Sek110237  * IO Overhead CPU is the amount of CPU that is incurred running
193*5184Sek110237  * the benchmark infrastructure.
194*5184Sek110237  *
195*5184Sek110237  * It is computed as the sum of micro-state cpu time for each
196*5184Sek110237  * thread around the op being tested.
197*5184Sek110237  *
198*5184Sek110237  * Overhead time is computed for each flow.
199*5184Sek110237  *
200*5184Sek110237  * System overhead is computed as the overhead for I/O flows
201*5184Sek110237  * plus all other time running non-io related flowops
202*5184Sek110237  *
203*5184Sek110237  */
204*5184Sek110237 
205*5184Sek110237 /*
206*5184Sek110237  * Computes and returns the overhead CPU time attibutable to
207*5184Sek110237  * IO type flowops.
208*5184Sek110237  */
209*5184Sek110237 static hrtime_t
210*5184Sek110237 io_stats_ohead(void)
211*5184Sek110237 {
212*5184Sek110237 	flowstat_t *iostat = &globalstats[FLOW_TYPE_IO];
213*5184Sek110237 	flowstat_t *aiostat = &globalstats[FLOW_TYPE_AIO];
214*5184Sek110237 	flowstat_t *glstat = &globalstats[FLOW_TYPE_GLOBAL];
215*5184Sek110237 
216*5184Sek110237 	filebench_log(LOG_DEBUG_NEVER,
217*5184Sek110237 	    "Computing overhead as %lld + %lld - %lld - %lld",
218*5184Sek110237 	    glstat->fs_mstate[FLOW_MSTATE_OHEAD],
219*5184Sek110237 	    glstat->fs_mstate[FLOW_MSTATE_CPU],
220*5184Sek110237 	    iostat->fs_mstate[FLOW_MSTATE_CPU],
221*5184Sek110237 	    aiostat->fs_mstate[FLOW_MSTATE_CPU]);
222*5184Sek110237 
223*5184Sek110237 	return ((glstat->fs_mstate[FLOW_MSTATE_OHEAD] +
224*5184Sek110237 	    glstat->fs_mstate[FLOW_MSTATE_CPU] -
225*5184Sek110237 	    iostat->fs_mstate[FLOW_MSTATE_CPU] -
226*5184Sek110237 	    aiostat->fs_mstate[FLOW_MSTATE_CPU]));
227*5184Sek110237 }
228*5184Sek110237 
229*5184Sek110237 /*
230*5184Sek110237  * Returns the total overhead CPU time.
231*5184Sek110237  */
232*5184Sek110237 static hrtime_t
233*5184Sek110237 gl_stats_ohead(void)
234*5184Sek110237 {
235*5184Sek110237 	flowstat_t *glstat = &globalstats[FLOW_TYPE_GLOBAL];
236*5184Sek110237 
237*5184Sek110237 	return (glstat->fs_mstate[FLOW_MSTATE_OHEAD]);
238*5184Sek110237 }
239*5184Sek110237 
240*5184Sek110237 /*
241*5184Sek110237  * Places the value represented by "name" into the var_integer field of the
242*5184Sek110237  * supplied var_t. Compares the supplied "name" with a set of predefined
243*5184Sek110237  * names and calculates the value from the appropriate globalstats field(s).
244*5184Sek110237  */
245*5184Sek110237 var_t *
246*5184Sek110237 stats_findvar(var_t *var, char *name)
247*5184Sek110237 {
248*5184Sek110237 	flowstat_t *iostat = &globalstats[FLOW_TYPE_IO];
249*5184Sek110237 	flowstat_t *aiostat = &globalstats[FLOW_TYPE_AIO];
250*5184Sek110237 	flowstat_t *glstat = &globalstats[FLOW_TYPE_GLOBAL];
251*5184Sek110237 
252*5184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "reading stats %s", name);
253*5184Sek110237 
254*5184Sek110237 	if (globalstats == NULL)
255*5184Sek110237 		globalstats = malloc(FLOW_TYPES * sizeof (flowstat_t));
256*5184Sek110237 
257*5184Sek110237 	if (strcmp(name, "iocount") == 0) {
258*5184Sek110237 		var->var_integer = iostat->fs_count +
259*5184Sek110237 		    aiostat->fs_count;
260*5184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "reading stats %s = %lld",
261*5184Sek110237 		    name, var->var_integer);
262*5184Sek110237 		return (var);
263*5184Sek110237 	}
264*5184Sek110237 
265*5184Sek110237 	if (strcmp(name, "iorate") == 0) {
266*5184Sek110237 		/* LINTED E_ASSIGMENT_CAUSE_LOSS_PREC */
267*5184Sek110237 		var->var_integer = (iostat->fs_count + aiostat->fs_count) /
268*5184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS);
269*5184Sek110237 		return (var);
270*5184Sek110237 	}
271*5184Sek110237 
272*5184Sek110237 
273*5184Sek110237 	if (strcmp(name, "ioreadrate") == 0) {
274*5184Sek110237 		/* LINTED E_ASSIGMENT_CAUSE_LOSS_PREC */
275*5184Sek110237 		var->var_integer = (iostat->fs_rcount + aiostat->fs_rcount) /
276*5184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS);
277*5184Sek110237 		return (var);
278*5184Sek110237 	}
279*5184Sek110237 
280*5184Sek110237 
281*5184Sek110237 	if (strcmp(name, "iowriterate") == 0) {
282*5184Sek110237 		/* LINTED E_ASSIGMENT_CAUSE_LOSS_PREC */
283*5184Sek110237 		var->var_integer = (iostat->fs_wcount + aiostat->fs_wcount) /
284*5184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS);
285*5184Sek110237 		return (var);
286*5184Sek110237 	}
287*5184Sek110237 
288*5184Sek110237 
289*5184Sek110237 	if (strcmp(name, "iobandwidth") == 0) {
290*5184Sek110237 		/* LINTED E_ASSIGMENT_CAUSE_LOSS_PREC */
291*5184Sek110237 		var->var_integer =
292*5184Sek110237 		    ((iostat->fs_bytes + aiostat->fs_bytes) / (1024 * 1024)) /
293*5184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS);
294*5184Sek110237 		return (var);
295*5184Sek110237 	}
296*5184Sek110237 
297*5184Sek110237 	if (strcmp(name, "iolatency") == 0) {
298*5184Sek110237 		var->var_integer = iostat->fs_count ?
299*5184Sek110237 		    iostat->fs_mstate[FLOW_MSTATE_LAT] /
300*5184Sek110237 		    (iostat->fs_count * 1000UL) : 0;
301*5184Sek110237 		return (var);
302*5184Sek110237 	}
303*5184Sek110237 
304*5184Sek110237 	if (strcmp(name, "iocpu") == 0) {
305*5184Sek110237 		var->var_integer = (iostat->fs_count + aiostat->fs_count) ?
306*5184Sek110237 		    (iostat->fs_mstate[FLOW_MSTATE_CPU] +
307*5184Sek110237 		    aiostat->fs_mstate[FLOW_MSTATE_CPU]) / ((iostat->fs_count +
308*5184Sek110237 		    aiostat->fs_count) * 1000UL) : 0;
309*5184Sek110237 		return (var);
310*5184Sek110237 	}
311*5184Sek110237 
312*5184Sek110237 
313*5184Sek110237 	if (strcmp(name, "oheadcpu") == 0) {
314*5184Sek110237 		var->var_integer = (iostat->fs_count + aiostat->fs_count) ?
315*5184Sek110237 		    io_stats_ohead() / ((iostat->fs_count +
316*5184Sek110237 		    aiostat->fs_count) * 1000UL) : 0;
317*5184Sek110237 		return (var);
318*5184Sek110237 	}
319*5184Sek110237 
320*5184Sek110237 	if (strcmp(name, "iowait") == 0) {
321*5184Sek110237 		var->var_integer = iostat->fs_count ?
322*5184Sek110237 		    iostat->fs_mstate[FLOW_MSTATE_WAIT] /
323*5184Sek110237 		    (iostat->fs_count * 1000UL) : 0;
324*5184Sek110237 		return (var);
325*5184Sek110237 	}
326*5184Sek110237 
327*5184Sek110237 	if (strcmp(name, "syscpu") == 0) {
328*5184Sek110237 		/* LINTED E_ASSIGMENT_CAUSE_LOSS_PREC */
329*5184Sek110237 		var->var_integer = glstat->fs_syscpu / 1000.0;
330*5184Sek110237 		return (var);
331*5184Sek110237 	}
332*5184Sek110237 
333*5184Sek110237 	if (strcmp(name, "iocpusys") == 0) {
334*5184Sek110237 		var->var_integer = (iostat->fs_count + aiostat->fs_count) ?
335*5184Sek110237 		    iostat->fs_syscpu / ((iostat->fs_count +
336*5184Sek110237 		    aiostat->fs_count) * 1000UL) : 0;
337*5184Sek110237 
338*5184Sek110237 		return (var);
339*5184Sek110237 	}
340*5184Sek110237 
341*5184Sek110237 	filebench_log(LOG_DEBUG_IMPL,
342*5184Sek110237 	    "error reading stats %s", name);
343*5184Sek110237 
344*5184Sek110237 	return (NULL);
345*5184Sek110237 }
346*5184Sek110237 
347*5184Sek110237 /*
348*5184Sek110237  * Initializes the static variable "stats_cputime" with the
349*5184Sek110237  * current cpu time, for use by kstats_read_cpu_relative.
350*5184Sek110237  */
351*5184Sek110237 void
352*5184Sek110237 stats_init(void)
353*5184Sek110237 {
354*5184Sek110237 #if defined(HAVE_LIBKSTAT) || defined(LINUX_PORT)
355*5184Sek110237 	stats_cputime = kstats_read_cpu();
356*5184Sek110237 #else
357*5184Sek110237 	stats_cputime = 0;
358*5184Sek110237 #endif /* HAVE_LIBKSTAT */
359*5184Sek110237 }
360*5184Sek110237 
361*5184Sek110237 /*
362*5184Sek110237  * Add a flowstat b to a, leave sum in a.
363*5184Sek110237  */
364*5184Sek110237 static void
365*5184Sek110237 stats_add(flowstat_t *a, flowstat_t *b)
366*5184Sek110237 {
367*5184Sek110237 	int i;
368*5184Sek110237 
369*5184Sek110237 	a->fs_count += b->fs_count;
370*5184Sek110237 	a->fs_rcount += b->fs_rcount;
371*5184Sek110237 	a->fs_wcount += b->fs_wcount;
372*5184Sek110237 	a->fs_bytes += b->fs_bytes;
373*5184Sek110237 	a->fs_rbytes += b->fs_rbytes;
374*5184Sek110237 	a->fs_wbytes += b->fs_wbytes;
375*5184Sek110237 
376*5184Sek110237 	for (i = 0; i < FLOW_MSTATES; i++)
377*5184Sek110237 		a->fs_mstate[i] += b->fs_mstate[i];
378*5184Sek110237 }
379*5184Sek110237 
380*5184Sek110237 /*
381*5184Sek110237  * Takes a "snapshot" of the global statistics. Actually, it calculates
382*5184Sek110237  * them from the local statistics maintained by each flowop.
383*5184Sek110237  * First the routine pauses filebench, then rolls the statistics for
384*5184Sek110237  * each flowop into its associated FLOW_MASTER flowop.
385*5184Sek110237  * Next all the FLOW_MASTER flowops' statistics are written
386*5184Sek110237  * to the log file followed by the global totals. Then filebench
387*5184Sek110237  * operation is allowed to resume.
388*5184Sek110237  */
389*5184Sek110237 void
390*5184Sek110237 stats_snap(void)
391*5184Sek110237 {
392*5184Sek110237 	flowstat_t *iostat = &globalstats[FLOW_TYPE_IO];
393*5184Sek110237 	flowstat_t *aiostat = &globalstats[FLOW_TYPE_AIO];
394*5184Sek110237 	flowstat_t *glstat = &globalstats[FLOW_TYPE_GLOBAL];
395*5184Sek110237 	hrtime_t cputime;
396*5184Sek110237 	flowop_t *flowop;
397*5184Sek110237 	char *str;
398*5184Sek110237 
399*5184Sek110237 	if (globalstats == NULL) {
400*5184Sek110237 		filebench_log(LOG_ERROR,
401*5184Sek110237 		    "'stats snap' called before 'stats clear'");
402*5184Sek110237 		return;
403*5184Sek110237 	}
404*5184Sek110237 
405*5184Sek110237 	globalstats->fs_etime = gethrtime();
406*5184Sek110237 
407*5184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT, "Stats period = %ds",
408*5184Sek110237 	    (globalstats->fs_etime - globalstats->fs_stime) / 1000000000);
409*5184Sek110237 
410*5184Sek110237 	/* Freeze statistics during update */
411*5184Sek110237 	filebench_shm->bequiet = 1;
412*5184Sek110237 
413*5184Sek110237 	flowop = filebench_shm->flowoplist;
414*5184Sek110237 	while (flowop) {
415*5184Sek110237 		flowop_t *flowop_master;
416*5184Sek110237 
417*5184Sek110237 		if (flowop->fo_instance == FLOW_MASTER) {
418*5184Sek110237 			flowop = flowop->fo_next;
419*5184Sek110237 			continue;
420*5184Sek110237 		}
421*5184Sek110237 
422*5184Sek110237 		flowop_master = flowop_find_one(flowop->fo_name,
423*5184Sek110237 		    FLOW_MASTER);
424*5184Sek110237 
425*5184Sek110237 		/* Roll up per-flowop into global stats */
426*5184Sek110237 		stats_add(&globalstats[flowop->fo_type],
427*5184Sek110237 		    &flowop->fo_stats);
428*5184Sek110237 		stats_add(&globalstats[FLOW_TYPE_GLOBAL],
429*5184Sek110237 		    &flowop->fo_stats);
430*5184Sek110237 
431*5184Sek110237 		if (flowop_master && IS_FLOW_ACTIVE(flowop)) {
432*5184Sek110237 			flowop_master->fo_stats.fs_active++;
433*5184Sek110237 		}
434*5184Sek110237 
435*5184Sek110237 		if (flowop_master) {
436*5184Sek110237 			/* Roll up per-flow stats into master */
437*5184Sek110237 			flowop_master->fo_stats.fs_children++;
438*5184Sek110237 			stats_add(&flowop_master->fo_stats, &flowop->fo_stats);
439*5184Sek110237 		} else {
440*5184Sek110237 			filebench_log(LOG_DEBUG_NEVER,
441*5184Sek110237 			    "flowop_stats could not find %s",
442*5184Sek110237 			    flowop->fo_name);
443*5184Sek110237 		}
444*5184Sek110237 
445*5184Sek110237 		filebench_log(LOG_DEBUG_SCRIPT,
446*5184Sek110237 		    "flowop %-20s-%4d  - %5d ops, %5.1lf, ops/s %5.1lfmb/s "
447*5184Sek110237 		    "%8.3fms/op",
448*5184Sek110237 		    flowop->fo_name,
449*5184Sek110237 		    flowop->fo_instance,
450*5184Sek110237 		    flowop->fo_stats.fs_count,
451*5184Sek110237 		    flowop->fo_stats.fs_count /
452*5184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
453*5184Sek110237 		    (flowop->fo_stats.fs_bytes / (1024 * 1024)) /
454*5184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
455*5184Sek110237 		    flowop->fo_stats.fs_count ?
456*5184Sek110237 		    flowop->fo_stats.fs_mstate[FLOW_MSTATE_LAT] /
457*5184Sek110237 		    (flowop->fo_stats.fs_count * 1000000.0) : 0);
458*5184Sek110237 
459*5184Sek110237 		flowop = flowop->fo_next;
460*5184Sek110237 
461*5184Sek110237 	}
462*5184Sek110237 
463*5184Sek110237 #if defined(HAVE_LIBKSTAT) || defined(LINUX_PORT)
464*5184Sek110237 	cputime = kstats_read_cpu_relative();
465*5184Sek110237 #endif /* HAVE_LIBKSTAT */
466*5184Sek110237 
467*5184Sek110237 	filebench_log(LOG_DEBUG_IMPL,
468*5184Sek110237 	    "cputime = %lld, ohead = %lld",
469*5184Sek110237 	    cputime / 1000000000,
470*5184Sek110237 	    io_stats_ohead() / 1000000000);
471*5184Sek110237 	iostat->fs_syscpu =
472*5184Sek110237 	    (cputime > io_stats_ohead()) ?
473*5184Sek110237 	    (cputime - io_stats_ohead()) : 0;
474*5184Sek110237 	glstat->fs_syscpu =
475*5184Sek110237 	    (cputime > gl_stats_ohead()) ?
476*5184Sek110237 	    (cputime - gl_stats_ohead()) : 0;
477*5184Sek110237 
478*5184Sek110237 
479*5184Sek110237 	flowop = filebench_shm->flowoplist;
480*5184Sek110237 	str = malloc(1048576);
481*5184Sek110237 	*str = NULL;
482*5184Sek110237 	(void) strcpy(str, "Per-Operation Breakdown\n");
483*5184Sek110237 	while (flowop) {
484*5184Sek110237 		char line[1024];
485*5184Sek110237 
486*5184Sek110237 		if (flowop->fo_instance != FLOW_MASTER) {
487*5184Sek110237 			flowop = flowop->fo_next;
488*5184Sek110237 			continue;
489*5184Sek110237 		}
490*5184Sek110237 
491*5184Sek110237 		(void) snprintf(line, sizeof (line), "%-20s %8.0lfops/s "
492*5184Sek110237 		    "%5.1lfmb/s %8.1fms/op %8.0fus/op-cpu\n",
493*5184Sek110237 		    flowop->fo_name,
494*5184Sek110237 		    flowop->fo_stats.fs_count /
495*5184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
496*5184Sek110237 		    (flowop->fo_stats.fs_bytes / (1024 * 1024)) /
497*5184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
498*5184Sek110237 		    flowop->fo_stats.fs_count ?
499*5184Sek110237 		    flowop->fo_stats.fs_mstate[FLOW_MSTATE_LAT] /
500*5184Sek110237 		    (flowop->fo_stats.fs_count * 1000000.0) : 0,
501*5184Sek110237 		    flowop->fo_stats.fs_count ?
502*5184Sek110237 		    flowop->fo_stats.fs_mstate[FLOW_MSTATE_CPU] /
503*5184Sek110237 		    (flowop->fo_stats.fs_count * 1000.0) : 0);
504*5184Sek110237 		(void) strcat(str, line);
505*5184Sek110237 
506*5184Sek110237 		flowop = flowop->fo_next;
507*5184Sek110237 	}
508*5184Sek110237 
509*5184Sek110237 	filebench_log(LOG_INFO, "%s", str);
510*5184Sek110237 	free(str);
511*5184Sek110237 
512*5184Sek110237 	filebench_log(LOG_INFO,
513*5184Sek110237 	    "\nIO Summary:      %5d ops %5.1lf ops/s, (%0.0lf/%0.0lf r/w) "
514*5184Sek110237 	    "%5.1lfmb/s, %6.0fus cpu/op, %5.1fms latency",
515*5184Sek110237 	    iostat->fs_count + aiostat->fs_count,
516*5184Sek110237 	    (iostat->fs_count + aiostat->fs_count) /
517*5184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
518*5184Sek110237 	    (iostat->fs_rcount + aiostat->fs_rcount) /
519*5184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
520*5184Sek110237 	    (iostat->fs_wcount + aiostat->fs_wcount) /
521*5184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
522*5184Sek110237 	    ((iostat->fs_bytes + aiostat->fs_bytes) / (1024 * 1024)) /
523*5184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
524*5184Sek110237 	    (iostat->fs_rcount + iostat->fs_wcount +
525*5184Sek110237 	    aiostat->fs_rcount + aiostat->fs_wcount) ?
526*5184Sek110237 	    (iostat->fs_syscpu / 1000.0) /
527*5184Sek110237 	    (iostat->fs_rcount + iostat->fs_wcount +
528*5184Sek110237 	    aiostat->fs_rcount + aiostat->fs_wcount) : 0,
529*5184Sek110237 	    (iostat->fs_rcount + iostat->fs_wcount) ?
530*5184Sek110237 	    iostat->fs_mstate[FLOW_MSTATE_LAT] /
531*5184Sek110237 	    ((iostat->fs_rcount + iostat->fs_wcount) * 1000000.0) : 0);
532*5184Sek110237 
533*5184Sek110237 
534*5184Sek110237 	filebench_shm->bequiet = 0;
535*5184Sek110237 }
536*5184Sek110237 
537*5184Sek110237 /*
538*5184Sek110237  * Dumps the per-operation statistics and global statistics to the dump file.
539*5184Sek110237  */
540*5184Sek110237 void
541*5184Sek110237 stats_dump(char *filename)
542*5184Sek110237 {
543*5184Sek110237 	flowstat_t *iostat = &globalstats[FLOW_TYPE_IO];
544*5184Sek110237 	flowstat_t *aiostat = &globalstats[FLOW_TYPE_AIO];
545*5184Sek110237 	flowop_t *flowop;
546*5184Sek110237 
547*5184Sek110237 	(void) strcpy(filebench_shm->dump_filename, filename);
548*5184Sek110237 
549*5184Sek110237 	filebench_log(LOG_INFO, "in statsdump %s", filename);
550*5184Sek110237 
551*5184Sek110237 	if (filebench_shm->dump_fd > 0) {
552*5184Sek110237 		(void) close(filebench_shm->dump_fd);
553*5184Sek110237 		filebench_shm->dump_fd = -1;
554*5184Sek110237 	}
555*5184Sek110237 
556*5184Sek110237 	filebench_log(LOG_DUMP, "Flowop totals:");
557*5184Sek110237 
558*5184Sek110237 	flowop = filebench_shm->flowoplist;
559*5184Sek110237 	while (flowop) {
560*5184Sek110237 
561*5184Sek110237 		if (flowop->fo_instance != FLOW_MASTER) {
562*5184Sek110237 			flowop = flowop->fo_next;
563*5184Sek110237 			continue;
564*5184Sek110237 		}
565*5184Sek110237 
566*5184Sek110237 		filebench_log(LOG_DUMP,
567*5184Sek110237 		    "%-20s %8.0lfops/s %5.1lfmb/s "
568*5184Sek110237 		    "%8.1fms/op %8.0fus/op-cpu",
569*5184Sek110237 		    flowop->fo_name,
570*5184Sek110237 		    flowop->fo_stats.fs_count /
571*5184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
572*5184Sek110237 		    (flowop->fo_stats.fs_bytes / (1024 * 1024)) /
573*5184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
574*5184Sek110237 		    flowop->fo_stats.fs_count ?
575*5184Sek110237 		    flowop->fo_stats.fs_mstate[FLOW_MSTATE_LAT] /
576*5184Sek110237 		    (flowop->fo_stats.fs_count * 1000000.0) : 0,
577*5184Sek110237 		    flowop->fo_stats.fs_count ?
578*5184Sek110237 		    flowop->fo_stats.fs_mstate[FLOW_MSTATE_CPU] /
579*5184Sek110237 		    (flowop->fo_stats.fs_count * 1000.0) : 0);
580*5184Sek110237 
581*5184Sek110237 		flowop = flowop->fo_next;
582*5184Sek110237 	}
583*5184Sek110237 
584*5184Sek110237 	filebench_log(LOG_DUMP, "");
585*5184Sek110237 	filebench_log(LOG_DUMP,
586*5184Sek110237 	    "IO Summary:      %8d ops %8.1lf ops/s, %8.0lf/%0.0lf r/w"
587*5184Sek110237 	    "%8.1lfmb/s, %8.0fuscpu/op",
588*5184Sek110237 
589*5184Sek110237 	    iostat->fs_count + aiostat->fs_count,
590*5184Sek110237 	    (iostat->fs_count + aiostat->fs_count) /
591*5184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
592*5184Sek110237 
593*5184Sek110237 	    (iostat->fs_rcount + aiostat->fs_rcount) /
594*5184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
595*5184Sek110237 
596*5184Sek110237 	    (iostat->fs_wcount + aiostat->fs_wcount) /
597*5184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
598*5184Sek110237 
599*5184Sek110237 	    ((iostat->fs_bytes + aiostat->fs_bytes) / (1024 * 1024)) /
600*5184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
601*5184Sek110237 
602*5184Sek110237 	    (iostat->fs_rcount + iostat->fs_wcount +
603*5184Sek110237 	    aiostat->fs_rcount + aiostat->fs_wcount) ?
604*5184Sek110237 	    (iostat->fs_syscpu / 1000.0) /
605*5184Sek110237 	    (iostat->fs_rcount + iostat->fs_wcount +
606*5184Sek110237 	    aiostat->fs_rcount + aiostat->fs_wcount) : 0);
607*5184Sek110237 }
608*5184Sek110237 
609*5184Sek110237 /*
610*5184Sek110237  * Same as stats_dump, but in xml format.
611*5184Sek110237  */
612*5184Sek110237 void
613*5184Sek110237 stats_xmldump(char *filename)
614*5184Sek110237 {
615*5184Sek110237 	flowstat_t *iostat = &globalstats[FLOW_TYPE_IO];
616*5184Sek110237 	flowstat_t *aiostat = &globalstats[FLOW_TYPE_AIO];
617*5184Sek110237 	flowop_t *flowop;
618*5184Sek110237 
619*5184Sek110237 	(void) strcpy(filebench_shm->dump_filename, filename);
620*5184Sek110237 
621*5184Sek110237 	if (filebench_shm->dump_fd > 0) {
622*5184Sek110237 		(void) close(filebench_shm->dump_fd);
623*5184Sek110237 		filebench_shm->dump_fd = -1;
624*5184Sek110237 	}
625*5184Sek110237 
626*5184Sek110237 	filebench_log(LOG_DUMP, "<stat_doc name=\"Filebench Workload\">");
627*5184Sek110237 	filebench_log(LOG_DUMP, "<stat_group name=\"Flowop totals\">");
628*5184Sek110237 	filebench_log(LOG_DUMP, "<cell_list>");
629*5184Sek110237 
630*5184Sek110237 	flowop = filebench_shm->flowoplist;
631*5184Sek110237 	while (flowop) {
632*5184Sek110237 		if (flowop->fo_instance != FLOW_MASTER) {
633*5184Sek110237 			flowop = flowop->fo_next;
634*5184Sek110237 			continue;
635*5184Sek110237 		}
636*5184Sek110237 
637*5184Sek110237 		filebench_log(LOG_DUMP, "<cell>%0.0lf</cell>",
638*5184Sek110237 		    flowop->fo_stats.fs_count /
639*5184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS));
640*5184Sek110237 		filebench_log(LOG_DUMP, "<cell>%0.1lf</cell>",
641*5184Sek110237 		    (flowop->fo_stats.fs_bytes / (1024 * 1024)) /
642*5184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS));
643*5184Sek110237 		filebench_log(LOG_DUMP, "<cell>%0.1lf</cell>",
644*5184Sek110237 		    flowop->fo_stats.fs_count ?
645*5184Sek110237 		    flowop->fo_stats.fs_mstate[FLOW_MSTATE_LAT] /
646*5184Sek110237 		    (flowop->fo_stats.fs_count * 1000000.0) : 0);
647*5184Sek110237 		filebench_log(LOG_DUMP, "<cell>%0.0lf</cell>",
648*5184Sek110237 		    flowop->fo_stats.fs_count ?
649*5184Sek110237 		    flowop->fo_stats.fs_mstate[FLOW_MSTATE_CPU] /
650*5184Sek110237 		    (flowop->fo_stats.fs_count * 1000.0) : 0);
651*5184Sek110237 
652*5184Sek110237 		flowop = flowop->fo_next;
653*5184Sek110237 	}
654*5184Sek110237 	filebench_log(LOG_DUMP, "</cell_list>");
655*5184Sek110237 
656*5184Sek110237 	filebench_log(LOG_DUMP, "<dim_list>");
657*5184Sek110237 	filebench_log(LOG_DUMP, "<dim>");
658*5184Sek110237 	filebench_log(LOG_DUMP, "<dimval>Operations/sec</dimval>");
659*5184Sek110237 	filebench_log(LOG_DUMP, "<dimval>MB/sec</dimval>");
660*5184Sek110237 	filebench_log(LOG_DUMP, "<dimval>Latency (ms per operation)</dimval>");
661*5184Sek110237 	filebench_log(LOG_DUMP, "<dimval>CPU (us per operation)</dimval>");
662*5184Sek110237 	filebench_log(LOG_DUMP, "</dim>");
663*5184Sek110237 
664*5184Sek110237 	filebench_log(LOG_DUMP, "<dim>");
665*5184Sek110237 	flowop = filebench_shm->flowoplist;
666*5184Sek110237 	while (flowop) {
667*5184Sek110237 		if (flowop->fo_instance != FLOW_MASTER) {
668*5184Sek110237 			flowop = flowop->fo_next;
669*5184Sek110237 			continue;
670*5184Sek110237 		}
671*5184Sek110237 		filebench_log(LOG_DUMP, "<dimval>%s</dimval>", flowop->fo_name);
672*5184Sek110237 		flowop = flowop->fo_next;
673*5184Sek110237 	}
674*5184Sek110237 	filebench_log(LOG_DUMP, "</dim>");
675*5184Sek110237 	filebench_log(LOG_DUMP, "</dim_list>");
676*5184Sek110237 	filebench_log(LOG_DUMP, "</stat_group>");
677*5184Sek110237 
678*5184Sek110237 	filebench_log(LOG_DUMP, "<stat_group name=\"IO Summary\">");
679*5184Sek110237 	filebench_log(LOG_DUMP, "<cell_list>");
680*5184Sek110237 	filebench_log(LOG_DUMP, "<cell>%0d</cell>",
681*5184Sek110237 	    iostat->fs_count + aiostat->fs_count);
682*5184Sek110237 	filebench_log(LOG_DUMP, "<cell>%0.1lf</cell>",
683*5184Sek110237 	    (iostat->fs_count + aiostat->fs_count) /
684*5184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS));
685*5184Sek110237 	filebench_log(LOG_DUMP, "<cell>%0.0lf</cell>",
686*5184Sek110237 	    (iostat->fs_rcount + aiostat->fs_rcount) /
687*5184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS));
688*5184Sek110237 	filebench_log(LOG_DUMP, "<cell>%0.0lf</cell>",
689*5184Sek110237 	    (iostat->fs_wcount + aiostat->fs_wcount) /
690*5184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS));
691*5184Sek110237 	filebench_log(LOG_DUMP, "<cell>%0.1lf</cell>",
692*5184Sek110237 	    ((iostat->fs_bytes + aiostat->fs_bytes) / (1024 * 1024)) /
693*5184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS));
694*5184Sek110237 	filebench_log(LOG_DUMP, "<cell>%0.0f</cell>",
695*5184Sek110237 	    (iostat->fs_rcount + iostat->fs_wcount + aiostat->fs_rcount +
696*5184Sek110237 	    aiostat->fs_wcount) ? (iostat->fs_syscpu / 1000.0) /
697*5184Sek110237 	    (iostat->fs_rcount + iostat->fs_wcount + aiostat->fs_rcount +
698*5184Sek110237 	    aiostat->fs_wcount) : 0);
699*5184Sek110237 	filebench_log(LOG_DUMP, "</cell_list>");
700*5184Sek110237 
701*5184Sek110237 	filebench_log(LOG_DUMP, "<dim_list>");
702*5184Sek110237 	filebench_log(LOG_DUMP, "<dim>");
703*5184Sek110237 	filebench_log(LOG_DUMP, "<dimval>Operations</dimval>");
704*5184Sek110237 	filebench_log(LOG_DUMP, "<dimval>Operations/sec</dimval>");
705*5184Sek110237 	filebench_log(LOG_DUMP, "<dimval>Reads</dimval>");
706*5184Sek110237 	filebench_log(LOG_DUMP, "<dimval>Writes</dimval>");
707*5184Sek110237 	filebench_log(LOG_DUMP, "<dimval>MB/sec</dimval>");
708*5184Sek110237 	filebench_log(LOG_DUMP, "<dimval>CPU (us per operation)</dimval>");
709*5184Sek110237 	filebench_log(LOG_DUMP, "</dim>");
710*5184Sek110237 
711*5184Sek110237 	filebench_log(LOG_DUMP, "<dim>");
712*5184Sek110237 	filebench_log(LOG_DUMP, "<dimval>IO Summary</dimval>");
713*5184Sek110237 	filebench_log(LOG_DUMP, "</dim>");
714*5184Sek110237 	filebench_log(LOG_DUMP, "</dim_list>");
715*5184Sek110237 	filebench_log(LOG_DUMP, "</stat_group>");
716*5184Sek110237 	filebench_log(LOG_DUMP, "</stat_doc>");
717*5184Sek110237 }
718*5184Sek110237 
719*5184Sek110237 /*
720*5184Sek110237  * Clears all the statistics variables (fo_stats) for every defined flowop.
721*5184Sek110237  * It also creates a global flowstat table if one doesn't already exist and
722*5184Sek110237  * clears it.
723*5184Sek110237  */
724*5184Sek110237 void
725*5184Sek110237 stats_clear(void)
726*5184Sek110237 {
727*5184Sek110237 	flowop_t *flowop;
728*5184Sek110237 
729*5184Sek110237 #ifdef HAVE_LIBKSTAT
730*5184Sek110237 	stats_cputime = kstats_read_cpu();
731*5184Sek110237 #else
732*5184Sek110237 	stats_cputime = 0;
733*5184Sek110237 #endif /* HAVE_LIBKSTAT */
734*5184Sek110237 
735*5184Sek110237 	if (globalstats == NULL)
736*5184Sek110237 		globalstats = malloc(FLOW_TYPES * sizeof (flowstat_t));
737*5184Sek110237 
738*5184Sek110237 	(void) memset(globalstats, 0, FLOW_TYPES * sizeof (flowstat_t));
739*5184Sek110237 
740*5184Sek110237 	flowop = filebench_shm->flowoplist;
741*5184Sek110237 
742*5184Sek110237 	while (flowop) {
743*5184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "Clearing stats for %s-%d",
744*5184Sek110237 		    flowop->fo_name,
745*5184Sek110237 		    flowop->fo_instance);
746*5184Sek110237 		(void) memset(&flowop->fo_stats, 0, sizeof (flowstat_t));
747*5184Sek110237 		flowop = flowop->fo_next;
748*5184Sek110237 	}
749*5184Sek110237 
750*5184Sek110237 	(void) memset(globalstats, 0, sizeof (flowstat_t));
751*5184Sek110237 	globalstats->fs_stime = gethrtime();
752*5184Sek110237 }
753