From b46928282e0a890f49250e79b81af773a2b7108f Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 27 Oct 2006 13:43:22 +0200 Subject: [PATCH] [PATCH] Add full command line parameter support You may now give full job options on the command line. Makes it easier to script fio or for one-off runs, as you don't have to write a job file and run that. Signed-off-by: Jens Axboe --- HOWTO | 12 ++- README | 22 ++--- fio.h | 4 +- init.c | 227 +++++++++++++++++++++++++++++++++++++--------------- ioengines.c | 2 +- parse.c | 32 +++++--- parse.h | 3 +- 7 files changed, 211 insertions(+), 91 deletions(-) diff --git a/HOWTO b/HOWTO index 227bc513..3f4a6675 100644 --- a/HOWTO +++ b/HOWTO @@ -82,6 +82,12 @@ more than one job file on the command line, fio will serialize the running of those files. Internally that is the same as using the 'stonewall' parameter described the the parameter section. +If the job file contains only one job, you may as well just give the +parameters on the command line. The command line parameters are identical +to the job parameters, with a few extra that control global parameters +(see README). For example, for the job file parameter iodepth=2, the +mirror command line option would be --iodepth 2 or --iodepth=2. + fio does not need to run as root, except if the files or devices specified in the job section requires that. Some other options may also be restricted, such as memory locking, io scheduler switching, and descreasing the nice value. @@ -136,7 +142,11 @@ Here we have no global section, as we only have one job defined anyway. We want to use async io here, with a depth of 4 for each file. We also increased the buffer size used to 32KiB and define numjobs to 4 to fork 4 identical jobs. The result is 4 processes each randomly writing -to their own 64MiB file. +to their own 64MiB file. Instead of using the above job file, you could +have given the parameters on the command line. For this case, you would +specify: + +$ fio --name=random-writers --ioengine=libaio --iodepth=4 --rw=randwrite --bs=32k --direct=0 --size=64m --numjobs=4 fio ships with a few example job files, you can also look there for inspiration. diff --git a/README b/README index 7e79d94a..04ff1eef 100644 --- a/README +++ b/README @@ -43,17 +43,17 @@ Command line ------------ $ fio - -t Runtime in seconds - -l Generate per-job latency logs - -w Generate per-job bandwidth logs - -o Log output to file - -m Minimal (terse) output - -h Print help info - -v Print version information and exit - -Any parameters following the options will be assumed to be job files. -You can add as many as you want, each job file will be regarded as a -separate group and fio will stonewall it's execution. + --output Write output to file + --timeout Runtime in seconds + --latency-log Generate per-job latency logs + --bandwidth-log Generate per-job bandwidth logs + --minimal Minimal (terse) output + --version Print version info and exit + +Any parameters following the options will be assumed to be job files, +unless they match a job file parameter. You can add as many as you want, +each job file will be regarded as a separate group and fio will stonewall +its execution. Job file diff --git a/fio.h b/fio.h index d8b01527..f8b8e389 100644 --- a/fio.h +++ b/fio.h @@ -164,7 +164,7 @@ struct fio_file { * This describes a single thread/process executing a fio job. */ struct thread_data { - char name[32]; + char *name; char *directory; char *filename; char verror[80]; @@ -575,7 +575,7 @@ struct ioengine_ops { #define FIO_IOOPS_VERSION 3 -extern struct ioengine_ops *load_ioengine(struct thread_data *, char *); +extern struct ioengine_ops *load_ioengine(struct thread_data *, const char *); extern void close_ioengine(struct thread_data *); /* diff --git a/init.c b/init.c index b0c35f10..c799bf5f 100644 --- a/init.c +++ b/init.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -52,10 +54,10 @@ #define td_var_offset(var) ((size_t) &((struct thread_data *)0)->var) -static int str_rw_cb(void *, char *); -static int str_ioengine_cb(void *, char *); -static int str_mem_cb(void *, char *); -static int str_verify_cb(void *, char *); +static int str_rw_cb(void *, const char *); +static int str_ioengine_cb(void *, const char *); +static int str_mem_cb(void *, const char *); +static int str_verify_cb(void *, const char *); static int str_lockmem_cb(void *, unsigned long *); static int str_prio_cb(void *, unsigned int *); static int str_prioclass_cb(void *, unsigned int *); @@ -345,9 +347,53 @@ static struct fio_option options[] = { }, }; +#define FIO_JOB_OPTS (sizeof(options) / sizeof(struct fio_option)) +#define FIO_CMD_OPTS (16) +#define FIO_GETOPT_JOB (0x89988998) + +/* + * Command line options. These will contain the above, plus a few + * extra that only pertain to fio itself and not jobs. + */ +static struct option long_options[FIO_JOB_OPTS + FIO_CMD_OPTS] = { + { + .name = "output", + .has_arg = required_argument, + .val = 'o', + }, + { + .name = "timeout", + .has_arg = required_argument, + .val = 't', + }, + { + .name = "latency-log", + .has_arg = required_argument, + .val = 'l', + }, + { + .name = "bandwidth-log", + .has_arg = required_argument, + .val = 'b', + }, + { + .name = "minimal", + .has_arg = optional_argument, + .val = 'm', + }, + { + .name = "version", + .has_arg = no_argument, + .val = 'v', + }, + { + .name = NULL, + }, +}; + static int def_timeout = DEF_TIMEOUT; -static char fio_version_string[] = "fio 1.5"; +static char fio_version_string[] = "fio 1.7"; static char **ini_file; static int max_jobs = MAX_JOBS; @@ -379,7 +425,6 @@ static struct thread_data *get_new_job(int global, struct thread_data *parent) td = &threads[thread_number++]; *td = *parent; - td->name[0] = '\0'; td->thread_number = thread_number; return td; @@ -555,8 +600,8 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num) if (td->write_bw_log) setup_log(&td->bw_log); - if (td->name[0] == '\0') - snprintf(td->name, sizeof(td->name)-1, "client%d", td->thread_number); + if (!td->name) + td->name = strdup(jobname); ddir = td->ddir + (!td->sequential << 1) + (td->iomix << 2); @@ -671,7 +716,7 @@ static int is_empty_or_comment(char *line) return 1; } -static int str_rw_cb(void *data, char *mem) +static int str_rw_cb(void *data, const char *mem) { struct thread_data *td = data; @@ -707,7 +752,7 @@ static int str_rw_cb(void *data, char *mem) return 1; } -static int str_verify_cb(void *data, char *mem) +static int str_verify_cb(void *data, const char *mem) { struct thread_data *td = data; @@ -726,7 +771,7 @@ static int str_verify_cb(void *data, char *mem) return 1; } -static int str_mem_cb(void *data, char *mem) +static int str_mem_cb(void *data, const char *mem) { struct thread_data *td = data; @@ -745,7 +790,7 @@ static int str_mem_cb(void *data, char *mem) return 1; } -static int str_ioengine_cb(void *data, char *str) +static int str_ioengine_cb(void *data, const char *str) { struct thread_data *td = data; @@ -929,64 +974,83 @@ static int fill_def_thread(void) static void usage(void) { printf("%s\n", fio_version_string); - printf("\t-o Write output to file\n"); - printf("\t-t Runtime in seconds\n"); - printf("\t-l Generate per-job latency logs\n"); - printf("\t-w Generate per-job bandwidth logs\n"); - printf("\t-m Minimal (terse) output\n"); - printf("\t-v Print version info and exit\n"); + printf("\t--output\tWrite output to file\n"); + printf("\t--timeout\tRuntime in seconds\n"); + printf("\t--latency-log\tGenerate per-job latency logs\n"); + printf("\t--bandwidth-log\tGenerate per-job bandwidth logs\n"); + printf("\t--minimal\tMinimal (terse) output\n"); + printf("\t--version\tPrint version info and exit\n"); } static int parse_cmd_line(int argc, char *argv[]) { - int c, idx = 1, ini_idx = 0; + struct thread_data *td = NULL; + int c, ini_idx = 0, lidx; - while ((c = getopt(argc, argv, "t:o:lwvhm")) != EOF) { + while ((c = getopt_long(argc, argv, "", long_options, &lidx)) != -1) { switch (c) { - case 't': - def_timeout = atoi(optarg); - idx = optind; - break; - case 'l': - write_lat_log = 1; - idx = optind; - break; - case 'w': - write_bw_log = 1; - idx = optind; - break; - case 'o': - f_out = fopen(optarg, "w+"); - if (!f_out) { - perror("fopen output"); - exit(1); - } - f_err = f_out; - idx = optind; - break; - case 'm': - terse_output = 1; - idx = optind; - break; - case 'h': - usage(); - exit(0); - case 'v': - printf("%s\n", fio_version_string); - exit(0); + case 't': + def_timeout = atoi(optarg); + break; + case 'l': + write_lat_log = 1; + break; + case 'w': + write_bw_log = 1; + break; + case 'o': + f_out = fopen(optarg, "w+"); + if (!f_out) { + perror("fopen output"); + exit(1); + } + f_err = f_out; + break; + case 'm': + terse_output = 1; + break; + case 'h': + usage(); + exit(0); + case 'v': + printf("%s\n", fio_version_string); + exit(0); + case FIO_GETOPT_JOB: { + const char *opt = long_options[lidx].name; + char *val = optarg; + + if (!td) { + td = get_new_job(0, &def_thread); + if (!td) + return 0; + } + if (parse_cmd_option(opt, val, options, td)) + printf("foo\n"); + break; + } + default: + printf("optarg <<%s>>\n", argv[optind]); + break; } } - while (idx < argc) { - ini_idx++; - ini_file = realloc(ini_file, ini_idx * sizeof(char *)); - ini_file[ini_idx - 1] = strdup(argv[idx]); - idx++; + if (td) { + const char *name = td->name; + int ret; + + if (!name) + name = "fio"; + + ret = add_job(td, name, 0); + if (ret) + put_job(td); } - if (!f_out) { - f_out = stdout; - f_err = stderr; + while (optind < argc) { + ini_idx++; + ini_file = realloc(ini_file, ini_idx * sizeof(char *)); + ini_file[ini_idx - 1] = strdup(argv[optind]); + optind++; } return ini_idx; @@ -1041,21 +1105,49 @@ static int setup_thread_area(void) return 0; } +/* + * Copy the fio options into the long options map, so we mirror + * job and cmd line options. + */ +static void dupe_job_options(void) +{ + struct fio_option *o; + unsigned int i; + + i = 0; + while (long_options[i].name) + i++; + + o = &options[0]; + while (o->name) { + long_options[i].name = o->name; + long_options[i].val = FIO_GETOPT_JOB; + if (o->type == FIO_OPT_STR_SET) + long_options[i].has_arg = no_argument; + else + long_options[i].has_arg = required_argument; + + i++; + o++; + assert(i < FIO_JOB_OPTS + FIO_CMD_OPTS); + } +} + int parse_options(int argc, char *argv[]) { int job_files, i; + f_out = stdout; + f_err = stderr; + + dupe_job_options(); + if (setup_thread_area()) return 1; if (fill_def_thread()) return 1; job_files = parse_cmd_line(argc, argv); - if (!job_files) { - log_err("Need job file(s)\n"); - usage(); - return 1; - } for (i = 0; i < job_files; i++) { if (fill_def_thread()) @@ -1066,5 +1158,12 @@ int parse_options(int argc, char *argv[]) } free(ini_file); + + if (!thread_number) { + log_err("No jobs defined(s)\n"); + usage(); + return 1; + } + return 0; } diff --git a/ioengines.c b/ioengines.c index 115e6f2c..6a5073cf 100644 --- a/ioengines.c +++ b/ioengines.c @@ -42,7 +42,7 @@ static int check_engine_ops(struct ioengine_ops *ops) return 0; } -struct ioengine_ops *load_ioengine(struct thread_data *td, char *name) +struct ioengine_ops *load_ioengine(struct thread_data *td, const char *name) { char engine[16], engine_lib[256]; struct ioengine_ops *ops, *ret; diff --git a/parse.c b/parse.c index 197a46e1..48703b69 100644 --- a/parse.c +++ b/parse.c @@ -48,9 +48,8 @@ static unsigned long get_mult_bytes(char c) /* * convert string into decimal value, noting any size suffix */ -static int str_to_decimal(char *p, unsigned long long *val, int kilo) +static int str_to_decimal(const char *str, unsigned long long *val, int kilo) { - char *str = p; int len; len = strlen(str); @@ -66,12 +65,12 @@ static int str_to_decimal(char *p, unsigned long long *val, int kilo) return 0; } -static int check_str_bytes(char *p, unsigned long long *val) +static int check_str_bytes(const char *p, unsigned long long *val) { return str_to_decimal(p, val, 1); } -static int check_str_time(char *p, unsigned long long *val) +static int check_str_time(const char *p, unsigned long long *val) { return str_to_decimal(p, val, 0); } @@ -109,7 +108,7 @@ static int check_range_bytes(char *str, unsigned long *val) return 1; } -static int check_int(char *p, unsigned int *val) +static int check_int(const char *p, unsigned int *val) { if (sscanf(p, "%u", val) == 1) return 0; @@ -132,16 +131,14 @@ static struct fio_option *find_option(struct fio_option *options, return NULL; } -static int handle_option(struct fio_option *o, char *ptr, void *data) +static int handle_option(struct fio_option *o, const char *ptr, void *data) { unsigned int il, *ilp; unsigned long long ull, *ullp; unsigned long ul1, ul2, *ulp1, *ulp2; - char *tmpbuf, **cp; + char **cp; int ret = 0, is_time = 0; - tmpbuf = malloc(4096); - switch (o->type) { case FIO_OPT_STR: { fio_opt_str_fn *fn = o->cb; @@ -239,13 +236,26 @@ static int handle_option(struct fio_option *o, char *ptr, void *data) ret = 1; } - free(tmpbuf); return ret; } +int parse_cmd_option(const char *opt, const char *val, + struct fio_option *options, void *data) +{ + struct fio_option *o; + + o = find_option(options, opt); + if (!o) { + fprintf(stderr, "Bad option %s\n", opt); + return 1; + } + + return handle_option(o, val, data); +} + int parse_option(const char *opt, struct fio_option *options, void *data) { - struct fio_option *o = find_option(options, opt); + struct fio_option *o; char *pre, *post; char tmp[64]; diff --git a/parse.h b/parse.h index c36bdc0e..be3d24ac 100644 --- a/parse.h +++ b/parse.h @@ -29,6 +29,7 @@ struct fio_option { typedef int (str_cb_fn)(void *, char *); extern int parse_option(const char *, struct fio_option *, void *); +extern int parse_cmd_option(const char *t, const char *l, struct fio_option *, void *); extern void strip_blank_front(char **); extern void strip_blank_end(char *); @@ -36,7 +37,7 @@ extern void strip_blank_end(char *); /* * Handlers for the options */ -typedef int (fio_opt_str_fn)(void *, char *); +typedef int (fio_opt_str_fn)(void *, const char *); typedef int (fio_opt_str_val_fn)(void *, unsigned long long *); typedef int (fio_opt_int_fn)(void *, unsigned int *); typedef int (fio_opt_str_set_fn)(void *); -- 2.25.1