/* * 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 <signal.h> #include <errno.h> #include <sys/types.h> #include <locale.h> #include <sys/utsname.h> #include <sys/statvfs.h> #ifdef HAVE_STDINT_H #include <stdint.h> #endif #include <fcntl.h> #include <sys/mman.h> #include <sys/wait.h> #ifdef HAVE_LIBTECLA #include <libtecla.h> #endif #include "parsertypes.h" #include "filebench.h" #include "utils.h" #include "stats.h" #include "vars.h" #include "eventgen.h" #ifdef HAVE_LIBTECLA #include "auto_comp.h" #endif #include "multi_client_sync.h" int dofile = FS_FALSE; static const char cmdname[] = "filebench"; static const char cmd_options[] = "pa:f:hi:s:m:"; static void usage(int); static cmd_t *cmd = NULL; /* Command being processed */ #ifdef HAVE_LIBTECLA static GetLine *gl; /* GetLine resource object */ #endif char *execname; char *fbbasepath = FILEBENCHDIR; char *fscriptname; int noproc = 0; var_t *var_list = NULL; pidlist_t *pidlist = NULL; char *cwd = NULL; FILE *parentscript = NULL; static int filecreate_done = 0; /* yacc externals */ extern FILE *yyin; extern int yydebug; extern void yyerror(char *s); /* utilities */ static void terminate(void); static cmd_t *alloc_cmd(void); static attr_t *alloc_attr(void); static attr_t *alloc_lvar_attr(var_t *var); static attr_t *get_attr(cmd_t *cmd, int64_t name); static attr_t *get_attr_fileset(cmd_t *cmd, int64_t name); static attr_t *get_attr_integer(cmd_t *cmd, int64_t name); static attr_t *get_attr_bool(cmd_t *cmd, int64_t name); static void get_attr_lvars(cmd_t *cmd, flowop_t *flowop); static var_t *alloc_var(void); static var_t *get_var(cmd_t *cmd, int64_t name); static list_t *alloc_list(); static probtabent_t *alloc_probtabent(void); static void add_lvar_to_list(var_t *newlvar, var_t **lvar_list); /* Info Commands */ static void parser_list(cmd_t *); static void parser_flowop_list(cmd_t *); /* Define Commands */ static void parser_proc_define(cmd_t *); static void parser_thread_define(cmd_t *, procflow_t *, int instances); static void parser_flowop_define(cmd_t *, threadflow_t *, flowop_t **, int); static void parser_file_define(cmd_t *); static void parser_fileset_define(cmd_t *); static void parser_randvar_define(cmd_t *); static void parser_randvar_set(cmd_t *); static void parser_composite_flowop_define(cmd_t *); /* Create Commands */ static void parser_proc_create(cmd_t *); static void parser_thread_create(cmd_t *); static void parser_flowop_create(cmd_t *); static void parser_fileset_create(cmd_t *); /* set commands */ static void parser_set_integer(cmd_t *cmd); static void parser_set_var(cmd_t *cmd); static void parser_set_var_op_int(cmd_t *cmd); static void parser_set_int_op_var(cmd_t *cmd); static void parser_set_var_op_var(cmd_t *cmd); /* Shutdown Commands */ static void parser_proc_shutdown(cmd_t *); static void parser_filebench_shutdown(cmd_t *cmd); /* Other Commands */ static void parser_echo(cmd_t *cmd); static void parser_foreach_integer(cmd_t *cmd); static void parser_foreach_string(cmd_t *cmd); static void parser_fscheck(cmd_t *cmd); static void parser_fsflush(cmd_t *cmd); static void parser_log(cmd_t *cmd); static void parser_statscmd(cmd_t *cmd); static void parser_statsdump(cmd_t *cmd); static void parser_statsxmldump(cmd_t *cmd); static void parser_statsmultidump(cmd_t *cmd); static void parser_usage(cmd_t *cmd); static void parser_vars(cmd_t *cmd); static void parser_printvars(cmd_t *cmd); static void parser_system(cmd_t *cmd); static void parser_statssnap(cmd_t *cmd); static void parser_directory(cmd_t *cmd); static void parser_eventgen(cmd_t *cmd); static void parser_enable_mc(cmd_t *cmd); static void parser_domultisync(cmd_t *cmd); static void parser_run(cmd_t *cmd); static void parser_run_variable(cmd_t *cmd); static void parser_sleep(cmd_t *cmd); static void parser_sleep_variable(cmd_t *cmd); static void parser_warmup(cmd_t *cmd); static void parser_warmup_variable(cmd_t *cmd); static void parser_help(cmd_t *cmd); static void arg_parse(const char *command); static void parser_abort(int arg); static void parser_version(cmd_t *cmd); %} %union { int64_t ival; uchar_t bval; char * sval; fs_u val; avd_t avd; cmd_t *cmd; attr_t *attr; list_t *list; probtabent_t *rndtb; } %start commands %token FSC_LIST FSC_DEFINE FSC_EXEC FSC_QUIT FSC_DEBUG FSC_CREATE %token FSC_SLEEP FSC_STATS FSC_FOREACH FSC_SET FSC_SHUTDOWN FSC_LOG %token FSC_SYSTEM FSC_FLOWOP FSC_EVENTGEN FSC_ECHO FSC_LOAD FSC_RUN %token FSC_WARMUP FSC_NOUSESTATS FSC_FSCHECK FSC_FSFLUSH %token FSC_USAGE FSC_HELP FSC_VARS FSC_VERSION FSC_ENABLE FSC_DOMULTISYNC %token FSV_STRING FSV_VAL_INT FSV_VAL_BOOLEAN FSV_VARIABLE FSV_WHITESTRING %token FSV_RANDUNI FSV_RANDTAB FSV_RANDVAR FSV_URAND FSV_RAND48 %token FST_INT FST_BOOLEAN %token FSE_FILE FSE_PROC FSE_THREAD FSE_CLEAR FSE_ALL FSE_SNAP FSE_DUMP %token FSE_DIRECTORY FSE_COMMAND FSE_FILESET FSE_XMLDUMP FSE_RAND FSE_MODE %token FSE_MULTI FSE_MULTIDUMP %token FSK_SEPLST FSK_OPENLST FSK_CLOSELST FSK_ASSIGN FSK_IN FSK_QUOTE %token FSK_DIRSEPLST FSK_PLUS FSK_MINUS FSK_MULTIPLY FSK_DIVIDE %token FSA_SIZE FSA_PREALLOC FSA_PARALLOC FSA_PATH FSA_REUSE %token FSA_PROCESS FSA_MEMSIZE FSA_RATE FSA_CACHED FSA_READONLY FSA_TRUSTTREE %token FSA_IOSIZE FSA_FILE FSA_WSS FSA_NAME FSA_RANDOM FSA_INSTANCES %token FSA_DSYNC FSA_TARGET FSA_ITERS FSA_NICE FSA_VALUE FSA_BLOCKING %token FSA_HIGHWATER FSA_DIRECTIO FSA_DIRWIDTH FSA_FD FSA_SRCFD FSA_ROTATEFD %token FSA_NAMELENGTH FSA_FILESIZE FSA_ENTRIES FSA_FILESIZEGAMMA FSA_DIRDEPTHRV %token FSA_DIRGAMMA FSA_USEISM FSA_TYPE FSA_RANDTABLE FSA_RANDSRC FSA_RANDROUND %token FSA_LEAFDIRS FSA_INDEXED FSA_FSTYPE %token FSA_RANDSEED FSA_RANDGAMMA FSA_RANDMEAN FSA_RANDMIN FSA_MASTER %token FSA_CLIENT %token FSS_TYPE FSS_SEED FSS_GAMMA FSS_MEAN FSS_MIN FSS_SRC FSS_ROUND %token FSV_SET_LOCAL_VAR FSA_LVAR_ASSIGN %token FSA_ALLDONE FSA_FIRSTDONE FSA_TIMEOUT %type <ival> FSV_VAL_INT %type <bval> FSV_VAL_BOOLEAN %type <sval> FSV_STRING %type <sval> FSV_WHITESTRING %type <sval> FSV_VARIABLE %type <sval> FSV_RANDVAR %type <sval> FSK_ASSIGN %type <sval> FSV_SET_LOCAL_VAR %type <ival> FSC_LIST FSC_DEFINE FSC_SET FSC_LOAD FSC_RUN FSC_ENABLE %type <ival> FSC_DOMULTISYNC %type <ival> FSE_FILE FSE_PROC FSE_THREAD FSE_CLEAR FSC_HELP FSC_VERSION %type <sval> name %type <ival> entity %type <val> value %type <cmd> command inner_commands load_command run_command list_command %type <cmd> proc_define_command files_define_command randvar_define_command %type <cmd> fo_define_command debug_command create_command %type <cmd> sleep_command stats_command set_command shutdown_command %type <cmd> foreach_command log_command system_command flowop_command %type <cmd> eventgen_command quit_command flowop_list thread_list %type <cmd> thread echo_command usage_command help_command vars_command %type <cmd> version_command enable_command multisync_command %type <cmd> warmup_command fscheck_command fsflush_command %type <cmd> set_integer_command set_other_command %type <attr> files_attr_op files_attr_ops pt_attr_op pt_attr_ops %type <attr> fo_attr_op fo_attr_ops ev_attr_op ev_attr_ops %type <attr> randvar_attr_op randvar_attr_ops randvar_attr_typop %type <attr> randvar_attr_srcop attr_value attr_list_value %type <attr> comp_lvar_def comp_attr_op comp_attr_ops %type <attr> enable_multi_ops enable_multi_op multisync_op %type <attr> fscheck_attr_op %type <list> integer_seplist string_seplist string_list var_string_list %type <list> var_string whitevar_string whitevar_string_list %type <ival> attrs_define_file attrs_define_thread attrs_flowop %type <ival> attrs_define_fileset attrs_define_proc attrs_eventgen attrs_define_comp %type <ival> files_attr_name pt_attr_name fo_attr_name ev_attr_name %type <ival> randvar_attr_name FSA_TYPE randtype_name randvar_attr_param %type <ival> randsrc_name FSA_RANDSRC randvar_attr_tsp em_attr_name %type <ival> FSS_TYPE FSS_SEED FSS_GAMMA FSS_MEAN FSS_MIN FSS_SRC %type <ival> fscheck_attr_name FSA_FSTYPE binary_op %type <rndtb> probtabentry_list probtabentry %type <avd> var_int_val %% commands: commands command { list_t *list = NULL; list_t *list_end = NULL; if ($2->cmd != NULL) $2->cmd($2); free($2); } | commands error { if (dofile) YYABORT; } |; inner_commands: command { filebench_log(LOG_DEBUG_IMPL, "inner_command %zx", $1); $$ = $1; } | inner_commands command { cmd_t *list = NULL; cmd_t *list_end = NULL; /* Find end of list */ for (list = $1; list != NULL; list = list->cmd_next) list_end = list; list_end->cmd_next = $2; filebench_log(LOG_DEBUG_IMPL, "inner_commands adding cmd %zx to list %zx", $2, $1); $$ = $1; }; command: proc_define_command | files_define_command | randvar_define_command | fo_define_command | debug_command | eventgen_command | create_command | echo_command | usage_command | vars_command | foreach_command | fscheck_command | fsflush_command | help_command | list_command | load_command | log_command | run_command | set_command | shutdown_command | sleep_command | warmup_command | stats_command | system_command | version_command | enable_command | multisync_command | quit_command; foreach_command: FSC_FOREACH { if (($$ = alloc_cmd()) == NULL) YYERROR; filebench_log(LOG_DEBUG_IMPL, "foreach_command %zx", $$); } | foreach_command FSV_VARIABLE FSK_IN integer_seplist FSK_OPENLST inner_commands FSK_CLOSELST { cmd_t *cmd, *inner_cmd; list_t *list; $$ = $1; $$->cmd_list = $6; $$->cmd_tgt1 = $2; $$->cmd_param_list = $4; $$->cmd = parser_foreach_integer; for (list = $$->cmd_param_list; list != NULL; list = list->list_next) { for (inner_cmd = $$->cmd_list; inner_cmd != NULL; inner_cmd = inner_cmd->cmd_next) { filebench_log(LOG_DEBUG_IMPL, "packing foreach: %zx %s=%llu, cmd %zx", $$, $$->cmd_tgt1, (u_longlong_t)avd_get_int(list->list_integer), inner_cmd); } } }| foreach_command FSV_VARIABLE FSK_IN string_seplist FSK_OPENLST inner_commands FSK_CLOSELST { cmd_t *cmd, *inner_cmd; list_t *list; $$ = $1; $$->cmd_list = $6; $$->cmd_tgt1 = $2; $$->cmd_param_list = $4; $$->cmd = parser_foreach_string; for (list = $$->cmd_param_list; list != NULL; list = list->list_next) { for (inner_cmd = $$->cmd_list; inner_cmd != NULL; inner_cmd = inner_cmd->cmd_next) { filebench_log(LOG_DEBUG_IMPL, "packing foreach: %zx %s=%s, cmd %zx", $$, $$->cmd_tgt1, *list->list_string, inner_cmd); } } }; integer_seplist: FSV_VAL_INT { if (($$ = alloc_list()) == NULL) YYERROR; $$->list_integer = avd_int_alloc($1); } | integer_seplist FSK_SEPLST FSV_VAL_INT { list_t *list = NULL; list_t *list_end = NULL; if (($$ = alloc_list()) == NULL) YYERROR; $$->list_integer = avd_int_alloc($3); /* Find end of list */ for (list = $1; list != NULL; list = list->list_next) list_end = list; list_end->list_next = $$; $$ = $1; }; string_seplist: FSK_QUOTE FSV_WHITESTRING FSK_QUOTE { if (($$ = alloc_list()) == NULL) YYERROR; $$->list_string = avd_str_alloc($2); } | string_seplist FSK_SEPLST FSK_QUOTE FSV_WHITESTRING FSK_QUOTE { list_t *list = NULL; list_t *list_end = NULL; if (($$ = alloc_list()) == NULL) YYERROR; $$->list_string = avd_str_alloc($4); /* Find end of list */ for (list = $1; list != NULL; list = list->list_next) list_end = list; list_end->list_next = $$; $$ = $1; }; eventgen_command: FSC_EVENTGEN { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = &parser_eventgen; } | eventgen_command ev_attr_ops { $1->cmd_attr_list = $2; }; system_command: FSC_SYSTEM whitevar_string_list { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd_param_list = $2; $$->cmd = parser_system; }; echo_command: FSC_ECHO whitevar_string_list { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd_param_list = $2; $$->cmd = parser_echo; }; version_command: FSC_VERSION { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = parser_version; }; usage_command: FSC_USAGE whitevar_string_list { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd_param_list = $2; $$->cmd = parser_usage; }; vars_command: FSC_VARS { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = parser_printvars; }; enable_command: FSC_ENABLE FSE_MULTI { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = parser_enable_mc; } | enable_command enable_multi_ops { $1->cmd_attr_list = $2; }; multisync_command: FSC_DOMULTISYNC multisync_op { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = parser_domultisync; $$->cmd_attr_list = $2; } string_list: FSV_VARIABLE { if (($$ = alloc_list()) == NULL) YYERROR; $$->list_string = avd_str_alloc($1); } | string_list FSK_SEPLST FSV_VARIABLE { list_t *list = NULL; list_t *list_end = NULL; if (($$ = alloc_list()) == NULL) YYERROR; $$->list_string = avd_str_alloc($3); /* Find end of list */ for (list = $1; list != NULL; list = list->list_next) list_end = list; list_end->list_next = $$; $$ = $1; }; var_string: FSV_VARIABLE { if (($$ = alloc_list()) == NULL) YYERROR; $$->list_string = avd_str_alloc($1); } | FSV_STRING { if (($$ = alloc_list()) == NULL) YYERROR; $$->list_string = avd_str_alloc($1); }; var_string_list: var_string { $$ = $1; }| var_string FSV_STRING { list_t *list = NULL; list_t *list_end = NULL; /* Add string */ if (($$ = alloc_list()) == NULL) YYERROR; $$->list_string = avd_str_alloc($2); /* Find end of list */ for (list = $1; list != NULL; list = list->list_next) list_end = list; list_end->list_next = $$; $$ = $1; }| var_string FSV_VARIABLE { list_t *list = NULL; list_t *list_end = NULL; /* Add variable */ if (($$ = alloc_list()) == NULL) YYERROR; $$->list_string = avd_str_alloc($2); /* Find end of list */ for (list = $1; list != NULL; list = list->list_next) list_end = list; list_end->list_next = $$; $$ = $1; } |var_string_list FSV_STRING { list_t *list = NULL; list_t *list_end = NULL; /* Add string */ if (($$ = alloc_list()) == NULL) YYERROR; $$->list_string = avd_str_alloc($2); /* Find end of list */ for (list = $1; list != NULL; list = list->list_next) list_end = list; list_end->list_next = $$; $$ = $1; }| var_string_list FSV_VARIABLE { list_t *list = NULL; list_t *list_end = NULL; /* Add variable */ if (($$ = alloc_list()) == NULL) YYERROR; $$->list_string = avd_str_alloc($2); /* Find end of list */ for (list = $1; list != NULL; list = list->list_next) list_end = list; list_end->list_next = $$; $$ = $1; }; whitevar_string: FSK_QUOTE FSV_VARIABLE { if (($$ = alloc_list()) == NULL) YYERROR; $$->list_string = avd_str_alloc($2); } | FSK_QUOTE FSV_WHITESTRING { if (($$ = alloc_list()) == NULL) YYERROR; $$->list_string = avd_str_alloc($2); }; whitevar_string_list: whitevar_string FSV_WHITESTRING { list_t *list = NULL; list_t *list_end = NULL; /* Add string */ if (($$ = alloc_list()) == NULL) YYERROR; $$->list_string = avd_str_alloc($2); /* Find end of list */ for (list = $1; list != NULL; list = list->list_next) list_end = list; list_end->list_next = $$; $$ = $1; }| whitevar_string FSV_VARIABLE { list_t *list = NULL; list_t *list_end = NULL; /* Add variable */ if (($$ = alloc_list()) == NULL) YYERROR; $$->list_string = avd_str_alloc($2); /* Find end of list */ for (list = $1; list != NULL; list = list->list_next) list_end = list; list_end->list_next = $$; $$ = $1; }| whitevar_string FSV_RANDVAR randvar_attr_tsp { list_t *list = NULL; list_t *list_end = NULL; /* Add variable */ if (($$ = alloc_list()) == NULL) YYERROR; $$->list_string = avd_str_alloc($2); $$->list_integer = avd_int_alloc($3); /* Find end of list */ for (list = $1; list != NULL; list = list->list_next) list_end = list; list_end->list_next = $$; $$ = $1; }| whitevar_string_list FSV_WHITESTRING { list_t *list = NULL; list_t *list_end = NULL; /* Add string */ if (($$ = alloc_list()) == NULL) YYERROR; $$->list_string = avd_str_alloc($2); /* Find end of list */ for (list = $1; list != NULL; list = list->list_next) list_end = list; list_end->list_next = $$; $$ = $1; }| whitevar_string_list FSV_VARIABLE { list_t *list = NULL; list_t *list_end = NULL; /* Add variable */ if (($$ = alloc_list()) == NULL) YYERROR; $$->list_string = avd_str_alloc($2); /* Find end of list */ for (list = $1; list != NULL; list = list->list_next) list_end = list; list_end->list_next = $$; $$ = $1; }| whitevar_string_list FSV_RANDVAR randvar_attr_tsp { list_t *list = NULL; list_t *list_end = NULL; /* Add variable */ if (($$ = alloc_list()) == NULL) YYERROR; $$->list_string = avd_str_alloc($2); $$->list_integer = avd_int_alloc($3); /* Find end of list */ for (list = $1; list != NULL; list = list->list_next) list_end = list; list_end->list_next = $$; $$ = $1; }| whitevar_string_list FSK_QUOTE { $$ = $1; }| whitevar_string FSK_QUOTE { $$ = $1; }; list_command: FSC_LIST { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = &parser_list; } | list_command FSC_FLOWOP { $1->cmd = &parser_flowop_list; }; fscheck_command: FSC_FSCHECK fscheck_attr_op { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = &parser_fscheck; $$->cmd_attr_list = $2; } | fscheck_command fscheck_attr_op { $1->cmd_attr_list->attr_next = $2; }; fsflush_command: FSC_FSFLUSH fscheck_attr_op { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = &parser_fsflush; $$->cmd_attr_list = $2; }; log_command: FSC_LOG whitevar_string_list { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = &parser_log; $$->cmd_param_list = $2; }; debug_command: FSC_DEBUG FSV_VAL_INT { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = NULL; filebench_shm->shm_debug_level = $2; if (filebench_shm->shm_debug_level > 9) yydebug = 1; }; set_command: set_integer_command | set_other_command; set_integer_command: FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_VAL_INT { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd_tgt1 = $2; $$->cmd_qty = $4; if (parentscript) { parser_vars($$); } $$->cmd = parser_set_integer; }| FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_VARIABLE { if (($$ = alloc_cmd()) == NULL) YYERROR; var_assign_var($2, $4); $$->cmd_tgt1 = $2; $$->cmd_tgt2 = $4; if (parentscript) { parser_vars($$); } $$->cmd = parser_set_var; } | set_integer_command binary_op FSV_VAL_INT { if ($1->cmd == parser_set_integer) { switch ($2) { case FSK_PLUS: var_assign_integer($1->cmd_tgt1, $1->cmd_qty + $3); break; case FSK_MINUS: var_assign_integer($1->cmd_tgt1, $1->cmd_qty - $3); break; case FSK_MULTIPLY: var_assign_integer($1->cmd_tgt1, $1->cmd_qty * $3); break; case FSK_DIVIDE: var_assign_integer($1->cmd_tgt1, $1->cmd_qty / $3); break; } $$->cmd = NULL; } else { $1->cmd_qty = $3; $1->cmd_subtype = $2; $1->cmd = parser_set_var_op_int; } } | set_integer_command binary_op FSV_VARIABLE { $1->cmd_tgt3 = $3; $1->cmd_subtype = $2; if ($1->cmd == parser_set_integer) { $$->cmd = parser_set_int_op_var; } else { $1->cmd = parser_set_var_op_var; } }; set_other_command: FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_VAL_BOOLEAN { if (($$ = alloc_cmd()) == NULL) YYERROR; var_assign_boolean($2, $4); if (parentscript) { $$->cmd_tgt1 = $2; parser_vars($$); } $$->cmd = NULL; } | FSC_SET FSV_VARIABLE FSK_ASSIGN FSK_QUOTE FSV_WHITESTRING FSK_QUOTE { if (($$ = alloc_cmd()) == NULL) YYERROR; var_assign_string($2, $5); if (parentscript) { $$->cmd_tgt1 = $2; parser_vars($$); } $$->cmd = NULL; }| FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_STRING { if (($$ = alloc_cmd()) == NULL) YYERROR; var_assign_string($2, $4); if (parentscript) { $$->cmd_tgt1 = $2; parser_vars($$); } $$->cmd = NULL; } | FSC_SET FSE_MODE FSC_QUIT FSA_TIMEOUT { filebench_shm->shm_rmode = FILEBENCH_MODE_TIMEOUT; if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = NULL; } | FSC_SET FSE_MODE FSC_QUIT FSA_ALLDONE { filebench_shm->shm_rmode = FILEBENCH_MODE_QALLDONE; if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = NULL; } | FSC_SET FSE_MODE FSC_QUIT FSA_FIRSTDONE { filebench_shm->shm_rmode = FILEBENCH_MODE_Q1STDONE; if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = NULL; } | FSC_SET FSE_MODE FSC_NOUSESTATS { filebench_shm->shm_mmode |= FILEBENCH_MODE_NOUSAGE; filebench_log(LOG_INFO, "disabling CPU usage statistics"); if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = NULL; } | FSC_SET FSV_RANDVAR FSS_TYPE FSK_ASSIGN randvar_attr_typop { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = &parser_randvar_set; $$->cmd_tgt1 = $2; $$->cmd_qty = FSS_TYPE; $$->cmd_attr_list = $5; } | FSC_SET FSV_RANDVAR FSS_SRC FSK_ASSIGN randvar_attr_srcop { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = &parser_randvar_set; $$->cmd_tgt1 = $2; $$->cmd_qty = FSS_SRC; $$->cmd_attr_list = $5; } | FSC_SET FSV_RANDVAR randvar_attr_param FSK_ASSIGN attr_value { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = &parser_randvar_set; $$->cmd_tgt1 = $2; $$->cmd_qty = $3; $$->cmd_attr_list = $5; }; stats_command: FSC_STATS FSE_SNAP { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = (void (*)(struct cmd *))&parser_statssnap; break; } | FSC_STATS FSE_CLEAR { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = (void (*)(struct cmd *))&stats_clear; } | FSC_STATS FSE_DIRECTORY var_string_list { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd_param_list = $3; $$->cmd = (void (*)(struct cmd *))&parser_directory; } | FSC_STATS FSE_COMMAND whitevar_string_list { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd_param_list = $3; $$->cmd = parser_statscmd; }| FSC_STATS FSE_DUMP whitevar_string_list { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd_param_list = $3; $$->cmd = parser_statsdump; }| FSC_STATS FSE_XMLDUMP whitevar_string_list { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd_param_list = $3; $$->cmd = parser_statsxmldump; }| FSC_STATS FSE_MULTIDUMP whitevar_string_list { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd_param_list = $3; $$->cmd = parser_statsmultidump; }; quit_command: FSC_QUIT { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = parser_filebench_shutdown; }; flowop_list: flowop_command { $$ = $1; }| flowop_list flowop_command { cmd_t *list = NULL; cmd_t *list_end = NULL; /* Find end of list */ for (list = $1; list != NULL; list = list->cmd_next) list_end = list; list_end->cmd_next = $2; filebench_log(LOG_DEBUG_IMPL, "flowop_list adding cmd %zx to list %zx", $2, $1); $$ = $1; }; thread: FSE_THREAD pt_attr_ops FSK_OPENLST flowop_list FSK_CLOSELST { /* * Allocate a cmd node per thread, with a * list of flowops attached to the cmd_list */ if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd_list = $4; $$->cmd_attr_list = $2; }; thread_list: thread { $$ = $1; }| thread_list thread { cmd_t *list = NULL; cmd_t *list_end = NULL; /* Find end of list */ for (list = $1; list != NULL; list = list->cmd_next) list_end = list; list_end->cmd_next = $2; filebench_log(LOG_DEBUG_IMPL, "thread_list adding cmd %zx to list %zx", $2, $1); $$ = $1; }; proc_define_command: FSC_DEFINE FSE_PROC pt_attr_ops FSK_OPENLST thread_list FSK_CLOSELST { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = &parser_proc_define; $$->cmd_list = $5; $$->cmd_attr_list = $3; } | proc_define_command pt_attr_ops { $1->cmd_attr_list = $2; }; files_define_command: FSC_DEFINE FSE_FILE { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = &parser_file_define; }| FSC_DEFINE FSE_FILESET { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = &parser_fileset_define; } | files_define_command files_attr_ops { $1->cmd_attr_list = $2; }; randvar_define_command: FSC_DEFINE FSE_RAND randvar_attr_ops { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = &parser_randvar_define; $$->cmd_attr_list = $3; }; fo_define_command: FSC_DEFINE FSC_FLOWOP comp_attr_ops FSK_OPENLST flowop_list FSK_CLOSELST { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = &parser_composite_flowop_define; $$->cmd_list = $5; $$->cmd_attr_list = $3; } | fo_define_command comp_attr_ops { $1->cmd_attr_list = $2; }; create_command: FSC_CREATE entity { if (($$ = alloc_cmd()) == NULL) YYERROR; switch ($2) { case FSE_PROC: $$->cmd = &parser_proc_create; break; case FSE_FILESET: case FSE_FILE: $$->cmd = &parser_fileset_create; break; default: filebench_log(LOG_ERROR, "unknown entity", $2); YYERROR; } }; shutdown_command: FSC_SHUTDOWN entity { if (($$ = alloc_cmd()) == NULL) YYERROR; switch ($2) { case FSE_PROC: $$->cmd = &parser_proc_shutdown; break; case FSE_FILE: case FSE_FILESET: $$->cmd = &parser_fileset_shutdown; break; default: filebench_log(LOG_ERROR, "unknown entity", $2); YYERROR; } }; warmup_command: FSC_WARMUP FSV_VAL_INT { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = parser_warmup; $$->cmd_qty = $2; } | FSC_WARMUP FSV_VARIABLE { fbint_t *integer; if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = parser_warmup_variable; $$->cmd_tgt1 = fb_stralloc($2); }; sleep_command: FSC_SLEEP FSV_VAL_INT { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = parser_sleep; $$->cmd_qty = $2; } | FSC_SLEEP FSV_VARIABLE { fbint_t *integer; if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = parser_sleep_variable; $$->cmd_tgt1 = fb_stralloc($2); }; run_command: FSC_RUN FSV_VAL_INT { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = parser_run; $$->cmd_qty = $2; } | FSC_RUN FSV_VARIABLE { fbint_t *integer; if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = parser_run_variable; $$->cmd_tgt1 = fb_stralloc($2); } | FSC_RUN { fbint_t *integer; if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = parser_run; $$->cmd_qty = 60UL; }; help_command: FSC_HELP { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd = parser_help; }; flowop_command: FSC_FLOWOP name { if (($$ = alloc_cmd()) == NULL) YYERROR; $$->cmd_name = fb_stralloc($2); } | flowop_command fo_attr_ops { $1->cmd_attr_list = $2; }; load_command: FSC_LOAD FSV_STRING { FILE *newfile; char loadfile[128]; if (($$ = alloc_cmd()) == NULL) YYERROR; (void) strcpy(loadfile, $2); (void) strcat(loadfile, ".f"); if ((newfile = fopen(loadfile, "r")) == NULL) { (void) strcpy(loadfile, fbbasepath); (void) strcat(loadfile, "/workloads/"); (void) strcat(loadfile, $2); (void) strcat(loadfile, ".f"); if ((newfile = fopen(loadfile, "r")) == NULL) { filebench_log(LOG_ERROR, "Cannot open %s", loadfile); YYERROR; } } parentscript = yyin; yyin = newfile; yy_switchfileparent(yyin); }; entity: FSE_PROC {$$ = FSE_PROC;} | FSE_THREAD {$$ = FSE_THREAD;} | FSE_FILESET {$$ = FSE_FILESET;} | FSE_FILE {$$ = FSE_FILE;}; value: FSV_VAL_INT { $$.i = $1;} | FSV_STRING { $$.s = $1;} | FSV_VAL_BOOLEAN { $$.b = $1;}; name: FSV_STRING; /* attribute parsing for define file and define fileset */ files_attr_ops: files_attr_op { $$ = $1; } | files_attr_ops FSK_SEPLST files_attr_op { attr_t *attr = NULL; attr_t *list_end = NULL; for (attr = $1; attr != NULL; attr = attr->attr_next) list_end = attr; /* Find end of list */ list_end->attr_next = $3; $$ = $1; }; files_attr_op: files_attr_name FSK_ASSIGN attr_list_value { $$ = $3; $$->attr_name = $1; } | files_attr_name { if (($$ = alloc_attr()) == NULL) YYERROR; $$->attr_name = $1; }; /* attribute parsing for random variables */ randvar_attr_ops: randvar_attr_op { $$ = $1; } | randvar_attr_ops FSK_SEPLST randvar_attr_op { attr_t *attr = NULL; attr_t *list_end = NULL; for (attr = $1; attr != NULL; attr = attr->attr_next) list_end = attr; /* Find end of list */ list_end->attr_next = $3; $$ = $1; } | randvar_attr_ops FSK_SEPLST FSA_RANDTABLE FSK_ASSIGN FSK_OPENLST probtabentry_list FSK_CLOSELST { attr_t *attr = NULL; attr_t *list_end = NULL; for (attr = $1; attr != NULL; attr = attr->attr_next) list_end = attr; /* Find end of list */ if ((attr = alloc_attr()) == NULL) YYERROR; attr->attr_name = FSA_RANDTABLE; attr->attr_obj = (void *)$6; list_end->attr_next = attr; $$ = $1; }; randvar_attr_op: randvar_attr_name FSK_ASSIGN attr_list_value { $$ = $3; $$->attr_name = $1; } | randvar_attr_name { if (($$ = alloc_attr()) == NULL) YYERROR; $$->attr_name = $1; } | FSA_TYPE FSK_ASSIGN randvar_attr_typop { $$ = $3; $$->attr_name = FSA_TYPE; } | FSA_RANDSRC FSK_ASSIGN randvar_attr_srcop { $$ = $3; $$->attr_name = FSA_RANDSRC; }; probtabentry: FSK_OPENLST var_int_val FSK_SEPLST var_int_val FSK_SEPLST var_int_val FSK_CLOSELST { if (($$ = alloc_probtabent()) == NULL) YYERROR; $$->pte_percent = $2; $$->pte_segmin = $4; $$->pte_segmax = $6; }; /* attribute parsing for prob density function table */ probtabentry_list: probtabentry { $$ = $1; } | probtabentry_list FSK_SEPLST probtabentry { probtabent_t *pte = NULL; probtabent_t *ptelist_end = NULL; for (pte = $1; pte != NULL; pte = pte->pte_next) ptelist_end = pte; /* Find end of prob table entry list */ ptelist_end->pte_next = $3; $$ = $1; }; /* attribute parsing for define thread and process */ pt_attr_ops: pt_attr_op { $$ = $1; } | pt_attr_ops FSK_SEPLST pt_attr_op { attr_t *attr = NULL; attr_t *list_end = NULL; for (attr = $1; attr != NULL; attr = attr->attr_next) list_end = attr; /* Find end of list */ list_end->attr_next = $3; $$ = $1; }; pt_attr_op: pt_attr_name FSK_ASSIGN attr_value { $$ = $3; $$->attr_name = $1; } | pt_attr_name { if (($$ = alloc_attr()) == NULL) YYERROR; $$->attr_name = $1; }; /* attribute parsing for flowops */ fo_attr_ops: fo_attr_op { $$ = $1; } | fo_attr_ops FSK_SEPLST fo_attr_op { attr_t *attr = NULL; attr_t *list_end = NULL; for (attr = $1; attr != NULL; attr = attr->attr_next) list_end = attr; /* Find end of list */ list_end->attr_next = $3; $$ = $1; } | fo_attr_ops FSK_SEPLST comp_lvar_def { attr_t *attr = NULL; attr_t *list_end = NULL; for (attr = $1; attr != NULL; attr = attr->attr_next) list_end = attr; /* Find end of list */ list_end->attr_next = $3; $$ = $1; }; fo_attr_op: fo_attr_name FSK_ASSIGN attr_value { $$ = $3; $$->attr_name = $1; } | fo_attr_name { if (($$ = alloc_attr()) == NULL) YYERROR; $$->attr_name = $1; }; /* attribute parsing for Event Generator */ ev_attr_ops: ev_attr_op { $$ = $1; } | ev_attr_ops FSK_SEPLST ev_attr_op { attr_t *attr = NULL; attr_t *list_end = NULL; for (attr = $1; attr != NULL; attr = attr->attr_next) list_end = attr; /* Find end of list */ list_end->attr_next = $3; $$ = $1; }; ev_attr_op: ev_attr_name FSK_ASSIGN attr_value { $$ = $3; $$->attr_name = $1; } | ev_attr_name { if (($$ = alloc_attr()) == NULL) YYERROR; $$->attr_name = $1; }; /* attribute parsing for enable multiple client command */ enable_multi_ops: enable_multi_op { $$ = $1; } | enable_multi_ops FSK_SEPLST enable_multi_op { attr_t *attr = NULL; attr_t *list_end = NULL; for (attr = $1; attr != NULL; attr = attr->attr_next) list_end = attr; /* Find end of list */ list_end->attr_next = $3; $$ = $1; }; enable_multi_op: em_attr_name FSK_ASSIGN attr_value { $$ = $3; $$->attr_name = $1; }; multisync_op: FSA_VALUE FSK_ASSIGN attr_value { $$ = $3; $$->attr_name = FSA_VALUE; }; fscheck_attr_op: fscheck_attr_name FSK_ASSIGN FSV_STRING { if (($$ = alloc_attr()) == NULL) YYERROR; $$->attr_avd = avd_str_alloc($3); $$->attr_name = $1; }; binary_op: FSK_PLUS {$$ = FSK_PLUS;} | FSK_MINUS {$$ = FSK_MINUS;} | FSK_MULTIPLY {$$ = FSK_MULTIPLY;} | FSK_DIVIDE {$$ = FSK_DIVIDE;}; files_attr_name: attrs_define_file |attrs_define_fileset; pt_attr_name: attrs_define_thread |attrs_define_proc; fo_attr_name: attrs_flowop; ev_attr_name: attrs_eventgen; attrs_define_proc: FSA_NICE { $$ = FSA_NICE;} | FSA_NAME { $$ = FSA_NAME;} | FSA_INSTANCES { $$ = FSA_INSTANCES;}; attrs_define_file: FSA_SIZE { $$ = FSA_SIZE;} | FSA_NAME { $$ = FSA_NAME;} | FSA_PATH { $$ = FSA_PATH;} | FSA_READONLY { $$ = FSA_READONLY;} | FSA_TRUSTTREE { $$ = FSA_TRUSTTREE;} | FSA_REUSE { $$ = FSA_REUSE;} | FSA_PREALLOC { $$ = FSA_PREALLOC;} | FSA_PARALLOC { $$ = FSA_PARALLOC;}; attrs_define_fileset: FSA_SIZE { $$ = FSA_SIZE;} | FSA_NAME { $$ = FSA_NAME;} | FSA_PATH { $$ = FSA_PATH;} | FSA_DIRWIDTH { $$ = FSA_DIRWIDTH;} | FSA_DIRDEPTHRV { $$ = FSA_DIRDEPTHRV;} | FSA_PREALLOC { $$ = FSA_PREALLOC;} | FSA_PARALLOC { $$ = FSA_PARALLOC;} | FSA_REUSE { $$ = FSA_REUSE;} | FSA_READONLY { $$ = FSA_READONLY;} | FSA_TRUSTTREE { $$ = FSA_TRUSTTREE;} | FSA_FILESIZEGAMMA { $$ = FSA_FILESIZEGAMMA;} | FSA_DIRGAMMA { $$ = FSA_DIRGAMMA;} | FSA_CACHED { $$ = FSA_CACHED;} | FSA_ENTRIES { $$ = FSA_ENTRIES;} | FSA_LEAFDIRS { $$ = FSA_LEAFDIRS;}; randvar_attr_name: FSA_NAME { $$ = FSA_NAME;} | FSA_RANDSEED { $$ = FSA_RANDSEED;} | FSA_RANDGAMMA { $$ = FSA_RANDGAMMA;} | FSA_RANDMEAN { $$ = FSA_RANDMEAN;} | FSA_RANDMIN { $$ = FSA_RANDMIN;} | FSA_RANDROUND { $$ = FSA_RANDROUND;}; randvar_attr_tsp: FSS_TYPE { $$ = FSS_TYPE;} | FSS_SRC { $$ = FSS_SRC;} | FSS_SEED { $$ = FSS_SEED;} | FSS_GAMMA { $$ = FSS_GAMMA;} | FSS_MEAN { $$ = FSS_MEAN;} | FSS_MIN { $$ = FSS_MIN;} | FSS_ROUND { $$ = FSS_ROUND;}; randvar_attr_param: FSS_SEED { $$ = FSS_SEED;} | FSS_GAMMA { $$ = FSS_GAMMA;} | FSS_MEAN { $$ = FSS_MEAN;} | FSS_MIN { $$ = FSS_MIN;} | FSS_ROUND { $$ = FSS_ROUND;}; randvar_attr_typop: randtype_name { if (($$ = alloc_attr()) == NULL) YYERROR; $$->attr_avd = avd_int_alloc($1); }; randtype_name: FSV_RANDUNI { $$ = FSV_RANDUNI;} | FSV_RANDTAB { $$ = FSV_RANDTAB;} | FSA_RANDGAMMA { $$ = FSA_RANDGAMMA;}; randvar_attr_srcop: randsrc_name { if (($$ = alloc_attr()) == NULL) YYERROR; $$->attr_avd = avd_int_alloc($1); }; randsrc_name: FSV_URAND { $$ = FSV_URAND;} | FSV_RAND48 { $$ = FSV_RAND48;}; attrs_define_thread: FSA_PROCESS { $$ = FSA_PROCESS;} | FSA_NAME { $$ = FSA_NAME;} | FSA_MEMSIZE { $$ = FSA_MEMSIZE;} | FSA_USEISM { $$ = FSA_USEISM;} | FSA_INSTANCES { $$ = FSA_INSTANCES;}; attrs_flowop: FSA_WSS { $$ = FSA_WSS;} | FSA_FILE { $$ = FSA_FILE;} | FSA_NAME { $$ = FSA_NAME;} | FSA_RANDOM { $$ = FSA_RANDOM;} | FSA_FD { $$ = FSA_FD;} | FSA_SRCFD { $$ = FSA_SRCFD;} | FSA_ROTATEFD { $$ = FSA_ROTATEFD;} | FSA_DSYNC { $$ = FSA_DSYNC;} | FSA_DIRECTIO { $$ = FSA_DIRECTIO;} | FSA_INDEXED { $$ = FSA_INDEXED;} | FSA_TARGET { $$ = FSA_TARGET;} | FSA_ITERS { $$ = FSA_ITERS;} | FSA_VALUE { $$ = FSA_VALUE;} | FSA_BLOCKING { $$ = FSA_BLOCKING;} | FSA_HIGHWATER { $$ = FSA_HIGHWATER;} | FSA_IOSIZE { $$ = FSA_IOSIZE;}; attrs_eventgen: FSA_RATE { $$ = FSA_RATE;}; em_attr_name: FSA_MASTER { $$ = FSA_MASTER;} | FSA_CLIENT { $$ = FSA_CLIENT;}; fscheck_attr_name: FSA_PATH { $$ = FSA_PATH;} | FSA_FSTYPE { $$ = FSA_FSTYPE;}; comp_attr_ops: comp_attr_op { $$ = $1; } | comp_attr_ops FSK_SEPLST comp_attr_op { attr_t *attr = NULL; attr_t *list_end = NULL; for (attr = $1; attr != NULL; attr = attr->attr_next) list_end = attr; /* Find end of list */ list_end->attr_next = $3; $$ = $1; } | comp_attr_ops FSK_SEPLST comp_lvar_def { attr_t *attr = NULL; attr_t *list_end = NULL; for (attr = $1; attr != NULL; attr = attr->attr_next) list_end = attr; /* Find end of list */ list_end->attr_next = $3; $$ = $1; }; comp_attr_op: attrs_define_comp FSK_ASSIGN attr_value { $$ = $3; $$->attr_name = $1; }; comp_lvar_def: FSV_VARIABLE FSK_ASSIGN FSV_VAL_BOOLEAN { if (($$ = alloc_lvar_attr(var_lvar_assign_boolean($1, $3))) == NULL) YYERROR; } | FSV_VARIABLE FSK_ASSIGN FSV_VAL_INT { if (($$ = alloc_lvar_attr(var_lvar_assign_integer($1, $3))) == NULL) YYERROR; } | FSV_VARIABLE FSK_ASSIGN FSK_QUOTE FSV_WHITESTRING FSK_QUOTE { if (($$ = alloc_lvar_attr(var_lvar_assign_string($1, $4))) == NULL) YYERROR; } | FSV_VARIABLE FSK_ASSIGN FSV_STRING { if (($$ = alloc_lvar_attr(var_lvar_assign_string($1, $3))) == NULL) YYERROR; } | FSV_VARIABLE FSK_ASSIGN FSV_VARIABLE { if (($$ = alloc_lvar_attr(var_lvar_assign_var($1, $3))) == NULL) YYERROR; } | FSV_VARIABLE { if (($$ = alloc_lvar_attr(var_lvar_alloc_local($1))) == NULL) YYERROR; }; attrs_define_comp: FSA_NAME { $$ = FSA_NAME;} | FSA_ITERS { $$ = FSA_ITERS;}; attr_value: FSV_STRING { if (($$ = alloc_attr()) == NULL) YYERROR; $$->attr_avd = avd_str_alloc($1); } | FSV_VAL_INT { if (($$ = alloc_attr()) == NULL) YYERROR; $$->attr_avd = avd_int_alloc($1); } | FSV_VAL_BOOLEAN { if (($$ = alloc_attr()) == NULL) YYERROR; $$->attr_avd = avd_bool_alloc($1); } | FSV_VARIABLE { if (($$ = alloc_attr()) == NULL) YYERROR; $$->attr_avd = var_ref_attr($1); }; attr_list_value: var_string_list { if (($$ = alloc_attr()) == NULL) YYERROR; $$->attr_param_list = $1; } | FSV_STRING { if (($$ = alloc_attr()) == NULL) YYERROR; $$->attr_avd = avd_str_alloc($1); } | FSV_VAL_INT { if (($$ = alloc_attr()) == NULL) YYERROR; $$->attr_avd = avd_int_alloc($1); } | FSV_VAL_BOOLEAN { if (($$ = alloc_attr()) == NULL) YYERROR; $$->attr_avd = avd_bool_alloc($1); } | FSV_VARIABLE { if (($$ = alloc_attr()) == NULL) YYERROR; $$->attr_avd = var_ref_attr($1); }; var_int_val: FSV_VAL_INT { $$ = avd_int_alloc($1); } | FSV_VARIABLE { $$ = var_ref_attr($1); }; %% /* * The following 'c' routines implement the various commands defined in the * above yacc parser code. The yacc portion checks the syntax of the commands * found in a workload file, or typed on interactive command lines, parsing * the commands' parameters into lists. The lists are then passed in a cmd_t * struct for each command to its related routine in the following section * for actual execution. This section also includes a few utility routines * and the main entry point for the program. */ /* * Entry point for filebench. Processes command line arguements. The -f * option will read in a workload file (the full name and extension must * must be given). The -a, -s, -m and -i options are used by worker process * to receive their name, the base address of shared memory, its path, and * the process' instance number, respectively. This information is supplied * by the master process when it execs worker processes under the process * model of execution. If the worker process arguments are passed then main * will call the procflow_exec routine which creates worker threadflows and * flowops and executes the procflow's portion of the workload model until * completion. If worker process arguments are not passed to the process, * then it becomes the master process for a filebench run. It initializes * the various filebench components and either executes the supplied workload * file, or enters interactive mode. */ int main(int argc, char *argv[]) { int opt; int docmd = FS_FALSE; int instance; char procname[128]; caddr_t shmaddr; char dir[MAXPATHLEN]; #ifdef HAVE_SETRLIMIT struct rlimit rlp; #endif #ifdef HAVE_LIBTECLA char *line; #else char line[1024]; #endif char shmpathtmp[1024]; #ifdef HAVE_SETRLIMIT /* Set resource limits */ (void) getrlimit(RLIMIT_NOFILE, &rlp); rlp.rlim_cur = rlp.rlim_max; setrlimit(RLIMIT_NOFILE, &rlp); #endif yydebug = 0; execname = argv[0]; *procname = 0; cwd = getcwd(dir, MAXPATHLEN); while ((opt = getopt(argc, argv, cmd_options)) != (int)EOF) { switch (opt) { case 'h': usage(2); break; case 'p': noproc = 1; break; case 'f': if (optarg == NULL) usage(1); if ((yyin = fopen(optarg, "r")) == NULL) { (void) fprintf(stderr, "Cannot open file %s", optarg); exit(1); } dofile = FS_TRUE; fscriptname = optarg; break; case 'a': if (optarg == NULL) usage(1); sscanf(optarg, "%s", &procname[0]); break; case 's': if (optarg == NULL) usage(1); #if defined(_LP64) || (__WORDSIZE == 64) sscanf(optarg, "%llx", &shmaddr); #else sscanf(optarg, "%x", &shmaddr); #endif break; case 'm': if (optarg == NULL) usage(1); sscanf(optarg, "%s", shmpathtmp); shmpath = shmpathtmp; break; case 'i': if (optarg == NULL) usage(1); sscanf(optarg, "%d", &instance); break; case '?': default: usage(1); break; } } #ifdef USE_PROCESS_MODEL if (!(*procname)) #endif printf("FileBench Version %s\n", FILEBENCH_VERSION); filebench_init(); /* get process pid for use with message logging */ my_pid = getpid(); #ifdef USE_PROCESS_MODEL if (*procname) { /* A child FileBench instance */ if (ipc_attach(shmaddr) < 0) { filebench_log(LOG_ERROR, "Cannot attach shm for %s", procname); exit(1); } /* get correct function pointer for each child process */ filebench_plugin_funcvecinit(); if (procflow_exec(procname, instance) < 0) { filebench_log(LOG_ERROR, "Cannot startup process %s", procname); exit(1); } exit(0); } #endif /* master (or only) process */ ipc_init(); if (fscriptname) (void) strcpy(filebench_shm->shm_fscriptname, fscriptname); filebench_plugin_funcvecinit(); flowop_init(); stats_init(); eventgen_init(); signal(SIGINT, parser_abort); if (dofile) yyparse(); else { #ifdef HAVE_LIBTECLA if ((gl = new_GetLine(MAX_LINE_LEN, MAX_CMD_HIST)) == NULL) { filebench_log(LOG_ERROR, "Failed to create GetLine object"); filebench_shutdown(1); } if (gl_customize_completion(gl, NULL, command_complete)) { filebench_log(LOG_ERROR, "Failed to register auto-completion function"); filebench_shutdown(1); } while (line = gl_get_line(gl, FILEBENCH_PROMPT, NULL, -1)) { arg_parse(line); yyparse(); } del_GetLine(gl); #else while (!feof(stdin)) { printf(FILEBENCH_PROMPT); fflush(stdout); if (fgets(line, sizeof (line), stdin) == NULL) { if (errno == EINTR) continue; else break; } arg_parse(line); yyparse(); } printf("\n"); #endif /* HAVE_LIBTECLA */ } parser_filebench_shutdown((cmd_t *)0); return (0); } /* * arg_parse() puts the parser into command parsing mode. Create a tmpfile * and instruct the parser to read instructions from this location by setting * yyin to the value returned by tmpfile. Write the command into the file. * Then seek back to to the start of the file so that the parser can read * the instructions. */ static void arg_parse(const char *command) { if ((yyin = tmpfile()) == NULL) { filebench_log(LOG_FATAL, "Exiting: Cannot create tmpfile: %s", strerror(errno)); exit(1); } if (fwrite(command, strlen(command), 1, yyin) != 1) filebench_log(LOG_FATAL, "Cannot write tmpfile: %s", strerror(errno)); if (fseek(yyin, 0, SEEK_SET) != 0) filebench_log(LOG_FATAL, "Cannot seek tmpfile: %s", strerror(errno)); } /* * Converts a list of var_strings or ordinary strings to a single ordinary * string. It returns a pointer to the string (in malloc'd memory) if found, * or NULL otherwise. */ char * parser_list2string(list_t *list) { list_t *l; char *string; char *tmp; fbint_t *integer; if ((string = malloc(MAXPATHLEN)) == NULL) { filebench_log(LOG_ERROR, "Failed to allocate memory"); return (NULL); } *string = 0; /* printf("parser_list2string: called\n"); */ /* Format args */ for (l = list; l != NULL; l = l->list_next) { char *lstr = avd_get_str(l->list_string); filebench_log(LOG_DEBUG_SCRIPT, "converting string '%s'", lstr); /* see if it is a random variable */ if (l->list_integer) { fbint_t param_name; tmp = NULL; param_name = avd_get_int(l->list_integer); switch (param_name) { case FSS_TYPE: tmp = var_randvar_to_string(lstr, RAND_PARAM_TYPE); break; case FSS_SRC: tmp = var_randvar_to_string(lstr, RAND_PARAM_SRC); break; case FSS_SEED: tmp = var_randvar_to_string(lstr, RAND_PARAM_SEED); break; case FSS_MIN: tmp = var_randvar_to_string(lstr, RAND_PARAM_MIN); break; case FSS_MEAN: tmp = var_randvar_to_string(lstr, RAND_PARAM_MEAN); break; case FSS_GAMMA: tmp = var_randvar_to_string(lstr, RAND_PARAM_GAMMA); break; case FSS_ROUND: tmp = var_randvar_to_string(lstr, RAND_PARAM_ROUND); break; } if (tmp) { (void) strcat(string, tmp); free(tmp); } else { (void) strcat(string, lstr); } } else { /* perhaps a normal variable? */ if ((tmp = var_to_string(lstr)) != NULL) { (void) strcat(string, tmp); free(tmp); } else { (void) strcat(string, lstr); } } } return (string); } /* * If the list just contains a single string starting with '$', then find * or create the named var and return the var's var_string component. * Otherwise, convert the list to a string, and allocate a var_string * containing a copy of that string. On failure either returns NULL * or shuts down the run. */ avd_t parser_list2varstring(list_t *list) { char *lstr = avd_get_str(list->list_string); /* printf("parser_list2varstring: Called\n"); */ /* Special case - variable name */ if ((list->list_next == NULL) && (*lstr == '$')) return (var_ref_attr(lstr)); return (avd_str_alloc(parser_list2string(list))); } /* * Looks for the var named in list_string of the first element of the * supplied list. If found, returns the var_val portion of the var in * an attribute value descriptor. If the var is not found, cannot be * allocated, the supplied list is NULL, or the list_string filed is * empty, returns NULL. */ avd_t parser_list2avd(list_t *list) { avd_t avd; char *lstr; if (list && ((lstr = avd_get_str(list->list_string)) != NULL)) { avd = var_ref_attr(lstr); return (avd); } return (NULL); } /* * Sets the event generator rate from the attribute supplied with the * command. If the attribute doesn't exist the routine does nothing. */ static void parser_eventgen(cmd_t *cmd) { attr_t *attr; /* Get the rate from attribute */ if (attr = get_attr_integer(cmd, FSA_RATE)) { if (attr->attr_avd) { eventgen_setrate(attr->attr_avd); } } } /* * Assigns the designated integer variable successive values from the * supplied comma seperated integer list. After each successive integer * assignment, it executes the bracket enclosed list of commands. For * example, repeated runs of a workload with increasing io sizes can * be done using the following command line: * foreach $iosize in 2k, 4k, 8k {run 60} */ static void parser_foreach_integer(cmd_t *cmd) { list_t *list = cmd->cmd_param_list; cmd_t *inner_cmd; for (; list != NULL; list = list->list_next) { fbint_t list_int = avd_get_int(list->list_integer); var_assign_integer(cmd->cmd_tgt1, list_int); filebench_log(LOG_VERBOSE, "Iterating %s=%llu", cmd->cmd_tgt1, (u_longlong_t)list_int); for (inner_cmd = cmd->cmd_list; inner_cmd != NULL; inner_cmd = inner_cmd->cmd_next) { inner_cmd->cmd(inner_cmd); } } } /* * Similar to parser_foreach_integer(), except takes a list of strings after * the "in" token. For example, to run twice using a different directory, * perhaps using a different filesystem, the following command line * could be used: * foreach $dir in "/ufs_top/fbt", "/zfs_top/fbt" {run 60) */ static void parser_foreach_string(cmd_t *cmd) { list_t *list = cmd->cmd_param_list; for (; list != NULL; list = list->list_next) { cmd_t *inner_cmd; char *lstr = avd_get_str(list->list_string); var_assign_string(cmd->cmd_tgt1, lstr); filebench_log(LOG_VERBOSE, "Iterating %s=%s", cmd->cmd_tgt1, lstr); for (inner_cmd = cmd->cmd_list; inner_cmd != NULL; inner_cmd = inner_cmd->cmd_next) { inner_cmd->cmd(inner_cmd); } } } /* * Lists the fileset name, path name and average size for all defined * filesets. */ static void parser_list(cmd_t *cmd) { (void) fileset_iter(fileset_print); } /* * Lists the flowop name and instance number for all flowops. */ static void parser_flowop_list(cmd_t *cmd) { flowop_printall(); } /* * Calls procflow_define() to allocate "instances" number of procflow(s) * (processes) with the supplied name. The default number of instances is * one. An optional priority level attribute can be supplied and is stored in * pf_nice. Finally the routine loops through the list of inner commands, if * any, which are defines for threadflows, and passes them one at a time to * parser_thread_define() to allocate threadflow entities for the process(es). */ static void parser_proc_define(cmd_t *cmd) { procflow_t *procflow, template; char *name; attr_t *attr; avd_t var_instances; fbint_t instances; cmd_t *inner_cmd; /* Get the name of the process */ if (attr = get_attr(cmd, FSA_NAME)) { name = avd_get_str(attr->attr_avd); } else { filebench_log(LOG_ERROR, "define proc: proc specifies no name"); filebench_shutdown(1); } /* Get the memory size from attribute */ if (attr = get_attr_integer(cmd, FSA_INSTANCES)) { if (AVD_IS_RANDOM(attr->attr_avd)) { filebench_log(LOG_ERROR, "proc_define: Instances attr cannot be random"); filebench_shutdown(1); } var_instances = attr->attr_avd; instances = avd_get_int(var_instances); filebench_log(LOG_DEBUG_IMPL, "Setting instances = %llu", (u_longlong_t)instances); } else { filebench_log(LOG_DEBUG_IMPL, "Defaulting to instances = 1"); var_instances = avd_int_alloc(1); instances = 1; } if ((procflow = procflow_define(name, NULL, var_instances)) == NULL) { filebench_log(LOG_ERROR, "Failed to instantiate %d %s process(es)\n", instances, name); filebench_shutdown(1); } /* Get the pri from attribute */ if (attr = get_attr_integer(cmd, FSA_NICE)) { if (AVD_IS_RANDOM(attr->attr_avd)) { filebench_log(LOG_ERROR, "proc_define: priority cannot be random"); filebench_shutdown(1); } filebench_log(LOG_DEBUG_IMPL, "Setting pri = %llu", (u_longlong_t)avd_get_int(attr->attr_avd)); procflow->pf_nice = attr->attr_avd; } else procflow->pf_nice = avd_int_alloc(0); /* Create the list of threads for this process */ for (inner_cmd = cmd->cmd_list; inner_cmd != NULL; inner_cmd = inner_cmd->cmd_next) { parser_thread_define(inner_cmd, procflow, instances); } } /* * Calls threadflow_define() to allocate "instances" number of threadflow(s) * (threads) with the supplied name. The default number of instances is * one. Two other optional attributes may be supplied, one to set the memory * size, stored in tf_memsize, and to select the use of Interprocess Shared * Memory, which sets the THREADFLOW_USEISM flag in tf_attrs. Finally * the routine loops through the list of inner commands, if any, which are * defines for flowops, and passes them one at a time to * parser_flowop_define() to allocate flowop entities for the threadflows. */ static void parser_thread_define(cmd_t *cmd, procflow_t *procflow, int procinstances) { threadflow_t *threadflow, template; attr_t *attr; avd_t instances; cmd_t *inner_cmd; char *name; memset(&template, 0, sizeof (threadflow_t)); /* Get the name of the thread */ if (attr = get_attr(cmd, FSA_NAME)) { name = avd_get_str(attr->attr_avd); } else { filebench_log(LOG_ERROR, "define thread: thread in process %s specifies no name", procflow->pf_name); filebench_shutdown(1); } /* Get the number of instances from attribute */ if (attr = get_attr_integer(cmd, FSA_INSTANCES)) { if (AVD_IS_RANDOM(attr->attr_avd)) { filebench_log(LOG_ERROR, "define thread: Instances attr cannot be random"); filebench_shutdown(1); } filebench_log(LOG_DEBUG_IMPL, "define thread: Setting instances = %llu", (u_longlong_t)avd_get_int(attr->attr_avd)); instances = attr->attr_avd; } else instances = avd_int_alloc(1); /* Get the memory size from attribute */ if (attr = get_attr_integer(cmd, FSA_MEMSIZE)) { if (AVD_IS_RANDOM(attr->attr_avd)) { filebench_log(LOG_ERROR, "define thread: Memory size cannot be random"); filebench_shutdown(1); } filebench_log(LOG_DEBUG_IMPL, "define thread: Setting memsize = %llu", (u_longlong_t)avd_get_int(attr->attr_avd)); template.tf_memsize = attr->attr_avd; } else template.tf_memsize = avd_int_alloc(0); if ((threadflow = threadflow_define(procflow, name, &template, instances)) == NULL) { filebench_log(LOG_ERROR, "define thread: Failed to instantiate thread\n"); filebench_shutdown(1); } /* Use ISM Memory? */ if (attr = get_attr(cmd, FSA_USEISM)) { threadflow->tf_attrs |= THREADFLOW_USEISM; } /* Create the list of flowops */ for (inner_cmd = cmd->cmd_list; inner_cmd != NULL; inner_cmd = inner_cmd->cmd_next) { parser_flowop_define(inner_cmd, threadflow, &threadflow->tf_thrd_fops, FLOW_MASTER); } } /* * Fills in the attributes for a newly allocated flowop */ static void parser_flowop_get_attrs(cmd_t *cmd, flowop_t *flowop) { attr_t *attr; /* Get the filename from attribute */ if (attr = get_attr(cmd, FSA_FILE)) { flowop->fo_filename = attr->attr_avd; if (flowop->fo_filename == NULL) { filebench_log(LOG_ERROR, "define flowop: no filename specfied"); filebench_shutdown(1); } } else { /* no filename attribute specified */ flowop->fo_filename = NULL; } /* Get the iosize of the op */ if (attr = get_attr_integer(cmd, FSA_IOSIZE)) flowop->fo_iosize = attr->attr_avd; else flowop->fo_iosize = avd_int_alloc(0); /* Get the working set size of the op */ if (attr = get_attr_integer(cmd, FSA_WSS)) flowop->fo_wss = attr->attr_avd; else flowop->fo_wss = avd_int_alloc(0); /* Random I/O? */ if (attr = get_attr_bool(cmd, FSA_RANDOM)) flowop->fo_random = attr->attr_avd; else flowop->fo_random = avd_bool_alloc(FALSE); /* Sync I/O? */ if (attr = get_attr_bool(cmd, FSA_DSYNC)) flowop->fo_dsync = attr->attr_avd; else flowop->fo_dsync = avd_bool_alloc(FALSE); /* Target, for wakeup etc */ if (attr = get_attr(cmd, FSA_TARGET)) (void) strcpy(flowop->fo_targetname, avd_get_str(attr->attr_avd)); /* Value */ if (attr = get_attr_integer(cmd, FSA_VALUE)) flowop->fo_value = attr->attr_avd; else flowop->fo_value = avd_int_alloc(0); /* FD */ if (attr = get_attr_integer(cmd, FSA_FD)) { flowop->fo_fdnumber = avd_get_int(attr->attr_avd); if (flowop->fo_filename != NULL) filebench_log(LOG_DEBUG_SCRIPT, "It is not " "advisable to supply both an fd number " "and a fileset name in most cases"); } /* Rotatefd? */ if (attr = get_attr_bool(cmd, FSA_ROTATEFD)) flowop->fo_rotatefd = attr->attr_avd; else flowop->fo_rotatefd = avd_bool_alloc(FALSE); /* SRC FD, for copies etc... */ if (attr = get_attr_integer(cmd, FSA_SRCFD)) flowop->fo_srcfdnumber = avd_get_int(attr->attr_avd); /* Blocking operation? */ if (attr = get_attr_bool(cmd, FSA_BLOCKING)) flowop->fo_blocking = attr->attr_avd; else flowop->fo_blocking = avd_bool_alloc(FALSE); /* Direct I/O Operation */ if (attr = get_attr_bool(cmd, FSA_DIRECTIO)) flowop->fo_directio = attr->attr_avd; else flowop->fo_directio = avd_bool_alloc(FALSE); /* Highwater mark */ if (attr = get_attr_integer(cmd, FSA_HIGHWATER)) { flowop->fo_highwater = attr->attr_avd; if (AVD_IS_RANDOM(attr->attr_avd)) { filebench_log(LOG_ERROR, "define flowop: Highwater attr cannot be random"); filebench_shutdown(1); } } else { flowop->fo_highwater = avd_int_alloc(1); } /* find file or leaf directory by index number */ if (attr = get_attr_integer(cmd, FSA_INDEXED)) flowop->fo_fileindex = attr->attr_avd; else flowop->fo_fileindex = NULL; } /* * defines the FLOW_MASTER flowops within a FLOW_MASTER instance of * a composit flowop. Default attributes from the FLOW_INNER_DEF instances * of the composit flowop's inner flowops are used if set. Otherwise * default attributes from the FLOW_MASTER instance of the composit flowop * are used, which may include defaults from the original FLOW_DEFINITION * of the composit flowop. */ static void parser_inner_flowop_define(threadflow_t *thread, flowop_t *comp0_flow, flowop_t *comp_mstr_flow) { flowop_t *inner_flowtype, *inner_flowop; /* follow flowop list, creating composit names */ inner_flowtype = comp0_flow->fo_comp_fops; comp_mstr_flow->fo_comp_fops = NULL; while (inner_flowtype) { char fullname[MAXPATHLEN]; /* create composite_name.name for new flowop */ snprintf(fullname, MAXPATHLEN, "%s.%s", comp_mstr_flow->fo_name, inner_flowtype->fo_name); if ((inner_flowop = flowop_define(thread, fullname, inner_flowtype, &comp_mstr_flow->fo_comp_fops, FLOW_MASTER, 0)) == NULL) { filebench_log(LOG_ERROR, "define flowop: Failed to instantiate flowop %s\n", fullname); filebench_shutdown(1); } /* if applicable, update filename attribute */ if (inner_flowop->fo_filename) { char *name; /* fix up avd_t */ avd_update(&inner_flowop->fo_filename, comp_mstr_flow->fo_lvar_list); /* see if ready to get the file or fileset */ name = avd_get_str(inner_flowop->fo_filename); if (name) { inner_flowop->fo_fileset = fileset_find(name); if (inner_flowop->fo_fileset == NULL) { filebench_log(LOG_ERROR, "inr flowop %s: file %s not found", inner_flowop->fo_name, name); filebench_shutdown(1); } } } /* update attributes from local variables */ avd_update(&inner_flowop->fo_iters, comp_mstr_flow->fo_lvar_list); /* if the inner flowop is a composit flowop, recurse */ if (inner_flowtype->fo_type == FLOW_TYPE_COMPOSITE) { var_t *newlvar, *proto_lvars, *lvar_ptr; proto_lvars = inner_flowop->fo_lvar_list; inner_flowop->fo_lvar_list = 0; for (lvar_ptr = inner_flowtype->fo_lvar_list; lvar_ptr; lvar_ptr = lvar_ptr->var_next) { if ((newlvar = var_lvar_alloc_local( lvar_ptr->var_name)) != NULL) { add_lvar_to_list(newlvar, &inner_flowop->fo_lvar_list); var_update_comp_lvars(newlvar, proto_lvars, comp_mstr_flow->fo_lvar_list); } } parser_inner_flowop_define(thread, inner_flowtype, inner_flowop); inner_flowtype = inner_flowtype->fo_exec_next; continue; } avd_update(&inner_flowop->fo_iosize, comp_mstr_flow->fo_lvar_list); avd_update(&inner_flowop->fo_wss, comp_mstr_flow->fo_lvar_list); avd_update(&inner_flowop->fo_iters, comp_mstr_flow->fo_lvar_list); avd_update(&inner_flowop->fo_value, comp_mstr_flow->fo_lvar_list); avd_update(&inner_flowop->fo_random, comp_mstr_flow->fo_lvar_list); avd_update(&inner_flowop->fo_dsync, comp_mstr_flow->fo_lvar_list); avd_update(&inner_flowop->fo_rotatefd, comp_mstr_flow->fo_lvar_list); avd_update(&inner_flowop->fo_blocking, comp_mstr_flow->fo_lvar_list); avd_update(&inner_flowop->fo_directio, comp_mstr_flow->fo_lvar_list); avd_update(&inner_flowop->fo_highwater, comp_mstr_flow->fo_lvar_list); inner_flowtype = inner_flowtype->fo_exec_next; } } /* * Calls flowop_define() to allocate a flowop with the supplied name. * The allocated flowop inherits attributes from a base flowop of the * same type. If the new flowop has a file or fileset attribute specified, * it must specify a defined fileobj or fileset or an error will be logged. * The new flowop may also have the following attributes set by * the program: * - file size (fo_iosize) * - working set size (fo_wss) * - do random io (fo_random) * - do synchronous io (fo_dsync) * - perform each operation multiple times before advancing (fo_iter) * - target name (fo_targetname) * - An integer value (fo_value) * - a file descriptor (fo_fd) * - specify to rotate file descriptors (fo_rotatefd) * - a source fd (fo_srcfdnumber) * - specify a blocking operation (fo_blocking) * - specify a highwater mark (fo_highwater) * * After all the supplied attributes are stored in their respective locations * in the flowop object, the flowop's init function is called. No errors are * returned, but the filebench run will be terminated if the flowtype is not * specified, a name for the new flowop is not supplied, the flowop_define * call fails, or a file or fileset name is supplied but the corresponding * fileobj or fileset cannot be located. */ static void parser_flowop_define(cmd_t *cmd, threadflow_t *thread, flowop_t **flowoplist_hdp, int category) { flowop_t *flowop, *flowop_type; char *type = (char *)cmd->cmd_name; char *name; attr_t *attr; /* Get the inherited flowop */ flowop_type = flowop_find(type); if (flowop_type == NULL) { filebench_log(LOG_ERROR, "define flowop: flowop type %s not found", type); filebench_shutdown(1); } /* Get the name of the flowop */ if (attr = get_attr(cmd, FSA_NAME)) { name = avd_get_str(attr->attr_avd); } else { filebench_log(LOG_ERROR, "define flowop: flowop %s specifies no name", flowop_type->fo_name); filebench_shutdown(1); } if ((flowop = flowop_define(thread, name, flowop_type, flowoplist_hdp, category, 0)) == NULL) { filebench_log(LOG_ERROR, "define flowop: Failed to instantiate flowop %s\n", cmd->cmd_name); filebench_shutdown(1); } /* Iterations */ if (attr = get_attr_integer(cmd, FSA_ITERS)) flowop->fo_iters = attr->attr_avd; else flowop->fo_iters = avd_int_alloc(1); /* if this is a use of a composit flowop, create inner FLOW MASTERS */ if (flowop_type->fo_type == FLOW_TYPE_COMPOSITE) { get_attr_lvars(cmd, flowop); if (category == FLOW_MASTER) parser_inner_flowop_define(thread, flowop_type, flowop); } else { parser_flowop_get_attrs(cmd, flowop); } } static void parser_composite_flowop_define(cmd_t *cmd) { flowop_t *flowop; cmd_t *inner_cmd; char *name; attr_t *attr; /* Get the name of the flowop */ if (attr = get_attr(cmd, FSA_NAME)) { name = avd_get_str(attr->attr_avd); } else { filebench_log(LOG_ERROR, "define flowop: Composit flowop specifies no name"); filebench_shutdown(1); } if ((flowop = flowop_new_composite_define(name)) == NULL) { filebench_log(LOG_ERROR, "define flowop: Failed to instantiate flowop %s\n", cmd->cmd_name); filebench_shutdown(1); } /* place any local var_t variables on the flowop's local list */ get_attr_lvars(cmd, flowop); /* Iterations */ if (attr = get_attr_integer(cmd, FSA_ITERS)) flowop->fo_iters = attr->attr_avd; else flowop->fo_iters = avd_int_alloc(1); /* define inner flowops */ for (inner_cmd = cmd->cmd_list; inner_cmd != NULL; inner_cmd = inner_cmd->cmd_next) { parser_flowop_define(inner_cmd, NULL, &flowop->fo_comp_fops, FLOW_INNER_DEF); } } /* * Calls fileset_define() to allocate a fileset with the supplied name and * initializes the fileset's pathname attribute, and optionally the * fileset_cached, fileset_reuse, fileset_prealloc and fileset_size attributes. * */ static fileset_t * parser_fileset_define_common(cmd_t *cmd) { fileset_t *fileset; avd_t name; attr_t *attr; avd_t pathname; /* * Make sure all plugin flowops are initialized. * Defaults to local fs for now */ flowop_plugin_flowinit(); /* Get the name of the file */ if (attr = get_attr_fileset(cmd, FSA_NAME)) { name = attr->attr_avd; } else { filebench_log(LOG_ERROR, "define fileset: file or fileset specifies no name"); return (NULL); } if ((fileset = fileset_define(name)) == NULL) { filebench_log(LOG_ERROR, "define file: failed to instantiate file %s\n", avd_get_str(name)); return (NULL); } /* Get the pathname from attribute */ if ((attr = get_attr(cmd, FSA_PATH)) == NULL) { filebench_log(LOG_ERROR, "define file: no pathname specified"); return (NULL); } /* Expand variables in pathname */ if ((pathname = parser_list2varstring(attr->attr_param_list)) == NULL) { filebench_log(LOG_ERROR, "Cannot interpret path"); return (NULL); } fileset->fs_path = pathname; /* How much should we preallocate? */ if ((attr = get_attr_integer(cmd, FSA_PREALLOC)) && attr->attr_avd) { if (AVD_IS_RANDOM(attr->attr_avd)) { filebench_log(LOG_ERROR, "define fileset: Prealloc attr cannot be random"); filebench_shutdown(1); } fileset->fs_preallocpercent = attr->attr_avd; } else if (attr && !attr->attr_avd) { fileset->fs_preallocpercent = avd_int_alloc(100); } else { fileset->fs_preallocpercent = avd_int_alloc(0); } /* Should we preallocate? */ if (attr = get_attr_bool(cmd, FSA_PREALLOC)) fileset->fs_prealloc = attr->attr_avd; else fileset->fs_prealloc = avd_bool_alloc(FALSE); /* Should we prealloc in parallel? */ if (attr = get_attr_bool(cmd, FSA_PARALLOC)) fileset->fs_paralloc = attr->attr_avd; else fileset->fs_paralloc = avd_bool_alloc(FALSE); /* Should we allow writes to the file? */ if (attr = get_attr_bool(cmd, FSA_READONLY)) fileset->fs_readonly = attr->attr_avd; else fileset->fs_readonly = avd_bool_alloc(FALSE); /* Should we reuse the existing file? */ if (attr = get_attr_bool(cmd, FSA_REUSE)) fileset->fs_reuse = attr->attr_avd; else fileset->fs_reuse = avd_bool_alloc(FALSE); /* Should we check for files actual existance? */ if (attr = get_attr_bool(cmd, FSA_TRUSTTREE)) fileset->fs_trust_tree = attr->attr_avd; else fileset->fs_trust_tree = avd_bool_alloc(FALSE); /* Should we leave in cache? */ if (attr = get_attr_bool(cmd, FSA_CACHED)) fileset->fs_cached = attr->attr_avd; else fileset->fs_cached = avd_bool_alloc(FALSE); /* Get the mean or absolute size of the file */ if (attr = get_attr_integer(cmd, FSA_SIZE)) fileset->fs_size = attr->attr_avd; else fileset->fs_size = avd_int_alloc(0); return (fileset); } /* * Calls parser_fileset_define_common() to allocate a fileset with * one entry and optionally the fileset_prealloc. sets the fileset_entries, * fileset_dirwidth, fileset_dirgamma, and fileset_sizegamma attributes * to appropriate values for emulating the old "fileobj" entity */ static void parser_file_define(cmd_t *cmd) { fileset_t *fileset; attr_t *attr; if ((fileset = parser_fileset_define_common(cmd)) == NULL) { filebench_log(LOG_ERROR, "define file: failed to instantiate file"); filebench_shutdown(1); return; } /* fileset is emulating a single file */ fileset->fs_attrs = FILESET_IS_FILE; /* Set the size of the fileset to 1 */ fileset->fs_entries = avd_int_alloc(1); /* Set the mean dir width to more than 1 */ fileset->fs_dirwidth = avd_int_alloc(10); /* Set the dir and size gammas to 0 */ fileset->fs_dirgamma = avd_int_alloc(0); fileset->fs_sizegamma = avd_int_alloc(0); } /* * Calls parser_fileset_define_common() to allocate a fileset with the * supplied name and initializes the fileset's fileset_preallocpercent, * fileset_prealloc, fileset_entries, fileset_dirwidth, fileset_dirgamma, * and fileset_sizegamma attributes. */ static void parser_fileset_define(cmd_t *cmd) { fileset_t *fileset; attr_t *attr; if ((fileset = parser_fileset_define_common(cmd)) == NULL) { filebench_log(LOG_ERROR, "define fileset: failed to instantiate fileset"); filebench_shutdown(1); return; } /* Get the number of files in the fileset */ if (attr = get_attr_integer(cmd, FSA_ENTRIES)) { fileset->fs_entries = attr->attr_avd; } else { fileset->fs_entries = avd_int_alloc(0); } /* Get the number of leafdirs in the fileset */ if (attr = get_attr_integer(cmd, FSA_LEAFDIRS)) { fileset->fs_leafdirs = attr->attr_avd; } else { fileset->fs_leafdirs = avd_int_alloc(0); } if ((avd_get_int(fileset->fs_entries) == 0) && (avd_get_int(fileset->fs_leafdirs) == 0)) { filebench_log(LOG_ERROR, "Fileset has no files or leafdirs"); } /* Get the mean dir width of the fileset */ if (attr = get_attr_integer(cmd, FSA_DIRWIDTH)) { fileset->fs_dirwidth = attr->attr_avd; } else { filebench_log(LOG_ERROR, "Fileset has zero directory width"); fileset->fs_dirwidth = avd_int_alloc(0); } /* Get the random variable for dir depth, if supplied */ if (attr = get_attr_integer(cmd, FSA_DIRDEPTHRV)) { if (!AVD_IS_RANDOM(attr->attr_avd)) { filebench_log(LOG_ERROR, "Define fileset: dirdepthrv must be random var"); filebench_shutdown(1); } fileset->fs_dirdepthrv = attr->attr_avd; } else { fileset->fs_dirdepthrv = NULL; } /* Get the gamma value for dir depth distributions */ if (attr = get_attr_integer(cmd, FSA_DIRGAMMA)) { if (AVD_IS_RANDOM(attr->attr_avd)) { filebench_log(LOG_ERROR, "Define fileset: dirgamma attr cannot be random"); filebench_shutdown(1); } fileset->fs_dirgamma = attr->attr_avd; } else fileset->fs_dirgamma = avd_int_alloc(1500); /* Get the gamma value for dir width distributions */ if (attr = get_attr_integer(cmd, FSA_FILESIZEGAMMA)) { if (AVD_IS_RANDOM(attr->attr_avd)) { filebench_log(LOG_ERROR, "Define fileset: filesizegamma cannot be random"); filebench_shutdown(1); } fileset->fs_sizegamma = attr->attr_avd; } else fileset->fs_sizegamma = avd_int_alloc(1500); } /* * Creates and starts all defined procflow processes. The call to * procflow_init() results in creation of the requested number of * process instances for each previously defined procflow. The * child processes exec() a new instance of filebench, passing it * the instance number and address of the shared memory region. * The child processes will then create their threads and flowops. * The routine then unlocks the run_lock to allow all the processes' * threads to start and waits for all of them to begin execution. * Finally, it records the start time and resets the event generation * system. */ static void parser_proc_create(cmd_t *cmd) { filebench_shm->shm_1st_err = 0; filebench_shm->shm_f_abort = FILEBENCH_OK; if (procflow_init() != 0) { filebench_log(LOG_ERROR, "Failed to create processes\n"); filebench_shutdown(1); } /* Release the read lock, allowing threads to start */ (void) pthread_rwlock_unlock(&filebench_shm->shm_run_lock); /* Wait for all threads to start */ if (procflow_allstarted() != 0) { filebench_log(LOG_ERROR, "Could not start run"); return; } if (filebench_shm->shm_required && (ipc_ismcreate(filebench_shm->shm_required) < 0)) { filebench_log(LOG_ERROR, "Could not allocate shared memory"); return; } filebench_shm->shm_starttime = gethrtime(); eventgen_reset(); } /* * Calls fileset_createset() to populate all files and filesets and * create all associated, initially existant, files and subdirectories. * If errors are encountered, calls filebench_shutdown() * to exit filebench. */ static void parser_fileset_create(cmd_t *cmd) { if (!filecreate_done) { filecreate_done = 1; /* initialize the random number system first */ randdist_init(); /* create all the filesets */ if (fileset_createset(NULL) != 0) { filebench_log(LOG_ERROR, "Failed to create filesets"); filebench_shutdown(1); } } else { filebench_log(LOG_INFO, "Attempting to create fileset more than once, ignoring"); } } /* * Deletes the files and directories that represent files and filesets on the * storage medium. */ static void parser_fileset_shutdown(cmd_t *cmd) { filebench_log(LOG_INFO, "Shutting down filesets"); fileset_delete_all_filesets(); } /* * Shuts down all processes and their associated threads. When finished * it deletes interprocess shared memory and resets the event generator. * It does not exit the filebench program though. */ static void parser_proc_shutdown(cmd_t *cmd) { filebench_log(LOG_INFO, "Shutting down processes"); filecreate_done = 0; procflow_shutdown(); if (filebench_shm->shm_required) ipc_ismdelete(); eventgen_reset(); } /* * Ends filebench run after first destoring any interprocess * shared memory. The call to filebench_shutdown() * also causes filebench to exit. */ static void parser_filebench_shutdown(cmd_t *cmd) { int f_abort = filebench_shm->shm_f_abort; ipc_fini(); if (f_abort == FILEBENCH_ABORT_ERROR) filebench_shutdown(1); else filebench_shutdown(0); } /* * This is Used for timing runs.Pauses the master thread in one second * intervals until the supplied ptime runs out or the f_abort flag * is raised. If given a time of zero or less, or the mode is stop on * lack of resources, it will pause until f_abort is raised. */ static int parser_pause(int ptime) { int timeslept = 0; if ((filebench_shm->shm_rmode == FILEBENCH_MODE_TIMEOUT) && (ptime > 0)) { while (timeslept < ptime) { (void) sleep(1); timeslept++; if (filebench_shm->shm_f_abort) break; } } else { /* initial runtime of 0 means run till abort */ /* CONSTCOND */ while (1) { (void) sleep(1); timeslept++; if (filebench_shm->shm_f_abort) break; } } return (timeslept); } /* * Do a file bench run. Calls routines to create file sets, files, and * processes. It resets the statistics counters, then sleeps for the runtime * passed as an argument to it on the command line in 1 second increments. * When it is finished sleeping, it collects a snapshot of the statistics * and ends the run. */ static void parser_run(cmd_t *cmd) { int runtime; int timeslept; runtime = cmd->cmd_qty; parser_fileset_create(cmd); parser_proc_create(cmd); /* check for startup errors */ if (filebench_shm->shm_f_abort) return; filebench_log(LOG_INFO, "Running..."); stats_clear(); timeslept = parser_pause(runtime); filebench_log(LOG_INFO, "Run took %d seconds...", timeslept); parser_statssnap(cmd); parser_proc_shutdown(cmd); } /* * Similar to parser_run, but gets the sleep time from a variable * whose name is supplied as an argument to the command. */ static void parser_run_variable(cmd_t *cmd) { avd_t integer = var_ref_attr(cmd->cmd_tgt1); int runtime; int timeslept; if (integer == NULL) { filebench_log(LOG_ERROR, "Unknown variable %s", cmd->cmd_tgt1); return; } runtime = avd_get_int(integer); /* check for startup errors */ if (filebench_shm->shm_f_abort) return; filebench_log(LOG_INFO, "Running..."); stats_clear(); timeslept = parser_pause(runtime); filebench_log(LOG_INFO, "Run took %d seconds...", timeslept); parser_statssnap(cmd); parser_proc_shutdown(cmd); } char *usagestr = NULL; /* * Prints usage string if defined, else just a message requesting load of a * personality. */ static void parser_help(cmd_t *cmd) { if (usagestr) { filebench_log(LOG_INFO, "%s", usagestr); } else { filebench_log(LOG_INFO, "load <personality> (ls " "%s/workloads for list)", fbbasepath); } } char *varstr = NULL; /* * Prints the string of all var definitions, if there is one. */ static void parser_printvars(cmd_t *cmd) { char *str, *c; if (varstr) { str = strdup(varstr); for (c = str; *c != '\0'; c++) { if ((char)*c == '$') *c = ' '; } filebench_log(LOG_INFO, "%s", str); free(str); } } /* * Establishes multi-client synchronization socket with synch server. */ static void parser_enable_mc(cmd_t *cmd) { attr_t *attr; char *master; char *client; if (attr= get_attr(cmd, FSA_MASTER)) { master = avd_get_str(attr->attr_avd); } else { filebench_log(LOG_ERROR, "enable multi: no master specified"); return; } if (attr= get_attr(cmd, FSA_CLIENT)) { client = avd_get_str(attr->attr_avd); } else { filebench_log(LOG_ERROR, "enable multi: no client specified"); return; } mc_sync_open_sock(master, 8001, client); } /* * Exchanges multi-client synchronization message with synch server. */ static void parser_domultisync(cmd_t *cmd) { attr_t *attr; fbint_t value; if (attr = get_attr(cmd, FSA_VALUE)) value = avd_get_int(attr->attr_avd); else value = 1; mc_sync_synchronize((int)value); } /* * Used by the SET command to add a var and default value string to the * varstr string. It allocates a new, larger varstr string, copies the * old contents of varstr into it, then adds the new var string on the end. */ static void parser_vars(cmd_t *cmd) { char *string = cmd->cmd_tgt1; char *newvars; if (string == NULL) return; if (dofile) return; if (varstr == NULL) { newvars = malloc(strlen(string) + 2); *newvars = 0; } else { newvars = malloc(strlen(varstr) + strlen(string) + 2); (void) strcpy(newvars, varstr); } (void) strcat(newvars, string); (void) strcat(newvars, " "); if (varstr) free(varstr); varstr = newvars; } /* * used by the set command to set the integer part of a regular * variable, or the appropriate field of a random variable */ static void parser_set_integer(cmd_t *cmd) { var_assign_integer(cmd->cmd_tgt1, cmd->cmd_qty); } /* * used by the set command to set the integer part of a regular * variable from another variable, or the appropriate field of a * random variable from another variable */ static void parser_set_var(cmd_t *cmd) { var_assign_var(cmd->cmd_tgt1, cmd->cmd_tgt2); } /* * Used by the set command to set up for a binary operation of a * variable from a var, with an integer */ static void parser_set_var_op_int(cmd_t *cmd) { printf("parser_set_var_op_int: Called\n"); switch (cmd->cmd_subtype) { case FSK_PLUS: var_assign_op_var_int(cmd->cmd_tgt1, VAR_IND_INT_SUM_IV, cmd->cmd_tgt2, cmd->cmd_qty); break; case FSK_MINUS: var_assign_op_var_int(cmd->cmd_tgt1, VAR_IND_IV_DIF_INT, cmd->cmd_tgt2, cmd->cmd_qty); break; case FSK_MULTIPLY: var_assign_op_var_int(cmd->cmd_tgt1, VAR_IND_INT_MUL_IV, cmd->cmd_tgt2, cmd->cmd_qty); break; case FSK_DIVIDE: var_assign_op_var_int(cmd->cmd_tgt1, VAR_IND_IV_DIV_INT, cmd->cmd_tgt2, cmd->cmd_qty); break; } } /* * Used by the set command to set up for a binary operation of an * integer with a variable from a var */ static void parser_set_int_op_var(cmd_t *cmd) { switch (cmd->cmd_subtype) { case FSK_PLUS: var_assign_op_var_int(cmd->cmd_tgt1, VAR_IND_INT_SUM_IV, cmd->cmd_tgt3, cmd->cmd_qty); break; case FSK_MINUS: var_assign_op_var_int(cmd->cmd_tgt1, VAR_IND_INT_DIF_IV, cmd->cmd_tgt3, cmd->cmd_qty); break; case FSK_MULTIPLY: var_assign_op_var_int(cmd->cmd_tgt1, VAR_IND_INT_MUL_IV, cmd->cmd_tgt3, cmd->cmd_qty); break; case FSK_DIVIDE: var_assign_op_var_int(cmd->cmd_tgt1, VAR_IND_INT_DIV_IV, cmd->cmd_tgt3, cmd->cmd_qty); break; } } /* * Used by the set command to set up for a binary operation of two * variables from other vars. */ static void parser_set_var_op_var(cmd_t *cmd) { switch (cmd->cmd_subtype) { case FSK_PLUS: var_assign_op_var_var(cmd->cmd_tgt1, VAR_IND_IV_SUM_IV, cmd->cmd_tgt2, cmd->cmd_tgt3); break; case FSK_MINUS: var_assign_op_var_var(cmd->cmd_tgt1, VAR_IND_IV_DIF_IV, cmd->cmd_tgt2, cmd->cmd_tgt3); break; case FSK_MULTIPLY: var_assign_op_var_var(cmd->cmd_tgt1, VAR_IND_IV_MUL_IV, cmd->cmd_tgt2, cmd->cmd_tgt3); break; case FSK_DIVIDE: var_assign_op_var_var(cmd->cmd_tgt1, VAR_IND_IV_DIV_IV, cmd->cmd_tgt2, cmd->cmd_tgt3); break; } } /* * Sleeps for cmd->cmd_qty seconds, one second at a time. */ static void parser_warmup(cmd_t *cmd) { int sleeptime; /* check for startup errors */ if (filebench_shm->shm_f_abort) return; sleeptime = cmd->cmd_qty; filebench_log(LOG_INFO, "Warming up..."); (void) parser_pause(sleeptime); } /* * Same as parser_sleep, except the sleep time is obtained from a variable * whose name is passed to it as an argument on the command line. */ static void parser_warmup_variable(cmd_t *cmd) { avd_t integer = var_ref_attr(cmd->cmd_tgt1); int sleeptime; if (integer == NULL) { filebench_log(LOG_ERROR, "Unknown variable %s", cmd->cmd_tgt1); return; } sleeptime = avd_get_int(integer); /* check for startup errors */ if (filebench_shm->shm_f_abort) return; filebench_log(LOG_INFO, "Warming up..."); (void) parser_pause(sleeptime); } /* * Sleeps for cmd->cmd_qty seconds, one second at a time. */ static void parser_sleep(cmd_t *cmd) { int sleeptime; int timeslept; /* check for startup errors */ if (filebench_shm->shm_f_abort) return; sleeptime = cmd->cmd_qty; filebench_log(LOG_INFO, "Running..."); timeslept = parser_pause(sleeptime); filebench_log(LOG_INFO, "Run took %d seconds...", timeslept); } /* * Same as parser_sleep, except the sleep time is obtained from a variable * whose name is passed to it as an argument on the command line. */ static void parser_sleep_variable(cmd_t *cmd) { avd_t integer = var_ref_attr(cmd->cmd_tgt1); int sleeptime; int timeslept; if (integer == NULL) { filebench_log(LOG_ERROR, "Unknown variable %s", cmd->cmd_tgt1); return; } sleeptime = avd_get_int(integer); /* check for startup errors */ if (filebench_shm->shm_f_abort) return; filebench_log(LOG_INFO, "Running..."); timeslept = parser_pause(sleeptime); filebench_log(LOG_INFO, "Run took %d seconds...", timeslept); } /* * Parser log prints the values of a list of variables to the log file. * The list of variables is placed on the command line, separated * by comas and the entire list is enclosed in quotes. * For example, if $dir contains "/export/home/tmp" and $filesize = 1048576, * then typing: log "$dir, $filesize" prints: log /export/home/tmp, 1048576 */ static void parser_log(cmd_t *cmd) { char *string; if (cmd->cmd_param_list == NULL) return; string = parser_list2string(cmd->cmd_param_list); if (string == NULL) return; filebench_log(LOG_VERBOSE, "log %s", string); filebench_log(LOG_LOG, "%s", string); } /* * Implements the stats directory command. changes the directory for * dumping statistics to supplied directory path. For example: * stats directory /tmp * changes the stats directory to "/tmp". */ static void parser_directory(cmd_t *cmd) { char newdir[MAXPATHLEN]; char *dir; if ((dir = parser_list2string(cmd->cmd_param_list)) == NULL) { filebench_log(LOG_ERROR, "Cannot interpret directory"); return; } *newdir = 0; /* Change dir relative to cwd if path not fully qualified */ if (*dir != '/') { (void) strcat(newdir, cwd); (void) strcat(newdir, "/"); } (void) strcat(newdir, dir); (void) mkdir(newdir, 0755); filebench_log(LOG_VERBOSE, "Change dir to %s", newdir); chdir(newdir); free(dir); } #define PIPE_PARENT 1 #define PIPE_CHILD 0 /* * Runs the quoted unix command as a background process. Intended for * running statistics gathering utilities such as mpstat while the filebench * workload is running. Also records the pid's of the background processes * so that parser_statssnap() can terminate them when the run completes. */ static void parser_statscmd(cmd_t *cmd) { char *string; pid_t pid; pidlist_t *pidlistent; int pipe_fd[2]; int newstdout; if (cmd->cmd_param_list == NULL) return; string = parser_list2string(cmd->cmd_param_list); if (string == NULL) return; if ((pipe(pipe_fd)) < 0) { filebench_log(LOG_ERROR, "statscmd pipe failed"); return; } #ifdef HAVE_FORK1 if ((pid = fork1()) < 0) { filebench_log(LOG_ERROR, "statscmd fork failed"); return; } #elif HAVE_FORK if ((pid = fork()) < 0) { filebench_log(LOG_ERROR, "statscmd fork failed"); return; } #else Crash! - Need code to deal with no fork1! #endif /* HAVE_FORK1 */ if (pid == 0) { setsid(); filebench_log(LOG_VERBOSE, "Backgrounding %s", string); /* * Child * - close stdout * - dup to create new stdout * - close pipe fds */ (void) close(1); if ((newstdout = dup(pipe_fd[PIPE_CHILD])) < 0) { filebench_log(LOG_ERROR, "statscmd dup failed: %s", strerror(errno)); } (void) close(pipe_fd[PIPE_PARENT]); (void) close(pipe_fd[PIPE_CHILD]); if (system(string) < 0) { filebench_log(LOG_ERROR, "statscmd exec failed: %s", strerror(errno)); } /* Failed! */ exit(1); } else { /* Record pid in pidlist for subsequent reaping by stats snap */ if ((pidlistent = (pidlist_t *)malloc(sizeof (pidlist_t))) == NULL) { filebench_log(LOG_ERROR, "pidlistent malloc failed"); return; } pidlistent->pl_pid = pid; pidlistent->pl_fd = pipe_fd[PIPE_PARENT]; (void) close(pipe_fd[PIPE_CHILD]); /* Add fileobj to global list */ if (pidlist == NULL) { pidlist = pidlistent; pidlistent->pl_next = NULL; } else { pidlistent->pl_next = pidlist; pidlist = pidlistent; } } } /* * Launches a shell to run the unix command supplied in the argument. * The command should be enclosed in quotes, as in: * system "rm xyz" * which would run the "rm" utility to delete the file "xyz". */ static void parser_system(cmd_t *cmd) { char *string; if (cmd->cmd_param_list == NULL) return; string = parser_list2string(cmd->cmd_param_list); if (string == NULL) return; filebench_log(LOG_VERBOSE, "Running '%s'", string); if (system(string) < 0) { filebench_log(LOG_ERROR, "system exec failed: %s", strerror(errno)); filebench_shutdown(1); } free(string); } /* * Echos string supplied with command to the log. */ static void parser_echo(cmd_t *cmd) { char *string; if (cmd->cmd_param_list == NULL) return; string = parser_list2string(cmd->cmd_param_list); if (string == NULL) return; filebench_log(LOG_INFO, "%s", string); } /* * Checks to see if the specified data directory exists and it's mounted file * system is the correct type. */ static void parser_fscheck(cmd_t *cmd) { int fstype_idx; char *pathname = NULL; char *filesys = "tmpfs"; char string[MAXPATHLEN]; struct statvfs64 statbuf; attr_t *attr; if (cmd->cmd_attr_list == NULL) return; for (attr = cmd->cmd_attr_list; attr; attr = attr->attr_next) { switch(attr->attr_name) { case FSA_PATH: pathname = avd_get_str(attr->attr_avd); break; case FSA_FSTYPE: filesys = avd_get_str(attr->attr_avd); break; } } if (pathname == NULL) return; if (statvfs64(pathname, &statbuf) < 0) { filebench_log(LOG_ERROR, "%s error with supplied data path name: %s; exiting", strerror(errno), pathname); filebench_shutdown(1); return; } if (strncmp(filesys, statbuf.f_basetype, FSTYPSZ) != 0) { filebench_log(LOG_ERROR, "File System is of type %s, NOT %s as indicated", statbuf.f_basetype, filesys); filebench_shutdown(1); return; } } /* * Checks to see if any filesets need to have their caches flushed, and * if so invokes the fs_flush script. */ static void parser_fsflush(cmd_t *cmd) { fileset_t *fileset; char **fspathlist; char *pathname = NULL; char *filesys = NULL; char string[MAXPATHLEN]; attr_t *attr; int fsidx; if ((attr = cmd->cmd_attr_list) == NULL) return; /* Get supplied file system type */ if (attr->attr_name == FSA_FSTYPE) filesys = avd_get_str(attr->attr_avd); if (filesys == NULL) { filebench_log(LOG_ERROR, "FSFLUSH command lacks file system type"); return; } /* Check all filesets for any that remain cached and count them*/ fsidx = 0; for (fileset = filebench_shm->shm_filesetlist; fileset != NULL; fileset = fileset->fs_next) { if (avd_get_bool(fileset->fs_cached)) return; fsidx++; } /* allocated space for fileset path pointers */ fspathlist = (char **)malloc(fsidx * sizeof(char *)); /* If flushing still required, flush all filesets */ fsidx = 0; for (fileset = filebench_shm->shm_filesetlist; fileset != NULL; fileset = fileset->fs_next) { int idx; if ((pathname = avd_get_str(fileset->fs_path)) == NULL) return; for (idx = 0; idx < fsidx; idx++) { if (strcmp(pathname, fspathlist[idx]) == 0) break; } if (fsidx == idx) { /* found a new path */ fspathlist[fsidx++] = pathname; /* now flush it */ snprintf(string, MAXPATHLEN, "%s/scripts/fs_flush %s %s", fbbasepath, filesys, pathname); if (system(string) < 0) { filebench_log(LOG_ERROR, "exec of fs_flush script failed: %s", strerror(errno)); filebench_shutdown(1); } } } } /* * Prints out the version of FileBench. */ static void parser_version(cmd_t *cmd) { filebench_log(LOG_INFO, "FileBench Version: %s", FILEBENCH_VERSION); } /* * Adds the string supplied as the argument to the usage command * to the end of the string printed by the help command. */ static void parser_usage(cmd_t *cmd) { char *string; char *newusage; if (cmd->cmd_param_list == NULL) return; string = parser_list2string(cmd->cmd_param_list); if (string == NULL) return; if (dofile) return; if (usagestr == NULL) { newusage = malloc(strlen(string) + 2); *newusage = 0; } else { newusage = malloc(strlen(usagestr) + strlen(string) + 2); (void) strcpy(newusage, usagestr); } (void) strcat(newusage, "\n"); (void) strcat(newusage, string); if (usagestr) free(usagestr); usagestr = newusage; filebench_log(LOG_INFO, "%s", string); } /* * Updates the global dump filename with the filename supplied * as the command's argument. Then dumps the statistics of each * worker flowop into the dump file, followed by a summary of * overall totals. */ static void parser_statsdump(cmd_t *cmd) { char *string; if (cmd->cmd_param_list == NULL) return; string = parser_list2string(cmd->cmd_param_list); if (string == NULL) return; filebench_log(LOG_VERBOSE, "Stats dump to file '%s'", string); stats_dump(string); free(string); } /* * Same as statsdump, but outputs in a computer friendly format. */ static void parser_statsmultidump(cmd_t *cmd) { char *string; if (cmd->cmd_param_list == NULL) return; string = parser_list2string(cmd->cmd_param_list); if (string == NULL) return; filebench_log(LOG_VERBOSE, "Stats dump to file '%s'", string); stats_multidump(string); free(string); } /* * Same as parser_statsdump, but in xml format. */ static void parser_statsxmldump(cmd_t *cmd) { char *string; if (cmd->cmd_param_list == NULL) return; string = parser_list2string(cmd->cmd_param_list); if (string == NULL) return; filebench_log(LOG_VERBOSE, "Stats dump to file '%s'", string); stats_xmldump(string); free(string); } /* * Kills off background statistics collection processes, then takes a snapshot * of the filebench run's collected statistics using stats_snap() from * stats.c. */ static void parser_statssnap(cmd_t *cmd) { pidlist_t *pidlistent; int stat; pid_t pid; for (pidlistent = pidlist; pidlistent != NULL; pidlistent = pidlistent->pl_next) { filebench_log(LOG_VERBOSE, "Killing session %d for pid %d", getsid(pidlistent->pl_pid), pidlistent->pl_pid); if (pidlistent->pl_fd) (void) close(pidlistent->pl_fd); #ifdef HAVE_SIGSEND sigsend(P_SID, getsid(pidlistent->pl_pid), SIGTERM); #else (void) kill(-1, SIGTERM); #endif /* Close pipe */ if (pidlistent->pl_fd) (void) close(pidlistent->pl_fd); /* Wait for cmd and all its children */ while ((pid = waitpid(pidlistent->pl_pid * -1, &stat, 0)) > 0) filebench_log(LOG_DEBUG_IMPL, "Waited for pid %d", (int)pid); } for (pidlistent = pidlist; pidlistent != NULL; pidlistent = pidlistent->pl_next) { free(pidlistent); } pidlist = NULL; stats_snap(); } /* * Shutdown filebench. */ static void parser_abort(int arg) { (void) sigignore(SIGINT); filebench_log(LOG_INFO, "Aborting..."); filebench_shutdown(1); } /* * define a random variable and initialize the distribution parameters */ static void parser_randvar_define(cmd_t *cmd) { var_t *var; randdist_t *rndp; attr_t *attr; char *name; /* Get the name for the random variable */ if (attr = get_attr(cmd, FSA_NAME)) { name = avd_get_str(attr->attr_avd); } else { filebench_log(LOG_ERROR, "define randvar: no name specified"); return; } if ((var = var_define_randvar(name)) == NULL) { filebench_log(LOG_ERROR, "define randvar: failed for random variable %s", name); return; } rndp = var->var_val.randptr; rndp->rnd_type = 0; /* Get the source of the random numbers */ if (attr = get_attr_integer(cmd, FSA_RANDSRC)) { int randsrc = (int)avd_get_int(attr->attr_avd); switch (randsrc) { case FSV_URAND: rndp->rnd_type |= RAND_SRC_URANDOM; break; case FSV_RAND48: rndp->rnd_type |= RAND_SRC_GENERATOR; break; } } else { /* default to rand48 random number generator */ rndp->rnd_type |= RAND_SRC_GENERATOR; } /* Get the min value of the random distribution */ if (attr = get_attr_integer(cmd, FSA_RANDMIN)) rndp->rnd_min = attr->attr_avd; else rndp->rnd_min = avd_int_alloc(0); /* Get the roundoff value for the random distribution */ if (attr = get_attr_integer(cmd, FSA_RANDROUND)) rndp->rnd_round = attr->attr_avd; else rndp->rnd_round = avd_int_alloc(0); /* Get a tablular probablility distribution if there is one */ if (attr = get_attr(cmd, FSA_RANDTABLE)) { rndp->rnd_probtabs = (probtabent_t *)(attr->attr_obj); rndp->rnd_type |= RAND_TYPE_TABLE; /* no need for the rest of the attributes */ return; } else { rndp->rnd_probtabs = NULL; } /* Get the type for the random variable */ if (attr = get_attr(cmd, FSA_TYPE)) { int disttype = (int)avd_get_int(attr->attr_avd); switch (disttype) { case FSV_RANDUNI: rndp->rnd_type |= RAND_TYPE_UNIFORM; break; case FSA_RANDGAMMA: rndp->rnd_type |= RAND_TYPE_GAMMA; break; case FSV_RANDTAB: filebench_log(LOG_ERROR, "Table distribution type without prob table"); break; } } else { /* default to gamma distribution type */ rndp->rnd_type |= RAND_TYPE_GAMMA; } /* Get the seed for the random variable */ if (attr = get_attr_integer(cmd, FSA_RANDSEED)) rndp->rnd_seed = attr->attr_avd; else rndp->rnd_seed = avd_int_alloc(0); /* Get the gamma value of the random distribution */ if (attr = get_attr_integer(cmd, FSA_RANDGAMMA)) rndp->rnd_gamma = attr->attr_avd; else rndp->rnd_gamma = avd_int_alloc(1500); /* Get the mean value of the random distribution */ if (attr = get_attr_integer(cmd, FSA_RANDMEAN)) { rndp->rnd_mean = attr->attr_avd; } else if ((rndp->rnd_type & RAND_TYPE_MASK) == RAND_TYPE_GAMMA) { rndp->rnd_mean = NULL; } else { rndp->rnd_mean = avd_int_alloc(0); } } /* * Set a specified random distribution parameter in a random variable. */ static void parser_randvar_set(cmd_t *cmd) { var_t *src_var, *randvar; randdist_t *rndp; avd_t value; if ((randvar = var_find_randvar(cmd->cmd_tgt1)) == NULL) { filebench_log(LOG_ERROR, "set randvar: failed", cmd->cmd_tgt1); return; } rndp = randvar->var_val.randptr; value = cmd->cmd_attr_list->attr_avd; switch (cmd->cmd_qty) { case FSS_TYPE: { int disttype = (int)avd_get_int(value); rndp->rnd_type &= (~RAND_TYPE_MASK); switch (disttype) { case FSV_RANDUNI: rndp->rnd_type |= RAND_TYPE_UNIFORM; break; case FSA_RANDGAMMA: rndp->rnd_type |= RAND_TYPE_GAMMA; break; case FSV_RANDTAB: rndp->rnd_type |= RAND_TYPE_TABLE; break; } break; } case FSS_SRC: { int randsrc = (int)avd_get_int(value); rndp->rnd_type &= (~(RAND_SRC_URANDOM | RAND_SRC_GENERATOR)); switch (randsrc) { case FSV_URAND: rndp->rnd_type |= RAND_SRC_URANDOM; break; case FSV_RAND48: rndp->rnd_type |= RAND_SRC_GENERATOR; break; } break; } case FSS_SEED: rndp->rnd_seed = value; break; case FSS_GAMMA: rndp->rnd_gamma = value; break; case FSS_MEAN: rndp->rnd_mean = value; break; case FSS_MIN: rndp->rnd_min = value; break; case FSS_ROUND: rndp->rnd_round = value; break; default: filebench_log(LOG_ERROR, "setrandvar: undefined attribute"); } } /* * alloc_cmd() allocates the required resources for a cmd_t. On failure, a * filebench_log is issued and NULL is returned. */ static cmd_t * alloc_cmd(void) { cmd_t *cmd; if ((cmd = malloc(sizeof (cmd_t))) == NULL) { filebench_log(LOG_ERROR, "Alloc cmd failed"); return (NULL); } (void) memset(cmd, 0, sizeof (cmd_t)); return (cmd); } /* * Frees the resources of a cmd_t and then the cmd_t "cmd" itself. */ static void free_cmd(cmd_t *cmd) { free((void *)cmd->cmd_tgt1); free((void *)cmd->cmd_tgt2); free(cmd); } /* * Allocates an attr_t structure and zeros it. Returns NULL on failure, or * a pointer to the attr_t. */ static attr_t * alloc_attr(void) { attr_t *attr; if ((attr = malloc(sizeof (attr_t))) == NULL) { return (NULL); } (void) memset(attr, 0, sizeof (attr_t)); return (attr); } /* * Allocates a probtabent_t structure and zeros it. Returns NULL on failure, or * a pointer to the probtabent_t. */ static probtabent_t * alloc_probtabent(void) { probtabent_t *rte; if ((rte = malloc(sizeof (probtabent_t))) == NULL) { return (NULL); } (void) memset(rte, 0, sizeof (probtabent_t)); return (rte); } /* * Allocates an attr_t structure and puts the supplied var_t into * its attr_avd location, and sets its name to FSA_LVAR_ASSIGN */ static attr_t * alloc_lvar_attr(var_t *var) { attr_t *attr; if ((attr = alloc_attr()) == NULL) return (NULL); attr->attr_name = FSA_LVAR_ASSIGN; attr->attr_avd = (avd_t)var; return (attr); } /* * Searches the attribute list for the command for the named attribute type. * The attribute list is created by the parser from the list of attributes * supplied with certain commands, such as the define and flowop commands. * Returns a pointer to the attribute structure if the named attribute is * found, otherwise returns NULL. If the attribute includes a parameter list, * the list is converted to a string and stored in the attr_avd field of * the returned attr_t struct. */ static attr_t * get_attr_fileset(cmd_t *cmd, int64_t name) { attr_t *attr; attr_t *rtn = NULL; char *string; for (attr = cmd->cmd_attr_list; attr != NULL; attr = attr->attr_next) { filebench_log(LOG_DEBUG_IMPL, "attr %d = %d %llx?", attr->attr_name, name, attr->attr_avd); if (attr->attr_name == name) rtn = attr; } if (rtn == NULL) return (NULL); if (rtn->attr_param_list) { filebench_log(LOG_DEBUG_SCRIPT, "attr is param list"); rtn->attr_avd = parser_list2varstring(rtn->attr_param_list); } return (rtn); } /* * Searches the attribute list for the command for the named attribute type. * The attribute list is created by the parser from the list of attributes * supplied with certain commands, such as the define and flowop commands. * Returns a pointer to the attribute structure if the named attribute is * found, otherwise returns NULL. If the attribute includes a parameter list, * the list is converted to a string and stored in the attr_avd field of * the returned attr_t struct. */ static attr_t * get_attr(cmd_t *cmd, int64_t name) { attr_t *attr; attr_t *rtn = NULL; char *string; for (attr = cmd->cmd_attr_list; attr != NULL; attr = attr->attr_next) { filebench_log(LOG_DEBUG_IMPL, "attr %d = %d %llx?", attr->attr_name, name, attr->attr_avd); if (attr->attr_name == name) rtn = attr; } if (rtn == NULL) return (NULL); if (rtn->attr_param_list) { filebench_log(LOG_DEBUG_SCRIPT, "attr is param list"); string = parser_list2string(rtn->attr_param_list); if (string != NULL) { rtn->attr_avd = avd_str_alloc(string); filebench_log(LOG_DEBUG_SCRIPT, "attr string %s", string); } } return (rtn); } /* * Similar to get_attr, but converts the parameter string supplied with the * named attribute to an integer and stores the integer in the attr_avd * portion of the returned attr_t struct. */ static attr_t * get_attr_integer(cmd_t *cmd, int64_t name) { attr_t *attr; attr_t *rtn = NULL; for (attr = cmd->cmd_attr_list; attr != NULL; attr = attr->attr_next) { if (attr->attr_name == name) rtn = attr; } if (rtn == NULL) return (NULL); if (rtn->attr_param_list) rtn->attr_avd = parser_list2avd(rtn->attr_param_list); return (rtn); } /* * Similar to get_attr, but converts the parameter string supplied with the * named attribute to an integer and stores the integer in the attr_avd * portion of the returned attr_t struct. If no parameter string is supplied * then it defaults to TRUE (1). */ static attr_t * get_attr_bool(cmd_t *cmd, int64_t name) { attr_t *attr; attr_t *rtn = NULL; for (attr = cmd->cmd_attr_list; attr != NULL; attr = attr->attr_next) { if (attr->attr_name == name) rtn = attr; } if (rtn == NULL) return (NULL); if (rtn->attr_param_list) { rtn->attr_avd = parser_list2avd(rtn->attr_param_list); } else if (rtn->attr_avd == NULL) { rtn->attr_avd = avd_bool_alloc(TRUE); } /* boolean attributes cannot point to random variables */ if (AVD_IS_RANDOM(rtn->attr_avd)) { filebench_log(LOG_ERROR, "define flowop: Boolean attr %s cannot be random", name); filebench_shutdown(1); return (NULL); } return (rtn); } /* * removes the newly allocated local var from the shared local var * list, then puts it at the head of the private local var list * supplied as the second argument. */ static void add_lvar_to_list(var_t *newlvar, var_t **lvar_list) { var_t *prev; /* remove from shared local list, if there */ if (newlvar == filebench_shm->shm_var_loc_list) { /* on top of list, just grap */ filebench_shm->shm_var_loc_list = newlvar->var_next; } else { /* find newvar on list and remove */ for (prev = filebench_shm->shm_var_loc_list; prev; prev = prev->var_next) { if (prev->var_next == newlvar) prev->var_next = newlvar->var_next; } } newlvar->var_next = NULL; /* add to flowop private local list at head */ newlvar->var_next = *lvar_list; *lvar_list = newlvar; } /* * Searches the attribute list for the command for any allocated local * variables. The attribute list is created by the parser from the list of * attributes supplied with certain commands, such as the define and flowop * commands. Places all found local vars onto the flowop's local variable * list. */ static void get_attr_lvars(cmd_t *cmd, flowop_t *flowop) { attr_t *attr; var_t *list_tail, *orig_lvar_list; /* save the local var list */ orig_lvar_list = flowop->fo_lvar_list; for (attr = cmd->cmd_attr_list; attr != NULL; attr = attr->attr_next) { if (attr->attr_name == FSA_LVAR_ASSIGN) { var_t *newvar, *prev; if ((newvar = (var_t *)attr->attr_avd) == NULL) continue; add_lvar_to_list(newvar, &flowop->fo_lvar_list); var_update_comp_lvars(newvar, orig_lvar_list, NULL); } } } /* * Allocates memory for a list_t structure, initializes it to zero, and * returns a pointer to it. On failure, returns NULL. */ static list_t * alloc_list() { list_t *list; if ((list = malloc(sizeof (list_t))) == NULL) { return (NULL); } (void) memset(list, 0, sizeof (list_t)); return (list); } #define USAGE1 \ "Usage:\n" \ "go_filebench: interpret f script and generate file workload\n" \ "Options:\n" \ " [-h] Display verbose help\n" \ " [-p] Disable opening /proc to set uacct to enable truss\n" #define PARSER_CMDS \ "create [files|filesets|processes]\n" \ "stats [clear|snap]\n" \ "stats command \"shell command $var1,$var2...\"\n" \ "stats directory <directory>\n" \ "sleep <sleep-value>\n" \ "quit\n\n" \ "Variables:\n" \ "set $var = value\n" \ " $var - regular variables\n" \ " ${var} - internal special variables\n" \ " $(var) - environment variables\n\n" #define PARSER_EXAMPLE \ "Example:\n\n" \ "#!" FILEBENCHDIR "/bin/go_filebench -f\n" \ "\n" \ "define file name=bigfile,path=bigfile,size=1g,prealloc,reuse\n" \ "define process name=randomizer\n" \ "{\n" \ " thread random-thread procname=randomizer\n" \ " {\n" \ " flowop read name=random-read,filename=bigfile,iosize=16k,random\n" \ " }\n" \ "}\n" \ "create files\n" \ "create processes\n" \ "stats clear\n" \ "sleep 30\n" \ "stats snap\n" /* * usage() display brief or verbose help for the filebench(1) command. */ static void usage(int help) { if (help >= 1) (void) fprintf(stderr, USAGE1, cmdname); if (help >= 2) { (void) fprintf(stderr, "\n'f' language definition:\n\n"); fileset_usage(); procflow_usage(); threadflow_usage(); flowoplib_usage(); eventgen_usage(); (void) fprintf(stderr, PARSER_CMDS); (void) fprintf(stderr, PARSER_EXAMPLE); } exit(E_USAGE); } int yywrap() { char buf[1024]; if (parentscript) { yyin = parentscript; yy_switchfilescript(yyin); parentscript = NULL; return (0); } else return (1); }