init.c revision e9f98a793c10943814851e6840fc475d31cea0d1
1/* 2 * This file contains job initialization and setup functions. 3 */ 4#include <stdio.h> 5#include <stdlib.h> 6#include <unistd.h> 7#include <fcntl.h> 8#include <ctype.h> 9#include <string.h> 10#include <errno.h> 11#include <getopt.h> 12#include <sys/ipc.h> 13#include <sys/shm.h> 14#include <sys/types.h> 15#include <sys/stat.h> 16 17#include "fio.h" 18#include "parse.h" 19#include "smalloc.h" 20#include "filehash.h" 21#include "verify.h" 22 23static char fio_version_string[] = "fio 1.27"; 24 25#define FIO_RANDSEED (0xb1899bedUL) 26 27static char **ini_file; 28static int max_jobs = MAX_JOBS; 29static int dump_cmdline; 30 31static struct thread_data def_thread; 32struct thread_data *threads = NULL; 33 34int exitall_on_terminate = 0; 35int terse_output = 0; 36int eta_print; 37unsigned long long mlock_size = 0; 38FILE *f_out = NULL; 39FILE *f_err = NULL; 40char *job_section = NULL; 41 42int write_bw_log = 0; 43int read_only = 0; 44 45static int def_timeout; 46static int write_lat_log; 47 48static int prev_group_jobs; 49 50unsigned long fio_debug = 0; 51unsigned int fio_debug_jobno = -1; 52unsigned int *fio_debug_jobp = NULL; 53 54/* 55 * Command line options. These will contain the above, plus a few 56 * extra that only pertain to fio itself and not jobs. 57 */ 58static struct option l_opts[FIO_NR_OPTIONS] = { 59 { 60 .name = "output", 61 .has_arg = required_argument, 62 .val = 'o', 63 }, 64 { 65 .name = "timeout", 66 .has_arg = required_argument, 67 .val = 't', 68 }, 69 { 70 .name = "latency-log", 71 .has_arg = required_argument, 72 .val = 'l', 73 }, 74 { 75 .name = "bandwidth-log", 76 .has_arg = required_argument, 77 .val = 'b', 78 }, 79 { 80 .name = "minimal", 81 .has_arg = optional_argument, 82 .val = 'm', 83 }, 84 { 85 .name = "version", 86 .has_arg = no_argument, 87 .val = 'v', 88 }, 89 { 90 .name = "help", 91 .has_arg = no_argument, 92 .val = 'h', 93 }, 94 { 95 .name = "cmdhelp", 96 .has_arg = optional_argument, 97 .val = 'c', 98 }, 99 { 100 .name = "showcmd", 101 .has_arg = no_argument, 102 .val = 's', 103 }, 104 { 105 .name = "readonly", 106 .has_arg = no_argument, 107 .val = 'r', 108 }, 109 { 110 .name = "eta", 111 .has_arg = required_argument, 112 .val = 'e', 113 }, 114 { 115 .name = "debug", 116 .has_arg = required_argument, 117 .val = 'd', 118 }, 119 { 120 .name = "section", 121 .has_arg = required_argument, 122 .val = 'x', 123 }, 124 { 125 .name = "alloc-size", 126 .has_arg = required_argument, 127 .val = 'a', 128 }, 129 { 130 .name = NULL, 131 }, 132}; 133 134FILE *get_f_out() 135{ 136 return f_out; 137} 138 139FILE *get_f_err() 140{ 141 return f_err; 142} 143 144/* 145 * Return a free job structure. 146 */ 147static struct thread_data *get_new_job(int global, struct thread_data *parent) 148{ 149 struct thread_data *td; 150 151 if (global) 152 return &def_thread; 153 if (thread_number >= max_jobs) 154 return NULL; 155 156 td = &threads[thread_number++]; 157 *td = *parent; 158 159 dup_files(td, parent); 160 options_mem_dupe(td); 161 162 td->thread_number = thread_number; 163 return td; 164} 165 166static void put_job(struct thread_data *td) 167{ 168 if (td == &def_thread) 169 return; 170 171 if (td->error) 172 log_info("fio: %s\n", td->verror); 173 174 memset(&threads[td->thread_number - 1], 0, sizeof(*td)); 175 thread_number--; 176} 177 178static int setup_rate(struct thread_data *td) 179{ 180 unsigned long nr_reads_per_msec; 181 unsigned long long rate; 182 unsigned int bs; 183 184 if (!td->o.rate && !td->o.rate_iops) 185 return 0; 186 187 if (td_rw(td)) 188 bs = td->o.rw_min_bs; 189 else if (td_read(td)) 190 bs = td->o.min_bs[DDIR_READ]; 191 else 192 bs = td->o.min_bs[DDIR_WRITE]; 193 194 if (td->o.rate) { 195 rate = td->o.rate; 196 nr_reads_per_msec = (rate * 1024 * 1000LL) / bs; 197 } else 198 nr_reads_per_msec = td->o.rate_iops * 1000UL; 199 200 if (!nr_reads_per_msec) { 201 log_err("rate lower than supported\n"); 202 return -1; 203 } 204 205 td->rate_usec_cycle = 1000000000ULL / nr_reads_per_msec; 206 td->rate_pending_usleep = 0; 207 return 0; 208} 209 210static int fixed_block_size(struct thread_options *o) 211{ 212 return o->min_bs[DDIR_READ] == o->max_bs[DDIR_READ] && 213 o->min_bs[DDIR_WRITE] == o->max_bs[DDIR_WRITE] && 214 o->min_bs[DDIR_READ] == o->min_bs[DDIR_WRITE]; 215} 216 217/* 218 * Lazy way of fixing up options that depend on each other. We could also 219 * define option callback handlers, but this is easier. 220 */ 221static int fixup_options(struct thread_data *td) 222{ 223 struct thread_options *o = &td->o; 224 225#ifndef FIO_HAVE_PSHARED_MUTEX 226 if (!td->o.use_thread) { 227 log_info("fio: this platform does not support process shared" 228 " mutexes, forcing use of threads. Use the 'thread'" 229 " option to get rid of this warning.\n"); 230 td->o.use_thread = 1; 231 } 232#endif 233 234#ifndef FIO_HAVE_CPU_AFFINITY 235 if (td->o.gtod_cpu) { 236 log_err("fio: platform must support CPU affinity for" 237 "gettimeofday() offloading\n"); 238 return 1; 239 } 240#endif 241 242 if (read_only && td_write(td)) { 243 log_err("fio: job <%s> has write bit set, but fio is in" 244 " read-only mode\n", td->o.name); 245 return 1; 246 } 247 248 if (o->write_iolog_file && o->read_iolog_file) { 249 log_err("fio: read iolog overrides write_iolog\n"); 250 free(o->write_iolog_file); 251 o->write_iolog_file = NULL; 252 } 253 254 /* 255 * only really works for sequential io for now, and with 1 file 256 */ 257 if (o->zone_size && td_random(td) && o->open_files == 1) 258 o->zone_size = 0; 259 260 /* 261 * Reads can do overwrites, we always need to pre-create the file 262 */ 263 if (td_read(td) || td_rw(td)) 264 o->overwrite = 1; 265 266 if (!o->min_bs[DDIR_READ]) 267 o->min_bs[DDIR_READ] = o->bs[DDIR_READ]; 268 if (!o->max_bs[DDIR_READ]) 269 o->max_bs[DDIR_READ] = o->bs[DDIR_READ]; 270 if (!o->min_bs[DDIR_WRITE]) 271 o->min_bs[DDIR_WRITE] = o->bs[DDIR_WRITE]; 272 if (!o->max_bs[DDIR_WRITE]) 273 o->max_bs[DDIR_WRITE] = o->bs[DDIR_WRITE]; 274 275 o->rw_min_bs = min(o->min_bs[DDIR_READ], o->min_bs[DDIR_WRITE]); 276 277 /* 278 * For random IO, allow blockalign offset other than min_bs. 279 */ 280 if (!o->ba[DDIR_READ] || !td_random(td)) 281 o->ba[DDIR_READ] = o->min_bs[DDIR_READ]; 282 if (!o->ba[DDIR_WRITE] || !td_random(td)) 283 o->ba[DDIR_WRITE] = o->min_bs[DDIR_WRITE]; 284 285 if ((o->ba[DDIR_READ] != o->min_bs[DDIR_READ] || 286 o->ba[DDIR_WRITE] != o->min_bs[DDIR_WRITE]) && 287 !td->o.norandommap) { 288 log_err("fio: Any use of blockalign= turns off randommap\n"); 289 td->o.norandommap = 1; 290 } 291 292 if (!o->file_size_high) 293 o->file_size_high = o->file_size_low; 294 295 if (o->norandommap && o->verify != VERIFY_NONE 296 && !fixed_block_size(o)) { 297 log_err("fio: norandommap given for variable block sizes, " 298 "verify disabled\n"); 299 o->verify = VERIFY_NONE; 300 } 301 if (o->bs_unaligned && (o->odirect || td->io_ops->flags & FIO_RAWIO)) 302 log_err("fio: bs_unaligned may not work with raw io\n"); 303 304 /* 305 * thinktime_spin must be less than thinktime 306 */ 307 if (o->thinktime_spin > o->thinktime) 308 o->thinktime_spin = o->thinktime; 309 310 /* 311 * The low water mark cannot be bigger than the iodepth 312 */ 313 if (o->iodepth_low > o->iodepth || !o->iodepth_low) { 314 /* 315 * syslet work around - if the workload is sequential, 316 * we want to let the queue drain all the way down to 317 * avoid seeking between async threads 318 */ 319 if (!strcmp(td->io_ops->name, "syslet-rw") && !td_random(td)) 320 o->iodepth_low = 1; 321 else 322 o->iodepth_low = o->iodepth; 323 } 324 325 /* 326 * If batch number isn't set, default to the same as iodepth 327 */ 328 if (o->iodepth_batch > o->iodepth || !o->iodepth_batch) 329 o->iodepth_batch = o->iodepth; 330 331 if (o->nr_files > td->files_index) 332 o->nr_files = td->files_index; 333 334 if (o->open_files > o->nr_files || !o->open_files) 335 o->open_files = o->nr_files; 336 337 if ((o->rate && o->rate_iops) || (o->ratemin && o->rate_iops_min)) { 338 log_err("fio: rate and rate_iops are mutually exclusive\n"); 339 return 1; 340 } 341 if ((o->rate < o->ratemin) || (o->rate_iops < o->rate_iops_min)) { 342 log_err("fio: minimum rate exceeds rate\n"); 343 return 1; 344 } 345 346 if (!o->timeout && o->time_based) { 347 log_err("fio: time_based requires a runtime/timeout setting\n"); 348 o->time_based = 0; 349 } 350 351 if (o->fill_device && !o->size) 352 o->size = -1ULL; 353 354 if (td_rw(td) && td->o.verify != VERIFY_NONE) 355 log_info("fio: mixed read/write workload with verify. May not " 356 "work as expected, unless you pre-populated the file\n"); 357 358 if (td->o.verify != VERIFY_NONE) 359 td->o.refill_buffers = 1; 360 361 if (td->o.pre_read) 362 td->o.invalidate_cache = 0; 363 364 return 0; 365} 366 367/* 368 * This function leaks the buffer 369 */ 370static char *to_kmg(unsigned int val) 371{ 372 char *buf = malloc(32); 373 char post[] = { 0, 'K', 'M', 'G', 'P', 'E', 0 }; 374 char *p = post; 375 376 do { 377 if (val & 1023) 378 break; 379 380 val >>= 10; 381 p++; 382 } while (*p); 383 384 snprintf(buf, 31, "%u%c", val, *p); 385 return buf; 386} 387 388/* External engines are specified by "external:name.o") */ 389static const char *get_engine_name(const char *str) 390{ 391 char *p = strstr(str, ":"); 392 393 if (!p) 394 return str; 395 396 p++; 397 strip_blank_front(&p); 398 strip_blank_end(p); 399 return p; 400} 401 402static int exists_and_not_file(const char *filename) 403{ 404 struct stat sb; 405 406 if (lstat(filename, &sb) == -1) 407 return 0; 408 409 if (S_ISREG(sb.st_mode)) 410 return 0; 411 412 return 1; 413} 414 415void td_fill_rand_seeds(struct thread_data *td) 416{ 417 os_random_seed(td->rand_seeds[0], &td->bsrange_state); 418 os_random_seed(td->rand_seeds[1], &td->verify_state); 419 os_random_seed(td->rand_seeds[2], &td->rwmix_state); 420 421 if (td->o.file_service_type == FIO_FSERVICE_RANDOM) 422 os_random_seed(td->rand_seeds[3], &td->next_file_state); 423 424 os_random_seed(td->rand_seeds[5], &td->file_size_state); 425 426 if (!td_random(td)) 427 return; 428 429 if (td->o.rand_repeatable) 430 td->rand_seeds[4] = FIO_RANDSEED * td->thread_number; 431 432 os_random_seed(td->rand_seeds[4], &td->random_state); 433} 434 435/* 436 * Initialize the various random states we need (random io, block size ranges, 437 * read/write mix, etc). 438 */ 439static int init_random_state(struct thread_data *td) 440{ 441 int fd; 442 443 fd = open("/dev/urandom", O_RDONLY); 444 if (fd == -1) { 445 td_verror(td, errno, "open"); 446 return 1; 447 } 448 449 if (read(fd, td->rand_seeds, sizeof(td->rand_seeds)) < 450 (int) sizeof(td->rand_seeds)) { 451 td_verror(td, EIO, "read"); 452 close(fd); 453 return 1; 454 } 455 456 close(fd); 457 td_fill_rand_seeds(td); 458 return 0; 459} 460 461/* 462 * Adds a job to the list of things todo. Sanitizes the various options 463 * to make sure we don't have conflicts, and initializes various 464 * members of td. 465 */ 466static int add_job(struct thread_data *td, const char *jobname, int job_add_num) 467{ 468 const char *ddir_str[] = { NULL, "read", "write", "rw", NULL, 469 "randread", "randwrite", "randrw" }; 470 unsigned int i; 471 const char *engine; 472 char fname[PATH_MAX]; 473 int numjobs, file_alloced; 474 475 /* 476 * the def_thread is just for options, it's not a real job 477 */ 478 if (td == &def_thread) 479 return 0; 480 481 /* 482 * if we are just dumping the output command line, don't add the job 483 */ 484 if (dump_cmdline) { 485 put_job(td); 486 return 0; 487 } 488 489 engine = get_engine_name(td->o.ioengine); 490 td->io_ops = load_ioengine(td, engine); 491 if (!td->io_ops) { 492 log_err("fio: failed to load engine %s\n", engine); 493 goto err; 494 } 495 496 if (td->o.use_thread) 497 nr_thread++; 498 else 499 nr_process++; 500 501 if (td->o.odirect) 502 td->io_ops->flags |= FIO_RAWIO; 503 504 file_alloced = 0; 505 if (!td->o.filename && !td->files_index && !td->o.read_iolog_file) { 506 file_alloced = 1; 507 508 if (td->o.nr_files == 1 && exists_and_not_file(jobname)) 509 add_file(td, jobname); 510 else { 511 for (i = 0; i < td->o.nr_files; i++) { 512 sprintf(fname, "%s.%d.%d", jobname, 513 td->thread_number, i); 514 add_file(td, fname); 515 } 516 } 517 } 518 519 if (fixup_options(td)) 520 goto err; 521 522 if (td->io_ops->flags & FIO_DISKLESSIO) { 523 struct fio_file *f; 524 525 for_each_file(td, f, i) 526 f->real_file_size = -1ULL; 527 } 528 529 td->mutex = fio_mutex_init(0); 530 531 td->ts.clat_stat[0].min_val = td->ts.clat_stat[1].min_val = ULONG_MAX; 532 td->ts.slat_stat[0].min_val = td->ts.slat_stat[1].min_val = ULONG_MAX; 533 td->ts.bw_stat[0].min_val = td->ts.bw_stat[1].min_val = ULONG_MAX; 534 td->ddir_nr = td->o.ddir_nr; 535 536 if ((td->o.stonewall || td->o.numjobs > 1 || td->o.new_group) 537 && prev_group_jobs) { 538 prev_group_jobs = 0; 539 groupid++; 540 } 541 542 td->groupid = groupid; 543 prev_group_jobs++; 544 545 if (init_random_state(td)) 546 goto err; 547 548 if (setup_rate(td)) 549 goto err; 550 551 if (td->o.write_lat_log) { 552 setup_log(&td->ts.slat_log); 553 setup_log(&td->ts.clat_log); 554 } 555 if (td->o.write_bw_log) 556 setup_log(&td->ts.bw_log); 557 558 if (!td->o.name) 559 td->o.name = strdup(jobname); 560 561 if (!terse_output) { 562 if (!job_add_num) { 563 if (!strcmp(td->io_ops->name, "cpuio")) { 564 log_info("%s: ioengine=cpu, cpuload=%u," 565 " cpucycle=%u\n", td->o.name, 566 td->o.cpuload, 567 td->o.cpucycle); 568 } else { 569 char *c1, *c2, *c3, *c4; 570 571 c1 = to_kmg(td->o.min_bs[DDIR_READ]); 572 c2 = to_kmg(td->o.max_bs[DDIR_READ]); 573 c3 = to_kmg(td->o.min_bs[DDIR_WRITE]); 574 c4 = to_kmg(td->o.max_bs[DDIR_WRITE]); 575 576 log_info("%s: (g=%d): rw=%s, bs=%s-%s/%s-%s," 577 " ioengine=%s, iodepth=%u\n", 578 td->o.name, td->groupid, 579 ddir_str[td->o.td_ddir], 580 c1, c2, c3, c4, 581 td->io_ops->name, 582 td->o.iodepth); 583 584 free(c1); 585 free(c2); 586 free(c3); 587 free(c4); 588 } 589 } else if (job_add_num == 1) 590 log_info("...\n"); 591 } 592 593 /* 594 * recurse add identical jobs, clear numjobs and stonewall options 595 * as they don't apply to sub-jobs 596 */ 597 numjobs = td->o.numjobs; 598 while (--numjobs) { 599 struct thread_data *td_new = get_new_job(0, td); 600 601 if (!td_new) 602 goto err; 603 604 td_new->o.numjobs = 1; 605 td_new->o.stonewall = 0; 606 td_new->o.new_group = 0; 607 608 if (file_alloced) { 609 td_new->o.filename = NULL; 610 td_new->files_index = 0; 611 td_new->files_size = 0; 612 td_new->files = NULL; 613 } 614 615 job_add_num = numjobs - 1; 616 617 if (add_job(td_new, jobname, job_add_num)) 618 goto err; 619 } 620 621 return 0; 622err: 623 put_job(td); 624 return -1; 625} 626 627static int skip_this_section(const char *name) 628{ 629 if (!job_section) 630 return 0; 631 if (!strncmp(name, "global", 6)) 632 return 0; 633 634 return strcmp(job_section, name); 635} 636 637static int is_empty_or_comment(char *line) 638{ 639 unsigned int i; 640 641 for (i = 0; i < strlen(line); i++) { 642 if (line[i] == ';') 643 return 1; 644 if (line[i] == '#') 645 return 1; 646 if (!isspace(line[i]) && !iscntrl(line[i])) 647 return 0; 648 } 649 650 return 1; 651} 652 653/* 654 * This is our [ini] type file parser. 655 */ 656static int parse_jobs_ini(char *file, int stonewall_flag) 657{ 658 unsigned int global; 659 struct thread_data *td; 660 char *string, *name; 661 FILE *f; 662 char *p; 663 int ret = 0, stonewall; 664 int first_sect = 1; 665 int skip_fgets = 0; 666 int inside_skip = 0; 667 char **opts; 668 int i, alloc_opts, num_opts; 669 670 if (!strcmp(file, "-")) 671 f = stdin; 672 else 673 f = fopen(file, "r"); 674 675 if (!f) { 676 perror("fopen job file"); 677 return 1; 678 } 679 680 string = malloc(4096); 681 682 /* 683 * it's really 256 + small bit, 280 should suffice 684 */ 685 name = malloc(280); 686 memset(name, 0, 280); 687 688 alloc_opts = 8; 689 opts = malloc(sizeof(char *) * alloc_opts); 690 num_opts = 0; 691 692 stonewall = stonewall_flag; 693 do { 694 /* 695 * if skip_fgets is set, we already have loaded a line we 696 * haven't handled. 697 */ 698 if (!skip_fgets) { 699 p = fgets(string, 4095, f); 700 if (!p) 701 break; 702 } 703 704 skip_fgets = 0; 705 strip_blank_front(&p); 706 strip_blank_end(p); 707 708 if (is_empty_or_comment(p)) 709 continue; 710 if (sscanf(p, "[%255s]", name) != 1) { 711 if (inside_skip) 712 continue; 713 log_err("fio: option <%s> outside of [] job section\n", 714 p); 715 break; 716 } 717 718 if (skip_this_section(name)) { 719 inside_skip = 1; 720 continue; 721 } else 722 inside_skip = 0; 723 724 global = !strncmp(name, "global", 6); 725 726 name[strlen(name) - 1] = '\0'; 727 728 if (dump_cmdline) { 729 if (first_sect) 730 log_info("fio "); 731 if (!global) 732 log_info("--name=%s ", name); 733 first_sect = 0; 734 } 735 736 td = get_new_job(global, &def_thread); 737 if (!td) { 738 ret = 1; 739 break; 740 } 741 742 /* 743 * Seperate multiple job files by a stonewall 744 */ 745 if (!global && stonewall) { 746 td->o.stonewall = stonewall; 747 stonewall = 0; 748 } 749 750 num_opts = 0; 751 memset(opts, 0, alloc_opts * sizeof(char *)); 752 753 while ((p = fgets(string, 4096, f)) != NULL) { 754 if (is_empty_or_comment(p)) 755 continue; 756 757 strip_blank_front(&p); 758 759 /* 760 * new section, break out and make sure we don't 761 * fgets() a new line at the top. 762 */ 763 if (p[0] == '[') { 764 skip_fgets = 1; 765 break; 766 } 767 768 strip_blank_end(p); 769 770 if (num_opts == alloc_opts) { 771 alloc_opts <<= 1; 772 opts = realloc(opts, 773 alloc_opts * sizeof(char *)); 774 } 775 776 opts[num_opts] = strdup(p); 777 num_opts++; 778 } 779 780 ret = fio_options_parse(td, opts, num_opts); 781 if (!ret) { 782 if (dump_cmdline) 783 for (i = 0; i < num_opts; i++) 784 log_info("--%s ", opts[i]); 785 786 ret = add_job(td, name, 0); 787 } else { 788 log_err("fio: job %s dropped\n", name); 789 put_job(td); 790 } 791 792 for (i = 0; i < num_opts; i++) 793 free(opts[i]); 794 num_opts = 0; 795 } while (!ret); 796 797 if (dump_cmdline) 798 log_info("\n"); 799 800 for (i = 0; i < num_opts; i++) 801 free(opts[i]); 802 803 free(string); 804 free(name); 805 free(opts); 806 if (f != stdin) 807 fclose(f); 808 return ret; 809} 810 811static int fill_def_thread(void) 812{ 813 memset(&def_thread, 0, sizeof(def_thread)); 814 815 fio_getaffinity(getpid(), &def_thread.o.cpumask); 816 817 /* 818 * fill default options 819 */ 820 fio_fill_default_options(&def_thread); 821 822 def_thread.o.timeout = def_timeout; 823 def_thread.o.write_bw_log = write_bw_log; 824 def_thread.o.write_lat_log = write_lat_log; 825 826 return 0; 827} 828 829static void free_shm(void) 830{ 831 struct shmid_ds sbuf; 832 833 if (threads) { 834 void *tp = threads; 835 836 threads = NULL; 837 file_hash_exit(); 838 fio_debug_jobp = NULL; 839 shmdt(tp); 840 shmctl(shm_id, IPC_RMID, &sbuf); 841 } 842 843 scleanup(); 844} 845 846/* 847 * The thread area is shared between the main process and the job 848 * threads/processes. So setup a shared memory segment that will hold 849 * all the job info. We use the end of the region for keeping track of 850 * open files across jobs, for file sharing. 851 */ 852static int setup_thread_area(void) 853{ 854 void *hash; 855 856 /* 857 * 1024 is too much on some machines, scale max_jobs if 858 * we get a failure that looks like too large a shm segment 859 */ 860 do { 861 size_t size = max_jobs * sizeof(struct thread_data); 862 863 size += file_hash_size; 864 size += sizeof(unsigned int); 865 866 shm_id = shmget(0, size, IPC_CREAT | 0600); 867 if (shm_id != -1) 868 break; 869 if (errno != EINVAL) { 870 perror("shmget"); 871 break; 872 } 873 874 max_jobs >>= 1; 875 } while (max_jobs); 876 877 if (shm_id == -1) 878 return 1; 879 880 threads = shmat(shm_id, NULL, 0); 881 if (threads == (void *) -1) { 882 perror("shmat"); 883 return 1; 884 } 885 886 memset(threads, 0, max_jobs * sizeof(struct thread_data)); 887 hash = (void *) threads + max_jobs * sizeof(struct thread_data); 888 fio_debug_jobp = (void *) hash + file_hash_size; 889 *fio_debug_jobp = -1; 890 file_hash_init(hash); 891 atexit(free_shm); 892 return 0; 893} 894 895static void usage(const char *name) 896{ 897 printf("%s\n", fio_version_string); 898 printf("%s [options] [job options] <job file(s)>\n", name); 899 printf("\t--debug=options\tEnable debug logging\n"); 900 printf("\t--output\tWrite output to file\n"); 901 printf("\t--timeout\tRuntime in seconds\n"); 902 printf("\t--latency-log\tGenerate per-job latency logs\n"); 903 printf("\t--bandwidth-log\tGenerate per-job bandwidth logs\n"); 904 printf("\t--minimal\tMinimal (terse) output\n"); 905 printf("\t--version\tPrint version info and exit\n"); 906 printf("\t--help\t\tPrint this page\n"); 907 printf("\t--cmdhelp=cmd\tPrint command help, \"all\" for all of" 908 " them\n"); 909 printf("\t--showcmd\tTurn a job file into command line options\n"); 910 printf("\t--eta=when\tWhen ETA estimate should be printed\n"); 911 printf("\t \tMay be \"always\", \"never\" or \"auto\"\n"); 912 printf("\t--readonly\tTurn on safety read-only checks, preventing" 913 " writes\n"); 914 printf("\t--section=name\tOnly run specified section in job file\n"); 915 printf("\t--alloc-size=kb\tSet smalloc pool to this size in kb" 916 " (def 1024)\n"); 917} 918 919#ifdef FIO_INC_DEBUG 920struct debug_level debug_levels[] = { 921 { .name = "process", .shift = FD_PROCESS, }, 922 { .name = "file", .shift = FD_FILE, }, 923 { .name = "io", .shift = FD_IO, }, 924 { .name = "mem", .shift = FD_MEM, }, 925 { .name = "blktrace", .shift = FD_BLKTRACE }, 926 { .name = "verify", .shift = FD_VERIFY }, 927 { .name = "random", .shift = FD_RANDOM }, 928 { .name = "parse", .shift = FD_PARSE }, 929 { .name = "diskutil", .shift = FD_DISKUTIL }, 930 { .name = "job", .shift = FD_JOB }, 931 { .name = "mutex", .shift = FD_MUTEX }, 932 { .name = NULL, }, 933}; 934 935static int set_debug(const char *string) 936{ 937 struct debug_level *dl; 938 char *p = (char *) string; 939 char *opt; 940 int i; 941 942 if (!strcmp(string, "?") || !strcmp(string, "help")) { 943 int i; 944 945 log_info("fio: dumping debug options:"); 946 for (i = 0; debug_levels[i].name; i++) { 947 dl = &debug_levels[i]; 948 log_info("%s,", dl->name); 949 } 950 log_info("all\n"); 951 return 1; 952 } 953 954 while ((opt = strsep(&p, ",")) != NULL) { 955 int found = 0; 956 957 if (!strncmp(opt, "all", 3)) { 958 log_info("fio: set all debug options\n"); 959 fio_debug = ~0UL; 960 continue; 961 } 962 963 for (i = 0; debug_levels[i].name; i++) { 964 dl = &debug_levels[i]; 965 found = !strncmp(opt, dl->name, strlen(dl->name)); 966 if (!found) 967 continue; 968 969 if (dl->shift == FD_JOB) { 970 opt = strchr(opt, ':'); 971 if (!opt) { 972 log_err("fio: missing job number\n"); 973 break; 974 } 975 opt++; 976 fio_debug_jobno = atoi(opt); 977 log_info("fio: set debug jobno %d\n", 978 fio_debug_jobno); 979 } else { 980 log_info("fio: set debug option %s\n", opt); 981 fio_debug |= (1UL << dl->shift); 982 } 983 break; 984 } 985 986 if (!found) 987 log_err("fio: debug mask %s not found\n", opt); 988 } 989 return 0; 990} 991#else 992static int set_debug(const char *string) 993{ 994 log_err("fio: debug tracing not included in build\n"); 995 return 1; 996} 997#endif 998 999static int parse_cmd_line(int argc, char *argv[]) 1000{ 1001 struct thread_data *td = NULL; 1002 int c, ini_idx = 0, lidx, ret = 0, do_exit = 0, exit_val = 0; 1003 1004 while ((c = getopt_long_only(argc, argv, "", l_opts, &lidx)) != -1) { 1005 switch (c) { 1006 case 'a': 1007 smalloc_pool_size = atoi(optarg); 1008 break; 1009 case 't': 1010 def_timeout = atoi(optarg); 1011 break; 1012 case 'l': 1013 write_lat_log = 1; 1014 break; 1015 case 'w': 1016 write_bw_log = 1; 1017 break; 1018 case 'o': 1019 f_out = fopen(optarg, "w+"); 1020 if (!f_out) { 1021 perror("fopen output"); 1022 exit(1); 1023 } 1024 f_err = f_out; 1025 break; 1026 case 'm': 1027 terse_output = 1; 1028 break; 1029 case 'h': 1030 usage(argv[0]); 1031 exit(0); 1032 case 'c': 1033 exit(fio_show_option_help(optarg)); 1034 case 's': 1035 dump_cmdline = 1; 1036 break; 1037 case 'r': 1038 read_only = 1; 1039 break; 1040 case 'v': 1041 printf("%s\n", fio_version_string); 1042 exit(0); 1043 case 'e': 1044 if (!strcmp("always", optarg)) 1045 eta_print = FIO_ETA_ALWAYS; 1046 else if (!strcmp("never", optarg)) 1047 eta_print = FIO_ETA_NEVER; 1048 break; 1049 case 'd': 1050 if (set_debug(optarg)) 1051 do_exit++; 1052 break; 1053 case 'x': 1054 if (!strcmp(optarg, "global")) { 1055 log_err("fio: can't use global as only " 1056 "section\n"); 1057 do_exit++; 1058 exit_val = 1; 1059 break; 1060 } 1061 if (job_section) 1062 free(job_section); 1063 job_section = strdup(optarg); 1064 break; 1065 case FIO_GETOPT_JOB: { 1066 const char *opt = l_opts[lidx].name; 1067 char *val = optarg; 1068 1069 if (!strncmp(opt, "name", 4) && td) { 1070 ret = add_job(td, td->o.name ?: "fio", 0); 1071 if (ret) { 1072 put_job(td); 1073 return 0; 1074 } 1075 td = NULL; 1076 } 1077 if (!td) { 1078 int is_section = !strncmp(opt, "name", 4); 1079 int global = 0; 1080 1081 if (!is_section || !strncmp(val, "global", 6)) 1082 global = 1; 1083 1084 if (is_section && skip_this_section(val)) 1085 continue; 1086 1087 td = get_new_job(global, &def_thread); 1088 if (!td) 1089 return 0; 1090 } 1091 1092 ret = fio_cmd_option_parse(td, opt, val); 1093 break; 1094 } 1095 default: 1096 do_exit++; 1097 exit_val = 1; 1098 break; 1099 } 1100 } 1101 1102 if (do_exit) 1103 exit(exit_val); 1104 1105 if (td) { 1106 if (!ret) 1107 ret = add_job(td, td->o.name ?: "fio", 0); 1108 if (ret) 1109 put_job(td); 1110 } 1111 1112 while (optind < argc) { 1113 ini_idx++; 1114 ini_file = realloc(ini_file, ini_idx * sizeof(char *)); 1115 ini_file[ini_idx - 1] = strdup(argv[optind]); 1116 optind++; 1117 } 1118 1119 return ini_idx; 1120} 1121 1122int parse_options(int argc, char *argv[]) 1123{ 1124 int job_files, i; 1125 1126 f_out = stdout; 1127 f_err = stderr; 1128 1129 fio_options_dup_and_init(l_opts); 1130 1131 if (setup_thread_area()) 1132 return 1; 1133 if (fill_def_thread()) 1134 return 1; 1135 1136 job_files = parse_cmd_line(argc, argv); 1137 1138 for (i = 0; i < job_files; i++) { 1139 if (fill_def_thread()) 1140 return 1; 1141 if (parse_jobs_ini(ini_file[i], i)) 1142 return 1; 1143 free(ini_file[i]); 1144 } 1145 1146 free(ini_file); 1147 options_mem_free(&def_thread); 1148 1149 if (!thread_number) { 1150 if (dump_cmdline) 1151 return 0; 1152 1153 log_err("No jobs defined(s)\n"); 1154 usage(argv[0]); 1155 return 1; 1156 } 1157 1158 if (def_thread.o.gtod_offload) { 1159 fio_gtod_init(); 1160 fio_gtod_offload = 1; 1161 fio_gtod_cpu = def_thread.o.gtod_cpu; 1162 } 1163 1164 return 0; 1165} 1166