15184Sek110237 /*
25184Sek110237  * CDDL HEADER START
35184Sek110237  *
45184Sek110237  * The contents of this file are subject to the terms of the
55184Sek110237  * Common Development and Distribution License (the "License").
65184Sek110237  * You may not use this file except in compliance with the License.
75184Sek110237  *
85184Sek110237  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95184Sek110237  * or http://www.opensolaris.org/os/licensing.
105184Sek110237  * See the License for the specific language governing permissions
115184Sek110237  * and limitations under the License.
125184Sek110237  *
135184Sek110237  * When distributing Covered Code, include this CDDL HEADER in each
145184Sek110237  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155184Sek110237  * If applicable, add the following below this CDDL HEADER, with the
165184Sek110237  * fields enclosed by brackets "[]" replaced with your own identifying
175184Sek110237  * information: Portions Copyright [yyyy] [name of copyright owner]
185184Sek110237  *
195184Sek110237  * CDDL HEADER END
205184Sek110237  */
215184Sek110237 /*
226084Saw148015  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
235184Sek110237  * Use is subject to license terms.
245184Sek110237  */
255184Sek110237 
265184Sek110237 #pragma ident	"%Z%%M%	%I%	%E% SMI"
275184Sek110237 
285184Sek110237 #include "config.h"
295184Sek110237 
305184Sek110237 #include <stdio.h>
315184Sek110237 #include <fcntl.h>
325184Sek110237 #include <sys/types.h>
335184Sek110237 
345184Sek110237 #ifdef HAVE_SYSINFO
355184Sek110237 #include <sys/sysinfo.h>
365184Sek110237 #endif
375184Sek110237 
385184Sek110237 #ifdef HAVE_LIBKSTAT
395184Sek110237 #include <kstat.h>
405184Sek110237 #include <sys/cpuvar.h>
415184Sek110237 #endif /* HAVE_LIBKSTAT */
425184Sek110237 
435184Sek110237 #include <stdarg.h>
445184Sek110237 
455184Sek110237 #include "filebench.h"
465184Sek110237 #include "flowop.h"
475184Sek110237 #include "vars.h"
485184Sek110237 #include "stats.h"
495184Sek110237 
505184Sek110237 /*
515184Sek110237  * A set of routines for collecting and dumping various filebench
525184Sek110237  * run statistics.
535184Sek110237  */
545184Sek110237 
555184Sek110237 /* Global statistics */
565184Sek110237 static flowstat_t *globalstats = NULL;
575184Sek110237 
585184Sek110237 static hrtime_t stats_cputime = 0;
595184Sek110237 
605184Sek110237 #ifdef HAVE_LIBKSTAT
615184Sek110237 static kstat_ctl_t *kstatp = NULL;
625184Sek110237 static kstat_t *sysinfo_ksp = NULL;
635184Sek110237 
645184Sek110237 /*
655184Sek110237  * Uses the kstat library or, if it is not available, the /proc/stat file
665184Sek110237  * to obtain cpu statistics. Collects statistics for each cpu, initializes
675184Sek110237  * a local pointer to the sysinfo kstat, and returns the sum of user and
685184Sek110237  * kernel time for all the cpus.
695184Sek110237  */
706212Saw148015 static fbint_t
715184Sek110237 kstats_read_cpu(void)
725184Sek110237 {
735184Sek110237 	int ncpus;
745184Sek110237 	kstat_t	**cpu_stat_list = NULL;
755184Sek110237 	ulong_t cputime_states[CPU_STATES];
765184Sek110237 	hrtime_t cputime;
775184Sek110237 	int i;
785184Sek110237 
795184Sek110237 	kstat_t *ksp;
805184Sek110237 
815184Sek110237 	if (kstatp == NULL) {
825184Sek110237 		if ((kstatp = kstat_open()) == (kstat_ctl_t *)NULL) {
835184Sek110237 			filebench_log(LOG_ERROR, "Cannot read kstats");
845184Sek110237 			return (-1);
855184Sek110237 		}
865184Sek110237 	}
875184Sek110237 
885184Sek110237 	/*
895184Sek110237 	 * Per-CPU statistics
905184Sek110237 	 */
915184Sek110237 
925184Sek110237 	ncpus = 0;
935184Sek110237 	for (ksp = kstatp->kc_chain; ksp; ksp = ksp->ks_next)
945184Sek110237 		if (strncmp(ksp->ks_name, "cpu_stat", 8) == 0)
955184Sek110237 			ncpus++;
965184Sek110237 
975184Sek110237 	if ((cpu_stat_list =
985184Sek110237 	    (kstat_t **)malloc(ncpus * sizeof (kstat_t *))) == NULL) {
995184Sek110237 		filebench_log(LOG_ERROR, "malloc failed");
1005184Sek110237 		return (-1);
1015184Sek110237 	}
1025184Sek110237 
1035184Sek110237 	ncpus = 0;
1045184Sek110237 	for (ksp = kstatp->kc_chain; ksp; ksp = ksp->ks_next)
1055184Sek110237 		if (strncmp(ksp->ks_name, "cpu_stat", 8) == 0 &&
1065184Sek110237 		    kstat_read(kstatp, ksp, NULL) != -1)
1075184Sek110237 			cpu_stat_list[ncpus++] = ksp;
1085184Sek110237 
1095184Sek110237 	if (ncpus == 0) {
1105184Sek110237 		filebench_log(LOG_ERROR,
1115184Sek110237 		    "kstats can't find any cpu statistics");
1125184Sek110237 		return (0);
1135184Sek110237 	}
1145184Sek110237 
1155184Sek110237 	if (sysinfo_ksp == NULL)
1165184Sek110237 		sysinfo_ksp = kstat_lookup(kstatp, "unix", 0, "sysinfo");
1175184Sek110237 
1185184Sek110237 	/* Sum across all CPUs */
1195184Sek110237 	(void) memset(&cputime_states, 0, sizeof (cputime_states));
1205184Sek110237 	for (i = 0; i < ncpus; i++) {
1215184Sek110237 		cpu_stat_t cpu_stats;
1225184Sek110237 		int j;
1235184Sek110237 
1245184Sek110237 		(void) kstat_read(kstatp, cpu_stat_list[i],
1255184Sek110237 		    (void *) &cpu_stats);
1265184Sek110237 		for (j = 0; j < CPU_STATES; j++)
1275184Sek110237 			cputime_states[j] += cpu_stats.cpu_sysinfo.cpu[j];
1285184Sek110237 	}
1295184Sek110237 
1305184Sek110237 	cputime = cputime_states[CPU_KERNEL] + cputime_states[CPU_USER];
1315184Sek110237 
1325184Sek110237 	return (10000000LL * cputime);
1335184Sek110237 }
1345184Sek110237 #else /* HAVE_LIBKSTAT */
1355184Sek110237 #ifdef HAVE_PROC_STAT
1365184Sek110237 static FILE *statfd = 0;
1376212Saw148015 fbint_t
1385184Sek110237 kstats_read_cpu(void)
1395184Sek110237 {
1405184Sek110237 	/*
1415184Sek110237 	 * Linux provides system wide statistics in /proc/stat
1425184Sek110237 	 * The entry for cpu is
1435184Sek110237 	 * cpu  1636 67 1392 208671 5407 20 12
1445184Sek110237 	 * cpu0 626 8 997 104476 2499 7 7
1455184Sek110237 	 * cpu1 1010 58 395 104195 2907 13 5
1465184Sek110237 	 *
1475184Sek110237 	 * The number of jiffies (1/100ths of  a  second)  that  the
1485184Sek110237 	 * system  spent  in  user mode, user mode with low priority
1495184Sek110237 	 * (nice), system mode, and  the  idle  task,  respectively.
1505184Sek110237 	 */
1515184Sek110237 	unsigned int user, nice, system;
1525184Sek110237 	char cpu[128]; /* placeholder to read "cpu" */
1535184Sek110237 	if (statfd == 0) {
1545184Sek110237 		statfd = fopen("/proc/stat", "r");
1555184Sek110237 		if (statfd < 0) {
1565184Sek110237 			filebench_log(LOG_ERROR, "Cannot open /proc/stat");
1575184Sek110237 			return (-1);
1585184Sek110237 		}
1595184Sek110237 	}
1605184Sek110237 	if (fscanf(statfd, "%s %u %u %u", cpu, &user, &nice, &system) < 0) {
1615184Sek110237 		filebench_log(LOG_ERROR, "Cannot read /proc/stat");
1625184Sek110237 		return (-1);
1635184Sek110237 	}
1645184Sek110237 	/* convert jiffies to nanosecs */
1655184Sek110237 	return ((user+nice+system)*1000000);
1665184Sek110237 }
1675184Sek110237 
1685184Sek110237 #else /* HAVE_PROC_STAT */
1696212Saw148015 fbint_t
1705184Sek110237 kstats_read_cpu(void)
1715184Sek110237 {
1725184Sek110237 	return (0);
1735184Sek110237 }
1745184Sek110237 #endif
1755184Sek110237 #endif /* HAVE_LIBKSTAT */
1765184Sek110237 
1775184Sek110237 /*
1785184Sek110237  * Returns the net cpu time used since the beginning of the run.
1795184Sek110237  * Just calls kstat_read_cpu() and subtracts stats_cputime which
1805184Sek110237  * is set at the beginning of the filebench run.
1815184Sek110237  */
1825184Sek110237 static hrtime_t
1835184Sek110237 kstats_read_cpu_relative(void)
1845184Sek110237 {
1855184Sek110237 	hrtime_t cputime;
1865184Sek110237 
1875184Sek110237 	cputime = kstats_read_cpu();
1885184Sek110237 	return (cputime - stats_cputime);
1895184Sek110237 }
1905184Sek110237 
1915184Sek110237 /*
1925184Sek110237  * IO Overhead CPU is the amount of CPU that is incurred running
1935184Sek110237  * the benchmark infrastructure.
1945184Sek110237  *
1955184Sek110237  * It is computed as the sum of micro-state cpu time for each
1965184Sek110237  * thread around the op being tested.
1975184Sek110237  *
1985184Sek110237  * Overhead time is computed for each flow.
1995184Sek110237  *
2005184Sek110237  * System overhead is computed as the overhead for I/O flows
2015184Sek110237  * plus all other time running non-io related flowops
2025184Sek110237  *
2035184Sek110237  */
2045184Sek110237 
2055184Sek110237 /*
2065184Sek110237  * Computes and returns the overhead CPU time attibutable to
2075184Sek110237  * IO type flowops.
2085184Sek110237  */
2095184Sek110237 static hrtime_t
2105184Sek110237 io_stats_ohead(void)
2115184Sek110237 {
2125184Sek110237 	flowstat_t *iostat = &globalstats[FLOW_TYPE_IO];
2135184Sek110237 	flowstat_t *aiostat = &globalstats[FLOW_TYPE_AIO];
2145184Sek110237 	flowstat_t *glstat = &globalstats[FLOW_TYPE_GLOBAL];
2155184Sek110237 
2165184Sek110237 	filebench_log(LOG_DEBUG_NEVER,
217*6286Saw148015 	    "Computing overhead as %llu + %llu - %llu - %llu",
218*6286Saw148015 	    (u_longlong_t)glstat->fs_mstate[FLOW_MSTATE_OHEAD],
219*6286Saw148015 	    (u_longlong_t)glstat->fs_mstate[FLOW_MSTATE_CPU],
220*6286Saw148015 	    (u_longlong_t)iostat->fs_mstate[FLOW_MSTATE_CPU],
221*6286Saw148015 	    (u_longlong_t)aiostat->fs_mstate[FLOW_MSTATE_CPU]);
2225184Sek110237 
2235184Sek110237 	return ((glstat->fs_mstate[FLOW_MSTATE_OHEAD] +
2245184Sek110237 	    glstat->fs_mstate[FLOW_MSTATE_CPU] -
2255184Sek110237 	    iostat->fs_mstate[FLOW_MSTATE_CPU] -
2265184Sek110237 	    aiostat->fs_mstate[FLOW_MSTATE_CPU]));
2275184Sek110237 }
2285184Sek110237 
2295184Sek110237 /*
2305184Sek110237  * Returns the total overhead CPU time.
2315184Sek110237  */
2325184Sek110237 static hrtime_t
2335184Sek110237 gl_stats_ohead(void)
2345184Sek110237 {
2355184Sek110237 	flowstat_t *glstat = &globalstats[FLOW_TYPE_GLOBAL];
2365184Sek110237 
2375184Sek110237 	return (glstat->fs_mstate[FLOW_MSTATE_OHEAD]);
2385184Sek110237 }
2395184Sek110237 
2405184Sek110237 /*
2416212Saw148015  * Places the value represented by "name" into the var_val.integer field of the
2425184Sek110237  * supplied var_t. Compares the supplied "name" with a set of predefined
2435184Sek110237  * names and calculates the value from the appropriate globalstats field(s).
2445184Sek110237  */
2455184Sek110237 var_t *
2465184Sek110237 stats_findvar(var_t *var, char *name)
2475184Sek110237 {
2485184Sek110237 	flowstat_t *iostat = &globalstats[FLOW_TYPE_IO];
2495184Sek110237 	flowstat_t *aiostat = &globalstats[FLOW_TYPE_AIO];
2505184Sek110237 	flowstat_t *glstat = &globalstats[FLOW_TYPE_GLOBAL];
2515184Sek110237 
2525184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "reading stats %s", name);
2535184Sek110237 
2545184Sek110237 	if (globalstats == NULL)
2555184Sek110237 		globalstats = malloc(FLOW_TYPES * sizeof (flowstat_t));
2565184Sek110237 
2575184Sek110237 	if (strcmp(name, "iocount") == 0) {
2586212Saw148015 		fbint_t stat;
2596212Saw148015 
2606212Saw148015 		stat = iostat->fs_count + aiostat->fs_count;
2616212Saw148015 		VAR_SET_INT(var, stat);
262*6286Saw148015 		filebench_log(LOG_DEBUG_IMPL, "reading stats %s = %llu",
263*6286Saw148015 		    name, (u_longlong_t)stat);
2645184Sek110237 		return (var);
2655184Sek110237 	}
2665184Sek110237 
2675184Sek110237 	if (strcmp(name, "iorate") == 0) {
2686212Saw148015 		fbint_t stat;
2696212Saw148015 
2705184Sek110237 		/* LINTED E_ASSIGMENT_CAUSE_LOSS_PREC */
2716212Saw148015 		stat = (iostat->fs_count + aiostat->fs_count) /
2725184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS);
2736212Saw148015 		VAR_SET_INT(var, stat);
2745184Sek110237 		return (var);
2755184Sek110237 	}
2765184Sek110237 
2775184Sek110237 
2785184Sek110237 	if (strcmp(name, "ioreadrate") == 0) {
2796212Saw148015 		fbint_t stat;
2806212Saw148015 
2815184Sek110237 		/* LINTED E_ASSIGMENT_CAUSE_LOSS_PREC */
2826212Saw148015 		stat = (iostat->fs_rcount + aiostat->fs_rcount) /
2835184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS);
2846212Saw148015 		VAR_SET_INT(var, stat);
2855184Sek110237 		return (var);
2865184Sek110237 	}
2875184Sek110237 
2885184Sek110237 
2895184Sek110237 	if (strcmp(name, "iowriterate") == 0) {
2906212Saw148015 		fbint_t stat;
2916212Saw148015 
2925184Sek110237 		/* LINTED E_ASSIGMENT_CAUSE_LOSS_PREC */
2936212Saw148015 		stat = (iostat->fs_wcount + aiostat->fs_wcount) /
2945184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS);
2956212Saw148015 		VAR_SET_INT(var, stat);
2965184Sek110237 		return (var);
2975184Sek110237 	}
2985184Sek110237 
2995184Sek110237 
3005184Sek110237 	if (strcmp(name, "iobandwidth") == 0) {
3016212Saw148015 		fbint_t stat;
3026212Saw148015 
3035184Sek110237 		/* LINTED E_ASSIGMENT_CAUSE_LOSS_PREC */
3046212Saw148015 		stat =
3055184Sek110237 		    ((iostat->fs_bytes + aiostat->fs_bytes) / (1024 * 1024)) /
3065184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS);
3076212Saw148015 		VAR_SET_INT(var, stat);
3085184Sek110237 		return (var);
3095184Sek110237 	}
3105184Sek110237 
3115184Sek110237 	if (strcmp(name, "iolatency") == 0) {
3126212Saw148015 		fbint_t stat;
3136212Saw148015 
3146212Saw148015 		stat = iostat->fs_count ? iostat->fs_mstate[FLOW_MSTATE_LAT] /
3155184Sek110237 		    (iostat->fs_count * 1000UL) : 0;
3166212Saw148015 		VAR_SET_INT(var, stat);
3175184Sek110237 		return (var);
3185184Sek110237 	}
3195184Sek110237 
3205184Sek110237 	if (strcmp(name, "iocpu") == 0) {
3216212Saw148015 		fbint_t stat;
3226212Saw148015 
3236212Saw148015 		stat = (iostat->fs_count + aiostat->fs_count) ?
3245184Sek110237 		    (iostat->fs_mstate[FLOW_MSTATE_CPU] +
3255184Sek110237 		    aiostat->fs_mstate[FLOW_MSTATE_CPU]) / ((iostat->fs_count +
3265184Sek110237 		    aiostat->fs_count) * 1000UL) : 0;
3276212Saw148015 		VAR_SET_INT(var, stat);
3285184Sek110237 		return (var);
3295184Sek110237 	}
3305184Sek110237 
3315184Sek110237 
3325184Sek110237 	if (strcmp(name, "oheadcpu") == 0) {
3336212Saw148015 		fbint_t stat;
3346212Saw148015 
3356212Saw148015 		stat = (iostat->fs_count + aiostat->fs_count) ?
3365184Sek110237 		    io_stats_ohead() / ((iostat->fs_count +
3375184Sek110237 		    aiostat->fs_count) * 1000UL) : 0;
3386212Saw148015 		VAR_SET_INT(var, stat);
3395184Sek110237 		return (var);
3405184Sek110237 	}
3415184Sek110237 
3425184Sek110237 	if (strcmp(name, "iowait") == 0) {
3436212Saw148015 		fbint_t stat;
3446212Saw148015 
3456212Saw148015 		stat = iostat->fs_count ?
3465184Sek110237 		    iostat->fs_mstate[FLOW_MSTATE_WAIT] /
3475184Sek110237 		    (iostat->fs_count * 1000UL) : 0;
3486212Saw148015 		VAR_SET_INT(var, stat);
3495184Sek110237 		return (var);
3505184Sek110237 	}
3515184Sek110237 
3525184Sek110237 	if (strcmp(name, "syscpu") == 0) {
3536212Saw148015 		fbint_t stat;
3546212Saw148015 
3555184Sek110237 		/* LINTED E_ASSIGMENT_CAUSE_LOSS_PREC */
3566212Saw148015 		stat = glstat->fs_syscpu / 1000.0;
3576212Saw148015 		VAR_SET_INT(var, stat);
3585184Sek110237 		return (var);
3595184Sek110237 	}
3605184Sek110237 
3615184Sek110237 	if (strcmp(name, "iocpusys") == 0) {
3626212Saw148015 		fbint_t stat;
3636212Saw148015 
3646212Saw148015 		stat = (iostat->fs_count + aiostat->fs_count) ?
3655184Sek110237 		    iostat->fs_syscpu / ((iostat->fs_count +
3665184Sek110237 		    aiostat->fs_count) * 1000UL) : 0;
3675184Sek110237 
3686212Saw148015 		VAR_SET_INT(var, stat);
3695184Sek110237 		return (var);
3705184Sek110237 	}
3715184Sek110237 
3725184Sek110237 	filebench_log(LOG_DEBUG_IMPL,
3735184Sek110237 	    "error reading stats %s", name);
3745184Sek110237 
3755184Sek110237 	return (NULL);
3765184Sek110237 }
3775184Sek110237 
3785184Sek110237 /*
3795184Sek110237  * Initializes the static variable "stats_cputime" with the
3805184Sek110237  * current cpu time, for use by kstats_read_cpu_relative.
3815184Sek110237  */
3825184Sek110237 void
3835184Sek110237 stats_init(void)
3845184Sek110237 {
3855184Sek110237 #if defined(HAVE_LIBKSTAT) || defined(LINUX_PORT)
3865184Sek110237 	stats_cputime = kstats_read_cpu();
3875184Sek110237 #else
3885184Sek110237 	stats_cputime = 0;
3895184Sek110237 #endif /* HAVE_LIBKSTAT */
3905184Sek110237 }
3915184Sek110237 
3925184Sek110237 /*
3935184Sek110237  * Add a flowstat b to a, leave sum in a.
3945184Sek110237  */
3955184Sek110237 static void
3965184Sek110237 stats_add(flowstat_t *a, flowstat_t *b)
3975184Sek110237 {
3985184Sek110237 	int i;
3995184Sek110237 
4005184Sek110237 	a->fs_count += b->fs_count;
4015184Sek110237 	a->fs_rcount += b->fs_rcount;
4025184Sek110237 	a->fs_wcount += b->fs_wcount;
4035184Sek110237 	a->fs_bytes += b->fs_bytes;
4045184Sek110237 	a->fs_rbytes += b->fs_rbytes;
4055184Sek110237 	a->fs_wbytes += b->fs_wbytes;
4065184Sek110237 
4075184Sek110237 	for (i = 0; i < FLOW_MSTATES; i++)
4085184Sek110237 		a->fs_mstate[i] += b->fs_mstate[i];
4095184Sek110237 }
4105184Sek110237 
4115184Sek110237 /*
4125184Sek110237  * Takes a "snapshot" of the global statistics. Actually, it calculates
4135184Sek110237  * them from the local statistics maintained by each flowop.
4145184Sek110237  * First the routine pauses filebench, then rolls the statistics for
4155184Sek110237  * each flowop into its associated FLOW_MASTER flowop.
4165184Sek110237  * Next all the FLOW_MASTER flowops' statistics are written
4175184Sek110237  * to the log file followed by the global totals. Then filebench
4185184Sek110237  * operation is allowed to resume.
4195184Sek110237  */
4205184Sek110237 void
4215184Sek110237 stats_snap(void)
4225184Sek110237 {
4235184Sek110237 	flowstat_t *iostat = &globalstats[FLOW_TYPE_IO];
4245184Sek110237 	flowstat_t *aiostat = &globalstats[FLOW_TYPE_AIO];
4255184Sek110237 	flowstat_t *glstat = &globalstats[FLOW_TYPE_GLOBAL];
4265184Sek110237 	hrtime_t cputime;
4275184Sek110237 	flowop_t *flowop;
4285184Sek110237 	char *str;
4295184Sek110237 
4305184Sek110237 	if (globalstats == NULL) {
4315184Sek110237 		filebench_log(LOG_ERROR,
4325184Sek110237 		    "'stats snap' called before 'stats clear'");
4335184Sek110237 		return;
4345184Sek110237 	}
4355184Sek110237 
4366084Saw148015 	/* don't print out if run ended in error */
4376084Saw148015 	if (filebench_shm->f_abort == FILEBENCH_ABORT_ERROR) {
4386084Saw148015 		filebench_log(LOG_ERROR,
4396084Saw148015 		    "NO VALID RESULTS! FileBench run terminated prematurely");
4406084Saw148015 		return;
4416084Saw148015 	}
4426084Saw148015 
4435184Sek110237 	globalstats->fs_etime = gethrtime();
4445184Sek110237 
4455184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT, "Stats period = %ds",
4465184Sek110237 	    (globalstats->fs_etime - globalstats->fs_stime) / 1000000000);
4475184Sek110237 
4485184Sek110237 	/* Freeze statistics during update */
4495184Sek110237 	filebench_shm->bequiet = 1;
4505184Sek110237 
4515184Sek110237 	flowop = filebench_shm->flowoplist;
4525184Sek110237 	while (flowop) {
4535184Sek110237 		flowop_t *flowop_master;
4545184Sek110237 
4556212Saw148015 		if (flowop->fo_instance <= FLOW_DEFINITION) {
4565184Sek110237 			flowop = flowop->fo_next;
4575184Sek110237 			continue;
4585184Sek110237 		}
4595184Sek110237 
4606212Saw148015 		flowop_master = flowop_find_one(flowop->fo_name, FLOW_MASTER);
4615184Sek110237 
4625184Sek110237 		/* Roll up per-flowop into global stats */
4635184Sek110237 		stats_add(&globalstats[flowop->fo_type],
4645184Sek110237 		    &flowop->fo_stats);
4655184Sek110237 		stats_add(&globalstats[FLOW_TYPE_GLOBAL],
4665184Sek110237 		    &flowop->fo_stats);
4675184Sek110237 
4685184Sek110237 		if (flowop_master && IS_FLOW_ACTIVE(flowop)) {
4695184Sek110237 			flowop_master->fo_stats.fs_active++;
4705184Sek110237 		}
4715184Sek110237 
4725184Sek110237 		if (flowop_master) {
4735184Sek110237 			/* Roll up per-flow stats into master */
4745184Sek110237 			flowop_master->fo_stats.fs_children++;
4755184Sek110237 			stats_add(&flowop_master->fo_stats, &flowop->fo_stats);
4765184Sek110237 		} else {
4775184Sek110237 			filebench_log(LOG_DEBUG_NEVER,
4785184Sek110237 			    "flowop_stats could not find %s",
4795184Sek110237 			    flowop->fo_name);
4805184Sek110237 		}
4815184Sek110237 
4825184Sek110237 		filebench_log(LOG_DEBUG_SCRIPT,
4835184Sek110237 		    "flowop %-20s-%4d  - %5d ops, %5.1lf, ops/s %5.1lfmb/s "
4845184Sek110237 		    "%8.3fms/op",
4855184Sek110237 		    flowop->fo_name,
4865184Sek110237 		    flowop->fo_instance,
4875184Sek110237 		    flowop->fo_stats.fs_count,
4885184Sek110237 		    flowop->fo_stats.fs_count /
4895184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
4905184Sek110237 		    (flowop->fo_stats.fs_bytes / (1024 * 1024)) /
4915184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
4925184Sek110237 		    flowop->fo_stats.fs_count ?
4935184Sek110237 		    flowop->fo_stats.fs_mstate[FLOW_MSTATE_LAT] /
4945184Sek110237 		    (flowop->fo_stats.fs_count * 1000000.0) : 0);
4955184Sek110237 
4965184Sek110237 		flowop = flowop->fo_next;
4975184Sek110237 
4985184Sek110237 	}
4995184Sek110237 
5005184Sek110237 #if defined(HAVE_LIBKSTAT) || defined(LINUX_PORT)
5015184Sek110237 	cputime = kstats_read_cpu_relative();
5025184Sek110237 #endif /* HAVE_LIBKSTAT */
5035184Sek110237 
5045184Sek110237 	filebench_log(LOG_DEBUG_IMPL,
505*6286Saw148015 	    "cputime = %llu, ohead = %llu",
506*6286Saw148015 	    (u_longlong_t)(cputime / 1000000000),
507*6286Saw148015 	    (u_longlong_t)(io_stats_ohead() / 1000000000));
5085184Sek110237 	iostat->fs_syscpu =
5095184Sek110237 	    (cputime > io_stats_ohead()) ?
5105184Sek110237 	    (cputime - io_stats_ohead()) : 0;
5115184Sek110237 	glstat->fs_syscpu =
5125184Sek110237 	    (cputime > gl_stats_ohead()) ?
5135184Sek110237 	    (cputime - gl_stats_ohead()) : 0;
5145184Sek110237 
5155184Sek110237 
5165184Sek110237 	flowop = filebench_shm->flowoplist;
5175184Sek110237 	str = malloc(1048576);
5185184Sek110237 	*str = NULL;
5195184Sek110237 	(void) strcpy(str, "Per-Operation Breakdown\n");
5205184Sek110237 	while (flowop) {
5215184Sek110237 		char line[1024];
5225184Sek110237 
5235184Sek110237 		if (flowop->fo_instance != FLOW_MASTER) {
5245184Sek110237 			flowop = flowop->fo_next;
5255184Sek110237 			continue;
5265184Sek110237 		}
5275184Sek110237 
5285184Sek110237 		(void) snprintf(line, sizeof (line), "%-20s %8.0lfops/s "
5295184Sek110237 		    "%5.1lfmb/s %8.1fms/op %8.0fus/op-cpu\n",
5305184Sek110237 		    flowop->fo_name,
5315184Sek110237 		    flowop->fo_stats.fs_count /
5325184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
5335184Sek110237 		    (flowop->fo_stats.fs_bytes / (1024 * 1024)) /
5345184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
5355184Sek110237 		    flowop->fo_stats.fs_count ?
5365184Sek110237 		    flowop->fo_stats.fs_mstate[FLOW_MSTATE_LAT] /
5375184Sek110237 		    (flowop->fo_stats.fs_count * 1000000.0) : 0,
5385184Sek110237 		    flowop->fo_stats.fs_count ?
5395184Sek110237 		    flowop->fo_stats.fs_mstate[FLOW_MSTATE_CPU] /
5405184Sek110237 		    (flowop->fo_stats.fs_count * 1000.0) : 0);
5415184Sek110237 		(void) strcat(str, line);
5425184Sek110237 
5435184Sek110237 		flowop = flowop->fo_next;
5445184Sek110237 	}
5455184Sek110237 
5465184Sek110237 	filebench_log(LOG_INFO, "%s", str);
5475184Sek110237 	free(str);
5485184Sek110237 
5495184Sek110237 	filebench_log(LOG_INFO,
5505184Sek110237 	    "\nIO Summary:      %5d ops %5.1lf ops/s, (%0.0lf/%0.0lf r/w) "
5515184Sek110237 	    "%5.1lfmb/s, %6.0fus cpu/op, %5.1fms latency",
5525184Sek110237 	    iostat->fs_count + aiostat->fs_count,
5535184Sek110237 	    (iostat->fs_count + aiostat->fs_count) /
5545184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
5555184Sek110237 	    (iostat->fs_rcount + aiostat->fs_rcount) /
5565184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
5575184Sek110237 	    (iostat->fs_wcount + aiostat->fs_wcount) /
5585184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
5595184Sek110237 	    ((iostat->fs_bytes + aiostat->fs_bytes) / (1024 * 1024)) /
5605184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
5615184Sek110237 	    (iostat->fs_rcount + iostat->fs_wcount +
5625184Sek110237 	    aiostat->fs_rcount + aiostat->fs_wcount) ?
5635184Sek110237 	    (iostat->fs_syscpu / 1000.0) /
5645184Sek110237 	    (iostat->fs_rcount + iostat->fs_wcount +
5655184Sek110237 	    aiostat->fs_rcount + aiostat->fs_wcount) : 0,
5665184Sek110237 	    (iostat->fs_rcount + iostat->fs_wcount) ?
5675184Sek110237 	    iostat->fs_mstate[FLOW_MSTATE_LAT] /
5685184Sek110237 	    ((iostat->fs_rcount + iostat->fs_wcount) * 1000000.0) : 0);
5695184Sek110237 
5705184Sek110237 
5715184Sek110237 	filebench_shm->bequiet = 0;
5725184Sek110237 }
5735184Sek110237 
5745184Sek110237 /*
5755184Sek110237  * Dumps the per-operation statistics and global statistics to the dump file.
5765184Sek110237  */
5775184Sek110237 void
5785184Sek110237 stats_dump(char *filename)
5795184Sek110237 {
5805184Sek110237 	flowstat_t *iostat = &globalstats[FLOW_TYPE_IO];
5815184Sek110237 	flowstat_t *aiostat = &globalstats[FLOW_TYPE_AIO];
5825184Sek110237 	flowop_t *flowop;
5835184Sek110237 
5846084Saw148015 	/* don't dump stats if run ended in error */
5856084Saw148015 	if (filebench_shm->f_abort == FILEBENCH_ABORT_ERROR)
5866084Saw148015 		return;
5876084Saw148015 
5885184Sek110237 	(void) strcpy(filebench_shm->dump_filename, filename);
5895184Sek110237 
5905184Sek110237 	filebench_log(LOG_INFO, "in statsdump %s", filename);
5915184Sek110237 
5925184Sek110237 	if (filebench_shm->dump_fd > 0) {
5935184Sek110237 		(void) close(filebench_shm->dump_fd);
5945184Sek110237 		filebench_shm->dump_fd = -1;
5955184Sek110237 	}
5965184Sek110237 
5975184Sek110237 	filebench_log(LOG_DUMP, "Flowop totals:");
5985184Sek110237 
5995184Sek110237 	flowop = filebench_shm->flowoplist;
6005184Sek110237 	while (flowop) {
6015184Sek110237 
6025184Sek110237 		if (flowop->fo_instance != FLOW_MASTER) {
6035184Sek110237 			flowop = flowop->fo_next;
6045184Sek110237 			continue;
6055184Sek110237 		}
6065184Sek110237 
6075184Sek110237 		filebench_log(LOG_DUMP,
6085184Sek110237 		    "%-20s %8.0lfops/s %5.1lfmb/s "
6095184Sek110237 		    "%8.1fms/op %8.0fus/op-cpu",
6105184Sek110237 		    flowop->fo_name,
6115184Sek110237 		    flowop->fo_stats.fs_count /
6125184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
6135184Sek110237 		    (flowop->fo_stats.fs_bytes / (1024 * 1024)) /
6145184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
6155184Sek110237 		    flowop->fo_stats.fs_count ?
6165184Sek110237 		    flowop->fo_stats.fs_mstate[FLOW_MSTATE_LAT] /
6175184Sek110237 		    (flowop->fo_stats.fs_count * 1000000.0) : 0,
6185184Sek110237 		    flowop->fo_stats.fs_count ?
6195184Sek110237 		    flowop->fo_stats.fs_mstate[FLOW_MSTATE_CPU] /
6205184Sek110237 		    (flowop->fo_stats.fs_count * 1000.0) : 0);
6215184Sek110237 
6225184Sek110237 		flowop = flowop->fo_next;
6235184Sek110237 	}
6245184Sek110237 
6255184Sek110237 	filebench_log(LOG_DUMP, "");
6265184Sek110237 	filebench_log(LOG_DUMP,
6275184Sek110237 	    "IO Summary:      %8d ops %8.1lf ops/s, %8.0lf/%0.0lf r/w"
6285184Sek110237 	    "%8.1lfmb/s, %8.0fuscpu/op",
6295184Sek110237 
6305184Sek110237 	    iostat->fs_count + aiostat->fs_count,
6315184Sek110237 	    (iostat->fs_count + aiostat->fs_count) /
6325184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
6335184Sek110237 
6345184Sek110237 	    (iostat->fs_rcount + aiostat->fs_rcount) /
6355184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
6365184Sek110237 
6375184Sek110237 	    (iostat->fs_wcount + aiostat->fs_wcount) /
6385184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
6395184Sek110237 
6405184Sek110237 	    ((iostat->fs_bytes + aiostat->fs_bytes) / (1024 * 1024)) /
6415184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS),
6425184Sek110237 
6435184Sek110237 	    (iostat->fs_rcount + iostat->fs_wcount +
6445184Sek110237 	    aiostat->fs_rcount + aiostat->fs_wcount) ?
6455184Sek110237 	    (iostat->fs_syscpu / 1000.0) /
6465184Sek110237 	    (iostat->fs_rcount + iostat->fs_wcount +
6475184Sek110237 	    aiostat->fs_rcount + aiostat->fs_wcount) : 0);
6485184Sek110237 }
6495184Sek110237 
6505184Sek110237 /*
6515184Sek110237  * Same as stats_dump, but in xml format.
6525184Sek110237  */
6535184Sek110237 void
6545184Sek110237 stats_xmldump(char *filename)
6555184Sek110237 {
6565184Sek110237 	flowstat_t *iostat = &globalstats[FLOW_TYPE_IO];
6575184Sek110237 	flowstat_t *aiostat = &globalstats[FLOW_TYPE_AIO];
6585184Sek110237 	flowop_t *flowop;
6595184Sek110237 
6606084Saw148015 	/* don't dump stats if run ended in error */
6616084Saw148015 	if (filebench_shm->f_abort == FILEBENCH_ABORT_ERROR)
6626084Saw148015 		return;
6636084Saw148015 
6645184Sek110237 	(void) strcpy(filebench_shm->dump_filename, filename);
6655184Sek110237 
6665184Sek110237 	if (filebench_shm->dump_fd > 0) {
6675184Sek110237 		(void) close(filebench_shm->dump_fd);
6685184Sek110237 		filebench_shm->dump_fd = -1;
6695184Sek110237 	}
6705184Sek110237 
6715184Sek110237 	filebench_log(LOG_DUMP, "<stat_doc name=\"Filebench Workload\">");
6725184Sek110237 	filebench_log(LOG_DUMP, "<stat_group name=\"Flowop totals\">");
6735184Sek110237 	filebench_log(LOG_DUMP, "<cell_list>");
6745184Sek110237 
6755184Sek110237 	flowop = filebench_shm->flowoplist;
6765184Sek110237 	while (flowop) {
6775184Sek110237 		if (flowop->fo_instance != FLOW_MASTER) {
6785184Sek110237 			flowop = flowop->fo_next;
6795184Sek110237 			continue;
6805184Sek110237 		}
6815184Sek110237 
6825184Sek110237 		filebench_log(LOG_DUMP, "<cell>%0.0lf</cell>",
6835184Sek110237 		    flowop->fo_stats.fs_count /
6845184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS));
6855184Sek110237 		filebench_log(LOG_DUMP, "<cell>%0.1lf</cell>",
6865184Sek110237 		    (flowop->fo_stats.fs_bytes / (1024 * 1024)) /
6875184Sek110237 		    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS));
6885184Sek110237 		filebench_log(LOG_DUMP, "<cell>%0.1lf</cell>",
6895184Sek110237 		    flowop->fo_stats.fs_count ?
6905184Sek110237 		    flowop->fo_stats.fs_mstate[FLOW_MSTATE_LAT] /
6915184Sek110237 		    (flowop->fo_stats.fs_count * 1000000.0) : 0);
6925184Sek110237 		filebench_log(LOG_DUMP, "<cell>%0.0lf</cell>",
6935184Sek110237 		    flowop->fo_stats.fs_count ?
6945184Sek110237 		    flowop->fo_stats.fs_mstate[FLOW_MSTATE_CPU] /
6955184Sek110237 		    (flowop->fo_stats.fs_count * 1000.0) : 0);
6965184Sek110237 
6975184Sek110237 		flowop = flowop->fo_next;
6985184Sek110237 	}
6995184Sek110237 	filebench_log(LOG_DUMP, "</cell_list>");
7005184Sek110237 
7015184Sek110237 	filebench_log(LOG_DUMP, "<dim_list>");
7025184Sek110237 	filebench_log(LOG_DUMP, "<dim>");
7035184Sek110237 	filebench_log(LOG_DUMP, "<dimval>Operations/sec</dimval>");
7045184Sek110237 	filebench_log(LOG_DUMP, "<dimval>MB/sec</dimval>");
7055184Sek110237 	filebench_log(LOG_DUMP, "<dimval>Latency (ms per operation)</dimval>");
7065184Sek110237 	filebench_log(LOG_DUMP, "<dimval>CPU (us per operation)</dimval>");
7075184Sek110237 	filebench_log(LOG_DUMP, "</dim>");
7085184Sek110237 
7095184Sek110237 	filebench_log(LOG_DUMP, "<dim>");
7105184Sek110237 	flowop = filebench_shm->flowoplist;
7115184Sek110237 	while (flowop) {
7125184Sek110237 		if (flowop->fo_instance != FLOW_MASTER) {
7135184Sek110237 			flowop = flowop->fo_next;
7145184Sek110237 			continue;
7155184Sek110237 		}
7165184Sek110237 		filebench_log(LOG_DUMP, "<dimval>%s</dimval>", flowop->fo_name);
7175184Sek110237 		flowop = flowop->fo_next;
7185184Sek110237 	}
7195184Sek110237 	filebench_log(LOG_DUMP, "</dim>");
7205184Sek110237 	filebench_log(LOG_DUMP, "</dim_list>");
7215184Sek110237 	filebench_log(LOG_DUMP, "</stat_group>");
7225184Sek110237 
7235184Sek110237 	filebench_log(LOG_DUMP, "<stat_group name=\"IO Summary\">");
7245184Sek110237 	filebench_log(LOG_DUMP, "<cell_list>");
7255184Sek110237 	filebench_log(LOG_DUMP, "<cell>%0d</cell>",
7265184Sek110237 	    iostat->fs_count + aiostat->fs_count);
7275184Sek110237 	filebench_log(LOG_DUMP, "<cell>%0.1lf</cell>",
7285184Sek110237 	    (iostat->fs_count + aiostat->fs_count) /
7295184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS));
7305184Sek110237 	filebench_log(LOG_DUMP, "<cell>%0.0lf</cell>",
7315184Sek110237 	    (iostat->fs_rcount + aiostat->fs_rcount) /
7325184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS));
7335184Sek110237 	filebench_log(LOG_DUMP, "<cell>%0.0lf</cell>",
7345184Sek110237 	    (iostat->fs_wcount + aiostat->fs_wcount) /
7355184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS));
7365184Sek110237 	filebench_log(LOG_DUMP, "<cell>%0.1lf</cell>",
7375184Sek110237 	    ((iostat->fs_bytes + aiostat->fs_bytes) / (1024 * 1024)) /
7385184Sek110237 	    ((globalstats->fs_etime - globalstats->fs_stime) / FSECS));
7395184Sek110237 	filebench_log(LOG_DUMP, "<cell>%0.0f</cell>",
7405184Sek110237 	    (iostat->fs_rcount + iostat->fs_wcount + aiostat->fs_rcount +
7415184Sek110237 	    aiostat->fs_wcount) ? (iostat->fs_syscpu / 1000.0) /
7425184Sek110237 	    (iostat->fs_rcount + iostat->fs_wcount + aiostat->fs_rcount +
7435184Sek110237 	    aiostat->fs_wcount) : 0);
7445184Sek110237 	filebench_log(LOG_DUMP, "</cell_list>");
7455184Sek110237 
7465184Sek110237 	filebench_log(LOG_DUMP, "<dim_list>");
7475184Sek110237 	filebench_log(LOG_DUMP, "<dim>");
7485184Sek110237 	filebench_log(LOG_DUMP, "<dimval>Operations</dimval>");
7495184Sek110237 	filebench_log(LOG_DUMP, "<dimval>Operations/sec</dimval>");
7505184Sek110237 	filebench_log(LOG_DUMP, "<dimval>Reads</dimval>");
7515184Sek110237 	filebench_log(LOG_DUMP, "<dimval>Writes</dimval>");
7525184Sek110237 	filebench_log(LOG_DUMP, "<dimval>MB/sec</dimval>");
7535184Sek110237 	filebench_log(LOG_DUMP, "<dimval>CPU (us per operation)</dimval>");
7545184Sek110237 	filebench_log(LOG_DUMP, "</dim>");
7555184Sek110237 
7565184Sek110237 	filebench_log(LOG_DUMP, "<dim>");
7575184Sek110237 	filebench_log(LOG_DUMP, "<dimval>IO Summary</dimval>");
7585184Sek110237 	filebench_log(LOG_DUMP, "</dim>");
7595184Sek110237 	filebench_log(LOG_DUMP, "</dim_list>");
7605184Sek110237 	filebench_log(LOG_DUMP, "</stat_group>");
7615184Sek110237 	filebench_log(LOG_DUMP, "</stat_doc>");
7625184Sek110237 }
7635184Sek110237 
7645184Sek110237 /*
7655184Sek110237  * Clears all the statistics variables (fo_stats) for every defined flowop.
7665184Sek110237  * It also creates a global flowstat table if one doesn't already exist and
7675184Sek110237  * clears it.
7685184Sek110237  */
7695184Sek110237 void
7705184Sek110237 stats_clear(void)
7715184Sek110237 {
7725184Sek110237 	flowop_t *flowop;
7735184Sek110237 
7745184Sek110237 #ifdef HAVE_LIBKSTAT
7755184Sek110237 	stats_cputime = kstats_read_cpu();
7765184Sek110237 #else
7775184Sek110237 	stats_cputime = 0;
7785184Sek110237 #endif /* HAVE_LIBKSTAT */
7795184Sek110237 
7805184Sek110237 	if (globalstats == NULL)
7815184Sek110237 		globalstats = malloc(FLOW_TYPES * sizeof (flowstat_t));
7825184Sek110237 
7835184Sek110237 	(void) memset(globalstats, 0, FLOW_TYPES * sizeof (flowstat_t));
7845184Sek110237 
7855184Sek110237 	flowop = filebench_shm->flowoplist;
7865184Sek110237 
7875184Sek110237 	while (flowop) {
7885184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "Clearing stats for %s-%d",
7895184Sek110237 		    flowop->fo_name,
7905184Sek110237 		    flowop->fo_instance);
7915184Sek110237 		(void) memset(&flowop->fo_stats, 0, sizeof (flowstat_t));
7925184Sek110237 		flowop = flowop->fo_next;
7935184Sek110237 	}
7945184Sek110237 
7955184Sek110237 	(void) memset(globalstats, 0, sizeof (flowstat_t));
7965184Sek110237 	globalstats->fs_stime = gethrtime();
7975184Sek110237 }
798