Fix string copy compilation warnings
[fio.git] / init.c
diff --git a/init.c b/init.c
index 8a80138394cfdf81689f23f3f057c653874b3b86..f2ba1e208e8f1cc44d37490501abb932c6b6ed4b 100644 (file)
--- a/init.c
+++ b/init.c
@@ -4,14 +4,17 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <fcntl.h>
 #include <ctype.h>
 #include <string.h>
 #include <errno.h>
 #include <sys/ipc.h>
 #include <sys/types.h>
-#include <sys/stat.h>
 #include <dlfcn.h>
+#ifdef CONFIG_VALGRIND_DEV
+#include <valgrind/drd.h>
+#else
+#define DRD_IGNORE_VAR(x) do { } while (0)
+#endif
 
 #include "fio.h"
 #ifndef FIO_NO_HAVE_SHM_H
@@ -27,6 +30,7 @@
 #include "idletime.h"
 #include "filelock.h"
 #include "steadystate.h"
+#include "blktrace.h"
 
 #include "oslib/getopt.h"
 #include "oslib/strcasestr.h"
@@ -41,15 +45,16 @@ const char fio_version_string[] = FIO_VERSION;
 
 static char **ini_file;
 static int max_jobs = FIO_MAX_JOBS;
-static int dump_cmdline;
-static int parse_only;
+static bool dump_cmdline;
+static bool parse_only;
+static bool merge_blktrace_only;
 
 static struct thread_data def_thread;
 struct thread_data *threads = NULL;
 static char **job_sections;
 static int nr_job_sections;
 
-int exitall_on_terminate = 0;
+bool exitall_on_terminate = false;
 int output_format = FIO_OUTPUT_NORMAL;
 int eta_print = FIO_ETA_AUTO;
 unsigned int eta_interval_msec = 1000;
@@ -59,12 +64,13 @@ FILE *f_err = NULL;
 char *exec_profile = NULL;
 int warnings_fatal = 0;
 int terse_version = 3;
-int is_backend = 0;
+bool is_backend = false;
+bool is_local_backend = false;
 int nr_clients = 0;
-int log_syslog = 0;
+bool log_syslog = false;
 
-int write_bw_log = 0;
-int read_only = 0;
+bool write_bw_log = false;
+bool read_only = false;
 int status_interval = 0;
 
 char *trigger_file = NULL;
@@ -79,6 +85,7 @@ static int prev_group_jobs;
 unsigned long fio_debug = 0;
 unsigned int fio_debug_jobno = -1;
 unsigned int *fio_debug_jobp = NULL;
+unsigned int *fio_warned = NULL;
 
 static char cmd_optstr[256];
 static bool did_arg;
@@ -254,7 +261,7 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
        {
                .name           = (char *) "status-interval",
                .has_arg        = required_argument,
-               .val            = 'L',
+               .val            = 'L' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "trigger-file",
@@ -281,6 +288,11 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
                .has_arg        = required_argument,
                .val            = 'K',
        },
+       {
+               .name           = (char *) "merge-blktrace-only",
+               .has_arg        = no_argument,
+               .val            = 'A' | FIO_CLIENT_FLAG,
+       },
        {
                .name           = NULL,
        },
@@ -309,6 +321,7 @@ static void free_shm(void)
        if (threads) {
                flow_exit();
                fio_debug_jobp = NULL;
+               fio_warned = NULL;
                free_threads_shm();
        }
 
@@ -331,6 +344,8 @@ static void free_shm(void)
  */
 static int setup_thread_area(void)
 {
+       int i;
+
        if (threads)
                return 0;
 
@@ -341,7 +356,7 @@ static int setup_thread_area(void)
        do {
                size_t size = max_jobs * sizeof(struct thread_data);
 
-               size += sizeof(unsigned int);
+               size += 2 * sizeof(unsigned int);
 
 #ifndef CONFIG_NO_SHM
                shm_id = shmget(0, size, IPC_CREAT | 0600);
@@ -374,8 +389,12 @@ static int setup_thread_area(void)
 #endif
 
        memset(threads, 0, max_jobs * sizeof(struct thread_data));
+       for (i = 0; i < max_jobs; i++)
+               DRD_IGNORE_VAR(threads[i]);
        fio_debug_jobp = (unsigned int *)(threads + max_jobs);
        *fio_debug_jobp = -1;
+       fio_warned = fio_debug_jobp + 1;
+       *fio_warned = 0;
 
        flow_init();
 
@@ -520,7 +539,7 @@ static void put_job(struct thread_data *td)
 
 static int __setup_rate(struct thread_data *td, enum fio_ddir ddir)
 {
-       unsigned int bs = td->o.min_bs[ddir];
+       unsigned long long bs = td->o.min_bs[ddir];
 
        assert(ddir_rw(ddir));
 
@@ -563,23 +582,6 @@ static int fixed_block_size(struct thread_options *o)
                o->min_bs[DDIR_READ] == o->min_bs[DDIR_TRIM];
 }
 
-
-static unsigned long long get_rand_start_delay(struct thread_data *td)
-{
-       unsigned long long delayrange;
-       uint64_t frand_max;
-       unsigned long r;
-
-       delayrange = td->o.start_delay_high - td->o.start_delay;
-
-       frand_max = rand_max(&td->delay_state);
-       r = __rand(&td->delay_state);
-       delayrange = (unsigned long long) ((double) delayrange * (r / (frand_max + 1.0)));
-
-       delayrange += td->o.start_delay;
-       return delayrange;
-}
-
 /*
  * <3 Johannes
  */
@@ -600,13 +602,19 @@ static int fixup_options(struct thread_data *td)
        struct thread_options *o = &td->o;
        int ret = 0;
 
+       if (read_only && (td_write(td) || td_trim(td))) {
+               log_err("fio: trim and write operations are not allowed"
+                        " with the --readonly parameter.\n");
+               ret |= 1;
+       }
+
 #ifndef CONFIG_PSHARED
        if (!o->use_thread) {
                log_info("fio: this platform does not support process shared"
                         " mutexes, forcing use of threads. Use the 'thread'"
                         " option to get rid of this warning.\n");
                o->use_thread = 1;
-               ret = warnings_fatal;
+               ret |= warnings_fatal;
        }
 #endif
 
@@ -614,20 +622,37 @@ static int fixup_options(struct thread_data *td)
                log_err("fio: read iolog overrides write_iolog\n");
                free(o->write_iolog_file);
                o->write_iolog_file = NULL;
-               ret = warnings_fatal;
+               ret |= warnings_fatal;
+       }
+
+       if (o->zone_mode == ZONE_MODE_NONE && o->zone_size) {
+               log_err("fio: --zonemode=none and --zonesize are not compatible.\n");
+               ret |= 1;
+       }
+
+       if (o->zone_mode == ZONE_MODE_STRIDED && !o->zone_size) {
+               log_err("fio: --zonesize must be specified when using --zonemode=strided.\n");
+               ret |= 1;
+       }
+
+       if (o->zone_mode == ZONE_MODE_NOT_SPECIFIED) {
+               if (o->zone_size)
+                       o->zone_mode = ZONE_MODE_STRIDED;
+               else
+                       o->zone_mode = ZONE_MODE_NONE;
        }
 
        /*
-        * only really works with 1 file
+        * Strided zone mode only really works with 1 file.
         */
-       if (o->zone_size && o->open_files > 1)
-               o->zone_size = 0;
+       if (o->zone_mode == ZONE_MODE_STRIDED && o->open_files > 1)
+               o->zone_mode = ZONE_MODE_NONE;
 
        /*
         * If zone_range isn't specified, backward compatibility dictates it
         * should be made equal to zone_size.
         */
-       if (o->zone_size && !o->zone_range)
+       if (o->zone_mode == ZONE_MODE_STRIDED && !o->zone_range)
                o->zone_range = o->zone_size;
 
        /*
@@ -668,20 +693,25 @@ static int fixup_options(struct thread_data *td)
            !o->norandommap) {
                log_err("fio: Any use of blockalign= turns off randommap\n");
                o->norandommap = 1;
-               ret = warnings_fatal;
+               ret |= warnings_fatal;
        }
 
        if (!o->file_size_high)
                o->file_size_high = o->file_size_low;
 
-       if (o->start_delay_high)
-               o->start_delay = get_rand_start_delay(td);
+       if (o->start_delay_high) {
+               if (!o->start_delay_orig)
+                       o->start_delay_orig = o->start_delay;
+               o->start_delay = rand_between(&td->delay_state,
+                                               o->start_delay_orig,
+                                               o->start_delay_high);
+       }
 
        if (o->norandommap && o->verify != VERIFY_NONE
            && !fixed_block_size(o))  {
                log_err("fio: norandommap given for variable block sizes, "
                        "verify limited\n");
-               ret = warnings_fatal;
+               ret |= warnings_fatal;
        }
        if (o->bs_unaligned && (o->odirect || td_ioengine_flagged(td, FIO_RAWIO)))
                log_err("fio: bs_unaligned may not work with raw io\n");
@@ -714,19 +744,12 @@ static int fixup_options(struct thread_data *td)
        /*
         * There's no need to check for in-flight overlapping IOs if the job
         * isn't changing data or the maximum iodepth is guaranteed to be 1
+        * when we are not in offload mode
         */
        if (o->serialize_overlap && !(td->flags & TD_F_READ_IOLOG) &&
-           (!(td_write(td) || td_trim(td)) || o->iodepth == 1))
-               o->serialize_overlap = 0;
-       /*
-        * Currently can't check for overlaps in offload mode
-        */
-       if (o->serialize_overlap && o->io_submit_mode == IO_MODE_OFFLOAD) {
-               log_err("fio: checking for in-flight overlaps when the "
-                       "io_submit_mode is offload is not supported\n");
+           (!(td_write(td) || td_trim(td)) || o->iodepth == 1) &&
+           o->io_submit_mode != IO_MODE_OFFLOAD)
                o->serialize_overlap = 0;
-               ret = warnings_fatal;
-       }
 
        if (o->nr_files > td->files_index)
                o->nr_files = td->files_index;
@@ -739,7 +762,7 @@ static int fixup_options(struct thread_data *td)
            ((o->ratemin[DDIR_READ] + o->ratemin[DDIR_WRITE] + o->ratemin[DDIR_TRIM]) &&
            (o->rate_iops_min[DDIR_READ] + o->rate_iops_min[DDIR_WRITE] + o->rate_iops_min[DDIR_TRIM]))) {
                log_err("fio: rate and rate_iops are mutually exclusive\n");
-               ret = 1;
+               ret |= 1;
        }
        if ((o->rate[DDIR_READ] && (o->rate[DDIR_READ] < o->ratemin[DDIR_READ])) ||
            (o->rate[DDIR_WRITE] && (o->rate[DDIR_WRITE] < o->ratemin[DDIR_WRITE])) ||
@@ -748,13 +771,13 @@ static int fixup_options(struct thread_data *td)
            (o->rate_iops[DDIR_WRITE] && (o->rate_iops[DDIR_WRITE] < o->rate_iops_min[DDIR_WRITE])) ||
            (o->rate_iops[DDIR_TRIM] && (o->rate_iops[DDIR_TRIM] < o->rate_iops_min[DDIR_TRIM]))) {
                log_err("fio: minimum rate exceeds rate\n");
-               ret = 1;
+               ret |= 1;
        }
 
        if (!o->timeout && o->time_based) {
                log_err("fio: time_based requires a runtime/timeout setting\n");
                o->time_based = 0;
-               ret = warnings_fatal;
+               ret |= warnings_fatal;
        }
 
        if (o->fill_device && !o->size)
@@ -770,7 +793,7 @@ static int fixup_options(struct thread_data *td)
                        log_info("fio: multiple writers may overwrite blocks "
                                "that belong to other jobs. This can cause "
                                "verification failures.\n");
-                       ret = warnings_fatal;
+                       ret |= warnings_fatal;
                }
 
                /*
@@ -782,7 +805,7 @@ static int fixup_options(struct thread_data *td)
                        log_info("fio: verification read phase will never "
                                 "start because write phase uses all of "
                                 "runtime\n");
-                       ret = warnings_fatal;
+                       ret |= warnings_fatal;
                }
 
                if (!fio_option_is_set(o, refill_buffers))
@@ -802,11 +825,12 @@ static int fixup_options(struct thread_data *td)
                        o->verify_interval = o->min_bs[DDIR_READ];
 
                /*
-                * Verify interval must be a factor or both min and max
+                * Verify interval must be a factor of both min and max
                 * write size
                 */
-               if (o->verify_interval % o->min_bs[DDIR_WRITE] ||
-                   o->verify_interval % o->max_bs[DDIR_WRITE])
+               if (!o->verify_interval ||
+                   (o->min_bs[DDIR_WRITE] % o->verify_interval) ||
+                   (o->max_bs[DDIR_WRITE] % o->verify_interval))
                        o->verify_interval = gcd(o->min_bs[DDIR_WRITE],
                                                        o->max_bs[DDIR_WRITE]);
        }
@@ -817,15 +841,15 @@ static int fixup_options(struct thread_data *td)
                if (td_ioengine_flagged(td, FIO_PIPEIO)) {
                        log_info("fio: cannot pre-read files with an IO engine"
                                 " that isn't seekable. Pre-read disabled.\n");
-                       ret = warnings_fatal;
+                       ret |= warnings_fatal;
                }
        }
 
-       if (!o->unit_base) {
+       if (o->unit_base == N2S_NONE) {
                if (td_ioengine_flagged(td, FIO_BIT_BASED))
-                       o->unit_base = 1;
+                       o->unit_base = N2S_BITPERSEC;
                else
-                       o->unit_base = 8;
+                       o->unit_base = N2S_BYTEPERSEC;
        }
 
 #ifndef FIO_HAVE_ANY_FALLOCATE
@@ -841,7 +865,7 @@ static int fixup_options(struct thread_data *td)
                         " this warning\n");
                o->fsync_blocks = o->fdatasync_blocks;
                o->fdatasync_blocks = 0;
-               ret = warnings_fatal;
+               ret |= warnings_fatal;
        }
 #endif
 
@@ -854,7 +878,7 @@ static int fixup_options(struct thread_data *td)
                log_err("fio: Windows does not support direct or non-buffered io with"
                                " the synchronous ioengines. Use the 'windowsaio' ioengine"
                                " with 'direct=1' and 'iodepth=1' instead.\n");
-               ret = 1;
+               ret |= 1;
        }
 #endif
 
@@ -885,9 +909,9 @@ static int fixup_options(struct thread_data *td)
         * If size is set but less than the min block size, complain
         */
        if (o->size && o->size < td_min_bs(td)) {
-               log_err("fio: size too small, must not be less than minimum block size: %llu < %u\n",
+               log_err("fio: size too small, must not be less than minimum block size: %llu < %llu\n",
                        (unsigned long long) o->size, td_min_bs(td));
-               ret = 1;
+               ret |= 1;
        }
 
        /*
@@ -904,7 +928,7 @@ static int fixup_options(struct thread_data *td)
 
        if (td_ioengine_flagged(td, FIO_NOEXTEND) && o->file_append) {
                log_err("fio: can't append/extent with IO engine %s\n", td->io_ops->name);
-               ret = 1;
+               ret |= 1;
        }
 
        if (fio_option_is_set(o, gtod_cpu)) {
@@ -921,7 +945,7 @@ static int fixup_options(struct thread_data *td)
                log_err("fio: block error histogram only available "
                        "with a single file per job, but %d files "
                        "provided\n", o->nr_files);
-               ret = 1;
+               ret |= 1;
        }
 
        if (fio_option_is_set(o, clat_percentiles) &&
@@ -935,7 +959,7 @@ static int fixup_options(struct thread_data *td)
                   o->lat_percentiles && o->clat_percentiles) {
                log_err("fio: lat_percentiles and clat_percentiles are "
                        "mutually exclusive\n");
-               ret = 1;
+               ret |= 1;
        }
 
        if (o->disable_lat)
@@ -985,23 +1009,26 @@ void td_fill_verify_state_seed(struct thread_data *td)
 
 static void td_fill_rand_seeds_internal(struct thread_data *td, bool use64)
 {
+       unsigned int read_seed = td->rand_seeds[FIO_RAND_BS_OFF];
+       unsigned int write_seed = td->rand_seeds[FIO_RAND_BS1_OFF];
+       unsigned int trim_seed = td->rand_seeds[FIO_RAND_BS2_OFF];
        int i;
 
        /*
         * trimwrite is special in that we need to generate the same
         * offsets to get the "write after trim" effect. If we are
         * using bssplit to set buffer length distributions, ensure that
-        * we seed the trim and write generators identically.
+        * we seed the trim and write generators identically. Ditto for
+        * verify, read and writes must have the same seed, if we are doing
+        * read verify.
         */
-       if (td_trimwrite(td)) {
-               init_rand_seed(&td->bsrange_state[DDIR_READ], td->rand_seeds[FIO_RAND_BS_OFF], use64);
-               init_rand_seed(&td->bsrange_state[DDIR_WRITE], td->rand_seeds[FIO_RAND_BS1_OFF], use64);
-               init_rand_seed(&td->bsrange_state[DDIR_TRIM], td->rand_seeds[FIO_RAND_BS1_OFF], use64);
-       } else {
-               init_rand_seed(&td->bsrange_state[DDIR_READ], td->rand_seeds[FIO_RAND_BS_OFF], use64);
-               init_rand_seed(&td->bsrange_state[DDIR_WRITE], td->rand_seeds[FIO_RAND_BS1_OFF], use64);
-               init_rand_seed(&td->bsrange_state[DDIR_TRIM], td->rand_seeds[FIO_RAND_BS2_OFF], use64);
-       }
+       if (td->o.verify != VERIFY_NONE)
+               write_seed = read_seed;
+       if (td_trimwrite(td))
+               trim_seed = write_seed;
+       init_rand_seed(&td->bsrange_state[DDIR_READ], read_seed, use64);
+       init_rand_seed(&td->bsrange_state[DDIR_WRITE], write_seed, use64);
+       init_rand_seed(&td->bsrange_state[DDIR_TRIM], trim_seed, use64);
 
        td_fill_verify_state_seed(td);
        init_rand_seed(&td->rwmix_state, td->rand_seeds[FIO_RAND_MIX_OFF], false);
@@ -1172,7 +1199,7 @@ static void init_flags(struct thread_data *td)
            fio_option_is_set(o, zero_buffers)))
                td->flags |= TD_F_SCRAMBLE_BUFFERS;
        if (o->verify != VERIFY_NONE)
-               td->flags |= TD_F_VER_NONE;
+               td->flags |= TD_F_DO_VERIFY;
 
        if (o->verify_async || o->io_submit_mode == IO_MODE_OFFLOAD)
                td->flags |= TD_F_NEED_LOCK;
@@ -1190,7 +1217,7 @@ static void init_flags(struct thread_data *td)
 
 static int setup_random_seeds(struct thread_data *td)
 {
-       unsigned long seed;
+       uint64_t seed;
        unsigned int i;
 
        if (!td->o.rand_repeatable && !fio_option_is_set(&td->o, rand_seed)) {
@@ -1391,6 +1418,17 @@ static bool wait_for_ok(const char *jobname, struct thread_options *o)
        return true;
 }
 
+/*
+ * Treat an empty log file name the same as a one not given
+ */
+static const char *make_log_name(const char *logname, const char *jobname)
+{
+       if (logname && strcmp(logname, ""))
+               return logname;
+
+       return jobname;
+}
+
 /*
  * Adds a job to the list of things todo. Sanitizes the various options
  * to make sure we don't have conflicts, and initializes various
@@ -1400,7 +1438,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                   int recursed, int client_type)
 {
        unsigned int i;
-       char fname[PATH_MAX];
+       char fname[PATH_MAX + 1];
        int numjobs, file_alloced;
        struct thread_options *o = &td->o;
        char logname[PATH_MAX + 32];
@@ -1441,6 +1479,11 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                }
        }
 
+       if (setup_random_seeds(td)) {
+               td_verror(td, errno, "setup_random_seeds");
+               goto err;
+       }
+
        if (fixup_options(td))
                goto err;
 
@@ -1466,7 +1509,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                        f->real_file_size = -1ULL;
        }
 
-       td->mutex = fio_mutex_init(FIO_MUTEX_LOCKED);
+       td->sem = fio_sem_init(FIO_SEM_LOCKED);
 
        td->ts.clat_percentiles = o->clat_percentiles;
        td->ts.lat_percentiles = o->lat_percentiles;
@@ -1481,6 +1524,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                td->ts.bw_stat[i].min_val = ULONG_MAX;
                td->ts.iops_stat[i].min_val = ULONG_MAX;
        }
+       td->ts.sync_stat.min_val = ULONG_MAX;
        td->ddir_seq_nr = o->ddir_seq_nr;
 
        if ((o->stonewall || o->new_group) && prev_group_jobs) {
@@ -1495,11 +1539,6 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
        td->groupid = groupid;
        prev_group_jobs++;
 
-       if (setup_random_seeds(td)) {
-               td_verror(td, errno, "setup_random_seeds");
-               goto err;
-       }
-
        if (setup_rate(td))
                goto err;
 
@@ -1514,7 +1553,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                        .log_gz = o->log_gz,
                        .log_gz_store = o->log_gz_store,
                };
-               const char *pre = o->lat_log_file ? o->lat_log_file : o->name;
+               const char *pre = make_log_name(o->lat_log_file, o->name);
                const char *suf;
 
                if (p.log_gz_store)
@@ -1533,6 +1572,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                gen_log_name(logname, sizeof(logname), "clat", pre,
                                td->thread_number, suf, o->per_job_logs);
                setup_log(&td->clat_log, &p, logname);
+
        }
 
        if (o->write_hist_log) {
@@ -1546,7 +1586,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                        .log_gz = o->log_gz,
                        .log_gz_store = o->log_gz_store,
                };
-               const char *pre = o->hist_log_file ? o->hist_log_file : o->name;
+               const char *pre = make_log_name(o->hist_log_file, o->name);
                const char *suf;
 
 #ifndef CONFIG_ZLIB
@@ -1577,14 +1617,14 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                        .log_gz = o->log_gz,
                        .log_gz_store = o->log_gz_store,
                };
-               const char *pre = o->bw_log_file ? o->bw_log_file : o->name;
+               const char *pre = make_log_name(o->bw_log_file, o->name);
                const char *suf;
 
                if (fio_option_is_set(o, bw_avg_time))
                        p.avg_msec = min(o->log_avg_msec, o->bw_avg_time);
                else
                        o->bw_avg_time = p.avg_msec;
-       
+
                p.hist_msec = o->log_hist_msec;
                p.hist_coarseness = o->log_hist_coarseness;
 
@@ -1608,14 +1648,14 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                        .log_gz = o->log_gz,
                        .log_gz_store = o->log_gz_store,
                };
-               const char *pre = o->iops_log_file ? o->iops_log_file : o->name;
+               const char *pre = make_log_name(o->iops_log_file, o->name);
                const char *suf;
 
                if (fio_option_is_set(o, iops_avg_time))
                        p.avg_msec = min(o->log_avg_msec, o->iops_avg_time);
                else
                        o->iops_avg_time = p.avg_msec;
-       
+
                p.hist_msec = o->log_hist_msec;
                p.hist_coarseness = o->log_hist_coarseness;
 
@@ -1641,6 +1681,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                                char *c1, *c2, *c3, *c4;
                                char *c5 = NULL, *c6 = NULL;
                                int i2p = is_power_of_2(o->kb_base);
+                               struct buf_output out;
 
                                c1 = num2str(o->min_bs[DDIR_READ], o->sig_figs, 1, i2p, N2S_BYTE);
                                c2 = num2str(o->max_bs[DDIR_READ], o->sig_figs, 1, i2p, N2S_BYTE);
@@ -1652,19 +1693,22 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                                        c6 = num2str(o->max_bs[DDIR_TRIM], o->sig_figs, 1, i2p, N2S_BYTE);
                                }
 
-                               log_info("%s: (g=%d): rw=%s, ", td->o.name,
+                               buf_output_init(&out);
+                               __log_buf(&out, "%s: (g=%d): rw=%s, ", td->o.name,
                                                        td->groupid,
                                                        ddir_str(o->td_ddir));
 
                                if (o->bs_is_seq_rand)
-                                       log_info("bs=(R) %s-%s, (W) %s-%s, bs_is_seq_rand, ",
+                                       __log_buf(&out, "bs=(R) %s-%s, (W) %s-%s, bs_is_seq_rand, ",
                                                        c1, c2, c3, c4);
                                else
-                                       log_info("bs=(R) %s-%s, (W) %s-%s, (T) %s-%s, ",
+                                       __log_buf(&out, "bs=(R) %s-%s, (W) %s-%s, (T) %s-%s, ",
                                                        c1, c2, c3, c4, c5, c6);
 
-                               log_info("ioengine=%s, iodepth=%u\n",
+                               __log_buf(&out, "ioengine=%s, iodepth=%u\n",
                                                td->io_ops->name, o->iodepth);
+                               log_info_buf(out.buf, out.buflen);
+                               buf_output_free(&out);
 
                                free(c1);
                                free(c2);
@@ -1680,6 +1724,14 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
        if (td_steadystate_init(td))
                goto err;
 
+       if (o->merge_blktrace_file && !merge_blktrace_iologs(td))
+               goto err;
+
+       if (merge_blktrace_only) {
+               put_job(td);
+               return 0;
+       }
+
        /*
         * recurse add identical jobs, clear numjobs and stonewall options
         * as they don't apply to sub-jobs
@@ -1957,7 +2009,8 @@ static int __parse_jobs_ini(struct thread_data *td,
                        if (p[0] == '[') {
                                if (nested) {
                                        log_err("No new sections in included files\n");
-                                       return 1;
+                                       ret = 1;
+                                       goto out;
                                }
 
                                skip_fgets = 1;
@@ -1987,7 +2040,8 @@ static int __parse_jobs_ini(struct thread_data *td,
                                        strncpy(full_fn,
                                                file, (ts - file) + 1);
                                        strncpy(full_fn + (ts - file) + 1,
-                                               filename, strlen(filename));
+                                               filename,
+                                               len - (ts - file) - 1);
                                        full_fn[len - 1] = 0;
                                        filename = full_fn;
                                }
@@ -2088,7 +2142,7 @@ static int fill_def_thread(void)
 static void show_debug_categories(void)
 {
 #ifdef FIO_INC_DEBUG
-       struct debug_level *dl = &debug_levels[0];
+       const struct debug_level *dl = &debug_levels[0];
        int curlen, first = 1;
 
        curlen = 0;
@@ -2128,6 +2182,7 @@ static void usage(const char *name)
        printf("  --debug=options\tEnable debug logging. May be one/more of:\n");
        show_debug_categories();
        printf("  --parse-only\t\tParse options only, don't start any IO\n");
+       printf("  --merge-blktrace-only\tMerge blktraces only, don't start any IO\n");
        printf("  --output\t\tWrite output to file\n");
        printf("  --bandwidth-log\tGenerate aggregate bandwidth logs\n");
        printf("  --minimal\t\tMinimal (terse) output\n");
@@ -2147,7 +2202,7 @@ static void usage(const char *name)
        printf("  --showcmd\t\tTurn a job file into command line options\n");
        printf("  --eta=when\t\tWhen ETA estimate should be printed\n");
        printf("            \t\tMay be \"always\", \"never\" or \"auto\"\n");
-       printf("  --eta-newline=time\tForce a new line for every 'time'");
+       printf("  --eta-newline=t\tForce a new line for every 't'");
        printf(" period passed\n");
        printf("  --status-interval=t\tForce full status dump every");
        printf(" 't' period passed\n");
@@ -2178,7 +2233,7 @@ static void usage(const char *name)
 }
 
 #ifdef FIO_INC_DEBUG
-struct debug_level debug_levels[] = {
+const struct debug_level debug_levels[] = {
        { .name = "process",
          .help = "Process creation/exit logging",
          .shift = FD_PROCESS,
@@ -2251,12 +2306,16 @@ struct debug_level debug_levels[] = {
          .help = "Helper thread logging",
          .shift = FD_HELPERTHREAD,
        },
+       { .name = "zbd",
+         .help = "Zoned Block Device logging",
+         .shift = FD_ZBD,
+       },
        { .name = NULL, },
 };
 
 static int set_debug(const char *string)
 {
-       struct debug_level *dl;
+       const struct debug_level *dl;
        char *p = (char *) string;
        char *opt;
        int i;
@@ -2422,7 +2481,7 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
        char *ostr = cmd_optstr;
        char *pid_file = NULL;
        void *cur_client = NULL;
-       int backend = 0;
+       bool backend = false;
 
        /*
         * Reset optind handling, since we may call this multiple times
@@ -2448,7 +2507,7 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        exit_val = 1;
                        break;
                case 'b':
-                       write_bw_log = 1;
+                       write_bw_log = true;
                        break;
                case 'o': {
                        FILE *tmp;
@@ -2503,7 +2562,7 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        break;
                case 's':
                        did_arg = true;
-                       dump_cmdline = 1;
+                       dump_cmdline = true;
                        break;
                case 'r':
                        read_only = 1;
@@ -2569,7 +2628,7 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        break;
                case 'P':
                        did_arg = true;
-                       parse_only = 1;
+                       parse_only = true;
                        break;
                case 'x': {
                        size_t new_size;
@@ -2694,8 +2753,8 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        }
                        if (optarg)
                                fio_server_set_arg(optarg);
-                       is_backend = 1;
-                       backend = 1;
+                       is_backend = true;
+                       backend = true;
 #else
                        log_err("fio: client/server requires SHM support\n");
                        do_exit++;
@@ -2840,10 +2899,16 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        }
                        trigger_timeout /= 1000000;
                        break;
+
+               case 'A':
+                       did_arg = true;
+                       merge_blktrace_only = true;
+                       break;
                case '?':
                        log_err("%s: unrecognized option '%s'\n", argv[0],
                                                        argv[optind - 1]);
                        show_closest_option(argv[optind - 1]);
+                       /* fall through */
                default:
                        do_exit++;
                        exit_val = 1;