On 12/09/2017 16:16, David Hildenbrand wrote: > I want to use this in another file, so instead of replicating, factoring > it out feels like the right thing to do. > > Let's directly provide it for all architectures, they just have > to implement get_time_ms() in asm/time.h to use it. > > Signed-off-by: David Hildenbrand <david@xxxxxxxxxx> > --- > > The next step would be allowing to add custom parameters to a test. > (defining them similar to the test cases). > > Any other requests/nice to have? Does this make sense? It does, but we have at least this, vmx.c (v1 and v2) and vmexit.c, with not exactly overlapping usecases and command-line syntax. Would it make sense to have an API that is not home-grown, e.g. a subset of GTest? The command-line arguments could be globs (like GTest's "-p"). Paolo > lib/s390x/asm/time.h | 24 ++++++++++++ > lib/testrun.c | 89 ++++++++++++++++++++++++++++++++++++++++++ > lib/testrun.h | 41 ++++++++++++++++++++ > s390x/Makefile | 1 + > s390x/intercept.c | 106 ++++++++------------------------------------------- > 5 files changed, 170 insertions(+), 91 deletions(-) > create mode 100644 lib/s390x/asm/time.h > create mode 100644 lib/testrun.c > create mode 100644 lib/testrun.h > > diff --git a/lib/s390x/asm/time.h b/lib/s390x/asm/time.h > new file mode 100644 > index 0000000..856facd > --- /dev/null > +++ b/lib/s390x/asm/time.h > @@ -0,0 +1,24 @@ > +/* > + * Copyright (c) 2017 Red Hat Inc > + * > + * Authors: > + * Thomas Huth <thuth@xxxxxxxxxx> > + * David Hildenbrand <david@xxxxxxxxxx> > + * > + * This code is free software; you can redistribute it and/or modify it > + * under the terms of the GNU Library General Public License version 2. > + */ > +#ifndef __ASMS390X_TIME_H > +#define __ASMS390X_TIME_H > + > +static inline uint64_t get_clock_ms(void) > +{ > + uint64_t clk; > + > + asm volatile(" stck %0 " : : "Q"(clk) : "memory"); > + > + /* Bit 51 is incrememented each microsecond */ > + return (clk >> (63 - 51)) / 1000; > +} > + > +#endif > diff --git a/lib/testrun.c b/lib/testrun.c > new file mode 100644 > index 0000000..6034767 > --- /dev/null > +++ b/lib/testrun.c > @@ -0,0 +1,89 @@ > +/* > + * Utility functions for running/configuring a set of tests > + * > + * Copyright (c) 2017 Red Hat Inc > + * > + * Authors: > + * Thomas Huth <thuth@xxxxxxxxxx> > + * David Hildenbrand <david@xxxxxxxxxx> > + * > + * This code is free software; you can redistribute it and/or modify it > + * under the terms of the GNU Library General Public License version 2. > + */ > + > +#include <testrun.h> > +#include <asm/time.h> > + > +void parse_test_args(const int argc, const char **argv, struct test *test) > +{ > + bool run_all = true; > + int i, ti; > + > + test->iterations = 0; > + test->time_to_run = 0; > + > + for (i = 1; i < argc; i++) { > + if (!strcmp("-i", argv[i])) { > + i++; > + if (i >= argc) > + report_abort("-i needs a parameter"); > + test->iterations = atol(argv[i]); > + } else if (!strcmp("-t", argv[i])) { > + i++; > + if (i >= argc) > + report_abort("-t needs a parameter"); > + test->time_to_run = atol(argv[i]); > + } else for (ti = 0; test->test_cases[ti].name != NULL; ti++) { > + if (!strcmp(test->test_cases[ti].name, argv[i])) { > + run_all = false; > + test->test_cases[ti].run_it = true; > + break; > + } else if (test->test_cases[ti + 1].name == NULL) { > + report_abort("Unsupported parameter '%s'", > + argv[i]); > + } > + } > + } > + > + if (test->iterations == 0 && test->time_to_run == 0) > + test->iterations = 1; > + > + if ((test->iterations > 1 || test->time_to_run) && > + !test->multirun_supported) > + report_abort("Test does not support multiruns."); > + > + if (run_all) { > + for (ti = 0; test->test_cases[ti].name != NULL; ti++) > + test->test_cases[ti].run_it = true; > + } > +} > + > +int run_test(struct test *test) > +{ > + unsigned long iterations = test->iterations; > + unsigned long time_to_run = test->time_to_run; > + uint64_t startclk; > + int ti; > + > + report_prefix_push(test->name); > + startclk = get_clock_ms(); > + for (;;) { > + for (ti = 0; test->test_cases[ti].name != NULL; ti++) { > + report_prefix_push(test->test_cases[ti].name); > + if (test->test_cases[ti].run_it) > + test->test_cases[ti].func(); > + report_prefix_pop(); > + } > + if (iterations) { > + iterations -= 1; > + if (iterations == 0) > + break; > + } > + if (time_to_run) { > + if (get_clock_ms() - startclk > time_to_run) > + break; > + } > + } > + report_prefix_pop(); > + return report_summary(); > +} > diff --git a/lib/testrun.h b/lib/testrun.h > new file mode 100644 > index 0000000..8f9a2d7 > --- /dev/null > +++ b/lib/testrun.h > @@ -0,0 +1,41 @@ > +/* > + * Utility functions for running/configuring a set of test cases > + * > + * Copyright (c) 2017 Red Hat Inc > + * > + * Authors: > + * Thomas Huth <thuth@xxxxxxxxxx> > + * David Hildenbrand <david@xxxxxxxxxx> > + * > + * This code is free software; you can redistribute it and/or modify it > + * under the terms of the GNU Library General Public License version 2. > + */ > +#ifndef __TESTRUN_H > +#define __TESTRUN_H > + > +#include <libcflat.h> > + > +struct test_case { > + const char *name; > + void (*func)(void); > + /* initialized by parse_test_args() */ > + bool run_it; > +}; > + > +#define DEFINE_TEST_CASE(_name, _func) { _name, _func, false } > +#define DEFINE_TEST_CASE_END_OF_LIST() { } > + > +struct test { > + /* to be initialized by the test */ > + const char *name; > + bool multirun_supported; > + struct test_case *test_cases; > + /* initialized by parse_test_args() */ > + unsigned long iterations; > + unsigned long time_to_run; > +}; > + > +void parse_test_args(int argc, const char **argv, struct test *test); > +int run_test(struct test *test); > + > +#endif /* __TESTRUN_H */ > diff --git a/s390x/Makefile b/s390x/Makefile > index bc099da..e174895 100644 > --- a/s390x/Makefile > +++ b/s390x/Makefile > @@ -21,6 +21,7 @@ include $(SRCDIR)/scripts/asm-offsets.mak > > cflatobjs += lib/util.o > cflatobjs += lib/alloc.o > +cflatobjs += lib/testrun.o > cflatobjs += lib/s390x/io.o > cflatobjs += lib/s390x/stack.o > cflatobjs += lib/s390x/sclp-ascii.o > diff --git a/s390x/intercept.c b/s390x/intercept.c > index 59e5fca..15e50f6 100644 > --- a/s390x/intercept.c > +++ b/s390x/intercept.c > @@ -10,15 +10,13 @@ > * under the terms of the GNU Library General Public License version 2. > */ > #include <libcflat.h> > +#include <testrun.h> > #include <asm/asm-offsets.h> > #include <asm/interrupt.h> > #include <asm/page.h> > > static uint8_t pagebuf[PAGE_SIZE * 2] __attribute__((aligned(PAGE_SIZE * 2))); > > -static unsigned long nr_iterations; > -static unsigned long time_to_run; > - > /* Test the STORE PREFIX instruction */ > static void test_stpx(void) > { > @@ -159,95 +157,21 @@ static void test_testblock(void) > check_pgm_int_code(PGM_INT_CODE_ADDRESSING); > } > > -static uint64_t get_clock_ms(void) > -{ > - uint64_t clk; > - > - asm volatile(" stck %0 " : : "Q"(clk) : "memory"); > - > - /* Bit 51 is incrememented each microsecond */ > - return (clk >> (63 - 51)) / 1000; > -} > - > -struct { > - const char *name; > - void (*func)(void); > - bool run_it; > -} tests[] = { > - { "stpx", test_stpx, false }, > - { "spx", test_spx, false }, > - { "stap", test_stap, false }, > - { "stidp", test_stidp, false }, > - { "testblock", test_testblock, false }, > - { NULL, NULL, false } > -}; > - > -static void parse_intercept_test_args(int argc, char **argv) > -{ > - int i, ti; > - bool run_all = true; > - > - for (i = 1; i < argc; i++) { > - if (!strcmp("-i", argv[i])) { > - i++; > - if (i >= argc) > - report_abort("-i needs a parameter"); > - nr_iterations = atol(argv[i]); > - } else if (!strcmp("-t", argv[i])) { > - i++; > - if (i >= argc) > - report_abort("-t needs a parameter"); > - time_to_run = atol(argv[i]); > - } else for (ti = 0; tests[ti].name != NULL; ti++) { > - if (!strcmp(tests[ti].name, argv[i])) { > - run_all = false; > - tests[ti].run_it = true; > - break; > - } else if (tests[ti + 1].name == NULL) { > - report_abort("Unsupported parameter '%s'", > - argv[i]); > - } > - } > - } > - > - if (run_all) { > - for (ti = 0; tests[ti].name != NULL; ti++) > - tests[ti].run_it = true; > +static struct test test = { > + .name = "intercept", > + .multirun_supported = true, > + .test_cases = (struct test_case[]) { > + DEFINE_TEST_CASE("stpx", test_stpx), > + DEFINE_TEST_CASE("spx", test_spx), > + DEFINE_TEST_CASE("stap", test_stap), > + DEFINE_TEST_CASE("stidp", test_stidp), > + DEFINE_TEST_CASE("testblock", test_testblock), > + DEFINE_TEST_CASE_END_OF_LIST() > } > -} > +}; > > -int main(int argc, char **argv) > +int main(int argc, const char **argv) > { > - uint64_t startclk; > - int ti; > - > - parse_intercept_test_args(argc, argv); > - > - if (nr_iterations == 0 && time_to_run == 0) > - nr_iterations = 1; > - > - report_prefix_push("intercept"); > - > - startclk = get_clock_ms(); > - for (;;) { > - for (ti = 0; tests[ti].name != NULL; ti++) { > - report_prefix_push(tests[ti].name); > - if (tests[ti].run_it) > - tests[ti].func(); > - report_prefix_pop(); > - } > - if (nr_iterations) { > - nr_iterations -= 1; > - if (nr_iterations == 0) > - break; > - } > - if (time_to_run) { > - if (get_clock_ms() - startclk > time_to_run) > - break; > - } > - } > - > - report_prefix_pop(); > - > - return report_summary(); > + parse_test_args(argc, argv, &test); > + return run_test(&test); > } >