summaryrefslogtreecommitdiff
path: root/usr/src/cmd/filebench/common/misc.c
diff options
context:
space:
mode:
authorek110237 <none@none>2007-10-04 16:13:05 -0700
committerek110237 <none@none>2007-10-04 16:13:05 -0700
commitf2fc321be9b4df7748e8c31a5edd154b0177b139 (patch)
tree2307e47f27e8d0a9c4a0576e5a94d9b4840619b9 /usr/src/cmd/filebench/common/misc.c
parent7a2b8adf6aeff0652f1bd855449b3a4eaffc24a6 (diff)
downloadillumos-joyent-f2fc321be9b4df7748e8c31a5edd154b0177b139.tar.gz
PSARC 2007/448 Filebench
6581098 FileBench should be included in Solaris
Diffstat (limited to 'usr/src/cmd/filebench/common/misc.c')
-rw-r--r--usr/src/cmd/filebench/common/misc.c495
1 files changed, 495 insertions, 0 deletions
diff --git a/usr/src/cmd/filebench/common/misc.c b/usr/src/cmd/filebench/common/misc.c
new file mode 100644
index 0000000000..2d4281fe22
--- /dev/null
+++ b/usr/src/cmd/filebench/common/misc.c
@@ -0,0 +1,495 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <time.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <strings.h>
+#include "filebench.h"
+#include "ipc.h"
+#include "eventgen.h"
+#include "utils.h"
+
+/*
+ * Routines to access high resolution system time, initialize and
+ * shutdown filebench, obtain system generated random numbers from
+ * "urandom", log filebench run progress and errors, and access system
+ * information strings.
+ */
+
+
+#if !defined(sun) && defined(USE_RDTSC)
+/*
+ * Lets us use the rdtsc instruction to get highres time.
+ * Thanks to libmicro
+ */
+uint64_t cpu_hz = 0;
+
+/*
+ * Uses the rdtsc instruction to get high resolution (cpu
+ * clock ticks) time. Only used for non Sun compiles.
+ */
+__inline__ long long
+rdtsc(void)
+{
+ unsigned long long x;
+ __asm__ volatile(".byte 0x0f, 0x31" : "=A" (x));
+ return (x);
+}
+
+/*
+ * Get high resolution time in nanoseconds. This is the version
+ * used when not compiled for Sun systems. It uses rdtsc call to
+ * get clock ticks and converts to nanoseconds
+ */
+uint64_t
+gethrtime(void)
+{
+ uint64_t hrt;
+
+ /* convert to nanosecs and return */
+ hrt = 1000000000UL * rdtsc() / cpu_hz;
+ return (hrt);
+}
+
+/*
+ * Gets CPU clock frequency in MHz from cpuinfo file.
+ * Converts to cpu_hz and stores in cpu_hz global uint64_t.
+ * Only used for non Sun compiles.
+ */
+static uint64_t
+parse_cpu_hz(void)
+{
+ /*
+ * Parse the following from /proc/cpuinfo.
+ * cpu MHz : 2191.563
+ */
+ FILE *cpuinfo;
+ double hertz = -1;
+ uint64_t hz;
+
+ if ((cpuinfo = fopen("/proc/cpuinfo", "r")) == NULL) {
+ filebench_log(LOG_ERROR, "open /proc/cpuinfo failed: %s",
+ strerror(errno));
+ filebench_shutdown(1);
+ }
+ while (!feof(cpuinfo)) {
+ char buffer[80];
+
+ fgets(buffer, 80, cpuinfo);
+ if (strlen(buffer) == 0) continue;
+ if (strncasecmp(buffer, "cpu MHz", 7) == 0) {
+ char *token = strtok(buffer, ":");
+
+ if (token != NULL) {
+ token = strtok((char *)NULL, ":");
+ hertz = strtod(token, NULL);
+ }
+ break;
+ }
+ }
+ printf("CPU Mhz %9.6f, sysconf:%ld\n", hertz, sysconf(_SC_CLK_TCK));
+ hz = hertz * 1000000;
+
+ return (hz);
+}
+
+#elif !defined(sun)
+
+/*
+ * Get high resolution time in nanoseconds. This is the version
+ * used if compiled for Sun systems. It calls gettimeofday
+ * to get current time and converts it to nanoseconds.
+ */
+uint64_t
+gethrtime(void)
+{
+ struct timeval tv;
+ uint64_t hrt;
+
+ gettimeofday(&tv, NULL);
+
+ hrt = (uint64_t)tv.tv_sec * 1000000000UL +
+ (uint64_t)tv.tv_usec * 1000UL;
+ return (hrt);
+}
+#endif
+
+static int urandomfd;
+
+/*
+ * Main filebench initialization. Opens the random number
+ * "device" file or shuts down the run if one is not found.
+ * Sets the cpu clock frequency variable or shuts down the
+ * run if one is not found.
+ */
+void
+filebench_init(void)
+{
+ /* open the "urandom" random number device file */
+ if ((urandomfd = open("/dev/urandom", O_RDONLY)) < 0) {
+ filebench_log(LOG_ERROR, "open /dev/urandom failed: %s",
+ strerror(errno));
+ filebench_shutdown(1);
+ }
+#if defined(USE_RDTSC) && (LINUX_PORT)
+ cpu_hz = parse_cpu_hz();
+ if (cpu_hz <= 0) {
+ filebench_log(LOG_ERROR, "Error getting CPU Mhz: %s",
+ strerror(errno));
+ filebench_shutdown(1);
+ }
+#endif /* USE_RDTSC */
+
+}
+
+/*
+ * Reads a 64 bit random number from the urandom "file".
+ * Shuts down the run if the read fails. Otherwise returns
+ * the random number after rounding it off by "round".
+ * Returns 0 on success, -1 on failure.
+ */
+int
+filebench_randomno64(uint64_t *randp, uint64_t max, uint64_t round)
+{
+ uint64_t random;
+
+ /* check for round value too large */
+ if (max <= round) {
+ *randp = 0;
+
+ /* if it just fits, its ok, otherwise error */
+ if (max == round)
+ return (0);
+ else
+ return (-1);
+ }
+
+ if (read(urandomfd, &random,
+ sizeof (uint64_t)) != sizeof (uint64_t)) {
+ filebench_log(LOG_ERROR, "read /dev/urandom failed: %s",
+ strerror(errno));
+ filebench_shutdown(1);
+ }
+
+ /* clip with max and optionally round */
+ max -= round;
+ random = random / (FILEBENCH_RANDMAX64 / max);
+ if (round) {
+ random = random / round;
+ random *= round;
+ }
+ if (random > max)
+ random = max;
+
+ *randp = random;
+ return (0);
+}
+
+
+/*
+ * Reads a 32 bit random number from the urandom "file".
+ * Shuts down the run if the read fails. Otherwise returns
+ * the random number after rounding it off by "round".
+ * Returns 0 on success, -1 on failure.
+ */
+int
+filebench_randomno32(uint32_t *randp, uint32_t max, uint32_t round)
+{
+ uint32_t random;
+
+ /* check for round value too large */
+ if (max <= round) {
+ *randp = 0;
+
+ /* if it just fits, its ok, otherwise error */
+ if (max == round)
+ return (0);
+ else
+ return (-1);
+ }
+
+ if (read(urandomfd, &random,
+ sizeof (uint32_t)) != sizeof (uint32_t)) {
+ filebench_log(LOG_ERROR, "read /dev/urandom failed: %s",
+ strerror(errno));
+ filebench_shutdown(1);
+ }
+
+ /* clip with max and optionally round */
+ max -= round;
+ random = random / (FILEBENCH_RANDMAX32 / max);
+ if (round) {
+ random = random / round;
+ random *= round;
+ }
+ if (random > max)
+ random = max;
+
+ *randp = random;
+ return (0);
+}
+
+extern int lex_lineno;
+
+/*
+ * Writes a message consisting of information formated by
+ * "fmt" to the log file, dump file or stdout. The supplied
+ * "level" argument determines which file to write to and
+ * what other actions to take. The level LOG_LOG writes to
+ * the "log" file, and will open the file on the first
+ * invocation. The level LOG_DUMP writes to the "dump" file,
+ * and will open it on the first invocation. Other levels
+ * print to the stdout device, with the amount of information
+ * dependent on the error level and the current error level
+ * setting in filebench_shm->debug_level.
+ */
+void filebench_log
+__V((int level, const char *fmt, ...))
+{
+ va_list args;
+ hrtime_t now;
+ char line[131072];
+ char buf[131072];
+
+ if (level == LOG_FATAL)
+ goto fatal;
+
+ /* open logfile if not already open and writing to it */
+ if ((level == LOG_LOG) &&
+ (filebench_shm->log_fd < 0)) {
+ char path[MAXPATHLEN];
+ char *s;
+
+ (void) strcpy(path, filebench_shm->fscriptname);
+ if ((s = strstr(path, ".f")))
+ *s = 0;
+ else
+ (void) strcpy(path, "filebench");
+
+ (void) strcat(path, ".csv");
+
+ filebench_shm->log_fd =
+ open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
+ }
+
+ /*
+ * if logfile still not open, switch to LOG_ERROR level so
+ * it gets reported to stdout
+ */
+ if ((level == LOG_LOG) &&
+ (filebench_shm->log_fd < 0)) {
+ (void) snprintf(line, sizeof (line), "Open logfile failed: %s",
+ strerror(errno));
+ level = LOG_ERROR;
+ }
+
+ /* open dumpfile if not already open and writing to it */
+ if ((level == LOG_DUMP) &&
+ (*filebench_shm->dump_filename == 0))
+ return;
+
+ if ((level == LOG_DUMP) &&
+ (filebench_shm->dump_fd < 0)) {
+
+ filebench_shm->dump_fd =
+ open(filebench_shm->dump_filename,
+ O_RDWR | O_CREAT | O_TRUNC, 0666);
+ }
+
+ if ((level == LOG_DUMP) &&
+ (filebench_shm->dump_fd < 0)) {
+ (void) snprintf(line, sizeof (line), "Open logfile failed: %s",
+ strerror(errno));
+ level = LOG_ERROR;
+ }
+
+ /* Only log greater than debug setting */
+ if ((level != LOG_DUMP) && (level != LOG_LOG) &&
+ (level > filebench_shm->debug_level))
+ return;
+
+ now = gethrtime();
+
+fatal:
+
+#ifdef __STDC__
+ va_start(args, fmt);
+#else
+ char *fmt;
+ va_start(args);
+ fmt = va_arg(args, char *);
+#endif
+
+ (void) vsprintf(line, fmt, args);
+
+ va_end(args);
+
+ if (level == LOG_FATAL) {
+ (void) fprintf(stdout, "%s\n", line);
+ return;
+ }
+
+ /* Serialize messages to log */
+ (void) ipc_mutex_lock(&filebench_shm->msg_lock);
+
+ if (level == LOG_LOG) {
+ if (filebench_shm->log_fd > 0) {
+ (void) snprintf(buf, sizeof (buf), "%s\n", line);
+ (void) write(filebench_shm->log_fd, buf, strlen(buf));
+ (void) fsync(filebench_shm->log_fd);
+ }
+
+ } else if (level == LOG_DUMP) {
+ if (filebench_shm->dump_fd != -1) {
+ (void) snprintf(buf, sizeof (buf), "%s\n", line);
+ (void) write(filebench_shm->dump_fd, buf, strlen(buf));
+ (void) fsync(filebench_shm->dump_fd);
+ }
+
+ } else if (filebench_shm->debug_level > LOG_INFO) {
+ (void) fprintf(stdout, "%5ld: %4.3f: %s",
+ pid, (now - filebench_shm->epoch) / FSECS,
+ line);
+
+ } else {
+ (void) fprintf(stdout, "%4.3f: %s",
+ (now - filebench_shm->epoch) / FSECS,
+ line);
+ }
+
+ if (level == LOG_ERROR) {
+ (void) fprintf(stdout, " on line %d", lex_lineno);
+ }
+
+ if ((level != LOG_LOG) && (level != LOG_DUMP)) {
+ (void) fprintf(stdout, "\n");
+ (void) fflush(stdout);
+ }
+
+ (void) ipc_mutex_unlock(&filebench_shm->msg_lock);
+}
+
+/*
+ * Stops the run and exits filebench. If filebench is
+ * currently running a workload, calls procflow_shutdown()
+ * to stop the run. Also closes and deletes shared memory.
+ */
+void
+filebench_shutdown(int error) {
+ filebench_log(LOG_DEBUG_IMPL, "Shutdown");
+ (void) unlink("/tmp/filebench_shm");
+ if (filebench_shm->allrunning)
+ procflow_shutdown();
+ filebench_shm->f_abort = 1;
+ ipc_ismdelete();
+ exit(error);
+}
+
+/*
+ * Put the hostname in ${hostname}. The system supplied
+ * host name string is copied into an allocated string and
+ * the pointer to the string is placed in the supplied
+ * variable "var". If var->var_string already points to
+ * a string, the string is freed. The routine always
+ * returns zero (0).
+ */
+var_t *
+host_var(var_t *var)
+{
+ char hoststr[128];
+
+ (void) gethostname(hoststr, 128);
+ if (var->var_string)
+ free(var->var_string);
+ var->var_string = fb_stralloc(hoststr);
+ return (0);
+}
+
+/*
+ * Put the date string in ${date}. The system supplied date is
+ * copied into an allocated string and the pointer to the string
+ * is placed in the supplied var_t's var_string. If
+ * var->var_string already points to a string, the string
+ * is freed. The routine always returns a pointer to the
+ * supplied var_t.
+ */
+var_t *
+date_var(var_t *var)
+{
+ char datestr[128];
+#ifdef HAVE_CFTIME
+ time_t t = time(NULL);
+#else
+ struct tm t;
+#endif
+
+#ifdef HAVE_CFTIME
+ cftime(datestr, "%y%m%d%H" "%M", &t);
+#else
+ (void) strftime(datestr, sizeof (datestr), "%y%m%d%H %M", &t);
+#endif
+
+ if (var->var_string)
+ free(var->var_string);
+ var->var_string = fb_stralloc(datestr);
+
+ return (var);
+}
+
+extern char *fscriptname;
+
+/*
+ * Put the script name in ${script}. The path name of the script
+ * used with this filebench run trimmed of the trailing ".f" and
+ * all leading subdirectories. The remaining script name is
+ * copied into the var_string field of the supplied variable
+ * "var". The routine always returns a pointer to the supplied
+ * var_t.
+ */
+var_t *
+script_var(var_t *var)
+{
+ char *scriptstr;
+ char *f = fb_stralloc(fscriptname);
+
+ /* Trim the .f suffix */
+ for (scriptstr = f + strlen(f) - 1; scriptstr != f; scriptstr--) {
+ if (*scriptstr == '.') {
+ *scriptstr = 0;
+ break;
+ }
+ }
+
+ var->var_string = fb_stralloc(basename(f));
+ free(f);
+
+ return (var);
+}