Message ID | 20210210175118.19709-12-dwagner@suse.de |
---|---|
State | New |
Headers | show |
Series | Generate machine-readable output | expand |
On Wed, 10 Feb 2021, Daniel Wagner wrote: > Write the test results as JSON output to a file. This allows to > simplifies any parsing later on. > > Signed-off-by: Daniel Wagner <dwagner@suse.de> > --- > src/oslat/oslat.c | 97 +++++++++++++++++++++++++++++++++++++++-------- > 1 file changed, 82 insertions(+), 15 deletions(-) > > diff --git a/src/oslat/oslat.c b/src/oslat/oslat.c > index 9e6f70600830..987f2002985d 100644 > --- a/src/oslat/oslat.c > +++ b/src/oslat/oslat.c > @@ -29,7 +29,7 @@ > #include <numa.h> > #include <math.h> > #include <limits.h> > -#include <linux/unistd.h> > +#include <inttypes.h> > > #include <sys/prctl.h> > #include <sys/stat.h> > @@ -41,6 +41,8 @@ > #include <sys/mman.h> > #include <sys/syscall.h> > > +#include <linux/unistd.h> > + > #include "rt-utils.h" > #include "rt-numa.h" > #include "rt-error.h" > @@ -171,6 +173,7 @@ struct global { > uint64_t bias; > int single_preheat_thread; > int output_omit_zero_buckets; > + char outfile[MAX_PATH]; > > /* Mutable state. */ > volatile enum command cmd; > @@ -479,6 +482,39 @@ static void write_summary(struct thread *t) > printf("\n"); > } > > +static void write_summary_json(FILE *f, void *data) > +{ > + struct thread *t = data; > + int i, j, comma; > + > + fprintf(f, " \"num_threads\": %d,\n", g.n_threads); > + fprintf(f, " \"thread\": {\n"); > + for (i = 0; i < g.n_threads; ++i) { > + fprintf(f, " \"%u\": {\n", i); > + fprintf(f, " \"cpu\": %d,\n", t[i].core_i); > + fprintf(f, " \"freq\": %d,\n", t[i].cpu_mhz); > + fprintf(f, " \"min\": %" PRIu64 ",\n", t[i].minlat); > + fprintf(f, " \"avg\": %3lf,\n", t[i].average); > + fprintf(f, " \"max\": %" PRIu64 ",\n", t[i].maxlat); > + fprintf(f, " \"duration\": %.3f,\n", > + cycles_to_sec(&(t[i]), t[i].runtime)); > + fprintf(f, " \"histogram\": {"); > + for (j = 0, comma = 0; j < g.bucket_size; j++) { > + if (t[i].buckets[j] == 0) > + continue; > + fprintf(f, "%s", comma ? ",\n" : "\n"); > + fprintf(f, " \"%" PRIu64 "\": %" PRIu64, > + g.bias+j+1, t[i].buckets[j]); > + comma = 1; > + } > + if (comma) > + fprintf(f, "\n"); > + fprintf(f, " }\n"); > + fprintf(f, " }%s\n", i == g.n_threads - 1 ? "" : ","); > + } > + fprintf(f, " }\n"); > +} > + > static void run_expt(struct thread *threads, int runtime_secs) > { > int i; > @@ -533,6 +569,7 @@ static void usage(int error) > " NOTE: please make sure the CPU frequency on all testing cores\n" > " are locked before using this parmater. If you don't know how\n" > " to lock the freq then please don't use this parameter.\n" > + " --output=FILENAME write final results into FILENAME, JSON formatted\n" > "-T, --trace-threshold Stop the test when threshold triggered (in us),\n" > " print a marker in ftrace and stop ftrace too.\n" > "-v, --version Display the version of the software.\n" > @@ -557,34 +594,45 @@ static int workload_select(char *name) > return -1; > } > > +enum option_value { > + OPT_BUCKETSIZE=1, OPT_CPU_LIST, OPT_CPU_MAIN_THREAD, > + OPT_DURATION, OPT_RT_PRIO, OPT_HELP, OPT_TRACE_TH, > + OPT_WORKLOAD, OPT_WORKLOAD_MEM, OPT_BIAS, OPT_OUTPUT, > + OPT_SINGLE_PREHEAT, OPT_ZERO_OMIT, OPT_VERSION > + > +}; > + > /* Process commandline options */ > static void parse_options(int argc, char *argv[]) > { > while (1) { > + int option_index = 0; > static struct option options[] = { > - { "bucket-size", required_argument, NULL, 'b' }, > - { "cpu-list", required_argument, NULL, 'c' }, > - { "cpu-main-thread", required_argument, NULL, 'C'}, > - { "duration", required_argument, NULL, 'D' }, > - { "rtprio", required_argument, NULL, 'f' }, > - { "help", no_argument, NULL, 'h' }, > - { "trace-threshold", required_argument, NULL, 'T' }, > - { "workload", required_argument, NULL, 'w'}, > - { "workload-mem", required_argument, NULL, 'm'}, > - { "bias", no_argument, NULL, 'B'}, > - { "single-preheat", no_argument, NULL, 's'}, > - { "zero-omit", no_argument, NULL, 'u'}, > - { "version", no_argument, NULL, 'v'}, > + { "bucket-size",required_argument, NULL, OPT_BUCKETSIZE }, > + { "cpu-list", required_argument, NULL, OPT_CPU_LIST }, > + { "cpu-main-thread", required_argument, NULL, OPT_CPU_MAIN_THREAD}, > + { "duration", required_argument, NULL, OPT_DURATION }, > + { "rtprio", required_argument, NULL, OPT_RT_PRIO }, > + { "help", no_argument, NULL, OPT_HELP }, > + { "trace-threshold", required_argument, NULL, OPT_TRACE_TH }, > + { "workload", required_argument, NULL, OPT_WORKLOAD }, > + { "workload-mem", required_argument, NULL, OPT_WORKLOAD_MEM }, > + { "bias", no_argument, NULL, OPT_BIAS }, > + { "single-preheat", no_argument, NULL, OPT_SINGLE_PREHEAT }, > + { "output", required_argument, NULL, OPT_OUTPUT }, > + { "zero-omit", no_argument, NULL, OPT_ZERO_OMIT }, > + { "version", no_argument, NULL, OPT_VERSION }, > { NULL, 0, NULL, 0 }, > }; > int i, c = getopt_long(argc, argv, "b:Bc:C:D:f:hm:sw:T:vz", > - options, NULL); > + options, &option_index); > long ncores; > > if (c == -1) > break; > > switch (c) { > + case OPT_BUCKETSIZE: > case 'b': > g.bucket_size = strtol(optarg, NULL, 10); > if (g.bucket_size > 1024 || g.bucket_size <= 4) { > @@ -593,12 +641,15 @@ static void parse_options(int argc, char *argv[]) > exit(1); > } > break; > + case OPT_BIAS: > case 'B': > g.enable_bias = 1; > break; > + case OPT_CPU_LIST: > case 'c': > g.cpu_list = strdup(optarg); > break; > + case OPT_CPU_MAIN_THREAD: > case 'C': > ncores = sysconf(_SC_NPROCESSORS_CONF); > g.cpu_main_thread = strtol(optarg, NULL, 10); > @@ -608,6 +659,7 @@ static void parse_options(int argc, char *argv[]) > exit(1); > } > break; > + case OPT_DURATION: > case 'D': > g.runtime = parse_time_string(optarg); > if (!g.runtime) { > @@ -615,6 +667,7 @@ static void parse_options(int argc, char *argv[]) > exit(1); > } > break; > + case OPT_RT_PRIO: > case 'f': > g.rtprio = strtol(optarg, NULL, 10); > if (g.rtprio < 1 || g.rtprio > 99) { > @@ -622,6 +675,10 @@ static void parse_options(int argc, char *argv[]) > exit(1); > } > break; > + case OPT_OUTPUT: > + strncpy(g.outfile, optarg, strnlen(optarg, MAX_PATH-1)); > + break; > + case OPT_TRACE_TH: > case 'T': > g.trace_threshold = strtol(optarg, NULL, 10); > if (g.trace_threshold <= 0) { > @@ -630,6 +687,7 @@ static void parse_options(int argc, char *argv[]) > } > enable_trace_mark(); > break; > + case OPT_WORKLOAD: > case 'w': > if (workload_select(optarg)) { > printf("Unknown workload '%s'. Please choose from: ", optarg); > @@ -642,12 +700,14 @@ static void parse_options(int argc, char *argv[]) > exit(1); > } > break; > + case OPT_WORKLOAD_MEM: > case 'm': > if (parse_mem_string(optarg, &g.workload_mem_size)) { > printf("Unknown workload memory size '%s'.\n\n", optarg); > exit(1); > } > break; > + case OPT_SINGLE_PREHEAT: > case 's': > /* > * Only use one core for pre-heat. Then if --bias is used, the > @@ -655,6 +715,7 @@ static void parse_options(int argc, char *argv[]) > */ > g.single_preheat_thread = true; > break; > + case OPT_VERSION: > case 'v': > /* > * Because we always dump the version even before parsing options, > @@ -662,9 +723,11 @@ static void parse_options(int argc, char *argv[]) > */ > exit(0); > break; > + case OPT_ZERO_OMIT: > case 'z': > g.output_omit_zero_buckets = 1; > break; > + case OPT_HELP: > case 'h': > usage(0); > break; > @@ -781,6 +844,10 @@ int main(int argc, char *argv[]) > > write_summary(threads); > > + if (strlen(g.outfile) != 0) > + rt_write_json(g.outfile, argc, argv, > + write_summary_json, threads); > + > if (g.cpu_list) { > free(g.cpu_list); > g.cpu_list = NULL; > -- > 2.30.0 > > - Added a space after a common parse_options Signed-off-by: John Kacur <jkacur@redhat.com>
diff --git a/src/oslat/oslat.c b/src/oslat/oslat.c index 9e6f70600830..987f2002985d 100644 --- a/src/oslat/oslat.c +++ b/src/oslat/oslat.c @@ -29,7 +29,7 @@ #include <numa.h> #include <math.h> #include <limits.h> -#include <linux/unistd.h> +#include <inttypes.h> #include <sys/prctl.h> #include <sys/stat.h> @@ -41,6 +41,8 @@ #include <sys/mman.h> #include <sys/syscall.h> +#include <linux/unistd.h> + #include "rt-utils.h" #include "rt-numa.h" #include "rt-error.h" @@ -171,6 +173,7 @@ struct global { uint64_t bias; int single_preheat_thread; int output_omit_zero_buckets; + char outfile[MAX_PATH]; /* Mutable state. */ volatile enum command cmd; @@ -479,6 +482,39 @@ static void write_summary(struct thread *t) printf("\n"); } +static void write_summary_json(FILE *f, void *data) +{ + struct thread *t = data; + int i, j, comma; + + fprintf(f, " \"num_threads\": %d,\n", g.n_threads); + fprintf(f, " \"thread\": {\n"); + for (i = 0; i < g.n_threads; ++i) { + fprintf(f, " \"%u\": {\n", i); + fprintf(f, " \"cpu\": %d,\n", t[i].core_i); + fprintf(f, " \"freq\": %d,\n", t[i].cpu_mhz); + fprintf(f, " \"min\": %" PRIu64 ",\n", t[i].minlat); + fprintf(f, " \"avg\": %3lf,\n", t[i].average); + fprintf(f, " \"max\": %" PRIu64 ",\n", t[i].maxlat); + fprintf(f, " \"duration\": %.3f,\n", + cycles_to_sec(&(t[i]), t[i].runtime)); + fprintf(f, " \"histogram\": {"); + for (j = 0, comma = 0; j < g.bucket_size; j++) { + if (t[i].buckets[j] == 0) + continue; + fprintf(f, "%s", comma ? ",\n" : "\n"); + fprintf(f, " \"%" PRIu64 "\": %" PRIu64, + g.bias+j+1, t[i].buckets[j]); + comma = 1; + } + if (comma) + fprintf(f, "\n"); + fprintf(f, " }\n"); + fprintf(f, " }%s\n", i == g.n_threads - 1 ? "" : ","); + } + fprintf(f, " }\n"); +} + static void run_expt(struct thread *threads, int runtime_secs) { int i; @@ -533,6 +569,7 @@ static void usage(int error) " NOTE: please make sure the CPU frequency on all testing cores\n" " are locked before using this parmater. If you don't know how\n" " to lock the freq then please don't use this parameter.\n" + " --output=FILENAME write final results into FILENAME, JSON formatted\n" "-T, --trace-threshold Stop the test when threshold triggered (in us),\n" " print a marker in ftrace and stop ftrace too.\n" "-v, --version Display the version of the software.\n" @@ -557,34 +594,45 @@ static int workload_select(char *name) return -1; } +enum option_value { + OPT_BUCKETSIZE=1, OPT_CPU_LIST, OPT_CPU_MAIN_THREAD, + OPT_DURATION, OPT_RT_PRIO, OPT_HELP, OPT_TRACE_TH, + OPT_WORKLOAD, OPT_WORKLOAD_MEM, OPT_BIAS, OPT_OUTPUT, + OPT_SINGLE_PREHEAT, OPT_ZERO_OMIT, OPT_VERSION + +}; + /* Process commandline options */ static void parse_options(int argc, char *argv[]) { while (1) { + int option_index = 0; static struct option options[] = { - { "bucket-size", required_argument, NULL, 'b' }, - { "cpu-list", required_argument, NULL, 'c' }, - { "cpu-main-thread", required_argument, NULL, 'C'}, - { "duration", required_argument, NULL, 'D' }, - { "rtprio", required_argument, NULL, 'f' }, - { "help", no_argument, NULL, 'h' }, - { "trace-threshold", required_argument, NULL, 'T' }, - { "workload", required_argument, NULL, 'w'}, - { "workload-mem", required_argument, NULL, 'm'}, - { "bias", no_argument, NULL, 'B'}, - { "single-preheat", no_argument, NULL, 's'}, - { "zero-omit", no_argument, NULL, 'u'}, - { "version", no_argument, NULL, 'v'}, + { "bucket-size",required_argument, NULL, OPT_BUCKETSIZE }, + { "cpu-list", required_argument, NULL, OPT_CPU_LIST }, + { "cpu-main-thread", required_argument, NULL, OPT_CPU_MAIN_THREAD}, + { "duration", required_argument, NULL, OPT_DURATION }, + { "rtprio", required_argument, NULL, OPT_RT_PRIO }, + { "help", no_argument, NULL, OPT_HELP }, + { "trace-threshold", required_argument, NULL, OPT_TRACE_TH }, + { "workload", required_argument, NULL, OPT_WORKLOAD }, + { "workload-mem", required_argument, NULL, OPT_WORKLOAD_MEM }, + { "bias", no_argument, NULL, OPT_BIAS }, + { "single-preheat", no_argument, NULL, OPT_SINGLE_PREHEAT }, + { "output", required_argument, NULL, OPT_OUTPUT }, + { "zero-omit", no_argument, NULL, OPT_ZERO_OMIT }, + { "version", no_argument, NULL, OPT_VERSION }, { NULL, 0, NULL, 0 }, }; int i, c = getopt_long(argc, argv, "b:Bc:C:D:f:hm:sw:T:vz", - options, NULL); + options, &option_index); long ncores; if (c == -1) break; switch (c) { + case OPT_BUCKETSIZE: case 'b': g.bucket_size = strtol(optarg, NULL, 10); if (g.bucket_size > 1024 || g.bucket_size <= 4) { @@ -593,12 +641,15 @@ static void parse_options(int argc, char *argv[]) exit(1); } break; + case OPT_BIAS: case 'B': g.enable_bias = 1; break; + case OPT_CPU_LIST: case 'c': g.cpu_list = strdup(optarg); break; + case OPT_CPU_MAIN_THREAD: case 'C': ncores = sysconf(_SC_NPROCESSORS_CONF); g.cpu_main_thread = strtol(optarg, NULL, 10); @@ -608,6 +659,7 @@ static void parse_options(int argc, char *argv[]) exit(1); } break; + case OPT_DURATION: case 'D': g.runtime = parse_time_string(optarg); if (!g.runtime) { @@ -615,6 +667,7 @@ static void parse_options(int argc, char *argv[]) exit(1); } break; + case OPT_RT_PRIO: case 'f': g.rtprio = strtol(optarg, NULL, 10); if (g.rtprio < 1 || g.rtprio > 99) { @@ -622,6 +675,10 @@ static void parse_options(int argc, char *argv[]) exit(1); } break; + case OPT_OUTPUT: + strncpy(g.outfile, optarg, strnlen(optarg, MAX_PATH-1)); + break; + case OPT_TRACE_TH: case 'T': g.trace_threshold = strtol(optarg, NULL, 10); if (g.trace_threshold <= 0) { @@ -630,6 +687,7 @@ static void parse_options(int argc, char *argv[]) } enable_trace_mark(); break; + case OPT_WORKLOAD: case 'w': if (workload_select(optarg)) { printf("Unknown workload '%s'. Please choose from: ", optarg); @@ -642,12 +700,14 @@ static void parse_options(int argc, char *argv[]) exit(1); } break; + case OPT_WORKLOAD_MEM: case 'm': if (parse_mem_string(optarg, &g.workload_mem_size)) { printf("Unknown workload memory size '%s'.\n\n", optarg); exit(1); } break; + case OPT_SINGLE_PREHEAT: case 's': /* * Only use one core for pre-heat. Then if --bias is used, the @@ -655,6 +715,7 @@ static void parse_options(int argc, char *argv[]) */ g.single_preheat_thread = true; break; + case OPT_VERSION: case 'v': /* * Because we always dump the version even before parsing options, @@ -662,9 +723,11 @@ static void parse_options(int argc, char *argv[]) */ exit(0); break; + case OPT_ZERO_OMIT: case 'z': g.output_omit_zero_buckets = 1; break; + case OPT_HELP: case 'h': usage(0); break; @@ -781,6 +844,10 @@ int main(int argc, char *argv[]) write_summary(threads); + if (strlen(g.outfile) != 0) + rt_write_json(g.outfile, argc, argv, + write_summary_json, threads); + if (g.cpu_list) { free(g.cpu_list); g.cpu_list = NULL;
Write the test results as JSON output to a file. This allows to simplifies any parsing later on. Signed-off-by: Daniel Wagner <dwagner@suse.de> --- src/oslat/oslat.c | 97 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 82 insertions(+), 15 deletions(-)