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 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 #include <stdio.h>
     29 #include <fcntl.h>
     30 #include <limits.h>
     31 #include <time.h>
     32 #include <libgen.h>
     33 #include <unistd.h>
     34 #include <strings.h>
     35 #include "filebench.h"
     36 #include "ipc.h"
     37 #include "eventgen.h"
     38 #include "utils.h"
     39 
     40 /*
     41  * Routines to access high resolution system time, initialize and
     42  * shutdown filebench, log filebench run progress and errors, and
     43  * access system information strings.
     44  */
     45 
     46 
     47 #if !defined(sun) && defined(USE_RDTSC)
     48 /*
     49  * Lets us use the rdtsc instruction to get highres time.
     50  * Thanks to libmicro
     51  */
     52 uint64_t	cpu_hz = 0;
     53 
     54 /*
     55  * Uses the rdtsc instruction to get high resolution (cpu
     56  * clock ticks) time. Only used for non Sun compiles.
     57  */
     58 __inline__ long long
     59 rdtsc(void)
     60 {
     61 	unsigned long long x;
     62 	__asm__ volatile(".byte 0x0f, 0x31" : "=A" (x));
     63 	return (x);
     64 }
     65 
     66 /*
     67  * Get high resolution time in nanoseconds. This is the version
     68  * used when not compiled for Sun systems. It uses rdtsc call to
     69  * get clock ticks and converts to nanoseconds
     70  */
     71 uint64_t
     72 gethrtime(void)
     73 {
     74 	uint64_t hrt;
     75 
     76 	/* convert to nanosecs and return */
     77 	hrt = 1000000000UL * rdtsc() / cpu_hz;
     78 	return (hrt);
     79 }
     80 
     81 /*
     82  * Gets CPU clock frequency in MHz from cpuinfo file.
     83  * Converts to cpu_hz and stores in cpu_hz global uint64_t.
     84  * Only used for non Sun compiles.
     85  */
     86 static uint64_t
     87 parse_cpu_hz(void)
     88 {
     89 	/*
     90 	 * Parse the following from /proc/cpuinfo.
     91 	 * cpu MHz		: 2191.563
     92 	 */
     93 	FILE *cpuinfo;
     94 	double hertz = -1;
     95 	uint64_t hz;
     96 
     97 	if ((cpuinfo = fopen("/proc/cpuinfo", "r")) == NULL) {
     98 		filebench_log(LOG_ERROR, "open /proc/cpuinfo failed: %s",
     99 		    strerror(errno));
    100 		filebench_shutdown(1);
    101 	}
    102 	while (!feof(cpuinfo)) {
    103 		char buffer[80];
    104 
    105 		fgets(buffer, 80, cpuinfo);
    106 		if (strlen(buffer) == 0) continue;
    107 		if (strncasecmp(buffer, "cpu MHz", 7) == 0) {
    108 			char *token = strtok(buffer, ":");
    109 
    110 			if (token != NULL) {
    111 				token = strtok((char *)NULL, ":");
    112 				hertz = strtod(token, NULL);
    113 			}
    114 			break;
    115 		}
    116 	}
    117 	hz = hertz * 1000000;
    118 
    119 	return (hz);
    120 }
    121 
    122 #elif !defined(sun)
    123 
    124 /*
    125  * Get high resolution time in nanoseconds. This is the version
    126  * used if compiled for Sun systems. It calls gettimeofday
    127  * to get current time and converts it to nanoseconds.
    128  */
    129 uint64_t
    130 gethrtime(void)
    131 {
    132 	struct timeval tv;
    133 	uint64_t hrt;
    134 
    135 	gettimeofday(&tv, NULL);
    136 
    137 	hrt = (uint64_t)tv.tv_sec * 1000000000UL +
    138 	    (uint64_t)tv.tv_usec * 1000UL;
    139 	return (hrt);
    140 }
    141 #endif
    142 
    143 /*
    144  * Main filebench initialization. Opens the random number
    145  * "device" file or shuts down the run if one is not found.
    146  * Sets the cpu clock frequency variable or shuts down the
    147  * run if one is not found.
    148  */
    149 void
    150 filebench_init(void)
    151 {
    152 	fb_random_init();
    153 
    154 #if defined(USE_RDTSC) && (LINUX_PORT)
    155 	cpu_hz = parse_cpu_hz();
    156 	if (cpu_hz <= 0) {
    157 		filebench_log(LOG_ERROR, "Error getting CPU Mhz: %s",
    158 		    strerror(errno));
    159 		filebench_shutdown(1);
    160 	}
    161 #endif /* USE_RDTSC */
    162 
    163 }
    164 
    165 extern int lex_lineno;
    166 
    167 /*
    168  * Writes a message consisting of information formated by
    169  * "fmt" to the log file, dump file or stdout.  The supplied
    170  * "level" argument determines which file to write to and
    171  * what other actions to take. The level LOG_LOG writes to
    172  * the "log" file, and will open the file on the first
    173  * invocation. The level LOG_DUMP writes to the "dump" file,
    174  * and will open it on the first invocation. Other levels
    175  * print to the stdout device, with the amount of information
    176  * dependent on the error level and the current error level
    177  * setting in filebench_shm->shm_debug_level.
    178  */
    179 void filebench_log
    180 __V((int level, const char *fmt, ...))
    181 {
    182 	va_list args;
    183 	hrtime_t now;
    184 	char line[131072];
    185 	char buf[131072];
    186 
    187 	if (level == LOG_FATAL)
    188 		goto fatal;
    189 
    190 	/* open logfile if not already open and writing to it */
    191 	if ((level == LOG_LOG) &&
    192 	    (filebench_shm->shm_log_fd < 0)) {
    193 		char path[MAXPATHLEN];
    194 		char *s;
    195 
    196 		(void) strcpy(path, filebench_shm->shm_fscriptname);
    197 		if ((s = strstr(path, ".f")))
    198 			*s = 0;
    199 		else
    200 			(void) strcpy(path, "filebench");
    201 
    202 		(void) strcat(path, ".csv");
    203 
    204 		filebench_shm->shm_log_fd =
    205 		    open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
    206 	}
    207 
    208 	/*
    209 	 * if logfile still not open, switch to LOG_ERROR level so
    210 	 * it gets reported to stdout
    211 	 */
    212 	if ((level == LOG_LOG) &&
    213 	    (filebench_shm->shm_log_fd < 0)) {
    214 		(void) snprintf(line, sizeof (line),  "Open logfile failed: %s",
    215 		    strerror(errno));
    216 		level = LOG_ERROR;
    217 	}
    218 
    219 	/* open dumpfile if not already open and writing to it */
    220 	if ((level == LOG_DUMP) &&
    221 	    (*filebench_shm->shm_dump_filename == 0))
    222 		return;
    223 
    224 	if ((level == LOG_DUMP) &&
    225 	    (filebench_shm->shm_dump_fd < 0)) {
    226 
    227 		filebench_shm->shm_dump_fd =
    228 		    open(filebench_shm->shm_dump_filename,
    229 		    O_RDWR | O_CREAT | O_TRUNC, 0666);
    230 	}
    231 
    232 	if ((level == LOG_DUMP) &&
    233 	    (filebench_shm->shm_dump_fd < 0)) {
    234 		(void) snprintf(line, sizeof (line), "Open logfile failed: %s",
    235 		    strerror(errno));
    236 		level = LOG_ERROR;
    237 	}
    238 
    239 	/* Quit if this is a LOG_ERROR messages and they are disabled */
    240 	if ((filebench_shm->shm_1st_err) && (level == LOG_ERROR))
    241 		return;
    242 
    243 	if (level == LOG_ERROR1) {
    244 		if (filebench_shm->shm_1st_err)
    245 			return;
    246 
    247 		/* A LOG_ERROR1 temporarily disables LOG_ERROR messages */
    248 		filebench_shm->shm_1st_err = 1;
    249 		level = LOG_ERROR;
    250 	}
    251 
    252 	/* Only log greater than debug setting */
    253 	if ((level != LOG_DUMP) && (level != LOG_LOG) &&
    254 	    (level > filebench_shm->shm_debug_level))
    255 		return;
    256 
    257 	now = gethrtime();
    258 
    259 fatal:
    260 
    261 #ifdef __STDC__
    262 	va_start(args, fmt);
    263 #else
    264 	char *fmt;
    265 	va_start(args);
    266 	fmt = va_arg(args, char *);
    267 #endif
    268 
    269 	(void) vsprintf(line, fmt, args);
    270 
    271 	va_end(args);
    272 
    273 	if (level == LOG_FATAL) {
    274 		(void) fprintf(stderr, "%s\n", line);
    275 		return;
    276 	}
    277 
    278 	/* Serialize messages to log */
    279 	(void) ipc_mutex_lock(&filebench_shm->shm_msg_lock);
    280 
    281 	if (level == LOG_LOG) {
    282 		if (filebench_shm->shm_log_fd > 0) {
    283 			(void) snprintf(buf, sizeof (buf), "%s\n", line);
    284 			(void) write(filebench_shm->shm_log_fd, buf,
    285 			    strlen(buf));
    286 			(void) fsync(filebench_shm->shm_log_fd);
    287 			(void) ipc_mutex_unlock(&filebench_shm->shm_msg_lock);
    288 			return;
    289 		}
    290 
    291 	} else if (level == LOG_DUMP) {
    292 		if (filebench_shm->shm_dump_fd != -1) {
    293 			(void) snprintf(buf, sizeof (buf), "%s\n", line);
    294 			(void) write(filebench_shm->shm_dump_fd, buf,
    295 			    strlen(buf));
    296 			(void) fsync(filebench_shm->shm_dump_fd);
    297 			(void) ipc_mutex_unlock(&filebench_shm->shm_msg_lock);
    298 			return;
    299 		}
    300 
    301 	} else if (filebench_shm->shm_debug_level > LOG_INFO) {
    302 		if (level < LOG_INFO)
    303 			(void) fprintf(stderr, "%5d: ", (int)my_pid);
    304 		else
    305 			(void) fprintf(stdout, "%5d: ", (int)my_pid);
    306 	}
    307 
    308 	if (level < LOG_INFO) {
    309 		(void) fprintf(stderr, "%4.3f: %s",
    310 		    (now - filebench_shm->shm_epoch) / FSECS,
    311 		    line);
    312 
    313 		if (my_procflow == NULL)
    314 			(void) fprintf(stderr, " on line %d", lex_lineno);
    315 
    316 		(void) fprintf(stderr, "\n");
    317 		(void) fflush(stderr);
    318 	} else {
    319 		(void) fprintf(stdout, "%4.3f: %s",
    320 		    (now - filebench_shm->shm_epoch) / FSECS,
    321 		    line);
    322 		(void) fprintf(stdout, "\n");
    323 		(void) fflush(stdout);
    324 	}
    325 
    326 	(void) ipc_mutex_unlock(&filebench_shm->shm_msg_lock);
    327 }
    328 
    329 /*
    330  * Stops the run and exits filebench. If filebench is
    331  * currently running a workload, calls procflow_shutdown()
    332  * to stop the run. Also closes and deletes shared memory.
    333  */
    334 void
    335 filebench_shutdown(int error) {
    336 
    337 	if (error) {
    338 		filebench_log(LOG_DEBUG_IMPL, "Shutdown on error");
    339 		filebench_shm->shm_f_abort = FILEBENCH_ABORT_ERROR;
    340 	} else {
    341 		filebench_log(LOG_DEBUG_IMPL, "Shutdown");
    342 	}
    343 
    344 	procflow_shutdown();
    345 
    346 	(void) unlink("/tmp/filebench_shm");
    347 	ipc_ismdelete();
    348 	exit(error);
    349 }
    350 
    351 /*
    352  * Put the hostname in ${hostname}. The system supplied
    353  * host name string is copied into an allocated string and
    354  * the pointer to the string is placed in the supplied
    355  * variable "var". If var->var_val.string already points to
    356  * a string, the string is freed. The routine always
    357  * returns zero (0).
    358  */
    359 var_t *
    360 host_var(var_t *var)
    361 {
    362 	char hoststr[128];
    363 	char *strptr;
    364 
    365 	(void) gethostname(hoststr, 128);
    366 	if (VAR_HAS_STRING(var) && var->var_val.string)
    367 		free(var->var_val.string);
    368 
    369 	if ((strptr = fb_stralloc(hoststr)) == NULL) {
    370 		filebench_log(LOG_ERROR,
    371 		    "unable to allocate string for host name");
    372 		return (NULL);
    373 	}
    374 
    375 	VAR_SET_STR(var, strptr);
    376 	return (0);
    377 }
    378 
    379 /*
    380  * Put the date string in ${date}. The system supplied date is
    381  * copied into an allocated string and the pointer to the string
    382  * is placed in the supplied var_t's var_val.string. If
    383  * var->var_val.string already points to a string, the string
    384  * is freed. The routine returns a pointer to the supplied var_t,
    385  * unless it is unable to allocate string for the date, in which
    386  * case it returns NULL.
    387  */
    388 var_t *
    389 date_var(var_t *var)
    390 {
    391 	char datestr[128];
    392 	char *strptr;
    393 #ifdef HAVE_CFTIME
    394 	time_t t = time(NULL);
    395 #else
    396 	struct tm t;
    397 #endif
    398 
    399 #ifdef HAVE_CFTIME
    400 	cftime(datestr, "%y%m%d%H" "%M", &t);
    401 #else
    402 	(void) strftime(datestr, sizeof (datestr), "%y%m%d%H %M", &t);
    403 #endif
    404 
    405 	if (VAR_HAS_STRING(var) && var->var_val.string)
    406 		free(var->var_val.string);
    407 
    408 	if ((strptr = fb_stralloc(datestr)) == NULL) {
    409 		filebench_log(LOG_ERROR,
    410 		    "unable to allocate string for date");
    411 		return (NULL);
    412 	}
    413 
    414 	VAR_SET_STR(var, strptr);
    415 
    416 	return (var);
    417 }
    418 
    419 extern char *fscriptname;
    420 
    421 /*
    422  * Put the script name in ${script}. The path name of the script
    423  * used with this filebench run trimmed of the trailing ".f" and
    424  * all leading subdirectories. The remaining script name is
    425  * copied into the var_val.string field of the supplied variable
    426  * "var". The routine returns a pointer to the supplied var_t,
    427  * unless it is unable to allocate string space, in which case it
    428  * returns NULL.
    429  */
    430 var_t *
    431 script_var(var_t *var)
    432 {
    433 	char *scriptstr;
    434 	char *f = fb_stralloc(fscriptname);
    435 	char *strptr;
    436 
    437 	/* Trim the .f suffix */
    438 	for (scriptstr = f + strlen(f) - 1; scriptstr != f; scriptstr--) {
    439 		if (*scriptstr == '.') {
    440 			*scriptstr = 0;
    441 			break;
    442 		}
    443 	}
    444 
    445 	if ((strptr = fb_stralloc(basename(f))) == NULL) {
    446 		filebench_log(LOG_ERROR,
    447 		    "unable to allocate string for script name");
    448 		free(f);
    449 		return (NULL);
    450 	}
    451 
    452 	VAR_SET_STR(var, strptr);
    453 	free(f);
    454 
    455 	return (var);
    456 }
    457