Add a new key in the json per-job output, job_start, that records the
job start time obtained via a call to clock_gettime using the clock_id
specified by the new job_start_clock_id option. This allows times of fio
jobs and log entries to be compared/ordered against each other and
against other system events recorded against the same clock_id.
Add a note to the documentation for group_reporting about how there are
several per-job values for which only the first job's value is recorded
in the json output format when group_reporting is enabled.
Fixes #1544
Signed-off-by: Nick Neumann nick@pcpartpicker.com
15 files changed:
calls will be excluded from other uses. Fio will manually clear it from the
CPU mask of other jobs.
calls will be excluded from other uses. Fio will manually clear it from the
CPU mask of other jobs.
+.. option:: job_start_clock_id=int
+ The clock_id passed to the call to `clock_gettime` used to record job_start
+ in the `json` output format. Default is 0, or CLOCK_REALTIME.
+
Target file/device
~~~~~~~~~~~~~~~~~~
Target file/device
~~~~~~~~~~~~~~~~~~
same reporting group, unless if separated by a :option:`stonewall`, or by
using :option:`new_group`.
same reporting group, unless if separated by a :option:`stonewall`, or by
using :option:`new_group`.
+ NOTE: When :option: `group_reporting` is used along with `json` output,
+ there are certain per-job properties which can be different between jobs
+ but do not have a natural group-level equivalent. Examples include
+ `kb_base`, `unit_base`, `sig_figs`, `thread_number`, `pid`, and
+ `job_start`. For these properties, the values for the first job are
+ recorded for the group.
+
.. option:: new_group
Start a new reporting group. See: :option:`group_reporting`. If not given,
.. option:: new_group
Start a new reporting group. See: :option:`group_reporting`. If not given,
if (rate_submit_init(td, sk_out))
goto err;
if (rate_submit_init(td, sk_out))
goto err;
- set_epoch_time(td, o->log_unix_epoch | o->log_alternate_epoch, o->log_alternate_epoch_clock_id);
+ set_epoch_time(td, o->log_alternate_epoch_clock_id, o->job_start_clock_id);
fio_getrusage(&td->ru_start);
memcpy(&td->bw_sample_time, &td->epoch, sizeof(td->epoch));
memcpy(&td->iops_sample_time, &td->epoch, sizeof(td->epoch));
fio_getrusage(&td->ru_start);
memcpy(&td->bw_sample_time, &td->epoch, sizeof(td->epoch));
memcpy(&td->iops_sample_time, &td->epoch, sizeof(td->epoch));
o->log_unix_epoch = le32_to_cpu(top->log_unix_epoch);
o->log_alternate_epoch = le32_to_cpu(top->log_alternate_epoch);
o->log_alternate_epoch_clock_id = le32_to_cpu(top->log_alternate_epoch_clock_id);
o->log_unix_epoch = le32_to_cpu(top->log_unix_epoch);
o->log_alternate_epoch = le32_to_cpu(top->log_alternate_epoch);
o->log_alternate_epoch_clock_id = le32_to_cpu(top->log_alternate_epoch_clock_id);
+ o->job_start_clock_id = le32_to_cpu(top->job_start_clock_id);
o->norandommap = le32_to_cpu(top->norandommap);
o->softrandommap = le32_to_cpu(top->softrandommap);
o->bs_unaligned = le32_to_cpu(top->bs_unaligned);
o->norandommap = le32_to_cpu(top->norandommap);
o->softrandommap = le32_to_cpu(top->softrandommap);
o->bs_unaligned = le32_to_cpu(top->bs_unaligned);
top->log_unix_epoch = cpu_to_le32(o->log_unix_epoch);
top->log_alternate_epoch = cpu_to_le32(o->log_alternate_epoch);
top->log_alternate_epoch_clock_id = cpu_to_le32(o->log_alternate_epoch_clock_id);
top->log_unix_epoch = cpu_to_le32(o->log_unix_epoch);
top->log_alternate_epoch = cpu_to_le32(o->log_alternate_epoch);
top->log_alternate_epoch_clock_id = cpu_to_le32(o->log_alternate_epoch_clock_id);
+ top->job_start_clock_id = cpu_to_le32(o->job_start_clock_id);
top->norandommap = cpu_to_le32(o->norandommap);
top->softrandommap = cpu_to_le32(o->softrandommap);
top->bs_unaligned = cpu_to_le32(o->bs_unaligned);
top->norandommap = cpu_to_le32(o->norandommap);
top->softrandommap = cpu_to_le32(o->softrandommap);
top->bs_unaligned = cpu_to_le32(o->bs_unaligned);
dst->error = le32_to_cpu(src->error);
dst->thread_number = le32_to_cpu(src->thread_number);
dst->groupid = le32_to_cpu(src->groupid);
dst->error = le32_to_cpu(src->error);
dst->thread_number = le32_to_cpu(src->thread_number);
dst->groupid = le32_to_cpu(src->groupid);
+ dst->job_start = le64_to_cpu(src->job_start);
dst->pid = le32_to_cpu(src->pid);
dst->members = le32_to_cpu(src->members);
dst->unified_rw_rep = le32_to_cpu(src->unified_rw_rep);
dst->pid = le32_to_cpu(src->pid);
dst->members = le32_to_cpu(src->members);
dst->unified_rw_rep = le32_to_cpu(src->unified_rw_rep);
\fBgettimeofday\fR\|(2) call. The CPU set aside for doing these time
calls will be excluded from other uses. Fio will manually clear it from the
CPU mask of other jobs.
\fBgettimeofday\fR\|(2) call. The CPU set aside for doing these time
calls will be excluded from other uses. Fio will manually clear it from the
CPU mask of other jobs.
+.TP
+.BI job_start_clock_id \fR=\fPint
+The clock_id passed to the call to \fBclock_gettime\fR used to record job_start
+in the \fBjson\fR output format. Default is 0, or CLOCK_REALTIME.
.SS "Target file/device"
.TP
.BI directory \fR=\fPstr
.SS "Target file/device"
.TP
.BI directory \fR=\fPstr
per-job, use \fBgroup_reporting\fR. Jobs in a file will be part of the
same reporting group, unless if separated by a \fBstonewall\fR, or by
using \fBnew_group\fR.
per-job, use \fBgroup_reporting\fR. Jobs in a file will be part of the
same reporting group, unless if separated by a \fBstonewall\fR, or by
using \fBnew_group\fR.
+.RS
+.P
+NOTE: When \fBgroup_reporting\fR is used along with \fBjson\fR output, there
+are certain per-job properties which can be different between jobs but do not
+have a natural group-level equivalent. Examples include \fBkb_base\fR,
+\fBunit_base\fR, \fBsig_figs\fR, \fBthread_number\fR, \fBpid\fR, and
+\fBjob_start\fR. For these properties, the values for the first job are
+recorded for the group.
+.RE
.TP
.BI new_group
Start a new reporting group. See: \fBgroup_reporting\fR. If not given,
.TP
.BI new_group
Start a new reporting group. See: \fBgroup_reporting\fR. If not given,
struct timespec start; /* start of this loop */
struct timespec epoch; /* time job was started */
struct timespec start; /* start of this loop */
struct timespec epoch; /* time job was started */
- unsigned long long alternate_epoch; /* Time job was started, clock_gettime's clock_id epoch based. */
+ unsigned long long alternate_epoch; /* Time job was started, as clock_gettime(log_alternate_epoch_clock_id) */
+ unsigned long long job_start; /* Time job was started, as clock_gettime(job_start_clock_id) */
struct timespec last_issue;
long time_offset;
struct timespec ts_cache;
struct timespec last_issue;
long time_offset;
struct timespec ts_cache;
extern bool in_ramp_time(struct thread_data *);
extern void fio_time_init(void);
extern void timespec_add_msec(struct timespec *, unsigned int);
extern bool in_ramp_time(struct thread_data *);
extern void fio_time_init(void);
extern void timespec_add_msec(struct timespec *, unsigned int);
-extern void set_epoch_time(struct thread_data *, int, clockid_t);
+extern void set_epoch_time(struct thread_data *, clockid_t, clockid_t);
- set_epoch_time(td, td->o.log_unix_epoch | td->o.log_alternate_epoch, td->o.log_alternate_epoch_clock_id);
+ set_epoch_time(td, td->o.log_alternate_epoch_clock_id, td->o.job_start_clock_id);
memcpy(&td->start, &td->epoch, sizeof(td->epoch));
memcpy(&td->iops_sample_time, &td->epoch, sizeof(td->epoch));
memcpy(&td->bw_sample_time, &td->epoch, sizeof(td->epoch));
memcpy(&td->start, &td->epoch, sizeof(td->epoch));
memcpy(&td->iops_sample_time, &td->epoch, sizeof(td->epoch));
memcpy(&td->bw_sample_time, &td->epoch, sizeof(td->epoch));
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_CLOCK,
},
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_CLOCK,
},
+ {
+ .name = "job_start_clock_id",
+ .lname = "Job start clock_id",
+ .type = FIO_OPT_INT,
+ .off1 = offsetof(struct thread_options, job_start_clock_id),
+ .help = "The clock_id passed to the call to clock_gettime used to record job_start in the json output format. Default is 0, or CLOCK_REALTIME",
+ .verify = gtod_cpu_verify,
+ .category = FIO_OPT_C_GENERAL,
+ .group = FIO_OPT_G_CLOCK,
+ },
{
.name = "unified_rw_reporting",
.lname = "Unified RW Reporting",
{
.name = "unified_rw_reporting",
.lname = "Unified RW Reporting",
if (td->io_ops->post_init && td->io_ops->post_init(td))
goto err_io_init;
if (td->io_ops->post_init && td->io_ops->post_init(td))
goto err_io_init;
- set_epoch_time(td, td->o.log_unix_epoch | td->o.log_alternate_epoch, td->o.log_alternate_epoch_clock_id);
+ set_epoch_time(td, td->o.log_alternate_epoch_clock_id, td->o.job_start_clock_id);
fio_getrusage(&td->ru_start);
clear_io_state(td, 1);
fio_getrusage(&td->ru_start);
clear_io_state(td, 1);
p.ts.error = cpu_to_le32(ts->error);
p.ts.thread_number = cpu_to_le32(ts->thread_number);
p.ts.groupid = cpu_to_le32(ts->groupid);
p.ts.error = cpu_to_le32(ts->error);
p.ts.thread_number = cpu_to_le32(ts->thread_number);
p.ts.groupid = cpu_to_le32(ts->groupid);
+ p.ts.job_start = cpu_to_le64(ts->job_start);
p.ts.pid = cpu_to_le32(ts->pid);
p.ts.members = cpu_to_le32(ts->members);
p.ts.unified_rw_rep = cpu_to_le32(ts->unified_rw_rep);
p.ts.pid = cpu_to_le32(ts->pid);
p.ts.members = cpu_to_le32(ts->members);
p.ts.unified_rw_rep = cpu_to_le32(ts->unified_rw_rep);
root = json_create_object();
json_object_add_value_string(root, "jobname", ts->name);
json_object_add_value_int(root, "groupid", ts->groupid);
root = json_create_object();
json_object_add_value_string(root, "jobname", ts->name);
json_object_add_value_int(root, "groupid", ts->groupid);
+ json_object_add_value_int(root, "job_start", ts->job_start);
json_object_add_value_int(root, "error", ts->error);
/* ETA Info */
json_object_add_value_int(root, "error", ts->error);
/* ETA Info */
*/
ts->thread_number = td->thread_number;
ts->groupid = td->groupid;
*/
ts->thread_number = td->thread_number;
ts->groupid = td->groupid;
+ ts->job_start = td->job_start;
/*
* first pid in group, not very useful...
/*
* first pid in group, not very useful...
s = get_sample(iolog, cur_log, cur_log->nr_samples);
s->data = data;
s = get_sample(iolog, cur_log, cur_log->nr_samples);
s->data = data;
- s->time = t + (iolog->td ? iolog->td->alternate_epoch : 0);
+ s->time = t;
+ if (iolog->td && (iolog->td->o.log_unix_epoch || iolog->td->o.log_alternate_epoch))
+ s->time += iolog->td->alternate_epoch;
io_sample_set_ddir(iolog, s, ddir);
s->bs = bs;
s->priority = priority;
io_sample_set_ddir(iolog, s, ddir);
s->bs = bs;
s->priority = priority;
uint32_t error;
uint32_t thread_number;
uint32_t groupid;
uint32_t error;
uint32_t thread_number;
uint32_t groupid;
+ uint64_t job_start; /* Time job was started, as clock_gettime(job_start_clock_id) */
uint32_t pid;
char description[FIO_JOBDESC_SIZE];
uint32_t members;
uint32_t pid;
char description[FIO_JOBDESC_SIZE];
uint32_t members;
unsigned int unified_rw_rep;
unsigned int gtod_reduce;
unsigned int gtod_cpu;
unsigned int unified_rw_rep;
unsigned int gtod_reduce;
unsigned int gtod_cpu;
+ unsigned int job_start_clock_id;
enum fio_cs clocksource;
unsigned int no_stall;
unsigned int trim_percentage;
enum fio_cs clocksource;
unsigned int no_stall;
unsigned int trim_percentage;
uint32_t iodepth_batch_complete_min;
uint32_t iodepth_batch_complete_max;
uint32_t serialize_overlap;
uint32_t iodepth_batch_complete_min;
uint32_t iodepth_batch_complete_max;
uint32_t serialize_overlap;
uint64_t size;
uint64_t io_size;
uint64_t size;
uint64_t io_size;
uint32_t fill_device;
uint32_t file_append;
uint32_t unique_filename;
uint32_t fill_device;
uint32_t file_append;
uint32_t unique_filename;
uint64_t file_size_low;
uint64_t file_size_high;
uint64_t start_offset;
uint64_t start_offset_align;
uint32_t start_offset_nz;
uint64_t file_size_low;
uint64_t file_size_high;
uint64_t start_offset;
uint64_t start_offset_align;
uint32_t start_offset_nz;
uint64_t bs[DDIR_RWDIR_CNT];
uint64_t ba[DDIR_RWDIR_CNT];
uint64_t bs[DDIR_RWDIR_CNT];
uint64_t ba[DDIR_RWDIR_CNT];
struct zone_split zone_split[DDIR_RWDIR_CNT][ZONESPLIT_MAX];
uint32_t zone_split_nr[DDIR_RWDIR_CNT];
struct zone_split zone_split[DDIR_RWDIR_CNT][ZONESPLIT_MAX];
uint32_t zone_split_nr[DDIR_RWDIR_CNT];
fio_fp64_t zipf_theta;
fio_fp64_t pareto_h;
fio_fp64_t gauss_dev;
fio_fp64_t zipf_theta;
fio_fp64_t pareto_h;
fio_fp64_t gauss_dev;
uint32_t unified_rw_rep;
uint32_t gtod_reduce;
uint32_t gtod_cpu;
uint32_t unified_rw_rep;
uint32_t gtod_reduce;
uint32_t gtod_cpu;
+ uint32_t job_start_clock_id;
uint32_t clocksource;
uint32_t no_stall;
uint32_t trim_percentage;
uint32_t clocksource;
uint32_t no_stall;
uint32_t trim_percentage;
uint32_t lat_percentiles;
uint32_t slat_percentiles;
uint32_t percentile_precision;
uint32_t lat_percentiles;
uint32_t slat_percentiles;
uint32_t percentile_precision;
fio_fp64_t percentile_list[FIO_IO_U_LIST_MAX_LEN];
uint8_t read_iolog_file[FIO_TOP_STR_MAX];
fio_fp64_t percentile_list[FIO_IO_U_LIST_MAX_LEN];
uint8_t read_iolog_file[FIO_TOP_STR_MAX];
fio_gettime(&genesis, NULL);
}
fio_gettime(&genesis, NULL);
}
-void set_epoch_time(struct thread_data *td, int log_alternate_epoch, clockid_t clock_id)
+void set_epoch_time(struct thread_data *td, clockid_t log_alternate_epoch_clock_id, clockid_t job_start_clock_id)
fio_gettime(&td->epoch, NULL);
fio_gettime(&td->epoch, NULL);
- if (log_alternate_epoch) {
- struct timespec ts;
- clock_gettime(clock_id, &ts);
- td->alternate_epoch = (unsigned long long)(ts.tv_sec) * 1000 +
- (unsigned long long)(ts.tv_nsec) / 1000000;
+ clock_gettime(log_alternate_epoch_clock_id, &ts);
+ td->alternate_epoch = (unsigned long long)(ts.tv_sec) * 1000 +
+ (unsigned long long)(ts.tv_nsec) / 1000000;
+ if (job_start_clock_id == log_alternate_epoch_clock_id)
+ {
+ td->job_start = td->alternate_epoch;
+ }
+ else
+ {
+ clock_gettime(job_start_clock_id, &ts);
+ td->job_start = (unsigned long long)(ts.tv_sec) * 1000 +
+ (unsigned long long)(ts.tv_nsec) / 1000000;