/* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Portions Copyright 2008 Denis Cheng */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include "filebench.h" #include "vars.h" #include "misc.h" #include "utils.h" #include "stats.h" #include "eventgen.h" #include "fb_random.h" static var_t *var_find_dynamic(char *name); static boolean_t var_get_bool(var_t *var); static fbint_t var_get_int(var_t *var); static double var_get_dbl(var_t *var); /* * The filebench variables system has attribute value descriptors (avd_t) * where an avd contains a boolean, integer, double, string, random * distribution object ptr, boolean ptr, integer ptr, double ptr, * string ptr, or variable ptr. The system also has the variables * themselves, (var_t), which are named, typed entities which can be * allocated, selected and changed using the "set" command and used in * attribute assignments. The variables contain either a boolean, an * integer, a double, a string or pointer to an associated random * distribution object. Both avd_t and var_t entities are allocated * from interprocess shared memory space. * * The attribute descriptors implement delayed binding to variable values, * which is necessary because the values of variables may be changed * between the time the workload file is loaded and it is actually run, * either by further "set" commands in the file or from the command line * interface. For random variables, they actually point to the random * distribution object, allowing FileBench to invoke the appropriate * random distribution function on each access to the attribute. However, * for static attributes, the value is just loaded in the descriptor * directly, avoiding the need to allocate a variable to hold the static * value. * * The routines in this module are used to allocate, locate, and * manipulate the attribute descriptors, and vars. Routines are * also included to convert between the component strings, doubles * and integers of vars, and said components of avd_t. */ /* * returns a pointer to a string indicating the type of data contained * in the supplied attribute variable descriptor. */ static char * avd_get_type_string(avd_t avd) { switch (avd->avd_type) { case AVD_INVALID: return ("uninitialized"); case AVD_VAL_BOOL: return ("boolean value"); case AVD_VARVAL_BOOL: return ("points to boolean in var_t"); case AVD_VAL_INT: return ("integer value"); case AVD_VARVAL_INT: return ("points to integer in var_t"); case AVD_VAL_STR: return ("string"); case AVD_VARVAL_STR: return ("points to string in var_t"); case AVD_VAL_DBL: return ("double float value"); case AVD_VARVAL_DBL: return ("points to double float in var_t"); case AVD_IND_VAR: return ("points to a var_t"); case AVD_IND_RANDVAR: return ("points to var_t's random distribution object"); default: return ("illegal avd type"); } } /* * returns a pointer to a string indicating the type of data contained * in the supplied variable. */ static char * var_get_type_string(var_t *ivp) { switch (ivp->var_type & VAR_TYPE_SET_MASK) { case VAR_TYPE_BOOL_SET: return ("boolean"); case VAR_TYPE_INT_SET: return ("integer"); case VAR_TYPE_STR_SET: return ("string"); case VAR_TYPE_DBL_SET: return ("double float"); case VAR_TYPE_RAND_SET: return ("random"); default: return ("empty"); } } /* * Returns the fbint_t pointed to by the supplied avd_t "avd". */ fbint_t avd_get_int(avd_t avd) { randdist_t *rndp; if (avd == NULL) return (0); switch (avd->avd_type) { case AVD_VAL_INT: return (avd->avd_val.intval); case AVD_VARVAL_INT: if (avd->avd_val.intptr) return (*(avd->avd_val.intptr)); else return (0); case AVD_IND_VAR: return (var_get_int(avd->avd_val.varptr)); case AVD_IND_RANDVAR: if ((rndp = avd->avd_val.randptr) == NULL) return (0); else return ((fbint_t)rndp->rnd_get(rndp)); default: filebench_log(LOG_ERROR, "Attempt to get integer from %s avd", avd_get_type_string(avd)); return (0); } } /* * Returns the floating point value of a variable pointed to by the * supplied avd_t "avd". Intended to get the actual (double) value * supplied by the random variable. */ double avd_get_dbl(avd_t avd) { randdist_t *rndp; if (avd == NULL) return (0.0); switch (avd->avd_type) { case AVD_VAL_INT: return ((double)avd->avd_val.intval); case AVD_VAL_DBL: return (avd->avd_val.dblval); case AVD_VARVAL_INT: if (avd->avd_val.intptr) return ((double)(*(avd->avd_val.intptr))); else return (0.0); case AVD_VARVAL_DBL: if (avd->avd_val.dblptr) return (*(avd->avd_val.dblptr)); else return (0.0); case AVD_IND_VAR: return (var_get_dbl(avd->avd_val.varptr)); case AVD_IND_RANDVAR: if ((rndp = avd->avd_val.randptr) == NULL) { return (0.0); } else return (rndp->rnd_get(rndp)); default: filebench_log(LOG_ERROR, "Attempt to get floating point from %s avd", avd_get_type_string(avd)); return (0.0); } } /* * Returns the boolean pointed to by the supplied avd_t "avd". */ boolean_t avd_get_bool(avd_t avd) { if (avd == NULL) return (0); switch (avd->avd_type) { case AVD_VAL_BOOL: return (avd->avd_val.boolval); case AVD_VARVAL_BOOL: if (avd->avd_val.boolptr) return (*(avd->avd_val.boolptr)); else return (FALSE); /* for backwards compatibility with old workloads */ case AVD_VAL_INT: if (avd->avd_val.intval != 0) return (TRUE); else return (FALSE); case AVD_VARVAL_INT: if (avd->avd_val.intptr) if (*(avd->avd_val.intptr) != 0) return (TRUE); return (FALSE); case AVD_IND_VAR: return (var_get_bool(avd->avd_val.varptr)); default: filebench_log(LOG_ERROR, "Attempt to get boolean from %s avd", avd_get_type_string(avd)); return (FALSE); } } /* * Returns the string pointed to by the supplied avd_t "avd". */ char * avd_get_str(avd_t avd) { var_t *ivp; if (avd == NULL) return (NULL); switch (avd->avd_type) { case AVD_VAL_STR: return (avd->avd_val.strval); case AVD_VARVAL_STR: if (avd->avd_val.strptr) return (*avd->avd_val.strptr); else return (NULL); case AVD_IND_VAR: ivp = avd->avd_val.varptr; if (ivp && VAR_HAS_STRING(ivp)) return (ivp->var_val.string); filebench_log(LOG_ERROR, "Attempt to get string from %s var $%s", var_get_type_string(ivp), ivp->var_name); return (NULL); default: filebench_log(LOG_ERROR, "Attempt to get string from %s avd", avd_get_type_string(avd)); return (NULL); } } /* * Allocates a avd_t from ipc memory space. * logs an error and returns NULL on failure. */ static avd_t avd_alloc_cmn(void) { avd_t rtn; if ((rtn = (avd_t)ipc_malloc(FILEBENCH_AVD)) == NULL) filebench_log(LOG_ERROR, "Avd alloc failed"); return (rtn); } /* * pre-loads the allocated avd_t with the boolean_t "bool". * Returns the avd_t on success, NULL on failure. */ avd_t avd_bool_alloc(boolean_t bool) { avd_t avd; if ((avd = avd_alloc_cmn()) == NULL) return (NULL); avd->avd_type = AVD_VAL_BOOL; avd->avd_val.boolval = bool; filebench_log(LOG_DEBUG_IMPL, "Alloc boolean %d", bool); return (avd); } /* * pre-loads the allocated avd_t with the fbint_t "integer". * Returns the avd_t on success, NULL on failure. */ avd_t avd_int_alloc(fbint_t integer) { avd_t avd; if ((avd = avd_alloc_cmn()) == NULL) return (NULL); avd->avd_type = AVD_VAL_INT; avd->avd_val.intval = integer; filebench_log(LOG_DEBUG_IMPL, "Alloc integer %llu", (u_longlong_t)integer); return (avd); } /* * Gets a avd_t and points it to the var that * it will eventually be filled from */ static avd_t avd_alloc_var_ptr(var_t *var) { avd_t avd; if (var == NULL) return (NULL); if ((avd = avd_alloc_cmn()) == NULL) return (NULL); switch (var->var_type & VAR_TYPE_SET_MASK) { case VAR_TYPE_BOOL_SET: avd->avd_type = AVD_VARVAL_BOOL; avd->avd_val.boolptr = (&var->var_val.boolean); break; case VAR_TYPE_INT_SET: avd->avd_type = AVD_VARVAL_INT; avd->avd_val.intptr = (&var->var_val.integer); break; case VAR_TYPE_STR_SET: avd->avd_type = AVD_VARVAL_STR; avd->avd_val.strptr = &(var->var_val.string); break; case VAR_TYPE_DBL_SET: avd->avd_type = AVD_VARVAL_DBL; avd->avd_val.dblptr = &(var->var_val.dbl_flt); break; case VAR_TYPE_RAND_SET: avd->avd_type = AVD_IND_RANDVAR; avd->avd_val.randptr = var->var_val.randptr; break; case VAR_TYPE_INDVAR_SET: avd->avd_type = AVD_IND_VAR; if ((var->var_type & VAR_INDVAR_MASK) == VAR_IND_ASSIGN) avd->avd_val.varptr = var->var_varptr1; else avd->avd_val.varptr = var; break; default: avd->avd_type = AVD_IND_VAR; avd->avd_val.varptr = var; break; } return (avd); } /* * Gets a avd_t, then allocates and initializes a piece of * shared string memory, putting the pointer to it into the just * allocated string pointer location. The routine returns a pointer * to the string pointer location or returns NULL on error. */ avd_t avd_str_alloc(char *string) { avd_t avd; if (string == NULL) { filebench_log(LOG_ERROR, "No string supplied\n"); return (NULL); } if ((avd = avd_alloc_cmn()) == NULL) return (NULL); avd->avd_type = AVD_VAL_STR; avd->avd_val.strval = ipc_stralloc(string); filebench_log(LOG_DEBUG_IMPL, "Alloc string %s ptr %zx", string, avd); return (avd); } /* * Allocates a var (var_t) from interprocess shared memory. * Places the allocated var on the end of the globally shared * shm_var_list. Finally, the routine allocates a string containing * a copy of the supplied "name" string. If any allocations * fails, returns NULL, otherwise it returns a pointer to the * newly allocated var. */ static var_t * var_alloc_cmn(char *name, int var_type) { var_t **var_listp; var_t *var = NULL; var_t *prev = NULL; var_t *newvar; if ((newvar = (var_t *)ipc_malloc(FILEBENCH_VARIABLE)) == NULL) { filebench_log(LOG_ERROR, "Out of memory for variables"); return (NULL); } (void) memset(newvar, 0, sizeof (newvar)); newvar->var_type = var_type; if ((newvar->var_name = ipc_stralloc(name)) == NULL) { filebench_log(LOG_ERROR, "Out of memory for variables"); return (NULL); } switch (var_type & VAR_TYPE_MASK) { case VAR_TYPE_RANDOM: case VAR_TYPE_GLOBAL: var_listp = &filebench_shm->shm_var_list; break; case VAR_TYPE_DYNAMIC: var_listp = &filebench_shm->shm_var_dyn_list; break; case VAR_TYPE_LOCAL: /* place on head of shared local list */ newvar->var_next = filebench_shm->shm_var_loc_list; filebench_shm->shm_var_loc_list = newvar; return (newvar); default: var_listp = &filebench_shm->shm_var_list; break; } /* add to the end of list */ for (var = *var_listp; var != NULL; var = var->var_next) prev = var; /* Find end of list */ if (prev != NULL) prev->var_next = newvar; else *var_listp = newvar; return (newvar); } /* * Allocates a var (var_t) from interprocess shared memory after * first adjusting the name to elminate the leading $. Places the * allocated var temporarily on the end of the globally * shared var_loc_list. If the allocation fails, returns NULL, * otherwise it returns a pointer to the newly allocated var. */ var_t * var_lvar_alloc_local(char *name) { if (name[0] == '$') name += 1; return (var_alloc_cmn(name, VAR_TYPE_LOCAL)); } /* * Allocates a var (var_t) from interprocess shared memory and * places the allocated var on the end of the globally shared * shm_var_list. If the allocation fails, returns NULL, otherwise * it returns a pointer to the newly allocated var. */ static var_t * var_alloc(char *name) { return (var_alloc_cmn(name, VAR_TYPE_GLOBAL)); } /* * Allocates a var (var_t) from interprocess shared memory. * Places the allocated var on the end of the globally shared * shm_var_dyn_list. If the allocation fails, returns NULL, otherwise * it returns a pointer to the newly allocated var. */ static var_t * var_alloc_dynamic(char *name) { return (var_alloc_cmn(name, VAR_TYPE_DYNAMIC)); } /* * Searches for var_t with name "name" in the shm_var_loc_list, * then, if not found, in the global shm_var_list. If a matching * local or global var is found, returns a pointer to the var_t, * otherwise returns NULL. */ static var_t * var_find(char *name) { var_t *var; for (var = filebench_shm->shm_var_loc_list; var != NULL; var = var->var_next) { if (strcmp(var->var_name, name) == 0) return (var); } for (var = filebench_shm->shm_var_list; var != NULL; var = var->var_next) { if (strcmp(var->var_name, name) == 0) return (var); } return (NULL); } /* * Searches for var_t with name "name" in the supplied shm_var_list. * If not found there, checks the global list. If still * unsuccessful, returns NULL. Otherwise returns a pointer to the var_t. */ static var_t * var_find_list_only(char *name, var_t *var_list) { var_t *var; for (var = var_list; var != NULL; var = var->var_next) { if (strcmp(var->var_name, name) == 0) return (var); } return (NULL); } /* * Searches for var_t with name "name" in the supplied shm_var_list. * If not found there, checks the global list. If still * unsuccessful, returns NULL. Otherwise returns a pointer to the var_t. */ static var_t * var_find_list(char *name, var_t *var_list) { var_t *var; if ((var = var_find_list_only(name, var_list)) != NULL) return (var); else return (var_find(name)); } /* * Searches for the named var and returns it if found. If not * found it allocates a new variable */ static var_t * var_find_alloc(char *name) { var_t *var; if (name == NULL) { filebench_log(LOG_ERROR, "var_find_alloc: Var name not supplied"); return (NULL); } name += 1; if ((var = var_find(name)) == NULL) { var = var_alloc(name); } return (var); } /* * Searches for the named var, and, if found, sets its * var_val.boolean's value to that of the supplied boolean. * If not found, the routine allocates a new var and sets * its var_val.boolean's value to that of the supplied * boolean. If the named var cannot be found or allocated * the routine returns -1, otherwise it returns 0. */ int var_assign_boolean(char *name, boolean_t bool) { var_t *var; if ((var = var_find_alloc(name)) == NULL) { filebench_log(LOG_ERROR, "Cannot assign variable %s", name); return (-1); } if ((var->var_type & VAR_TYPE_MASK) == VAR_TYPE_RANDOM) { filebench_log(LOG_ERROR, "Cannot assign integer to random variable %s", name); return (-1); } VAR_SET_BOOL(var, bool); filebench_log(LOG_DEBUG_SCRIPT, "Assign boolean %s=%d", name, bool); return (0); } /* * Searches for the named var, and, if found, sets its * var_integer's value to that of the supplied integer. * If not found, the routine allocates a new var and sets * its var_integers's value to that of the supplied * integer. If the named var cannot be found or allocated * the routine returns -1, otherwise it returns 0. */ int var_assign_integer(char *name, fbint_t integer) { var_t *var; if ((var = var_find_alloc(name)) == NULL) { filebench_log(LOG_ERROR, "Cannot assign variable %s", name); return (-1); } if ((var->var_type & VAR_TYPE_MASK) == VAR_TYPE_RANDOM) { filebench_log(LOG_ERROR, "Cannot assign integer to random variable %s", name); return (-1); } VAR_SET_INT(var, integer); filebench_log(LOG_DEBUG_SCRIPT, "Assign integer %s=%llu", name, (u_longlong_t)integer); return (0); } /* * Add, subtract, multiply or divide two integers based on optype * passed from caller. */ static fbint_t var_binary_integer_op(var_t *var) { fbint_t result; fbint_t src1, src2; if (var == NULL) return (0); switch (var->var_type & VAR_INDBINOP_MASK) { case VAR_IND_BINOP_INT: src2 = var->var_val.integer; break; case VAR_IND_BINOP_DBL: src2 = (fbint_t)var->var_val.dbl_flt; break; case VAR_IND_BINOP_VAR: if (var->var_val.varptr2 != NULL) src2 = var_get_int(var->var_val.varptr2); else src2 = 0; break; } if (var->var_varptr1 != NULL) src1 = var_get_int(var->var_varptr1); else src1 = 0; switch (var->var_type & VAR_INDVAR_MASK) { case VAR_IND_VAR_SUM_VC: result = src1 + src2; break; case VAR_IND_VAR_DIF_VC: result = src1 - src2; break; case VAR_IND_C_DIF_VAR: result = src2 - src1; break; case VAR_IND_VAR_MUL_VC: result = src1 * src2; break; case VAR_IND_VAR_DIV_VC: result = src1 / src2; break; case VAR_IND_C_DIV_VAR: result = src2 / src1; break; default: filebench_log(LOG_DEBUG_IMPL, "var_binary_integer_op: Called with unknown IND_TYPE"); result = 0; break; } return (result); } /* * Add, subtract, multiply or divide two double precision floating point * numbers based on optype passed from caller. */ static double var_binary_dbl_flt_op(var_t *var) { double result; double src1, src2; if (var == NULL) return (0.0); switch (var->var_type & VAR_INDBINOP_MASK) { case VAR_IND_BINOP_INT: src2 = (double)var->var_val.integer; break; case VAR_IND_BINOP_DBL: src2 = var->var_val.dbl_flt; break; case VAR_IND_BINOP_VAR: if (var->var_val.varptr2 != NULL) src2 = var_get_dbl(var->var_val.varptr2); else src2 = 0; break; } if (var->var_varptr1 != NULL) src1 = var_get_dbl(var->var_varptr1); else src1 = 0; switch (var->var_type & VAR_INDVAR_MASK) { case VAR_IND_VAR_SUM_VC: result = src1 + src2; break; case VAR_IND_VAR_DIF_VC: result = src1 - src2; break; case VAR_IND_C_DIF_VAR: result = src2 - src1; break; case VAR_IND_VAR_MUL_VC: result = src1 * src2; break; case VAR_IND_C_DIV_VAR: result = src2 / src1; break; case VAR_IND_VAR_DIV_VC: result = src1 / src2; break; default: filebench_log(LOG_DEBUG_IMPL, "var_binary_dbl_flt_op: Called with unknown IND_TYPE"); result = 0; break; } return (result); } /* * Perform a binary operation on a variable and an integer */ int var_assign_op_var_int(char *name, int optype, char *src1, fbint_t src2) { var_t *var; var_t *var_src1; if ((var_src1 = var_find(src1+1)) == NULL) return (FILEBENCH_ERROR); if ((var = var_find_alloc(name)) == NULL) return (FILEBENCH_ERROR); if ((var->var_type & VAR_TYPE_MASK) == VAR_TYPE_RANDOM) { filebench_log(LOG_ERROR, "Cannot assign integer to random variable %s", name); return (FILEBENCH_ERROR); } VAR_SET_BINOP_INDVAR(var, var_src1, optype); var->var_val.integer = src2; return (FILEBENCH_OK); } int var_assign_op_var_var(char *name, int optype, char *src1, char *src2) { var_t *var; var_t *var_src1; var_t *var_src2; if ((var_src1 = var_find(src1+1)) == NULL) return (FILEBENCH_ERROR); if ((var_src2 = var_find(src2+1)) == NULL) return (FILEBENCH_ERROR); if ((var = var_find_alloc(name)) == NULL) return (FILEBENCH_ERROR); if ((var->var_type & VAR_TYPE_MASK) == VAR_TYPE_RANDOM) { filebench_log(LOG_ERROR, "Cannot assign integer to random variable %s", name); return (FILEBENCH_ERROR); } VAR_SET_BINOP_INDVAR(var, var_src1, optype); var->var_val.varptr2 = var_src2; return (FILEBENCH_OK); } /* * Find a variable, and set it to random type. * If it does not have a random extension, allocate one */ var_t * var_find_randvar(char *name) { var_t *newvar; name += 1; if ((newvar = var_find(name)) == NULL) { filebench_log(LOG_ERROR, "failed to locate random variable $%s\n", name); return (NULL); } /* set randdist pointer unless it is already set */ if (((newvar->var_type & VAR_TYPE_MASK) != VAR_TYPE_RANDOM) || !VAR_HAS_RANDDIST(newvar)) { filebench_log(LOG_ERROR, "Found variable $%s not random\n", name); return (NULL); } return (newvar); } /* * Allocate a variable, and set it to random type. Then * allocate a random extension. */ var_t * var_define_randvar(char *name) { var_t *newvar; randdist_t *rndp = NULL; name += 1; /* make sure variable doesn't already exist */ if (var_find(name) != NULL) { filebench_log(LOG_ERROR, "variable name already in use\n"); return (NULL); } /* allocate a random variable */ if ((newvar = var_alloc_cmn(name, VAR_TYPE_RANDOM)) == NULL) { filebench_log(LOG_ERROR, "failed to alloc random variable\n"); return (NULL); } /* set randdist pointer */ if ((rndp = randdist_alloc()) == NULL) { filebench_log(LOG_ERROR, "failed to alloc random distribution object\n"); return (NULL); } rndp->rnd_var = newvar; VAR_SET_RAND(newvar, rndp); return (newvar); } /* * Searches for the named var, and if found returns an avd_t * pointing to the var's var_integer, var_string or var_double * as appropriate. If not found, attempts to allocate * a var named "name" and returns an avd_t to it with * no value set. If the var cannot be found or allocated, an * error is logged and the run is terminated. */ avd_t var_ref_attr(char *name) { var_t *var; name += 1; if ((var = var_find(name)) == NULL) var = var_find_dynamic(name); if (var == NULL) var = var_alloc(name); if (var == NULL) { filebench_log(LOG_ERROR, "Invalid variable $%s", name); filebench_shutdown(1); } /* allocate pointer to var and return */ return (avd_alloc_var_ptr(var)); } /* * Converts the contents of a var to a string */ static char * var_get_string(var_t *var) { if ((var->var_type & VAR_TYPE_MASK) == VAR_TYPE_RANDOM) { switch (var->var_val.randptr->rnd_type & RAND_TYPE_MASK) { case RAND_TYPE_UNIFORM: return (fb_stralloc("uniform random var")); case RAND_TYPE_GAMMA: return (fb_stralloc("gamma random var")); case RAND_TYPE_TABLE: return (fb_stralloc("tabular random var")); default: return (fb_stralloc("unitialized random var")); } } if (VAR_HAS_STRING(var) && var->var_val.string) return (fb_stralloc(var->var_val.string)); if (VAR_HAS_BOOLEAN(var)) { if (var->var_val.boolean) return (fb_stralloc("true")); else return (fb_stralloc("false")); } if (VAR_HAS_INTEGER(var)) { char tmp[128]; (void) snprintf(tmp, sizeof (tmp), "%llu", (u_longlong_t)var->var_val.integer); return (fb_stralloc(tmp)); } if (VAR_HAS_INDVAR(var)) { var_t *ivp; if ((ivp = var->var_varptr1) != NULL) { return (var_get_string(ivp)); } } if (VAR_HAS_BINOP(var)) { char tmp[128]; (void) snprintf(tmp, sizeof (tmp), "%llu", var_binary_integer_op(var)); return (fb_stralloc(tmp)); } return (fb_stralloc("No default")); } /* * Searches for the named var, and if found copies the var_val.string, * if it exists, a decimal number string representation of * var_val.integer, the state of var_val.boolean, or the type of random * distribution employed, into a malloc'd bit of memory using fb_stralloc(). * Returns a pointer to the created string, or NULL on failure. */ char * var_to_string(char *name) { var_t *var; name += 1; if ((var = var_find(name)) == NULL) var = var_find_dynamic(name); if (var == NULL) return (NULL); return (var_get_string(var)); } /* * Returns the boolean from the supplied var_t "var". */ static boolean_t var_get_bool(var_t *var) { if (var == NULL) return (0); if (VAR_HAS_BOOLEAN(var)) return (var->var_val.boolean); if (VAR_HAS_INTEGER(var)) { if (var->var_val.integer == 0) return (FALSE); else return (TRUE); } filebench_log(LOG_ERROR, "Attempt to get boolean from %s var $%s", var_get_type_string(var), var->var_name); return (FALSE); } /* * Returns the fbint_t from the supplied var_t "var". */ static fbint_t var_get_int(var_t *var) { randdist_t *rndp; if (var == NULL) return (0); if (VAR_HAS_INTEGER(var)) return (var->var_val.integer); if (VAR_HAS_RANDDIST(var)) { if ((rndp = var->var_val.randptr) != NULL) return ((fbint_t)rndp->rnd_get(rndp)); } if (VAR_HAS_BINOP(var)) return (var_binary_integer_op(var)); filebench_log(LOG_ERROR, "Attempt to get integer from %s var $%s", var_get_type_string(var), var->var_name); return (0); } /* * Returns the floating point value of a variable pointed to by the * supplied var_t "var". Intended to get the actual (double) value * supplied by the random variable. */ static double var_get_dbl(var_t *var) { randdist_t *rndp; if (var == NULL) return (0.0); if (VAR_HAS_INTEGER(var)) return ((double)var->var_val.integer); if (VAR_HAS_DOUBLE(var)) return (var->var_val.dbl_flt); if (VAR_HAS_RANDDIST(var)) { if ((rndp = var->var_val.randptr) != NULL) return (rndp->rnd_get(rndp)); } if (VAR_HAS_BINOP(var)) return (var_binary_dbl_flt_op(var)); filebench_log(LOG_ERROR, "Attempt to get double float from %s var $%s", var_get_type_string(var), var->var_name); return (0.0); } /* * Searches for the named var, and if found returns the value, * of var_val.boolean. If the var is not found, or a boolean * value has not been set, logs an error and returns 0. */ boolean_t var_to_boolean(char *name) { var_t *var; name += 1; if ((var = var_find(name)) == NULL) var = var_find_dynamic(name); if (var != NULL) return (var_get_bool(var)); filebench_log(LOG_ERROR, "Variable %s referenced before set", name); return (FALSE); } /* * Searches for the named var, and if found returns the value, * of var_val.integer. If the var is not found, or the an * integer value has not been set, logs an error and returns 0. */ fbint_t var_to_integer(char *name) { var_t *var; name += 1; if ((var = var_find(name)) == NULL) var = var_find_dynamic(name); if (var != NULL) return (var_get_int(var)); filebench_log(LOG_ERROR, "Variable %s referenced before set", name); return (0); } /* * Searches for the named var, and if found returns the value, * of var_val.dbl_flt. If the var is not found, or the * floating value has not been set, logs an error and returns 0.0. */ double var_to_double(char *name) { var_t *var; name += 1; if ((var = var_find(name)) == NULL) var = var_find_dynamic(name); if (var != NULL) return (var_get_dbl(var)); filebench_log(LOG_ERROR, "Variable %s referenced before set", name); return (0.0); } /* * Searches for the named random var, and if found, converts the * requested parameter into a string or a decimal number string * representation, into a malloc'd bit of memory using fb_stralloc(). * Returns a pointer to the created string, or calls var_to_string() * if a random variable isn't found. */ char * var_randvar_to_string(char *name, int param_name) { var_t *var; fbint_t value; if ((var = var_find(name + 1)) == NULL) return (var_to_string(name)); if (((var->var_type & VAR_TYPE_MASK) != VAR_TYPE_RANDOM) || !VAR_HAS_RANDDIST(var)) return (var_to_string(name)); switch (param_name) { case RAND_PARAM_TYPE: switch (var->var_val.randptr->rnd_type & RAND_TYPE_MASK) { case RAND_TYPE_UNIFORM: return (fb_stralloc("uniform")); case RAND_TYPE_GAMMA: return (fb_stralloc("gamma")); case RAND_TYPE_TABLE: return (fb_stralloc("tabular")); default: return (fb_stralloc("uninitialized")); } case RAND_PARAM_SRC: if (var->var_val.randptr->rnd_type & RAND_SRC_GENERATOR) return (fb_stralloc("rand48")); else return (fb_stralloc("urandom")); case RAND_PARAM_SEED: value = avd_get_int(var->var_val.randptr->rnd_seed); break; case RAND_PARAM_MIN: value = avd_get_int(var->var_val.randptr->rnd_min); break; case RAND_PARAM_MEAN: value = avd_get_int(var->var_val.randptr->rnd_mean); break; case RAND_PARAM_GAMMA: value = avd_get_int(var->var_val.randptr->rnd_gamma); break; case RAND_PARAM_ROUND: value = avd_get_int(var->var_val.randptr->rnd_round); break; default: return (NULL); } /* just an integer value if we got here */ { char tmp[128]; (void) snprintf(tmp, sizeof (tmp), "%llu", (u_longlong_t)value); return (fb_stralloc(tmp)); } } /* * Copies the value stored in the source string into the destination * string. Returns -1 if any problems encountered, 0 otherwise. */ static int var_copy(var_t *dst_var, var_t *src_var) { if (VAR_HAS_BOOLEAN(src_var)) { VAR_SET_BOOL(dst_var, src_var->var_val.boolean); filebench_log(LOG_DEBUG_SCRIPT, "Assign var %s=%s", dst_var->var_name, dst_var->var_val.boolean?"true":"false"); } if (VAR_HAS_INTEGER(src_var)) { VAR_SET_INT(dst_var, src_var->var_val.integer); filebench_log(LOG_DEBUG_SCRIPT, "Assign var %s=%llu", dst_var->var_name, (u_longlong_t)dst_var->var_val.integer); } if (VAR_HAS_DOUBLE(src_var)) { VAR_SET_DBL(dst_var, src_var->var_val.dbl_flt); filebench_log(LOG_DEBUG_SCRIPT, "Assign var %s=%lf", dst_var->var_name, dst_var->var_val.dbl_flt); } if (VAR_HAS_STRING(src_var)) { char *strptr; if ((strptr = ipc_stralloc(src_var->var_val.string)) == NULL) { filebench_log(LOG_ERROR, "Cannot assign string for variable %s", dst_var->var_name); return (-1); } VAR_SET_STR(dst_var, strptr); filebench_log(LOG_DEBUG_SCRIPT, "Assign var %s=%s", dst_var->var_name, dst_var->var_val.string); } if (VAR_HAS_INDVAR(src_var)) { VAR_SET_INDVAR(dst_var, src_var->var_varptr1); filebench_log(LOG_DEBUG_SCRIPT, "Assign var %s to var %s", dst_var->var_name, src_var->var_name); } return (0); } /* * Searches for the var named "name", and if not found * allocates it. The then copies the value from * the src_var into the destination var "name" * If the var "name" cannot be found or allocated, or the var "src_name" * cannot be found, the routine returns -1, otherwise it returns 0. */ int var_assign_var(char *name, char *src_name) { var_t *dst_var, *src_var; name += 1; src_name += 1; if ((src_var = var_find(src_name)) == NULL) { filebench_log(LOG_ERROR, "Cannot find source variable %s", src_name); return (-1); } if ((dst_var = var_find(name)) == NULL) dst_var = var_alloc(name); if (dst_var == NULL) { filebench_log(LOG_ERROR, "Cannot assign variable %s", name); return (-1); } if ((dst_var->var_type & VAR_TYPE_MASK) == VAR_TYPE_RANDOM) { filebench_log(LOG_ERROR, "Cannot assign var to Random variable %s", name); return (-1); } return (var_copy(dst_var, src_var)); } /* * Like var_assign_integer, only this routine copies the * supplied "string" into the var named "name". If the var * named "name" cannot be found then it is first allocated * before the copy. Space for the string in the var comes * from interprocess shared memory. If the var "name" * cannot be found or allocated, or the memory for the * var_val.string copy of "string" cannot be allocated, the * routine returns -1, otherwise it returns 0. */ int var_assign_string(char *name, char *string) { var_t *var; char *strptr; name += 1; if ((var = var_find(name)) == NULL) var = var_alloc(name); if (var == NULL) { filebench_log(LOG_ERROR, "Cannot assign variable %s", name); return (-1); } if ((var->var_type & VAR_TYPE_MASK) == VAR_TYPE_RANDOM) { filebench_log(LOG_ERROR, "Cannot assign string to random variable %s", name); return (-1); } if ((strptr = ipc_stralloc(string)) == NULL) { filebench_log(LOG_ERROR, "Cannot assign variable %s", name); return (-1); } VAR_SET_STR(var, strptr); filebench_log(LOG_DEBUG_SCRIPT, "Var assign string $%s=%s", name, string); return (0); } /* * Allocates a local var. The then extracts the var_string from * the var named "string" and copies it into the var_string * of the var "name", after first allocating a piece of * interprocess shared string memory. Returns a pointer to the * newly allocated local var or NULL on error. */ var_t * var_lvar_assign_var(char *name, char *src_name) { var_t *dst_var, *src_var; src_name += 1; if ((src_var = var_find(src_name)) == NULL) { filebench_log(LOG_ERROR, "Cannot find source variable %s", src_name); return (NULL); } dst_var = var_lvar_alloc_local(name); if (dst_var == NULL) { filebench_log(LOG_ERROR, "Cannot assign variable %s", name); return (NULL); } /* * if referencing another local var which is currently * empty, indirect to it */ if ((src_var->var_type & VAR_TYPE_MASK) == VAR_TYPE_LOCAL) { VAR_SET_INDVAR(dst_var, src_var); filebench_log(LOG_DEBUG_SCRIPT, "Assign local var %s to %s", name, src_name); return (dst_var); } if (VAR_HAS_BOOLEAN(src_var)) { VAR_SET_BOOL(dst_var, src_var->var_val.boolean); filebench_log(LOG_DEBUG_SCRIPT, "Assign var (%s, %p)=%s", name, dst_var, src_var->var_val.boolean?"true":"false"); } else if (VAR_HAS_INTEGER(src_var)) { VAR_SET_INT(dst_var, src_var->var_val.integer); filebench_log(LOG_DEBUG_SCRIPT, "Assign var (%s, %p)=%llu", name, dst_var, (u_longlong_t)src_var->var_val.integer); } else if (VAR_HAS_STRING(src_var)) { char *strptr; if ((strptr = ipc_stralloc(src_var->var_val.string)) == NULL) { filebench_log(LOG_ERROR, "Cannot assign variable %s", name); return (NULL); } VAR_SET_STR(dst_var, strptr); filebench_log(LOG_DEBUG_SCRIPT, "Assign var (%s, %p)=%s", name, dst_var, src_var->var_val.string); } else if (VAR_HAS_DOUBLE(src_var)) { /* LINTED E_ASSIGMENT_CAUSE_LOSS_PREC */ VAR_SET_INT(dst_var, src_var->var_val.dbl_flt); filebench_log(LOG_DEBUG_SCRIPT, "Assign var (%s, %p)=%8.2f", name, dst_var, src_var->var_val.dbl_flt); } else if (VAR_HAS_RANDDIST(src_var)) { VAR_SET_RAND(dst_var, src_var->var_val.randptr); filebench_log(LOG_DEBUG_SCRIPT, "Assign var (%s, %p)=%llu", name, dst_var, (u_longlong_t)src_var->var_val.integer); } return (dst_var); } /* * the routine allocates a new local var and sets * its var_boolean's value to that of the supplied * boolean. It returns a pointer to the new local var */ var_t * var_lvar_assign_boolean(char *name, boolean_t bool) { var_t *var; var = var_lvar_alloc_local(name); if (var == NULL) { filebench_log(LOG_ERROR, "Cannot assign variable %s", name); return (NULL); } VAR_SET_BOOL(var, bool); filebench_log(LOG_DEBUG_SCRIPT, "Assign integer %s=%s", name, bool ? "true" : "false"); return (var); } /* * the routine allocates a new local var and sets * its var_integers's value to that of the supplied * integer. It returns a pointer to the new local var */ var_t * var_lvar_assign_integer(char *name, fbint_t integer) { var_t *var; var = var_lvar_alloc_local(name); if (var == NULL) { filebench_log(LOG_ERROR, "Cannot assign variable %s", name); return (NULL); } VAR_SET_INT(var, integer); filebench_log(LOG_DEBUG_SCRIPT, "Assign integer %s=%llu", name, (u_longlong_t)integer); return (var); } /* * the routine allocates a new local var and sets * its var_dbl_flt value to that of the supplied * double precission floating point number. It returns * a pointer to the new local var */ var_t * var_lvar_assign_double(char *name, double dbl) { var_t *var; var = var_lvar_alloc_local(name); if (var == NULL) { filebench_log(LOG_ERROR, "Cannot assign variable %s", name); return (NULL); } VAR_SET_DBL(var, dbl); filebench_log(LOG_DEBUG_SCRIPT, "Assign integer %s=%8.2f", name, dbl); return (var); } /* * Like var_lvar_assign_integer, only this routine copies the * supplied "string" into the var named "name". If the var * named "name" cannot be found then it is first allocated * before the copy. Space for the string in the var comes * from interprocess shared memory. The allocated local var * is returned at as a char *, or NULL on error. */ var_t * var_lvar_assign_string(char *name, char *string) { var_t *var; char *strptr; var = var_lvar_alloc_local(name); if (var == NULL) { filebench_log(LOG_ERROR, "Cannot assign variable %s", name); return (NULL); } if ((strptr = ipc_stralloc(string)) == NULL) { filebench_log(LOG_ERROR, "Cannot assign variable %s", name); return (NULL); } VAR_SET_STR(var, strptr); filebench_log(LOG_DEBUG_SCRIPT, "Lvar_assign_string (%s, %p)=%s", name, var, string); return (var); } /* * Tests to see if the supplied variable name without the portion after * the last period is that of a random variable. If it is, it returns * the number of characters to backspace to skip the period and field * name. Otherwise it returns 0. */ int var_is_set4_randvar(char *name) { var_t *var; char varname[128]; int namelength; char *sp; (void) strncpy(varname, name, 128); namelength = strlen(varname); sp = varname + namelength; while (sp != varname) { int c = *sp; *sp = 0; if (c == '.') break; sp--; } /* not a variable name + field? */ if (sp == varname) return (0); /* first part not a variable name? */ if ((var = var_find(varname+1)) == NULL) return (0); /* Make sure it is a random variable */ if ((var->var_type & VAR_TYPE_MASK) != VAR_TYPE_RANDOM) return (0); /* calculate offset from end of random variable name */ return (namelength - (sp - varname)); } /* * Implements a simple path name like scheme for finding values * to place in certain specially named vars. The first part of * the name is interpreted as a category of either: stats, * eventgen, date, script, or host var. If a match is found, * the appropriate routine is called to fill in the requested * value in the provided var_t, and a pointer to the supplied * var_t is returned. If the requested value is not found, NULL * is returned. */ static var_t * var_find_internal(var_t *var) { char *n = fb_stralloc(var->var_name); char *name = n; var_t *rtn = NULL; name++; if (name[strlen(name) - 1] != '}') return (NULL); name[strlen(name) - 1] = 0; if (strncmp(name, STATS_VAR, strlen(STATS_VAR)) == 0) rtn = stats_findvar(var, name + strlen(STATS_VAR)); if (strcmp(name, EVENTGEN_VAR) == 0) rtn = eventgen_ratevar(var); if (strcmp(name, DATE_VAR) == 0) rtn = date_var(var); if (strcmp(name, SCRIPT_VAR) == 0) rtn = script_var(var); if (strcmp(name, HOST_VAR) == 0) rtn = host_var(var); free(n); return (rtn); } /* * Calls the C library routine getenv() to obtain the value * for the environment variable specified by var->var_name. * If found, the value string is returned in var->var_val.string. * If the requested value is not found, NULL is returned. */ static var_t * var_find_environment(var_t *var) { char *n = fb_stralloc(var->var_name); char *name = n; char *strptr; name++; if (name[strlen(name) - 1] != ')') { free(n); return (NULL); } name[strlen(name) - 1] = 0; if ((strptr = getenv(name)) != NULL) { free(n); VAR_SET_STR(var, strptr); return (var); } else { free(n); return (NULL); } } /* * Look up special variables. The "name" argument is used to find * the desired special var and fill it with an appropriate string * value. Looks for an already allocated var of the same name on * the shm_var_dyn_list. If not found a new dynamic var is allocated. * if the name begins with '{', it is an internal variable, and * var_find_internal() is called. If the name begins with '(' it * is an environment varable, and var_find_environment() is * called. On success, a pointer to the var_t is returned, * otherwise, NULL is returned. */ static var_t * var_find_dynamic(char *name) { var_t *var = NULL; var_t *v = filebench_shm->shm_var_dyn_list; var_t *rtn; /* * Lookup a reference to the var handle for this * special var */ for (v = filebench_shm->shm_var_dyn_list; v != NULL; v = v->var_next) { if (strcmp(v->var_name, name) == 0) { var = v; break; } } if (var == NULL) var = var_alloc_dynamic(name); /* Internal system control variable */ if (*name == '{') { rtn = var_find_internal(var); if (rtn == NULL) filebench_log(LOG_ERROR, "Cannot find internal variable %s", var->var_name); return (rtn); } /* Lookup variable in environment */ if (*name == '(') { rtn = var_find_environment(var); if (rtn == NULL) filebench_log(LOG_ERROR, "Cannot find environment variable %s", var->var_name); return (rtn); } return (NULL); } /* * replace the avd_t attribute value descriptor in the new FLOW_MASTER flowop * that points to a local variable with a new avd_t containing * the actual value from the local variable. */ void avd_update(avd_t *avdp, var_t *lvar_list) { var_t *old_lvar, *new_lvar; if ((*avdp)->avd_type == AVD_IND_VAR) { /* Make sure there is a local var */ if ((old_lvar = (*avdp)->avd_val.varptr) == NULL) { filebench_log(LOG_ERROR, "avd_update: local var not found"); return; } } else { /* Empty or not indirect, so no update needed */ return; } /* allocate a new avd using the new or old lvar contents */ if ((new_lvar = var_find_list(old_lvar->var_name, lvar_list)) != NULL) (*avdp) = avd_alloc_var_ptr(new_lvar); else (*avdp) = avd_alloc_var_ptr(old_lvar); } void var_update_comp_lvars(var_t *newlvar, var_t *proto_comp_vars, var_t *mstr_lvars) { var_t *proto_lvar; /* find the prototype lvar from the inherited list */ proto_lvar = var_find_list_only(newlvar->var_name, proto_comp_vars); if (proto_lvar == NULL) return; /* * if the new local variable has not already been assigned * a value, try to copy a value from the prototype local variable */ if ((newlvar->var_type & VAR_TYPE_SET_MASK) == 0) { /* copy value from prototype lvar to new lvar */ (void) var_copy(newlvar, proto_lvar); } /* If proto lvar is indirect, see if we can colapse indirection */ if (VAR_HAS_INDVAR(proto_lvar)) { var_t *uplvp; uplvp = (var_t *)proto_lvar->var_varptr1; /* search for more current uplvar on comp master list */ if (mstr_lvars) { uplvp = var_find_list_only( uplvp->var_name, mstr_lvars); VAR_SET_INDVAR(newlvar, uplvp); } if (VAR_HAS_INDVAR(uplvp)) VAR_SET_INDVAR(newlvar, uplvp->var_varptr1); } }