From cc372b17f2827e89da79241f1bbaca1e7c650611 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Mon, 17 Sep 2012 09:12:18 +0200 Subject: [PATCH] Add a simple json encoder and use it to print fio output in json format Signed-off-by: Jens Axboe --- Makefile | 2 +- diskutil.c | 62 +++++++++- diskutil.h | 6 +- init.c | 14 ++- json.c | 336 +++++++++++++++++++++++++++++++++++++++++++++++++++++ json.h | 77 ++++++++++++ stat.c | 221 ++++++++++++++++++++++++++++++++++- 7 files changed, 705 insertions(+), 13 deletions(-) create mode 100644 json.c create mode 100644 json.h diff --git a/Makefile b/Makefile index 9d3945b3..bac062c6 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ SOURCE := gettime.c fio.c ioengines.c init.c stat.c log.c time.c filesetup.c \ rbtree.c smalloc.c filehash.c profile.c debug.c lib/rand.c \ lib/num2str.c lib/ieee754.c $(wildcard crc/*.c) engines/cpu.c \ engines/mmap.c engines/sync.c engines/null.c engines/net.c \ - memalign.c server.c client.c iolog.c backend.c libfio.c flow.c + memalign.c server.c client.c iolog.c backend.c libfio.c flow.c json.c ifeq ($(UNAME), Linux) SOURCE += diskutil.c fifo.c blktrace.c helpers.c cgroup.c trim.c \ diff --git a/diskutil.c b/diskutil.c index a3a5b4dd..d2c0b976 100644 --- a/diskutil.c +++ b/diskutil.c @@ -597,10 +597,60 @@ void print_disk_util(struct disk_util_stat *dus, struct disk_util_agg *agg, log_info("\n"); } -void show_disk_util(int terse) +static void print_disk_util_json(struct disk_util *du, struct json_array *array) +{ + double util = 0; + struct disk_util_stat *dus = &du->dus; + struct disk_util_agg *agg = &du->agg; + struct json_object *obj; + + obj = json_create_object(); + json_array_add_value_object(array, obj); + + if (dus->msec) + util = (double) 100 * dus->io_ticks / (double) dus->msec; + if (util > 100.0) + util = 100.0; + + + json_object_add_value_string(obj, "name", dus->name); + json_object_add_value_int(obj, "read_ios", dus->ios[0]); + json_object_add_value_int(obj, "write_ios", dus->ios[1]); + json_object_add_value_int(obj, "read_merges", dus->merges[0]); + json_object_add_value_int(obj, "write_merges", dus->merges[1]); + json_object_add_value_int(obj, "read_ticks", dus->ticks[0]); + json_object_add_value_int(obj, "write_ticks", dus->ticks[1]); + json_object_add_value_int(obj, "in_queue", dus->time_in_queue); + json_object_add_value_float(obj, "util", util); + + /* + * If the device has slaves, aggregate the stats for + * those slave devices also. + */ + if (!agg->slavecount) + return; + json_object_add_value_int(obj, "aggr_read_ios", + agg->ios[0] / agg->slavecount); + json_object_add_value_int(obj, "aggr_write_ios", + agg->ios[1] / agg->slavecount); + json_object_add_value_int(obj, "aggr_read_merges", + agg->merges[0] / agg->slavecount); + json_object_add_value_int(obj, "aggr_write_merge", + agg->merges[1] / agg->slavecount); + json_object_add_value_int(obj, "aggr_read_ticks", + agg->ticks[0] / agg->slavecount); + json_object_add_value_int(obj, "aggr_write_ticks", + agg->ticks[1] / agg->slavecount); + json_object_add_value_int(obj, "aggr_in_queue", + agg->time_in_queue / agg->slavecount); + json_object_add_value_float(obj, "aggr_util", agg->max_util.u.f); +} + +void show_disk_util(int terse, struct json_object *parent) { struct flist_head *entry; struct disk_util *du; + struct json_array *array = NULL; fio_mutex_down(disk_util_mutex); @@ -612,11 +662,19 @@ void show_disk_util(int terse) if (!terse) log_info("\nDisk stats (read/write):\n"); + if (terse && terse_version == 4) { + array = json_create_array(); + json_object_add_value_array(parent, "disk_util", array); + } + flist_for_each(entry, &disk_list) { du = flist_entry(entry, struct disk_util, list); aggregate_slaves_stats(du); - print_disk_util(&du->dus, &du->agg, terse); + if (terse && terse_version == 4) + print_disk_util_json(du, array); + else + print_disk_util(&du->dus, &du->agg, terse); } fio_mutex_up(disk_util_mutex); diff --git a/diskutil.h b/diskutil.h index 88dde555..b2231502 100644 --- a/diskutil.h +++ b/diskutil.h @@ -1,6 +1,6 @@ #ifndef FIO_DISKUTIL_H #define FIO_DISKUTIL_H - +#include "json.h" #define FIO_DU_NAME_SZ 64 /* @@ -101,14 +101,14 @@ extern void wait_for_disk_thread_exit(void); */ #ifdef FIO_HAVE_DISK_UTIL extern void print_disk_util(struct disk_util_stat *, struct disk_util_agg *, int terse); -extern void show_disk_util(int terse); +extern void show_disk_util(int terse, struct json_object *parent); extern void free_disk_util(void); extern void init_disk_util(struct thread_data *); extern int update_io_ticks(void); extern void setup_disk_util(void); #else #define print_disk_util(dus, agg, terse) -#define show_disk_util(terse) +#define show_disk_util(terse, parent) #define free_disk_util() #define init_disk_util(td) #define setup_disk_util() diff --git a/init.c b/init.c index 54ee1db6..ca8047b0 100644 --- a/init.c +++ b/init.c @@ -116,6 +116,11 @@ static struct option l_opts[FIO_NR_OPTIONS] = { .has_arg = optional_argument, .val = 'm' | FIO_CLIENT_FLAG, }, + { + .name = (char *) "json-output", + .has_arg = optional_argument, + .val = 'J' | FIO_CLIENT_FLAG, + }, { .name = (char *) "version", .has_arg = no_argument, @@ -1408,6 +1413,10 @@ int parse_cmd_line(int argc, char *argv[]) case 'm': terse_output = 1; break; + case 'J': + terse_version = 4; + terse_output = 1; + break; case 'h': if (!cur_client) { usage(argv[0]); @@ -1439,9 +1448,10 @@ int parse_cmd_line(int argc, char *argv[]) } break; case 'V': + if (terse_version == 4) + break; terse_version = atoi(optarg); - if (!(terse_version == 2 || terse_version == 3) || - (terse_version == 4)) { + if (!(terse_version == 2 || terse_version == 3)) { log_err("fio: bad terse version format\n"); exit_val = 1; do_exit++; diff --git a/json.c b/json.c new file mode 100644 index 00000000..707ea141 --- /dev/null +++ b/json.c @@ -0,0 +1,336 @@ +#include +#include +#include +#include +#include +#include "json.h" +#include "log.h" + +struct json_object *json_create_object(void) +{ + struct json_object *obj = malloc(sizeof(struct json_object)); + if (obj) + memset(obj, 0, sizeof(struct json_object)); + return obj; +} + +struct json_array *json_create_array(void) +{ + struct json_array *array = malloc(sizeof(struct json_array)); + if (array) + memset(array, 0, sizeof(struct json_array)); + return array; +} + +static struct json_pair *json_create_pair(const char *name, struct json_value *value) +{ + struct json_pair *pair = malloc(sizeof(struct json_pair)); + if (pair) { + pair->name = strdup(name); + pair->value = value; + + value->parent_type = JSON_PARENT_TYPE_PAIR; + value->parent_pair = pair; + } + return pair; +} + +static struct json_value *json_create_value_int(long number) +{ + struct json_value *value = malloc(sizeof(struct json_value)); + + if (value) { + value->type = JSON_TYPE_INTEGER; + value->integer_number = number; + } + return value; +} + +static struct json_value *json_create_value_float(float number) +{ + struct json_value *value = malloc(sizeof(struct json_value)); + + if (value) { + value->type = JSON_TYPE_FLOAT; + value->float_number = number; + } + return value; +} + +static struct json_value *json_create_value_string(const char *str) +{ + struct json_value *value = malloc(sizeof(struct json_value)); + + if (value) { + value->type = JSON_TYPE_STRING; + value->string = strdup(str); + if (!value->string) { + free(value); + value = NULL; + } + } + return value; +} + +static struct json_value *json_create_value_object(struct json_object *obj) +{ + struct json_value *value = malloc(sizeof(struct json_value)); + + if (value) { + value->type = JSON_TYPE_OBJECT; + value->object = obj; + obj->parent = value; + } + return value; +} + +static struct json_value *json_create_value_array(struct json_array *array) +{ + struct json_value *value = malloc(sizeof(struct json_value)); + + if (value) { + value->type = JSON_TYPE_ARRAY; + value->array = array; + array->parent = value; + } + return value; +} + +static void json_free_array(struct json_array *array); +static void json_free_pair(struct json_pair *pair); +static void json_free_value(struct json_value *value); +void json_free_object(struct json_object *obj) +{ + int i; + + for (i = 0; i < obj->pair_cnt; i++) + json_free_pair(obj->pairs[i]); + free(obj->pairs); + free(obj); +} + +static void json_free_array(struct json_array *array) +{ + int i; + + for (i = 0; i < array->value_cnt; i++) + json_free_value(array->values[i]); + free(array->values); + free(array); +} + +static void json_free_pair(struct json_pair *pair) +{ + json_free_value(pair->value); + free(pair->name); + free(pair); +} + +static void json_free_value(struct json_value *value) +{ + switch (value->type) { + case JSON_TYPE_STRING: + free(value->string); + break; + case JSON_TYPE_OBJECT: + json_free_object(value->object); + break; + case JSON_TYPE_ARRAY: + json_free_array(value->array); + break; + } + free(value); +} + +static int json_array_add_value(struct json_array *array, struct json_value *value) +{ + struct json_value **values = realloc(array->values, + sizeof(struct json_value *) * (array->value_cnt + 1)); + + if (!values) + return ENOMEM; + values[array->value_cnt] = value; + array->value_cnt++; + array->values = values; + + value->parent_type = JSON_PARENT_TYPE_ARRAY; + value->parent_array = array; + return 0; +} + +static int json_object_add_pair(struct json_object *obj, struct json_pair *pair) +{ + struct json_pair **pairs = realloc(obj->pairs, + sizeof(struct json_pair *) * (obj->pair_cnt + 1)); + if (!pairs) + return ENOMEM; + pairs[obj->pair_cnt] = pair; + obj->pair_cnt++; + obj->pairs = pairs; + + pair->parent = obj; + return 0; +} + +int json_object_add_value_type(struct json_object *obj, const char *name, int type, ...) +{ + struct json_value *value; + struct json_pair *pair; + va_list args; + int ret; + + va_start(args, type); + if (type == JSON_TYPE_STRING) + value = json_create_value_string(va_arg(args, char *)); + else if (type == JSON_TYPE_INTEGER) + value = json_create_value_int(va_arg(args, long)); + else if (type == JSON_TYPE_FLOAT) + value = json_create_value_float(va_arg(args, double)); + else if (type == JSON_TYPE_OBJECT) + value = json_create_value_object(va_arg(args, struct json_object *)); + else + value = json_create_value_array(va_arg(args, struct json_array *)); + va_end(args); + + if (!value) + return ENOMEM; + + pair = json_create_pair(name, value); + if (!pair) { + json_free_value(value); + return ENOMEM; + } + ret = json_object_add_pair(obj, pair); + if (ret) { + json_free_pair(pair); + return ENOMEM; + } + return 0; +} + +static void json_print_array(struct json_array *array); +int json_array_add_value_type(struct json_array *array, int type, ...) +{ + struct json_value *value; + va_list args; + int ret; + + va_start(args, type); + if (type == JSON_TYPE_STRING) + value = json_create_value_string(va_arg(args, char *)); + else if (type == JSON_TYPE_INTEGER) + value = json_create_value_int(va_arg(args, long)); + else if (type == JSON_TYPE_FLOAT) + value = json_create_value_float(va_arg(args, double)); + else if (type == JSON_TYPE_OBJECT) + value = json_create_value_object(va_arg(args, struct json_object *)); + else + value = json_create_value_array(va_arg(args, struct json_array *)); + va_end(args); + + if (!value) + return ENOMEM; + + ret = json_array_add_value(array, value); + if (ret) { + json_free_value(value); + return ENOMEM; + } + return 0; +} + +static int json_value_level(struct json_value *value); +static int json_pair_level(struct json_pair *pair); +static int json_array_level(struct json_array *array); +static int json_object_level(struct json_object *object) +{ + if (object->parent == NULL) + return 0; + return json_value_level(object->parent); +} + +static int json_pair_level(struct json_pair *pair) +{ + return json_object_level(pair->parent) + 1; +} + +static int json_array_level(struct json_array *array) +{ + return json_value_level(array->parent); +} + +static int json_value_level(struct json_value *value) +{ + if (value->parent_type == JSON_PARENT_TYPE_PAIR) + return json_pair_level(value->parent_pair); + else + return json_array_level(value->parent_array) + 1; +} + +static void json_print_level(int level) +{ + while (level-- > 0) + log_info(" "); +} + +static void json_print_pair(struct json_pair *pair); +static void json_print_array(struct json_array *array); +static void json_print_value(struct json_value *value); +void json_print_object(struct json_object *obj) +{ + int i; + + log_info("{\n"); + for (i = 0; i < obj->pair_cnt; i++) { + if (i > 0) + log_info(",\n"); + json_print_pair(obj->pairs[i]); + } + log_info("\n"); + json_print_level(json_object_level(obj)); + log_info("}"); +} + +static void json_print_pair(struct json_pair *pair) +{ + json_print_level(json_pair_level(pair)); + log_info("\"%s\" : ", pair->name); + json_print_value(pair->value); +} + +static void json_print_array(struct json_array *array) +{ + int i; + + log_info("[\n"); + for (i = 0; i < array->value_cnt; i++) { + if (i > 0) + log_info(",\n"); + json_print_level(json_value_level(array->values[i])); + json_print_value(array->values[i]); + } + log_info("\n"); + json_print_level(json_array_level(array)); + log_info("]"); +} + +static void json_print_value(struct json_value *value) +{ + switch (value->type) { + case JSON_TYPE_STRING: + log_info("\"%s\"", value->string); + break; + case JSON_TYPE_INTEGER: + log_info("%ld", value->integer_number); + break; + case JSON_TYPE_FLOAT: + log_info("%.2f", value->float_number); + break; + case JSON_TYPE_OBJECT: + json_print_object(value->object); + break; + case JSON_TYPE_ARRAY: + json_print_array(value->array); + break; + } +} diff --git a/json.h b/json.h new file mode 100644 index 00000000..4d05e823 --- /dev/null +++ b/json.h @@ -0,0 +1,77 @@ +#ifndef __JSON__H +#define __JSON__H +struct json_object; +struct json_array; +struct json_pair; + +#define JSON_TYPE_STRING 0 +#define JSON_TYPE_INTEGER 1 +#define JSON_TYPE_FLOAT 2 +#define JSON_TYPE_OBJECT 3 +#define JSON_TYPE_ARRAY 4 +#define JSON_PARENT_TYPE_PAIR 0 +#define JSON_PARENT_TYPE_ARRAY 1 +struct json_value { + int type; + union { + long integer_number; + double float_number; + char *string; + struct json_object *object; + struct json_array *array; + }; + int parent_type; + union { + struct json_pair *parent_pair; + struct json_array *parent_array; + }; +}; + +struct json_array { + struct json_value **values; + int value_cnt; + struct json_value *parent; +}; + +struct json_object { + struct json_pair **pairs; + int pair_cnt; + struct json_value *parent; +}; + +struct json_pair { + char *name; + struct json_value *value; + struct json_object *parent; +}; + +struct json_object *json_create_object(void); +struct json_array *json_create_array(void); + +void json_free_object(struct json_object *obj); + +int json_object_add_value_type(struct json_object *obj, const char *name, int type, ...); +#define json_object_add_value_int(obj, name, val) \ + json_object_add_value_type((obj), name, JSON_TYPE_INTEGER, (val)) +#define json_object_add_value_float(obj, name, val) \ + json_object_add_value_type((obj), name, JSON_TYPE_FLOAT, (val)) +#define json_object_add_value_string(obj, name, val) \ + json_object_add_value_type((obj), name, JSON_TYPE_STRING, (val)) +#define json_object_add_value_object(obj, name, val) \ + json_object_add_value_type((obj), name, JSON_TYPE_OBJECT, (val)) +#define json_object_add_value_array(obj, name, val) \ + json_object_add_value_type((obj), name, JSON_TYPE_ARRAY, (val)) +int json_array_add_value_type(struct json_array *array, int type, ...); +#define json_array_add_value_int(obj, val) \ + json_array_add_value_type((obj), JSON_TYPE_INTEGER, (val)) +#define json_array_add_value_float(obj, val) \ + json_array_add_value_type((obj), JSON_TYPE_FLOAT, (val)) +#define json_array_add_value_string(obj, val) \ + json_array_add_value_type((obj), JSON_TYPE_STRING, (val)) +#define json_array_add_value_object(obj, val) \ + json_array_add_value_type((obj), JSON_TYPE_OBJECT, (val)) +#define json_array_add_value_array(obj, val) \ + json_array_add_value_type((obj), JSON_TYPE_ARRAY, (val)) + +void json_print_object(struct json_object *obj); +#endif diff --git a/stat.c b/stat.c index 4e00177b..4d3e9747 100644 --- a/stat.c +++ b/stat.c @@ -10,6 +10,7 @@ #include "fio.h" #include "diskutil.h" #include "lib/ieee754.h" +#include "json.h" void update_rusage_stat(struct thread_data *td) { @@ -678,6 +679,109 @@ static void show_ddir_status_terse(struct thread_stat *ts, log_info(";%lu;%lu;%f%%;%f;%f", 0UL, 0UL, 0.0, 0.0, 0.0); } +static void add_ddir_status_json(struct thread_stat *ts, + struct group_run_stats *rs, int ddir, struct json_object *parent) +{ + unsigned long min, max; + unsigned long long bw, iops; + unsigned int *ovals = NULL; + double mean, dev; + unsigned int len, minv, maxv; + int i; + const char *ddirname[] = {"read", "write", "trim"}; + struct json_object *dir_object, *tmp_object, *percentile_object; + char buf[120]; + double p_of_agg = 100.0; + + assert(ddir_rw(ddir)); + + dir_object = json_create_object(); + json_object_add_value_object(parent, ddirname[ddir], dir_object); + + iops = bw = 0; + if (ts->runtime[ddir]) { + uint64_t runt = ts->runtime[ddir]; + + bw = ((1000 * ts->io_bytes[ddir]) / runt) / 1024; + iops = (1000 * (uint64_t) ts->total_io_u[ddir]) / runt; + } + + json_object_add_value_int(dir_object, "io_bytes", ts->io_bytes[ddir] >> 10); + json_object_add_value_int(dir_object, "bw", bw); + json_object_add_value_int(dir_object, "iops", iops); + json_object_add_value_int(dir_object, "runtime", ts->runtime[ddir]); + + if (!calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev)) { + min = max = 0; + mean = dev = 0.0; + } + tmp_object = json_create_object(); + json_object_add_value_object(dir_object, "slat", tmp_object); + json_object_add_value_int(tmp_object, "min", min); + json_object_add_value_int(tmp_object, "max", max); + json_object_add_value_float(tmp_object, "mean", mean); + json_object_add_value_float(tmp_object, "stddev", dev); + + if (!calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev)) { + min = max = 0; + mean = dev = 0.0; + } + tmp_object = json_create_object(); + json_object_add_value_object(dir_object, "clat", tmp_object); + json_object_add_value_int(tmp_object, "min", min); + json_object_add_value_int(tmp_object, "max", max); + json_object_add_value_float(tmp_object, "mean", mean); + json_object_add_value_float(tmp_object, "stddev", dev); + + if (ts->clat_percentiles) { + len = calc_clat_percentiles(ts->io_u_plat[ddir], + ts->clat_stat[ddir].samples, + ts->percentile_list, &ovals, &maxv, + &minv); + } else + len = 0; + + percentile_object = json_create_object(); + json_object_add_value_object(tmp_object, "percentile", percentile_object); + for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++) { + if (i >= len) { + json_object_add_value_int(percentile_object, "0.00", 0); + continue; + } + snprintf(buf, sizeof(buf) - 1, "%2.2f", ts->percentile_list[i].u.f); + json_object_add_value_int(percentile_object, (const char *)buf, ovals[i]); + } + + if (!calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev)) { + min = max = 0; + mean = dev = 0.0; + } + tmp_object = json_create_object(); + json_object_add_value_object(dir_object, "lat", tmp_object); + json_object_add_value_int(tmp_object, "min", min); + json_object_add_value_int(tmp_object, "max", max); + json_object_add_value_float(tmp_object, "mean", mean); + json_object_add_value_float(tmp_object, "stddev", dev); + if (ovals) + free(ovals); + + if (!calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) { + if (rs->agg[ddir]) { + p_of_agg = mean * 100 / (double) rs->agg[ddir]; + if (p_of_agg > 100.0) + p_of_agg = 100.0; + } + } else { + min = max = 0; + p_of_agg = mean = dev = 0.0; + } + json_object_add_value_int(dir_object, "bw_min", min); + json_object_add_value_int(dir_object, "bw_max", max); + json_object_add_value_float(dir_object, "bw_agg", mean); + json_object_add_value_float(dir_object, "bw_mean", mean); + json_object_add_value_float(dir_object, "bw_dev", dev); +} + static void show_thread_status_terse_v2(struct thread_stat *ts, struct group_run_stats *rs) { @@ -790,7 +894,7 @@ static void show_thread_status_terse_v3_v4(struct thread_stat *ts, log_info(";%3.2f%%", io_u_lat_m[i]); /* disk util stats, if any */ - show_disk_util(1); + show_disk_util(1, NULL); /* Additional output if continue_on_error set - default off*/ if (ts->continue_on_error) @@ -803,6 +907,90 @@ static void show_thread_status_terse_v3_v4(struct thread_stat *ts, log_info("\n"); } +static struct json_object *show_thread_status_json(struct thread_stat *ts, + struct group_run_stats *rs) +{ + struct json_object *root, *tmp; + double io_u_dist[FIO_IO_U_MAP_NR]; + double io_u_lat_u[FIO_IO_U_LAT_U_NR]; + double io_u_lat_m[FIO_IO_U_LAT_M_NR]; + double usr_cpu, sys_cpu; + int i; + + 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, "error", ts->error); + + add_ddir_status_json(ts, rs, DDIR_READ, root); + add_ddir_status_json(ts, rs, DDIR_WRITE, root); +// add_ddir_status_json(ts, rs, DDIR_TRIM, root); + + /* CPU Usage */ + if (ts->total_run_time) { + double runt = (double) ts->total_run_time; + + usr_cpu = (double) ts->usr_time * 100 / runt; + sys_cpu = (double) ts->sys_time * 100 / runt; + } else { + usr_cpu = 0; + sys_cpu = 0; + } + json_object_add_value_float(root, "usr_cpu", usr_cpu); + json_object_add_value_float(root, "sys_cpu", sys_cpu); + json_object_add_value_int(root, "ctx", ts->ctx); + json_object_add_value_int(root, "majf", ts->majf); + json_object_add_value_int(root, "minf", ts->minf); + + + /* Calc % distribution of IO depths, usecond, msecond latency */ + stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist); + stat_calc_lat_u(ts, io_u_lat_u); + stat_calc_lat_m(ts, io_u_lat_m); + + tmp = json_create_object(); + json_object_add_value_object(root, "iodepth_level", tmp); + /* Only show fixed 7 I/O depth levels*/ + for (i = 0; i < 7; i++) { + char name[20]; + if (i < 6) + snprintf(name, 19, "%d", 1 << i); + else + snprintf(name, 19, ">=%d", 1 << i); + json_object_add_value_float(tmp, (const char *)name, io_u_dist[i]); + } + + tmp = json_create_object(); + json_object_add_value_object(root, "latency_us", tmp); + /* Microsecond latency */ + for (i = 0; i < FIO_IO_U_LAT_U_NR; i++) { + const char *ranges[] = { "2", "4", "10", "20", "50", "100", + "250", "500", "750", "1000", }; + json_object_add_value_float(tmp, ranges[i], io_u_lat_u[i]); + } + /* Millisecond latency */ + tmp = json_create_object(); + json_object_add_value_object(root, "latency_ms", tmp); + for (i = 0; i < FIO_IO_U_LAT_M_NR; i++) { + const char *ranges[] = { "2", "4", "10", "20", "50", "100", + "250", "500", "750", "1000", "2000", + ">=2000", }; + json_object_add_value_float(tmp, ranges[i], io_u_lat_m[i]); + } + + /* Additional output if continue_on_error set - default off*/ + if (ts->continue_on_error) { + json_object_add_value_int(root, "total_err", ts->total_err_count); + json_object_add_value_int(root, "total_err", ts->first_error); + } + + /* Additional output if description is set */ + if (strlen(ts->description)) + json_object_add_value_string(root, "desc", ts->description); + + return root; +} + static void show_thread_status_terse(struct thread_stat *ts, struct group_run_stats *rs) { @@ -949,6 +1137,8 @@ void show_run_stats(void) struct thread_stat *threadstats, *ts; int i, j, nr_ts, last_ts, idx; int kb_base_warned = 0; + struct json_object *root = NULL; + struct json_array *array = NULL; runstats = malloc(sizeof(struct group_run_stats) * (groupid + 1)); @@ -1093,17 +1283,38 @@ void show_run_stats(void) if (!terse_output) log_info("\n"); + if (terse_output && terse_version == 4) { + root = json_create_object(); + json_object_add_value_string(root, "fio version", fio_version_string); + array = json_create_array(); + json_object_add_value_array(root, "jobs", array); + } + for (i = 0; i < nr_ts; i++) { ts = &threadstats[i]; rs = &runstats[ts->groupid]; if (is_backend) fio_server_send_ts(ts, rs); - else if (terse_output) - show_thread_status_terse(ts, rs); - else + else if (terse_output) { + if (terse_version != 4) + show_thread_status_terse(ts, rs); + else { + struct json_object *tmp = show_thread_status_json(ts, + rs); + json_array_add_value_object(array, tmp); + } + } else show_thread_status(ts, rs); } + if (terse_output && terse_version == 4) { + /* disk util stats, if any */ + show_disk_util(1, root); + + json_print_object(root); + log_info("\n"); + json_free_object(root); + } for (i = 0; i < groupid + 1; i++) { rs = &runstats[i]; @@ -1118,7 +1329,7 @@ void show_run_stats(void) if (is_backend) fio_server_send_du(); else if (!terse_output) - show_disk_util(0); + show_disk_util(0, NULL); free(runstats); free(threadstats); -- 2.25.1