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