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 /* yacc externals */ 79 extern FILE *yyin; 80 extern int yydebug; 81 extern void yyerror(char *s); 82 83 /* utilities */ 84 static void terminate(void); 85 static cmd_t *alloc_cmd(void); 86 static attr_t *alloc_attr(void); 87 static attr_t *get_attr(cmd_t *cmd, int64_t name); 88 static attr_t *get_attr_integer(cmd_t *cmd, int64_t name); 89 static attr_t *get_attr_bool(cmd_t *cmd, int64_t name); 90 static var_t *alloc_var(void); 91 static var_t *get_var(cmd_t *cmd, int64_t name); 92 static list_t *alloc_list(); 93 94 /* Info Commands */ 95 static void parser_show(cmd_t *); 96 static void parser_list(cmd_t *); 97 98 /* Define Commands */ 99 static void parser_proc_define(cmd_t *); 100 static void parser_thread_define(cmd_t *, procflow_t *, int instances); 101 static void parser_flowop_define(cmd_t *, threadflow_t *); 102 static void parser_file_define(cmd_t *); 103 static void parser_fileset_define(cmd_t *); 104 105 /* Create Commands */ 106 static void parser_proc_create(cmd_t *); 107 static void parser_thread_create(cmd_t *); 108 static void parser_flowop_create(cmd_t *); 109 static void parser_file_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_FILE: 830 $$->cmd = &parser_file_create; 831 break; 832 case FSE_FILESET: 833 $$->cmd = &parser_fileset_create; 834 break; 835 default: 836 filebench_log(LOG_ERROR, "unknown entity", $2); 837 YYERROR; 838 } 839 840 }; 841 842 shutdown_command: FSC_SHUTDOWN entity 843 { 844 if (($$ = alloc_cmd()) == NULL) 845 YYERROR; 846 switch ($2) { 847 case FSE_PROC: 848 $$->cmd = &parser_proc_shutdown; 849 break; 850 default: 851 filebench_log(LOG_ERROR, "unknown entity", $2); 852 YYERROR; 853 } 854 855 }; 856 857 sleep_command: FSC_SLEEP FSV_VAL_INT 858 { 859 if (($$ = alloc_cmd()) == NULL) 860 YYERROR; 861 $$->cmd = parser_sleep; 862 $$->cmd_qty = $2; 863 } 864 | FSC_SLEEP FSV_VARIABLE 865 { 866 vinteger_t *integer; 867 868 if (($$ = alloc_cmd()) == NULL) 869 YYERROR; 870 $$->cmd = parser_sleep_variable; 871 $$->cmd_tgt1 = fb_stralloc($2); 872 }; 873 874 run_command: FSC_RUN FSV_VAL_INT 875 { 876 if (($$ = alloc_cmd()) == NULL) 877 YYERROR; 878 $$->cmd = parser_run; 879 $$->cmd_qty = $2; 880 } 881 | FSC_RUN FSV_VARIABLE 882 { 883 vinteger_t *integer; 884 885 if (($$ = alloc_cmd()) == NULL) 886 YYERROR; 887 $$->cmd = parser_run_variable; 888 $$->cmd_tgt1 = fb_stralloc($2); 889 } 890 | FSC_RUN 891 { 892 vinteger_t *integer; 893 894 if (($$ = alloc_cmd()) == NULL) 895 YYERROR; 896 $$->cmd = parser_run; 897 $$->cmd_qty = 60UL; 898 }; 899 900 help_command: FSC_HELP 901 { 902 if (($$ = alloc_cmd()) == NULL) 903 YYERROR; 904 $$->cmd = parser_help; 905 }; 906 907 flowop_command: FSC_FLOWOP name 908 { 909 if (($$ = alloc_cmd()) == NULL) 910 YYERROR; 911 $$->cmd_name = fb_stralloc($2); 912 } 913 | flowop_command attr_ops 914 { 915 $1->cmd_attr_list = $2; 916 }; 917 918 load_command: FSC_LOAD FSV_STRING 919 { 920 FILE *newfile; 921 char loadfile[128]; 922 923 if (($$ = alloc_cmd()) == NULL) 924 YYERROR; 925 926 (void) strcpy(loadfile, $2); 927 (void) strcat(loadfile, ".f"); 928 929 if ((newfile = fopen(loadfile, "r")) == NULL) { 930 (void) strcpy(loadfile, FILEBENCHDIR); 931 (void) strcat(loadfile, "/workloads/"); 932 (void) strcat(loadfile, $2); 933 (void) strcat(loadfile, ".f"); 934 if ((newfile = fopen(loadfile, "r")) == NULL) { 935 filebench_log(LOG_ERROR, "Cannot open %s", loadfile); 936 YYERROR; 937 } 938 } 939 940 parentscript = yyin; 941 yyin = newfile; 942 yy_switchfileparent(yyin); 943 }; 944 945 entity: FSE_PROC {$$ = FSE_PROC;} 946 | FSE_THREAD {$$ = FSE_THREAD;} 947 | FSE_FILESET {$$ = FSE_FILESET;} 948 | FSE_FILE {$$ = FSE_FILE;}; 949 950 value: FSV_VAL_INT { $$.i = $1;} 951 | FSV_STRING { $$.s = $1;} 952 | FSV_VAL_BOOLEAN { $$.b = $1;}; 953 954 name: FSV_STRING; 955 956 attr_ops: attr_op 957 { 958 $$ = $1; 959 } 960 | attr_ops FSK_SEPLST attr_op 961 { 962 attr_t *attr = NULL; 963 attr_t *list_end = NULL; 964 965 for (attr = $1; attr != NULL; 966 attr = attr->attr_next) 967 list_end = attr; /* Find end of list */ 968 969 list_end->attr_next = $3; 970 971 $$ = $1; 972 }; 973 974 attr_op: attr_name FSK_ASSIGN attr_value 975 { 976 $$ = $3; 977 $$->attr_name = $1; 978 } 979 | attr_name 980 { 981 if (($$ = alloc_attr()) == NULL) 982 YYERROR; 983 $$->attr_name = $1; 984 } 985 986 attr_name: attrs_define_file 987 |attrs_define_fileset 988 |attrs_define_thread 989 |attrs_define_proc 990 |attrs_flowop 991 |attrs_eventgen; 992 993 attrs_define_proc: 994 FSA_NICE { $$ = FSA_NICE;} 995 |FSA_INSTANCES { $$ = FSA_INSTANCES;}; 996 997 attrs_define_file: 998 FSA_SIZE { $$ = FSA_SIZE;} 999 | FSA_PATH { $$ = FSA_PATH;} 1000 | FSA_REUSE { $$ = FSA_REUSE;} 1001 | FSA_PREALLOC { $$ = FSA_PREALLOC;} 1002 | FSA_PARALLOC { $$ = FSA_PARALLOC;}; 1003 1004 attrs_define_fileset: 1005 FSA_SIZE { $$ = FSA_SIZE;} 1006 | FSA_PATH { $$ = FSA_PATH;} 1007 | FSA_DIRWIDTH { $$ = FSA_DIRWIDTH;} 1008 | FSA_PREALLOC { $$ = FSA_PREALLOC;} 1009 | FSA_FILESIZEGAMMA { $$ = FSA_FILESIZEGAMMA;} 1010 | FSA_DIRGAMMA { $$ = FSA_DIRGAMMA;} 1011 | FSA_CACHED { $$ = FSA_CACHED;} 1012 | FSA_ENTRIES { $$ = FSA_ENTRIES;}; 1013 1014 attrs_define_thread: 1015 FSA_PROCESS { $$ = FSA_PROCESS;} 1016 |FSA_MEMSIZE { $$ = FSA_MEMSIZE;} 1017 |FSA_USEISM { $$ = FSA_USEISM;} 1018 |FSA_INSTANCES { $$ = FSA_INSTANCES;}; 1019 1020 attrs_flowop: 1021 FSA_WSS { $$ = FSA_WSS;} 1022 |FSA_FILE { $$ = FSA_FILE;} 1023 |FSA_NAME { $$ = FSA_NAME;} 1024 |FSA_RANDOM { $$ = FSA_RANDOM;} 1025 |FSA_FD { $$ = FSA_FD;} 1026 |FSA_SRCFD { $$ = FSA_SRCFD;} 1027 |FSA_ROTATEFD { $$ = FSA_ROTATEFD;} 1028 |FSA_DSYNC { $$ = FSA_DSYNC;} 1029 |FSA_DIRECTIO { $$ = FSA_DIRECTIO;} 1030 |FSA_TARGET { $$ = FSA_TARGET;} 1031 |FSA_ITERS { $$ = FSA_ITERS;} 1032 |FSA_VALUE { $$ = FSA_VALUE;} 1033 |FSA_BLOCKING { $$ = FSA_BLOCKING;} 1034 |FSA_HIGHWATER { $$ = FSA_HIGHWATER;} 1035 |FSA_IOSIZE { $$ = FSA_IOSIZE;}; 1036 1037 attrs_eventgen: 1038 FSA_RATE { $$ = FSA_RATE;}; 1039 1040 attr_value: var_string_list { 1041 if (($$ = alloc_attr()) == NULL) 1042 YYERROR; 1043 $$->attr_param_list = $1; 1044 } | FSV_STRING 1045 { 1046 if (($$ = alloc_attr()) == NULL) 1047 YYERROR; 1048 $$->attr_string = string_alloc($1); 1049 } | FSV_VAL_INT { 1050 if (($$ = alloc_attr()) == NULL) 1051 YYERROR; 1052 $$->attr_integer = integer_alloc($1); 1053 } | FSV_VARIABLE { 1054 if (($$ = alloc_attr()) == NULL) 1055 YYERROR; 1056 $$->attr_integer = var_ref_integer($1); 1057 $$->attr_string = var_ref_string($1); 1058 }; 1059 1060 %% 1061 1062 /* 1063 * The following 'c' routines implement the various commands defined in the 1064 * above yacc parser code. The yacc portion checks the syntax of the commands 1065 * found in a workload file, or typed on interactive command lines, parsing 1066 * the commands' parameters into lists. The lists are then passed in a cmd_t 1067 * struct for each command to its related routine in the following section 1068 * for actual execution. This section also includes a few utility routines 1069 * and the main entry point for the program. 1070 */ 1071 1072 /* 1073 * Entry point for filebench. Processes command line arguements. The -f 1074 * option will read in a workload file (the full name and extension must 1075 * must be given). The -a, -s, -m and -i options are used by worker process 1076 * to receive their name, the base address of shared memory, its path, and 1077 * the process' instance number, respectively. This information is supplied 1078 * by the master process when it execs worker processes under the process 1079 * model of execution. If the worker process arguments are passed then main 1080 * will call the procflow_exec routine which creates worker threadflows and 1081 * flowops and executes the procflow's portion of the workload model until 1082 * completion. If worker process arguments are not passed to the process, 1083 * then it becomes the master process for a filebench run. It initializes 1084 * the various filebench components and either executes the supplied workload 1085 * file, or enters interactive mode. 1086 */ 1087 1088 int 1089 main(int argc, char *argv[]) 1090 { 1091 int opt; 1092 int docmd = FS_FALSE; 1093 int instance; 1094 char procname[128]; 1095 caddr_t shmaddr; 1096 char dir[MAXPATHLEN]; 1097 #ifdef HAVE_SETRLIMIT 1098 struct rlimit rlp; 1099 #endif 1100 #ifdef HAVE_LIBTECLA 1101 char *line; 1102 #else 1103 char line[1024]; 1104 #endif 1105 char shmpathtmp[1024]; 1106 1107 #ifdef HAVE_SETRLIMIT 1108 /* Set resource limits */ 1109 (void) getrlimit(RLIMIT_NOFILE, &rlp); 1110 rlp.rlim_cur = rlp.rlim_max; 1111 setrlimit(RLIMIT_NOFILE, &rlp); 1112 #endif 1113 1114 yydebug = 0; 1115 execname = argv[0]; 1116 *procname = 0; 1117 cwd = getcwd(dir, MAXPATHLEN); 1118 1119 while ((opt = getopt(argc, argv, cmd_options)) != (int)EOF) { 1120 1121 switch (opt) { 1122 case 'h': 1123 usage(2); 1124 break; 1125 1126 case 'p': 1127 noproc = 1; 1128 break; 1129 1130 case 'f': 1131 if (optarg == NULL) 1132 usage(1); 1133 if ((yyin = fopen(optarg, "r")) == NULL) { 1134 (void) fprintf(stderr, 1135 "Cannot open file %s", optarg); 1136 exit(1); 1137 } 1138 dofile = FS_TRUE; 1139 fscriptname = optarg; 1140 1141 break; 1142 1143 case 'a': 1144 if (optarg == NULL) 1145 usage(1); 1146 sscanf(optarg, "%s", &procname[0]); 1147 break; 1148 1149 case 's': 1150 if (optarg == NULL) 1151 usage(1); 1152 #if defined(_LP64) || (__WORDSIZE == 64) 1153 sscanf(optarg, "%llx", &shmaddr); 1154 #else 1155 sscanf(optarg, "%x", &shmaddr); 1156 #endif 1157 break; 1158 1159 case 'm': 1160 if (optarg == NULL) 1161 usage(1); 1162 sscanf(optarg, "%s", shmpathtmp); 1163 shmpath = shmpathtmp; 1164 break; 1165 1166 case 'i': 1167 if (optarg == NULL) 1168 usage(1); 1169 sscanf(optarg, "%d", &instance); 1170 break; 1171 1172 case '?': 1173 default: 1174 usage(1); 1175 break; 1176 } 1177 } 1178 1179 #ifdef USE_PROCESS_MODEL 1180 if (!(*procname)) 1181 #endif 1182 printf("FileBench Version %s\n", FILEBENCH_VERSION); 1183 filebench_init(); 1184 1185 #ifdef USE_PROCESS_MODEL 1186 if (*procname) { 1187 pid = getpid(); 1188 1189 if (ipc_attach(shmaddr) < 0) { 1190 filebench_log(LOG_ERROR, "Cannot attach shm for %s", 1191 procname); 1192 exit(1); 1193 } 1194 1195 if (procflow_exec(procname, instance) < 0) { 1196 filebench_log(LOG_ERROR, "Cannot startup process %s", 1197 procname); 1198 exit(1); 1199 } 1200 exit(1); 1201 } 1202 #endif 1203 1204 pid = getpid(); 1205 ipc_init(); 1206 1207 if (fscriptname) 1208 (void) strcpy(filebench_shm->fscriptname, fscriptname); 1209 1210 flowop_init(); 1211 stats_init(); 1212 eventgen_init(); 1213 1214 signal(SIGINT, parser_abort); 1215 1216 if (dofile) 1217 yyparse(); 1218 else { 1219 #ifdef HAVE_LIBTECLA 1220 if ((gl = new_GetLine(MAX_LINE_LEN, MAX_CMD_HIST)) == NULL) { 1221 filebench_log(LOG_ERROR, 1222 "Failed to create GetLine object"); 1223 filebench_shutdown(1); 1224 } 1225 1226 if (gl_customize_completion(gl, NULL, command_complete)) { 1227 filebench_log(LOG_ERROR, 1228 "Failed to register auto-completion function"); 1229 filebench_shutdown(1); 1230 } 1231 1232 while (line = gl_get_line(gl, FILEBENCH_PROMPT, NULL, -1)) { 1233 arg_parse(line); 1234 yyparse(); 1235 } 1236 1237 del_GetLine(gl); 1238 #else 1239 while (!feof(stdin)) { 1240 printf(FILEBENCH_PROMPT); 1241 fflush(stdout); 1242 if (fgets(line, sizeof (line), stdin) == NULL) { 1243 if (errno == EINTR) 1244 continue; 1245 else 1246 break; 1247 } 1248 arg_parse(line); 1249 yyparse(); 1250 } 1251 printf("\n"); 1252 #endif /* HAVE_LIBTECLA */ 1253 } 1254 1255 parser_filebench_shutdown((cmd_t *)0); 1256 1257 return (0); 1258 } 1259 1260 /* 1261 * arg_parse() puts the parser into command parsing mode. Create a tmpfile 1262 * and instruct the parser to read instructions from this location by setting 1263 * yyin to the value returned by tmpfile. Write the command into the file. 1264 * Then seek back to to the start of the file so that the parser can read 1265 * the instructions. 1266 */ 1267 static void 1268 arg_parse(const char *command) 1269 { 1270 if ((yyin = tmpfile()) == NULL) 1271 filebench_log(LOG_FATAL, 1272 "Cannot create tmpfile: %s", strerror(errno)); 1273 1274 if (fwrite(command, strlen(command), 1, yyin) != 1) 1275 filebench_log(LOG_FATAL, 1276 "Cannot write tmpfile: %s", strerror(errno)); 1277 1278 if (fseek(yyin, 0, SEEK_SET) != 0) 1279 filebench_log(LOG_FATAL, 1280 "Cannot seek tmpfile: %s", strerror(errno)); 1281 } 1282 1283 /* 1284 * Converts a list of var_strings or ordinary strings to a single ordinary 1285 * string. It returns a pointer to the string (in malloc'd memory) if found, 1286 * or NULL otherwise. 1287 */ 1288 char * 1289 parser_list2string(list_t *list) 1290 { 1291 list_t *l; 1292 char *string; 1293 char *tmp; 1294 vinteger_t *integer; 1295 1296 if ((string = malloc(MAXPATHLEN)) == NULL) { 1297 filebench_log(LOG_ERROR, "Failed to allocate memory"); 1298 return (NULL); 1299 } 1300 1301 *string = 0; 1302 1303 1304 /* Format args */ 1305 for (l = list; l != NULL; 1306 l = l->list_next) { 1307 filebench_log(LOG_DEBUG_SCRIPT, 1308 "converting string '%s'", *l->list_string); 1309 1310 if ((tmp = var_to_string(*l->list_string)) != NULL) { 1311 (void) strcat(string, tmp); 1312 free(tmp); 1313 } else { 1314 (void) strcat(string, *l->list_string); 1315 } 1316 } 1317 return (string); 1318 } 1319 1320 /* 1321 * If the list just contains a single string starting with '$', then find 1322 * or create the named var and return the var's var_string component. 1323 * Otherwise, convert the list to a string, and allocate a var_string 1324 * containing a copy of that string. On failure either returns NULL 1325 * or shuts down the run. 1326 */ 1327 var_string_t 1328 parser_list2varstring(list_t *list) 1329 { 1330 /* Special case - variable name */ 1331 if ((list->list_next == NULL) && (*(*list->list_string) == '$')) 1332 return (var_ref_string(*list->list_string)); 1333 1334 return (string_alloc(parser_list2string(list))); 1335 } 1336 1337 /* 1338 * Looks for the var named in list_string of the first element of the 1339 * supplied list. If found, returns the var_integer portion of the var. 1340 * If the var is not found, cannot be allocated, the supplied list is 1341 * NULL, or the list_string filed is empty, returns NULL. 1342 */ 1343 var_integer_t 1344 parser_list2integer(list_t *list) 1345 { 1346 var_integer_t v; 1347 1348 if (list && (*(list->list_string) != NULL)) { 1349 v = var_ref_integer(*(list->list_string)); 1350 return (v); 1351 } 1352 1353 return (NULL); 1354 } 1355 1356 /* 1357 * Sets the event generator rate from the attribute supplied with the 1358 * command. If the attribute doesn't exist the routine does nothing. 1359 */ 1360 static void 1361 parser_eventgen(cmd_t *cmd) 1362 { 1363 attr_t *attr; 1364 vinteger_t rate; 1365 1366 /* Get the rate from attribute */ 1367 if (attr = get_attr_integer(cmd, FSA_RATE)) { 1368 if (attr->attr_integer) { 1369 filebench_log(LOG_VERBOSE, 1370 "Eventgen: %lld per second", 1371 *attr->attr_integer); 1372 eventgen_setrate(*attr->attr_integer); 1373 } 1374 } 1375 1376 } 1377 1378 /* 1379 * Assigns the designated integer variable successive values from the 1380 * supplied comma seperated integer list. After each successive integer 1381 * assignment, it executes the bracket enclosed list of commands. For 1382 * example, repeated runs of a workload with increasing io sizes can 1383 * be done using the following command line: 1384 * foreach $iosize in 2k, 4k, 8k {run 60} 1385 */ 1386 static void 1387 parser_foreach_integer(cmd_t *cmd) 1388 { 1389 list_t *list = cmd->cmd_param_list; 1390 cmd_t *inner_cmd; 1391 1392 for (; list != NULL; list = list->list_next) { 1393 var_assign_integer(cmd->cmd_tgt1, *list->list_integer); 1394 filebench_log(LOG_VERBOSE, "Iterating %s=%lld", 1395 cmd->cmd_tgt1, 1396 *list->list_integer); 1397 for (inner_cmd = cmd->cmd_list; inner_cmd != NULL; 1398 inner_cmd = inner_cmd->cmd_next) { 1399 inner_cmd->cmd(inner_cmd); 1400 } 1401 } 1402 } 1403 1404 /* 1405 * Similar to parser_foreach_integer(), except takes a list of strings after 1406 * the "in" token. For example, to run twice using a different directory, 1407 * perhaps using a different filesystem, the following command line 1408 * could be used: 1409 * foreach $dir in "/ufs_top/fbt", "/zfs_top/fbt" {run 60) 1410 */ 1411 static void 1412 parser_foreach_string(cmd_t *cmd) 1413 { 1414 list_t *list = cmd->cmd_param_list; 1415 cmd_t *inner_cmd; 1416 1417 for (; list != NULL; list = list->list_next) { 1418 var_assign_string(cmd->cmd_tgt1, *list->list_string); 1419 filebench_log(LOG_VERBOSE, "Iterating %s=%s", 1420 cmd->cmd_tgt1, 1421 *list->list_string); 1422 for (inner_cmd = cmd->cmd_list; inner_cmd != NULL; 1423 inner_cmd = inner_cmd->cmd_next) { 1424 inner_cmd->cmd(inner_cmd); 1425 } 1426 } 1427 } 1428 1429 /* 1430 * Lists the file name, path name and file size for all defined file objects. 1431 */ 1432 static void 1433 parser_list(cmd_t *cmd) 1434 { 1435 (void) fileobj_iter(fileobj_print); 1436 } 1437 1438 /* 1439 * Calls procflow_define() to allocate "instances" number of procflow(s) 1440 * (processes) with the supplied name. The default number of instances is 1441 * one. An optional priority level attribute can be supplied and is stored in 1442 * pf_nice. Finally the routine loops through the list of inner commands, if 1443 * any, which are defines for threadflows, and passes them one at a time to 1444 * parser_thread_define() to allocate threadflow entities for the process(es). 1445 */ 1446 static void 1447 parser_proc_define(cmd_t *cmd) 1448 { 1449 procflow_t *procflow, template; 1450 char *name; 1451 attr_t *attr; 1452 var_integer_t instances = integer_alloc(1); 1453 cmd_t *inner_cmd; 1454 1455 /* Get the name of the process */ 1456 if (attr = get_attr(cmd, FSA_NAME)) { 1457 name = *attr->attr_string; 1458 } else { 1459 filebench_log(LOG_ERROR, 1460 "define proc: proc specifies no name"); 1461 filebench_shutdown(1); 1462 } 1463 1464 /* Get the memory size from attribute */ 1465 if (attr = get_attr_integer(cmd, FSA_INSTANCES)) { 1466 filebench_log(LOG_DEBUG_IMPL, 1467 "Setting instances = %lld", 1468 *attr->attr_integer); 1469 instances = attr->attr_integer; 1470 } 1471 1472 if ((procflow = procflow_define(name, NULL, instances)) == NULL) { 1473 filebench_log(LOG_ERROR, 1474 "Failed to instantiate %d %s process(es)\n", 1475 instances, name); 1476 filebench_shutdown(1); 1477 } 1478 1479 /* Get the pri from attribute */ 1480 if (attr = get_attr_integer(cmd, FSA_NICE)) { 1481 filebench_log(LOG_DEBUG_IMPL, "Setting pri = %lld", 1482 *attr->attr_integer); 1483 procflow->pf_nice = attr->attr_integer; 1484 } else 1485 procflow->pf_nice = integer_alloc(0); 1486 1487 1488 /* Create the list of threads for this process */ 1489 for (inner_cmd = cmd->cmd_list; inner_cmd != NULL; 1490 inner_cmd = inner_cmd->cmd_next) { 1491 parser_thread_define(inner_cmd, procflow, *instances); 1492 } 1493 } 1494 1495 /* 1496 * Calls threadflow_define() to allocate "instances" number of threadflow(s) 1497 * (threads) with the supplied name. The default number of instances is 1498 * one. Two other optional attributes may be supplied, one to set the memory 1499 * size, stored in tf_memsize, and to select the use of Interprocess Shared 1500 * Memory, which sets the THREADFLOW_USEISM flag in tf_attrs. Finally 1501 * the routine loops through the list of inner commands, if any, which are 1502 * defines for flowops, and passes them one at a time to 1503 * parser_flowop_define() to allocate flowop entities for the threadflows. 1504 */ 1505 static void 1506 parser_thread_define(cmd_t *cmd, procflow_t *procflow, int procinstances) 1507 { 1508 threadflow_t *threadflow, template; 1509 attr_t *attr; 1510 var_integer_t instances = integer_alloc(1); 1511 cmd_t *inner_cmd; 1512 char *name; 1513 1514 memset(&template, 0, sizeof (threadflow_t)); 1515 1516 /* Get the name of the thread */ 1517 if (attr = get_attr(cmd, FSA_NAME)) { 1518 name = *attr->attr_string; 1519 } else { 1520 filebench_log(LOG_ERROR, 1521 "define thread: thread in process %s specifies no name", 1522 procflow->pf_name); 1523 filebench_shutdown(1); 1524 } 1525 1526 /* Get the number of instances from attribute */ 1527 if (attr = get_attr_integer(cmd, FSA_INSTANCES)) { 1528 filebench_log(LOG_DEBUG_IMPL, 1529 "define thread: Setting instances = %lld", 1530 *attr->attr_integer); 1531 instances = attr->attr_integer; 1532 } 1533 1534 /* Get the memory size from attribute */ 1535 if (attr = get_attr_integer(cmd, FSA_MEMSIZE)) { 1536 filebench_log(LOG_DEBUG_IMPL, 1537 "define thread: Setting memsize = %lld", 1538 *attr->attr_integer); 1539 template.tf_memsize = attr->attr_integer; 1540 } else 1541 template.tf_memsize = integer_alloc(0); 1542 1543 if ((threadflow = threadflow_define(procflow, name, 1544 &template, instances)) == NULL) { 1545 filebench_log(LOG_ERROR, 1546 "define thread: Failed to instantiate thread\n"); 1547 filebench_shutdown(1); 1548 } 1549 1550 /* Use ISM Memory? */ 1551 if (attr = get_attr(cmd, FSA_USEISM)) { 1552 threadflow->tf_attrs |= THREADFLOW_USEISM; 1553 } 1554 1555 /* Create the list of flowops */ 1556 for (inner_cmd = cmd->cmd_list; inner_cmd != NULL; 1557 inner_cmd = inner_cmd->cmd_next) { 1558 parser_flowop_define(inner_cmd, threadflow); 1559 } 1560 } 1561 1562 /* 1563 * Calls flowop_define() to allocate a flowop with the supplied name. 1564 * The allocated flowop inherits attributes from a base flowop of the 1565 * same type. If the new flowop has a file or fileset attribute specified, 1566 * it must specify a defined fileobj or fileset or an error will be logged. 1567 * The new flowop may also have the following attributes set by 1568 * the program: 1569 * - file size (fo_iosize) 1570 * - working set size (fo_wss) 1571 * - do random io (fo_random) 1572 * - do synchronous io (fo_dsync) 1573 * - perform each operation multiple times before advancing (fo_iter) 1574 * - target name (fo_targetname) 1575 * - An integer value (fo_value) 1576 * - a file descriptor (fo_fd) 1577 * - specify to rotate file descriptors (fo_rotatefd) 1578 * - a source fd (fo_srcfdnumber) 1579 * - specify a blocking operation (fo_blocking) 1580 * - specify a highwater mark (fo_highwater) 1581 * 1582 * After all the supplied attributes are stored in their respective locations 1583 * in the flowop object, the flowop's init function is called. No errors are 1584 * returned, but the filebench run will be terminated if the flowtype is not 1585 * specified, a name for the new flowop is not supplied, the flowop_define 1586 * call fails, or a file or fileset name is supplied but the corresponding 1587 * fileobj or fileset cannot be located. 1588 */ 1589 static void 1590 parser_flowop_define(cmd_t *cmd, threadflow_t *thread) 1591 { 1592 flowop_t *flowop, *flowop_type; 1593 fileobj_t *fileobj; 1594 char *type = (char *)cmd->cmd_name; 1595 char *name; 1596 attr_t *attr; 1597 1598 /* Get the inherited flowop */ 1599 flowop_type = flowop_find(type); 1600 if (flowop_type == NULL) { 1601 filebench_log(LOG_ERROR, 1602 "define flowop: flowop type %s not found", 1603 type); 1604 filebench_shutdown(1); 1605 } 1606 1607 /* Get the name of the flowop */ 1608 if (attr = get_attr(cmd, FSA_NAME)) { 1609 name = *attr->attr_string; 1610 } else { 1611 filebench_log(LOG_ERROR, 1612 "define flowop: flowop %s specifies no name", 1613 flowop_type->fo_name); 1614 filebench_shutdown(1); 1615 } 1616 1617 if ((flowop = flowop_define(thread, name, 1618 flowop_type, FLOW_MASTER, 0)) == NULL) { 1619 filebench_log(LOG_ERROR, 1620 "define flowop: Failed to instantiate flowop %s\n", 1621 cmd->cmd_name); 1622 filebench_shutdown(1); 1623 } 1624 1625 /* Get the filename from attribute */ 1626 if (attr = get_attr(cmd, FSA_FILE)) { 1627 flowop->fo_file = fileobj_find(*attr->attr_string); 1628 flowop->fo_fileset = fileset_find(*attr->attr_string); 1629 1630 if ((flowop->fo_file == NULL) && 1631 (flowop->fo_fileset == NULL)) { 1632 filebench_log(LOG_ERROR, 1633 "define flowop: file %s not found", 1634 *attr->attr_string); 1635 filebench_shutdown(1); 1636 } 1637 } 1638 1639 /* Get the iosize of the op */ 1640 if (attr = get_attr_integer(cmd, FSA_IOSIZE)) 1641 flowop->fo_iosize = attr->attr_integer; 1642 else 1643 flowop->fo_iosize = integer_alloc(0); 1644 1645 /* Get the working set size of the op */ 1646 if (attr = get_attr_integer(cmd, FSA_WSS)) 1647 flowop->fo_wss = attr->attr_integer; 1648 else 1649 flowop->fo_wss = integer_alloc(0); 1650 1651 /* Random I/O? */ 1652 if (attr = get_attr_bool(cmd, FSA_RANDOM)) 1653 flowop->fo_random = attr->attr_integer; 1654 else 1655 flowop->fo_random = integer_alloc(0); 1656 1657 /* Sync I/O? */ 1658 if (attr = get_attr_bool(cmd, FSA_DSYNC)) 1659 flowop->fo_dsync = attr->attr_integer; 1660 else 1661 flowop->fo_dsync = integer_alloc(0); 1662 1663 /* Iterations */ 1664 if (attr = get_attr_integer(cmd, FSA_ITERS)) 1665 flowop->fo_iters = attr->attr_integer; 1666 else 1667 flowop->fo_iters = integer_alloc(1); 1668 1669 1670 /* Target, for wakeup etc */ 1671 if (attr = get_attr(cmd, FSA_TARGET)) 1672 (void) strcpy(flowop->fo_targetname, *attr->attr_string); 1673 1674 /* Value */ 1675 if (attr = get_attr_integer(cmd, FSA_VALUE)) 1676 flowop->fo_value = attr->attr_integer; 1677 else 1678 flowop->fo_value = integer_alloc(0); 1679 1680 /* FD */ 1681 if (attr = get_attr_integer(cmd, FSA_FD)) 1682 flowop->fo_fdnumber = *attr->attr_integer; 1683 1684 /* Rotatefd? */ 1685 if (attr = get_attr_bool(cmd, FSA_ROTATEFD)) 1686 flowop->fo_rotatefd = attr->attr_integer; 1687 else 1688 flowop->fo_rotatefd = integer_alloc(0); 1689 1690 /* SRC FD, for copies etc... */ 1691 if (attr = get_attr_integer(cmd, FSA_SRCFD)) 1692 flowop->fo_srcfdnumber = *attr->attr_integer; 1693 1694 /* Blocking operation? */ 1695 if (attr = get_attr_bool(cmd, FSA_BLOCKING)) 1696 flowop->fo_blocking = attr->attr_integer; 1697 else 1698 flowop->fo_blocking = integer_alloc(0); 1699 1700 /* Blocking operation? */ 1701 if (attr = get_attr_bool(cmd, FSA_DIRECTIO)) 1702 flowop->fo_directio = attr->attr_integer; 1703 else 1704 flowop->fo_directio = integer_alloc(0); 1705 1706 /* Highwater mark */ 1707 if (attr = get_attr_integer(cmd, FSA_HIGHWATER)) 1708 flowop->fo_highwater = attr->attr_integer; 1709 else 1710 flowop->fo_highwater = integer_alloc(1); 1711 } 1712 1713 /* 1714 * Calls fileobj_define() to allocate a fileobj with the supplied name 1715 * and initializes the fileobj's pathname attribute, fo_path and fo_create, 1716 * and optionally the fo_prealloc, fo_paralloc, fo_reuse, fo_cached, 1717 * and fo_size attributes. 1718 */ 1719 static void 1720 parser_file_define(cmd_t *cmd) 1721 { 1722 fileobj_t *fileobj; 1723 char *name; 1724 attr_t *attr; 1725 var_string_t pathname; 1726 1727 /* Get the name of the file */ 1728 if (attr = get_attr(cmd, FSA_NAME)) { 1729 name = *attr->attr_string; 1730 } else { 1731 filebench_log(LOG_ERROR, 1732 "define file: file specifies no name"); 1733 return; 1734 } 1735 1736 if ((fileobj = fileobj_define(name)) == NULL) { 1737 filebench_log(LOG_ERROR, 1738 "define file: failed to instantiate file %s\n", 1739 cmd->cmd_name); 1740 return; 1741 } 1742 1743 /* Get the pathname from attribute */ 1744 if ((attr = get_attr(cmd, FSA_PATH)) == NULL) { 1745 filebench_log(LOG_ERROR, 1746 "define file: no pathname specified"); 1747 return; 1748 } 1749 1750 /* Expand variables in pathname */ 1751 if ((pathname = parser_list2varstring(attr->attr_param_list)) 1752 == NULL) { 1753 filebench_log(LOG_ERROR, "Cannot interpret path"); 1754 return; 1755 } 1756 1757 fileobj->fo_path = pathname; 1758 1759 /* For now, all files are pre-created */ 1760 fileobj->fo_create = integer_alloc(1); 1761 1762 /* Should we preallocate? */ 1763 if (attr = get_attr_bool(cmd, FSA_PREALLOC)) { 1764 fileobj->fo_prealloc = attr->attr_integer; 1765 } else 1766 fileobj->fo_prealloc = integer_alloc(0); 1767 1768 /* Should we prealloc in parallel? */ 1769 if (attr = get_attr_bool(cmd, FSA_PARALLOC)) { 1770 fileobj->fo_paralloc = attr->attr_integer; 1771 } else 1772 fileobj->fo_paralloc = integer_alloc(0); 1773 1774 /* Should we reuse the existing file? */ 1775 if (attr = get_attr_bool(cmd, FSA_REUSE)) { 1776 fileobj->fo_reuse = attr->attr_integer; 1777 } else 1778 fileobj->fo_reuse = integer_alloc(0); 1779 1780 /* Should we leave in cache? */ 1781 if (attr = get_attr_bool(cmd, FSA_CACHED)) { 1782 fileobj->fo_cached = attr->attr_integer; 1783 } else 1784 fileobj->fo_cached = integer_alloc(0); 1785 1786 /* Get the size of the file */ 1787 if (attr = get_attr_integer(cmd, FSA_SIZE)) { 1788 fileobj->fo_size = attr->attr_integer; 1789 } else 1790 fileobj->fo_size = integer_alloc(0); 1791 1792 } 1793 1794 /* 1795 * Calls fileset_define() to allocate a fileset with the supplied name and 1796 * initializes the fileset's pathname attribute, and optionally the fs_cached, 1797 * fs_reuse, fs_preallocpercent, fs_prealloc, fs_entries, fs_dirwidth, 1798 * fs_size, fs_dirgamma, and fs_sizegamma attributes. 1799 */ 1800 static void 1801 parser_fileset_define(cmd_t *cmd) 1802 { 1803 fileset_t *fileset; 1804 char *name; 1805 attr_t *attr; 1806 var_string_t pathname; 1807 1808 /* Get the name of the file */ 1809 if (attr = get_attr(cmd, FSA_NAME)) { 1810 name = *attr->attr_string; 1811 } else { 1812 filebench_log(LOG_ERROR, 1813 "define file: file specifies no name"); 1814 return; 1815 } 1816 1817 if ((fileset = fileset_define(name)) == NULL) { 1818 filebench_log(LOG_ERROR, 1819 "define file: failed to instantiate file %s\n", 1820 cmd->cmd_name); 1821 return; 1822 } 1823 1824 /* Get the pathname from attribute */ 1825 if ((attr = get_attr(cmd, FSA_PATH)) == NULL) { 1826 filebench_log(LOG_ERROR, "define file: no pathname specified"); 1827 return; 1828 } 1829 1830 /* Expand variables in pathname */ 1831 if ((pathname = parser_list2varstring(attr->attr_param_list)) == NULL) { 1832 filebench_log(LOG_ERROR, "Cannot interpret path"); 1833 return; 1834 } 1835 1836 fileset->fs_path = pathname; 1837 1838 /* Should we leave in cache? */ 1839 if (attr = get_attr_bool(cmd, FSA_CACHED)) { 1840 fileset->fs_cached = attr->attr_integer; 1841 } else 1842 fileset->fs_cached = integer_alloc(0); 1843 1844 /* Should we reuse the existing file? */ 1845 if (attr = get_attr_bool(cmd, FSA_REUSE)) { 1846 fileset->fs_reuse = attr->attr_integer; 1847 } else 1848 fileset->fs_reuse = integer_alloc(0); 1849 1850 /* How much should we prealloc */ 1851 if ((attr = get_attr_integer(cmd, FSA_PREALLOC)) && 1852 attr->attr_integer) { 1853 fileset->fs_preallocpercent = attr->attr_integer; 1854 } else if (attr && !attr->attr_integer) { 1855 fileset->fs_preallocpercent = integer_alloc(100); 1856 } else { 1857 fileset->fs_preallocpercent = integer_alloc(0); 1858 } 1859 1860 /* Should we preallocate? */ 1861 if (attr = get_attr_bool(cmd, FSA_PREALLOC)) { 1862 fileset->fs_prealloc = attr->attr_integer; 1863 } else 1864 fileset->fs_prealloc = integer_alloc(0); 1865 1866 /* Get the size of the fileset */ 1867 if (attr = get_attr_integer(cmd, FSA_ENTRIES)) { 1868 fileset->fs_entries = attr->attr_integer; 1869 } else { 1870 filebench_log(LOG_ERROR, "Fileset has zero entries"); 1871 fileset->fs_entries = integer_alloc(0); 1872 } 1873 1874 /* Get the mean dir width of the fileset */ 1875 if (attr = get_attr_integer(cmd, FSA_DIRWIDTH)) { 1876 fileset->fs_dirwidth = attr->attr_integer; 1877 } else { 1878 filebench_log(LOG_ERROR, "Fileset has zero directory width"); 1879 fileset->fs_dirwidth = integer_alloc(0); 1880 } 1881 1882 /* Get the mean or absolute size of the file */ 1883 if (attr = get_attr_integer(cmd, FSA_SIZE)) { 1884 fileset->fs_size = attr->attr_integer; 1885 } else 1886 fileset->fs_size = integer_alloc(0); 1887 1888 /* Get the gamma value for dir width distributions */ 1889 if (attr = get_attr_integer(cmd, FSA_DIRGAMMA)) { 1890 fileset->fs_dirgamma = attr->attr_integer; 1891 } else 1892 fileset->fs_dirgamma = integer_alloc(1500); 1893 1894 /* Get the gamma value for dir width distributions */ 1895 if (attr = get_attr_integer(cmd, FSA_FILESIZEGAMMA)) { 1896 fileset->fs_sizegamma = attr->attr_integer; 1897 } else 1898 fileset->fs_sizegamma = integer_alloc(1500); 1899 } 1900 1901 /* 1902 * Creates and starts all defined procflow processes. The call to 1903 * procflow_init() results in creation of the requested number of 1904 * process instances for each previously defined procflow. The 1905 * child processes exec() a new instance of filebench, passing it 1906 * the instance number and address of the shared memory region. 1907 * The child processes will then create their threads and flowops. 1908 * The routine then unlocks the run_lock to allow all the processes' 1909 * threads to start and waits for all of them to begin execution. 1910 * Finally, it records the start time and resets the event generation 1911 * system. 1912 */ 1913 static void 1914 parser_proc_create(cmd_t *cmd) 1915 { 1916 if (procflow_init() != 0) { 1917 filebench_log(LOG_ERROR, "Failed to create processes\n"); 1918 filebench_shutdown(1); 1919 } 1920 1921 /* Release the read lock, allowing threads to start */ 1922 (void) pthread_rwlock_unlock(&filebench_shm->run_lock); 1923 1924 /* Wait for all threads to start */ 1925 if (procflow_allstarted() != 0) { 1926 filebench_log(LOG_ERROR, "Could not start run"); 1927 return; 1928 } 1929 1930 1931 if (filebench_shm->shm_required && 1932 (ipc_ismcreate(filebench_shm->shm_required) < 0)) { 1933 filebench_log(LOG_ERROR, "Could not allocate shared memory"); 1934 return; 1935 } 1936 1937 filebench_shm->starttime = gethrtime(); 1938 eventgen_reset(); 1939 } 1940 1941 /* 1942 * Calls fileobj_init() to create and optionally pre fill files 1943 * for all fileobjs on the master list of fileobjs (filelist). 1944 * If errors are encountered, calls filebench_shutdown() 1945 * to exit filebench. 1946 */ 1947 static void 1948 parser_file_create(cmd_t *cmd) 1949 { 1950 fileobj_t *fileobj; 1951 1952 if (fileobj_init() != 0) { 1953 filebench_log(LOG_ERROR, "Failed to create files"); 1954 filebench_shutdown(1); 1955 } 1956 } 1957 1958 /* 1959 * Calls fileset_createset() to populate all filesets and create all 1960 * associated, initially existant, files and subdirectories. 1961 * If errors are encountered, calls filebench_shutdown() 1962 * to exit filebench. 1963 */ 1964 static void 1965 parser_fileset_create(cmd_t *cmd) 1966 { 1967 fileset_t *fileset; 1968 1969 if (fileset_createset(NULL) != 0) { 1970 filebench_log(LOG_ERROR, "Failed to create filesets"); 1971 filebench_shutdown(1); 1972 } 1973 } 1974 1975 /* 1976 * Shuts down all processes and their associated threads. When finished 1977 * it deletes interprocess shared memory and resets the event generator. 1978 * It does not exit the filebench program though. 1979 */ 1980 static void 1981 parser_proc_shutdown(cmd_t *cmd) 1982 { 1983 filebench_log(LOG_INFO, "Shutting down processes"); 1984 procflow_shutdown(); 1985 if (filebench_shm->shm_required) 1986 ipc_ismdelete(); 1987 eventgen_reset(); 1988 } 1989 1990 /* 1991 * Ends filebench run after first destoring any interprocess 1992 * shared memory. The call to filebench_shutdown() 1993 * also causes filebench to exit. 1994 */ 1995 static void 1996 parser_filebench_shutdown(cmd_t *cmd) 1997 { 1998 ipc_cleanup(); 1999 filebench_shutdown(1); 2000 } 2001 2002 /* 2003 * Sleeps for cmd->cmd_qty seconds, one second at a time. 2004 */ 2005 static void 2006 parser_sleep(cmd_t *cmd) 2007 { 2008 int sleeptime; 2009 2010 /* check for startup errors */ 2011 if (filebench_shm->f_abort) 2012 return; 2013 2014 sleeptime = cmd->cmd_qty; 2015 filebench_log(LOG_INFO, "Running..."); 2016 while (sleeptime) { 2017 (void) sleep(1); 2018 sleeptime--; 2019 if (filebench_shm->f_abort) 2020 break; 2021 } 2022 filebench_log(LOG_INFO, "Run took %lld seconds...", 2023 cmd->cmd_qty - sleeptime); 2024 } 2025 2026 /* 2027 * Do a file bench run. Calls routines to create file sets, files, and 2028 * processes. It resets the statistics counters, then sleeps for the runtime 2029 * passed as an argument to it on the command line in 1 second increments. 2030 * When it is finished sleeping, it collects a snapshot of the statistics 2031 * and ends the run. 2032 */ 2033 static void 2034 parser_run(cmd_t *cmd) 2035 { 2036 int runtime; 2037 2038 runtime = cmd->cmd_qty; 2039 parser_fileset_create(cmd); 2040 parser_file_create(cmd); 2041 parser_proc_create(cmd); 2042 2043 /* check for startup errors */ 2044 if (filebench_shm->f_abort) 2045 return; 2046 2047 filebench_log(LOG_INFO, "Running..."); 2048 stats_clear(); 2049 while (runtime) { 2050 (void) sleep(1); 2051 runtime--; 2052 if (filebench_shm->f_abort) 2053 break; 2054 } 2055 filebench_log(LOG_INFO, "Run took %lld seconds...", 2056 cmd->cmd_qty - runtime); 2057 parser_statssnap(cmd); 2058 parser_proc_shutdown(cmd); 2059 } 2060 2061 /* 2062 * Similar to parser_run, but gets the sleep time from a variable 2063 * whose name is supplied as an argument to the command. 2064 */ 2065 static void 2066 parser_run_variable(cmd_t *cmd) 2067 { 2068 vinteger_t *integer = var_ref_integer(cmd->cmd_tgt1); 2069 int runtime; 2070 2071 if (integer == NULL) { 2072 filebench_log(LOG_ERROR, "Unknown variable %s", 2073 cmd->cmd_tgt1); 2074 return; 2075 } 2076 2077 runtime = *integer; 2078 2079 /* check for startup errors */ 2080 if (filebench_shm->f_abort) 2081 return; 2082 2083 filebench_log(LOG_INFO, "Running..."); 2084 stats_clear(); 2085 while (runtime) { 2086 (void) sleep(1); 2087 runtime--; 2088 if (filebench_shm->f_abort) 2089 break; 2090 } 2091 filebench_log(LOG_INFO, "Run took %lld seconds...", 2092 *integer - runtime); 2093 parser_statssnap(cmd); 2094 } 2095 2096 char *usagestr = NULL; 2097 2098 /* 2099 * Prints usage string if defined, else just a message requesting load of a 2100 * personality. 2101 */ 2102 static void 2103 parser_help(cmd_t *cmd) 2104 { 2105 int runtime; 2106 2107 if (usagestr) { 2108 filebench_log(LOG_INFO, "%s", usagestr); 2109 } else { 2110 filebench_log(LOG_INFO, 2111 "load <personality> (ls " 2112 "/usr/benchmarks/filebench/workloads for list)"); 2113 } 2114 } 2115 2116 char *varstr = NULL; 2117 2118 /* 2119 * Prints the string of all var definitions, if there is one. 2120 */ 2121 static void 2122 parser_printvars(cmd_t *cmd) 2123 { 2124 int runtime; 2125 char *str, *c; 2126 2127 if (varstr) { 2128 str = strdup(varstr); 2129 for (c = str; *c != '\0'; c++) { 2130 if ((char)*c == '$') 2131 *c = ' '; 2132 } 2133 filebench_log(LOG_INFO, "%s", str); 2134 free(str); 2135 } 2136 } 2137 2138 /* 2139 * Used by the SET command to add a var and default value string to the 2140 * varstr string. It allocates a new, larger varstr string, copies the 2141 * old contents of varstr into it, then adds the new var string on the end. 2142 */ 2143 static void 2144 parser_vars(cmd_t *cmd) 2145 { 2146 char *string = cmd->cmd_tgt1; 2147 char *newvars; 2148 2149 if (string == NULL) 2150 return; 2151 2152 if (dofile) 2153 return; 2154 2155 if (varstr == NULL) { 2156 newvars = malloc(strlen(string) + 2); 2157 *newvars = 0; 2158 } else { 2159 newvars = malloc(strlen(varstr) + strlen(string) + 2); 2160 (void) strcpy(newvars, varstr); 2161 } 2162 (void) strcat(newvars, string); 2163 (void) strcat(newvars, " "); 2164 2165 if (varstr) 2166 free(varstr); 2167 2168 varstr = newvars; 2169 } 2170 2171 /* 2172 * Same as parser_sleep, except the sleep time is obtained from a variable 2173 * whose name is passed to it as an argument on the command line. 2174 */ 2175 static void 2176 parser_sleep_variable(cmd_t *cmd) 2177 { 2178 vinteger_t *integer = var_ref_integer(cmd->cmd_tgt1); 2179 int sleeptime; 2180 2181 if (integer == NULL) { 2182 filebench_log(LOG_ERROR, "Unknown variable %s", 2183 cmd->cmd_tgt1); 2184 return; 2185 } 2186 2187 sleeptime = *integer; 2188 2189 /* check for startup errors */ 2190 if (filebench_shm->f_abort) 2191 return; 2192 2193 filebench_log(LOG_INFO, "Running..."); 2194 while (sleeptime) { 2195 (void) sleep(1); 2196 sleeptime--; 2197 if (filebench_shm->f_abort) 2198 break; 2199 } 2200 filebench_log(LOG_INFO, "Run took %lld seconds...", 2201 *integer - sleeptime); 2202 } 2203 2204 /* 2205 * Parser log prints the values of a list of variables to the log file. 2206 * The list of variables is placed on the command line, separated 2207 * by comas and the entire list is enclosed in quotes. 2208 * For example, if $dir contains "/export/home/tmp" and $filesize = 1048576, 2209 * then typing: log "$dir, $filesize" prints: log /export/home/tmp, 1048576 2210 */ 2211 static void 2212 parser_log(cmd_t *cmd) 2213 { 2214 char *string; 2215 2216 if (cmd->cmd_param_list == NULL) 2217 return; 2218 2219 string = parser_list2string(cmd->cmd_param_list); 2220 2221 if (string == NULL) 2222 return; 2223 2224 filebench_log(LOG_VERBOSE, "log %s", string); 2225 filebench_log(LOG_LOG, "%s", string); 2226 } 2227 2228 /* 2229 * Implements the stats directory command. changes the directory for 2230 * dumping statistics to supplied directory path. For example: 2231 * stats directory /tmp 2232 * changes the stats directory to "/tmp". 2233 */ 2234 static void 2235 parser_directory(cmd_t *cmd) 2236 { 2237 char newdir[MAXPATHLEN]; 2238 char *dir; 2239 2240 if ((dir = parser_list2string(cmd->cmd_param_list)) == NULL) { 2241 filebench_log(LOG_ERROR, "Cannot interpret directory"); 2242 return; 2243 } 2244 2245 *newdir = 0; 2246 /* Change dir relative to cwd if path not fully qualified */ 2247 if (*dir != '/') { 2248 (void) strcat(newdir, cwd); 2249 (void) strcat(newdir, "/"); 2250 } 2251 (void) strcat(newdir, dir); 2252 (void) mkdir(newdir, 0755); 2253 filebench_log(LOG_VERBOSE, "Change dir to %s", newdir); 2254 chdir(newdir); 2255 free(dir); 2256 } 2257 2258 #define PIPE_PARENT 1 2259 #define PIPE_CHILD 0 2260 2261 /* 2262 * Runs the quoted unix command as a background process. Intended for 2263 * running statistics gathering utilities such as mpstat while the filebench 2264 * workload is running. Also records the pid's of the background processes 2265 * so that parser_statssnap() can terminate them when the run completes. 2266 */ 2267 static void 2268 parser_statscmd(cmd_t *cmd) 2269 { 2270 char *string; 2271 pid_t pid; 2272 pidlist_t *pidlistent; 2273 int pipe_fd[2]; 2274 int newstdout; 2275 2276 if (cmd->cmd_param_list == NULL) 2277 return; 2278 2279 string = parser_list2string(cmd->cmd_param_list); 2280 2281 if (string == NULL) 2282 return; 2283 2284 if ((pipe(pipe_fd)) < 0) { 2285 filebench_log(LOG_ERROR, "statscmd pipe failed"); 2286 return; 2287 } 2288 2289 #ifdef HAVE_FORK1 2290 if ((pid = fork1()) < 0) { 2291 filebench_log(LOG_ERROR, "statscmd fork failed"); 2292 return; 2293 } 2294 #elif HAVE_FORK 2295 if ((pid = fork()) < 0) { 2296 filebench_log(LOG_ERROR, "statscmd fork failed"); 2297 return; 2298 } 2299 #else 2300 Crash! - Need code to deal with no fork1! 2301 #endif /* HAVE_FORK1 */ 2302 2303 if (pid == 0) { 2304 2305 setsid(); 2306 2307 filebench_log(LOG_VERBOSE, 2308 "Backgrounding %s", string); 2309 /* 2310 * Child 2311 * - close stdout 2312 * - dup to create new stdout 2313 * - close pipe fds 2314 */ 2315 (void) close(1); 2316 2317 if ((newstdout = dup(pipe_fd[PIPE_CHILD])) < 0) { 2318 filebench_log(LOG_ERROR, 2319 "statscmd dup failed: %s", 2320 strerror(errno)); 2321 } 2322 2323 (void) close(pipe_fd[PIPE_PARENT]); 2324 (void) close(pipe_fd[PIPE_CHILD]); 2325 2326 if (system(string) < 0) { 2327 filebench_log(LOG_ERROR, 2328 "statscmd exec failed: %s", 2329 strerror(errno)); 2330 } 2331 /* Failed! */ 2332 exit(1); 2333 2334 } else { 2335 2336 /* Record pid in pidlist for subsequent reaping by stats snap */ 2337 if ((pidlistent = (pidlist_t *)malloc(sizeof (pidlist_t))) 2338 == NULL) { 2339 filebench_log(LOG_ERROR, "pidlistent malloc failed"); 2340 return; 2341 } 2342 2343 pidlistent->pl_pid = pid; 2344 pidlistent->pl_fd = pipe_fd[PIPE_PARENT]; 2345 (void) close(pipe_fd[PIPE_CHILD]); 2346 2347 /* Add fileobj to global list */ 2348 if (pidlist == NULL) { 2349 pidlist = pidlistent; 2350 pidlistent->pl_next = NULL; 2351 } else { 2352 pidlistent->pl_next = pidlist; 2353 pidlist = pidlistent; 2354 } 2355 } 2356 } 2357 2358 /* 2359 * Launches a shell to run the unix command supplied in the argument. 2360 * The command should be enclosed in quotes, as in: 2361 * system "rm xyz" 2362 * which would run the "rm" utility to delete the file "xyz". 2363 */ 2364 static void 2365 parser_system(cmd_t *cmd) 2366 { 2367 char *string; 2368 2369 if (cmd->cmd_param_list == NULL) 2370 return; 2371 2372 string = parser_list2string(cmd->cmd_param_list); 2373 2374 if (string == NULL) 2375 return; 2376 2377 filebench_log(LOG_VERBOSE, 2378 "Running '%s'", string); 2379 2380 if (system(string) < 0) { 2381 filebench_log(LOG_ERROR, 2382 "system exec failed: %s", 2383 strerror(errno)); 2384 } 2385 free(string); 2386 } 2387 2388 /* 2389 * Echos string supplied with command to the log. 2390 */ 2391 static void 2392 parser_echo(cmd_t *cmd) 2393 { 2394 char *string; 2395 2396 if (cmd->cmd_param_list == NULL) 2397 return; 2398 2399 string = parser_list2string(cmd->cmd_param_list); 2400 2401 if (string == NULL) 2402 return; 2403 2404 filebench_log(LOG_INFO, "%s", string); 2405 } 2406 2407 2408 /* 2409 * Adds the string supplied as the argument to the usage command 2410 * to the end of the string printed by the help command. 2411 */ 2412 static void 2413 parser_usage(cmd_t *cmd) 2414 { 2415 char *string; 2416 char *newusage; 2417 2418 if (cmd->cmd_param_list == NULL) 2419 return; 2420 2421 string = parser_list2string(cmd->cmd_param_list); 2422 2423 if (string == NULL) 2424 return; 2425 2426 if (dofile) 2427 return; 2428 2429 if (usagestr == NULL) { 2430 newusage = malloc(strlen(string) + 2); 2431 *newusage = 0; 2432 } else { 2433 newusage = malloc(strlen(usagestr) + strlen(string) + 2); 2434 (void) strcpy(newusage, usagestr); 2435 } 2436 (void) strcat(newusage, "\n"); 2437 (void) strcat(newusage, string); 2438 2439 if (usagestr) 2440 free(usagestr); 2441 2442 usagestr = newusage; 2443 2444 filebench_log(LOG_INFO, "%s", string); 2445 } 2446 2447 /* 2448 * Updates the global dump filename with the filename supplied 2449 * as the command's argument. Then dumps the statistics of each 2450 * worker flowop into the dump file, followed by a summary of 2451 * overall totals. 2452 */ 2453 static void 2454 parser_statsdump(cmd_t *cmd) 2455 { 2456 char *string; 2457 2458 if (cmd->cmd_param_list == NULL) 2459 return; 2460 2461 string = parser_list2string(cmd->cmd_param_list); 2462 2463 if (string == NULL) 2464 return; 2465 2466 filebench_log(LOG_VERBOSE, 2467 "Stats dump to file '%s'", string); 2468 2469 stats_dump(string); 2470 2471 free(string); 2472 } 2473 2474 /* 2475 * Same as parser_statsdump, but in xml format. 2476 */ 2477 static void 2478 parser_statsxmldump(cmd_t *cmd) 2479 { 2480 char *string; 2481 2482 if (cmd->cmd_param_list == NULL) 2483 return; 2484 2485 string = parser_list2string(cmd->cmd_param_list); 2486 2487 if (string == NULL) 2488 return; 2489 2490 filebench_log(LOG_VERBOSE, 2491 "Stats dump to file '%s'", string); 2492 2493 stats_xmldump(string); 2494 2495 free(string); 2496 } 2497 2498 /* 2499 * Kills off background statistics collection processes, then takes a snapshot 2500 * of the filebench run's collected statistics using stats_snap() from 2501 * stats.c. 2502 */ 2503 static void 2504 parser_statssnap(cmd_t *cmd) 2505 { 2506 pidlist_t *pidlistent; 2507 int stat; 2508 pid_t pid; 2509 2510 for (pidlistent = pidlist; pidlistent != NULL; 2511 pidlistent = pidlistent->pl_next) { 2512 filebench_log(LOG_VERBOSE, "Killing session %d for pid %d", 2513 getsid(pidlistent->pl_pid), 2514 pidlistent->pl_pid); 2515 if (pidlistent->pl_fd) 2516 (void) close(pidlistent->pl_fd); 2517 #ifdef HAVE_SIGSEND 2518 sigsend(P_SID, getsid(pidlistent->pl_pid), SIGTERM); 2519 #else 2520 (void) kill(-1, SIGTERM); 2521 #endif 2522 2523 /* Close pipe */ 2524 if (pidlistent->pl_fd) 2525 (void) close(pidlistent->pl_fd); 2526 2527 /* Wait for cmd and all its children */ 2528 while ((pid = waitpid(pidlistent->pl_pid * -1, &stat, 0)) > 0) 2529 filebench_log(LOG_DEBUG_IMPL, 2530 "Waited for pid %lld", pid); 2531 } 2532 2533 for (pidlistent = pidlist; pidlistent != NULL; 2534 pidlistent = pidlistent->pl_next) { 2535 free(pidlistent); 2536 } 2537 2538 pidlist = NULL; 2539 stats_snap(); 2540 } 2541 2542 /* 2543 * Shutdown filebench. 2544 */ 2545 static void 2546 parser_abort(int arg) 2547 { 2548 (void) sigignore(SIGINT); 2549 filebench_log(LOG_INFO, "Aborting..."); 2550 filebench_shutdown(1); 2551 } 2552 2553 /* 2554 * alloc_cmd() allocates the required resources for a cmd_t. On failure, a 2555 * filebench_log is issued and NULL is returned. 2556 */ 2557 static cmd_t * 2558 alloc_cmd(void) 2559 { 2560 cmd_t *cmd; 2561 2562 if ((cmd = malloc(sizeof (cmd_t))) == NULL) { 2563 filebench_log(LOG_ERROR, "Alloc cmd failed"); 2564 return (NULL); 2565 } 2566 2567 (void) memset(cmd, 0, sizeof (cmd_t)); 2568 2569 return (cmd); 2570 } 2571 2572 /* 2573 * Frees the resources of a cmd_t and then the cmd_t "cmd" itself. 2574 */ 2575 static void 2576 free_cmd(cmd_t *cmd) 2577 { 2578 free((void *)cmd->cmd_tgt1); 2579 free((void *)cmd->cmd_tgt2); 2580 free(cmd); 2581 } 2582 2583 /* 2584 * Allocates an attr_t structure and zeros it. Returns NULL on failure, or 2585 * a pointer to the attr_t. 2586 */ 2587 static attr_t * 2588 alloc_attr() 2589 { 2590 attr_t *attr; 2591 2592 if ((attr = malloc(sizeof (attr_t))) == NULL) { 2593 return (NULL); 2594 } 2595 2596 (void) memset(attr, 0, sizeof (attr_t)); 2597 return (attr); 2598 } 2599 2600 /* 2601 * Searches the attribute list for the command for the named attribute type. 2602 * The attribute list is created by the parser from the list of attributes 2603 * supplied with certain commands, such as the define and flowop commands. 2604 * Returns a pointer to the attribute structure if the named attribute is 2605 * found, otherwise returns NULL. If the attribute includes a parameter list, 2606 * the list is converted to a string and stored in the attr_string field of 2607 * the returned attr_t struct. 2608 */ 2609 static attr_t * 2610 get_attr(cmd_t *cmd, int64_t name) 2611 { 2612 attr_t *attr; 2613 attr_t *rtn = NULL; 2614 char *string; 2615 2616 for (attr = cmd->cmd_attr_list; attr != NULL; 2617 attr = attr->attr_next) { 2618 filebench_log(LOG_DEBUG_IMPL, 2619 "attr %d = %d %llx?", 2620 attr->attr_name, 2621 name, 2622 attr->attr_integer); 2623 2624 if (attr->attr_name == name) 2625 rtn = attr; 2626 } 2627 2628 if (rtn == NULL) 2629 return (NULL); 2630 2631 if (rtn->attr_param_list) { 2632 filebench_log(LOG_DEBUG_SCRIPT, "attr is param list"); 2633 string = parser_list2string(rtn->attr_param_list); 2634 if (string != NULL) { 2635 rtn->attr_string = string_alloc(string); 2636 filebench_log(LOG_DEBUG_SCRIPT, 2637 "attr string %s", string); 2638 } 2639 } 2640 2641 return (rtn); 2642 } 2643 2644 /* 2645 * Similar to get_attr, but converts the parameter string supplied with the 2646 * named attribute to an integer and stores the integer in the attr_integer 2647 * portion of the returned attr_t struct. 2648 */ 2649 static attr_t * 2650 get_attr_integer(cmd_t *cmd, int64_t name) 2651 { 2652 attr_t *attr; 2653 attr_t *rtn = NULL; 2654 2655 for (attr = cmd->cmd_attr_list; attr != NULL; 2656 attr = attr->attr_next) { 2657 if (attr->attr_name == name) 2658 rtn = attr; 2659 } 2660 2661 if (rtn == NULL) 2662 return (NULL); 2663 2664 if (rtn->attr_param_list) { 2665 rtn->attr_integer = parser_list2integer(rtn->attr_param_list); 2666 } 2667 2668 return (rtn); 2669 } 2670 2671 /* 2672 * Similar to get_attr, but converts the parameter string supplied with the 2673 * named attribute to an integer and stores the integer in the attr_integer 2674 * portion of the returned attr_t struct. If no parameter string is supplied 2675 * then it defaults to TRUE (1). 2676 */ 2677 static attr_t * 2678 get_attr_bool(cmd_t *cmd, int64_t name) 2679 { 2680 attr_t *attr; 2681 attr_t *rtn = NULL; 2682 2683 for (attr = cmd->cmd_attr_list; attr != NULL; 2684 attr = attr->attr_next) { 2685 if (attr->attr_name == name) 2686 rtn = attr; 2687 } 2688 2689 if (rtn == NULL) 2690 return (NULL); 2691 2692 if (rtn->attr_param_list) { 2693 rtn->attr_integer = parser_list2integer(rtn->attr_param_list); 2694 } else if (rtn->attr_integer == 0) { 2695 rtn->attr_integer = integer_alloc(1); 2696 } 2697 2698 return (rtn); 2699 } 2700 2701 /* 2702 * Allocates memory for a list_t structure, initializes it to zero, and 2703 * returns a pointer to it. On failure, returns NULL. 2704 */ 2705 static list_t * 2706 alloc_list() 2707 { 2708 list_t *list; 2709 2710 if ((list = malloc(sizeof (list_t))) == NULL) { 2711 return (NULL); 2712 } 2713 2714 (void) memset(list, 0, sizeof (list_t)); 2715 return (list); 2716 } 2717 2718 2719 #define USAGE1 \ 2720 "Usage:\n" \ 2721 "%s: interpret f script and generate file workload\n" \ 2722 "Options:\n" \ 2723 " [-h] Display verbose help\n" \ 2724 " [-p] Disable opening /proc to set uacct to enable truss\n" 2725 2726 #define PARSER_CMDS \ 2727 "create [files|filesets|processes]\n" \ 2728 "stats [clear|snap]\n" \ 2729 "stats command \"shell command $var1,$var2...\"\n" \ 2730 "stats directory <directory>\n" \ 2731 "sleep <sleep-value>\n" \ 2732 "quit\n\n" \ 2733 "Variables:\n" \ 2734 "set $var = value\n" \ 2735 " $var - regular variables\n" \ 2736 " ${var} - internal special variables\n" \ 2737 " $(var) - environment variables\n\n" 2738 2739 #define PARSER_EXAMPLE \ 2740 "Example:\n\n" \ 2741 "#!/usr/bin/filebench -f\n" \ 2742 "\n" \ 2743 "define file name=bigfile,path=bigfile,size=1g,prealloc,reuse\n" \ 2744 "define process name=randomizer\n" \ 2745 "{\n" \ 2746 " thread random-thread procname=randomizer\n" \ 2747 " {\n" \ 2748 " flowop read name=random-read,filename=bigfile,iosize=16k,random\n" \ 2749 " }\n" \ 2750 "}\n" \ 2751 "create files\n" \ 2752 "create processes\n" \ 2753 "stats clear\n" \ 2754 "sleep 30\n" \ 2755 "stats snap\n" 2756 2757 /* 2758 * usage() display brief or verbose help for the filebench(1) command. 2759 */ 2760 static void 2761 usage(int help) 2762 { 2763 if (help >= 1) 2764 (void) fprintf(stderr, USAGE1, cmdname); 2765 if (help >= 2) { 2766 2767 (void) fprintf(stderr, 2768 "\n'f' language definition:\n\n"); 2769 fileobj_usage(); 2770 fileset_usage(); 2771 procflow_usage(); 2772 threadflow_usage(); 2773 flowoplib_usage(); 2774 eventgen_usage(); 2775 (void) fprintf(stderr, PARSER_CMDS); 2776 (void) fprintf(stderr, PARSER_EXAMPLE); 2777 } 2778 exit(E_USAGE); 2779 } 2780 2781 int 2782 yywrap() 2783 { 2784 char buf[1024]; 2785 2786 if (parentscript) { 2787 yyin = parentscript; 2788 yy_switchfilescript(yyin); 2789 parentscript = NULL; 2790 return (0); 2791 } else 2792 return (1); 2793 } 2794