1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Portions Copyright 2008 Denis Cheng
27  */
28 
29 %{
30 
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <float.h>
35 #include <limits.h>
36 #include <signal.h>
37 #include <errno.h>
38 #include <sys/types.h>
39 #include <locale.h>
40 #include <sys/utsname.h>
41 #include <sys/statvfs.h>
42 #ifdef HAVE_STDINT_H
43 #include <stdint.h>
44 #endif
45 #include <fcntl.h>
46 #include <sys/mman.h>
47 #include <sys/wait.h>
48 #include "parsertypes.h"
49 #include "filebench.h"
50 #include "utils.h"
51 #include "stats.h"
52 #include "vars.h"
53 #include "eventgen.h"
54 #include "aslr.h"
55 #include "multi_client_sync.h"
56 
57 /* yacc and lex externals */
58 extern FILE *yyin;
59 extern int yydebug;
60 extern void yyerror(char *s);
61 extern int yylex(void);
62 
63 /* executable name to execute worker processes later */
64 char *execname;
65 
66 /* utilities */
67 static cmd_t *alloc_cmd(void);
68 static attr_t *alloc_attr(void);
69 static attr_t *alloc_lvar_attr(var_t *var);
70 static attr_t *get_attr(cmd_t *cmd, int64_t name);
71 static void get_attr_lvars(cmd_t *cmd, flowop_t *flowop);
72 static list_t *alloc_list();
73 static probtabent_t *alloc_probtabent(void);
74 static void add_lvar_to_list(var_t *newlvar, var_t **lvar_list);
75 
76 /* Info Commands */
77 static void parser_fileset_list(cmd_t *);
78 static void parser_flowop_list(cmd_t *);
79 
80 /* Define Commands */
81 static void parser_proc_define(cmd_t *);
82 static void parser_thread_define(cmd_t *, procflow_t *);
83 static void parser_flowop_define(cmd_t *, threadflow_t *, flowop_t **, int);
84 static void parser_composite_flowop_define(cmd_t *);
85 static void parser_file_define(cmd_t *);
86 static void parser_fileset_define(cmd_t *);
87 static void parser_var_assign_random(char *, cmd_t *);
88 static void parser_var_assign_custom(char *, cmd_t *);
89 
90 /* Create Commands */
91 static void parser_fileset_create(cmd_t *);
92 
93 /* Run Commands */
94 static void parser_run(cmd_t *cmd);
95 static void parser_run_variable(cmd_t *cmd);
96 static void parser_psrun(cmd_t *cmd);
97 
98 /* Shutdown (Quit) Commands */
99 static void parser_filebench_shutdown(cmd_t *cmd);
100 
101 /* Other Commands */
102 static void parser_echo(cmd_t *cmd);
103 static void parser_system(cmd_t *cmd);
104 static void parser_eventgen(cmd_t *cmd);
105 static void parser_enable_mc(cmd_t *cmd);
106 static void parser_domultisync(cmd_t *cmd);
107 static void parser_sleep(cmd_t *cmd);
108 static void parser_sleep_variable(cmd_t *cmd);
109 static void parser_version(cmd_t *cmd);
110 static void parser_enable_lathist(cmd_t *cmd);
111 
112 %}
113 
114 %union {
115 	int64_t		 ival;
116 	unsigned char	 bval;
117 	char *		 sval;
118 	avd_t		 avd;
119 	cmd_t		*cmd;
120 	attr_t		*attr;
121 	list_t		*list;
122 	probtabent_t	*rndtb;
123 }
124 
125 %start commands
126 
127 %token FSC_LIST FSC_DEFINE FSC_QUIT FSC_DEBUG FSC_CREATE FSC_SLEEP FSC_SET
128 %token FSC_SYSTEM FSC_EVENTGEN FSC_ECHO FSC_RUN FSC_PSRUN FSC_VERSION FSC_ENABLE
129 %token FSC_DOMULTISYNC
130 
131 %token FSV_STRING FSV_VAL_POSINT FSV_VAL_NEGINT FSV_VAL_BOOLEAN FSV_VARIABLE
132 %token FSV_WHITESTRING FSV_RANDUNI FSV_RANDTAB FSV_URAND FSV_RAND48
133 
134 %token FSE_FILE FSE_FILES FSE_FILESET FSE_PROC FSE_THREAD FSE_FLOWOP FSE_CVAR
135 %token FSE_RAND FSE_MODE FSE_MULTI
136 
137 %token FSK_SEPLST FSK_OPENLST FSK_CLOSELST FSK_OPENPAR FSK_CLOSEPAR FSK_ASSIGN
138 %token FSK_IN FSK_QUOTE
139 
140 %token FSA_SIZE FSA_PREALLOC FSA_PARALLOC FSA_PATH FSA_REUSE
141 %token FSA_MEMSIZE FSA_RATE FSA_READONLY FSA_TRUSTTREE
142 %token FSA_IOSIZE FSA_FILENAME FSA_WSS FSA_NAME FSA_RANDOM FSA_INSTANCES
143 %token FSA_DSYNC FSA_TARGET FSA_ITERS FSA_NICE FSA_VALUE FSA_BLOCKING
144 %token FSA_HIGHWATER FSA_DIRECTIO FSA_DIRWIDTH FSA_FD FSA_SRCFD FSA_ROTATEFD
145 %token FSA_ENTRIES FSA_DIRDEPTHRV FSA_DIRGAMMA FSA_USEISM FSA_TYPE
146 %token FSA_LEAFDIRS FSA_INDEXED FSA_RANDTABLE FSA_RANDSRC FSA_ROUND
147 %token FSA_RANDSEED FSA_RANDGAMMA FSA_RANDMEAN FSA_MIN FSA_MAX FSA_MASTER
148 %token FSA_CLIENT FSS_TYPE FSS_SEED FSS_GAMMA FSS_MEAN FSS_MIN FSS_SRC FSS_ROUND
149 %token FSA_LVAR_ASSIGN FSA_ALLDONE FSA_FIRSTDONE FSA_TIMEOUT FSA_LATHIST
150 %token FSA_NOREADAHEAD FSA_IOPRIO FSA_WRITEONLY FSA_PARAMETERS FSA_NOUSESTATS
151 
152 %type <ival> FSV_VAL_POSINT FSV_VAL_NEGINT
153 %type <bval> FSV_VAL_BOOLEAN
154 %type <sval> FSV_STRING FSV_WHITESTRING FSV_VARIABLE FSK_ASSIGN
155 
156 %type <ival> FSC_DEFINE FSC_SET FSC_RUN FSC_ENABLE FSC_PSRUN
157 %type <ival> FSC_DOMULTISYNC
158 %type <ival> FSE_FILE FSE_FILES FSE_PROC FSE_THREAD FSC_VERSION
159 
160 %type <sval> name
161 
162 %type <cmd> command run_command list_command psrun_command
163 %type <cmd> proc_define_command files_define_command
164 %type <cmd> flowop_define_command debug_command create_command
165 %type <cmd> sleep_command set_command
166 %type <cmd> system_command flowop_command
167 %type <cmd> eventgen_command quit_command flowop_list thread_list
168 %type <cmd> thread echo_command
169 %type <cmd> version_command enable_command multisync_command
170 %type <cmd> set_variable set_random_variable set_custom_variable set_mode
171 
172 %type <attr> fileset_attr_op fileset_attr_ops file_attr_ops file_attr_op p_attr_op t_attr_op p_attr_ops t_attr_ops
173 %type <attr> fo_attr_op fo_attr_ops ev_attr_op ev_attr_ops
174 %type <attr> randvar_attr_op randvar_attr_ops randvar_attr_typop
175 %type <attr> randvar_attr_srcop attr_value
176 %type <attr> comp_lvar_def comp_attr_op comp_attr_ops
177 %type <attr> enable_multi_ops enable_multi_op multisync_op
178 %type <attr> cvar_attr_ops cvar_attr_op
179 %type <list> whitevar_string whitevar_string_list
180 %type <ival> attrs_define_thread attrs_flowop
181 %type <ival> attrs_define_fileset attrs_define_file attrs_define_proc attrs_eventgen attrs_define_comp
182 %type <ival> randvar_attr_name FSA_TYPE randtype_name
183 %type <ival> randsrc_name FSA_RANDSRC em_attr_name
184 %type <ival> FSS_TYPE FSS_SEED FSS_GAMMA FSS_MEAN FSS_MIN FSS_SRC
185 %type <ival> cvar_attr_name
186 
187 %type <rndtb>  probtabentry_list probtabentry
188 %type <avd> var_int_val
189 %%
190 
191 commands: commands command
192 {
193 	if ($2->cmd)
194 		$2->cmd($2);
195 
196 	free($2);
197 }
198 | commands error
199 {
200 	YYABORT;
201 }
202 |;
203 
204 command:
205   proc_define_command
206 | files_define_command
207 | flowop_define_command
208 | debug_command
209 | eventgen_command
210 | create_command
211 | echo_command
212 | list_command
213 | run_command
214 | psrun_command
215 | set_command
216 | quit_command
217 | sleep_command
218 | system_command
219 | version_command
220 | enable_command
221 | multisync_command
222 
223 eventgen_command: FSC_EVENTGEN
224 {
225 	if (($$ = alloc_cmd()) == NULL)
226 		YYERROR;
227 	$$->cmd = &parser_eventgen;
228 }
229 | eventgen_command ev_attr_ops
230 {
231 	$1->cmd_attr_list = $2;
232 };
233 
234 system_command: FSC_SYSTEM whitevar_string_list
235 {
236 	if (($$ = alloc_cmd()) == NULL)
237 		YYERROR;
238 
239 	$$->cmd_param_list = $2;
240 	$$->cmd = parser_system;
241 };
242 
243 echo_command: FSC_ECHO whitevar_string_list
244 {
245 	if (($$ = alloc_cmd()) == NULL)
246 		YYERROR;
247 
248 	$$->cmd_param_list = $2;
249 	$$->cmd = parser_echo;
250 };
251 
252 version_command: FSC_VERSION
253 {
254 	if (($$ = alloc_cmd()) == NULL)
255 		YYERROR;
256 	$$->cmd = parser_version;
257 };
258 
259 enable_command: FSC_ENABLE FSE_MULTI enable_multi_ops
260 {
261 
262 	if (($$ = alloc_cmd()) == NULL)
263 		YYERROR;
264 
265 	$$->cmd = parser_enable_mc;
266 	$$->cmd_attr_list = $3;
267 }
268 | FSC_ENABLE FSA_LATHIST
269 {
270 	if (($$ = alloc_cmd()) == NULL)
271 		YYERROR;
272 
273 	$$->cmd = parser_enable_lathist;
274 };
275 
276 multisync_command: FSC_DOMULTISYNC multisync_op
277 {
278 	if (($$ = alloc_cmd()) == NULL)
279 		YYERROR;
280 
281 	$$->cmd = parser_domultisync;
282 	$$->cmd_attr_list = $2;
283 }
284 
285 whitevar_string: FSK_QUOTE FSV_VARIABLE
286 {
287 	if (($$ = alloc_list()) == NULL)
288 			YYERROR;
289 
290 	$$->list_string = avd_str_alloc($2);
291 }
292 | FSK_QUOTE FSV_WHITESTRING
293 {
294 	if (($$ = alloc_list()) == NULL)
295 			YYERROR;
296 
297 	$$->list_string = avd_str_alloc($2);
298 };
299 
300 whitevar_string_list: whitevar_string FSV_WHITESTRING
301 {
302 	list_t *list = NULL;
303 	list_t *list_end = NULL;
304 
305 	/* Add string */
306 	if (($$ = alloc_list()) == NULL)
307 		YYERROR;
308 
309 	$$->list_string = avd_str_alloc($2);
310 
311 	/* Find end of list */
312 	for (list = $1; list != NULL;
313 	    list = list->list_next)
314 		list_end = list;
315 	list_end->list_next = $$;
316 	$$ = $1;
317 
318 }| whitevar_string FSV_VARIABLE
319 {
320 	list_t *list = NULL;
321 	list_t *list_end = NULL;
322 
323 	/* Add variable */
324 	if (($$ = alloc_list()) == NULL)
325 		YYERROR;
326 
327 	$$->list_string = avd_str_alloc($2);
328 
329 	/* Find end of list */
330 	for (list = $1; list != NULL;
331 	    list = list->list_next)
332 		list_end = list;
333 	list_end->list_next = $$;
334 	$$ = $1;
335 }| whitevar_string_list FSV_WHITESTRING
336 {
337 	list_t *list = NULL;
338 	list_t *list_end = NULL;
339 
340 	/* Add string */
341 	if (($$ = alloc_list()) == NULL)
342 		YYERROR;
343 
344 	$$->list_string = avd_str_alloc($2);
345 
346 	/* Find end of list */
347 	for (list = $1; list != NULL;
348 	    list = list->list_next)
349 		list_end = list;
350 	list_end->list_next = $$;
351 	$$ = $1;
352 
353 }| whitevar_string_list FSV_VARIABLE
354 {
355 	list_t *list = NULL;
356 	list_t *list_end = NULL;
357 
358 	/* Add variable */
359 	if (($$ = alloc_list()) == NULL)
360 		YYERROR;
361 
362 	$$->list_string = avd_str_alloc($2);
363 
364 	/* Find end of list */
365 	for (list = $1; list != NULL;
366 	    list = list->list_next)
367 		list_end = list;
368 	list_end->list_next = $$;
369 	$$ = $1;
370 }| whitevar_string_list FSK_QUOTE
371 {
372 	$$ = $1;
373 }| whitevar_string FSK_QUOTE
374 {
375 	$$ = $1;
376 };
377 
378 list_command: FSC_LIST FSE_FILESET
379 {
380 	if (($$ = alloc_cmd()) == NULL)
381 		YYERROR;
382 	$$->cmd = &parser_fileset_list;
383 }
384 | FSC_LIST FSE_FLOWOP
385 {
386 	if (($$ = alloc_cmd()) == NULL)
387 		YYERROR;
388 	$$->cmd = &parser_flowop_list;
389 };
390 
391 debug_command: FSC_DEBUG FSV_VAL_POSINT
392 {
393 	if (($$ = alloc_cmd()) == NULL)
394 		YYERROR;
395 	$$->cmd = NULL;
396 	filebench_shm->shm_debug_level = $2;
397 	if (filebench_shm->shm_debug_level > 10) {
398 		filebench_log(LOG_ERROR, "Debug level set out of range."
399 					"  Adjusting to 10.");
400 			filebench_shm->shm_debug_level = 10;
401 		}
402 	if (filebench_shm->shm_debug_level > 9)
403 		yydebug = 1;
404 };
405 
406 set_command: set_variable | set_random_variable | set_custom_variable | set_mode;
407 
408 set_variable: FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_VAL_POSINT
409 {
410 	$$ = alloc_cmd();
411 	if (!$$)
412 		YYERROR;
413 
414 	var_assign_integer($2, $4);
415 
416 	$$->cmd = NULL;
417 }
418 |
419 FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_VAL_BOOLEAN
420 {
421 	$$ = alloc_cmd();
422 	if (!$$)
423 		YYERROR;
424 
425 	var_assign_boolean($2, $4);
426 
427 	$$->cmd = NULL;
428 }
429 | FSC_SET FSV_VARIABLE FSK_ASSIGN FSK_QUOTE FSV_WHITESTRING FSK_QUOTE
430 {
431 	$$ = alloc_cmd();
432 	if (!$$)
433 		YYERROR;
434 
435 	var_assign_string($2, $5);
436 
437 	$$->cmd = NULL;
438 }
439 | FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_STRING
440 {
441 	$$ = alloc_cmd();
442 	if (!$$)
443 		YYERROR;
444 
445 	var_assign_string($2, $4);
446 
447 	$$->cmd = NULL;
448 };
449 
450 set_random_variable: FSC_SET FSV_VARIABLE FSK_ASSIGN FSE_RAND FSK_OPENPAR randvar_attr_ops FSK_CLOSEPAR
451 {
452 	$$ = alloc_cmd();
453 	if (!$$)
454 		YYERROR;
455 
456 	$$->cmd_attr_list = $6;
457 	$$->cmd = NULL;
458 
459 	parser_var_assign_random($2, $$);
460 }
461 
462 set_custom_variable: FSC_SET FSV_VARIABLE FSK_ASSIGN FSE_CVAR FSK_OPENPAR cvar_attr_ops FSK_CLOSEPAR
463 {
464 	$$ = alloc_cmd();
465 	if (!$$)
466 		YYERROR;
467 
468 	$$->cmd_attr_list = $6;
469 	$$->cmd = NULL;
470 
471 	parser_var_assign_custom($2, $$);
472 };
473 
474 set_mode: FSC_SET FSE_MODE FSC_QUIT FSA_TIMEOUT
475 {
476 	$$ = alloc_cmd();
477 	if (!$$)
478 		YYERROR;
479 
480 	filebench_shm->shm_rmode = FILEBENCH_MODE_TIMEOUT;
481 
482 	$$->cmd = NULL;
483 }
484 | FSC_SET FSE_MODE FSC_QUIT FSA_ALLDONE
485 {
486 	$$ = alloc_cmd();
487 	if (!$$)
488 		YYERROR;
489 
490 	filebench_shm->shm_rmode = FILEBENCH_MODE_QALLDONE;
491 
492 	$$->cmd = NULL;
493 }
494 | FSC_SET FSE_MODE FSC_QUIT FSA_FIRSTDONE
495 {
496 	$$ = alloc_cmd();
497 	if (!$$)
498 		YYERROR;
499 
500 	filebench_shm->shm_rmode = FILEBENCH_MODE_Q1STDONE;
501 
502 	$$->cmd = NULL;
503 }
504 | FSC_SET FSE_MODE FSA_NOUSESTATS
505 {
506 	$$ = alloc_cmd();
507 	if (!$$)
508 		YYERROR;
509 
510 	filebench_log(LOG_INFO, "Disabling CPU usage statistics");
511 	filebench_shm->shm_mmode |= FILEBENCH_MODE_NOUSAGE;
512 
513 	$$->cmd = NULL;
514 };
515 
516 quit_command: FSC_QUIT
517 {
518 	if (($$ = alloc_cmd()) == NULL)
519 		YYERROR;
520 	$$->cmd = parser_filebench_shutdown;
521 };
522 
523 flowop_list: flowop_command
524 {
525 	$$ = $1;
526 }| flowop_list flowop_command
527 {
528 	cmd_t *list = NULL;
529 	cmd_t *list_end = NULL;
530 
531 	/* Find end of list */
532 	for (list = $1; list != NULL;
533 	    list = list->cmd_next)
534 		list_end = list;
535 
536 	list_end->cmd_next = $2;
537 
538 	filebench_log(LOG_DEBUG_IMPL,
539 	    "flowop_list adding cmd %zx to list %zx", $2, $1);
540 
541 	$$ = $1;
542 };
543 
544 thread: FSE_THREAD t_attr_ops FSK_OPENLST flowop_list FSK_CLOSELST
545 {
546 	/*
547 	 * Allocate a cmd node per thread, with a
548 	 * list of flowops attached to the cmd_list
549 	 */
550 	if (($$ = alloc_cmd()) == NULL)
551 		YYERROR;
552 	$$->cmd_list = $4;
553 	$$->cmd_attr_list = $2;
554 };
555 
556 thread_list: thread
557 {
558 	$$ = $1;
559 }| thread_list thread
560 {
561 	cmd_t *list = NULL;
562 	cmd_t *list_end = NULL;
563 
564 	/* Find end of list */
565 	for (list = $1; list != NULL;
566 	    list = list->cmd_next)
567 		list_end = list;
568 
569 	list_end->cmd_next = $2;
570 
571 	filebench_log(LOG_DEBUG_IMPL,
572 	    "thread_list adding cmd %zx to list %zx", $2, $1);
573 
574 	$$ = $1;
575 };
576 
577 proc_define_command: FSC_DEFINE FSE_PROC p_attr_ops FSK_OPENLST thread_list FSK_CLOSELST
578 {
579 	$$ = alloc_cmd();
580 	if (!$$)
581 		YYERROR;
582 	$$->cmd = &parser_proc_define;
583 	$$->cmd_list = $5;
584 	$$->cmd_attr_list = $3;
585 }
586 
587 files_define_command: FSC_DEFINE FSE_FILE file_attr_ops
588 {
589 	$$ = alloc_cmd();
590 	if (!$$)
591 		YYERROR;
592 
593 	$$->cmd = &parser_file_define;
594 	$$->cmd_attr_list = $3;
595 }| FSC_DEFINE FSE_FILESET fileset_attr_ops
596 {
597 	$$ = alloc_cmd();
598 	if (!$$)
599 		YYERROR;
600 
601 	$$->cmd = &parser_fileset_define;
602 	$$->cmd_attr_list = $3;
603 }
604 
605 create_command: FSC_CREATE FSE_FILES
606 {
607 	if (($$ = alloc_cmd()) == NULL)
608 		YYERROR;
609 
610 	$$->cmd = &parser_fileset_create;
611 };
612 
613 sleep_command: FSC_SLEEP FSV_VAL_POSINT
614 {
615 	if (($$ = alloc_cmd()) == NULL)
616 		YYERROR;
617 	$$->cmd = parser_sleep;
618 	$$->cmd_qty = $2;
619 }
620 | FSC_SLEEP FSV_VARIABLE
621 {
622 	if (($$ = alloc_cmd()) == NULL)
623 		YYERROR;
624 	$$->cmd = parser_sleep_variable;
625 	$$->cmd_tgt1 = fb_stralloc($2);
626 }
627 
628 run_command: FSC_RUN FSV_VAL_POSINT
629 {
630 	if (($$ = alloc_cmd()) == NULL)
631 		YYERROR;
632 	$$->cmd = parser_run;
633 	$$->cmd_qty = $2;
634 }
635 | FSC_RUN FSV_VARIABLE
636 {
637 	if (($$ = alloc_cmd()) == NULL)
638 		YYERROR;
639 	$$->cmd = parser_run_variable;
640 	$$->cmd_tgt1 = fb_stralloc($2);
641 }
642 | FSC_RUN
643 {
644 	if (($$ = alloc_cmd()) == NULL)
645 		YYERROR;
646 	$$->cmd = parser_run;
647 	$$->cmd_qty = 0;
648 };
649 
650 psrun_command: FSC_PSRUN
651 {
652 	if (($$ = alloc_cmd()) == NULL)
653 		YYERROR;
654 	$$->cmd = parser_psrun;
655 	$$->cmd_qty1 = 0;
656 	$$->cmd_qty = 0;
657 }
658 | FSC_PSRUN FSV_VAL_NEGINT
659 {
660 	if (($$ = alloc_cmd()) == NULL)
661 		YYERROR;
662 	$$->cmd = parser_psrun;
663 	$$->cmd_qty1 = $2;
664 	$$->cmd_qty = 0;
665 
666 }
667 | FSC_PSRUN FSV_VAL_POSINT
668 {
669 	if (($$ = alloc_cmd()) == NULL)
670 		YYERROR;
671 	$$->cmd = parser_psrun;
672 	$$->cmd_qty1 = $2;
673 	$$->cmd_qty = 0;
674 
675 }
676 | FSC_PSRUN FSV_VAL_NEGINT FSV_VAL_POSINT
677 {
678 	if (($$ = alloc_cmd()) == NULL)
679 		YYERROR;
680 	$$->cmd = parser_psrun;
681 	$$->cmd_qty1 = $2;
682 	$$->cmd_qty = $3;
683 }
684 | FSC_PSRUN FSV_VAL_POSINT FSV_VAL_POSINT
685 {
686 	if (($$ = alloc_cmd()) == NULL)
687 		YYERROR;
688 	$$->cmd = parser_psrun;
689 	$$->cmd_qty1 = $2;
690 	$$->cmd_qty = $3;
691 };
692 
693 flowop_command: FSE_FLOWOP name
694 {
695 	if (($$ = alloc_cmd()) == NULL)
696 		YYERROR;
697 	$$->cmd_name = fb_stralloc($2);
698 }
699 | flowop_command fo_attr_ops
700 {
701 	$1->cmd_attr_list = $2;
702 };
703 
704 name: FSV_STRING;
705 
706 file_attr_ops: file_attr_op
707 {
708 	$$ = $1;
709 }
710 | file_attr_ops FSK_SEPLST file_attr_op
711 {
712 	attr_t *attr = NULL;
713 	attr_t *list_end = NULL;
714 
715 	for (attr = $1; attr; attr = attr->attr_next)
716 		list_end = attr;
717 
718 	list_end->attr_next = $3;
719 
720 	$$ = $1;
721 };
722 
723 fileset_attr_ops: fileset_attr_op
724 {
725 	$$ = $1;
726 }
727 | fileset_attr_ops FSK_SEPLST fileset_attr_op
728 {
729 	attr_t *attr = NULL;
730 	attr_t *list_end = NULL;
731 
732 	for (attr = $1; attr; attr = attr->attr_next)
733 		list_end = attr;
734 
735 	list_end->attr_next = $3;
736 
737 	$$ = $1;
738 };
739 
740 file_attr_op: attrs_define_file FSK_ASSIGN attr_value
741 {
742 	$$ = $3;
743 	$$->attr_name = $1;
744 }
745 | attrs_define_file
746 {
747 	$$ = alloc_attr();
748 	if (!$$)
749 		YYERROR;
750 
751 	$$->attr_name = $1;
752 	$$->attr_avd = avd_bool_alloc(TRUE);
753 };
754 
755 fileset_attr_op: attrs_define_fileset FSK_ASSIGN attr_value
756 {
757 	$$ = $3;
758 	$$->attr_name = $1;
759 }
760 | attrs_define_fileset
761 {
762 	$$ = alloc_attr();
763 	if (!$$)
764 		YYERROR;
765 
766 	$$->attr_name = $1;
767 	$$->attr_avd = avd_bool_alloc(TRUE);
768 };
769 
770 /* attribute parsing for random variables */
771 randvar_attr_ops: randvar_attr_op
772 {
773 	$$ = $1;
774 }
775 | randvar_attr_ops FSK_SEPLST randvar_attr_op
776 {
777 	attr_t *attr = NULL;
778 	attr_t *list_end = NULL;
779 
780 	for (attr = $1; attr != NULL;
781 	    attr = attr->attr_next)
782 		list_end = attr; /* Find end of list */
783 
784 	list_end->attr_next = $3;
785 
786 	$$ = $1;
787 }
788 | randvar_attr_ops FSK_SEPLST FSA_RANDTABLE FSK_ASSIGN FSK_OPENLST probtabentry_list FSK_CLOSELST
789 {
790 	attr_t *attr = NULL;
791 	attr_t *list_end = NULL;
792 
793 	for (attr = $1; attr != NULL;
794 	    attr = attr->attr_next)
795 		list_end = attr; /* Find end of list */
796 
797 
798 	if ((attr = alloc_attr()) == NULL)
799 		YYERROR;
800 
801 	attr->attr_name = FSA_RANDTABLE;
802 	attr->attr_obj = (void *)$6;
803 	list_end->attr_next = attr;
804 	$$ = $1;
805 };
806 
807 randvar_attr_op: randvar_attr_name FSK_ASSIGN attr_value
808 {
809 	$$ = $3;
810 	$$->attr_name = $1;
811 }
812 | randvar_attr_name
813 {
814 	if (($$ = alloc_attr()) == NULL)
815 		YYERROR;
816 	$$->attr_name = $1;
817 	$$->attr_avd = avd_bool_alloc(TRUE);
818 }
819 | FSA_TYPE FSK_ASSIGN randvar_attr_typop
820 {
821 	$$ = $3;
822 	$$->attr_name = FSA_TYPE;
823 }
824 | FSA_RANDSRC FSK_ASSIGN randvar_attr_srcop
825 {
826 	$$ = $3;
827 	$$->attr_name = FSA_RANDSRC;
828 };
829 
830 probtabentry: FSK_OPENLST var_int_val FSK_SEPLST var_int_val FSK_SEPLST var_int_val FSK_CLOSELST
831 {
832 	if (($$ = alloc_probtabent()) == NULL)
833 		YYERROR;
834 	$$->pte_percent = $2;
835 	$$->pte_segmin  = $4;
836 	$$->pte_segmax  = $6;
837 };
838 
839 /* attribute parsing for prob density function table */
840 probtabentry_list: probtabentry
841 {
842 	$$ = $1;
843 }
844 | probtabentry_list FSK_SEPLST probtabentry
845 {
846 	probtabent_t *pte = NULL;
847 	probtabent_t *ptelist_end = NULL;
848 
849 	for (pte = $1; pte != NULL;
850 	    pte = pte->pte_next)
851 		ptelist_end = pte; /* Find end of prob table entry list */
852 
853 	ptelist_end->pte_next = $3;
854 
855 	$$ = $1;
856 };
857 
858 p_attr_ops: p_attr_op
859 {
860 	$$ = $1;
861 }
862 | p_attr_ops FSK_SEPLST p_attr_op
863 {
864 	attr_t *attr = NULL;
865 	attr_t *list_end = NULL;
866 
867 	for (attr = $1; attr != NULL;
868 	    attr = attr->attr_next)
869 		list_end = attr; /* Find end of list */
870 
871 	list_end->attr_next = $3;
872 
873 	$$ = $1;
874 };
875 
876 p_attr_op: attrs_define_proc FSK_ASSIGN attr_value
877 {
878 	$$ = $3;
879 	$$->attr_name = $1;
880 }
881 | attrs_define_proc
882 {
883 	$$ = alloc_attr();
884 	if (!$$)
885 		YYERROR;
886 	$$->attr_name = $1;
887 	$$->attr_avd = avd_bool_alloc(TRUE);
888 };
889 
890 /* attribute parsing thread options */
891 t_attr_ops: t_attr_op
892 {
893 	$$ = $1;
894 }
895 | t_attr_ops FSK_SEPLST t_attr_op
896 {
897 	attr_t *attr = NULL;
898 	attr_t *list_end = NULL;
899 
900 	for (attr = $1; attr != NULL;
901 	    attr = attr->attr_next)
902 		list_end = attr; /* Find end of list */
903 
904 	list_end->attr_next = $3;
905 
906 	$$ = $1;
907 };
908 
909 t_attr_op: attrs_define_thread FSK_ASSIGN attr_value
910 {
911 	$$ = $3;
912 	$$->attr_name = $1;
913 }
914 | attrs_define_thread
915 {
916 	if (($$ = alloc_attr()) == NULL)
917 		YYERROR;
918 	$$->attr_name = $1;
919 	$$->attr_avd = avd_bool_alloc(TRUE);
920 };
921 
922 /* attribute parsing for flowops */
923 fo_attr_ops: fo_attr_op
924 {
925 	$$ = $1;
926 }
927 | fo_attr_ops FSK_SEPLST fo_attr_op
928 {
929 	attr_t *attr = NULL;
930 	attr_t *list_end = NULL;
931 
932 	for (attr = $1; attr != NULL;
933 	    attr = attr->attr_next)
934 		list_end = attr; /* Find end of list */
935 
936 	list_end->attr_next = $3;
937 
938 	$$ = $1;
939 }
940 | fo_attr_ops FSK_SEPLST comp_lvar_def
941 {
942 	attr_t *attr = NULL;
943 	attr_t *list_end = NULL;
944 
945 	for (attr = $1; attr != NULL;
946 	    attr = attr->attr_next)
947 		list_end = attr; /* Find end of list */
948 
949 	list_end->attr_next = $3;
950 
951 	$$ = $1;
952 };
953 
954 fo_attr_op: attrs_flowop FSK_ASSIGN attr_value
955 {
956 	$$ = $3;
957 	$$->attr_name = $1;
958 }
959 | attrs_flowop
960 {
961 	if (($$ = alloc_attr()) == NULL)
962 		YYERROR;
963 	$$->attr_name = $1;
964 	$$->attr_avd = avd_bool_alloc(TRUE);
965 };
966 
967 /* attribute parsing for Event Generator */
968 ev_attr_ops: ev_attr_op
969 {
970 	$$ = $1;
971 }
972 | ev_attr_ops FSK_SEPLST ev_attr_op
973 {
974 	attr_t *attr = NULL;
975 	attr_t *list_end = NULL;
976 
977 	for (attr = $1; attr != NULL;
978 	    attr = attr->attr_next)
979 		list_end = attr; /* Find end of list */
980 
981 	list_end->attr_next = $3;
982 
983 	$$ = $1;
984 };
985 
986 ev_attr_op: attrs_eventgen FSK_ASSIGN attr_value
987 {
988 	$$ = $3;
989 	$$->attr_name = $1;
990 }
991 | attrs_eventgen
992 {
993 	if (($$ = alloc_attr()) == NULL)
994 		YYERROR;
995 	$$->attr_name = $1;
996 	$$->attr_avd = avd_bool_alloc(TRUE);
997 };
998 
999 /* attribute parsing for enable multiple client command */
1000 enable_multi_ops: enable_multi_op
1001 {
1002 	$$ = $1;
1003 }
1004 | enable_multi_ops FSK_SEPLST enable_multi_op
1005 {
1006 	attr_t *attr = NULL;
1007 	attr_t *list_end = NULL;
1008 
1009 	for (attr = $1; attr != NULL;
1010 	    attr = attr->attr_next)
1011 		list_end = attr; /* Find end of list */
1012 
1013 	list_end->attr_next = $3;
1014 
1015 	$$ = $1;
1016 };
1017 
1018 enable_multi_op: em_attr_name FSK_ASSIGN attr_value
1019 {
1020 	$$ = $3;
1021 	$$->attr_name = $1;
1022 };
1023 
1024 multisync_op: FSA_VALUE FSK_ASSIGN attr_value
1025 {
1026 	$$ = $3;
1027 	$$->attr_name = FSA_VALUE;
1028 };
1029 
1030 /*
1031  * Attribute names
1032  */
1033 attrs_define_proc:
1034   FSA_NAME { $$ = FSA_NAME;}
1035 | FSA_INSTANCES { $$ = FSA_INSTANCES;}
1036 | FSA_NICE { $$ = FSA_NICE;}
1037 
1038 attrs_define_file:
1039   FSA_NAME { $$ = FSA_NAME;}
1040 | FSA_PATH { $$ = FSA_PATH;}
1041 | FSA_SIZE { $$ = FSA_SIZE;}
1042 | FSA_PREALLOC { $$ = FSA_PREALLOC;}
1043 | FSA_PARALLOC { $$ = FSA_PARALLOC;}
1044 | FSA_REUSE { $$ = FSA_REUSE;}
1045 | FSA_TRUSTTREE { $$ = FSA_TRUSTTREE;}
1046 | FSA_READONLY { $$ = FSA_READONLY;}
1047 | FSA_WRITEONLY { $$ = FSA_WRITEONLY;}
1048 
1049 attrs_define_fileset:
1050   FSA_NAME { $$ = FSA_NAME;}
1051 | FSA_PATH { $$ = FSA_PATH;}
1052 | FSA_ENTRIES { $$ = FSA_ENTRIES;}
1053 | FSA_SIZE { $$ = FSA_SIZE;}
1054 | FSA_PREALLOC { $$ = FSA_PREALLOC;}
1055 | FSA_PARALLOC { $$ = FSA_PARALLOC;}
1056 | FSA_REUSE { $$ = FSA_REUSE;}
1057 | FSA_TRUSTTREE { $$ = FSA_TRUSTTREE;}
1058 | FSA_READONLY { $$ = FSA_READONLY;}
1059 | FSA_WRITEONLY { $$ = FSA_WRITEONLY;}
1060 | FSA_DIRWIDTH { $$ = FSA_DIRWIDTH;}
1061 | FSA_DIRDEPTHRV { $$ = FSA_DIRDEPTHRV;}
1062 | FSA_DIRGAMMA { $$ = FSA_DIRGAMMA;}
1063 | FSA_LEAFDIRS { $$ = FSA_LEAFDIRS;};
1064 
1065 randvar_attr_name:
1066   FSA_NAME { $$ = FSA_NAME;}
1067 | FSA_RANDSEED { $$ = FSA_RANDSEED;}
1068 | FSA_RANDGAMMA { $$ = FSA_RANDGAMMA;}
1069 | FSA_RANDMEAN { $$ = FSA_RANDMEAN;}
1070 | FSA_MIN { $$ = FSA_MIN;}
1071 | FSA_ROUND { $$ = FSA_ROUND;};
1072 
1073 randvar_attr_typop: randtype_name
1074 {
1075 	if (($$ = alloc_attr()) == NULL)
1076 		YYERROR;
1077 	$$->attr_avd = avd_int_alloc($1);
1078 };
1079 
1080 randtype_name:
1081   FSV_RANDUNI { $$ = FSV_RANDUNI;}
1082 | FSV_RANDTAB { $$ = FSV_RANDTAB;}
1083 | FSA_RANDGAMMA { $$ = FSA_RANDGAMMA;};
1084 
1085 randvar_attr_srcop: randsrc_name
1086 {
1087 	if (($$ = alloc_attr()) == NULL)
1088 		YYERROR;
1089 	$$->attr_avd = avd_int_alloc($1);
1090 };
1091 
1092 randsrc_name:
1093   FSV_URAND { $$ = FSV_URAND;}
1094 | FSV_RAND48 { $$ = FSV_RAND48;};
1095 
1096 cvar_attr_name:
1097   FSA_TYPE { $$ = FSA_TYPE;}
1098 | FSA_PARAMETERS { $$ = FSA_PARAMETERS;}
1099 | FSA_MIN { $$ = FSA_MIN;}
1100 | FSA_MAX { $$ = FSA_MAX;}
1101 | FSA_ROUND { $$ = FSA_ROUND;};
1102 
1103 attrs_define_thread:
1104   FSA_NAME { $$ = FSA_NAME;}
1105 | FSA_MEMSIZE { $$ = FSA_MEMSIZE;}
1106 | FSA_USEISM { $$ = FSA_USEISM;}
1107 | FSA_INSTANCES { $$ = FSA_INSTANCES;}
1108 | FSA_IOPRIO { $$ = FSA_IOPRIO;};
1109 
1110 attrs_flowop:
1111   FSA_WSS { $$ = FSA_WSS;}
1112 | FSA_FILENAME { $$ = FSA_FILENAME;}
1113 | FSA_NAME { $$ = FSA_NAME;}
1114 | FSA_RANDOM { $$ = FSA_RANDOM;}
1115 | FSA_FD { $$ = FSA_FD;}
1116 | FSA_SRCFD { $$ = FSA_SRCFD;}
1117 | FSA_ROTATEFD { $$ = FSA_ROTATEFD;}
1118 | FSA_DSYNC { $$ = FSA_DSYNC;}
1119 | FSA_DIRECTIO { $$ = FSA_DIRECTIO;}
1120 | FSA_INDEXED { $$ = FSA_INDEXED;}
1121 | FSA_TARGET { $$ = FSA_TARGET;}
1122 | FSA_ITERS { $$ = FSA_ITERS;}
1123 | FSA_VALUE { $$ = FSA_VALUE;}
1124 | FSA_BLOCKING { $$ = FSA_BLOCKING;}
1125 | FSA_HIGHWATER { $$ = FSA_HIGHWATER;}
1126 | FSA_IOSIZE { $$ = FSA_IOSIZE;}
1127 | FSA_NOREADAHEAD { $$ = FSA_NOREADAHEAD;};
1128 
1129 attrs_eventgen:
1130   FSA_RATE { $$ = FSA_RATE;};
1131 
1132 em_attr_name:
1133   FSA_MASTER { $$ = FSA_MASTER;}
1134 | FSA_CLIENT { $$ = FSA_CLIENT;};
1135 
1136 comp_attr_ops: comp_attr_op
1137 {
1138 	$$ = $1;
1139 }
1140 | comp_attr_ops FSK_SEPLST comp_attr_op
1141 {
1142 	attr_t *attr = NULL;
1143 	attr_t *list_end = NULL;
1144 
1145 	for (attr = $1; attr != NULL;
1146 	    attr = attr->attr_next)
1147 		list_end = attr; /* Find end of list */
1148 
1149 	list_end->attr_next = $3;
1150 
1151 	$$ = $1;
1152 }
1153 | comp_attr_ops FSK_SEPLST comp_lvar_def
1154 {
1155 	attr_t *attr = NULL;
1156 	attr_t *list_end = NULL;
1157 
1158 	for (attr = $1; attr != NULL;
1159 	    attr = attr->attr_next)
1160 		list_end = attr; /* Find end of list */
1161 
1162 	list_end->attr_next = $3;
1163 
1164 	$$ = $1;
1165 };
1166 
1167 comp_attr_op: attrs_define_comp FSK_ASSIGN attr_value
1168 {
1169 	$$ = $3;
1170 	$$->attr_name = $1;
1171 };
1172 
1173 comp_lvar_def: FSV_VARIABLE FSK_ASSIGN FSV_VAL_BOOLEAN
1174 {
1175 	if (($$ = alloc_lvar_attr(var_lvar_assign_boolean($1, $3))) == NULL)
1176 		YYERROR;
1177 }
1178 | FSV_VARIABLE FSK_ASSIGN FSV_VAL_POSINT
1179 {
1180 	if (($$ = alloc_lvar_attr(var_lvar_assign_integer($1, $3))) == NULL)
1181 		YYERROR;
1182 }
1183 | FSV_VARIABLE FSK_ASSIGN FSK_QUOTE FSV_WHITESTRING FSK_QUOTE
1184 {
1185 	if (($$ = alloc_lvar_attr(var_lvar_assign_string($1, $4))) == NULL)
1186 		YYERROR;
1187 }
1188 | FSV_VARIABLE FSK_ASSIGN FSV_STRING
1189 {
1190 	if (($$ = alloc_lvar_attr(var_lvar_assign_string($1, $3))) == NULL)
1191 		YYERROR;
1192 }
1193 | FSV_VARIABLE FSK_ASSIGN FSV_VARIABLE
1194 {
1195 	if (($$ = alloc_lvar_attr(var_lvar_assign_var($1, $3))) == NULL)
1196 		YYERROR;
1197 }
1198 | FSV_VARIABLE
1199 {
1200 	if (($$ = alloc_lvar_attr(var_lvar_alloc_local($1))) == NULL)
1201 		YYERROR;
1202 };
1203 
1204 flowop_define_command: FSC_DEFINE FSE_FLOWOP comp_attr_ops FSK_OPENLST flowop_list FSK_CLOSELST
1205 {
1206 	if (($$ = alloc_cmd()) == NULL)
1207 		YYERROR;
1208 	$$->cmd = &parser_composite_flowop_define;
1209 	$$->cmd_list = $5;
1210 	$$->cmd_attr_list = $3;
1211 }
1212 | flowop_define_command comp_attr_ops
1213 {
1214 	$1->cmd_attr_list = $2;
1215 };
1216 
1217 /* attribute parsing for custom variables */
1218 cvar_attr_ops: cvar_attr_op
1219 {
1220 	$$ = $1;
1221 }
1222 | cvar_attr_ops FSK_SEPLST cvar_attr_op
1223 {
1224 	attr_t *attr = NULL;
1225 	attr_t *list_end = NULL;
1226 
1227 	for (attr = $1; attr != NULL;
1228 	    attr = attr->attr_next)
1229 		list_end = attr; /* Find end of list */
1230 
1231 	list_end->attr_next = $3;
1232 
1233 	$$ = $1;
1234 }
1235 
1236 cvar_attr_op: cvar_attr_name FSK_ASSIGN attr_value
1237 {
1238 	$$ = $3;
1239 	$$->attr_name = $1;
1240 };
1241 
1242 attrs_define_comp:
1243   FSA_NAME { $$ = FSA_NAME;}
1244 | FSA_ITERS { $$ = FSA_ITERS;};
1245 
1246 attr_value: FSV_STRING
1247 {
1248 	$$ = alloc_attr();
1249 	if (!$$)
1250 		YYERROR;
1251 	$$->attr_avd = avd_str_alloc($1);
1252 } | FSK_QUOTE FSV_WHITESTRING FSK_QUOTE {
1253 	$$ = alloc_attr();
1254 	if (!$$)
1255 		YYERROR;
1256 	$$->attr_avd = avd_str_alloc($2);
1257 } | FSV_VAL_POSINT {
1258 	$$ = alloc_attr();
1259 	if (!$$)
1260 		YYERROR;
1261 	$$->attr_avd = avd_int_alloc($1);
1262 } | FSV_VAL_BOOLEAN {
1263 	$$ = alloc_attr();
1264 	if (!$$)
1265 		YYERROR;
1266 	$$->attr_avd = avd_bool_alloc($1);
1267 } | FSV_VARIABLE {
1268 	$$ = alloc_attr();
1269 	if (!$$)
1270 		YYERROR;
1271 	$$->attr_avd = avd_var_alloc($1);
1272 };
1273 
1274 var_int_val: FSV_VAL_POSINT
1275 {
1276 	$$ = avd_int_alloc($1);
1277 } | FSV_VARIABLE
1278 {
1279 	$$ = avd_var_alloc($1);
1280 };
1281 
1282 %%
1283 
1284 /*
1285  * The following C routines implement the various commands defined in the above
1286  * yacc parser code. The yacc portion checks the syntax of the commands found
1287  * in a workload file and parses the commands' parameters into lists. The lists
1288  * are then passed in a cmd_t struct for each command to its related routine in
1289  * the following section for actual execution.  This section also includes a
1290  * few utility routines and the main entry point for the program.
1291  */
1292 
1293 #define	USAGE \
1294 "Usage: " \
1295 "filebench {-f <wmlscript> | -h | -c [cvartype]}\n\n" \
1296 "  Filebench version " FILEBENCH_VERSION "\n\n" \
1297 "  Filebench is a file system and storage benchmark that interprets a script\n" \
1298 "  written in its Workload Model Language (WML), and procees to generate the\n" \
1299 "  specified workload. Refer to the README for more details.\n\n" \
1300 "  Visit github.com/filebench/filebench for WML definition and tutorials.\n\n" \
1301 "Options:\n" \
1302 "   -f <wmlscript> generate workload from the specified file\n" \
1303 "   -h             display this help message\n" \
1304 "   -c             display supported cvar types\n" \
1305 "   -c [cvartype]  display options of the specific cvar type\n\n"
1306 
1307 static void
1308 usage_exit(int ret, const char *msg)
1309 {
1310 	if (ret) {
1311 		(void)fprintf(stderr, "Usage error: %s\n\n", msg);
1312 		(void)fprintf(stderr, USAGE);
1313 	} else
1314 		printf(USAGE);
1315 	exit(ret);
1316 }
1317 
1318 struct fbparams {
1319 	char *execname;
1320 	char *fscriptname;
1321 	char *procname;
1322 	char *shmaddr;
1323 	char *shmpath;
1324 	int instance;
1325 	char *cvartype;
1326 };
1327 
1328 static void
init_fbparams(struct fbparams * fbparams)1329 init_fbparams(struct fbparams *fbparams)
1330 {
1331 	memset(fbparams, 0, sizeof(*fbparams));
1332 	fbparams->instance = -1;
1333 }
1334 
1335 #define FB_MODE_NONE		0
1336 #define FB_MODE_HELP		1
1337 #define FB_MODE_MASTER		2
1338 #define FB_MODE_WORKER		3
1339 #define FB_MODE_CVARS		4
1340 
1341 static int
parse_options(int argc,char * argv[],struct fbparams * fbparams)1342 parse_options(int argc, char *argv[], struct fbparams *fbparams)
1343 {
1344 	const char cmd_options[] = "m:s:a:i:hf:c:";
1345 	int mode = FB_MODE_NONE;
1346 	int opt;
1347 
1348 	init_fbparams(fbparams);
1349 	fbparams->execname = argv[0];
1350 
1351  	/*
1352 	 * We don't want getopt() to print error messages because
1353 	 * sometimes what it percieves as an error is actually not
1354 	 * an error.  For example, "-c" option might have or might
1355 	 * not have an argument.  If opterr is non-zero, getopt()
1356 	 * prints an error message when "-c"'s argument is missing.
1357 	 */
1358 	opterr = 0;
1359 
1360 	/* Either
1361 		(-f <wmlscript>) or
1362 		(-a and -s and -m and -i) or
1363 		(-c [cvartype]) or
1364 		(-h)
1365 	   must be specified */
1366 	while ((opt = getopt(argc, argv, cmd_options)) > 0) {
1367 		switch (opt) {
1368 		/* public parameters */
1369 		case 'h':
1370 			if (mode != FB_MODE_NONE)
1371 				usage_exit(1, "Too many options specified");
1372 			mode = FB_MODE_HELP;
1373 			break;
1374 		case 'c':
1375 			if (mode != FB_MODE_NONE)
1376 				usage_exit(1, "Too many options specified");
1377 			mode = FB_MODE_CVARS;
1378 			fbparams->cvartype = optarg;
1379 			break;
1380 		case 'f':
1381 			if (mode != FB_MODE_NONE)
1382 				usage_exit(1, "Too many options specified");
1383 			mode = FB_MODE_MASTER;
1384 			fbparams->fscriptname = optarg;
1385 			break;
1386 		/* private parameters: when filebench calls itself */
1387 		case 'a':
1388 			if (mode != FB_MODE_NONE &&
1389 				(mode != FB_MODE_WORKER || fbparams->procname))
1390 					usage_exit(1, "Too many options");
1391 			mode = FB_MODE_WORKER;
1392 			fbparams->procname = optarg;
1393 			break;
1394 		case 's':
1395 			if (mode != FB_MODE_NONE &&
1396 				(mode != FB_MODE_WORKER || fbparams->shmaddr))
1397 					usage_exit(1, "Too many options");
1398 			mode = FB_MODE_WORKER;
1399 			sscanf(optarg, "%p", &fbparams->shmaddr);
1400 			break;
1401 		case 'm':
1402 			if (mode != FB_MODE_NONE &&
1403 				(mode != FB_MODE_WORKER || fbparams->shmpath))
1404 					usage_exit(1, "Too many options");
1405 			mode = FB_MODE_WORKER;
1406 			fbparams->shmpath = optarg;
1407 			break;
1408 		case 'i':
1409 			if (mode != FB_MODE_NONE &&
1410 				(mode != FB_MODE_WORKER || fbparams->instance != -1))
1411 					usage_exit(1, "Too many options");
1412 			mode = FB_MODE_WORKER;
1413 			sscanf(optarg, "%d", &fbparams->instance);
1414 			break;
1415 		case '?':
1416 			if (optopt == 'c') {
1417 				if (mode != FB_MODE_NONE)
1418 					usage_exit(1, "Too many options");
1419 				mode = FB_MODE_CVARS;
1420 				break;
1421 			}
1422 		default:
1423 			usage_exit(1, "Unrecognized option");
1424 			break;
1425 		}
1426 	}
1427 
1428 	if (mode == FB_MODE_NONE)
1429 		usage_exit(1, "No runtime options specified");
1430 
1431 	if (mode == FB_MODE_WORKER) {
1432 		if (!fbparams->procname ||
1433 			!fbparams->shmaddr ||
1434 			!fbparams->shmpath ||
1435 			fbparams->instance == -1)
1436 			usage_exit(1, "Invalid worker settings");
1437 	}
1438 
1439 	return mode;
1440 }
1441 
1442 static void
worker_mode(struct fbparams * fbparams)1443 worker_mode(struct fbparams *fbparams)
1444 {
1445 	int ret;
1446 
1447 	ret = ipc_attach(fbparams->shmaddr, fbparams->shmpath);
1448 	if (ret < 0) {
1449 		filebench_log(LOG_FATAL, "Cannot attach shm for %s",
1450 		    fbparams->procname);
1451 		exit(1);
1452 	}
1453 
1454 	/* get correct function pointer for each working process */
1455 	flowop_init(0);
1456 
1457 	/* load custom variable libraries and revalidate handles */
1458 	ret = init_cvar_libraries();
1459 	if (ret)
1460 		exit(1);
1461 
1462 	ret = revalidate_cvar_handles();
1463 	if (ret)
1464 		exit(1);
1465 
1466 	/* execute corresponding procflow */
1467 	ret = procflow_exec(fbparams->procname, fbparams->instance);
1468 	if (ret < 0) {
1469 		filebench_log(LOG_FATAL, "Cannot startup process %s",
1470 		    fbparams->procname);
1471 		exit(1);
1472 	}
1473 
1474 	exit(0);
1475 }
1476 
parser_list_cvar_types(void)1477 void parser_list_cvar_types(void)
1478 {
1479 	cvar_library_info_t *t;
1480 
1481 	if (!filebench_shm->shm_cvar_lib_info_list) {
1482 		printf("No custom variables supported.\n");
1483 		return;
1484 	}
1485 
1486 	printf("Custom variable types supported:\n");
1487 	for (t = filebench_shm->shm_cvar_lib_info_list; t; t = t->next)
1488 		printf("  %s\n", t->type);
1489 
1490 	return;
1491 }
1492 
parser_list_cvar_type_parameters(char * type)1493 void parser_list_cvar_type_parameters(char *type)
1494 {
1495 	const char *version = NULL;
1496 	const char *usage = NULL;
1497 
1498 	cvar_library_info_t *t;
1499 
1500 	for (t = filebench_shm->shm_cvar_lib_info_list; t != NULL; t = t->next) {
1501 		if (!strcmp(type, t->type))
1502 			break;
1503 	}
1504 
1505 	if (!t) {
1506 		printf("Unknown custom variable type %s.\n", type);
1507 		return;
1508 	}
1509 
1510 	printf("Custom variable type: %s\n", t->type);
1511 	printf("Supporting library: %s\n", t->filename);
1512 
1513 	if (cvar_libraries[t->index]->cvar_op.cvar_version)
1514 		version = cvar_libraries[t->index]->cvar_op.cvar_version();
1515 
1516 	if (cvar_libraries[t->index]->cvar_op.cvar_usage)
1517 		usage = cvar_libraries[t->index]->cvar_op.cvar_usage();
1518 
1519 
1520 	if (version)
1521 		printf("Version: %s\n", version);
1522 	else
1523 		printf("Oops. No version information provided.\n");
1524 
1525 	if (usage)
1526 		printf("Usage:\n%s\n", usage);
1527 	else
1528 		printf("Oops. No usage information provided.\n");
1529 
1530 	return;
1531 }
1532 
1533 static void
cvars_mode(struct fbparams * fbparams)1534 cvars_mode(struct fbparams *fbparams)
1535 {
1536 	int ret;
1537 
1538 	ipc_init();
1539 
1540 	ret = init_cvar_library_info(FBLIBDIR);
1541 	if (ret)
1542 		filebench_shutdown(1);
1543 
1544 	ret = init_cvar_libraries();
1545 	if (ret)
1546 		filebench_shutdown(1);
1547 
1548 	if (fbparams->cvartype)
1549 		parser_list_cvar_type_parameters(fbparams->cvartype);
1550 	else
1551 		parser_list_cvar_types();
1552 
1553 	ipc_fini();
1554 
1555 	exit(0);
1556 }
1557 
1558 /*
1559  * Shutdown filebench.
1560  */
1561 static void
parser_abort(int arg)1562 parser_abort(int arg)
1563 {
1564 	(void) sigignore(SIGINT);
1565 	filebench_log(LOG_INFO, "Aborting...");
1566 	filebench_shutdown(1);
1567 }
1568 
1569 static void
master_mode(struct fbparams * fbparams)1570 master_mode(struct fbparams *fbparams) {
1571 	int ret;
1572 
1573 	printf("Filebench Version %s\n", FILEBENCH_VERSION);
1574 
1575 	yyin = fopen(fbparams->fscriptname, "r");
1576 	if (!yyin) {
1577 		filebench_log(LOG_FATAL,
1578 			"Cannot open file %s!", fbparams->fscriptname);
1579 		exit(1);
1580 	}
1581 
1582 	execname = fbparams->execname;
1583 	fb_set_shmmax();
1584 
1585 	ipc_init();
1586 
1587 	/* Below we initialize things that depend on IPC */
1588 	(void)strcpy(filebench_shm->shm_fscriptname,
1589 				fbparams->fscriptname);
1590 
1591 	flowop_init(1);
1592 	eventgen_init();
1593 
1594 	/* Initialize custom variables. */
1595 	ret = init_cvar_library_info(FBLIBDIR);
1596 	if (ret)
1597 		filebench_shutdown(1);
1598 
1599 	ret = init_cvar_libraries();
1600 	if (ret)
1601 		filebench_shutdown(1);
1602 
1603 	signal(SIGINT, parser_abort);
1604 
1605 	/* yyparse() after it parsed complete grammar */
1606 	yyparse();
1607 
1608 	/* We only get here if there was no
1609 	   run (or similar) command in the
1610 	   end of the WML script. */
1611 	printf("Warning: no run command in the WML script!\n");
1612 	parser_filebench_shutdown((cmd_t *)0);
1613 }
1614 
1615 static void
init_common()1616 init_common()
1617 {
1618 	disable_aslr();
1619 	my_pid = getpid();
1620 	fb_set_rlimit();
1621 }
1622 
1623 /*
1624  * Entry point for Filebench. Processes command line arguments. The -f option
1625  * will read in a workload file (the full name and extension must must be
1626  * given). The -a, -s, -m and -i options are used by the worker process to
1627  * receive the name, the base address of shared memory, its path, and the
1628  * process' instance number, respectively. This information is supplied by the
1629  * master process when it execs worker processes. If the worker process
1630  * arguments are passed then main will call the procflow_exec() routine which
1631  * creates worker threadflows and flowops and executes the procflow's portion of
1632  * the workload model until completion. If worker process arguments are not
1633  * passed to the process, then it becomes the master process for a filebench
1634  * run. It initializes the various filebench components and either executes the
1635  * supplied workload file, or enters interactive mode.
1636  */
1637 int
main(int argc,char * argv[])1638 main(int argc, char *argv[])
1639 {
1640 	struct fbparams fbparams;
1641 	int mode;
1642 
1643 	/* parse_options() exits if detects wrong usage */
1644 	mode = parse_options(argc, argv, &fbparams);
1645 
1646 	if (mode == FB_MODE_HELP)
1647 		usage_exit(0, NULL);
1648 
1649 	if (mode == FB_MODE_CVARS)
1650 		cvars_mode(&fbparams);
1651 
1652 	init_common();
1653 
1654 	if (mode == FB_MODE_MASTER)
1655 		master_mode(&fbparams);
1656 
1657 	if (mode == FB_MODE_WORKER)
1658 		worker_mode(&fbparams);
1659 
1660 	/* We should never reach this point */
1661 	return 0;
1662 }
1663 
1664 /*
1665  * Converts a list of var_strings or ordinary strings to a single ordinary
1666  * string. It returns a pointer to the string (in malloc'd memory) if found,
1667  * or NULL otherwise.
1668  */
1669 char *
parser_list2string(list_t * list)1670 parser_list2string(list_t *list)
1671 {
1672 	list_t *l;
1673 	char *string;
1674 	char *tmp;
1675 
1676 	string = malloc(MAXPATHLEN);
1677 	if (!string) {
1678 		filebench_log(LOG_ERROR, "Failed to allocate memory");
1679 		return NULL;
1680 	}
1681 
1682 	*string = 0;
1683 
1684 	/* Format args */
1685 	for (l = list; l != NULL; l = l->list_next) {
1686 
1687 		char *lstr = avd_get_str(l->list_string);
1688 
1689 		filebench_log(LOG_DEBUG_SCRIPT, "converting string '%s'", lstr);
1690 
1691 		/* see if it is a random variable */
1692 		if (l->list_integer) {
1693 			fbint_t param_name;
1694 
1695 			tmp = NULL;
1696 			param_name = avd_get_int(l->list_integer);
1697 
1698 			switch (param_name) {
1699 			case FSS_TYPE:
1700 				tmp = var_randvar_to_string(lstr,
1701 				    RAND_PARAM_TYPE);
1702 				break;
1703 
1704 			case FSS_SRC:
1705 				tmp = var_randvar_to_string(lstr,
1706 				    RAND_PARAM_SRC);
1707 				break;
1708 
1709 			case FSS_SEED:
1710 				tmp = var_randvar_to_string(lstr,
1711 				    RAND_PARAM_SEED);
1712 				break;
1713 
1714 			case FSS_MIN:
1715 				tmp = var_randvar_to_string(lstr,
1716 				    RAND_PARAM_MIN);
1717 				break;
1718 
1719 			case FSS_MEAN:
1720 				tmp = var_randvar_to_string(lstr,
1721 				    RAND_PARAM_MEAN);
1722 				break;
1723 
1724 			case FSS_GAMMA:
1725 				tmp = var_randvar_to_string(lstr,
1726 				    RAND_PARAM_GAMMA);
1727 				break;
1728 
1729 			case FSS_ROUND:
1730 				tmp = var_randvar_to_string(lstr,
1731 				    RAND_PARAM_ROUND);
1732 				break;
1733 			}
1734 
1735 			if (tmp) {
1736 				(void) strcat(string, tmp);
1737 				free(tmp);
1738 			} else {
1739 				(void) strcat(string, lstr);
1740 			}
1741 		} else {
1742 			/* perhaps a normal variable? */
1743 			if ((tmp = var_to_string(lstr)) != NULL) {
1744 				(void) strcat(string, tmp);
1745 				free(tmp);
1746 			} else {
1747 				(void) strcat(string, lstr);
1748 			}
1749 		}
1750 	}
1751 
1752 	return string;
1753 }
1754 
1755 /*
1756  * If the list just contains a single string starting with '$', then find
1757  * or create the named var and return the var's var_string component.
1758  * Otherwise, convert the list to a string, and allocate a var_string
1759  * containing a copy of that string. On failure either returns NULL
1760  * or shuts down the run.
1761  */
1762 avd_t
parser_list2varstring(list_t * list)1763 parser_list2varstring(list_t *list)
1764 {
1765 	char *lstr = avd_get_str(list->list_string);
1766 
1767 	/* Special case - variable name */
1768 	if ((list->list_next == NULL) && (*lstr == '$'))
1769 		return avd_var_alloc(lstr);
1770 
1771 	return (avd_str_alloc(parser_list2string(list)));
1772 }
1773 
1774 /*
1775  * Looks for the var named in list_string of the first element of the
1776  * supplied list. If found, returns the var_val portion of the var in
1777  * an attribute value descriptor. If the var is not found, cannot be
1778  * allocated, the supplied list is NULL, or the list_string filed is
1779  * empty, returns NULL.
1780  */
1781 avd_t
parser_list2avd(list_t * list)1782 parser_list2avd(list_t *list)
1783 {
1784 	avd_t avd;
1785 	char *lstr;
1786 
1787 	if (list && ((lstr = avd_get_str(list->list_string)) != NULL)) {
1788 		avd = avd_var_alloc(lstr);
1789 		return (avd);
1790 	}
1791 
1792 	return (NULL);
1793 }
1794 
1795 /*
1796  * Sets the event generator rate from the attribute supplied with the
1797  * command. If the attribute doesn't exist the routine does nothing.
1798  */
1799 static void
parser_eventgen(cmd_t * cmd)1800 parser_eventgen(cmd_t *cmd)
1801 {
1802 	attr_t *attr;
1803 
1804 	/* Get the rate from attribute */
1805 	if ((attr = get_attr(cmd, FSA_RATE))) {
1806 		if (attr->attr_avd) {
1807 			eventgen_setrate(attr->attr_avd);
1808 		}
1809 	}
1810 }
1811 
1812 /*
1813  * Lists the fileset name, path name and average size for all defined
1814  * filesets.
1815  */
1816 static void
parser_fileset_list(cmd_t * cmd)1817 parser_fileset_list(cmd_t *cmd)
1818 {
1819 	(void) fileset_iter(fileset_print);
1820 }
1821 
1822 /*
1823  * Lists the flowop name and instance number for all flowops.
1824  */
1825 static void
parser_flowop_list(cmd_t * cmd)1826 parser_flowop_list(cmd_t *cmd)
1827 {
1828 	flowop_printall();
1829 }
1830 
1831 /*
1832  * Calls procflow_define() to allocate "instances" number of  procflow(s)
1833  * (processes) with the supplied name. The default number of instances is
1834  * one. An optional priority level attribute can be supplied and is stored in
1835  * pf_nice. Finally the routine loops through the list of inner commands, if
1836  * any, which are defines for threadflows, and passes them one at a time to
1837  * parser_thread_define() to allocate threadflow entities for the process(es).
1838  */
1839 static void
parser_proc_define(cmd_t * cmd)1840 parser_proc_define(cmd_t *cmd)
1841 {
1842 	procflow_t *procflow;
1843 	char *name = NULL;
1844 	attr_t *attr;
1845 	avd_t var_instances;
1846 	fbint_t instances;
1847 	cmd_t *inner_cmd;
1848 
1849 	attr = get_attr(cmd, FSA_NAME);
1850 	if (attr)
1851 		name = avd_get_str(attr->attr_avd);
1852 	else {
1853 		filebench_log(LOG_ERROR, "process specifies no name");
1854 		filebench_shutdown(1);
1855 	}
1856 
1857 	attr = get_attr(cmd, FSA_INSTANCES);
1858 	if (attr) {
1859 		var_instances = attr->attr_avd;
1860 		instances = avd_get_int(var_instances);
1861 		filebench_log(LOG_DEBUG_IMPL,
1862 		    "Setting instances = %llu", (u_longlong_t)instances);
1863 	} else {
1864 		filebench_log(LOG_DEBUG_IMPL,
1865 		    "Defaulting to instances = 1");
1866 		var_instances = avd_int_alloc(1);
1867 		instances = 1;
1868 	}
1869 
1870 	procflow = procflow_define(name, var_instances);
1871 	if (!procflow) {
1872 		filebench_log(LOG_ERROR,
1873 		    "Failed to instantiate %d %s process(es)\n",
1874 		    instances, name);
1875 		filebench_shutdown(1);
1876 	}
1877 
1878 	attr = get_attr(cmd, FSA_NICE);
1879 	if (attr) {
1880 		filebench_log(LOG_DEBUG_IMPL, "Setting pri = %llu",
1881 			    (u_longlong_t)avd_get_int(attr->attr_avd));
1882 		procflow->pf_nice = attr->attr_avd;
1883 	} else
1884 		procflow->pf_nice = avd_int_alloc(0);
1885 
1886 	/* Create the list of threads for this process  */
1887 	for (inner_cmd = cmd->cmd_list; inner_cmd;
1888 	    	inner_cmd = inner_cmd->cmd_next)
1889 		parser_thread_define(inner_cmd, procflow);
1890 }
1891 
1892 /*
1893  * Calls threadflow_define() to allocate "instances" number of  threadflow(s)
1894  * (threads) with the supplied name. The default number of instances is
1895  * one. Two other optional attributes may be supplied, one to set the memory
1896  * size, stored in tf_memsize, and to select the use of Interprocess Shared
1897  * Memory, which sets the THREADFLOW_USEISM flag in tf_attrs. Finally
1898  * the routine loops through the list of inner commands, if any, which are
1899  * defines for flowops, and passes them one at a time to
1900  * parser_flowop_define() to allocate flowop entities for the threadflows.
1901  */
1902 static void
parser_thread_define(cmd_t * cmd,procflow_t * procflow)1903 parser_thread_define(cmd_t *cmd, procflow_t *procflow)
1904 {
1905 	threadflow_t *threadflow, template;
1906 	attr_t *attr;
1907 	avd_t instances;
1908 	cmd_t *inner_cmd;
1909 	char *name = NULL;
1910 
1911 	memset(&template, 0, sizeof (threadflow_t));
1912 
1913 	attr = get_attr(cmd, FSA_NAME);
1914 	if (attr)
1915 		name = avd_get_str(attr->attr_avd);
1916 	else {
1917 		filebench_log(LOG_ERROR,
1918 		    "thread in process %s specifies no name",
1919 		    procflow->pf_name);
1920 		filebench_shutdown(1);
1921 	}
1922 
1923 	attr = get_attr(cmd, FSA_INSTANCES);
1924 	if (attr)
1925 		instances = attr->attr_avd;
1926 	else
1927 		instances = avd_int_alloc(1);
1928 
1929 	attr = get_attr(cmd, FSA_MEMSIZE);
1930 	if (attr)
1931 		template.tf_memsize = attr->attr_avd;
1932 	else /* XXX: really, memsize zero is default?.. */
1933 		template.tf_memsize = avd_int_alloc(0);
1934 
1935 	attr = get_attr(cmd, FSA_IOPRIO);
1936 	if (attr)
1937 		template.tf_ioprio = attr->attr_avd;
1938 	else /* XXX: really, ioprio is 8 by default?.. */
1939 		template.tf_ioprio = avd_int_alloc(8);
1940 
1941 
1942 	threadflow = threadflow_define(procflow, name, &template, instances);
1943 	if (!threadflow) {
1944 		filebench_log(LOG_ERROR,
1945 		    "failed to instantiate thread\n");
1946 		filebench_shutdown(1);
1947 	}
1948 
1949 	attr = get_attr(cmd, FSA_USEISM);
1950 	if (attr)
1951 		threadflow->tf_attrs |= THREADFLOW_USEISM;
1952 
1953 	/* create the list of flowops */
1954 	for (inner_cmd = cmd->cmd_list; inner_cmd;
1955 	    inner_cmd = inner_cmd->cmd_next)
1956 		parser_flowop_define(inner_cmd, threadflow,
1957 		    &threadflow->tf_thrd_fops, FLOW_MASTER);
1958 }
1959 
1960 /*
1961  * Fills in the attributes for a newly allocated flowop
1962  */
1963 static void
parser_flowop_get_attrs(cmd_t * cmd,flowop_t * flowop)1964 parser_flowop_get_attrs(cmd_t *cmd, flowop_t *flowop)
1965 {
1966 	attr_t *attr;
1967 
1968 	/* Get the filename from attribute */
1969 	if ((attr = get_attr(cmd, FSA_FILENAME))) {
1970 		flowop->fo_filename = attr->attr_avd;
1971 		if (flowop->fo_filename == NULL) {
1972 			filebench_log(LOG_ERROR,
1973 			    "define flowop: no filename specfied");
1974 			filebench_shutdown(1);
1975 		}
1976 	} else {
1977 		/* no filename attribute specified */
1978 		flowop->fo_filename = NULL;
1979 	}
1980 
1981 	/* Get the iosize of the op */
1982 	if ((attr = get_attr(cmd, FSA_IOSIZE)))
1983 		flowop->fo_iosize = attr->attr_avd;
1984 	else
1985 		flowop->fo_iosize = avd_int_alloc(0);
1986 
1987 	/* Get the working set size of the op */
1988 	if ((attr = get_attr(cmd, FSA_WSS)))
1989 		flowop->fo_wss = attr->attr_avd;
1990 	else
1991 		flowop->fo_wss = avd_int_alloc(0);
1992 
1993 	/* Random I/O? */
1994 	if ((attr = get_attr(cmd, FSA_RANDOM)))
1995 		flowop->fo_random = attr->attr_avd;
1996 	else
1997 		flowop->fo_random = avd_bool_alloc(FALSE);
1998 
1999 	/* Sync I/O? */
2000 	if ((attr = get_attr(cmd, FSA_DSYNC)))
2001 		flowop->fo_dsync = attr->attr_avd;
2002 	else
2003 		flowop->fo_dsync = avd_bool_alloc(FALSE);
2004 
2005 	/* Target, for wakeup etc */
2006 	if ((attr = get_attr(cmd, FSA_TARGET)))
2007 		(void) strcpy(flowop->fo_targetname,
2008 		    avd_get_str(attr->attr_avd));
2009 
2010 	/* Value */
2011 	if ((attr = get_attr(cmd, FSA_VALUE)))
2012 		flowop->fo_value = attr->attr_avd;
2013 	else
2014 		flowop->fo_value = avd_int_alloc(0);
2015 
2016 	/* FD */
2017 	if ((attr = get_attr(cmd, FSA_FD))) {
2018 		flowop->fo_fdnumber = avd_get_int(attr->attr_avd);
2019 		if (flowop->fo_filename != NULL)
2020 			filebench_log(LOG_DEBUG_SCRIPT, "It is not "
2021 			    "advisable to supply both an fd number "
2022 			    "and a fileset name in most cases");
2023 	}
2024 
2025 	/* Rotatefd? */
2026 	if ((attr = get_attr(cmd, FSA_ROTATEFD)))
2027 		flowop->fo_rotatefd = attr->attr_avd;
2028 	else
2029 		flowop->fo_rotatefd = avd_bool_alloc(FALSE);
2030 
2031 	/* SRC FD, for copies etc... */
2032 	if ((attr = get_attr(cmd, FSA_SRCFD)))
2033 		flowop->fo_srcfdnumber = avd_get_int(attr->attr_avd);
2034 
2035 	/* Blocking operation? */
2036 	if ((attr = get_attr(cmd, FSA_BLOCKING)))
2037 		flowop->fo_blocking = attr->attr_avd;
2038 	else
2039 		flowop->fo_blocking = avd_bool_alloc(FALSE);
2040 
2041 	/* Direct I/O Operation */
2042 	if ((attr = get_attr(cmd, FSA_DIRECTIO)))
2043 		flowop->fo_directio = attr->attr_avd;
2044 	else
2045 		flowop->fo_directio = avd_bool_alloc(FALSE);
2046 
2047 	/* Highwater mark */
2048 	if ((attr = get_attr(cmd, FSA_HIGHWATER))) {
2049 		flowop->fo_highwater = attr->attr_avd;
2050 		if (AVD_IS_RANDOM(attr->attr_avd)) {
2051 			filebench_log(LOG_ERROR,
2052 			    "define flowop: Highwater attr cannot be random");
2053 			filebench_shutdown(1);
2054 		}
2055 	} else {
2056 		flowop->fo_highwater = avd_int_alloc(1);
2057 	}
2058 
2059 	/* find file or leaf directory by index number */
2060 	if ((attr = get_attr(cmd, FSA_INDEXED)))
2061 		flowop->fo_fileindex = attr->attr_avd;
2062 	else
2063 		flowop->fo_fileindex = NULL;
2064 
2065 	/* Read Ahead Diable */
2066 	if ((attr = get_attr(cmd, FSA_NOREADAHEAD)))
2067 		flowop->fo_noreadahead = attr->attr_avd;
2068 	else
2069 		flowop->fo_noreadahead = avd_bool_alloc(FALSE);
2070 
2071 
2072 }
2073 
2074 /*
2075  * defines the FLOW_MASTER flowops within a FLOW_MASTER instance of
2076  * a composit flowop. Default attributes from the FLOW_INNER_DEF instances
2077  * of the composit flowop's inner flowops are used if set. Otherwise
2078  * default attributes from the FLOW_MASTER instance of the composit flowop
2079  * are used, which may include defaults from the original FLOW_DEFINITION
2080  * of the composit flowop.
2081  */
2082 static void
parser_inner_flowop_define(threadflow_t * thread,flowop_t * comp0_flow,flowop_t * comp_mstr_flow)2083 parser_inner_flowop_define(threadflow_t *thread, flowop_t *comp0_flow,
2084 			   flowop_t *comp_mstr_flow)
2085 {
2086 	flowop_t *inner_flowtype, *inner_flowop;
2087 
2088 	/* follow flowop list, creating composit names */
2089 	inner_flowtype = comp0_flow->fo_comp_fops;
2090 	comp_mstr_flow->fo_comp_fops = NULL;
2091 
2092 	while (inner_flowtype) {
2093 		char fullname[MAXPATHLEN];
2094 
2095 		/* create composite_name.name for new flowop */
2096 		snprintf(fullname, MAXPATHLEN, "%s.%s",
2097 		    comp_mstr_flow->fo_name, inner_flowtype->fo_name);
2098 
2099 		if ((inner_flowop = flowop_define(thread, fullname,
2100 		    inner_flowtype, &comp_mstr_flow->fo_comp_fops,
2101 		    FLOW_MASTER, 0)) == NULL) {
2102 			filebench_log(LOG_ERROR,
2103 			    "define flowop: Failed to instantiate flowop %s\n",
2104 			    fullname);
2105 			filebench_shutdown(1);
2106 		}
2107 
2108 		/* if applicable, update filename attribute */
2109 		if (inner_flowop->fo_filename) {
2110 			char *name;
2111 
2112 			/* fix up avd_t */
2113 			avd_update(&inner_flowop->fo_filename,
2114 			    comp_mstr_flow->fo_lvar_list);
2115 
2116 			/* see if ready to get the file or fileset */
2117 			name = avd_get_str(inner_flowop->fo_filename);
2118 			if (name) {
2119 
2120 				inner_flowop->fo_fileset = fileset_find(name);
2121 
2122 				if (inner_flowop->fo_fileset == NULL) {
2123 					filebench_log(LOG_ERROR,
2124 					    "inr flowop %s: file %s not found",
2125 					    inner_flowop->fo_name, name);
2126 					filebench_shutdown(1);
2127 				}
2128 			}
2129 		}
2130 
2131 		/* update attributes from local variables */
2132 		avd_update(&inner_flowop->fo_iters,
2133 		    comp_mstr_flow->fo_lvar_list);
2134 
2135 		/* if the inner flowop is a composit flowop, recurse */
2136 		if (inner_flowtype->fo_type == FLOW_TYPE_COMPOSITE) {
2137 			var_t *newlvar, *proto_lvars, *lvar_ptr;
2138 
2139 			proto_lvars = inner_flowop->fo_lvar_list;
2140 			inner_flowop->fo_lvar_list = 0;
2141 
2142 			for (lvar_ptr = inner_flowtype->fo_lvar_list; lvar_ptr;
2143 			    lvar_ptr = lvar_ptr->var_next) {
2144 
2145 				if ((newlvar = var_lvar_alloc_local(
2146 				    lvar_ptr->var_name)) != NULL) {
2147 
2148 					add_lvar_to_list(newlvar,
2149 					    &inner_flowop->fo_lvar_list);
2150 
2151 					var_update_comp_lvars(newlvar,
2152 					    proto_lvars,
2153 					    comp_mstr_flow->fo_lvar_list);
2154 				}
2155 			}
2156 
2157 			parser_inner_flowop_define(thread,
2158 			    inner_flowtype,
2159 			    inner_flowop);
2160 
2161 			inner_flowtype = inner_flowtype->fo_exec_next;
2162 			continue;
2163 		}
2164 
2165 		avd_update(&inner_flowop->fo_iosize,
2166 		    comp_mstr_flow->fo_lvar_list);
2167 		avd_update(&inner_flowop->fo_wss,
2168 		    comp_mstr_flow->fo_lvar_list);
2169 		avd_update(&inner_flowop->fo_iters,
2170 		    comp_mstr_flow->fo_lvar_list);
2171 		avd_update(&inner_flowop->fo_value,
2172 		    comp_mstr_flow->fo_lvar_list);
2173 		avd_update(&inner_flowop->fo_random,
2174 		    comp_mstr_flow->fo_lvar_list);
2175 		avd_update(&inner_flowop->fo_dsync,
2176 		    comp_mstr_flow->fo_lvar_list);
2177 		avd_update(&inner_flowop->fo_rotatefd,
2178 		    comp_mstr_flow->fo_lvar_list);
2179 		avd_update(&inner_flowop->fo_blocking,
2180 		    comp_mstr_flow->fo_lvar_list);
2181 		avd_update(&inner_flowop->fo_directio,
2182 		    comp_mstr_flow->fo_lvar_list);
2183 		avd_update(&inner_flowop->fo_highwater,
2184 		    comp_mstr_flow->fo_lvar_list);
2185 
2186 		inner_flowtype = inner_flowtype->fo_exec_next;
2187 	}
2188 }
2189 
2190 /*
2191  * Calls flowop_define() to allocate a flowop with the supplied name.
2192  * The allocated flowop inherits attributes from a base flowop of the
2193  * same type.  If the new flowop has a file or fileset attribute specified,
2194  * it must specify a defined fileobj or fileset or an error will be logged.
2195  * The new flowop may  also have the following attributes set by
2196  * the program:
2197  *  - file size (fo_iosize)
2198  *  - working set size (fo_wss)
2199  *  - do random io (fo_random)
2200  *  - do synchronous io (fo_dsync)
2201  *  - perform each operation multiple times before advancing (fo_iter)
2202  *  - target name (fo_targetname)
2203  *  - An integer value (fo_value)
2204  *  - a file descriptor (fo_fd)
2205  *  - specify to rotate file descriptors (fo_rotatefd)
2206  *  - a source fd (fo_srcfdnumber)
2207  *  - specify a blocking operation (fo_blocking)
2208  *  - specify a highwater mark (fo_highwater)
2209  *
2210  * After all the supplied attributes are stored in their respective locations
2211  * in the flowop object, the flowop's init function is called. No errors are
2212  * returned, but the filebench run will be terminated if the flowtype is not
2213  * specified, a name for the new flowop is not supplied, the flowop_define
2214  * call fails, or a file or fileset name is supplied but the corresponding
2215  * fileobj or fileset cannot be located.
2216  */
2217 static void
parser_flowop_define(cmd_t * cmd,threadflow_t * thread,flowop_t ** flowoplist_hdp,int category)2218 parser_flowop_define(cmd_t *cmd, threadflow_t *thread,
2219     flowop_t **flowoplist_hdp, int category)
2220 {
2221 	flowop_t *flowop, *flowop_type;
2222 	char *type = (char *)cmd->cmd_name;
2223 	char *name = NULL;
2224 	attr_t *attr;
2225 
2226 	/* Get the inherited flowop */
2227 	flowop_type = flowop_find(type);
2228 	if (flowop_type == NULL) {
2229 		filebench_log(LOG_ERROR,
2230 		    "define flowop: flowop type %s not found",
2231 		    type);
2232 		filebench_shutdown(1);
2233 	}
2234 
2235 	/* Get the name of the flowop */
2236 	if ((attr = get_attr(cmd, FSA_NAME))) {
2237 		name = avd_get_str(attr->attr_avd);
2238 	} else {
2239 		filebench_log(LOG_ERROR,
2240 		    "define flowop: flowop %s specifies no name",
2241 		    flowop_type->fo_name);
2242 		filebench_shutdown(1);
2243 	}
2244 
2245 	if ((flowop = flowop_define(thread, name,
2246 	    flowop_type, flowoplist_hdp, category, 0)) == NULL) {
2247 		filebench_log(LOG_ERROR,
2248 		    "define flowop: Failed to instantiate flowop %s\n",
2249 		    cmd->cmd_name);
2250 		filebench_shutdown(1);
2251 	}
2252 
2253 	/* Iterations */
2254 	if ((attr = get_attr(cmd, FSA_ITERS)))
2255 		flowop->fo_iters = attr->attr_avd;
2256 	else
2257 		flowop->fo_iters = avd_int_alloc(1);
2258 
2259 
2260 	/* if this is a use of a composit flowop, create inner FLOW MASTERS */
2261 	if (flowop_type->fo_type == FLOW_TYPE_COMPOSITE) {
2262 		get_attr_lvars(cmd, flowop);
2263 		if (category == FLOW_MASTER)
2264 			parser_inner_flowop_define(thread,
2265 			    flowop_type, flowop);
2266 	}
2267 	else {
2268 		parser_flowop_get_attrs(cmd, flowop);
2269 	}
2270 }
2271 
2272 static void
parser_composite_flowop_define(cmd_t * cmd)2273 parser_composite_flowop_define(cmd_t *cmd)
2274 {
2275 	flowop_t *flowop;
2276 	cmd_t *inner_cmd;
2277 	char *name = NULL;
2278 	attr_t *attr;
2279 
2280 	/* Get the name of the flowop */
2281 	if ((attr = get_attr(cmd, FSA_NAME))) {
2282 		name = avd_get_str(attr->attr_avd);
2283 	} else {
2284 		filebench_log(LOG_ERROR,
2285 		    "define flowop: Composit flowop specifies no name");
2286 
2287 		filebench_shutdown(1);
2288 	}
2289 
2290 	if ((flowop = flowop_new_composite_define(name)) == NULL) {
2291 		filebench_log(LOG_ERROR,
2292 		    "define flowop: Failed to instantiate flowop %s\n",
2293 		    cmd->cmd_name);
2294 		filebench_shutdown(1);
2295 	}
2296 
2297 	/* place any local var_t variables on the flowop's local list */
2298 	get_attr_lvars(cmd, flowop);
2299 
2300 	/* Iterations */
2301 	if ((attr = get_attr(cmd, FSA_ITERS)))
2302 		flowop->fo_iters = attr->attr_avd;
2303 	else
2304 		flowop->fo_iters = avd_int_alloc(1);
2305 
2306 	/* define inner flowops */
2307 	for (inner_cmd = cmd->cmd_list; inner_cmd != NULL;
2308 	    inner_cmd = inner_cmd->cmd_next) {
2309 		parser_flowop_define(inner_cmd, NULL,
2310 		    &flowop->fo_comp_fops, FLOW_INNER_DEF);
2311 	}
2312 }
2313 
2314 
2315 /*
2316  * First, we verify that mandatory attributes - name and path - are specified.
2317  * Then allocate a fileset structure and setup its fields. Notice, at this
2318  * point we should not verify if AVD type makes sense, because AVD type can
2319  * change as variables are set to other values after fileset definition.
2320 */
2321 static fileset_t *
parser_fileset_define_common(cmd_t * cmd)2322 parser_fileset_define_common(cmd_t *cmd)
2323 {
2324 	fileset_t *fileset;
2325 	attr_t *attr;
2326 	avd_t name;
2327 	avd_t path;
2328 
2329 	attr = get_attr(cmd, FSA_NAME);
2330 	if (attr)
2331 		name = attr->attr_avd;
2332 	else {
2333 		filebench_log(LOG_ERROR, "file[set] specifies no name");
2334 		return NULL;
2335 	}
2336 
2337 	attr = get_attr(cmd, FSA_PATH);
2338 	if (attr)
2339 		path = attr->attr_avd;
2340 	else {
2341 		filebench_log(LOG_ERROR, "file[set] specifies no path");
2342 		return NULL;
2343 	}
2344 
2345 	fileset = fileset_define(name, path);
2346 	if (!fileset) {
2347 		filebench_log(LOG_ERROR, "failed to instantiate file[set] %s\n",
2348 		    		avd_get_str(name));
2349 		return NULL;
2350 	}
2351 
2352 	attr = get_attr(cmd, FSA_PREALLOC);
2353 	if (attr)
2354 		fileset->fs_preallocpercent = attr->attr_avd;
2355 	else
2356 		fileset->fs_preallocpercent = avd_int_alloc(0);
2357 
2358 	attr = get_attr(cmd, FSA_PARALLOC);
2359 	if (attr)
2360 		fileset->fs_paralloc = attr->attr_avd;
2361 	else
2362 		fileset->fs_paralloc = avd_bool_alloc(FALSE);
2363 
2364 	attr = get_attr(cmd, FSA_READONLY);
2365 	if (attr)
2366 		fileset->fs_readonly = attr->attr_avd;
2367 	else
2368 		fileset->fs_readonly = avd_bool_alloc(FALSE);
2369 
2370 	attr = get_attr(cmd, FSA_WRITEONLY);
2371 	if (attr)
2372 		fileset->fs_writeonly = attr->attr_avd;
2373 	else
2374 		fileset->fs_writeonly = avd_bool_alloc(FALSE);
2375 
2376 	attr = get_attr(cmd, FSA_REUSE);
2377 	if (attr)
2378 		fileset->fs_reuse = attr->attr_avd;
2379 	else
2380 		fileset->fs_reuse = avd_bool_alloc(FALSE);
2381 
2382 	/* Should we check for files actual existance? */
2383 	attr = get_attr(cmd, FSA_TRUSTTREE);
2384 	if (attr )
2385 		fileset->fs_trust_tree = attr->attr_avd;
2386 	else
2387 		fileset->fs_trust_tree = avd_bool_alloc(FALSE);
2388 
2389 	attr = get_attr(cmd, FSA_SIZE);
2390 	if (attr)
2391 		fileset->fs_size = attr->attr_avd;
2392 	else
2393 		fileset->fs_size = avd_int_alloc(1024);
2394 
2395 	return fileset;
2396 }
2397 
2398 static void
parser_file_define(cmd_t * cmd)2399 parser_file_define(cmd_t *cmd)
2400 {
2401 	fileset_t *fileset;
2402 
2403 	fileset = parser_fileset_define_common(cmd);
2404 	if (!fileset) {
2405 		filebench_log(LOG_ERROR, "failed to instantiate file");
2406 		filebench_shutdown(1);
2407 		return;
2408 	}
2409 
2410 	/* fileset is emulating a single file */
2411 	fileset->fs_attrs = FILESET_IS_FILE;
2412 	fileset->fs_entries = avd_int_alloc(1);
2413 	/* Set the mean dir width to more than 1 */
2414 	fileset->fs_dirwidth = avd_int_alloc(10);
2415 	fileset->fs_dirgamma = avd_int_alloc(0);
2416 	fileset->fs_leafdirs = avd_int_alloc(0);
2417 }
2418 
2419 static void
parser_fileset_define(cmd_t * cmd)2420 parser_fileset_define(cmd_t *cmd)
2421 {
2422 	fileset_t *fileset;
2423 	attr_t *attr;
2424 
2425 	fileset = parser_fileset_define_common(cmd);
2426 	if (!fileset) {
2427 		filebench_log(LOG_ERROR, "failed to instantiate fileset");
2428 		filebench_shutdown(1);
2429 		return;
2430 	}
2431 
2432 	attr = get_attr(cmd, FSA_ENTRIES);
2433 	if (attr)
2434 		fileset->fs_entries = attr->attr_avd;
2435 	else
2436 		fileset->fs_entries = avd_int_alloc(0);
2437 
2438 	attr = get_attr(cmd, FSA_LEAFDIRS);
2439 	if (attr)
2440 		fileset->fs_leafdirs = attr->attr_avd;
2441 	else
2442 		fileset->fs_leafdirs = avd_int_alloc(0);
2443 
2444 	attr = get_attr(cmd, FSA_DIRWIDTH);
2445 	if (attr)
2446 		fileset->fs_dirwidth = attr->attr_avd;
2447 	else {
2448 		filebench_log(LOG_ERROR, "Fileset has no directory width");
2449 		fileset->fs_dirwidth = avd_int_alloc(0);
2450 	}
2451 
2452 	attr = get_attr(cmd, FSA_DIRDEPTHRV);
2453 	if (attr)
2454 		fileset->fs_dirdepthrv = attr->attr_avd;
2455 	else
2456 		fileset->fs_dirdepthrv = NULL;
2457 
2458 	attr = get_attr(cmd, FSA_DIRGAMMA);
2459 	if (attr)
2460 		fileset->fs_dirgamma = attr->attr_avd;
2461 	else
2462 		fileset->fs_dirgamma = avd_int_alloc(1500);
2463 }
2464 
2465 /*
2466  * Calls fileset_createsets() to populate all filesets and create all
2467  * associated, initially existant,  files and subdirectories.
2468  * If errors are encountered, calls filebench_shutdown() to exit Filebench.
2469  */
2470 static void
parser_fileset_create(cmd_t * cmd)2471 parser_fileset_create(cmd_t *cmd)
2472 {
2473 	int ret;
2474 
2475 	ret = fileset_createsets();
2476 	if (ret) {
2477 		filebench_log(LOG_ERROR, "Failed to create filesets");
2478 		filebench_shutdown(1);
2479 	}
2480 }
2481 
2482 /*
2483  * Ends filebench run after first destoring any interprocess
2484  * shared memory. The call to filebench_shutdown()
2485  * also causes filebench to exit.
2486  */
2487 static void
parser_filebench_shutdown(cmd_t * cmd)2488 parser_filebench_shutdown(cmd_t *cmd)
2489 {
2490 	int f_abort = filebench_shm->shm_f_abort;
2491 
2492 	ipc_fini();
2493 
2494 	if (f_abort == FILEBENCH_ABORT_ERROR)
2495 		filebench_shutdown(1);
2496 	else
2497 		filebench_shutdown(0);
2498 }
2499 
2500 /*
2501  * This is used for timing runs. Pauses the master thread in one second
2502  * intervals until the supplied ptime runs out or the f_abort flag
2503  * is raised. If given a time of zero, it will pause until f_abort is raised.
2504  */
2505 static int
parser_pause(int ptime)2506 parser_pause(int ptime)
2507 {
2508 	int timeslept = 0;
2509 
2510 	if (ptime) {
2511 		while (timeslept < ptime) {
2512 			(void) sleep(1);
2513 			timeslept++;
2514 			if (filebench_shm->shm_f_abort)
2515 				break;
2516 		}
2517 	} else {
2518 		/* initial runtime of 0 means run till abort */
2519 		/* CONSTCOND */
2520 		while (1) {
2521 			(void) sleep(1);
2522 			timeslept++;
2523 			if (filebench_shm->shm_f_abort)
2524 				break;
2525 		}
2526 	}
2527 
2528 	return (timeslept);
2529 }
2530 
2531 #define TIMED_RUNTIME_DEFAULT 60 /* In seconds */
2532 #define PERIOD_DEFAULT 10 /* In seconds */
2533 
2534 /*
2535  * Do a file bench run. Calls routines to create file sets, files, and
2536  * processes. It resets the statistics counters, then sleeps for the runtime
2537  * passed as an argument to it on the command line in 1 second increments.
2538  * When it is finished sleeping, it collects a snapshot of the statistics
2539  * and ends the run.
2540  */
2541 static void
parser_run(cmd_t * cmd)2542 parser_run(cmd_t *cmd)
2543 {
2544 	int runtime;
2545 	int timeslept;
2546 
2547 	runtime = cmd->cmd_qty;
2548 
2549 	parser_fileset_create(cmd);
2550 	proc_create();
2551 
2552 	/* check for startup errors */
2553 	if (filebench_shm->shm_f_abort)
2554 		return;
2555 
2556 	filebench_log(LOG_INFO, "Running...");
2557 	stats_clear();
2558 
2559 	/* If it is a timed mode and timeout is not specified use default */
2560 	if (filebench_shm->shm_rmode == FILEBENCH_MODE_TIMEOUT && !runtime)
2561 		runtime = TIMED_RUNTIME_DEFAULT;
2562 
2563 	timeslept = parser_pause(runtime);
2564 
2565 	filebench_log(LOG_INFO, "Run took %d seconds...", timeslept);
2566 	stats_snap();
2567 	proc_shutdown();
2568 	parser_filebench_shutdown((cmd_t *)0);
2569 }
2570 
2571 static void
parser_psrun(cmd_t * cmd)2572 parser_psrun(cmd_t *cmd)
2573 {
2574 	int runtime;
2575 	int period;
2576 	int timeslept = 0;
2577 	int reset_stats = 0;
2578 
2579 	runtime = cmd->cmd_qty;
2580 
2581 	/*
2582 	 * If period is negative then
2583 	 * we want to reset statistics
2584 	 * at the end of the every period
2585 	 */
2586 	if (cmd->cmd_qty1 < 0) {
2587 		period = -cmd->cmd_qty1;
2588 		reset_stats = 1;
2589 	} else if (cmd->cmd_qty1 > 0) {
2590 		period = cmd->cmd_qty1;
2591 		reset_stats = 0;
2592 	} else { /* (cmd->cmd_qty1) == 0 */
2593 		period = PERIOD_DEFAULT;
2594 		reset_stats = 0;
2595 	}
2596 
2597 	parser_fileset_create(cmd);
2598 	proc_create();
2599 
2600 	/* check for startup errors */
2601 	if (filebench_shm->shm_f_abort)
2602 		return;
2603 
2604 	filebench_log(LOG_INFO, "Running...");
2605 	stats_clear();
2606 
2607 	/* If it is a timed mode and timeout is not specified use default */
2608 	if (filebench_shm->shm_rmode == FILEBENCH_MODE_TIMEOUT && !runtime)
2609 		runtime = TIMED_RUNTIME_DEFAULT;
2610 
2611 	while (1) {
2612 		/* sleep the remaining time if a period is smaller */
2613 		if (filebench_shm->shm_rmode == FILEBENCH_MODE_TIMEOUT)
2614 			period = period > (runtime - timeslept) ?
2615 						(runtime - timeslept) : period;
2616 
2617 		timeslept += parser_pause(period);
2618 
2619 		if (filebench_shm->shm_f_abort)
2620 			break;
2621 
2622 		if (filebench_shm->shm_rmode == FILEBENCH_MODE_TIMEOUT &&
2623 							timeslept >= runtime)
2624 			break;
2625 
2626 		stats_snap();
2627 
2628 		if (reset_stats)
2629 			stats_clear();
2630 	}
2631 
2632 	filebench_log(LOG_INFO, "Run took %d seconds...", timeslept);
2633 	stats_snap();
2634 	proc_shutdown();
2635 	parser_filebench_shutdown((cmd_t *)0);
2636 }
2637 
2638 /*
2639  * Similar to parser_run, but gets the sleep time from a variable
2640  * whose name is supplied as an argument to the command.
2641  */
2642 static void
parser_run_variable(cmd_t * cmd)2643 parser_run_variable(cmd_t *cmd)
2644 {
2645 	avd_t integer = avd_var_alloc(cmd->cmd_tgt1);
2646 	int runtime;
2647 	int timeslept;
2648 
2649 	if (integer == NULL) {
2650 		filebench_log(LOG_ERROR, "Unknown variable %s",
2651 		cmd->cmd_tgt1);
2652 		return;
2653 	}
2654 
2655 	runtime = avd_get_int(integer);
2656 
2657 	parser_fileset_create(cmd);
2658 	proc_create();
2659 
2660 	/* check for startup errors */
2661 	if (filebench_shm->shm_f_abort)
2662 		return;
2663 
2664 	filebench_log(LOG_INFO, "Running...");
2665 	stats_clear();
2666 
2667 	/* If it is a timed mode and timeout is not specified use default */
2668 	if (filebench_shm->shm_rmode == FILEBENCH_MODE_TIMEOUT && !runtime)
2669 		runtime = TIMED_RUNTIME_DEFAULT;
2670 
2671 	timeslept = parser_pause(runtime);
2672 
2673 	filebench_log(LOG_INFO, "Run took %d seconds...", timeslept);
2674 	stats_snap();
2675 	proc_shutdown();
2676 	parser_filebench_shutdown((cmd_t *)0);
2677 }
2678 
2679 /*
2680  * Establishes multi-client synchronization socket with synch server.
2681  */
2682 static void
parser_enable_mc(cmd_t * cmd)2683 parser_enable_mc(cmd_t *cmd)
2684 {
2685 	attr_t *attr;
2686 	char *master;
2687 	char *client;
2688 
2689 	if ((attr = get_attr(cmd, FSA_MASTER))) {
2690 		master = avd_get_str(attr->attr_avd);
2691 	} else {
2692 		filebench_log(LOG_ERROR,
2693 		    "enable multi: no master specified");
2694 		return;
2695 	}
2696 
2697 	if ((attr = get_attr(cmd, FSA_CLIENT))) {
2698 		client = avd_get_str(attr->attr_avd);
2699 	} else {
2700 		filebench_log(LOG_ERROR,
2701 		    "enable multi: no client specified");
2702 		return;
2703 	}
2704 
2705 	mc_sync_open_sock(master, 8001, client);
2706 }
2707 
2708 /*
2709  * Exchanges multi-client synchronization message with synch server.
2710  */
2711 static void
parser_domultisync(cmd_t * cmd)2712 parser_domultisync(cmd_t *cmd)
2713 {
2714 	attr_t *attr;
2715 	fbint_t value;
2716 
2717 	if ((attr = get_attr(cmd, FSA_VALUE)))
2718 		value = avd_get_int(attr->attr_avd);
2719 	else
2720 		value = 1;
2721 
2722 	mc_sync_synchronize((int)value);
2723 }
2724 
2725 /*
2726  * Sleeps for cmd->cmd_qty seconds, one second at a time.
2727  */
2728 static void
parser_sleep(cmd_t * cmd)2729 parser_sleep(cmd_t *cmd)
2730 {
2731 	int sleeptime;
2732 	int timeslept;
2733 
2734 	/* check for startup errors */
2735 	if (filebench_shm->shm_f_abort)
2736 		return;
2737 
2738 	sleeptime = cmd->cmd_qty;
2739 	filebench_log(LOG_INFO, "Sleeping...");
2740 
2741 	timeslept = parser_pause(sleeptime);
2742 
2743 	filebench_log(LOG_INFO, "Slept for %d seconds...", timeslept);
2744 }
2745 
2746 /*
2747  * Same as parser_sleep, except the sleep time is obtained from a variable
2748  * whose name is passed to it as an argument on the command line.
2749  */
2750 static void
parser_sleep_variable(cmd_t * cmd)2751 parser_sleep_variable(cmd_t *cmd)
2752 {
2753 	avd_t integer = avd_var_alloc(cmd->cmd_tgt1);
2754 	int sleeptime;
2755 	int timeslept;
2756 
2757 	if (integer == NULL) {
2758 		filebench_log(LOG_ERROR, "Unknown variable %s",
2759 		cmd->cmd_tgt1);
2760 		return;
2761 	}
2762 
2763 	sleeptime = avd_get_int(integer);
2764 
2765 	/* check for startup errors */
2766 	if (filebench_shm->shm_f_abort)
2767 		return;
2768 
2769 	filebench_log(LOG_INFO, "Running...");
2770 
2771 	timeslept = parser_pause(sleeptime);
2772 
2773 	filebench_log(LOG_INFO, "Run took %d seconds...", timeslept);
2774 }
2775 
2776 /*
2777  * Launches a shell to run the unix command supplied in the argument.
2778  * The command should be enclosed in quotes, as in:
2779  * 	system "rm xyz"
2780  * which would run the "rm" utility to delete the file "xyz".
2781  */
2782 static void
parser_system(cmd_t * cmd)2783 parser_system(cmd_t *cmd)
2784 {
2785 	char *string;
2786 
2787 	if (!cmd->cmd_param_list)
2788 		return;
2789 
2790 	string = parser_list2string(cmd->cmd_param_list);
2791 
2792 	if (!string)
2793 		return;
2794 
2795 	filebench_log(LOG_VERBOSE, "Running '%s'", string);
2796 
2797 	if (system(string) < 0) {
2798 		filebench_log(LOG_ERROR,
2799 		    "system exec failed: %s",
2800 		    strerror(errno));
2801 		free(string);
2802 		filebench_shutdown(1);
2803 	}
2804 
2805 	free(string);
2806 }
2807 
2808 /*
2809  * Echos string supplied with command to the log.
2810  */
2811 static void
parser_echo(cmd_t * cmd)2812 parser_echo(cmd_t *cmd)
2813 {
2814 	char *string;
2815 
2816 	if (cmd->cmd_param_list == NULL)
2817 		return;
2818 
2819 	string = parser_list2string(cmd->cmd_param_list);
2820 
2821 	if (string == NULL)
2822 		return;
2823 
2824 	filebench_log(LOG_INFO, "%s", string);
2825 }
2826 
2827 /*
2828  * Prints out the version of Filebench.
2829  */
2830 static void
parser_version(cmd_t * cmd)2831 parser_version(cmd_t *cmd)
2832 {
2833 	filebench_log(LOG_INFO, "Filebench Version: %s", FILEBENCH_VERSION);
2834 }
2835 
2836 static void
parser_enable_lathist(cmd_t * cmd)2837 parser_enable_lathist(cmd_t *cmd)
2838 {
2839 	filebench_shm->lathist_enabled = 1;
2840 	filebench_log(LOG_INFO, "Latency histogram enabled");
2841 }
2842 
2843 /*
2844  * define a random variable and initialize the distribution parameters
2845  */
2846 static void
parser_var_assign_random(char * name,cmd_t * cmd)2847 parser_var_assign_random(char *name, cmd_t *cmd)
2848 {
2849 	randdist_t	*rndp;
2850 	attr_t		*attr;
2851 
2852 	rndp = randdist_alloc();
2853 	if (!rndp) {
2854 		filebench_log(LOG_ERROR,
2855 			"failed to alloc random distribution object\n");
2856 		return;
2857 	}
2858 
2859 	rndp->rnd_type = 0;
2860 
2861 	/* Get the source of the random numbers */
2862 	if ((attr = get_attr(cmd, FSA_RANDSRC))) {
2863 		int randsrc = (int)avd_get_int(attr->attr_avd);
2864 
2865 		switch (randsrc) {
2866 		case FSV_URAND:
2867 			rndp->rnd_type |= RAND_SRC_URANDOM;
2868 			break;
2869 		case FSV_RAND48:
2870 			rndp->rnd_type |= RAND_SRC_GENERATOR;
2871 			break;
2872 		}
2873 	} else {
2874 		/* default to rand48 random number generator */
2875 		rndp->rnd_type |= RAND_SRC_GENERATOR;
2876 	}
2877 
2878 	/* Get the min value of the random distribution */
2879 	if ((attr = get_attr(cmd, FSA_MIN)))
2880 		rndp->rnd_min = attr->attr_avd;
2881 	else
2882 		rndp->rnd_min = avd_int_alloc(0);
2883 
2884 	/* Get the roundoff value for the random distribution */
2885 	if ((attr = get_attr(cmd, FSA_ROUND)))
2886 		rndp->rnd_round = attr->attr_avd;
2887 	else
2888 		rndp->rnd_round = avd_int_alloc(0);
2889 
2890 	/* Get a tablular probablility distribution if there is one */
2891 	if ((attr = get_attr(cmd, FSA_RANDTABLE))) {
2892 		rndp->rnd_probtabs = (probtabent_t *)(attr->attr_obj);
2893 		rndp->rnd_type |= RAND_TYPE_TABLE;
2894 	} else {
2895 		rndp->rnd_probtabs = NULL;
2896 	}
2897 
2898 	/* Get the type for the random variable */
2899 	if ((attr = get_attr(cmd, FSA_TYPE))) {
2900 		int disttype = (int)avd_get_int(attr->attr_avd);
2901 
2902 		switch (disttype) {
2903 		case FSV_RANDUNI:
2904 			rndp->rnd_type |= RAND_TYPE_UNIFORM;
2905 			break;
2906 		case FSA_RANDGAMMA:
2907 			rndp->rnd_type |= RAND_TYPE_GAMMA;
2908 			break;
2909 		case FSV_RANDTAB:
2910 			filebench_log(LOG_ERROR,
2911 			    "Table distribution type without prob table");
2912 			break;
2913 		}
2914 	} else {
2915 		/* default to gamma distribution type */
2916 		rndp->rnd_type |= RAND_TYPE_GAMMA;
2917 	}
2918 
2919 	/* Get the seed for the random variable */
2920 	if ((attr = get_attr(cmd, FSA_RANDSEED)))
2921 		rndp->rnd_seed = attr->attr_avd;
2922 	else
2923 		rndp->rnd_seed = avd_int_alloc(0);
2924 
2925 	/* Get the gamma value of the random distribution */
2926 	if ((attr = get_attr(cmd, FSA_RANDGAMMA)))
2927 		rndp->rnd_gamma = attr->attr_avd;
2928 	else
2929 		rndp->rnd_gamma = avd_int_alloc(1500);
2930 
2931 	/* Get the mean value of the random distribution */
2932 	if ((attr = get_attr(cmd, FSA_RANDMEAN))) {
2933 		rndp->rnd_mean = attr->attr_avd;
2934 	} else if ((rndp->rnd_type & RAND_TYPE_MASK) == RAND_TYPE_GAMMA) {
2935 		rndp->rnd_mean = NULL;
2936 	} else {
2937 		rndp->rnd_mean = avd_int_alloc(0);
2938 	}
2939 
2940 	var_assign_random(name, rndp);
2941 
2942 	randdist_init(rndp);
2943 }
2944 
2945 /*
2946  * alloc_cmd() allocates the required resources for a cmd_t. On failure, a
2947  * filebench_log is issued and NULL is returned.
2948  */
2949 static cmd_t *
alloc_cmd(void)2950 alloc_cmd(void)
2951 {
2952 	cmd_t *cmd;
2953 
2954 	cmd = malloc(sizeof(*cmd));
2955 	if (!cmd) {
2956 		filebench_log(LOG_ERROR, "Alloc cmd failed");
2957 		return NULL;
2958 	}
2959 
2960 	memset(cmd, 0, sizeof (cmd_t));
2961 
2962 	return cmd;
2963 }
2964 
2965 /*
2966  * Allocates an attr_t structure and zeros it. Returns NULL on failure, or
2967  * a pointer to the attr_t.
2968  */
2969 static attr_t *
alloc_attr(void)2970 alloc_attr(void)
2971 {
2972 	attr_t *attr;
2973 
2974 	attr = malloc(sizeof(*attr));
2975 	if (!attr)
2976 		return (NULL);
2977 
2978 	(void) memset(attr, 0, sizeof(*attr));
2979 
2980 	return (attr);
2981 }
2982 
2983 /*
2984  * Allocates a probtabent_t structure and zeros it. Returns NULL on failure, or
2985  * a pointer to the probtabent_t.
2986  */
2987 static probtabent_t *
alloc_probtabent(void)2988 alloc_probtabent(void)
2989 {
2990 	probtabent_t *rte;
2991 
2992 	if ((rte = malloc(sizeof (probtabent_t))) == NULL) {
2993 		return (NULL);
2994 	}
2995 
2996 	(void) memset(rte, 0, sizeof (probtabent_t));
2997 	return (rte);
2998 }
2999 
3000 /*
3001  * Allocates an attr_t structure and puts the supplied var_t into
3002  * its attr_avd location, and sets its name to FSA_LVAR_ASSIGN
3003  */
3004 static attr_t *
alloc_lvar_attr(var_t * var)3005 alloc_lvar_attr(var_t *var)
3006 {
3007 	attr_t *attr;
3008 
3009 	if ((attr = alloc_attr()) == NULL)
3010 		return (NULL);
3011 
3012 	attr->attr_name = FSA_LVAR_ASSIGN;
3013 	attr->attr_avd = (avd_t)var;
3014 
3015 	return (attr);
3016 }
3017 
3018 /*
3019  * Searches the attribute list for the command for the named attribute type.
3020  * The attribute list is created by the parser from the list of attributes
3021  * supplied with certain commands, such as the define and flowop commands.
3022  * Returns a pointer to the attribute structure if the named attribute is
3023  * found, otherwise returns NULL. If the attribute includes a parameter list,
3024  * the list is converted to a string and stored in the attr_avd field of
3025  * the returned attr_t struct.
3026  */
3027 static attr_t *
get_attr(cmd_t * cmd,int64_t name)3028 get_attr(cmd_t *cmd, int64_t name)
3029 {
3030 	attr_t *attr;
3031 	attr_t *rtn = NULL;
3032 
3033 	for (attr = cmd->cmd_attr_list; attr != NULL;
3034 	    attr = attr->attr_next) {
3035 
3036 		filebench_log(LOG_DEBUG_IMPL,
3037 		    "attr %d = %d %llx?",
3038 		    attr->attr_name,
3039 		    name,
3040 		    attr->attr_avd);
3041 
3042 		if (attr->attr_name == name)
3043 			rtn = attr;
3044 	}
3045 
3046 	return rtn;
3047 }
3048 
3049 /*
3050  * removes the newly allocated local var from the shared local var
3051  * list, then puts it at the head of the private local var list
3052  * supplied as the second argument.
3053  */
3054 static void
add_lvar_to_list(var_t * newlvar,var_t ** lvar_list)3055 add_lvar_to_list(var_t *newlvar, var_t **lvar_list)
3056 {
3057 	var_t *prev;
3058 
3059 	/* remove from shared local list, if there */
3060 	if (newlvar == filebench_shm->shm_var_loc_list) {
3061 		/* on top of list, just grap */
3062 		filebench_shm->shm_var_loc_list = newlvar->var_next;
3063 	} else {
3064 		/* find newvar on list and remove */
3065 		for (prev = filebench_shm->shm_var_loc_list; prev;
3066 		    prev = prev->var_next) {
3067 			if (prev->var_next == newlvar)
3068 				prev->var_next = newlvar->var_next;
3069 		}
3070 	}
3071 	newlvar->var_next = NULL;
3072 
3073 	/* add to flowop private local list at head */
3074 	newlvar->var_next = *lvar_list;
3075 	*lvar_list = newlvar;
3076 }
3077 
3078 /*
3079  * Searches the attribute list for the command for any allocated local
3080  * variables. The attribute list is created by the parser from the list of
3081  * attributes supplied with certain commands, such as the define and flowop
3082  * commands. Places all found local vars onto the flowop's local variable
3083  * list.
3084  */
3085 static void
get_attr_lvars(cmd_t * cmd,flowop_t * flowop)3086 get_attr_lvars(cmd_t *cmd, flowop_t *flowop)
3087 {
3088 	attr_t *attr;
3089 	var_t *orig_lvar_list;
3090 
3091 	/* save the local var list */
3092 	orig_lvar_list = flowop->fo_lvar_list;
3093 
3094 	for (attr = cmd->cmd_attr_list; attr != NULL;
3095 	    attr = attr->attr_next) {
3096 
3097 		if (attr->attr_name == FSA_LVAR_ASSIGN) {
3098 			var_t *newvar;
3099 
3100 			if ((newvar = (var_t *)attr->attr_avd) == NULL)
3101 				continue;
3102 
3103 			add_lvar_to_list(newvar, &flowop->fo_lvar_list);
3104 			var_update_comp_lvars(newvar, orig_lvar_list, NULL);
3105 		}
3106 	}
3107 }
3108 
3109 /*
3110  * Allocates memory for a list_t structure, initializes it to zero, and
3111  * returns a pointer to it. On failure, returns NULL.
3112  */
3113 static list_t *
alloc_list()3114 alloc_list()
3115 {
3116 	list_t *list;
3117 
3118 	if ((list = malloc(sizeof (list_t))) == NULL) {
3119 		return (NULL);
3120 	}
3121 
3122 	(void) memset(list, 0, sizeof (list_t));
3123 	return (list);
3124 }
3125 
3126 /*
3127  * Define a custom variable and validate its parameters.
3128  * TODO: Clean up state when things go wrong.
3129  */
3130 static void
parser_var_assign_custom(char * name,cmd_t * cmd)3131 parser_var_assign_custom(char *name, cmd_t *cmd)
3132 {
3133 	cvar_t	*cvar;
3134 	attr_t	*attr;
3135 	char	*type;
3136 	char	*parameters;
3137 	int 	ret;
3138 
3139 	attr = get_attr(cmd, FSA_TYPE);
3140 	if (attr)
3141 		type = avd_get_str(attr->attr_avd);
3142 	else {
3143 		filebench_log(LOG_ERROR, "define cvar: no type specified");
3144 		filebench_shutdown(1);
3145 		return;
3146 	}
3147 
3148 	cvar = cvar_alloc();
3149 	if (!cvar) {
3150 		filebench_log(LOG_ERROR, "Failed to allocate custom variable");
3151 		filebench_shutdown(1);
3152 		return;
3153 	}
3154 
3155 	/* Initialize the custom variable mutex. */
3156 	(void) pthread_mutex_init(&cvar->cvar_lock,
3157 			ipc_mutexattr(IPC_MUTEX_NORMAL));
3158 
3159 	/* Get the min, max and round values for the custom variable. */
3160 	if ((attr = get_attr(cmd, FSA_MIN)))
3161 		cvar->min = avd_get_dbl(attr->attr_avd);
3162 	else
3163 		cvar->min = DBL_MIN;
3164 
3165 	if ((attr = get_attr(cmd, FSA_MAX)))
3166 		cvar->max = avd_get_dbl(attr->attr_avd);
3167 	else
3168 		cvar->max = DBL_MAX;
3169 
3170 	if ((attr = get_attr(cmd, FSA_ROUND)))
3171 		cvar->round = avd_get_dbl(attr->attr_avd);
3172 	else
3173 		cvar->round = 0;
3174 
3175 	attr = get_attr(cmd, FSA_PARAMETERS);
3176 	if (attr)
3177 		parameters = avd_get_str(attr->attr_avd);
3178 	else
3179 		parameters = NULL;
3180 
3181 	ret = init_cvar_handle(cvar, type, parameters);
3182 	if (ret) {
3183 		filebench_log(LOG_FATAL, "define cvar: failed for custom variable %s",
3184 		    name);
3185 		filebench_shutdown(1);
3186 		return;
3187 	}
3188 
3189 	var_assign_custom(name, cvar);
3190 }
3191