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 <stdio.h>
     27 #include <fcntl.h>
     28 #include <limits.h>
     29 #include <time.h>
     30 #include <libgen.h>
     31 #include <unistd.h>
     32 #include <strings.h>
     33 #include "filebench.h"
     34 #include "ipc.h"
     35 #include "eventgen.h"
     36 #include "utils.h"
     37 #include "fsplug.h"
     38 
     39 /* File System functions vector */
     40 fsplug_func_t *fs_functions_vec;
     41 
     42 /*
     43  * Routines to access high resolution system time, initialize and
     44  * shutdown filebench, log filebench run progress and errors, and
     45  * access system information strings.
     46  */
     47 
     48 #if !defined(sun) && defined(USE_RDTSC)
     49 /*
     50  * Lets us use the rdtsc instruction to get highres time.
     51  * Thanks to libmicro
     52  */
     53 uint64_t	cpu_hz = 0;
     54 
     55 /*
     56  * Uses the rdtsc instruction to get high resolution (cpu
     57  * clock ticks) time. Only used for non Sun compiles.
     58  */
     59 __inline__ long long
     60 rdtsc(void)
     61 {
     62 	unsigned long long x;
     63 	__asm__ volatile(".byte 0x0f, 0x31" : "=A" (x));
     64 	return (x);
     65 }
     66 
     67 /*
     68  * Get high resolution time in nanoseconds. This is the version
     69  * used when not compiled for Sun systems. It uses rdtsc call to
     70  * get clock ticks and converts to nanoseconds
     71  */
     72 uint64_t
     73 gethrtime(void)
     74 {
     75 	uint64_t hrt;
     76 
     77 	/* convert to nanosecs and return */
     78 	hrt = 1000000000UL * rdtsc() / cpu_hz;
     79 	return (hrt);
     80 }
     81 
     82 /*
     83  * Gets CPU clock frequency in MHz from cpuinfo file.
     84  * Converts to cpu_hz and stores in cpu_hz global uint64_t.
     85  * Only used for non Sun compiles.
     86  */
     87 static uint64_t
     88 parse_cpu_hz(void)
     89 {
     90 	/*
     91 	 * Parse the following from /proc/cpuinfo.
     92 	 * cpu MHz		: 2191.563
     93 	 */
     94 	FILE *cpuinfo;
     95 	double hertz = -1;
     96 	uint64_t hz;
     97 
     98 	if ((cpuinfo = fopen("/proc/cpuinfo", "r")) == NULL) {
     99 		filebench_log(LOG_ERROR, "open /proc/cpuinfo failed: %s",
    100 		    strerror(errno));
    101 		filebench_shutdown(1);
    102 	}
    103 	while (!feof(cpuinfo)) {
    104 		char buffer[80];
    105 
    106 		fgets(buffer, 80, cpuinfo);
    107 		if (strlen(buffer) == 0) continue;
    108 		if (strncasecmp(buffer, "cpu MHz", 7) == 0) {
    109 			char *token = strtok(buffer, ":");
    110 
    111 			if (token != NULL) {
    112 				token = strtok((char *)NULL, ":");
    113 				hertz = strtod(token, NULL);
    114 			}
    115 			break;
    116 		}
    117 	}
    118 	hz = hertz * 1000000;
    119 
    120 	return (hz);
    121 }
    122 
    123 #elif !defined(sun)
    124 
    125 /*
    126  * Get high resolution time in nanoseconds. This is the version
    127  * used if compiled for Sun systems. It calls gettimeofday
    128  * to get current time and converts it to nanoseconds.
    129  */
    130 uint64_t
    131 gethrtime(void)
    132 {
    133 	struct timeval tv;
    134 	uint64_t hrt;
    135 
    136 	gettimeofday(&tv, NULL);
    137 
    138 	hrt = (uint64_t)tv.tv_sec * 1000000000UL +
    139 	    (uint64_t)tv.tv_usec * 1000UL;
    140 	return (hrt);
    141 }
    142 #endif
    143 
    144 /*
    145  * Main filebench initialization. Opens the random number
    146  * "device" file or shuts down the run if one is not found.
    147  * Sets the cpu clock frequency variable or shuts down the
    148  * run if one is not found.
    149  */
    150 void
    151 filebench_init(void)
    152 {
    153 	fb_random_init();
    154 
    155 #if defined(USE_RDTSC) && (LINUX_PORT)
    156 	cpu_hz = parse_cpu_hz();
    157 	if (cpu_hz <= 0) {
    158 		filebench_log(LOG_ERROR, "Error getting CPU Mhz: %s",
    159 		    strerror(errno));
    160 		filebench_shutdown(1);
    161 	}
    162 #endif /* USE_RDTSC */
    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 %d", error);
    339 		(void) ipc_mutex_lock(&filebench_shm->shm_procflow_lock);
    340 		if (filebench_shm->shm_f_abort == FILEBENCH_ABORT_FINI) {
    341 			(void) ipc_mutex_unlock(
    342 			    &filebench_shm->shm_procflow_lock);
    343 			return;
    344 		}
    345 		filebench_shm->shm_f_abort = FILEBENCH_ABORT_ERROR;
    346 		(void) ipc_mutex_unlock(&filebench_shm->shm_procflow_lock);
    347 	} else {
    348 		filebench_log(LOG_DEBUG_IMPL, "Shutdown");
    349 	}
    350 
    351 	procflow_shutdown();
    352 
    353 	(void) unlink("/tmp/filebench_shm");
    354 	ipc_ismdelete();
    355 	exit(error);
    356 }
    357 
    358 /*
    359  * Put the hostname in ${hostname}. The system supplied
    360  * host name string is copied into an allocated string and
    361  * the pointer to the string is placed in the supplied
    362  * variable "var". If var->var_val.string already points to
    363  * a string, the string is freed. The routine always
    364  * returns zero (0).
    365  */
    366 var_t *
    367 host_var(var_t *var)
    368 {
    369 	char hoststr[128];
    370 	char *strptr;
    371 
    372 	(void) gethostname(hoststr, 128);
    373 	if (VAR_HAS_STRING(var) && var->var_val.string)
    374 		free(var->var_val.string);
    375 
    376 	if ((strptr = fb_stralloc(hoststr)) == NULL) {
    377 		filebench_log(LOG_ERROR,
    378 		    "unable to allocate string for host name");
    379 		return (NULL);
    380 	}
    381 
    382 	VAR_SET_STR(var, strptr);
    383 	return (0);
    384 }
    385 
    386 /*
    387  * Put the date string in ${date}. The system supplied date is
    388  * copied into an allocated string and the pointer to the string
    389  * is placed in the supplied var_t's var_val.string. If
    390  * var->var_val.string already points to a string, the string
    391  * is freed. The routine returns a pointer to the supplied var_t,
    392  * unless it is unable to allocate string for the date, in which
    393  * case it returns NULL.
    394  */
    395 var_t *
    396 date_var(var_t *var)
    397 {
    398 	char datestr[128];
    399 	char *strptr;
    400 #ifdef HAVE_CFTIME
    401 	time_t t = time(NULL);
    402 #else
    403 	struct tm t;
    404 #endif
    405 
    406 #ifdef HAVE_CFTIME
    407 	cftime(datestr, "%y%m%d%H" "%M", &t);
    408 #else
    409 	(void) strftime(datestr, sizeof (datestr), "%y%m%d%H %M", &t);
    410 #endif
    411 
    412 	if (VAR_HAS_STRING(var) && var->var_val.string)
    413 		free(var->var_val.string);
    414 
    415 	if ((strptr = fb_stralloc(datestr)) == NULL) {
    416 		filebench_log(LOG_ERROR,
    417 		    "unable to allocate string for date");
    418 		return (NULL);
    419 	}
    420 
    421 	VAR_SET_STR(var, strptr);
    422 
    423 	return (var);
    424 }
    425 
    426 extern char *fscriptname;
    427 
    428 /*
    429  * Put the script name in ${script}. The path name of the script
    430  * used with this filebench run trimmed of the trailing ".f" and
    431  * all leading subdirectories. The remaining script name is
    432  * copied into the var_val.string field of the supplied variable
    433  * "var". The routine returns a pointer to the supplied var_t,
    434  * unless it is unable to allocate string space, in which case it
    435  * returns NULL.
    436  */
    437 var_t *
    438 script_var(var_t *var)
    439 {
    440 	char *scriptstr;
    441 	char *f = fb_stralloc(fscriptname);
    442 	char *strptr;
    443 
    444 	/* Trim the .f suffix */
    445 	for (scriptstr = f + strlen(f) - 1; scriptstr != f; scriptstr--) {
    446 		if (*scriptstr == '.') {
    447 			*scriptstr = 0;
    448 			break;
    449 		}
    450 	}
    451 
    452 	if ((strptr = fb_stralloc(basename(f))) == NULL) {
    453 		filebench_log(LOG_ERROR,
    454 		    "unable to allocate string for script name");
    455 		free(f);
    456 		return (NULL);
    457 	}
    458 
    459 	VAR_SET_STR(var, strptr);
    460 	free(f);
    461 
    462 	return (var);
    463 }
    464 
    465 void fb_lfs_funcvecinit(void);
    466 
    467 /*
    468  * Initialize any "plug-in" I/O function vectors. Called by each
    469  * filebench process that is forked, as the vector is relative to
    470  * its image.
    471  */
    472 void
    473 filebench_plugin_funcvecinit(void)
    474 {
    475 
    476 	switch (filebench_shm->shm_filesys_type) {
    477 	case LOCAL_FS_PLUG:
    478 		fb_lfs_funcvecinit();
    479 		break;
    480 
    481 	case NFS3_PLUG:
    482 	case NFS4_PLUG:
    483 	case CIFS_PLUG:
    484 		break;
    485 	default:
    486 		filebench_log(LOG_ERROR,
    487 		    "filebench_plugin_funcvecinit: unknown file system");
    488 		break;
    489 	}
    490 }
    491