1 2 /* 3 * CDDL HEADER START 4 * 5 * The contents of this file are subject to the terms of the 6 * Common Development and Distribution License (the "License"). 7 * You may not use this file except in compliance with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 %{ 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 %} 30 31 %{ 32 33 #include <stdlib.h> 34 #include <stdio.h> 35 #include <string.h> 36 #include <signal.h> 37 #include <errno.h> 38 #include <sys/types.h> 39 #include <locale.h> 40 #include <sys/utsname.h> 41 #ifdef HAVE_STDINT_H 42 #include <stdint.h> 43 #endif 44 #include <fcntl.h> 45 #include <sys/mman.h> 46 #include <sys/wait.h> 47 #ifdef HAVE_LIBTECLA 48 #include <libtecla.h> 49 #endif 50 #include "parsertypes.h" 51 #include "filebench.h" 52 #include "utils.h" 53 #include "stats.h" 54 #include "vars.h" 55 #include "eventgen.h" 56 #ifdef HAVE_LIBTECLA 57 #include "auto_comp.h" 58 #endif 59 60 int dofile = FS_FALSE; 61 static const char cmdname[] = "filebench"; 62 static const char cmd_options[] = "pa:f:hi:s:m:"; 63 static void usage(int); 64 65 static cmd_t *cmd = NULL; /* Command being processed */ 66 #ifdef HAVE_LIBTECLA 67 static GetLine *gl; /* GetLine resource object */ 68 #endif 69 70 char *execname; 71 char *fscriptname; 72 int noproc = 0; 73 var_t *var_list = NULL; 74 pidlist_t *pidlist = NULL; 75 char *cwd = NULL; 76 FILE *parentscript = NULL; 77 78 static int filecreate_done = 0; 79 80 /* yacc externals */ 81 extern FILE *yyin; 82 extern int yydebug; 83 extern void yyerror(char *s); 84 85 /* utilities */ 86 static void terminate(void); 87 static cmd_t *alloc_cmd(void); 88 static attr_t *alloc_attr(void); 89 static attr_t *get_attr(cmd_t *cmd, int64_t name); 90 static attr_t *get_attr_integer(cmd_t *cmd, int64_t name); 91 static attr_t *get_attr_bool(cmd_t *cmd, int64_t name); 92 static var_t *alloc_var(void); 93 static var_t *get_var(cmd_t *cmd, int64_t name); 94 static list_t *alloc_list(); 95 96 /* Info Commands */ 97 static void parser_list(cmd_t *); 98 99 /* Define Commands */ 100 static void parser_proc_define(cmd_t *); 101 static void parser_thread_define(cmd_t *, procflow_t *, int instances); 102 static void parser_flowop_define(cmd_t *, threadflow_t *); 103 static void parser_file_define(cmd_t *); 104 static void parser_fileset_define(cmd_t *); 105 106 /* Create Commands */ 107 static void parser_proc_create(cmd_t *); 108 static void parser_thread_create(cmd_t *); 109 static void parser_flowop_create(cmd_t *); 110 static void parser_fileset_create(cmd_t *); 111 112 /* Shutdown Commands */ 113 static void parser_proc_shutdown(cmd_t *); 114 static void parser_filebench_shutdown(cmd_t *cmd); 115 116 /* Other Commands */ 117 static void parser_foreach_integer(cmd_t *cmd); 118 static void parser_foreach_string(cmd_t *cmd); 119 static void parser_sleep(cmd_t *cmd); 120 static void parser_sleep_variable(cmd_t *cmd); 121 static void parser_log(cmd_t *cmd); 122 static void parser_statscmd(cmd_t *cmd); 123 static void parser_statsdump(cmd_t *cmd); 124 static void parser_statsxmldump(cmd_t *cmd); 125 static void parser_echo(cmd_t *cmd); 126 static void parser_usage(cmd_t *cmd); 127 static void parser_vars(cmd_t *cmd); 128 static void parser_printvars(cmd_t *cmd); 129 static void parser_system(cmd_t *cmd); 130 static void parser_statssnap(cmd_t *cmd); 131 static void parser_directory(cmd_t *cmd); 132 static void parser_eventgen(cmd_t *cmd); 133 static void parser_run(cmd_t *cmd); 134 static void parser_run_variable(cmd_t *cmd); 135 static void parser_help(cmd_t *cmd); 136 static void arg_parse(const char *command); 137 static void parser_abort(int arg); 138 139 %} 140 141 %union { 142 int64_t ival; 143 uchar_t bval; 144 char * sval; 145 fs_u val; 146 cmd_t *cmd; 147 attr_t *attr; 148 list_t *list; 149 } 150 151 %start commands 152 153 %token FSC_LIST FSC_DEFINE FSC_EXEC FSC_QUIT FSC_DEBUG FSC_CREATE 154 %token FSC_SLEEP FSC_STATS FSC_FOREACH FSC_SET FSC_SHUTDOWN FSC_LOG 155 %token FSC_SYSTEM FSC_FLOWOP FSC_EVENTGEN FSC_ECHO FSC_LOAD FSC_RUN 156 %token FSC_USAGE FSC_HELP FSC_VARS 157 %token FSV_STRING FSV_VAL_INT FSV_VAL_BOOLEAN FSV_VARIABLE FSV_WHITESTRING 158 %token FST_INT FST_BOOLEAN 159 %token FSE_FILE FSE_PROC FSE_THREAD FSE_CLEAR FSE_ALL FSE_SNAP FSE_DUMP 160 %token FSE_DIRECTORY FSE_COMMAND FSE_FILESET FSE_XMLDUMP 161 %token FSK_SEPLST FSK_OPENLST FSK_CLOSELST FSK_ASSIGN FSK_IN FSK_QUOTE 162 %token FSK_DIRSEPLST 163 %token FSA_SIZE FSA_PREALLOC FSA_PARALLOC FSA_PATH FSA_REUSE 164 %token FSA_PROCESS FSA_MEMSIZE FSA_RATE FSA_CACHED 165 %token FSA_IOSIZE FSA_FILE FSA_WSS FSA_NAME FSA_RANDOM FSA_INSTANCES 166 %token FSA_DSYNC FSA_TARGET FSA_ITERS FSA_NICE FSA_VALUE FSA_BLOCKING 167 %token FSA_HIGHWATER FSA_DIRECTIO FSA_DIRWIDTH FSA_FD FSA_SRCFD FSA_ROTATEFD 168 %token FSA_NAMELENGTH FSA_FILESIZE FSA_ENTRIES FSA_FILESIZEGAMMA 169 %token FSA_DIRGAMMA FSA_USEISM 170 171 %type <ival> FSV_VAL_INT 172 %type <bval> FSV_VAL_BOOLEAN 173 %type <sval> FSV_STRING 174 %type <sval> FSV_WHITESTRING 175 %type <sval> FSV_VARIABLE 176 %type <sval> FSK_ASSIGN 177 178 %type <ival> FSC_LIST FSC_DEFINE FSC_SET FSC_LOAD FSC_RUN 179 %type <ival> FSE_FILE FSE_PROC FSE_THREAD FSE_CLEAR FSC_HELP 180 181 %type <sval> name 182 %type <ival> entity 183 %type <val> value 184 185 %type <cmd> command inner_commands load_command run_command 186 %type <cmd> list_command define_command debug_command create_command 187 %type <cmd> sleep_command stats_command set_command shutdown_command 188 %type <cmd> foreach_command log_command system_command flowop_command 189 %type <cmd> eventgen_command quit_command flowop_list thread_list 190 %type <cmd> thread echo_command usage_command help_command vars_command 191 192 %type <attr> attr_op attr_ops 193 %type <attr> attr_value 194 %type <list> integer_seplist string_seplist string_list var_string_list var_string 195 %type <list> whitevar_string whitevar_string_list 196 %type <ival> attrs_define_file attrs_define_thread attrs_flowop attrs_define_fileset 197 %type <ival> attrs_define_proc attrs_eventgen 198 %type <ival> attr_name 199 200 %% 201 202 commands: commands command 203 { 204 list_t *list = NULL; 205 list_t *list_end = NULL; 206 207 if ($2->cmd != NULL) 208 $2->cmd($2); 209 210 free($2); 211 } 212 | commands error 213 { 214 if (dofile) 215 YYABORT; 216 } 217 |; 218 219 inner_commands: command 220 { 221 filebench_log(LOG_DEBUG_IMPL, "inner_command %zx", $1); 222 $$ = $1; 223 } 224 | inner_commands command 225 { 226 cmd_t *list = NULL; 227 cmd_t *list_end = NULL; 228 229 /* Find end of list */ 230 for (list = $1; list != NULL; 231 list = list->cmd_next) 232 list_end = list; 233 234 list_end->cmd_next = $2; 235 236 filebench_log(LOG_DEBUG_IMPL, 237 "inner_commands adding cmd %zx to list %zx", $2, $1); 238 239 $$ = $1; 240 }; 241 242 command: 243 define_command 244 | debug_command 245 | eventgen_command 246 | create_command 247 | echo_command 248 | usage_command 249 | vars_command 250 | foreach_command 251 | help_command 252 | list_command 253 | load_command 254 | log_command 255 | run_command 256 | set_command 257 | shutdown_command 258 | sleep_command 259 | stats_command 260 | system_command 261 | quit_command; 262 263 foreach_command: FSC_FOREACH 264 { 265 if (($$ = alloc_cmd()) == NULL) 266 YYERROR; 267 filebench_log(LOG_DEBUG_IMPL, "foreach_command %zx", $$); 268 } 269 | foreach_command FSV_VARIABLE FSK_IN integer_seplist FSK_OPENLST inner_commands FSK_CLOSELST 270 { 271 cmd_t *cmd, *inner_cmd; 272 list_t *list; 273 274 $$ = $1; 275 $$->cmd_list = $6; 276 $$->cmd_tgt1 = $2; 277 $$->cmd_param_list = $4; 278 $$->cmd = parser_foreach_integer; 279 280 for (list = $$->cmd_param_list; list != NULL; 281 list = list->list_next) { 282 for (inner_cmd = $$->cmd_list; 283 inner_cmd != NULL; 284 inner_cmd = inner_cmd->cmd_next) { 285 filebench_log(LOG_DEBUG_IMPL, 286 "packing foreach: %zx %s=%lld, cmd %zx", 287 $$, 288 $$->cmd_tgt1, 289 *list->list_integer, inner_cmd); 290 } 291 } 292 }| foreach_command FSV_VARIABLE FSK_IN string_seplist FSK_OPENLST inner_commands FSK_CLOSELST 293 { 294 cmd_t *cmd, *inner_cmd; 295 list_t *list; 296 297 $$ = $1; 298 $$->cmd_list = $6; 299 $$->cmd_tgt1 = $2; 300 $$->cmd_param_list = $4; 301 $$->cmd = parser_foreach_string; 302 303 for (list = $$->cmd_param_list; list != NULL; 304 list = list->list_next) { 305 for (inner_cmd = $$->cmd_list; 306 inner_cmd != NULL; 307 inner_cmd = inner_cmd->cmd_next) { 308 filebench_log(LOG_DEBUG_IMPL, 309 "packing foreach: %zx %s=%s, cmd %zx", 310 $$, 311 $$->cmd_tgt1, 312 *list->list_string, inner_cmd); 313 } 314 } 315 }; 316 317 integer_seplist: FSV_VAL_INT 318 { 319 if (($$ = alloc_list()) == NULL) 320 YYERROR; 321 322 $$->list_integer = integer_alloc($1); 323 } 324 | integer_seplist FSK_SEPLST FSV_VAL_INT 325 { 326 list_t *list = NULL; 327 list_t *list_end = NULL; 328 329 if (($$ = alloc_list()) == NULL) 330 YYERROR; 331 332 $$->list_integer = integer_alloc($3); 333 334 /* Find end of list */ 335 for (list = $1; list != NULL; 336 list = list->list_next) 337 list_end = list; 338 list_end->list_next = $$; 339 $$ = $1; 340 }; 341 342 string_seplist: FSK_QUOTE FSV_WHITESTRING FSK_QUOTE 343 { 344 if (($$ = alloc_list()) == NULL) 345 YYERROR; 346 347 $$->list_string = string_alloc($2); 348 } 349 | string_seplist FSK_SEPLST FSK_QUOTE FSV_WHITESTRING FSK_QUOTE 350 { 351 list_t *list = NULL; 352 list_t *list_end = NULL; 353 354 if (($$ = alloc_list()) == NULL) 355 YYERROR; 356 357 $$->list_string = string_alloc($4); 358 359 /* Find end of list */ 360 for (list = $1; list != NULL; 361 list = list->list_next) 362 list_end = list; 363 list_end->list_next = $$; 364 $$ = $1; 365 }; 366 367 eventgen_command: FSC_EVENTGEN 368 { 369 if (($$ = alloc_cmd()) == NULL) 370 YYERROR; 371 $$->cmd = &parser_eventgen; 372 } 373 | eventgen_command attr_ops 374 { 375 $1->cmd_attr_list = $2; 376 }; 377 378 system_command: FSC_SYSTEM whitevar_string_list 379 { 380 if (($$ = alloc_cmd()) == NULL) 381 YYERROR; 382 383 $$->cmd_param_list = $2; 384 $$->cmd = parser_system; 385 }; 386 387 echo_command: FSC_ECHO whitevar_string_list 388 { 389 if (($$ = alloc_cmd()) == NULL) 390 YYERROR; 391 392 $$->cmd_param_list = $2; 393 $$->cmd = parser_echo; 394 }; 395 396 usage_command: FSC_USAGE whitevar_string_list 397 { 398 if (($$ = alloc_cmd()) == NULL) 399 YYERROR; 400 401 $$->cmd_param_list = $2; 402 $$->cmd = parser_usage; 403 }; 404 405 vars_command: FSC_VARS 406 { 407 if (($$ = alloc_cmd()) == NULL) 408 YYERROR; 409 410 $$->cmd = parser_printvars; 411 }; 412 413 string_list: FSV_VARIABLE 414 { 415 if (($$ = alloc_list()) == NULL) 416 YYERROR; 417 418 $$->list_string = string_alloc($1); 419 } 420 | string_list FSK_SEPLST FSV_VARIABLE 421 { 422 list_t *list = NULL; 423 list_t *list_end = NULL; 424 425 if (($$ = alloc_list()) == NULL) 426 YYERROR; 427 428 $$->list_string = string_alloc($3); 429 430 /* Find end of list */ 431 for (list = $1; list != NULL; 432 list = list->list_next) 433 list_end = list; 434 list_end->list_next = $$; 435 $$ = $1; 436 }; 437 438 var_string: FSV_VARIABLE 439 { 440 if (($$ = alloc_list()) == NULL) 441 YYERROR; 442 443 $$->list_string = string_alloc($1); 444 } 445 | FSV_STRING 446 { 447 if (($$ = alloc_list()) == NULL) 448 YYERROR; 449 450 $$->list_string = string_alloc($1); 451 }; 452 453 var_string_list: var_string 454 { 455 $$ = $1; 456 }| var_string FSV_STRING 457 { 458 list_t *list = NULL; 459 list_t *list_end = NULL; 460 461 /* Add string */ 462 if (($$ = alloc_list()) == NULL) 463 YYERROR; 464 465 $$->list_string = string_alloc($2); 466 467 /* Find end of list */ 468 for (list = $1; list != NULL; 469 list = list->list_next) 470 list_end = list; 471 list_end->list_next = $$; 472 $$ = $1; 473 474 }| var_string FSV_VARIABLE 475 { 476 list_t *list = NULL; 477 list_t *list_end = NULL; 478 479 /* Add variable */ 480 if (($$ = alloc_list()) == NULL) 481 YYERROR; 482 483 $$->list_string = string_alloc($2); 484 485 /* Find end of list */ 486 for (list = $1; list != NULL; 487 list = list->list_next) 488 list_end = list; 489 list_end->list_next = $$; 490 $$ = $1; 491 } |var_string_list FSV_STRING 492 { 493 list_t *list = NULL; 494 list_t *list_end = NULL; 495 496 /* Add string */ 497 if (($$ = alloc_list()) == NULL) 498 YYERROR; 499 500 $$->list_string = string_alloc($2); 501 502 /* Find end of list */ 503 for (list = $1; list != NULL; 504 list = list->list_next) 505 list_end = list; 506 list_end->list_next = $$; 507 $$ = $1; 508 509 }| var_string_list FSV_VARIABLE 510 { 511 list_t *list = NULL; 512 list_t *list_end = NULL; 513 514 /* Add variable */ 515 if (($$ = alloc_list()) == NULL) 516 YYERROR; 517 518 $$->list_string = string_alloc($2); 519 520 /* Find end of list */ 521 for (list = $1; list != NULL; 522 list = list->list_next) 523 list_end = list; 524 list_end->list_next = $$; 525 $$ = $1; 526 }; 527 528 whitevar_string: FSK_QUOTE FSV_VARIABLE 529 { 530 if (($$ = alloc_list()) == NULL) 531 YYERROR; 532 533 $$->list_string = string_alloc($2); 534 } 535 | FSK_QUOTE FSV_WHITESTRING 536 { 537 if (($$ = alloc_list()) == NULL) 538 YYERROR; 539 540 $$->list_string = string_alloc($2); 541 }; 542 543 whitevar_string_list: whitevar_string FSV_WHITESTRING 544 { 545 list_t *list = NULL; 546 list_t *list_end = NULL; 547 548 /* Add string */ 549 if (($$ = alloc_list()) == NULL) 550 YYERROR; 551 552 $$->list_string = string_alloc($2); 553 554 /* Find end of list */ 555 for (list = $1; list != NULL; 556 list = list->list_next) 557 list_end = list; 558 list_end->list_next = $$; 559 $$ = $1; 560 561 }| whitevar_string FSV_VARIABLE 562 { 563 list_t *list = NULL; 564 list_t *list_end = NULL; 565 566 /* Add variable */ 567 if (($$ = alloc_list()) == NULL) 568 YYERROR; 569 570 $$->list_string = string_alloc($2); 571 572 /* Find end of list */ 573 for (list = $1; list != NULL; 574 list = list->list_next) 575 list_end = list; 576 list_end->list_next = $$; 577 $$ = $1; 578 } |whitevar_string_list FSV_WHITESTRING 579 { 580 list_t *list = NULL; 581 list_t *list_end = NULL; 582 583 /* Add string */ 584 if (($$ = alloc_list()) == NULL) 585 YYERROR; 586 587 $$->list_string = string_alloc($2); 588 589 /* Find end of list */ 590 for (list = $1; list != NULL; 591 list = list->list_next) 592 list_end = list; 593 list_end->list_next = $$; 594 $$ = $1; 595 596 }| whitevar_string_list FSV_VARIABLE 597 { 598 list_t *list = NULL; 599 list_t *list_end = NULL; 600 601 /* Add variable */ 602 if (($$ = alloc_list()) == NULL) 603 YYERROR; 604 605 $$->list_string = string_alloc($2); 606 607 /* Find end of list */ 608 for (list = $1; list != NULL; 609 list = list->list_next) 610 list_end = list; 611 list_end->list_next = $$; 612 $$ = $1; 613 }| whitevar_string_list FSK_QUOTE 614 { 615 $$ = $1; 616 }| whitevar_string FSK_QUOTE 617 { 618 $$ = $1; 619 }; 620 621 list_command: FSC_LIST 622 { 623 if (($$ = alloc_cmd()) == NULL) 624 YYERROR; 625 $$->cmd = &parser_list; 626 }; 627 628 log_command: FSC_LOG whitevar_string_list 629 { 630 if (($$ = alloc_cmd()) == NULL) 631 YYERROR; 632 $$->cmd = &parser_log; 633 $$->cmd_param_list = $2; 634 }; 635 636 debug_command: FSC_DEBUG FSV_VAL_INT 637 { 638 if (($$ = alloc_cmd()) == NULL) 639 YYERROR; 640 $$->cmd = NULL; 641 filebench_shm->debug_level = $2; 642 if (filebench_shm->debug_level > 9) 643 yydebug = 1; 644 }; 645 646 set_command: FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_VAL_INT 647 { 648 if (($$ = alloc_cmd()) == NULL) 649 YYERROR; 650 var_assign_integer($2, $4); 651 if (parentscript) { 652 $$->cmd_tgt1 = $2; 653 parser_vars($$); 654 } 655 $$->cmd = NULL; 656 } 657 | FSC_SET FSV_VARIABLE FSK_ASSIGN FSK_QUOTE FSV_WHITESTRING FSK_QUOTE 658 { 659 if (($$ = alloc_cmd()) == NULL) 660 YYERROR; 661 var_assign_string($2, $5); 662 if (parentscript) { 663 $$->cmd_tgt1 = $2; 664 parser_vars($$); 665 } 666 $$->cmd = NULL; 667 }| FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_STRING 668 { 669 if (($$ = alloc_cmd()) == NULL) 670 YYERROR; 671 var_assign_string($2, $4); 672 if (parentscript) { 673 $$->cmd_tgt1 = $2; 674 parser_vars($$); 675 } 676 $$->cmd = NULL; 677 }| FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_VARIABLE 678 { 679 if (($$ = alloc_cmd()) == NULL) 680 YYERROR; 681 var_assign_var($2, $4); 682 if (parentscript) { 683 $$->cmd_tgt1 = $2; 684 parser_vars($$); 685 } 686 $$->cmd = NULL; 687 }; 688 689 stats_command: FSC_STATS FSE_SNAP 690 { 691 if (($$ = alloc_cmd()) == NULL) 692 YYERROR; 693 $$->cmd = (void (*)(struct cmd *))&parser_statssnap; 694 break; 695 696 } 697 | FSC_STATS FSE_CLEAR 698 { 699 if (($$ = alloc_cmd()) == NULL) 700 YYERROR; 701 $$->cmd = (void (*)(struct cmd *))&stats_clear; 702 703 } 704 | FSC_STATS FSE_DIRECTORY var_string_list 705 { 706 if (($$ = alloc_cmd()) == NULL) 707 YYERROR; 708 $$->cmd_param_list = $3; 709 $$->cmd = (void (*)(struct cmd *))&parser_directory; 710 711 } 712 | FSC_STATS FSE_COMMAND whitevar_string_list 713 { 714 if (($$ = alloc_cmd()) == NULL) 715 YYERROR; 716 717 $$->cmd_param_list = $3; 718 $$->cmd = parser_statscmd; 719 720 }| FSC_STATS FSE_DUMP whitevar_string_list 721 { 722 if (($$ = alloc_cmd()) == NULL) 723 YYERROR; 724 725 $$->cmd_param_list = $3; 726 $$->cmd = parser_statsdump; 727 }| FSC_STATS FSE_XMLDUMP whitevar_string_list 728 { 729 if (($$ = alloc_cmd()) == NULL) 730 YYERROR; 731 732 $$->cmd_param_list = $3; 733 $$->cmd = parser_statsxmldump; 734 }; 735 736 quit_command: FSC_QUIT 737 { 738 if (($$ = alloc_cmd()) == NULL) 739 YYERROR; 740 $$->cmd = parser_filebench_shutdown; 741 }; 742 743 flowop_list: flowop_command 744 { 745 $$ = $1; 746 }| flowop_list flowop_command 747 { 748 cmd_t *list = NULL; 749 cmd_t *list_end = NULL; 750 751 /* Find end of list */ 752 for (list = $1; list != NULL; 753 list = list->cmd_next) 754 list_end = list; 755 756 list_end->cmd_next = $2; 757 758 filebench_log(LOG_DEBUG_IMPL, 759 "flowop_list adding cmd %zx to list %zx", $2, $1); 760 761 $$ = $1; 762 }; 763 764 thread: FSE_THREAD attr_ops FSK_OPENLST flowop_list FSK_CLOSELST 765 { 766 /* 767 * Allocate a cmd node per thread, with a 768 * list of flowops attached to the cmd_list 769 */ 770 if (($$ = alloc_cmd()) == NULL) 771 YYERROR; 772 $$->cmd_list = $4; 773 $$->cmd_attr_list = $2; 774 }; 775 776 thread_list: thread 777 { 778 $$ = $1; 779 }| thread_list thread 780 { 781 cmd_t *list = NULL; 782 cmd_t *list_end = NULL; 783 784 /* Find end of list */ 785 for (list = $1; list != NULL; 786 list = list->cmd_next) 787 list_end = list; 788 789 list_end->cmd_next = $2; 790 791 filebench_log(LOG_DEBUG_IMPL, 792 "thread_list adding cmd %zx to list %zx", $2, $1); 793 794 $$ = $1; 795 }; 796 797 define_command: FSC_DEFINE FSE_PROC attr_ops FSK_OPENLST thread_list FSK_CLOSELST 798 { 799 if (($$ = alloc_cmd()) == NULL) 800 YYERROR; 801 $$->cmd = &parser_proc_define; 802 $$->cmd_list = $5; 803 $$->cmd_attr_list = $3; 804 805 }| FSC_DEFINE FSE_FILE 806 { 807 if (($$ = alloc_cmd()) == NULL) 808 YYERROR; 809 $$->cmd = &parser_file_define; 810 }| FSC_DEFINE FSE_FILESET 811 { 812 if (($$ = alloc_cmd()) == NULL) 813 YYERROR; 814 $$->cmd = &parser_fileset_define; 815 } 816 | define_command attr_ops 817 { 818 $1->cmd_attr_list = $2; 819 }; 820 821 create_command: FSC_CREATE entity 822 { 823 if (($$ = alloc_cmd()) == NULL) 824 YYERROR; 825 switch ($2) { 826 case FSE_PROC: 827 $$->cmd = &parser_proc_create; 828 break; 829 case FSE_FILESET: 830 case FSE_FILE: 831 $$->cmd = &parser_fileset_create; 832 break; 833 default: 834 filebench_log(LOG_ERROR, "unknown entity", $2); 835 YYERROR; 836 } 837 838 }; 839 840 shutdown_command: FSC_SHUTDOWN entity 841 { 842 if (($$ = alloc_cmd()) == NULL) 843 YYERROR; 844 switch ($2) { 845 case FSE_PROC: 846 $$->cmd = &parser_proc_shutdown; 847 break; 848 default: 849 filebench_log(LOG_ERROR, "unknown entity", $2); 850 YYERROR; 851 } 852 853 }; 854 855 sleep_command: FSC_SLEEP FSV_VAL_INT 856 { 857 if (($$ = alloc_cmd()) == NULL) 858 YYERROR; 859 $$->cmd = parser_sleep; 860 $$->cmd_qty = $2; 861 } 862 | FSC_SLEEP FSV_VARIABLE 863 { 864 vinteger_t *integer; 865 866 if (($$ = alloc_cmd()) == NULL) 867 YYERROR; 868 $$->cmd = parser_sleep_variable; 869 $$->cmd_tgt1 = fb_stralloc($2); 870 }; 871 872 run_command: FSC_RUN FSV_VAL_INT 873 { 874 if (($$ = alloc_cmd()) == NULL) 875 YYERROR; 876 $$->cmd = parser_run; 877 $$->cmd_qty = $2; 878 } 879 | FSC_RUN FSV_VARIABLE 880 { 881 vinteger_t *integer; 882 883 if (($$ = alloc_cmd()) == NULL) 884 YYERROR; 885 $$->cmd = parser_run_variable; 886 $$->cmd_tgt1 = fb_stralloc($2); 887 } 888 | FSC_RUN 889 { 890 vinteger_t *integer; 891 892 if (($$ = alloc_cmd()) == NULL) 893 YYERROR; 894 $$->cmd = parser_run; 895 $$->cmd_qty = 60UL; 896 }; 897 898 help_command: FSC_HELP 899 { 900 if (($$ = alloc_cmd()) == NULL) 901 YYERROR; 902 $$->cmd = parser_help; 903 }; 904 905 flowop_command: FSC_FLOWOP name 906 { 907 if (($$ = alloc_cmd()) == NULL) 908 YYERROR; 909 $$->cmd_name = fb_stralloc($2); 910 } 911 | flowop_command attr_ops 912 { 913 $1->cmd_attr_list = $2; 914 }; 915 916 load_command: FSC_LOAD FSV_STRING 917 { 918 FILE *newfile; 919 char loadfile[128]; 920 921 if (($$ = alloc_cmd()) == NULL) 922 YYERROR; 923 924 (void) strcpy(loadfile, $2); 925 (void) strcat(loadfile, ".f"); 926 927 if ((newfile = fopen(loadfile, "r")) == NULL) { 928 (void) strcpy(loadfile, FILEBENCHDIR); 929 (void) strcat(loadfile, "/workloads/"); 930 (void) strcat(loadfile, $2); 931 (void) strcat(loadfile, ".f"); 932 if ((newfile = fopen(loadfile, "r")) == NULL) { 933 filebench_log(LOG_ERROR, "Cannot open %s", loadfile); 934 YYERROR; 935 } 936 } 937 938 parentscript = yyin; 939 yyin = newfile; 940 yy_switchfileparent(yyin); 941 }; 942 943 entity: FSE_PROC {$$ = FSE_PROC;} 944 | FSE_THREAD {$$ = FSE_THREAD;} 945 | FSE_FILESET {$$ = FSE_FILESET;} 946 | FSE_FILE {$$ = FSE_FILE;}; 947 948 value: FSV_VAL_INT { $$.i = $1;} 949 | FSV_STRING { $$.s = $1;} 950 | FSV_VAL_BOOLEAN { $$.b = $1;}; 951 952 name: FSV_STRING; 953 954 attr_ops: attr_op 955 { 956 $$ = $1; 957 } 958 | attr_ops FSK_SEPLST attr_op 959 { 960 attr_t *attr = NULL; 961 attr_t *list_end = NULL; 962 963 for (attr = $1; attr != NULL; 964 attr = attr->attr_next) 965 list_end = attr; /* Find end of list */ 966 967 list_end->attr_next = $3; 968 969 $$ = $1; 970 }; 971 972 attr_op: attr_name FSK_ASSIGN attr_value 973 { 974 $$ = $3; 975 $$->attr_name = $1; 976 } 977 | attr_name 978 { 979 if (($$ = alloc_attr()) == NULL) 980 YYERROR; 981 $$->attr_name = $1; 982 } 983 984 attr_name: attrs_define_file 985 |attrs_define_fileset 986 |attrs_define_thread 987 |attrs_define_proc 988 |attrs_flowop 989 |attrs_eventgen; 990 991 attrs_define_proc: 992 FSA_NICE { $$ = FSA_NICE;} 993 |FSA_INSTANCES { $$ = FSA_INSTANCES;}; 994 995 attrs_define_file: 996 FSA_SIZE { $$ = FSA_SIZE;} 997 | FSA_PATH { $$ = FSA_PATH;} 998 | FSA_REUSE { $$ = FSA_REUSE;} 999 | FSA_PREALLOC { $$ = FSA_PREALLOC;} 1000 | FSA_PARALLOC { $$ = FSA_PARALLOC;}; 1001 1002 attrs_define_fileset: 1003 FSA_SIZE { $$ = FSA_SIZE;} 1004 | FSA_PATH { $$ = FSA_PATH;} 1005 | FSA_DIRWIDTH { $$ = FSA_DIRWIDTH;} 1006 | FSA_PREALLOC { $$ = FSA_PREALLOC;} 1007 | FSA_FILESIZEGAMMA { $$ = FSA_FILESIZEGAMMA;} 1008 | FSA_DIRGAMMA { $$ = FSA_DIRGAMMA;} 1009 | FSA_CACHED { $$ = FSA_CACHED;} 1010 | FSA_ENTRIES { $$ = FSA_ENTRIES;}; 1011 1012 attrs_define_thread: 1013 FSA_PROCESS { $$ = FSA_PROCESS;} 1014 |FSA_MEMSIZE { $$ = FSA_MEMSIZE;} 1015 |FSA_USEISM { $$ = FSA_USEISM;} 1016 |FSA_INSTANCES { $$ = FSA_INSTANCES;}; 1017 1018 attrs_flowop: 1019 FSA_WSS { $$ = FSA_WSS;} 1020 |FSA_FILE { $$ = FSA_FILE;} 1021 |FSA_NAME { $$ = FSA_NAME;} 1022 |FSA_RANDOM { $$ = FSA_RANDOM;} 1023 |FSA_FD { $$ = FSA_FD;} 1024 |FSA_SRCFD { $$ = FSA_SRCFD;} 1025 |FSA_ROTATEFD { $$ = FSA_ROTATEFD;} 1026 |FSA_DSYNC { $$ = FSA_DSYNC;} 1027 |FSA_DIRECTIO { $$ = FSA_DIRECTIO;} 1028 |FSA_TARGET { $$ = FSA_TARGET;} 1029 |FSA_ITERS { $$ = FSA_ITERS;} 1030 |FSA_VALUE { $$ = FSA_VALUE;} 1031 |FSA_BLOCKING { $$ = FSA_BLOCKING;} 1032 |FSA_HIGHWATER { $$ = FSA_HIGHWATER;} 1033 |FSA_IOSIZE { $$ = FSA_IOSIZE;}; 1034 1035 attrs_eventgen: 1036 FSA_RATE { $$ = FSA_RATE;}; 1037 1038 attr_value: var_string_list { 1039 if (($$ = alloc_attr()) == NULL) 1040 YYERROR; 1041 $$->attr_param_list = $1; 1042 } | FSV_STRING 1043 { 1044 if (($$ = alloc_attr()) == NULL) 1045 YYERROR; 1046 $$->attr_string = string_alloc($1); 1047 } | FSV_VAL_INT { 1048 if (($$ = alloc_attr()) == NULL) 1049 YYERROR; 1050 $$->attr_integer = integer_alloc($1); 1051 } | FSV_VARIABLE { 1052 if (($$ = alloc_attr()) == NULL) 1053 YYERROR; 1054 $$->attr_integer = var_ref_integer($1); 1055 $$->attr_string = var_ref_string($1); 1056 }; 1057 1058 %% 1059 1060 /* 1061 * The following 'c' routines implement the various commands defined in the 1062 * above yacc parser code. The yacc portion checks the syntax of the commands 1063 * found in a workload file, or typed on interactive command lines, parsing 1064 * the commands' parameters into lists. The lists are then passed in a cmd_t 1065 * struct for each command to its related routine in the following section 1066 * for actual execution. This section also includes a few utility routines 1067 * and the main entry point for the program. 1068 */ 1069 1070 /* 1071 * Entry point for filebench. Processes command line arguements. The -f 1072 * option will read in a workload file (the full name and extension must 1073 * must be given). The -a, -s, -m and -i options are used by worker process 1074 * to receive their name, the base address of shared memory, its path, and 1075 * the process' instance number, respectively. This information is supplied 1076 * by the master process when it execs worker processes under the process 1077 * model of execution. If the worker process arguments are passed then main 1078 * will call the procflow_exec routine which creates worker threadflows and 1079 * flowops and executes the procflow's portion of the workload model until 1080 * completion. If worker process arguments are not passed to the process, 1081 * then it becomes the master process for a filebench run. It initializes 1082 * the various filebench components and either executes the supplied workload 1083 * file, or enters interactive mode. 1084 */ 1085 1086 int 1087 main(int argc, char *argv[]) 1088 { 1089 int opt; 1090 int docmd = FS_FALSE; 1091 int instance; 1092 char procname[128]; 1093 caddr_t shmaddr; 1094 char dir[MAXPATHLEN]; 1095 #ifdef HAVE_SETRLIMIT 1096 struct rlimit rlp; 1097 #endif 1098 #ifdef HAVE_LIBTECLA 1099 char *line; 1100 #else 1101 char line[1024]; 1102 #endif 1103 char shmpathtmp[1024]; 1104 1105 #ifdef HAVE_SETRLIMIT 1106 /* Set resource limits */ 1107 (void) getrlimit(RLIMIT_NOFILE, &rlp); 1108 rlp.rlim_cur = rlp.rlim_max; 1109 setrlimit(RLIMIT_NOFILE, &rlp); 1110 #endif 1111 1112 yydebug = 0; 1113 execname = argv[0]; 1114 *procname = 0; 1115 cwd = getcwd(dir, MAXPATHLEN); 1116 1117 while ((opt = getopt(argc, argv, cmd_options)) != (int)EOF) { 1118 1119 switch (opt) { 1120 case 'h': 1121 usage(2); 1122 break; 1123 1124 case 'p': 1125 noproc = 1; 1126 break; 1127 1128 case 'f': 1129 if (optarg == NULL) 1130 usage(1); 1131 if ((yyin = fopen(optarg, "r")) == NULL) { 1132 (void) fprintf(stderr, 1133 "Cannot open file %s", optarg); 1134 exit(1); 1135 } 1136 dofile = FS_TRUE; 1137 fscriptname = optarg; 1138 1139 break; 1140 1141 case 'a': 1142 if (optarg == NULL) 1143 usage(1); 1144 sscanf(optarg, "%s", &procname[0]); 1145 break; 1146 1147 case 's': 1148 if (optarg == NULL) 1149 usage(1); 1150 #if defined(_LP64) || (__WORDSIZE == 64) 1151 sscanf(optarg, "%llx", &shmaddr); 1152 #else 1153 sscanf(optarg, "%x", &shmaddr); 1154 #endif 1155 break; 1156 1157 case 'm': 1158 if (optarg == NULL) 1159 usage(1); 1160 sscanf(optarg, "%s", shmpathtmp); 1161 shmpath = shmpathtmp; 1162 break; 1163 1164 case 'i': 1165 if (optarg == NULL) 1166 usage(1); 1167 sscanf(optarg, "%d", &instance); 1168 break; 1169 1170 case '?': 1171 default: 1172 usage(1); 1173 break; 1174 } 1175 } 1176 1177 #ifdef USE_PROCESS_MODEL 1178 if (!(*procname)) 1179 #endif 1180 printf("FileBench Version %s\n", FILEBENCH_VERSION); 1181 filebench_init(); 1182 1183 #ifdef USE_PROCESS_MODEL 1184 if (*procname) { 1185 pid = getpid(); 1186 1187 if (ipc_attach(shmaddr) < 0) { 1188 filebench_log(LOG_ERROR, "Cannot attach shm for %s", 1189 procname); 1190 exit(1); 1191 } 1192 1193 if (procflow_exec(procname, instance) < 0) { 1194 filebench_log(LOG_ERROR, "Cannot startup process %s", 1195 procname); 1196 exit(1); 1197 } 1198 exit(1); 1199 } 1200 #endif 1201 1202 pid = getpid(); 1203 ipc_init(); 1204 1205 if (fscriptname) 1206 (void) strcpy(filebench_shm->fscriptname, fscriptname); 1207 1208 flowop_init(); 1209 stats_init(); 1210 eventgen_init(); 1211 1212 signal(SIGINT, parser_abort); 1213 1214 if (dofile) 1215 yyparse(); 1216 else { 1217 #ifdef HAVE_LIBTECLA 1218 if ((gl = new_GetLine(MAX_LINE_LEN, MAX_CMD_HIST)) == NULL) { 1219 filebench_log(LOG_ERROR, 1220 "Failed to create GetLine object"); 1221 filebench_shutdown(1); 1222 } 1223 1224 if (gl_customize_completion(gl, NULL, command_complete)) { 1225 filebench_log(LOG_ERROR, 1226 "Failed to register auto-completion function"); 1227 filebench_shutdown(1); 1228 } 1229 1230 while (line = gl_get_line(gl, FILEBENCH_PROMPT, NULL, -1)) { 1231 arg_parse(line); 1232 yyparse(); 1233 } 1234 1235 del_GetLine(gl); 1236 #else 1237 while (!feof(stdin)) { 1238 printf(FILEBENCH_PROMPT); 1239 fflush(stdout); 1240 if (fgets(line, sizeof (line), stdin) == NULL) { 1241 if (errno == EINTR) 1242 continue; 1243 else 1244 break; 1245 } 1246 arg_parse(line); 1247 yyparse(); 1248 } 1249 printf("\n"); 1250 #endif /* HAVE_LIBTECLA */ 1251 } 1252 1253 parser_filebench_shutdown((cmd_t *)0); 1254 1255 return (0); 1256 } 1257 1258 /* 1259 * arg_parse() puts the parser into command parsing mode. Create a tmpfile 1260 * and instruct the parser to read instructions from this location by setting 1261 * yyin to the value returned by tmpfile. Write the command into the file. 1262 * Then seek back to to the start of the file so that the parser can read 1263 * the instructions. 1264 */ 1265 static void 1266 arg_parse(const char *command) 1267 { 1268 if ((yyin = tmpfile()) == NULL) 1269 filebench_log(LOG_FATAL, 1270 "Cannot create tmpfile: %s", strerror(errno)); 1271 1272 if (fwrite(command, strlen(command), 1, yyin) != 1) 1273 filebench_log(LOG_FATAL, 1274 "Cannot write tmpfile: %s", strerror(errno)); 1275 1276 if (fseek(yyin, 0, SEEK_SET) != 0) 1277 filebench_log(LOG_FATAL, 1278 "Cannot seek tmpfile: %s", strerror(errno)); 1279 } 1280 1281 /* 1282 * Converts a list of var_strings or ordinary strings to a single ordinary 1283 * string. It returns a pointer to the string (in malloc'd memory) if found, 1284 * or NULL otherwise. 1285 */ 1286 char * 1287 parser_list2string(list_t *list) 1288 { 1289 list_t *l; 1290 char *string; 1291 char *tmp; 1292 vinteger_t *integer; 1293 1294 if ((string = malloc(MAXPATHLEN)) == NULL) { 1295 filebench_log(LOG_ERROR, "Failed to allocate memory"); 1296 return (NULL); 1297 } 1298 1299 *string = 0; 1300 1301 1302 /* Format args */ 1303 for (l = list; l != NULL; 1304 l = l->list_next) { 1305 filebench_log(LOG_DEBUG_SCRIPT, 1306 "converting string '%s'", *l->list_string); 1307 1308 if ((tmp = var_to_string(*l->list_string)) != NULL) { 1309 (void) strcat(string, tmp); 1310 free(tmp); 1311 } else { 1312 (void) strcat(string, *l->list_string); 1313 } 1314 } 1315 return (string); 1316 } 1317 1318 /* 1319 * If the list just contains a single string starting with '$', then find 1320 * or create the named var and return the var's var_string component. 1321 * Otherwise, convert the list to a string, and allocate a var_string 1322 * containing a copy of that string. On failure either returns NULL 1323 * or shuts down the run. 1324 */ 1325 var_string_t 1326 parser_list2varstring(list_t *list) 1327 { 1328 /* Special case - variable name */ 1329 if ((list->list_next == NULL) && (*(*list->list_string) == '$')) 1330 return (var_ref_string(*list->list_string)); 1331 1332 return (string_alloc(parser_list2string(list))); 1333 } 1334 1335 /* 1336 * Looks for the var named in list_string of the first element of the 1337 * supplied list. If found, returns the var_integer portion of the var. 1338 * If the var is not found, cannot be allocated, the supplied list is 1339 * NULL, or the list_string filed is empty, returns NULL. 1340 */ 1341 var_integer_t 1342 parser_list2integer(list_t *list) 1343 { 1344 var_integer_t v; 1345 1346 if (list && (*(list->list_string) != NULL)) { 1347 v = var_ref_integer(*(list->list_string)); 1348 return (v); 1349 } 1350 1351 return (NULL); 1352 } 1353 1354 /* 1355 * Sets the event generator rate from the attribute supplied with the 1356 * command. If the attribute doesn't exist the routine does nothing. 1357 */ 1358 static void 1359 parser_eventgen(cmd_t *cmd) 1360 { 1361 attr_t *attr; 1362 vinteger_t rate; 1363 1364 /* Get the rate from attribute */ 1365 if (attr = get_attr_integer(cmd, FSA_RATE)) { 1366 if (attr->attr_integer) { 1367 filebench_log(LOG_VERBOSE, 1368 "Eventgen: %lld per second", 1369 *attr->attr_integer); 1370 eventgen_setrate(*attr->attr_integer); 1371 } 1372 } 1373 1374 } 1375 1376 /* 1377 * Assigns the designated integer variable successive values from the 1378 * supplied comma seperated integer list. After each successive integer 1379 * assignment, it executes the bracket enclosed list of commands. For 1380 * example, repeated runs of a workload with increasing io sizes can 1381 * be done using the following command line: 1382 * foreach $iosize in 2k, 4k, 8k {run 60} 1383 */ 1384 static void 1385 parser_foreach_integer(cmd_t *cmd) 1386 { 1387 list_t *list = cmd->cmd_param_list; 1388 cmd_t *inner_cmd; 1389 1390 for (; list != NULL; list = list->list_next) { 1391 var_assign_integer(cmd->cmd_tgt1, *list->list_integer); 1392 filebench_log(LOG_VERBOSE, "Iterating %s=%lld", 1393 cmd->cmd_tgt1, 1394 *list->list_integer); 1395 for (inner_cmd = cmd->cmd_list; inner_cmd != NULL; 1396 inner_cmd = inner_cmd->cmd_next) { 1397 inner_cmd->cmd(inner_cmd); 1398 } 1399 } 1400 } 1401 1402 /* 1403 * Similar to parser_foreach_integer(), except takes a list of strings after 1404 * the "in" token. For example, to run twice using a different directory, 1405 * perhaps using a different filesystem, the following command line 1406 * could be used: 1407 * foreach $dir in "/ufs_top/fbt", "/zfs_top/fbt" {run 60) 1408 */ 1409 static void 1410 parser_foreach_string(cmd_t *cmd) 1411 { 1412 list_t *list = cmd->cmd_param_list; 1413 cmd_t *inner_cmd; 1414 1415 for (; list != NULL; list = list->list_next) { 1416 var_assign_string(cmd->cmd_tgt1, *list->list_string); 1417 filebench_log(LOG_VERBOSE, "Iterating %s=%s", 1418 cmd->cmd_tgt1, 1419 *list->list_string); 1420 for (inner_cmd = cmd->cmd_list; inner_cmd != NULL; 1421 inner_cmd = inner_cmd->cmd_next) { 1422 inner_cmd->cmd(inner_cmd); 1423 } 1424 } 1425 } 1426 1427 /* 1428 * Lists the fileset name, path name and average size for all defined 1429 * filesets. 1430 */ 1431 static void 1432 parser_list(cmd_t *cmd) 1433 { 1434 (void) fileset_iter(fileset_print); 1435 } 1436 1437 /* 1438 * Calls procflow_define() to allocate "instances" number of procflow(s) 1439 * (processes) with the supplied name. The default number of instances is 1440 * one. An optional priority level attribute can be supplied and is stored in 1441 * pf_nice. Finally the routine loops through the list of inner commands, if 1442 * any, which are defines for threadflows, and passes them one at a time to 1443 * parser_thread_define() to allocate threadflow entities for the process(es). 1444 */ 1445 static void 1446 parser_proc_define(cmd_t *cmd) 1447 { 1448 procflow_t *procflow, template; 1449 char *name; 1450 attr_t *attr; 1451 var_integer_t instances = integer_alloc(1); 1452 cmd_t *inner_cmd; 1453 1454 /* Get the name of the process */ 1455 if (attr = get_attr(cmd, FSA_NAME)) { 1456 name = *attr->attr_string; 1457 } else { 1458 filebench_log(LOG_ERROR, 1459 "define proc: proc specifies no name"); 1460 filebench_shutdown(1); 1461 } 1462 1463 /* Get the memory size from attribute */ 1464 if (attr = get_attr_integer(cmd, FSA_INSTANCES)) { 1465 filebench_log(LOG_DEBUG_IMPL, 1466 "Setting instances = %lld", 1467 *attr->attr_integer); 1468 instances = attr->attr_integer; 1469 } 1470 1471 if ((procflow = procflow_define(name, NULL, instances)) == NULL) { 1472 filebench_log(LOG_ERROR, 1473 "Failed to instantiate %d %s process(es)\n", 1474 instances, name); 1475 filebench_shutdown(1); 1476 } 1477 1478 /* Get the pri from attribute */ 1479 if (attr = get_attr_integer(cmd, FSA_NICE)) { 1480 filebench_log(LOG_DEBUG_IMPL, "Setting pri = %lld", 1481 *attr->attr_integer); 1482 procflow->pf_nice = attr->attr_integer; 1483 } else 1484 procflow->pf_nice = integer_alloc(0); 1485 1486 1487 /* Create the list of threads for this process */ 1488 for (inner_cmd = cmd->cmd_list; inner_cmd != NULL; 1489 inner_cmd = inner_cmd->cmd_next) { 1490 parser_thread_define(inner_cmd, procflow, *instances); 1491 } 1492 } 1493 1494 /* 1495 * Calls threadflow_define() to allocate "instances" number of threadflow(s) 1496 * (threads) with the supplied name. The default number of instances is 1497 * one. Two other optional attributes may be supplied, one to set the memory 1498 * size, stored in tf_memsize, and to select the use of Interprocess Shared 1499 * Memory, which sets the THREADFLOW_USEISM flag in tf_attrs. Finally 1500 * the routine loops through the list of inner commands, if any, which are 1501 * defines for flowops, and passes them one at a time to 1502 * parser_flowop_define() to allocate flowop entities for the threadflows. 1503 */ 1504 static void 1505 parser_thread_define(cmd_t *cmd, procflow_t *procflow, int procinstances) 1506 { 1507 threadflow_t *threadflow, template; 1508 attr_t *attr; 1509 var_integer_t instances = integer_alloc(1); 1510 cmd_t *inner_cmd; 1511 char *name; 1512 1513 memset(&template, 0, sizeof (threadflow_t)); 1514 1515 /* Get the name of the thread */ 1516 if (attr = get_attr(cmd, FSA_NAME)) { 1517 name = *attr->attr_string; 1518 } else { 1519 filebench_log(LOG_ERROR, 1520 "define thread: thread in process %s specifies no name", 1521 procflow->pf_name); 1522 filebench_shutdown(1); 1523 } 1524 1525 /* Get the number of instances from attribute */ 1526 if (attr = get_attr_integer(cmd, FSA_INSTANCES)) { 1527 filebench_log(LOG_DEBUG_IMPL, 1528 "define thread: Setting instances = %lld", 1529 *attr->attr_integer); 1530 instances = attr->attr_integer; 1531 } 1532 1533 /* Get the memory size from attribute */ 1534 if (attr = get_attr_integer(cmd, FSA_MEMSIZE)) { 1535 filebench_log(LOG_DEBUG_IMPL, 1536 "define thread: Setting memsize = %lld", 1537 *attr->attr_integer); 1538 template.tf_memsize = attr->attr_integer; 1539 } else 1540 template.tf_memsize = integer_alloc(0); 1541 1542 if ((threadflow = threadflow_define(procflow, name, 1543 &template, instances)) == NULL) { 1544 filebench_log(LOG_ERROR, 1545 "define thread: Failed to instantiate thread\n"); 1546 filebench_shutdown(1); 1547 } 1548 1549 /* Use ISM Memory? */ 1550 if (attr = get_attr(cmd, FSA_USEISM)) { 1551 threadflow->tf_attrs |= THREADFLOW_USEISM; 1552 } 1553 1554 /* Create the list of flowops */ 1555 for (inner_cmd = cmd->cmd_list; inner_cmd != NULL; 1556 inner_cmd = inner_cmd->cmd_next) { 1557 parser_flowop_define(inner_cmd, threadflow); 1558 } 1559 } 1560 1561 /* 1562 * Calls flowop_define() to allocate a flowop with the supplied name. 1563 * The allocated flowop inherits attributes from a base flowop of the 1564 * same type. If the new flowop has a file or fileset attribute specified, 1565 * it must specify a defined fileobj or fileset or an error will be logged. 1566 * The new flowop may also have the following attributes set by 1567 * the program: 1568 * - file size (fo_iosize) 1569 * - working set size (fo_wss) 1570 * - do random io (fo_random) 1571 * - do synchronous io (fo_dsync) 1572 * - perform each operation multiple times before advancing (fo_iter) 1573 * - target name (fo_targetname) 1574 * - An integer value (fo_value) 1575 * - a file descriptor (fo_fd) 1576 * - specify to rotate file descriptors (fo_rotatefd) 1577 * - a source fd (fo_srcfdnumber) 1578 * - specify a blocking operation (fo_blocking) 1579 * - specify a highwater mark (fo_highwater) 1580 * 1581 * After all the supplied attributes are stored in their respective locations 1582 * in the flowop object, the flowop's init function is called. No errors are 1583 * returned, but the filebench run will be terminated if the flowtype is not 1584 * specified, a name for the new flowop is not supplied, the flowop_define 1585 * call fails, or a file or fileset name is supplied but the corresponding 1586 * fileobj or fileset cannot be located. 1587 */ 1588 static void 1589 parser_flowop_define(cmd_t *cmd, threadflow_t *thread) 1590 { 1591 flowop_t *flowop, *flowop_type; 1592 char *type = (char *)cmd->cmd_name; 1593 char *name; 1594 attr_t *attr; 1595 1596 /* Get the inherited flowop */ 1597 flowop_type = flowop_find(type); 1598 if (flowop_type == NULL) { 1599 filebench_log(LOG_ERROR, 1600 "define flowop: flowop type %s not found", 1601 type); 1602 filebench_shutdown(1); 1603 } 1604 1605 /* Get the name of the flowop */ 1606 if (attr = get_attr(cmd, FSA_NAME)) { 1607 name = *attr->attr_string; 1608 } else { 1609 filebench_log(LOG_ERROR, 1610 "define flowop: flowop %s specifies no name", 1611 flowop_type->fo_name); 1612 filebench_shutdown(1); 1613 } 1614 1615 if ((flowop = flowop_define(thread, name, 1616 flowop_type, FLOW_MASTER, 0)) == NULL) { 1617 filebench_log(LOG_ERROR, 1618 "define flowop: Failed to instantiate flowop %s\n", 1619 cmd->cmd_name); 1620 filebench_shutdown(1); 1621 } 1622 1623 /* Get the filename from attribute */ 1624 if (attr = get_attr(cmd, FSA_FILE)) { 1625 flowop->fo_fileset = fileset_find(*attr->attr_string); 1626 1627 if ((flowop->fo_fileset == NULL)) { 1628 filebench_log(LOG_ERROR, 1629 "define flowop: file %s not found", 1630 *attr->attr_string); 1631 filebench_shutdown(1); 1632 } 1633 } 1634 1635 /* Get the iosize of the op */ 1636 if (attr = get_attr_integer(cmd, FSA_IOSIZE)) 1637 flowop->fo_iosize = attr->attr_integer; 1638 else 1639 flowop->fo_iosize = integer_alloc(0); 1640 1641 /* Get the working set size of the op */ 1642 if (attr = get_attr_integer(cmd, FSA_WSS)) 1643 flowop->fo_wss = attr->attr_integer; 1644 else 1645 flowop->fo_wss = integer_alloc(0); 1646 1647 /* Random I/O? */ 1648 if (attr = get_attr_bool(cmd, FSA_RANDOM)) 1649 flowop->fo_random = attr->attr_integer; 1650 else 1651 flowop->fo_random = integer_alloc(0); 1652 1653 /* Sync I/O? */ 1654 if (attr = get_attr_bool(cmd, FSA_DSYNC)) 1655 flowop->fo_dsync = attr->attr_integer; 1656 else 1657 flowop->fo_dsync = integer_alloc(0); 1658 1659 /* Iterations */ 1660 if (attr = get_attr_integer(cmd, FSA_ITERS)) 1661 flowop->fo_iters = attr->attr_integer; 1662 else 1663 flowop->fo_iters = integer_alloc(1); 1664 1665 1666 /* Target, for wakeup etc */ 1667 if (attr = get_attr(cmd, FSA_TARGET)) 1668 (void) strcpy(flowop->fo_targetname, *attr->attr_string); 1669 1670 /* Value */ 1671 if (attr = get_attr_integer(cmd, FSA_VALUE)) 1672 flowop->fo_value = attr->attr_integer; 1673 else 1674 flowop->fo_value = integer_alloc(0); 1675 1676 /* FD */ 1677 if (attr = get_attr_integer(cmd, FSA_FD)) 1678 flowop->fo_fdnumber = *attr->attr_integer; 1679 1680 /* Rotatefd? */ 1681 if (attr = get_attr_bool(cmd, FSA_ROTATEFD)) 1682 flowop->fo_rotatefd = attr->attr_integer; 1683 else 1684 flowop->fo_rotatefd = integer_alloc(0); 1685 1686 /* SRC FD, for copies etc... */ 1687 if (attr = get_attr_integer(cmd, FSA_SRCFD)) 1688 flowop->fo_srcfdnumber = *attr->attr_integer; 1689 1690 /* Blocking operation? */ 1691 if (attr = get_attr_bool(cmd, FSA_BLOCKING)) 1692 flowop->fo_blocking = attr->attr_integer; 1693 else 1694 flowop->fo_blocking = integer_alloc(0); 1695 1696 /* Blocking operation? */ 1697 if (attr = get_attr_bool(cmd, FSA_DIRECTIO)) 1698 flowop->fo_directio = attr->attr_integer; 1699 else 1700 flowop->fo_directio = integer_alloc(0); 1701 1702 /* Highwater mark */ 1703 if (attr = get_attr_integer(cmd, FSA_HIGHWATER)) 1704 flowop->fo_highwater = attr->attr_integer; 1705 else 1706 flowop->fo_highwater = integer_alloc(1); 1707 } 1708 1709 /* 1710 * Calls fileset_define() to allocate a fileset with the supplied name and 1711 * initializes the fileset's pathname attribute, and optionally the fs_cached, 1712 * fs_reuse, fs_prealloc and fs_size attributes. 1713 * 1714 */ 1715 static fileset_t * 1716 parser_fileset_define_common(cmd_t *cmd) 1717 { 1718 fileset_t *fileset; 1719 char *name; 1720 attr_t *attr; 1721 var_string_t pathname; 1722 1723 /* Get the name of the file */ 1724 if (attr = get_attr(cmd, FSA_NAME)) { 1725 name = *attr->attr_string; 1726 } else { 1727 filebench_log(LOG_ERROR, 1728 "define fileset: file or fileset specifies no name"); 1729 return (NULL); 1730 } 1731 1732 if ((fileset = fileset_define(name)) == NULL) { 1733 filebench_log(LOG_ERROR, 1734 "define file: failed to instantiate file %s\n", 1735 name); 1736 return (NULL); 1737 } 1738 1739 /* Get the pathname from attribute */ 1740 if ((attr = get_attr(cmd, FSA_PATH)) == NULL) { 1741 filebench_log(LOG_ERROR, "define file: no pathname specified"); 1742 return (NULL); 1743 } 1744 1745 /* Expand variables in pathname */ 1746 if ((pathname = parser_list2varstring(attr->attr_param_list)) 1747 == NULL) { 1748 filebench_log(LOG_ERROR, "Cannot interpret path"); 1749 return (NULL); 1750 } 1751 1752 fileset->fs_path = pathname; 1753 1754 /* Should we prealloc in parallel? */ 1755 if (attr = get_attr_bool(cmd, FSA_PARALLOC)) { 1756 fileset->fs_paralloc = attr->attr_integer; 1757 } else 1758 fileset->fs_paralloc = integer_alloc(0); 1759 1760 /* Should we reuse the existing file? */ 1761 if (attr = get_attr_bool(cmd, FSA_REUSE)) { 1762 fileset->fs_reuse = attr->attr_integer; 1763 } else 1764 fileset->fs_reuse = integer_alloc(0); 1765 1766 /* Should we leave in cache? */ 1767 if (attr = get_attr_bool(cmd, FSA_CACHED)) { 1768 fileset->fs_cached = attr->attr_integer; 1769 } else 1770 fileset->fs_cached = integer_alloc(0); 1771 1772 /* Get the mean or absolute size of the file */ 1773 if (attr = get_attr_integer(cmd, FSA_SIZE)) { 1774 fileset->fs_size = attr->attr_integer; 1775 } else 1776 fileset->fs_size = integer_alloc(0); 1777 1778 return (fileset); 1779 } 1780 1781 /* 1782 * Calls parser_fileset_define_common() to allocate a fileset with 1783 * one entry and optionally the fs_prealloc. Sets the fs_preallocpercent, 1784 * fs_entries, fs_dirwidth, fs_dirgamma, and fs_sizegamma attributes 1785 * to appropriate values for emulating the old "fileobj" entity 1786 */ 1787 static void 1788 parser_file_define(cmd_t *cmd) 1789 { 1790 fileset_t *fileset; 1791 attr_t *attr; 1792 1793 if ((fileset = parser_fileset_define_common(cmd)) == NULL) { 1794 filebench_log(LOG_ERROR, 1795 "define file: failed to instantiate file"); 1796 filebench_shutdown(1); 1797 return; 1798 } 1799 1800 /* fileset is emulating a single file */ 1801 fileset->fs_attrs = FILESET_IS_FILE; 1802 1803 /* Set the size of the fileset to 1 */ 1804 fileset->fs_entries = integer_alloc(1); 1805 1806 /* Set the mean dir width to more than 1 */ 1807 fileset->fs_dirwidth = integer_alloc(10); 1808 1809 /* Set the dir and size gammas to 0 */ 1810 fileset->fs_dirgamma = integer_alloc(0); 1811 fileset->fs_sizegamma = integer_alloc(0); 1812 1813 /* if a raw device, all done */ 1814 if (fileset_checkraw(fileset)) { 1815 filebench_log(LOG_VERBOSE, "File %s/%s is RAW device", 1816 *fileset->fs_path, fileset->fs_name); 1817 return; 1818 } 1819 1820 /* Does file need to be preallocated? */ 1821 if (attr = get_attr_bool(cmd, FSA_PREALLOC)) { 1822 /* yes */ 1823 fileset->fs_prealloc = attr->attr_integer; 1824 fileset->fs_preallocpercent = integer_alloc(100); 1825 } else { 1826 /* no */ 1827 fileset->fs_prealloc = integer_alloc(0); 1828 fileset->fs_preallocpercent = integer_alloc(0); 1829 } 1830 } 1831 1832 /* 1833 * Calls parser_fileset_define_common() to allocate a fileset with the 1834 * supplied name and initializes the fileset's fs_preallocpercent, 1835 * fs_prealloc, fs_entries, fs_dirwidth, fs_dirgamma, and fs_sizegamma 1836 * attributes. 1837 */ 1838 static void 1839 parser_fileset_define(cmd_t *cmd) 1840 { 1841 fileset_t *fileset; 1842 attr_t *attr; 1843 1844 if ((fileset = parser_fileset_define_common(cmd)) == NULL) { 1845 filebench_log(LOG_ERROR, 1846 "define fileset: failed to instantiate fileset"); 1847 filebench_shutdown(1); 1848 return; 1849 } 1850 1851 /* if a raw device, Error */ 1852 if (fileset_checkraw(fileset)) { 1853 filebench_log(LOG_ERROR, 1854 "Fileset %s/%s: Cannot create a fileset on a RAW device", 1855 *fileset->fs_path, fileset->fs_name); 1856 filebench_shutdown(0); 1857 return; 1858 } 1859 1860 /* How much should we preallocate? */ 1861 if ((attr = get_attr_integer(cmd, FSA_PREALLOC)) && 1862 attr->attr_integer) { 1863 fileset->fs_preallocpercent = attr->attr_integer; 1864 } else if (attr && !attr->attr_integer) { 1865 fileset->fs_preallocpercent = integer_alloc(100); 1866 } else { 1867 fileset->fs_preallocpercent = integer_alloc(0); 1868 } 1869 1870 /* Should we preallocate? */ 1871 if (attr = get_attr_bool(cmd, FSA_PREALLOC)) { 1872 fileset->fs_prealloc = attr->attr_integer; 1873 } else 1874 fileset->fs_prealloc = integer_alloc(0); 1875 1876 /* Get the number of files in the fileset */ 1877 if (attr = get_attr_integer(cmd, FSA_ENTRIES)) { 1878 fileset->fs_entries = attr->attr_integer; 1879 } else { 1880 filebench_log(LOG_ERROR, "Fileset has zero entries"); 1881 fileset->fs_entries = integer_alloc(0); 1882 } 1883 1884 /* Get the mean dir width of the fileset */ 1885 if (attr = get_attr_integer(cmd, FSA_DIRWIDTH)) { 1886 fileset->fs_dirwidth = attr->attr_integer; 1887 } else { 1888 filebench_log(LOG_ERROR, "Fileset has zero directory width"); 1889 fileset->fs_dirwidth = integer_alloc(0); 1890 } 1891 1892 /* Get the gamma value for dir width distributions */ 1893 if (attr = get_attr_integer(cmd, FSA_DIRGAMMA)) { 1894 fileset->fs_dirgamma = attr->attr_integer; 1895 } else 1896 fileset->fs_dirgamma = integer_alloc(1500); 1897 1898 /* Get the gamma value for dir width distributions */ 1899 if (attr = get_attr_integer(cmd, FSA_FILESIZEGAMMA)) { 1900 fileset->fs_sizegamma = attr->attr_integer; 1901 } else 1902 fileset->fs_sizegamma = integer_alloc(1500); 1903 } 1904 1905 /* 1906 * Creates and starts all defined procflow processes. The call to 1907 * procflow_init() results in creation of the requested number of 1908 * process instances for each previously defined procflow. The 1909 * child processes exec() a new instance of filebench, passing it 1910 * the instance number and address of the shared memory region. 1911 * The child processes will then create their threads and flowops. 1912 * The routine then unlocks the run_lock to allow all the processes' 1913 * threads to start and waits for all of them to begin execution. 1914 * Finally, it records the start time and resets the event generation 1915 * system. 1916 */ 1917 static void 1918 parser_proc_create(cmd_t *cmd) 1919 { 1920 if (procflow_init() != 0) { 1921 filebench_log(LOG_ERROR, "Failed to create processes\n"); 1922 filebench_shutdown(1); 1923 } 1924 1925 /* Release the read lock, allowing threads to start */ 1926 (void) pthread_rwlock_unlock(&filebench_shm->run_lock); 1927 1928 /* Wait for all threads to start */ 1929 if (procflow_allstarted() != 0) { 1930 filebench_log(LOG_ERROR, "Could not start run"); 1931 return; 1932 } 1933 1934 1935 if (filebench_shm->shm_required && 1936 (ipc_ismcreate(filebench_shm->shm_required) < 0)) { 1937 filebench_log(LOG_ERROR, "Could not allocate shared memory"); 1938 return; 1939 } 1940 1941 filebench_shm->starttime = gethrtime(); 1942 eventgen_reset(); 1943 } 1944 1945 /* 1946 * Calls fileset_createset() to populate all files and filesets and 1947 * create all associated, initially existant, files and subdirectories. 1948 * If errors are encountered, calls filebench_shutdown() 1949 * to exit filebench. 1950 */ 1951 static void 1952 parser_fileset_create(cmd_t *cmd) 1953 { 1954 if (!filecreate_done) { 1955 filecreate_done = 1; 1956 if (fileset_createset(NULL) != 0) { 1957 filebench_log(LOG_ERROR, "Failed to create filesets"); 1958 filebench_shutdown(1); 1959 } 1960 } else { 1961 filebench_log(LOG_INFO, 1962 "Attempting to create fileset more than once, ignoring"); 1963 } 1964 1965 } 1966 1967 /* 1968 * Shuts down all processes and their associated threads. When finished 1969 * it deletes interprocess shared memory and resets the event generator. 1970 * It does not exit the filebench program though. 1971 */ 1972 static void 1973 parser_proc_shutdown(cmd_t *cmd) 1974 { 1975 filebench_log(LOG_INFO, "Shutting down processes"); 1976 filecreate_done = 0; 1977 procflow_shutdown(); 1978 if (filebench_shm->shm_required) 1979 ipc_ismdelete(); 1980 eventgen_reset(); 1981 } 1982 1983 /* 1984 * Ends filebench run after first destoring any interprocess 1985 * shared memory. The call to filebench_shutdown() 1986 * also causes filebench to exit. 1987 */ 1988 static void 1989 parser_filebench_shutdown(cmd_t *cmd) 1990 { 1991 ipc_cleanup(); 1992 filebench_shutdown(1); 1993 } 1994 1995 /* 1996 * Sleeps for cmd->cmd_qty seconds, one second at a time. 1997 */ 1998 static void 1999 parser_sleep(cmd_t *cmd) 2000 { 2001 int sleeptime; 2002 2003 /* check for startup errors */ 2004 if (filebench_shm->f_abort) 2005 return; 2006 2007 sleeptime = cmd->cmd_qty; 2008 filebench_log(LOG_INFO, "Running..."); 2009 while (sleeptime) { 2010 (void) sleep(1); 2011 sleeptime--; 2012 if (filebench_shm->f_abort) 2013 break; 2014 } 2015 filebench_log(LOG_INFO, "Run took %lld seconds...", 2016 cmd->cmd_qty - sleeptime); 2017 } 2018 2019 /* 2020 * Do a file bench run. Calls routines to create file sets, files, and 2021 * processes. It resets the statistics counters, then sleeps for the runtime 2022 * passed as an argument to it on the command line in 1 second increments. 2023 * When it is finished sleeping, it collects a snapshot of the statistics 2024 * and ends the run. 2025 */ 2026 static void 2027 parser_run(cmd_t *cmd) 2028 { 2029 int runtime; 2030 2031 runtime = cmd->cmd_qty; 2032 parser_fileset_create(cmd); 2033 parser_proc_create(cmd); 2034 2035 /* check for startup errors */ 2036 if (filebench_shm->f_abort) 2037 return; 2038 2039 filebench_log(LOG_INFO, "Running..."); 2040 stats_clear(); 2041 while (runtime) { 2042 (void) sleep(1); 2043 runtime--; 2044 if (filebench_shm->f_abort) 2045 break; 2046 } 2047 filebench_log(LOG_INFO, "Run took %lld seconds...", 2048 cmd->cmd_qty - runtime); 2049 parser_statssnap(cmd); 2050 parser_proc_shutdown(cmd); 2051 } 2052 2053 /* 2054 * Similar to parser_run, but gets the sleep time from a variable 2055 * whose name is supplied as an argument to the command. 2056 */ 2057 static void 2058 parser_run_variable(cmd_t *cmd) 2059 { 2060 vinteger_t *integer = var_ref_integer(cmd->cmd_tgt1); 2061 int runtime; 2062 2063 if (integer == NULL) { 2064 filebench_log(LOG_ERROR, "Unknown variable %s", 2065 cmd->cmd_tgt1); 2066 return; 2067 } 2068 2069 runtime = *integer; 2070 2071 /* check for startup errors */ 2072 if (filebench_shm->f_abort) 2073 return; 2074 2075 filebench_log(LOG_INFO, "Running..."); 2076 stats_clear(); 2077 while (runtime) { 2078 (void) sleep(1); 2079 runtime--; 2080 if (filebench_shm->f_abort) 2081 break; 2082 } 2083 filebench_log(LOG_INFO, "Run took %lld seconds...", 2084 *integer - runtime); 2085 parser_statssnap(cmd); 2086 } 2087 2088 char *usagestr = NULL; 2089 2090 /* 2091 * Prints usage string if defined, else just a message requesting load of a 2092 * personality. 2093 */ 2094 static void 2095 parser_help(cmd_t *cmd) 2096 { 2097 int runtime; 2098 2099 if (usagestr) { 2100 filebench_log(LOG_INFO, "%s", usagestr); 2101 } else { 2102 filebench_log(LOG_INFO, 2103 "load <personality> (ls " 2104 "/usr/benchmarks/filebench/workloads for list)"); 2105 } 2106 } 2107 2108 char *varstr = NULL; 2109 2110 /* 2111 * Prints the string of all var definitions, if there is one. 2112 */ 2113 static void 2114 parser_printvars(cmd_t *cmd) 2115 { 2116 int runtime; 2117 char *str, *c; 2118 2119 if (varstr) { 2120 str = strdup(varstr); 2121 for (c = str; *c != '\0'; c++) { 2122 if ((char)*c == '$') 2123 *c = ' '; 2124 } 2125 filebench_log(LOG_INFO, "%s", str); 2126 free(str); 2127 } 2128 } 2129 2130 /* 2131 * Used by the SET command to add a var and default value string to the 2132 * varstr string. It allocates a new, larger varstr string, copies the 2133 * old contents of varstr into it, then adds the new var string on the end. 2134 */ 2135 static void 2136 parser_vars(cmd_t *cmd) 2137 { 2138 char *string = cmd->cmd_tgt1; 2139 char *newvars; 2140 2141 if (string == NULL) 2142 return; 2143 2144 if (dofile) 2145 return; 2146 2147 if (varstr == NULL) { 2148 newvars = malloc(strlen(string) + 2); 2149 *newvars = 0; 2150 } else { 2151 newvars = malloc(strlen(varstr) + strlen(string) + 2); 2152 (void) strcpy(newvars, varstr); 2153 } 2154 (void) strcat(newvars, string); 2155 (void) strcat(newvars, " "); 2156 2157 if (varstr) 2158 free(varstr); 2159 2160 varstr = newvars; 2161 } 2162 2163 /* 2164 * Same as parser_sleep, except the sleep time is obtained from a variable 2165 * whose name is passed to it as an argument on the command line. 2166 */ 2167 static void 2168 parser_sleep_variable(cmd_t *cmd) 2169 { 2170 vinteger_t *integer = var_ref_integer(cmd->cmd_tgt1); 2171 int sleeptime; 2172 2173 if (integer == NULL) { 2174 filebench_log(LOG_ERROR, "Unknown variable %s", 2175 cmd->cmd_tgt1); 2176 return; 2177 } 2178 2179 sleeptime = *integer; 2180 2181 /* check for startup errors */ 2182 if (filebench_shm->f_abort) 2183 return; 2184 2185 filebench_log(LOG_INFO, "Running..."); 2186 while (sleeptime) { 2187 (void) sleep(1); 2188 sleeptime--; 2189 if (filebench_shm->f_abort) 2190 break; 2191 } 2192 filebench_log(LOG_INFO, "Run took %lld seconds...", 2193 *integer - sleeptime); 2194 } 2195 2196 /* 2197 * Parser log prints the values of a list of variables to the log file. 2198 * The list of variables is placed on the command line, separated 2199 * by comas and the entire list is enclosed in quotes. 2200 * For example, if $dir contains "/export/home/tmp" and $filesize = 1048576, 2201 * then typing: log "$dir, $filesize" prints: log /export/home/tmp, 1048576 2202 */ 2203 static void 2204 parser_log(cmd_t *cmd) 2205 { 2206 char *string; 2207 2208 if (cmd->cmd_param_list == NULL) 2209 return; 2210 2211 string = parser_list2string(cmd->cmd_param_list); 2212 2213 if (string == NULL) 2214 return; 2215 2216 filebench_log(LOG_VERBOSE, "log %s", string); 2217 filebench_log(LOG_LOG, "%s", string); 2218 } 2219 2220 /* 2221 * Implements the stats directory command. changes the directory for 2222 * dumping statistics to supplied directory path. For example: 2223 * stats directory /tmp 2224 * changes the stats directory to "/tmp". 2225 */ 2226 static void 2227 parser_directory(cmd_t *cmd) 2228 { 2229 char newdir[MAXPATHLEN]; 2230 char *dir; 2231 2232 if ((dir = parser_list2string(cmd->cmd_param_list)) == NULL) { 2233 filebench_log(LOG_ERROR, "Cannot interpret directory"); 2234 return; 2235 } 2236 2237 *newdir = 0; 2238 /* Change dir relative to cwd if path not fully qualified */ 2239 if (*dir != '/') { 2240 (void) strcat(newdir, cwd); 2241 (void) strcat(newdir, "/"); 2242 } 2243 (void) strcat(newdir, dir); 2244 (void) mkdir(newdir, 0755); 2245 filebench_log(LOG_VERBOSE, "Change dir to %s", newdir); 2246 chdir(newdir); 2247 free(dir); 2248 } 2249 2250 #define PIPE_PARENT 1 2251 #define PIPE_CHILD 0 2252 2253 /* 2254 * Runs the quoted unix command as a background process. Intended for 2255 * running statistics gathering utilities such as mpstat while the filebench 2256 * workload is running. Also records the pid's of the background processes 2257 * so that parser_statssnap() can terminate them when the run completes. 2258 */ 2259 static void 2260 parser_statscmd(cmd_t *cmd) 2261 { 2262 char *string; 2263 pid_t pid; 2264 pidlist_t *pidlistent; 2265 int pipe_fd[2]; 2266 int newstdout; 2267 2268 if (cmd->cmd_param_list == NULL) 2269 return; 2270 2271 string = parser_list2string(cmd->cmd_param_list); 2272 2273 if (string == NULL) 2274 return; 2275 2276 if ((pipe(pipe_fd)) < 0) { 2277 filebench_log(LOG_ERROR, "statscmd pipe failed"); 2278 return; 2279 } 2280 2281 #ifdef HAVE_FORK1 2282 if ((pid = fork1()) < 0) { 2283 filebench_log(LOG_ERROR, "statscmd fork failed"); 2284 return; 2285 } 2286 #elif HAVE_FORK 2287 if ((pid = fork()) < 0) { 2288 filebench_log(LOG_ERROR, "statscmd fork failed"); 2289 return; 2290 } 2291 #else 2292 Crash! - Need code to deal with no fork1! 2293 #endif /* HAVE_FORK1 */ 2294 2295 if (pid == 0) { 2296 2297 setsid(); 2298 2299 filebench_log(LOG_VERBOSE, 2300 "Backgrounding %s", string); 2301 /* 2302 * Child 2303 * - close stdout 2304 * - dup to create new stdout 2305 * - close pipe fds 2306 */ 2307 (void) close(1); 2308 2309 if ((newstdout = dup(pipe_fd[PIPE_CHILD])) < 0) { 2310 filebench_log(LOG_ERROR, 2311 "statscmd dup failed: %s", 2312 strerror(errno)); 2313 } 2314 2315 (void) close(pipe_fd[PIPE_PARENT]); 2316 (void) close(pipe_fd[PIPE_CHILD]); 2317 2318 if (system(string) < 0) { 2319 filebench_log(LOG_ERROR, 2320 "statscmd exec failed: %s", 2321 strerror(errno)); 2322 } 2323 /* Failed! */ 2324 exit(1); 2325 2326 } else { 2327 2328 /* Record pid in pidlist for subsequent reaping by stats snap */ 2329 if ((pidlistent = (pidlist_t *)malloc(sizeof (pidlist_t))) 2330 == NULL) { 2331 filebench_log(LOG_ERROR, "pidlistent malloc failed"); 2332 return; 2333 } 2334 2335 pidlistent->pl_pid = pid; 2336 pidlistent->pl_fd = pipe_fd[PIPE_PARENT]; 2337 (void) close(pipe_fd[PIPE_CHILD]); 2338 2339 /* Add fileobj to global list */ 2340 if (pidlist == NULL) { 2341 pidlist = pidlistent; 2342 pidlistent->pl_next = NULL; 2343 } else { 2344 pidlistent->pl_next = pidlist; 2345 pidlist = pidlistent; 2346 } 2347 } 2348 } 2349 2350 /* 2351 * Launches a shell to run the unix command supplied in the argument. 2352 * The command should be enclosed in quotes, as in: 2353 * system "rm xyz" 2354 * which would run the "rm" utility to delete the file "xyz". 2355 */ 2356 static void 2357 parser_system(cmd_t *cmd) 2358 { 2359 char *string; 2360 2361 if (cmd->cmd_param_list == NULL) 2362 return; 2363 2364 string = parser_list2string(cmd->cmd_param_list); 2365 2366 if (string == NULL) 2367 return; 2368 2369 filebench_log(LOG_VERBOSE, 2370 "Running '%s'", string); 2371 2372 if (system(string) < 0) { 2373 filebench_log(LOG_ERROR, 2374 "system exec failed: %s", 2375 strerror(errno)); 2376 } 2377 free(string); 2378 } 2379 2380 /* 2381 * Echos string supplied with command to the log. 2382 */ 2383 static void 2384 parser_echo(cmd_t *cmd) 2385 { 2386 char *string; 2387 2388 if (cmd->cmd_param_list == NULL) 2389 return; 2390 2391 string = parser_list2string(cmd->cmd_param_list); 2392 2393 if (string == NULL) 2394 return; 2395 2396 filebench_log(LOG_INFO, "%s", string); 2397 } 2398 2399 2400 /* 2401 * Adds the string supplied as the argument to the usage command 2402 * to the end of the string printed by the help command. 2403 */ 2404 static void 2405 parser_usage(cmd_t *cmd) 2406 { 2407 char *string; 2408 char *newusage; 2409 2410 if (cmd->cmd_param_list == NULL) 2411 return; 2412 2413 string = parser_list2string(cmd->cmd_param_list); 2414 2415 if (string == NULL) 2416 return; 2417 2418 if (dofile) 2419 return; 2420 2421 if (usagestr == NULL) { 2422 newusage = malloc(strlen(string) + 2); 2423 *newusage = 0; 2424 } else { 2425 newusage = malloc(strlen(usagestr) + strlen(string) + 2); 2426 (void) strcpy(newusage, usagestr); 2427 } 2428 (void) strcat(newusage, "\n"); 2429 (void) strcat(newusage, string); 2430 2431 if (usagestr) 2432 free(usagestr); 2433 2434 usagestr = newusage; 2435 2436 filebench_log(LOG_INFO, "%s", string); 2437 } 2438 2439 /* 2440 * Updates the global dump filename with the filename supplied 2441 * as the command's argument. Then dumps the statistics of each 2442 * worker flowop into the dump file, followed by a summary of 2443 * overall totals. 2444 */ 2445 static void 2446 parser_statsdump(cmd_t *cmd) 2447 { 2448 char *string; 2449 2450 if (cmd->cmd_param_list == NULL) 2451 return; 2452 2453 string = parser_list2string(cmd->cmd_param_list); 2454 2455 if (string == NULL) 2456 return; 2457 2458 filebench_log(LOG_VERBOSE, 2459 "Stats dump to file '%s'", string); 2460 2461 stats_dump(string); 2462 2463 free(string); 2464 } 2465 2466 /* 2467 * Same as parser_statsdump, but in xml format. 2468 */ 2469 static void 2470 parser_statsxmldump(cmd_t *cmd) 2471 { 2472 char *string; 2473 2474 if (cmd->cmd_param_list == NULL) 2475 return; 2476 2477 string = parser_list2string(cmd->cmd_param_list); 2478 2479 if (string == NULL) 2480 return; 2481 2482 filebench_log(LOG_VERBOSE, 2483 "Stats dump to file '%s'", string); 2484 2485 stats_xmldump(string); 2486 2487 free(string); 2488 } 2489 2490 /* 2491 * Kills off background statistics collection processes, then takes a snapshot 2492 * of the filebench run's collected statistics using stats_snap() from 2493 * stats.c. 2494 */ 2495 static void 2496 parser_statssnap(cmd_t *cmd) 2497 { 2498 pidlist_t *pidlistent; 2499 int stat; 2500 pid_t pid; 2501 2502 for (pidlistent = pidlist; pidlistent != NULL; 2503 pidlistent = pidlistent->pl_next) { 2504 filebench_log(LOG_VERBOSE, "Killing session %d for pid %d", 2505 getsid(pidlistent->pl_pid), 2506 pidlistent->pl_pid); 2507 if (pidlistent->pl_fd) 2508 (void) close(pidlistent->pl_fd); 2509 #ifdef HAVE_SIGSEND 2510 sigsend(P_SID, getsid(pidlistent->pl_pid), SIGTERM); 2511 #else 2512 (void) kill(-1, SIGTERM); 2513 #endif 2514 2515 /* Close pipe */ 2516 if (pidlistent->pl_fd) 2517 (void) close(pidlistent->pl_fd); 2518 2519 /* Wait for cmd and all its children */ 2520 while ((pid = waitpid(pidlistent->pl_pid * -1, &stat, 0)) > 0) 2521 filebench_log(LOG_DEBUG_IMPL, 2522 "Waited for pid %lld", pid); 2523 } 2524 2525 for (pidlistent = pidlist; pidlistent != NULL; 2526 pidlistent = pidlistent->pl_next) { 2527 free(pidlistent); 2528 } 2529 2530 pidlist = NULL; 2531 stats_snap(); 2532 } 2533 2534 /* 2535 * Shutdown filebench. 2536 */ 2537 static void 2538 parser_abort(int arg) 2539 { 2540 (void) sigignore(SIGINT); 2541 filebench_log(LOG_INFO, "Aborting..."); 2542 filebench_shutdown(1); 2543 } 2544 2545 /* 2546 * alloc_cmd() allocates the required resources for a cmd_t. On failure, a 2547 * filebench_log is issued and NULL is returned. 2548 */ 2549 static cmd_t * 2550 alloc_cmd(void) 2551 { 2552 cmd_t *cmd; 2553 2554 if ((cmd = malloc(sizeof (cmd_t))) == NULL) { 2555 filebench_log(LOG_ERROR, "Alloc cmd failed"); 2556 return (NULL); 2557 } 2558 2559 (void) memset(cmd, 0, sizeof (cmd_t)); 2560 2561 return (cmd); 2562 } 2563 2564 /* 2565 * Frees the resources of a cmd_t and then the cmd_t "cmd" itself. 2566 */ 2567 static void 2568 free_cmd(cmd_t *cmd) 2569 { 2570 free((void *)cmd->cmd_tgt1); 2571 free((void *)cmd->cmd_tgt2); 2572 free(cmd); 2573 } 2574 2575 /* 2576 * Allocates an attr_t structure and zeros it. Returns NULL on failure, or 2577 * a pointer to the attr_t. 2578 */ 2579 static attr_t * 2580 alloc_attr() 2581 { 2582 attr_t *attr; 2583 2584 if ((attr = malloc(sizeof (attr_t))) == NULL) { 2585 return (NULL); 2586 } 2587 2588 (void) memset(attr, 0, sizeof (attr_t)); 2589 return (attr); 2590 } 2591 2592 /* 2593 * Searches the attribute list for the command for the named attribute type. 2594 * The attribute list is created by the parser from the list of attributes 2595 * supplied with certain commands, such as the define and flowop commands. 2596 * Returns a pointer to the attribute structure if the named attribute is 2597 * found, otherwise returns NULL. If the attribute includes a parameter list, 2598 * the list is converted to a string and stored in the attr_string field of 2599 * the returned attr_t struct. 2600 */ 2601 static attr_t * 2602 get_attr(cmd_t *cmd, int64_t name) 2603 { 2604 attr_t *attr; 2605 attr_t *rtn = NULL; 2606 char *string; 2607 2608 for (attr = cmd->cmd_attr_list; attr != NULL; 2609 attr = attr->attr_next) { 2610 filebench_log(LOG_DEBUG_IMPL, 2611 "attr %d = %d %llx?", 2612 attr->attr_name, 2613 name, 2614 attr->attr_integer); 2615 2616 if (attr->attr_name == name) 2617 rtn = attr; 2618 } 2619 2620 if (rtn == NULL) 2621 return (NULL); 2622 2623 if (rtn->attr_param_list) { 2624 filebench_log(LOG_DEBUG_SCRIPT, "attr is param list"); 2625 string = parser_list2string(rtn->attr_param_list); 2626 if (string != NULL) { 2627 rtn->attr_string = string_alloc(string); 2628 filebench_log(LOG_DEBUG_SCRIPT, 2629 "attr string %s", string); 2630 } 2631 } 2632 2633 return (rtn); 2634 } 2635 2636 /* 2637 * Similar to get_attr, but converts the parameter string supplied with the 2638 * named attribute to an integer and stores the integer in the attr_integer 2639 * portion of the returned attr_t struct. 2640 */ 2641 static attr_t * 2642 get_attr_integer(cmd_t *cmd, int64_t name) 2643 { 2644 attr_t *attr; 2645 attr_t *rtn = NULL; 2646 2647 for (attr = cmd->cmd_attr_list; attr != NULL; 2648 attr = attr->attr_next) { 2649 if (attr->attr_name == name) 2650 rtn = attr; 2651 } 2652 2653 if (rtn == NULL) 2654 return (NULL); 2655 2656 if (rtn->attr_param_list) { 2657 rtn->attr_integer = parser_list2integer(rtn->attr_param_list); 2658 } 2659 2660 return (rtn); 2661 } 2662 2663 /* 2664 * Similar to get_attr, but converts the parameter string supplied with the 2665 * named attribute to an integer and stores the integer in the attr_integer 2666 * portion of the returned attr_t struct. If no parameter string is supplied 2667 * then it defaults to TRUE (1). 2668 */ 2669 static attr_t * 2670 get_attr_bool(cmd_t *cmd, int64_t name) 2671 { 2672 attr_t *attr; 2673 attr_t *rtn = NULL; 2674 2675 for (attr = cmd->cmd_attr_list; attr != NULL; 2676 attr = attr->attr_next) { 2677 if (attr->attr_name == name) 2678 rtn = attr; 2679 } 2680 2681 if (rtn == NULL) 2682 return (NULL); 2683 2684 if (rtn->attr_param_list) { 2685 rtn->attr_integer = parser_list2integer(rtn->attr_param_list); 2686 } else if (rtn->attr_integer == 0) { 2687 rtn->attr_integer = integer_alloc(1); 2688 } 2689 2690 return (rtn); 2691 } 2692 2693 /* 2694 * Allocates memory for a list_t structure, initializes it to zero, and 2695 * returns a pointer to it. On failure, returns NULL. 2696 */ 2697 static list_t * 2698 alloc_list() 2699 { 2700 list_t *list; 2701 2702 if ((list = malloc(sizeof (list_t))) == NULL) { 2703 return (NULL); 2704 } 2705 2706 (void) memset(list, 0, sizeof (list_t)); 2707 return (list); 2708 } 2709 2710 2711 #define USAGE1 \ 2712 "Usage:\n" \ 2713 "%s: interpret f script and generate file workload\n" \ 2714 "Options:\n" \ 2715 " [-h] Display verbose help\n" \ 2716 " [-p] Disable opening /proc to set uacct to enable truss\n" 2717 2718 #define PARSER_CMDS \ 2719 "create [files|filesets|processes]\n" \ 2720 "stats [clear|snap]\n" \ 2721 "stats command \"shell command $var1,$var2...\"\n" \ 2722 "stats directory <directory>\n" \ 2723 "sleep <sleep-value>\n" \ 2724 "quit\n\n" \ 2725 "Variables:\n" \ 2726 "set $var = value\n" \ 2727 " $var - regular variables\n" \ 2728 " ${var} - internal special variables\n" \ 2729 " $(var) - environment variables\n\n" 2730 2731 #define PARSER_EXAMPLE \ 2732 "Example:\n\n" \ 2733 "#!/usr/bin/filebench -f\n" \ 2734 "\n" \ 2735 "define file name=bigfile,path=bigfile,size=1g,prealloc,reuse\n" \ 2736 "define process name=randomizer\n" \ 2737 "{\n" \ 2738 " thread random-thread procname=randomizer\n" \ 2739 " {\n" \ 2740 " flowop read name=random-read,filename=bigfile,iosize=16k,random\n" \ 2741 " }\n" \ 2742 "}\n" \ 2743 "create files\n" \ 2744 "create processes\n" \ 2745 "stats clear\n" \ 2746 "sleep 30\n" \ 2747 "stats snap\n" 2748 2749 /* 2750 * usage() display brief or verbose help for the filebench(1) command. 2751 */ 2752 static void 2753 usage(int help) 2754 { 2755 if (help >= 1) 2756 (void) fprintf(stderr, USAGE1, cmdname); 2757 if (help >= 2) { 2758 2759 (void) fprintf(stderr, 2760 "\n'f' language definition:\n\n"); 2761 fileset_usage(); 2762 procflow_usage(); 2763 threadflow_usage(); 2764 flowoplib_usage(); 2765 eventgen_usage(); 2766 (void) fprintf(stderr, PARSER_CMDS); 2767 (void) fprintf(stderr, PARSER_EXAMPLE); 2768 } 2769 exit(E_USAGE); 2770 } 2771 2772 int 2773 yywrap() 2774 { 2775 char buf[1024]; 2776 2777 if (parentscript) { 2778 yyin = parentscript; 2779 yy_switchfilescript(yyin); 2780 parentscript = NULL; 2781 return (0); 2782 } else 2783 return (1); 2784 } 2785