Message ID | 20200421174456.28663-1-lamm@linux.ibm.com |
---|---|
State | New |
Headers | show |
Series | [v9] Fix time/tst-cpuclock1 intermitent failures | expand |
PING. Quoting Lucas A. M. Magalhaes via Libc-alpha (2020-04-21 14:44:56) > This test fails intermittently in systems with heavy load as > CLOCK_PROCESS_CPUTIME_ID is subject to scheduler pressure. Thus the > test boundaries were relaxed to keep it from failing on such systems. > > A refactor of the spent time checking was made with some support > functions. With the advantage to representing time jitter in percent > of the target. > > The values used by the test boundaries are all empirical. > > --- > > Hi Carlos, > I also got skeptical about my dataset so I genegerated another one. > That .4 boundry was indeed too low. Also, the support/tst-timespec was > not actually running any tests because of a lazy bug, sorry about that. > Thanks for taking the time for reviewing this. > > changes on V9: > Add new testcases > Fix support/tst-timespec do_run > Add comments > Change boundaries > > changes on V8: > Add support_timespec_ns > Add more tests > > This list of changes where getting to long. > --- > support/Makefile | 1 + > support/timespec.c | 64 +++++++++ > support/timespec.h | 8 ++ > support/tst-timespec.c | 320 +++++++++++++++++++++++++++++++++++++++++ > time/tst-cpuclock1.c | 52 +++---- > 5 files changed, 415 insertions(+), 30 deletions(-) > create mode 100644 support/tst-timespec.c > > diff --git a/support/Makefile b/support/Makefile > index 6e38b87ebe..cacaac96a5 100644 > --- a/support/Makefile > +++ b/support/Makefile > @@ -233,6 +233,7 @@ tests = \ > tst-test_compare \ > tst-test_compare_blob \ > tst-test_compare_string \ > + tst-timespec \ > tst-xreadlink \ > tst-xsigstack \ > > diff --git a/support/timespec.c b/support/timespec.c > index ea6b947546..103beafdc4 100644 > --- a/support/timespec.c > +++ b/support/timespec.c > @@ -19,6 +19,8 @@ > #include <support/timespec.h> > #include <stdio.h> > #include <stdint.h> > +#include <assert.h> > +#include <intprops.h> > > void > test_timespec_before_impl (const char *file, int line, > @@ -57,3 +59,65 @@ test_timespec_equal_or_after_impl (const char *file, int line, > (intmax_t) diff.tv_sec, (intmax_t) diff.tv_nsec); > } > } > + > +/* Convert TIME to nanoseconds stored in shrinked in a long. > + Returns long maximum or minimum if the conversion overflows > + or underflows, respectively. */ > +long > +support_timespec_ns (struct timespec time) > +{ > + long time_ns; > + if (INT_MULTIPLY_WRAPV(time.tv_sec, TIMESPEC_HZ, &time_ns)) > + { > + return (time.tv_sec < 0) ? TYPE_MINIMUM(long): TYPE_MAXIMUM(long); > + } > + if (INT_ADD_WRAPV(time_ns, time.tv_nsec, &time_ns)) > + { > + return (time.tv_nsec < 0) ? TYPE_MINIMUM(long): TYPE_MAXIMUM(long); > + } > + return time_ns; > +} > + > +/* Returns t normalized timespec with .tv_nsec < TIMESPEC_HZ > + and the whole seconds added to .tv_sec. If an overflow or > + underflow occurs the values are clamped to its maximum or > + minimum respectively. */ > +struct timespec > +support_timespec_normalize (struct timespec time) > +{ > + struct timespec norm; > + if (INT_ADD_WRAPV (time.tv_sec, (time.tv_nsec / TIMESPEC_HZ), &norm.tv_sec)) > + { > + norm.tv_sec = (time.tv_nsec < 0) ? TYPE_MINIMUM (time_t): TYPE_MAXIMUM (time_t); > + norm.tv_nsec = (time.tv_nsec < 0) ? -1 * (TIMESPEC_HZ - 1) : TIMESPEC_HZ - 1; > + return norm; > + } > + norm.tv_nsec = time.tv_nsec % TIMESPEC_HZ; > + return norm; > +} > + > +/* Returns TRUE if the observed time is within the given percentage > + bounds of the expected time, and FALSE otherwise. > + For example the call > + > + support_timespec_check_in_range(expected, observed, .5, 1.2); > + > + will check if > + > + .5 <= observed/expected <= 1.2 > + > + In other words it will check if observed time is within 50% to > + 120% of the expected time. */ > +int > +support_timespec_check_in_range (struct timespec expected, struct timespec observed, > + double lower_bound, double upper_bound) > +{ > + assert (upper_bound >= lower_bound); > + long expected_norm, observed_norm; > + expected_norm = support_timespec_ns (expected); > + /* Don't divide by zero */ > + assert(expected_norm != 0); > + observed_norm = support_timespec_ns (observed); > + double ratio = (double)observed_norm / expected_norm; > + return (lower_bound <= ratio && ratio <= upper_bound); > +} > diff --git a/support/timespec.h b/support/timespec.h > index c5852dfe75..fd5466745d 100644 > --- a/support/timespec.h > +++ b/support/timespec.h > @@ -48,6 +48,14 @@ void test_timespec_equal_or_after_impl (const char *file, int line, > const struct timespec left, > const struct timespec right); > > +long support_timespec_ns (struct timespec time); > + > +struct timespec support_timespec_normalize (struct timespec time); > + > +int support_timespec_check_in_range (struct timespec expected, struct timespec observed, > + double lower_bound, double upper_bound); > + > + > /* Check that the timespec on the left represents a time before the > time on the right. */ > #define TEST_TIMESPEC_BEFORE(left, right) \ > diff --git a/support/tst-timespec.c b/support/tst-timespec.c > new file mode 100644 > index 0000000000..b421b219fd > --- /dev/null > +++ b/support/tst-timespec.c > @@ -0,0 +1,320 @@ > +/* Test for support_timespec_check_in_range function. > + Copyright (C) 2020 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + <https://www.gnu.org/licenses/>. */ > + > +#include <support/timespec.h> > +#include <support/check.h> > +#include <limits.h> > + > +#define TIMESPEC_HZ 1000000000 > + > +struct timespec_ns_test_case > +{ > + struct timespec time; > + long time_ns; > +}; > + > +struct timespec_norm_test_case > +{ > + struct timespec time; > + struct timespec norm; > +}; > + > +struct timespec_test_case > +{ > + struct timespec expected; > + struct timespec observed; > + double upper_bound; > + double lower_bound; > + int result; > +}; > + > +/* Test cases for timespec_ns */ > +struct timespec_ns_test_case ns_cases[] = { > + {.time = {.tv_sec = 0, .tv_nsec = 0}, > + .time_ns = 0, > + }, > + {.time = {.tv_sec = 0, .tv_nsec = 1}, > + .time_ns = 1, > + }, > + {.time = {.tv_sec = 1, .tv_nsec = 0}, > + .time_ns = TIMESPEC_HZ, > + }, > + {.time = {.tv_sec = 1, .tv_nsec = 1}, > + .time_ns = TIMESPEC_HZ + 1, > + }, > + {.time = {.tv_sec = 0, .tv_nsec = -1}, > + .time_ns = -1, > + }, > + {.time = {.tv_sec = -1, .tv_nsec = 0}, > + .time_ns = -TIMESPEC_HZ, > + }, > + {.time = {.tv_sec = -1, .tv_nsec = -1}, > + .time_ns = -TIMESPEC_HZ - 1, > + }, > + {.time = {.tv_sec = 1, .tv_nsec = -1}, > + .time_ns = TIMESPEC_HZ - 1, > + }, > + {.time = {.tv_sec = -1, .tv_nsec = 1}, > + .time_ns = -TIMESPEC_HZ + 1, > + }, > + // Overflow bondary by 2 > + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ, .tv_nsec = LONG_MAX%TIMESPEC_HZ - 1}, > + .time_ns = LONG_MAX - 1, > + }, > + // Overflow bondary > + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ, .tv_nsec = LONG_MAX%TIMESPEC_HZ}, > + .time_ns = LONG_MAX, > + }, > + // Underflow bondary by 1 > + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ, .tv_nsec = LONG_MIN%TIMESPEC_HZ + 1}, > + .time_ns = LONG_MIN + 1, > + }, > + // Underflow bondary > + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ, .tv_nsec = LONG_MIN%TIMESPEC_HZ}, > + .time_ns = LONG_MIN, > + }, > + // Multiplication overflow > + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ + 1, .tv_nsec = 1}, > + .time_ns = LONG_MAX, > + }, > + // Multiplication underflow > + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ - 1, .tv_nsec = -1}, > + .time_ns = LONG_MIN, > + }, > + // Sum overflows > + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ, .tv_nsec = LONG_MAX%TIMESPEC_HZ + 1}, > + .time_ns = LONG_MAX, > + }, > + // Sum underflow > + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ, .tv_nsec = LONG_MIN%TIMESPEC_HZ - 1}, > + .time_ns = LONG_MIN, > + } > +}; > + > +/* Test cases for timespec_norm */ > +struct timespec_norm_test_case norm_cases[] = { > + // Positive cases > + {.time = {.tv_sec = 0, .tv_nsec = 0}, > + .norm = {.tv_sec = 0, .tv_nsec = 0} > + }, > + {.time = {.tv_sec = 1, .tv_nsec = 0}, > + .norm = {.tv_sec = 1, .tv_nsec = 0} > + }, > + {.time = {.tv_sec = 0, .tv_nsec = 1}, > + .norm = {.tv_sec = 0, .tv_nsec = 1} > + }, > + {.time = {.tv_sec = 0, .tv_nsec = TIMESPEC_HZ}, > + .norm = {.tv_sec = 1, .tv_nsec = 0} > + }, > + {.time = {.tv_sec = 0, .tv_nsec = TIMESPEC_HZ + 1}, > + .norm = {.tv_sec = 1, .tv_nsec = 1} > + }, > + {.time = {.tv_sec = 1, .tv_nsec = TIMESPEC_HZ}, > + .norm = {.tv_sec = 2, .tv_nsec = 0} > + }, > + {.time = {.tv_sec = 1, .tv_nsec = TIMESPEC_HZ + 1}, > + .norm = {.tv_sec = 2, .tv_nsec = 1} > + }, > + // Negative cases > + {.time = {.tv_sec = 0, .tv_nsec = -TIMESPEC_HZ}, > + .norm = {.tv_sec = -1, .tv_nsec = 0} > + }, > + {.time = {.tv_sec = 0, .tv_nsec = -TIMESPEC_HZ - 1}, > + .norm = {.tv_sec = -1, .tv_nsec = -1} > + }, > + {.time = {.tv_sec = -1, .tv_nsec = -TIMESPEC_HZ}, > + .norm = {.tv_sec = -2, .tv_nsec = 0} > + }, > + {.time = {.tv_sec = -1, .tv_nsec = -TIMESPEC_HZ - 1}, > + .norm = {.tv_sec = -2, .tv_nsec = -1} > + }, > + // Overflow bondary by 2 > + {.time = {.tv_sec = LONG_MAX - 2, .tv_nsec = TIMESPEC_HZ + 1}, > + .norm = {.tv_sec = LONG_MAX - 1, 1}, > + }, > + // Overflow bondary by 1 > + {.time = {.tv_sec = LONG_MAX - 1, .tv_nsec = TIMESPEC_HZ + 1}, > + .norm = {.tv_sec = LONG_MAX, .tv_nsec = 1}, > + }, > + // Underflow bondary by 2 > + {.time = {.tv_sec = LONG_MIN + 2, .tv_nsec = -TIMESPEC_HZ - 1}, > + .norm = {.tv_sec = LONG_MIN + 1, -1}, > + }, > + // Underflow bondary by 1 > + {.time = {.tv_sec = LONG_MIN + 1, .tv_nsec = -TIMESPEC_HZ - 1}, > + .norm = {.tv_sec = LONG_MIN, .tv_nsec = -1}, > + }, > + // SUM overflow > + {.time = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > + .norm = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ - 1}, > + }, > + // SUM underflow > + {.time = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > + .norm = {.tv_sec = LONG_MIN, .tv_nsec = -1 * (TIMESPEC_HZ - 1)}, > + } > +}; > + > +/* Test cases for timespec_check_in_range */ > +struct timespec_test_case check_cases[] = { > + // 0 - In range > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > + .observed = {.tv_sec = 1, .tv_nsec = 0}, > + .upper_bound = 1, .lower_bound = 1, .result = 1, > + }, > + // 1 - Out of range > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > + .observed = {.tv_sec = 2, .tv_nsec = 0}, > + .upper_bound = 1, .lower_bound = 1, .result = 0, > + }, > + // 2 - Upper Bound > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > + .observed = {.tv_sec = 2, .tv_nsec = 0}, > + .upper_bound = 2, .lower_bound = 1, .result = 1, > + }, > + // 3 - Lower Bound > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > + .observed = {.tv_sec = 0, .tv_nsec = 0}, > + .upper_bound = 1, .lower_bound = 0, .result = 1, > + }, > + // 4 - Out of range by nanosecs > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > + .observed = {.tv_sec = 1, .tv_nsec = 500}, > + .upper_bound = 1, .lower_bound = 1, .result = 0, > + }, > + // 5 - In range by nanosecs > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > + .observed = {.tv_sec = 1, .tv_nsec = 50000}, > + .upper_bound = 1.3, .lower_bound = 1, .result = 1, > + }, > + // 6 - Big nanosecs > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > + .observed = {.tv_sec = 0, .tv_nsec = 4000000}, > + .upper_bound = 1, .lower_bound = .001, .result = 1, > + }, > + // 7 - In range Negative values > + {.expected = {.tv_sec = -1, .tv_nsec = 0}, > + .observed = {.tv_sec = -1, .tv_nsec = 0}, > + .upper_bound = 1, .lower_bound = 1, .result = 1, > + }, > + // 8 - Out of range Negative values > + {.expected = {.tv_sec = -1, .tv_nsec = 0}, > + .observed = {.tv_sec = -1, .tv_nsec = 0}, > + .upper_bound = -1, .lower_bound = -1, .result = 0, > + }, > + // 9 - Negative values with negative nanosecs > + {.expected = {.tv_sec = -1, .tv_nsec = 0}, > + .observed = {.tv_sec = -1, .tv_nsec = -2000}, > + .upper_bound = 1, .lower_bound = 1, .result = 0, > + }, > + // 10 - Strict bounds > + {.expected = {.tv_sec = -1, .tv_nsec = 0}, > + .observed = {.tv_sec = -1, .tv_nsec = -20000}, > + .upper_bound = 1.00002, .lower_bound = 1.0000191, .result = 1, > + }, > + // 11 - Strict bounds with loose upper bound > + {.expected = {.tv_sec = 1, .tv_nsec = 20000}, > + .observed = {.tv_sec = 1, .tv_nsec = 30000}, > + .upper_bound = 1.0000100000, .lower_bound = 1.0000099998, .result = 1, > + }, > + // 12 - Strict bounds with loose lower bound > + {.expected = {.tv_sec = 1, .tv_nsec = 20000}, > + .observed = {.tv_sec = 1, .tv_nsec = 30000}, > + .upper_bound = 1.0000099999, .lower_bound = 1.00000999979, .result = 1, > + }, > + // 13 - Strict bounds highest precision > + {.expected = {.tv_sec = 1, .tv_nsec = 20000}, > + .observed = {.tv_sec = 1, .tv_nsec = 30000}, > + .upper_bound = 1.00000999980001, .lower_bound = 1.00000999979999, .result = 1, > + }, > + /* Maximum/Minimum long values */ > + // 14 > + {.expected = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ - 1}, > + .observed = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ - 2}, > + .upper_bound = 1, .lower_bound = .9, .result = 1, > + }, > + // 15 - support_timespec_ns overflow > + {.expected = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > + .observed = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > + .upper_bound = 1, .lower_bound = 1, .result = 1, > + }, > + // 16 - support_timespec_ns overflow + underflow > + {.expected = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > + .observed = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > + .upper_bound = 1, .lower_bound = 1, .result = 0, > + }, > + // 17 - support_timespec_ns underflow > + {.expected = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > + .observed = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > + .upper_bound = 1, .lower_bound = 1, .result = 1, > + }, > + // 18 - support_timespec_ns underflow + overflow > + {.expected = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > + .observed = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > + .upper_bound = 1, .lower_bound = 1, .result = 0, > + }, > + // 19 - Biggest division > + {.expected = {.tv_sec = LONG_MAX / TIMESPEC_HZ , .tv_nsec = TIMESPEC_HZ - 1}, > + .observed = {.tv_sec = 0, .tv_nsec = 1}, > + .upper_bound = 1, .lower_bound = 1.0842021724855044e-19, .result = 1, > + }, > + // 20 - Lowest division > + {.expected = {.tv_sec = 0, .tv_nsec = 1}, > + .observed = {.tv_sec = LONG_MAX / TIMESPEC_HZ , .tv_nsec = TIMESPEC_HZ - 1}, > + .upper_bound = LONG_MAX, .lower_bound = 1, .result = 1, > + }, > +}; > + > +static int > +do_test (void) > +{ > + int i = 0; > + int ntests = sizeof (ns_cases) / sizeof (ns_cases[0]); > + > + printf("Testing support_timespec_ns\n"); > + for (i = 0; i < ntests; i++) > + { > + TEST_COMPARE (support_timespec_ns (ns_cases[i].time), > + ns_cases[i].time_ns); > + } > + > + ntests = sizeof (norm_cases) / sizeof (norm_cases[0]); > + struct timespec result; > + printf("Testing support_timespec_normalize\n"); > + for (i = 0; i < ntests; i++) > + { > + result = support_timespec_normalize (norm_cases[i].time); > + TEST_COMPARE (norm_cases[i].norm.tv_sec, result.tv_sec); > + TEST_COMPARE (norm_cases[i].norm.tv_nsec, result.tv_nsec); > + } > + > + ntests = sizeof (check_cases) / sizeof (check_cases[0]); > + printf("Testing support_timespec_check_in_range\n"); > + for (i = 0; i < ntests; i++) > + { > + /* Its hard to find which test failed with just the TEST_COMPARE report. > + So here we print every running testcase as well. */ > + printf("Test case %d\n", i); > + TEST_COMPARE (support_timespec_check_in_range > + (check_cases[i].expected, check_cases[i].observed, > + check_cases[i].lower_bound, > + check_cases[i].upper_bound), check_cases[i].result); > + } > + return 0; > +} > + > +#include <support/test-driver.c> > diff --git a/time/tst-cpuclock1.c b/time/tst-cpuclock1.c > index 0120906f23..1ac611a92b 100644 > --- a/time/tst-cpuclock1.c > +++ b/time/tst-cpuclock1.c > @@ -26,6 +26,7 @@ > #include <signal.h> > #include <stdint.h> > #include <sys/wait.h> > +#include <support/timespec.h> > > /* This function is intended to rack up both user and system time. */ > static void > @@ -155,16 +156,12 @@ do_test (void) > printf ("live PID %d after sleep => %ju.%.9ju\n", > child, (uintmax_t) after.tv_sec, (uintmax_t) after.tv_nsec); > > - struct timespec diff = { .tv_sec = after.tv_sec - before.tv_sec, > - .tv_nsec = after.tv_nsec - before.tv_nsec }; > - if (diff.tv_nsec < 0) > - { > - --diff.tv_sec; > - diff.tv_nsec += 1000000000; > - } > - if (diff.tv_sec != 0 > - || diff.tv_nsec > 600000000 > - || diff.tv_nsec < 100000000) > + /* The bound values are empirically defined by testing this code over high cpu > + usage and different nice values. Of all the values we keep the 90th > + percentile of values and use those values for our testing allowed range. */ > + struct timespec diff = timespec_sub (support_timespec_normalize (after), > + support_timespec_normalize (before)); > + if (!support_timespec_check_in_range (sleeptime, diff, .9, 1.1)) > { > printf ("before - after %ju.%.9ju outside reasonable range\n", > (uintmax_t) diff.tv_sec, (uintmax_t) diff.tv_nsec); > @@ -194,19 +191,16 @@ do_test (void) > } > else > { > - struct timespec d = { .tv_sec = afterns.tv_sec - after.tv_sec, > - .tv_nsec = afterns.tv_nsec - after.tv_nsec }; > - if (d.tv_nsec < 0) > - { > - --d.tv_sec; > - d.tv_nsec += 1000000000; > - } > - if (d.tv_sec > 0 > - || d.tv_nsec < sleeptime.tv_nsec > - || d.tv_nsec > sleeptime.tv_nsec * 2) > + /* The bound values are empirically defined by testing this code over > + high cpu usage and different nice values. Of all the values we keep > + the 90th percentile of values and use those values for our testing > + allowed range. */ > + diff = timespec_sub (support_timespec_normalize (afterns), > + support_timespec_normalize (after)); > + if (!support_timespec_check_in_range (sleeptime, diff, .9, 1.2)) > { > printf ("nanosleep time %ju.%.9ju outside reasonable range\n", > - (uintmax_t) d.tv_sec, (uintmax_t) d.tv_nsec); > + (uintmax_t) diff.tv_sec, (uintmax_t) diff.tv_nsec); > result = 1; > } > } > @@ -240,15 +234,13 @@ do_test (void) > /* Should be close to 0.6. */ > printf ("dead PID %d => %ju.%.9ju\n", > child, (uintmax_t) dead.tv_sec, (uintmax_t) dead.tv_nsec); > - > - diff.tv_sec = dead.tv_sec - after.tv_sec; > - diff.tv_nsec = dead.tv_nsec - after.tv_nsec; > - if (diff.tv_nsec < 0) > - { > - --diff.tv_sec; > - diff.tv_nsec += 1000000000; > - } > - if (diff.tv_sec != 0 || diff.tv_nsec > 200000000) > + /* The bound values are empirically defined by testing this code over high cpu > + usage and different nice values. Of all the values we keep the 90th > + percentile of values and use those values for our testing allowed range. */ > + diff = timespec_sub (support_timespec_normalize (dead), > + support_timespec_normalize (after)); > + sleeptime.tv_nsec = 100000000; > + if (!support_timespec_check_in_range (sleeptime, diff, .9, 1.2)) > { > printf ("dead - after %ju.%.9ju outside reasonable range\n", > (uintmax_t) diff.tv_sec, (uintmax_t) diff.tv_nsec); > -- > 2.20.1 >
PING Quoting Lucas A. M. Magalhaes (2020-05-11 14:41:40) > PING. > > Quoting Lucas A. M. Magalhaes via Libc-alpha (2020-04-21 14:44:56) > > This test fails intermittently in systems with heavy load as > > CLOCK_PROCESS_CPUTIME_ID is subject to scheduler pressure. Thus the > > test boundaries were relaxed to keep it from failing on such systems. > > > > A refactor of the spent time checking was made with some support > > functions. With the advantage to representing time jitter in percent > > of the target. > > > > The values used by the test boundaries are all empirical. > > > > --- > > > > Hi Carlos, > > I also got skeptical about my dataset so I genegerated another one. > > That .4 boundry was indeed too low. Also, the support/tst-timespec was > > not actually running any tests because of a lazy bug, sorry about that. > > Thanks for taking the time for reviewing this. > > > > changes on V9: > > Add new testcases > > Fix support/tst-timespec do_run > > Add comments > > Change boundaries > > > > changes on V8: > > Add support_timespec_ns > > Add more tests > > > > This list of changes where getting to long. > > --- > > support/Makefile | 1 + > > support/timespec.c | 64 +++++++++ > > support/timespec.h | 8 ++ > > support/tst-timespec.c | 320 +++++++++++++++++++++++++++++++++++++++++ > > time/tst-cpuclock1.c | 52 +++---- > > 5 files changed, 415 insertions(+), 30 deletions(-) > > create mode 100644 support/tst-timespec.c > > > > diff --git a/support/Makefile b/support/Makefile > > index 6e38b87ebe..cacaac96a5 100644 > > --- a/support/Makefile > > +++ b/support/Makefile > > @@ -233,6 +233,7 @@ tests = \ > > tst-test_compare \ > > tst-test_compare_blob \ > > tst-test_compare_string \ > > + tst-timespec \ > > tst-xreadlink \ > > tst-xsigstack \ > > > > diff --git a/support/timespec.c b/support/timespec.c > > index ea6b947546..103beafdc4 100644 > > --- a/support/timespec.c > > +++ b/support/timespec.c > > @@ -19,6 +19,8 @@ > > #include <support/timespec.h> > > #include <stdio.h> > > #include <stdint.h> > > +#include <assert.h> > > +#include <intprops.h> > > > > void > > test_timespec_before_impl (const char *file, int line, > > @@ -57,3 +59,65 @@ test_timespec_equal_or_after_impl (const char *file, int line, > > (intmax_t) diff.tv_sec, (intmax_t) diff.tv_nsec); > > } > > } > > + > > +/* Convert TIME to nanoseconds stored in shrinked in a long. > > + Returns long maximum or minimum if the conversion overflows > > + or underflows, respectively. */ > > +long > > +support_timespec_ns (struct timespec time) > > +{ > > + long time_ns; > > + if (INT_MULTIPLY_WRAPV(time.tv_sec, TIMESPEC_HZ, &time_ns)) > > + { > > + return (time.tv_sec < 0) ? TYPE_MINIMUM(long): TYPE_MAXIMUM(long); > > + } > > + if (INT_ADD_WRAPV(time_ns, time.tv_nsec, &time_ns)) > > + { > > + return (time.tv_nsec < 0) ? TYPE_MINIMUM(long): TYPE_MAXIMUM(long); > > + } > > + return time_ns; > > +} > > + > > +/* Returns t normalized timespec with .tv_nsec < TIMESPEC_HZ > > + and the whole seconds added to .tv_sec. If an overflow or > > + underflow occurs the values are clamped to its maximum or > > + minimum respectively. */ > > +struct timespec > > +support_timespec_normalize (struct timespec time) > > +{ > > + struct timespec norm; > > + if (INT_ADD_WRAPV (time.tv_sec, (time.tv_nsec / TIMESPEC_HZ), &norm.tv_sec)) > > + { > > + norm.tv_sec = (time.tv_nsec < 0) ? TYPE_MINIMUM (time_t): TYPE_MAXIMUM (time_t); > > + norm.tv_nsec = (time.tv_nsec < 0) ? -1 * (TIMESPEC_HZ - 1) : TIMESPEC_HZ - 1; > > + return norm; > > + } > > + norm.tv_nsec = time.tv_nsec % TIMESPEC_HZ; > > + return norm; > > +} > > + > > +/* Returns TRUE if the observed time is within the given percentage > > + bounds of the expected time, and FALSE otherwise. > > + For example the call > > + > > + support_timespec_check_in_range(expected, observed, .5, 1.2); > > + > > + will check if > > + > > + .5 <= observed/expected <= 1.2 > > + > > + In other words it will check if observed time is within 50% to > > + 120% of the expected time. */ > > +int > > +support_timespec_check_in_range (struct timespec expected, struct timespec observed, > > + double lower_bound, double upper_bound) > > +{ > > + assert (upper_bound >= lower_bound); > > + long expected_norm, observed_norm; > > + expected_norm = support_timespec_ns (expected); > > + /* Don't divide by zero */ > > + assert(expected_norm != 0); > > + observed_norm = support_timespec_ns (observed); > > + double ratio = (double)observed_norm / expected_norm; > > + return (lower_bound <= ratio && ratio <= upper_bound); > > +} > > diff --git a/support/timespec.h b/support/timespec.h > > index c5852dfe75..fd5466745d 100644 > > --- a/support/timespec.h > > +++ b/support/timespec.h > > @@ -48,6 +48,14 @@ void test_timespec_equal_or_after_impl (const char *file, int line, > > const struct timespec left, > > const struct timespec right); > > > > +long support_timespec_ns (struct timespec time); > > + > > +struct timespec support_timespec_normalize (struct timespec time); > > + > > +int support_timespec_check_in_range (struct timespec expected, struct timespec observed, > > + double lower_bound, double upper_bound); > > + > > + > > /* Check that the timespec on the left represents a time before the > > time on the right. */ > > #define TEST_TIMESPEC_BEFORE(left, right) \ > > diff --git a/support/tst-timespec.c b/support/tst-timespec.c > > new file mode 100644 > > index 0000000000..b421b219fd > > --- /dev/null > > +++ b/support/tst-timespec.c > > @@ -0,0 +1,320 @@ > > +/* Test for support_timespec_check_in_range function. > > + Copyright (C) 2020 Free Software Foundation, Inc. > > + This file is part of the GNU C Library. > > + > > + The GNU C Library is free software; you can redistribute it and/or > > + modify it under the terms of the GNU Lesser General Public > > + License as published by the Free Software Foundation; either > > + version 2.1 of the License, or (at your option) any later version. > > + > > + The GNU C Library is distributed in the hope that it will be useful, > > + but WITHOUT ANY WARRANTY; without even the implied warranty of > > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > > + Lesser General Public License for more details. > > + > > + You should have received a copy of the GNU Lesser General Public > > + License along with the GNU C Library; if not, see > > + <https://www.gnu.org/licenses/>. */ > > + > > +#include <support/timespec.h> > > +#include <support/check.h> > > +#include <limits.h> > > + > > +#define TIMESPEC_HZ 1000000000 > > + > > +struct timespec_ns_test_case > > +{ > > + struct timespec time; > > + long time_ns; > > +}; > > + > > +struct timespec_norm_test_case > > +{ > > + struct timespec time; > > + struct timespec norm; > > +}; > > + > > +struct timespec_test_case > > +{ > > + struct timespec expected; > > + struct timespec observed; > > + double upper_bound; > > + double lower_bound; > > + int result; > > +}; > > + > > +/* Test cases for timespec_ns */ > > +struct timespec_ns_test_case ns_cases[] = { > > + {.time = {.tv_sec = 0, .tv_nsec = 0}, > > + .time_ns = 0, > > + }, > > + {.time = {.tv_sec = 0, .tv_nsec = 1}, > > + .time_ns = 1, > > + }, > > + {.time = {.tv_sec = 1, .tv_nsec = 0}, > > + .time_ns = TIMESPEC_HZ, > > + }, > > + {.time = {.tv_sec = 1, .tv_nsec = 1}, > > + .time_ns = TIMESPEC_HZ + 1, > > + }, > > + {.time = {.tv_sec = 0, .tv_nsec = -1}, > > + .time_ns = -1, > > + }, > > + {.time = {.tv_sec = -1, .tv_nsec = 0}, > > + .time_ns = -TIMESPEC_HZ, > > + }, > > + {.time = {.tv_sec = -1, .tv_nsec = -1}, > > + .time_ns = -TIMESPEC_HZ - 1, > > + }, > > + {.time = {.tv_sec = 1, .tv_nsec = -1}, > > + .time_ns = TIMESPEC_HZ - 1, > > + }, > > + {.time = {.tv_sec = -1, .tv_nsec = 1}, > > + .time_ns = -TIMESPEC_HZ + 1, > > + }, > > + // Overflow bondary by 2 > > + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ, .tv_nsec = LONG_MAX%TIMESPEC_HZ - 1}, > > + .time_ns = LONG_MAX - 1, > > + }, > > + // Overflow bondary > > + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ, .tv_nsec = LONG_MAX%TIMESPEC_HZ}, > > + .time_ns = LONG_MAX, > > + }, > > + // Underflow bondary by 1 > > + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ, .tv_nsec = LONG_MIN%TIMESPEC_HZ + 1}, > > + .time_ns = LONG_MIN + 1, > > + }, > > + // Underflow bondary > > + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ, .tv_nsec = LONG_MIN%TIMESPEC_HZ}, > > + .time_ns = LONG_MIN, > > + }, > > + // Multiplication overflow > > + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ + 1, .tv_nsec = 1}, > > + .time_ns = LONG_MAX, > > + }, > > + // Multiplication underflow > > + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ - 1, .tv_nsec = -1}, > > + .time_ns = LONG_MIN, > > + }, > > + // Sum overflows > > + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ, .tv_nsec = LONG_MAX%TIMESPEC_HZ + 1}, > > + .time_ns = LONG_MAX, > > + }, > > + // Sum underflow > > + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ, .tv_nsec = LONG_MIN%TIMESPEC_HZ - 1}, > > + .time_ns = LONG_MIN, > > + } > > +}; > > + > > +/* Test cases for timespec_norm */ > > +struct timespec_norm_test_case norm_cases[] = { > > + // Positive cases > > + {.time = {.tv_sec = 0, .tv_nsec = 0}, > > + .norm = {.tv_sec = 0, .tv_nsec = 0} > > + }, > > + {.time = {.tv_sec = 1, .tv_nsec = 0}, > > + .norm = {.tv_sec = 1, .tv_nsec = 0} > > + }, > > + {.time = {.tv_sec = 0, .tv_nsec = 1}, > > + .norm = {.tv_sec = 0, .tv_nsec = 1} > > + }, > > + {.time = {.tv_sec = 0, .tv_nsec = TIMESPEC_HZ}, > > + .norm = {.tv_sec = 1, .tv_nsec = 0} > > + }, > > + {.time = {.tv_sec = 0, .tv_nsec = TIMESPEC_HZ + 1}, > > + .norm = {.tv_sec = 1, .tv_nsec = 1} > > + }, > > + {.time = {.tv_sec = 1, .tv_nsec = TIMESPEC_HZ}, > > + .norm = {.tv_sec = 2, .tv_nsec = 0} > > + }, > > + {.time = {.tv_sec = 1, .tv_nsec = TIMESPEC_HZ + 1}, > > + .norm = {.tv_sec = 2, .tv_nsec = 1} > > + }, > > + // Negative cases > > + {.time = {.tv_sec = 0, .tv_nsec = -TIMESPEC_HZ}, > > + .norm = {.tv_sec = -1, .tv_nsec = 0} > > + }, > > + {.time = {.tv_sec = 0, .tv_nsec = -TIMESPEC_HZ - 1}, > > + .norm = {.tv_sec = -1, .tv_nsec = -1} > > + }, > > + {.time = {.tv_sec = -1, .tv_nsec = -TIMESPEC_HZ}, > > + .norm = {.tv_sec = -2, .tv_nsec = 0} > > + }, > > + {.time = {.tv_sec = -1, .tv_nsec = -TIMESPEC_HZ - 1}, > > + .norm = {.tv_sec = -2, .tv_nsec = -1} > > + }, > > + // Overflow bondary by 2 > > + {.time = {.tv_sec = LONG_MAX - 2, .tv_nsec = TIMESPEC_HZ + 1}, > > + .norm = {.tv_sec = LONG_MAX - 1, 1}, > > + }, > > + // Overflow bondary by 1 > > + {.time = {.tv_sec = LONG_MAX - 1, .tv_nsec = TIMESPEC_HZ + 1}, > > + .norm = {.tv_sec = LONG_MAX, .tv_nsec = 1}, > > + }, > > + // Underflow bondary by 2 > > + {.time = {.tv_sec = LONG_MIN + 2, .tv_nsec = -TIMESPEC_HZ - 1}, > > + .norm = {.tv_sec = LONG_MIN + 1, -1}, > > + }, > > + // Underflow bondary by 1 > > + {.time = {.tv_sec = LONG_MIN + 1, .tv_nsec = -TIMESPEC_HZ - 1}, > > + .norm = {.tv_sec = LONG_MIN, .tv_nsec = -1}, > > + }, > > + // SUM overflow > > + {.time = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > > + .norm = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ - 1}, > > + }, > > + // SUM underflow > > + {.time = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > > + .norm = {.tv_sec = LONG_MIN, .tv_nsec = -1 * (TIMESPEC_HZ - 1)}, > > + } > > +}; > > + > > +/* Test cases for timespec_check_in_range */ > > +struct timespec_test_case check_cases[] = { > > + // 0 - In range > > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > > + .observed = {.tv_sec = 1, .tv_nsec = 0}, > > + .upper_bound = 1, .lower_bound = 1, .result = 1, > > + }, > > + // 1 - Out of range > > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > > + .observed = {.tv_sec = 2, .tv_nsec = 0}, > > + .upper_bound = 1, .lower_bound = 1, .result = 0, > > + }, > > + // 2 - Upper Bound > > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > > + .observed = {.tv_sec = 2, .tv_nsec = 0}, > > + .upper_bound = 2, .lower_bound = 1, .result = 1, > > + }, > > + // 3 - Lower Bound > > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > > + .observed = {.tv_sec = 0, .tv_nsec = 0}, > > + .upper_bound = 1, .lower_bound = 0, .result = 1, > > + }, > > + // 4 - Out of range by nanosecs > > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > > + .observed = {.tv_sec = 1, .tv_nsec = 500}, > > + .upper_bound = 1, .lower_bound = 1, .result = 0, > > + }, > > + // 5 - In range by nanosecs > > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > > + .observed = {.tv_sec = 1, .tv_nsec = 50000}, > > + .upper_bound = 1.3, .lower_bound = 1, .result = 1, > > + }, > > + // 6 - Big nanosecs > > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > > + .observed = {.tv_sec = 0, .tv_nsec = 4000000}, > > + .upper_bound = 1, .lower_bound = .001, .result = 1, > > + }, > > + // 7 - In range Negative values > > + {.expected = {.tv_sec = -1, .tv_nsec = 0}, > > + .observed = {.tv_sec = -1, .tv_nsec = 0}, > > + .upper_bound = 1, .lower_bound = 1, .result = 1, > > + }, > > + // 8 - Out of range Negative values > > + {.expected = {.tv_sec = -1, .tv_nsec = 0}, > > + .observed = {.tv_sec = -1, .tv_nsec = 0}, > > + .upper_bound = -1, .lower_bound = -1, .result = 0, > > + }, > > + // 9 - Negative values with negative nanosecs > > + {.expected = {.tv_sec = -1, .tv_nsec = 0}, > > + .observed = {.tv_sec = -1, .tv_nsec = -2000}, > > + .upper_bound = 1, .lower_bound = 1, .result = 0, > > + }, > > + // 10 - Strict bounds > > + {.expected = {.tv_sec = -1, .tv_nsec = 0}, > > + .observed = {.tv_sec = -1, .tv_nsec = -20000}, > > + .upper_bound = 1.00002, .lower_bound = 1.0000191, .result = 1, > > + }, > > + // 11 - Strict bounds with loose upper bound > > + {.expected = {.tv_sec = 1, .tv_nsec = 20000}, > > + .observed = {.tv_sec = 1, .tv_nsec = 30000}, > > + .upper_bound = 1.0000100000, .lower_bound = 1.0000099998, .result = 1, > > + }, > > + // 12 - Strict bounds with loose lower bound > > + {.expected = {.tv_sec = 1, .tv_nsec = 20000}, > > + .observed = {.tv_sec = 1, .tv_nsec = 30000}, > > + .upper_bound = 1.0000099999, .lower_bound = 1.00000999979, .result = 1, > > + }, > > + // 13 - Strict bounds highest precision > > + {.expected = {.tv_sec = 1, .tv_nsec = 20000}, > > + .observed = {.tv_sec = 1, .tv_nsec = 30000}, > > + .upper_bound = 1.00000999980001, .lower_bound = 1.00000999979999, .result = 1, > > + }, > > + /* Maximum/Minimum long values */ > > + // 14 > > + {.expected = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ - 1}, > > + .observed = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ - 2}, > > + .upper_bound = 1, .lower_bound = .9, .result = 1, > > + }, > > + // 15 - support_timespec_ns overflow > > + {.expected = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > > + .observed = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > > + .upper_bound = 1, .lower_bound = 1, .result = 1, > > + }, > > + // 16 - support_timespec_ns overflow + underflow > > + {.expected = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > > + .observed = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > > + .upper_bound = 1, .lower_bound = 1, .result = 0, > > + }, > > + // 17 - support_timespec_ns underflow > > + {.expected = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > > + .observed = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > > + .upper_bound = 1, .lower_bound = 1, .result = 1, > > + }, > > + // 18 - support_timespec_ns underflow + overflow > > + {.expected = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > > + .observed = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > > + .upper_bound = 1, .lower_bound = 1, .result = 0, > > + }, > > + // 19 - Biggest division > > + {.expected = {.tv_sec = LONG_MAX / TIMESPEC_HZ , .tv_nsec = TIMESPEC_HZ - 1}, > > + .observed = {.tv_sec = 0, .tv_nsec = 1}, > > + .upper_bound = 1, .lower_bound = 1.0842021724855044e-19, .result = 1, > > + }, > > + // 20 - Lowest division > > + {.expected = {.tv_sec = 0, .tv_nsec = 1}, > > + .observed = {.tv_sec = LONG_MAX / TIMESPEC_HZ , .tv_nsec = TIMESPEC_HZ - 1}, > > + .upper_bound = LONG_MAX, .lower_bound = 1, .result = 1, > > + }, > > +}; > > + > > +static int > > +do_test (void) > > +{ > > + int i = 0; > > + int ntests = sizeof (ns_cases) / sizeof (ns_cases[0]); > > + > > + printf("Testing support_timespec_ns\n"); > > + for (i = 0; i < ntests; i++) > > + { > > + TEST_COMPARE (support_timespec_ns (ns_cases[i].time), > > + ns_cases[i].time_ns); > > + } > > + > > + ntests = sizeof (norm_cases) / sizeof (norm_cases[0]); > > + struct timespec result; > > + printf("Testing support_timespec_normalize\n"); > > + for (i = 0; i < ntests; i++) > > + { > > + result = support_timespec_normalize (norm_cases[i].time); > > + TEST_COMPARE (norm_cases[i].norm.tv_sec, result.tv_sec); > > + TEST_COMPARE (norm_cases[i].norm.tv_nsec, result.tv_nsec); > > + } > > + > > + ntests = sizeof (check_cases) / sizeof (check_cases[0]); > > + printf("Testing support_timespec_check_in_range\n"); > > + for (i = 0; i < ntests; i++) > > + { > > + /* Its hard to find which test failed with just the TEST_COMPARE report. > > + So here we print every running testcase as well. */ > > + printf("Test case %d\n", i); > > + TEST_COMPARE (support_timespec_check_in_range > > + (check_cases[i].expected, check_cases[i].observed, > > + check_cases[i].lower_bound, > > + check_cases[i].upper_bound), check_cases[i].result); > > + } > > + return 0; > > +} > > + > > +#include <support/test-driver.c> > > diff --git a/time/tst-cpuclock1.c b/time/tst-cpuclock1.c > > index 0120906f23..1ac611a92b 100644 > > --- a/time/tst-cpuclock1.c > > +++ b/time/tst-cpuclock1.c > > @@ -26,6 +26,7 @@ > > #include <signal.h> > > #include <stdint.h> > > #include <sys/wait.h> > > +#include <support/timespec.h> > > > > /* This function is intended to rack up both user and system time. */ > > static void > > @@ -155,16 +156,12 @@ do_test (void) > > printf ("live PID %d after sleep => %ju.%.9ju\n", > > child, (uintmax_t) after.tv_sec, (uintmax_t) after.tv_nsec); > > > > - struct timespec diff = { .tv_sec = after.tv_sec - before.tv_sec, > > - .tv_nsec = after.tv_nsec - before.tv_nsec }; > > - if (diff.tv_nsec < 0) > > - { > > - --diff.tv_sec; > > - diff.tv_nsec += 1000000000; > > - } > > - if (diff.tv_sec != 0 > > - || diff.tv_nsec > 600000000 > > - || diff.tv_nsec < 100000000) > > + /* The bound values are empirically defined by testing this code over high cpu > > + usage and different nice values. Of all the values we keep the 90th > > + percentile of values and use those values for our testing allowed range. */ > > + struct timespec diff = timespec_sub (support_timespec_normalize (after), > > + support_timespec_normalize (before)); > > + if (!support_timespec_check_in_range (sleeptime, diff, .9, 1.1)) > > { > > printf ("before - after %ju.%.9ju outside reasonable range\n", > > (uintmax_t) diff.tv_sec, (uintmax_t) diff.tv_nsec); > > @@ -194,19 +191,16 @@ do_test (void) > > } > > else > > { > > - struct timespec d = { .tv_sec = afterns.tv_sec - after.tv_sec, > > - .tv_nsec = afterns.tv_nsec - after.tv_nsec }; > > - if (d.tv_nsec < 0) > > - { > > - --d.tv_sec; > > - d.tv_nsec += 1000000000; > > - } > > - if (d.tv_sec > 0 > > - || d.tv_nsec < sleeptime.tv_nsec > > - || d.tv_nsec > sleeptime.tv_nsec * 2) > > + /* The bound values are empirically defined by testing this code over > > + high cpu usage and different nice values. Of all the values we keep > > + the 90th percentile of values and use those values for our testing > > + allowed range. */ > > + diff = timespec_sub (support_timespec_normalize (afterns), > > + support_timespec_normalize (after)); > > + if (!support_timespec_check_in_range (sleeptime, diff, .9, 1.2)) > > { > > printf ("nanosleep time %ju.%.9ju outside reasonable range\n", > > - (uintmax_t) d.tv_sec, (uintmax_t) d.tv_nsec); > > + (uintmax_t) diff.tv_sec, (uintmax_t) diff.tv_nsec); > > result = 1; > > } > > } > > @@ -240,15 +234,13 @@ do_test (void) > > /* Should be close to 0.6. */ > > printf ("dead PID %d => %ju.%.9ju\n", > > child, (uintmax_t) dead.tv_sec, (uintmax_t) dead.tv_nsec); > > - > > - diff.tv_sec = dead.tv_sec - after.tv_sec; > > - diff.tv_nsec = dead.tv_nsec - after.tv_nsec; > > - if (diff.tv_nsec < 0) > > - { > > - --diff.tv_sec; > > - diff.tv_nsec += 1000000000; > > - } > > - if (diff.tv_sec != 0 || diff.tv_nsec > 200000000) > > + /* The bound values are empirically defined by testing this code over high cpu > > + usage and different nice values. Of all the values we keep the 90th > > + percentile of values and use those values for our testing allowed range. */ > > + diff = timespec_sub (support_timespec_normalize (dead), > > + support_timespec_normalize (after)); > > + sleeptime.tv_nsec = 100000000; > > + if (!support_timespec_check_in_range (sleeptime, diff, .9, 1.2)) > > { > > printf ("dead - after %ju.%.9ju outside reasonable range\n", > > (uintmax_t) diff.tv_sec, (uintmax_t) diff.tv_nsec); > > -- > > 2.20.1 > >
PING Quoting Lucas A. M. Magalhaes (2020-05-25 08:46:50) > PING > > Quoting Lucas A. M. Magalhaes (2020-05-11 14:41:40) > > PING. > > > > Quoting Lucas A. M. Magalhaes via Libc-alpha (2020-04-21 14:44:56) > > > This test fails intermittently in systems with heavy load as > > > CLOCK_PROCESS_CPUTIME_ID is subject to scheduler pressure. Thus the > > > test boundaries were relaxed to keep it from failing on such systems. > > > > > > A refactor of the spent time checking was made with some support > > > functions. With the advantage to representing time jitter in percent > > > of the target. > > > > > > The values used by the test boundaries are all empirical. > > > > > > --- > > > > > > Hi Carlos, > > > I also got skeptical about my dataset so I genegerated another one. > > > That .4 boundry was indeed too low. Also, the support/tst-timespec was > > > not actually running any tests because of a lazy bug, sorry about that. > > > Thanks for taking the time for reviewing this. > > > > > > changes on V9: > > > Add new testcases > > > Fix support/tst-timespec do_run > > > Add comments > > > Change boundaries > > > > > > changes on V8: > > > Add support_timespec_ns > > > Add more tests > > > > > > This list of changes where getting to long. > > > --- > > > support/Makefile | 1 + > > > support/timespec.c | 64 +++++++++ > > > support/timespec.h | 8 ++ > > > support/tst-timespec.c | 320 +++++++++++++++++++++++++++++++++++++++++ > > > time/tst-cpuclock1.c | 52 +++---- > > > 5 files changed, 415 insertions(+), 30 deletions(-) > > > create mode 100644 support/tst-timespec.c > > > > > > diff --git a/support/Makefile b/support/Makefile > > > index 6e38b87ebe..cacaac96a5 100644 > > > --- a/support/Makefile > > > +++ b/support/Makefile > > > @@ -233,6 +233,7 @@ tests = \ > > > tst-test_compare \ > > > tst-test_compare_blob \ > > > tst-test_compare_string \ > > > + tst-timespec \ > > > tst-xreadlink \ > > > tst-xsigstack \ > > > > > > diff --git a/support/timespec.c b/support/timespec.c > > > index ea6b947546..103beafdc4 100644 > > > --- a/support/timespec.c > > > +++ b/support/timespec.c > > > @@ -19,6 +19,8 @@ > > > #include <support/timespec.h> > > > #include <stdio.h> > > > #include <stdint.h> > > > +#include <assert.h> > > > +#include <intprops.h> > > > > > > void > > > test_timespec_before_impl (const char *file, int line, > > > @@ -57,3 +59,65 @@ test_timespec_equal_or_after_impl (const char *file, int line, > > > (intmax_t) diff.tv_sec, (intmax_t) diff.tv_nsec); > > > } > > > } > > > + > > > +/* Convert TIME to nanoseconds stored in shrinked in a long. > > > + Returns long maximum or minimum if the conversion overflows > > > + or underflows, respectively. */ > > > +long > > > +support_timespec_ns (struct timespec time) > > > +{ > > > + long time_ns; > > > + if (INT_MULTIPLY_WRAPV(time.tv_sec, TIMESPEC_HZ, &time_ns)) > > > + { > > > + return (time.tv_sec < 0) ? TYPE_MINIMUM(long): TYPE_MAXIMUM(long); > > > + } > > > + if (INT_ADD_WRAPV(time_ns, time.tv_nsec, &time_ns)) > > > + { > > > + return (time.tv_nsec < 0) ? TYPE_MINIMUM(long): TYPE_MAXIMUM(long); > > > + } > > > + return time_ns; > > > +} > > > + > > > +/* Returns t normalized timespec with .tv_nsec < TIMESPEC_HZ > > > + and the whole seconds added to .tv_sec. If an overflow or > > > + underflow occurs the values are clamped to its maximum or > > > + minimum respectively. */ > > > +struct timespec > > > +support_timespec_normalize (struct timespec time) > > > +{ > > > + struct timespec norm; > > > + if (INT_ADD_WRAPV (time.tv_sec, (time.tv_nsec / TIMESPEC_HZ), &norm.tv_sec)) > > > + { > > > + norm.tv_sec = (time.tv_nsec < 0) ? TYPE_MINIMUM (time_t): TYPE_MAXIMUM (time_t); > > > + norm.tv_nsec = (time.tv_nsec < 0) ? -1 * (TIMESPEC_HZ - 1) : TIMESPEC_HZ - 1; > > > + return norm; > > > + } > > > + norm.tv_nsec = time.tv_nsec % TIMESPEC_HZ; > > > + return norm; > > > +} > > > + > > > +/* Returns TRUE if the observed time is within the given percentage > > > + bounds of the expected time, and FALSE otherwise. > > > + For example the call > > > + > > > + support_timespec_check_in_range(expected, observed, .5, 1.2); > > > + > > > + will check if > > > + > > > + .5 <= observed/expected <= 1.2 > > > + > > > + In other words it will check if observed time is within 50% to > > > + 120% of the expected time. */ > > > +int > > > +support_timespec_check_in_range (struct timespec expected, struct timespec observed, > > > + double lower_bound, double upper_bound) > > > +{ > > > + assert (upper_bound >= lower_bound); > > > + long expected_norm, observed_norm; > > > + expected_norm = support_timespec_ns (expected); > > > + /* Don't divide by zero */ > > > + assert(expected_norm != 0); > > > + observed_norm = support_timespec_ns (observed); > > > + double ratio = (double)observed_norm / expected_norm; > > > + return (lower_bound <= ratio && ratio <= upper_bound); > > > +} > > > diff --git a/support/timespec.h b/support/timespec.h > > > index c5852dfe75..fd5466745d 100644 > > > --- a/support/timespec.h > > > +++ b/support/timespec.h > > > @@ -48,6 +48,14 @@ void test_timespec_equal_or_after_impl (const char *file, int line, > > > const struct timespec left, > > > const struct timespec right); > > > > > > +long support_timespec_ns (struct timespec time); > > > + > > > +struct timespec support_timespec_normalize (struct timespec time); > > > + > > > +int support_timespec_check_in_range (struct timespec expected, struct timespec observed, > > > + double lower_bound, double upper_bound); > > > + > > > + > > > /* Check that the timespec on the left represents a time before the > > > time on the right. */ > > > #define TEST_TIMESPEC_BEFORE(left, right) \ > > > diff --git a/support/tst-timespec.c b/support/tst-timespec.c > > > new file mode 100644 > > > index 0000000000..b421b219fd > > > --- /dev/null > > > +++ b/support/tst-timespec.c > > > @@ -0,0 +1,320 @@ > > > +/* Test for support_timespec_check_in_range function. > > > + Copyright (C) 2020 Free Software Foundation, Inc. > > > + This file is part of the GNU C Library. > > > + > > > + The GNU C Library is free software; you can redistribute it and/or > > > + modify it under the terms of the GNU Lesser General Public > > > + License as published by the Free Software Foundation; either > > > + version 2.1 of the License, or (at your option) any later version. > > > + > > > + The GNU C Library is distributed in the hope that it will be useful, > > > + but WITHOUT ANY WARRANTY; without even the implied warranty of > > > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > > > + Lesser General Public License for more details. > > > + > > > + You should have received a copy of the GNU Lesser General Public > > > + License along with the GNU C Library; if not, see > > > + <https://www.gnu.org/licenses/>. */ > > > + > > > +#include <support/timespec.h> > > > +#include <support/check.h> > > > +#include <limits.h> > > > + > > > +#define TIMESPEC_HZ 1000000000 > > > + > > > +struct timespec_ns_test_case > > > +{ > > > + struct timespec time; > > > + long time_ns; > > > +}; > > > + > > > +struct timespec_norm_test_case > > > +{ > > > + struct timespec time; > > > + struct timespec norm; > > > +}; > > > + > > > +struct timespec_test_case > > > +{ > > > + struct timespec expected; > > > + struct timespec observed; > > > + double upper_bound; > > > + double lower_bound; > > > + int result; > > > +}; > > > + > > > +/* Test cases for timespec_ns */ > > > +struct timespec_ns_test_case ns_cases[] = { > > > + {.time = {.tv_sec = 0, .tv_nsec = 0}, > > > + .time_ns = 0, > > > + }, > > > + {.time = {.tv_sec = 0, .tv_nsec = 1}, > > > + .time_ns = 1, > > > + }, > > > + {.time = {.tv_sec = 1, .tv_nsec = 0}, > > > + .time_ns = TIMESPEC_HZ, > > > + }, > > > + {.time = {.tv_sec = 1, .tv_nsec = 1}, > > > + .time_ns = TIMESPEC_HZ + 1, > > > + }, > > > + {.time = {.tv_sec = 0, .tv_nsec = -1}, > > > + .time_ns = -1, > > > + }, > > > + {.time = {.tv_sec = -1, .tv_nsec = 0}, > > > + .time_ns = -TIMESPEC_HZ, > > > + }, > > > + {.time = {.tv_sec = -1, .tv_nsec = -1}, > > > + .time_ns = -TIMESPEC_HZ - 1, > > > + }, > > > + {.time = {.tv_sec = 1, .tv_nsec = -1}, > > > + .time_ns = TIMESPEC_HZ - 1, > > > + }, > > > + {.time = {.tv_sec = -1, .tv_nsec = 1}, > > > + .time_ns = -TIMESPEC_HZ + 1, > > > + }, > > > + // Overflow bondary by 2 > > > + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ, .tv_nsec = LONG_MAX%TIMESPEC_HZ - 1}, > > > + .time_ns = LONG_MAX - 1, > > > + }, > > > + // Overflow bondary > > > + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ, .tv_nsec = LONG_MAX%TIMESPEC_HZ}, > > > + .time_ns = LONG_MAX, > > > + }, > > > + // Underflow bondary by 1 > > > + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ, .tv_nsec = LONG_MIN%TIMESPEC_HZ + 1}, > > > + .time_ns = LONG_MIN + 1, > > > + }, > > > + // Underflow bondary > > > + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ, .tv_nsec = LONG_MIN%TIMESPEC_HZ}, > > > + .time_ns = LONG_MIN, > > > + }, > > > + // Multiplication overflow > > > + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ + 1, .tv_nsec = 1}, > > > + .time_ns = LONG_MAX, > > > + }, > > > + // Multiplication underflow > > > + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ - 1, .tv_nsec = -1}, > > > + .time_ns = LONG_MIN, > > > + }, > > > + // Sum overflows > > > + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ, .tv_nsec = LONG_MAX%TIMESPEC_HZ + 1}, > > > + .time_ns = LONG_MAX, > > > + }, > > > + // Sum underflow > > > + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ, .tv_nsec = LONG_MIN%TIMESPEC_HZ - 1}, > > > + .time_ns = LONG_MIN, > > > + } > > > +}; > > > + > > > +/* Test cases for timespec_norm */ > > > +struct timespec_norm_test_case norm_cases[] = { > > > + // Positive cases > > > + {.time = {.tv_sec = 0, .tv_nsec = 0}, > > > + .norm = {.tv_sec = 0, .tv_nsec = 0} > > > + }, > > > + {.time = {.tv_sec = 1, .tv_nsec = 0}, > > > + .norm = {.tv_sec = 1, .tv_nsec = 0} > > > + }, > > > + {.time = {.tv_sec = 0, .tv_nsec = 1}, > > > + .norm = {.tv_sec = 0, .tv_nsec = 1} > > > + }, > > > + {.time = {.tv_sec = 0, .tv_nsec = TIMESPEC_HZ}, > > > + .norm = {.tv_sec = 1, .tv_nsec = 0} > > > + }, > > > + {.time = {.tv_sec = 0, .tv_nsec = TIMESPEC_HZ + 1}, > > > + .norm = {.tv_sec = 1, .tv_nsec = 1} > > > + }, > > > + {.time = {.tv_sec = 1, .tv_nsec = TIMESPEC_HZ}, > > > + .norm = {.tv_sec = 2, .tv_nsec = 0} > > > + }, > > > + {.time = {.tv_sec = 1, .tv_nsec = TIMESPEC_HZ + 1}, > > > + .norm = {.tv_sec = 2, .tv_nsec = 1} > > > + }, > > > + // Negative cases > > > + {.time = {.tv_sec = 0, .tv_nsec = -TIMESPEC_HZ}, > > > + .norm = {.tv_sec = -1, .tv_nsec = 0} > > > + }, > > > + {.time = {.tv_sec = 0, .tv_nsec = -TIMESPEC_HZ - 1}, > > > + .norm = {.tv_sec = -1, .tv_nsec = -1} > > > + }, > > > + {.time = {.tv_sec = -1, .tv_nsec = -TIMESPEC_HZ}, > > > + .norm = {.tv_sec = -2, .tv_nsec = 0} > > > + }, > > > + {.time = {.tv_sec = -1, .tv_nsec = -TIMESPEC_HZ - 1}, > > > + .norm = {.tv_sec = -2, .tv_nsec = -1} > > > + }, > > > + // Overflow bondary by 2 > > > + {.time = {.tv_sec = LONG_MAX - 2, .tv_nsec = TIMESPEC_HZ + 1}, > > > + .norm = {.tv_sec = LONG_MAX - 1, 1}, > > > + }, > > > + // Overflow bondary by 1 > > > + {.time = {.tv_sec = LONG_MAX - 1, .tv_nsec = TIMESPEC_HZ + 1}, > > > + .norm = {.tv_sec = LONG_MAX, .tv_nsec = 1}, > > > + }, > > > + // Underflow bondary by 2 > > > + {.time = {.tv_sec = LONG_MIN + 2, .tv_nsec = -TIMESPEC_HZ - 1}, > > > + .norm = {.tv_sec = LONG_MIN + 1, -1}, > > > + }, > > > + // Underflow bondary by 1 > > > + {.time = {.tv_sec = LONG_MIN + 1, .tv_nsec = -TIMESPEC_HZ - 1}, > > > + .norm = {.tv_sec = LONG_MIN, .tv_nsec = -1}, > > > + }, > > > + // SUM overflow > > > + {.time = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > > > + .norm = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ - 1}, > > > + }, > > > + // SUM underflow > > > + {.time = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > > > + .norm = {.tv_sec = LONG_MIN, .tv_nsec = -1 * (TIMESPEC_HZ - 1)}, > > > + } > > > +}; > > > + > > > +/* Test cases for timespec_check_in_range */ > > > +struct timespec_test_case check_cases[] = { > > > + // 0 - In range > > > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > > > + .observed = {.tv_sec = 1, .tv_nsec = 0}, > > > + .upper_bound = 1, .lower_bound = 1, .result = 1, > > > + }, > > > + // 1 - Out of range > > > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > > > + .observed = {.tv_sec = 2, .tv_nsec = 0}, > > > + .upper_bound = 1, .lower_bound = 1, .result = 0, > > > + }, > > > + // 2 - Upper Bound > > > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > > > + .observed = {.tv_sec = 2, .tv_nsec = 0}, > > > + .upper_bound = 2, .lower_bound = 1, .result = 1, > > > + }, > > > + // 3 - Lower Bound > > > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > > > + .observed = {.tv_sec = 0, .tv_nsec = 0}, > > > + .upper_bound = 1, .lower_bound = 0, .result = 1, > > > + }, > > > + // 4 - Out of range by nanosecs > > > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > > > + .observed = {.tv_sec = 1, .tv_nsec = 500}, > > > + .upper_bound = 1, .lower_bound = 1, .result = 0, > > > + }, > > > + // 5 - In range by nanosecs > > > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > > > + .observed = {.tv_sec = 1, .tv_nsec = 50000}, > > > + .upper_bound = 1.3, .lower_bound = 1, .result = 1, > > > + }, > > > + // 6 - Big nanosecs > > > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > > > + .observed = {.tv_sec = 0, .tv_nsec = 4000000}, > > > + .upper_bound = 1, .lower_bound = .001, .result = 1, > > > + }, > > > + // 7 - In range Negative values > > > + {.expected = {.tv_sec = -1, .tv_nsec = 0}, > > > + .observed = {.tv_sec = -1, .tv_nsec = 0}, > > > + .upper_bound = 1, .lower_bound = 1, .result = 1, > > > + }, > > > + // 8 - Out of range Negative values > > > + {.expected = {.tv_sec = -1, .tv_nsec = 0}, > > > + .observed = {.tv_sec = -1, .tv_nsec = 0}, > > > + .upper_bound = -1, .lower_bound = -1, .result = 0, > > > + }, > > > + // 9 - Negative values with negative nanosecs > > > + {.expected = {.tv_sec = -1, .tv_nsec = 0}, > > > + .observed = {.tv_sec = -1, .tv_nsec = -2000}, > > > + .upper_bound = 1, .lower_bound = 1, .result = 0, > > > + }, > > > + // 10 - Strict bounds > > > + {.expected = {.tv_sec = -1, .tv_nsec = 0}, > > > + .observed = {.tv_sec = -1, .tv_nsec = -20000}, > > > + .upper_bound = 1.00002, .lower_bound = 1.0000191, .result = 1, > > > + }, > > > + // 11 - Strict bounds with loose upper bound > > > + {.expected = {.tv_sec = 1, .tv_nsec = 20000}, > > > + .observed = {.tv_sec = 1, .tv_nsec = 30000}, > > > + .upper_bound = 1.0000100000, .lower_bound = 1.0000099998, .result = 1, > > > + }, > > > + // 12 - Strict bounds with loose lower bound > > > + {.expected = {.tv_sec = 1, .tv_nsec = 20000}, > > > + .observed = {.tv_sec = 1, .tv_nsec = 30000}, > > > + .upper_bound = 1.0000099999, .lower_bound = 1.00000999979, .result = 1, > > > + }, > > > + // 13 - Strict bounds highest precision > > > + {.expected = {.tv_sec = 1, .tv_nsec = 20000}, > > > + .observed = {.tv_sec = 1, .tv_nsec = 30000}, > > > + .upper_bound = 1.00000999980001, .lower_bound = 1.00000999979999, .result = 1, > > > + }, > > > + /* Maximum/Minimum long values */ > > > + // 14 > > > + {.expected = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ - 1}, > > > + .observed = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ - 2}, > > > + .upper_bound = 1, .lower_bound = .9, .result = 1, > > > + }, > > > + // 15 - support_timespec_ns overflow > > > + {.expected = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > > > + .observed = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > > > + .upper_bound = 1, .lower_bound = 1, .result = 1, > > > + }, > > > + // 16 - support_timespec_ns overflow + underflow > > > + {.expected = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > > > + .observed = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > > > + .upper_bound = 1, .lower_bound = 1, .result = 0, > > > + }, > > > + // 17 - support_timespec_ns underflow > > > + {.expected = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > > > + .observed = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > > > + .upper_bound = 1, .lower_bound = 1, .result = 1, > > > + }, > > > + // 18 - support_timespec_ns underflow + overflow > > > + {.expected = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > > > + .observed = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > > > + .upper_bound = 1, .lower_bound = 1, .result = 0, > > > + }, > > > + // 19 - Biggest division > > > + {.expected = {.tv_sec = LONG_MAX / TIMESPEC_HZ , .tv_nsec = TIMESPEC_HZ - 1}, > > > + .observed = {.tv_sec = 0, .tv_nsec = 1}, > > > + .upper_bound = 1, .lower_bound = 1.0842021724855044e-19, .result = 1, > > > + }, > > > + // 20 - Lowest division > > > + {.expected = {.tv_sec = 0, .tv_nsec = 1}, > > > + .observed = {.tv_sec = LONG_MAX / TIMESPEC_HZ , .tv_nsec = TIMESPEC_HZ - 1}, > > > + .upper_bound = LONG_MAX, .lower_bound = 1, .result = 1, > > > + }, > > > +}; > > > + > > > +static int > > > +do_test (void) > > > +{ > > > + int i = 0; > > > + int ntests = sizeof (ns_cases) / sizeof (ns_cases[0]); > > > + > > > + printf("Testing support_timespec_ns\n"); > > > + for (i = 0; i < ntests; i++) > > > + { > > > + TEST_COMPARE (support_timespec_ns (ns_cases[i].time), > > > + ns_cases[i].time_ns); > > > + } > > > + > > > + ntests = sizeof (norm_cases) / sizeof (norm_cases[0]); > > > + struct timespec result; > > > + printf("Testing support_timespec_normalize\n"); > > > + for (i = 0; i < ntests; i++) > > > + { > > > + result = support_timespec_normalize (norm_cases[i].time); > > > + TEST_COMPARE (norm_cases[i].norm.tv_sec, result.tv_sec); > > > + TEST_COMPARE (norm_cases[i].norm.tv_nsec, result.tv_nsec); > > > + } > > > + > > > + ntests = sizeof (check_cases) / sizeof (check_cases[0]); > > > + printf("Testing support_timespec_check_in_range\n"); > > > + for (i = 0; i < ntests; i++) > > > + { > > > + /* Its hard to find which test failed with just the TEST_COMPARE report. > > > + So here we print every running testcase as well. */ > > > + printf("Test case %d\n", i); > > > + TEST_COMPARE (support_timespec_check_in_range > > > + (check_cases[i].expected, check_cases[i].observed, > > > + check_cases[i].lower_bound, > > > + check_cases[i].upper_bound), check_cases[i].result); > > > + } > > > + return 0; > > > +} > > > + > > > +#include <support/test-driver.c> > > > diff --git a/time/tst-cpuclock1.c b/time/tst-cpuclock1.c > > > index 0120906f23..1ac611a92b 100644 > > > --- a/time/tst-cpuclock1.c > > > +++ b/time/tst-cpuclock1.c > > > @@ -26,6 +26,7 @@ > > > #include <signal.h> > > > #include <stdint.h> > > > #include <sys/wait.h> > > > +#include <support/timespec.h> > > > > > > /* This function is intended to rack up both user and system time. */ > > > static void > > > @@ -155,16 +156,12 @@ do_test (void) > > > printf ("live PID %d after sleep => %ju.%.9ju\n", > > > child, (uintmax_t) after.tv_sec, (uintmax_t) after.tv_nsec); > > > > > > - struct timespec diff = { .tv_sec = after.tv_sec - before.tv_sec, > > > - .tv_nsec = after.tv_nsec - before.tv_nsec }; > > > - if (diff.tv_nsec < 0) > > > - { > > > - --diff.tv_sec; > > > - diff.tv_nsec += 1000000000; > > > - } > > > - if (diff.tv_sec != 0 > > > - || diff.tv_nsec > 600000000 > > > - || diff.tv_nsec < 100000000) > > > + /* The bound values are empirically defined by testing this code over high cpu > > > + usage and different nice values. Of all the values we keep the 90th > > > + percentile of values and use those values for our testing allowed range. */ > > > + struct timespec diff = timespec_sub (support_timespec_normalize (after), > > > + support_timespec_normalize (before)); > > > + if (!support_timespec_check_in_range (sleeptime, diff, .9, 1.1)) > > > { > > > printf ("before - after %ju.%.9ju outside reasonable range\n", > > > (uintmax_t) diff.tv_sec, (uintmax_t) diff.tv_nsec); > > > @@ -194,19 +191,16 @@ do_test (void) > > > } > > > else > > > { > > > - struct timespec d = { .tv_sec = afterns.tv_sec - after.tv_sec, > > > - .tv_nsec = afterns.tv_nsec - after.tv_nsec }; > > > - if (d.tv_nsec < 0) > > > - { > > > - --d.tv_sec; > > > - d.tv_nsec += 1000000000; > > > - } > > > - if (d.tv_sec > 0 > > > - || d.tv_nsec < sleeptime.tv_nsec > > > - || d.tv_nsec > sleeptime.tv_nsec * 2) > > > + /* The bound values are empirically defined by testing this code over > > > + high cpu usage and different nice values. Of all the values we keep > > > + the 90th percentile of values and use those values for our testing > > > + allowed range. */ > > > + diff = timespec_sub (support_timespec_normalize (afterns), > > > + support_timespec_normalize (after)); > > > + if (!support_timespec_check_in_range (sleeptime, diff, .9, 1.2)) > > > { > > > printf ("nanosleep time %ju.%.9ju outside reasonable range\n", > > > - (uintmax_t) d.tv_sec, (uintmax_t) d.tv_nsec); > > > + (uintmax_t) diff.tv_sec, (uintmax_t) diff.tv_nsec); > > > result = 1; > > > } > > > } > > > @@ -240,15 +234,13 @@ do_test (void) > > > /* Should be close to 0.6. */ > > > printf ("dead PID %d => %ju.%.9ju\n", > > > child, (uintmax_t) dead.tv_sec, (uintmax_t) dead.tv_nsec); > > > - > > > - diff.tv_sec = dead.tv_sec - after.tv_sec; > > > - diff.tv_nsec = dead.tv_nsec - after.tv_nsec; > > > - if (diff.tv_nsec < 0) > > > - { > > > - --diff.tv_sec; > > > - diff.tv_nsec += 1000000000; > > > - } > > > - if (diff.tv_sec != 0 || diff.tv_nsec > 200000000) > > > + /* The bound values are empirically defined by testing this code over high cpu > > > + usage and different nice values. Of all the values we keep the 90th > > > + percentile of values and use those values for our testing allowed range. */ > > > + diff = timespec_sub (support_timespec_normalize (dead), > > > + support_timespec_normalize (after)); > > > + sleeptime.tv_nsec = 100000000; > > > + if (!support_timespec_check_in_range (sleeptime, diff, .9, 1.2)) > > > { > > > printf ("dead - after %ju.%.9ju outside reasonable range\n", > > > (uintmax_t) diff.tv_sec, (uintmax_t) diff.tv_nsec); > > > -- > > > 2.20.1 > > >
On 4/21/20 1:44 PM, Lucas A. M. Magalhaes wrote: > This test fails intermittently in systems with heavy load as > CLOCK_PROCESS_CPUTIME_ID is subject to scheduler pressure. Thus the > test boundaries were relaxed to keep it from failing on such systems. > > A refactor of the spent time checking was made with some support > functions. With the advantage to representing time jitter in percent > of the target. > > The values used by the test boundaries are all empirical. > > --- > > Hi Carlos, > I also got skeptical about my dataset so I genegerated another one. > That .4 boundry was indeed too low. Also, the support/tst-timespec was > not actually running any tests because of a lazy bug, sorry about that. > Thanks for taking the time for reviewing this. *Almost* done. Please review suggestions and comment changes. Post v10 and I'll ACK that. > changes on V9: > Add new testcases > Fix support/tst-timespec do_run > Add comments > Change boundaries > > changes on V8: > Add support_timespec_ns > Add more tests > > This list of changes where getting to long. > --- > support/Makefile | 1 + > support/timespec.c | 64 +++++++++ > support/timespec.h | 8 ++ > support/tst-timespec.c | 320 +++++++++++++++++++++++++++++++++++++++++ > time/tst-cpuclock1.c | 52 +++---- > 5 files changed, 415 insertions(+), 30 deletions(-) > create mode 100644 support/tst-timespec.c > > diff --git a/support/Makefile b/support/Makefile > index 6e38b87ebe..cacaac96a5 100644 > --- a/support/Makefile > +++ b/support/Makefile > @@ -233,6 +233,7 @@ tests = \ > tst-test_compare \ > tst-test_compare_blob \ > tst-test_compare_string \ > + tst-timespec \ OK. Add new test. > tst-xreadlink \ > tst-xsigstack \ > > diff --git a/support/timespec.c b/support/timespec.c > index ea6b947546..103beafdc4 100644 > --- a/support/timespec.c > +++ b/support/timespec.c > @@ -19,6 +19,8 @@ > #include <support/timespec.h> > #include <stdio.h> > #include <stdint.h> > +#include <assert.h> > +#include <intprops.h> OK. > > void > test_timespec_before_impl (const char *file, int line, > @@ -57,3 +59,65 @@ test_timespec_equal_or_after_impl (const char *file, int line, > (intmax_t) diff.tv_sec, (intmax_t) diff.tv_nsec); > } > } > + > +/* Convert TIME to nanoseconds stored in shrinked in a long. Suggest: Convert TIME to nanoseconds stored in a long. > + Returns long maximum or minimum if the conversion overflows > + or underflows, respectively. */ OK. Good we document that we clamp at max or min with no wrapping. > +long > +support_timespec_ns (struct timespec time) > +{ > + long time_ns; > + if (INT_MULTIPLY_WRAPV(time.tv_sec, TIMESPEC_HZ, &time_ns)) > + { > + return (time.tv_sec < 0) ? TYPE_MINIMUM(long): TYPE_MAXIMUM(long); OK. Negative multiply that wraps clamps to minimum negative long, while positive multiple that wraps clamps to maximum positive long. > + } > + if (INT_ADD_WRAPV(time_ns, time.tv_nsec, &time_ns)) > + { > + return (time.tv_nsec < 0) ? TYPE_MINIMUM(long): TYPE_MAXIMUM(long); OK. Likewise for addition. > + } > + return time_ns; > +} > + > +/* Returns t normalized timespec with .tv_nsec < TIMESPEC_HZ Suggest: Returns time normalized timespec with .tv_nsec < TIMESPEC_HZ > + and the whole seconds added to .tv_sec. If an overflow or > + underflow occurs the values are clamped to its maximum or > + minimum respectively. */ > +struct timespec > +support_timespec_normalize (struct timespec time) > +{ > + struct timespec norm; > + if (INT_ADD_WRAPV (time.tv_sec, (time.tv_nsec / TIMESPEC_HZ), &norm.tv_sec)) > + { > + norm.tv_sec = (time.tv_nsec < 0) ? TYPE_MINIMUM (time_t): TYPE_MAXIMUM (time_t); OK. Yes, seconds clamp at min or max for the type. Which is normalized. > + norm.tv_nsec = (time.tv_nsec < 0) ? -1 * (TIMESPEC_HZ - 1) : TIMESPEC_HZ - 1; OK. Yes, nanoseconds clamp at min or max of (TIMESPEC_HZ - 1). > + return norm; > + } > + norm.tv_nsec = time.tv_nsec % TIMESPEC_HZ; OK. > + return norm; > +} > + > +/* Returns TRUE if the observed time is within the given percentage > + bounds of the expected time, and FALSE otherwise. > + For example the call > + > + support_timespec_check_in_range(expected, observed, .5, 1.2); Suggest: support_timespec_check_in_range(expected, observed, 0.5, 1.2); > + > + will check if > + > + .5 <= observed/expected <= 1.2 Suggest: 50% of expected <= observed <= 120% of expected > + > + In other words it will check if observed time is within 50% to > + 120% of the expected time. */ > +int > +support_timespec_check_in_range (struct timespec expected, struct timespec observed, > + double lower_bound, double upper_bound) > +{ > + assert (upper_bound >= lower_bound); > + long expected_norm, observed_norm; > + expected_norm = support_timespec_ns (expected); > + /* Don't divide by zero */ > + assert(expected_norm != 0); > + observed_norm = support_timespec_ns (observed); > + double ratio = (double)observed_norm / expected_norm; > + return (lower_bound <= ratio && ratio <= upper_bound); OK. > +} > diff --git a/support/timespec.h b/support/timespec.h > index c5852dfe75..fd5466745d 100644 > --- a/support/timespec.h > +++ b/support/timespec.h > @@ -48,6 +48,14 @@ void test_timespec_equal_or_after_impl (const char *file, int line, > const struct timespec left, > const struct timespec right); > > +long support_timespec_ns (struct timespec time); > + > +struct timespec support_timespec_normalize (struct timespec time); > + > +int support_timespec_check_in_range (struct timespec expected, struct timespec observed, > + double lower_bound, double upper_bound); OK. > + > + > /* Check that the timespec on the left represents a time before the > time on the right. */ > #define TEST_TIMESPEC_BEFORE(left, right) \ > diff --git a/support/tst-timespec.c b/support/tst-timespec.c > new file mode 100644 > index 0000000000..b421b219fd > --- /dev/null > +++ b/support/tst-timespec.c > @@ -0,0 +1,320 @@ > +/* Test for support_timespec_check_in_range function. > + Copyright (C) 2020 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + <https://www.gnu.org/licenses/>. */ > + > +#include <support/timespec.h> > +#include <support/check.h> > +#include <limits.h> > + > +#define TIMESPEC_HZ 1000000000 > + > +struct timespec_ns_test_case > +{ > + struct timespec time; > + long time_ns; > +}; > + > +struct timespec_norm_test_case > +{ > + struct timespec time; > + struct timespec norm; > +}; > + > +struct timespec_test_case > +{ > + struct timespec expected; > + struct timespec observed; > + double upper_bound; > + double lower_bound; > + int result; > +}; > + > +/* Test cases for timespec_ns */ > +struct timespec_ns_test_case ns_cases[] = { > + {.time = {.tv_sec = 0, .tv_nsec = 0}, > + .time_ns = 0, > + }, > + {.time = {.tv_sec = 0, .tv_nsec = 1}, > + .time_ns = 1, > + }, > + {.time = {.tv_sec = 1, .tv_nsec = 0}, > + .time_ns = TIMESPEC_HZ, > + }, > + {.time = {.tv_sec = 1, .tv_nsec = 1}, > + .time_ns = TIMESPEC_HZ + 1, > + }, > + {.time = {.tv_sec = 0, .tv_nsec = -1}, > + .time_ns = -1, > + }, > + {.time = {.tv_sec = -1, .tv_nsec = 0}, > + .time_ns = -TIMESPEC_HZ, > + }, > + {.time = {.tv_sec = -1, .tv_nsec = -1}, > + .time_ns = -TIMESPEC_HZ - 1, > + }, > + {.time = {.tv_sec = 1, .tv_nsec = -1}, > + .time_ns = TIMESPEC_HZ - 1, > + }, > + {.time = {.tv_sec = -1, .tv_nsec = 1}, > + .time_ns = -TIMESPEC_HZ + 1, > + }, > + // Overflow bondary by 2 Change all of these comments to use GNU-style e.g. /* Foo. */ > + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ, .tv_nsec = LONG_MAX%TIMESPEC_HZ - 1}, > + .time_ns = LONG_MAX - 1, > + }, > + // Overflow bondary > + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ, .tv_nsec = LONG_MAX%TIMESPEC_HZ}, > + .time_ns = LONG_MAX, > + }, > + // Underflow bondary by 1 > + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ, .tv_nsec = LONG_MIN%TIMESPEC_HZ + 1}, > + .time_ns = LONG_MIN + 1, > + }, > + // Underflow bondary > + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ, .tv_nsec = LONG_MIN%TIMESPEC_HZ}, > + .time_ns = LONG_MIN, > + }, > + // Multiplication overflow > + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ + 1, .tv_nsec = 1}, > + .time_ns = LONG_MAX, > + }, > + // Multiplication underflow > + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ - 1, .tv_nsec = -1}, > + .time_ns = LONG_MIN, > + }, > + // Sum overflows > + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ, .tv_nsec = LONG_MAX%TIMESPEC_HZ + 1}, > + .time_ns = LONG_MAX, > + }, > + // Sum underflow > + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ, .tv_nsec = LONG_MIN%TIMESPEC_HZ - 1}, > + .time_ns = LONG_MIN, > + } > +}; > + > +/* Test cases for timespec_norm */ > +struct timespec_norm_test_case norm_cases[] = { > + // Positive cases > + {.time = {.tv_sec = 0, .tv_nsec = 0}, > + .norm = {.tv_sec = 0, .tv_nsec = 0} > + }, > + {.time = {.tv_sec = 1, .tv_nsec = 0}, > + .norm = {.tv_sec = 1, .tv_nsec = 0} > + }, > + {.time = {.tv_sec = 0, .tv_nsec = 1}, > + .norm = {.tv_sec = 0, .tv_nsec = 1} > + }, > + {.time = {.tv_sec = 0, .tv_nsec = TIMESPEC_HZ}, > + .norm = {.tv_sec = 1, .tv_nsec = 0} > + }, > + {.time = {.tv_sec = 0, .tv_nsec = TIMESPEC_HZ + 1}, > + .norm = {.tv_sec = 1, .tv_nsec = 1} > + }, > + {.time = {.tv_sec = 1, .tv_nsec = TIMESPEC_HZ}, > + .norm = {.tv_sec = 2, .tv_nsec = 0} > + }, > + {.time = {.tv_sec = 1, .tv_nsec = TIMESPEC_HZ + 1}, > + .norm = {.tv_sec = 2, .tv_nsec = 1} > + }, > + // Negative cases > + {.time = {.tv_sec = 0, .tv_nsec = -TIMESPEC_HZ}, > + .norm = {.tv_sec = -1, .tv_nsec = 0} > + }, > + {.time = {.tv_sec = 0, .tv_nsec = -TIMESPEC_HZ - 1}, > + .norm = {.tv_sec = -1, .tv_nsec = -1} > + }, > + {.time = {.tv_sec = -1, .tv_nsec = -TIMESPEC_HZ}, > + .norm = {.tv_sec = -2, .tv_nsec = 0} > + }, > + {.time = {.tv_sec = -1, .tv_nsec = -TIMESPEC_HZ - 1}, > + .norm = {.tv_sec = -2, .tv_nsec = -1} > + }, > + // Overflow bondary by 2 > + {.time = {.tv_sec = LONG_MAX - 2, .tv_nsec = TIMESPEC_HZ + 1}, > + .norm = {.tv_sec = LONG_MAX - 1, 1}, > + }, > + // Overflow bondary by 1 > + {.time = {.tv_sec = LONG_MAX - 1, .tv_nsec = TIMESPEC_HZ + 1}, > + .norm = {.tv_sec = LONG_MAX, .tv_nsec = 1}, > + }, > + // Underflow bondary by 2 > + {.time = {.tv_sec = LONG_MIN + 2, .tv_nsec = -TIMESPEC_HZ - 1}, > + .norm = {.tv_sec = LONG_MIN + 1, -1}, > + }, > + // Underflow bondary by 1 > + {.time = {.tv_sec = LONG_MIN + 1, .tv_nsec = -TIMESPEC_HZ - 1}, > + .norm = {.tv_sec = LONG_MIN, .tv_nsec = -1}, > + }, > + // SUM overflow > + {.time = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > + .norm = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ - 1}, > + }, > + // SUM underflow > + {.time = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > + .norm = {.tv_sec = LONG_MIN, .tv_nsec = -1 * (TIMESPEC_HZ - 1)}, > + } > +}; > + > +/* Test cases for timespec_check_in_range */ > +struct timespec_test_case check_cases[] = { > + // 0 - In range > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > + .observed = {.tv_sec = 1, .tv_nsec = 0}, > + .upper_bound = 1, .lower_bound = 1, .result = 1, > + }, > + // 1 - Out of range > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > + .observed = {.tv_sec = 2, .tv_nsec = 0}, > + .upper_bound = 1, .lower_bound = 1, .result = 0, > + }, > + // 2 - Upper Bound > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > + .observed = {.tv_sec = 2, .tv_nsec = 0}, > + .upper_bound = 2, .lower_bound = 1, .result = 1, > + }, > + // 3 - Lower Bound > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > + .observed = {.tv_sec = 0, .tv_nsec = 0}, > + .upper_bound = 1, .lower_bound = 0, .result = 1, > + }, > + // 4 - Out of range by nanosecs > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > + .observed = {.tv_sec = 1, .tv_nsec = 500}, > + .upper_bound = 1, .lower_bound = 1, .result = 0, > + }, > + // 5 - In range by nanosecs > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > + .observed = {.tv_sec = 1, .tv_nsec = 50000}, > + .upper_bound = 1.3, .lower_bound = 1, .result = 1, > + }, > + // 6 - Big nanosecs > + {.expected = {.tv_sec = 1, .tv_nsec = 0}, > + .observed = {.tv_sec = 0, .tv_nsec = 4000000}, > + .upper_bound = 1, .lower_bound = .001, .result = 1, > + }, > + // 7 - In range Negative values > + {.expected = {.tv_sec = -1, .tv_nsec = 0}, > + .observed = {.tv_sec = -1, .tv_nsec = 0}, > + .upper_bound = 1, .lower_bound = 1, .result = 1, > + }, > + // 8 - Out of range Negative values > + {.expected = {.tv_sec = -1, .tv_nsec = 0}, > + .observed = {.tv_sec = -1, .tv_nsec = 0}, > + .upper_bound = -1, .lower_bound = -1, .result = 0, > + }, > + // 9 - Negative values with negative nanosecs > + {.expected = {.tv_sec = -1, .tv_nsec = 0}, > + .observed = {.tv_sec = -1, .tv_nsec = -2000}, > + .upper_bound = 1, .lower_bound = 1, .result = 0, > + }, > + // 10 - Strict bounds > + {.expected = {.tv_sec = -1, .tv_nsec = 0}, > + .observed = {.tv_sec = -1, .tv_nsec = -20000}, > + .upper_bound = 1.00002, .lower_bound = 1.0000191, .result = 1, > + }, > + // 11 - Strict bounds with loose upper bound > + {.expected = {.tv_sec = 1, .tv_nsec = 20000}, > + .observed = {.tv_sec = 1, .tv_nsec = 30000}, > + .upper_bound = 1.0000100000, .lower_bound = 1.0000099998, .result = 1, > + }, > + // 12 - Strict bounds with loose lower bound > + {.expected = {.tv_sec = 1, .tv_nsec = 20000}, > + .observed = {.tv_sec = 1, .tv_nsec = 30000}, > + .upper_bound = 1.0000099999, .lower_bound = 1.00000999979, .result = 1, > + }, > + // 13 - Strict bounds highest precision > + {.expected = {.tv_sec = 1, .tv_nsec = 20000}, > + .observed = {.tv_sec = 1, .tv_nsec = 30000}, > + .upper_bound = 1.00000999980001, .lower_bound = 1.00000999979999, .result = 1, > + }, > + /* Maximum/Minimum long values */ > + // 14 > + {.expected = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ - 1}, > + .observed = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ - 2}, > + .upper_bound = 1, .lower_bound = .9, .result = 1, > + }, > + // 15 - support_timespec_ns overflow > + {.expected = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > + .observed = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > + .upper_bound = 1, .lower_bound = 1, .result = 1, > + }, > + // 16 - support_timespec_ns overflow + underflow > + {.expected = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > + .observed = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > + .upper_bound = 1, .lower_bound = 1, .result = 0, > + }, > + // 17 - support_timespec_ns underflow > + {.expected = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > + .observed = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > + .upper_bound = 1, .lower_bound = 1, .result = 1, > + }, > + // 18 - support_timespec_ns underflow + overflow > + {.expected = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, > + .observed = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, > + .upper_bound = 1, .lower_bound = 1, .result = 0, > + }, > + // 19 - Biggest division > + {.expected = {.tv_sec = LONG_MAX / TIMESPEC_HZ , .tv_nsec = TIMESPEC_HZ - 1}, > + .observed = {.tv_sec = 0, .tv_nsec = 1}, > + .upper_bound = 1, .lower_bound = 1.0842021724855044e-19, .result = 1, > + }, > + // 20 - Lowest division > + {.expected = {.tv_sec = 0, .tv_nsec = 1}, > + .observed = {.tv_sec = LONG_MAX / TIMESPEC_HZ , .tv_nsec = TIMESPEC_HZ - 1}, > + .upper_bound = LONG_MAX, .lower_bound = 1, .result = 1, > + }, > +}; > + > +static int > +do_test (void) > +{ > + int i = 0; > + int ntests = sizeof (ns_cases) / sizeof (ns_cases[0]); > + > + printf("Testing support_timespec_ns\n"); > + for (i = 0; i < ntests; i++) > + { > + TEST_COMPARE (support_timespec_ns (ns_cases[i].time), > + ns_cases[i].time_ns); > + } > + > + ntests = sizeof (norm_cases) / sizeof (norm_cases[0]); > + struct timespec result; > + printf("Testing support_timespec_normalize\n"); > + for (i = 0; i < ntests; i++) > + { > + result = support_timespec_normalize (norm_cases[i].time); > + TEST_COMPARE (norm_cases[i].norm.tv_sec, result.tv_sec); > + TEST_COMPARE (norm_cases[i].norm.tv_nsec, result.tv_nsec); > + } > + > + ntests = sizeof (check_cases) / sizeof (check_cases[0]); > + printf("Testing support_timespec_check_in_range\n"); > + for (i = 0; i < ntests; i++) > + { > + /* Its hard to find which test failed with just the TEST_COMPARE report. > + So here we print every running testcase as well. */ > + printf("Test case %d\n", i); > + TEST_COMPARE (support_timespec_check_in_range > + (check_cases[i].expected, check_cases[i].observed, > + check_cases[i].lower_bound, > + check_cases[i].upper_bound), check_cases[i].result); > + } > + return 0; > +} > + > +#include <support/test-driver.c> OK. > diff --git a/time/tst-cpuclock1.c b/time/tst-cpuclock1.c > index 0120906f23..1ac611a92b 100644 > --- a/time/tst-cpuclock1.c > +++ b/time/tst-cpuclock1.c > @@ -26,6 +26,7 @@ > #include <signal.h> > #include <stdint.h> > #include <sys/wait.h> > +#include <support/timespec.h> > > /* This function is intended to rack up both user and system time. */ > static void > @@ -155,16 +156,12 @@ do_test (void) > printf ("live PID %d after sleep => %ju.%.9ju\n", > child, (uintmax_t) after.tv_sec, (uintmax_t) after.tv_nsec); > > - struct timespec diff = { .tv_sec = after.tv_sec - before.tv_sec, > - .tv_nsec = after.tv_nsec - before.tv_nsec }; > - if (diff.tv_nsec < 0) > - { > - --diff.tv_sec; > - diff.tv_nsec += 1000000000; > - } > - if (diff.tv_sec != 0 > - || diff.tv_nsec > 600000000 > - || diff.tv_nsec < 100000000) > + /* The bound values are empirically defined by testing this code over high cpu > + usage and different nice values. Of all the values we keep the 90th > + percentile of values and use those values for our testing allowed range. */ > + struct timespec diff = timespec_sub (support_timespec_normalize (after), > + support_timespec_normalize (before)); > + if (!support_timespec_check_in_range (sleeptime, diff, .9, 1.1)) OK. > { > printf ("before - after %ju.%.9ju outside reasonable range\n", > (uintmax_t) diff.tv_sec, (uintmax_t) diff.tv_nsec); > @@ -194,19 +191,16 @@ do_test (void) > } > else > { > - struct timespec d = { .tv_sec = afterns.tv_sec - after.tv_sec, > - .tv_nsec = afterns.tv_nsec - after.tv_nsec }; > - if (d.tv_nsec < 0) > - { > - --d.tv_sec; > - d.tv_nsec += 1000000000; > - } > - if (d.tv_sec > 0 > - || d.tv_nsec < sleeptime.tv_nsec > - || d.tv_nsec > sleeptime.tv_nsec * 2) > + /* The bound values are empirically defined by testing this code over > + high cpu usage and different nice values. Of all the values we keep > + the 90th percentile of values and use those values for our testing > + allowed range. */ > + diff = timespec_sub (support_timespec_normalize (afterns), > + support_timespec_normalize (after)); > + if (!support_timespec_check_in_range (sleeptime, diff, .9, 1.2)) OK. > { > printf ("nanosleep time %ju.%.9ju outside reasonable range\n", > - (uintmax_t) d.tv_sec, (uintmax_t) d.tv_nsec); > + (uintmax_t) diff.tv_sec, (uintmax_t) diff.tv_nsec); > result = 1; > } > } > @@ -240,15 +234,13 @@ do_test (void) > /* Should be close to 0.6. */ > printf ("dead PID %d => %ju.%.9ju\n", > child, (uintmax_t) dead.tv_sec, (uintmax_t) dead.tv_nsec); > - > - diff.tv_sec = dead.tv_sec - after.tv_sec; > - diff.tv_nsec = dead.tv_nsec - after.tv_nsec; > - if (diff.tv_nsec < 0) > - { > - --diff.tv_sec; > - diff.tv_nsec += 1000000000; > - } > - if (diff.tv_sec != 0 || diff.tv_nsec > 200000000) > + /* The bound values are empirically defined by testing this code over high cpu > + usage and different nice values. Of all the values we keep the 90th > + percentile of values and use those values for our testing allowed range. */ > + diff = timespec_sub (support_timespec_normalize (dead), > + support_timespec_normalize (after)); > + sleeptime.tv_nsec = 100000000; > + if (!support_timespec_check_in_range (sleeptime, diff, .9, 1.2)) OK. > { > printf ("dead - after %ju.%.9ju outside reasonable range\n", > (uintmax_t) diff.tv_sec, (uintmax_t) diff.tv_nsec); >
diff --git a/support/Makefile b/support/Makefile index 6e38b87ebe..cacaac96a5 100644 --- a/support/Makefile +++ b/support/Makefile @@ -233,6 +233,7 @@ tests = \ tst-test_compare \ tst-test_compare_blob \ tst-test_compare_string \ + tst-timespec \ tst-xreadlink \ tst-xsigstack \ diff --git a/support/timespec.c b/support/timespec.c index ea6b947546..103beafdc4 100644 --- a/support/timespec.c +++ b/support/timespec.c @@ -19,6 +19,8 @@ #include <support/timespec.h> #include <stdio.h> #include <stdint.h> +#include <assert.h> +#include <intprops.h> void test_timespec_before_impl (const char *file, int line, @@ -57,3 +59,65 @@ test_timespec_equal_or_after_impl (const char *file, int line, (intmax_t) diff.tv_sec, (intmax_t) diff.tv_nsec); } } + +/* Convert TIME to nanoseconds stored in shrinked in a long. + Returns long maximum or minimum if the conversion overflows + or underflows, respectively. */ +long +support_timespec_ns (struct timespec time) +{ + long time_ns; + if (INT_MULTIPLY_WRAPV(time.tv_sec, TIMESPEC_HZ, &time_ns)) + { + return (time.tv_sec < 0) ? TYPE_MINIMUM(long): TYPE_MAXIMUM(long); + } + if (INT_ADD_WRAPV(time_ns, time.tv_nsec, &time_ns)) + { + return (time.tv_nsec < 0) ? TYPE_MINIMUM(long): TYPE_MAXIMUM(long); + } + return time_ns; +} + +/* Returns t normalized timespec with .tv_nsec < TIMESPEC_HZ + and the whole seconds added to .tv_sec. If an overflow or + underflow occurs the values are clamped to its maximum or + minimum respectively. */ +struct timespec +support_timespec_normalize (struct timespec time) +{ + struct timespec norm; + if (INT_ADD_WRAPV (time.tv_sec, (time.tv_nsec / TIMESPEC_HZ), &norm.tv_sec)) + { + norm.tv_sec = (time.tv_nsec < 0) ? TYPE_MINIMUM (time_t): TYPE_MAXIMUM (time_t); + norm.tv_nsec = (time.tv_nsec < 0) ? -1 * (TIMESPEC_HZ - 1) : TIMESPEC_HZ - 1; + return norm; + } + norm.tv_nsec = time.tv_nsec % TIMESPEC_HZ; + return norm; +} + +/* Returns TRUE if the observed time is within the given percentage + bounds of the expected time, and FALSE otherwise. + For example the call + + support_timespec_check_in_range(expected, observed, .5, 1.2); + + will check if + + .5 <= observed/expected <= 1.2 + + In other words it will check if observed time is within 50% to + 120% of the expected time. */ +int +support_timespec_check_in_range (struct timespec expected, struct timespec observed, + double lower_bound, double upper_bound) +{ + assert (upper_bound >= lower_bound); + long expected_norm, observed_norm; + expected_norm = support_timespec_ns (expected); + /* Don't divide by zero */ + assert(expected_norm != 0); + observed_norm = support_timespec_ns (observed); + double ratio = (double)observed_norm / expected_norm; + return (lower_bound <= ratio && ratio <= upper_bound); +} diff --git a/support/timespec.h b/support/timespec.h index c5852dfe75..fd5466745d 100644 --- a/support/timespec.h +++ b/support/timespec.h @@ -48,6 +48,14 @@ void test_timespec_equal_or_after_impl (const char *file, int line, const struct timespec left, const struct timespec right); +long support_timespec_ns (struct timespec time); + +struct timespec support_timespec_normalize (struct timespec time); + +int support_timespec_check_in_range (struct timespec expected, struct timespec observed, + double lower_bound, double upper_bound); + + /* Check that the timespec on the left represents a time before the time on the right. */ #define TEST_TIMESPEC_BEFORE(left, right) \ diff --git a/support/tst-timespec.c b/support/tst-timespec.c new file mode 100644 index 0000000000..b421b219fd --- /dev/null +++ b/support/tst-timespec.c @@ -0,0 +1,320 @@ +/* Test for support_timespec_check_in_range function. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#include <support/timespec.h> +#include <support/check.h> +#include <limits.h> + +#define TIMESPEC_HZ 1000000000 + +struct timespec_ns_test_case +{ + struct timespec time; + long time_ns; +}; + +struct timespec_norm_test_case +{ + struct timespec time; + struct timespec norm; +}; + +struct timespec_test_case +{ + struct timespec expected; + struct timespec observed; + double upper_bound; + double lower_bound; + int result; +}; + +/* Test cases for timespec_ns */ +struct timespec_ns_test_case ns_cases[] = { + {.time = {.tv_sec = 0, .tv_nsec = 0}, + .time_ns = 0, + }, + {.time = {.tv_sec = 0, .tv_nsec = 1}, + .time_ns = 1, + }, + {.time = {.tv_sec = 1, .tv_nsec = 0}, + .time_ns = TIMESPEC_HZ, + }, + {.time = {.tv_sec = 1, .tv_nsec = 1}, + .time_ns = TIMESPEC_HZ + 1, + }, + {.time = {.tv_sec = 0, .tv_nsec = -1}, + .time_ns = -1, + }, + {.time = {.tv_sec = -1, .tv_nsec = 0}, + .time_ns = -TIMESPEC_HZ, + }, + {.time = {.tv_sec = -1, .tv_nsec = -1}, + .time_ns = -TIMESPEC_HZ - 1, + }, + {.time = {.tv_sec = 1, .tv_nsec = -1}, + .time_ns = TIMESPEC_HZ - 1, + }, + {.time = {.tv_sec = -1, .tv_nsec = 1}, + .time_ns = -TIMESPEC_HZ + 1, + }, + // Overflow bondary by 2 + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ, .tv_nsec = LONG_MAX%TIMESPEC_HZ - 1}, + .time_ns = LONG_MAX - 1, + }, + // Overflow bondary + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ, .tv_nsec = LONG_MAX%TIMESPEC_HZ}, + .time_ns = LONG_MAX, + }, + // Underflow bondary by 1 + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ, .tv_nsec = LONG_MIN%TIMESPEC_HZ + 1}, + .time_ns = LONG_MIN + 1, + }, + // Underflow bondary + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ, .tv_nsec = LONG_MIN%TIMESPEC_HZ}, + .time_ns = LONG_MIN, + }, + // Multiplication overflow + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ + 1, .tv_nsec = 1}, + .time_ns = LONG_MAX, + }, + // Multiplication underflow + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ - 1, .tv_nsec = -1}, + .time_ns = LONG_MIN, + }, + // Sum overflows + {.time = {.tv_sec = LONG_MAX / TIMESPEC_HZ, .tv_nsec = LONG_MAX%TIMESPEC_HZ + 1}, + .time_ns = LONG_MAX, + }, + // Sum underflow + {.time = {.tv_sec = LONG_MIN / TIMESPEC_HZ, .tv_nsec = LONG_MIN%TIMESPEC_HZ - 1}, + .time_ns = LONG_MIN, + } +}; + +/* Test cases for timespec_norm */ +struct timespec_norm_test_case norm_cases[] = { + // Positive cases + {.time = {.tv_sec = 0, .tv_nsec = 0}, + .norm = {.tv_sec = 0, .tv_nsec = 0} + }, + {.time = {.tv_sec = 1, .tv_nsec = 0}, + .norm = {.tv_sec = 1, .tv_nsec = 0} + }, + {.time = {.tv_sec = 0, .tv_nsec = 1}, + .norm = {.tv_sec = 0, .tv_nsec = 1} + }, + {.time = {.tv_sec = 0, .tv_nsec = TIMESPEC_HZ}, + .norm = {.tv_sec = 1, .tv_nsec = 0} + }, + {.time = {.tv_sec = 0, .tv_nsec = TIMESPEC_HZ + 1}, + .norm = {.tv_sec = 1, .tv_nsec = 1} + }, + {.time = {.tv_sec = 1, .tv_nsec = TIMESPEC_HZ}, + .norm = {.tv_sec = 2, .tv_nsec = 0} + }, + {.time = {.tv_sec = 1, .tv_nsec = TIMESPEC_HZ + 1}, + .norm = {.tv_sec = 2, .tv_nsec = 1} + }, + // Negative cases + {.time = {.tv_sec = 0, .tv_nsec = -TIMESPEC_HZ}, + .norm = {.tv_sec = -1, .tv_nsec = 0} + }, + {.time = {.tv_sec = 0, .tv_nsec = -TIMESPEC_HZ - 1}, + .norm = {.tv_sec = -1, .tv_nsec = -1} + }, + {.time = {.tv_sec = -1, .tv_nsec = -TIMESPEC_HZ}, + .norm = {.tv_sec = -2, .tv_nsec = 0} + }, + {.time = {.tv_sec = -1, .tv_nsec = -TIMESPEC_HZ - 1}, + .norm = {.tv_sec = -2, .tv_nsec = -1} + }, + // Overflow bondary by 2 + {.time = {.tv_sec = LONG_MAX - 2, .tv_nsec = TIMESPEC_HZ + 1}, + .norm = {.tv_sec = LONG_MAX - 1, 1}, + }, + // Overflow bondary by 1 + {.time = {.tv_sec = LONG_MAX - 1, .tv_nsec = TIMESPEC_HZ + 1}, + .norm = {.tv_sec = LONG_MAX, .tv_nsec = 1}, + }, + // Underflow bondary by 2 + {.time = {.tv_sec = LONG_MIN + 2, .tv_nsec = -TIMESPEC_HZ - 1}, + .norm = {.tv_sec = LONG_MIN + 1, -1}, + }, + // Underflow bondary by 1 + {.time = {.tv_sec = LONG_MIN + 1, .tv_nsec = -TIMESPEC_HZ - 1}, + .norm = {.tv_sec = LONG_MIN, .tv_nsec = -1}, + }, + // SUM overflow + {.time = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, + .norm = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ - 1}, + }, + // SUM underflow + {.time = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, + .norm = {.tv_sec = LONG_MIN, .tv_nsec = -1 * (TIMESPEC_HZ - 1)}, + } +}; + +/* Test cases for timespec_check_in_range */ +struct timespec_test_case check_cases[] = { + // 0 - In range + {.expected = {.tv_sec = 1, .tv_nsec = 0}, + .observed = {.tv_sec = 1, .tv_nsec = 0}, + .upper_bound = 1, .lower_bound = 1, .result = 1, + }, + // 1 - Out of range + {.expected = {.tv_sec = 1, .tv_nsec = 0}, + .observed = {.tv_sec = 2, .tv_nsec = 0}, + .upper_bound = 1, .lower_bound = 1, .result = 0, + }, + // 2 - Upper Bound + {.expected = {.tv_sec = 1, .tv_nsec = 0}, + .observed = {.tv_sec = 2, .tv_nsec = 0}, + .upper_bound = 2, .lower_bound = 1, .result = 1, + }, + // 3 - Lower Bound + {.expected = {.tv_sec = 1, .tv_nsec = 0}, + .observed = {.tv_sec = 0, .tv_nsec = 0}, + .upper_bound = 1, .lower_bound = 0, .result = 1, + }, + // 4 - Out of range by nanosecs + {.expected = {.tv_sec = 1, .tv_nsec = 0}, + .observed = {.tv_sec = 1, .tv_nsec = 500}, + .upper_bound = 1, .lower_bound = 1, .result = 0, + }, + // 5 - In range by nanosecs + {.expected = {.tv_sec = 1, .tv_nsec = 0}, + .observed = {.tv_sec = 1, .tv_nsec = 50000}, + .upper_bound = 1.3, .lower_bound = 1, .result = 1, + }, + // 6 - Big nanosecs + {.expected = {.tv_sec = 1, .tv_nsec = 0}, + .observed = {.tv_sec = 0, .tv_nsec = 4000000}, + .upper_bound = 1, .lower_bound = .001, .result = 1, + }, + // 7 - In range Negative values + {.expected = {.tv_sec = -1, .tv_nsec = 0}, + .observed = {.tv_sec = -1, .tv_nsec = 0}, + .upper_bound = 1, .lower_bound = 1, .result = 1, + }, + // 8 - Out of range Negative values + {.expected = {.tv_sec = -1, .tv_nsec = 0}, + .observed = {.tv_sec = -1, .tv_nsec = 0}, + .upper_bound = -1, .lower_bound = -1, .result = 0, + }, + // 9 - Negative values with negative nanosecs + {.expected = {.tv_sec = -1, .tv_nsec = 0}, + .observed = {.tv_sec = -1, .tv_nsec = -2000}, + .upper_bound = 1, .lower_bound = 1, .result = 0, + }, + // 10 - Strict bounds + {.expected = {.tv_sec = -1, .tv_nsec = 0}, + .observed = {.tv_sec = -1, .tv_nsec = -20000}, + .upper_bound = 1.00002, .lower_bound = 1.0000191, .result = 1, + }, + // 11 - Strict bounds with loose upper bound + {.expected = {.tv_sec = 1, .tv_nsec = 20000}, + .observed = {.tv_sec = 1, .tv_nsec = 30000}, + .upper_bound = 1.0000100000, .lower_bound = 1.0000099998, .result = 1, + }, + // 12 - Strict bounds with loose lower bound + {.expected = {.tv_sec = 1, .tv_nsec = 20000}, + .observed = {.tv_sec = 1, .tv_nsec = 30000}, + .upper_bound = 1.0000099999, .lower_bound = 1.00000999979, .result = 1, + }, + // 13 - Strict bounds highest precision + {.expected = {.tv_sec = 1, .tv_nsec = 20000}, + .observed = {.tv_sec = 1, .tv_nsec = 30000}, + .upper_bound = 1.00000999980001, .lower_bound = 1.00000999979999, .result = 1, + }, + /* Maximum/Minimum long values */ + // 14 + {.expected = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ - 1}, + .observed = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ - 2}, + .upper_bound = 1, .lower_bound = .9, .result = 1, + }, + // 15 - support_timespec_ns overflow + {.expected = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, + .observed = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, + .upper_bound = 1, .lower_bound = 1, .result = 1, + }, + // 16 - support_timespec_ns overflow + underflow + {.expected = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, + .observed = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, + .upper_bound = 1, .lower_bound = 1, .result = 0, + }, + // 17 - support_timespec_ns underflow + {.expected = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, + .observed = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, + .upper_bound = 1, .lower_bound = 1, .result = 1, + }, + // 18 - support_timespec_ns underflow + overflow + {.expected = {.tv_sec = LONG_MIN, .tv_nsec = -TIMESPEC_HZ}, + .observed = {.tv_sec = LONG_MAX, .tv_nsec = TIMESPEC_HZ}, + .upper_bound = 1, .lower_bound = 1, .result = 0, + }, + // 19 - Biggest division + {.expected = {.tv_sec = LONG_MAX / TIMESPEC_HZ , .tv_nsec = TIMESPEC_HZ - 1}, + .observed = {.tv_sec = 0, .tv_nsec = 1}, + .upper_bound = 1, .lower_bound = 1.0842021724855044e-19, .result = 1, + }, + // 20 - Lowest division + {.expected = {.tv_sec = 0, .tv_nsec = 1}, + .observed = {.tv_sec = LONG_MAX / TIMESPEC_HZ , .tv_nsec = TIMESPEC_HZ - 1}, + .upper_bound = LONG_MAX, .lower_bound = 1, .result = 1, + }, +}; + +static int +do_test (void) +{ + int i = 0; + int ntests = sizeof (ns_cases) / sizeof (ns_cases[0]); + + printf("Testing support_timespec_ns\n"); + for (i = 0; i < ntests; i++) + { + TEST_COMPARE (support_timespec_ns (ns_cases[i].time), + ns_cases[i].time_ns); + } + + ntests = sizeof (norm_cases) / sizeof (norm_cases[0]); + struct timespec result; + printf("Testing support_timespec_normalize\n"); + for (i = 0; i < ntests; i++) + { + result = support_timespec_normalize (norm_cases[i].time); + TEST_COMPARE (norm_cases[i].norm.tv_sec, result.tv_sec); + TEST_COMPARE (norm_cases[i].norm.tv_nsec, result.tv_nsec); + } + + ntests = sizeof (check_cases) / sizeof (check_cases[0]); + printf("Testing support_timespec_check_in_range\n"); + for (i = 0; i < ntests; i++) + { + /* Its hard to find which test failed with just the TEST_COMPARE report. + So here we print every running testcase as well. */ + printf("Test case %d\n", i); + TEST_COMPARE (support_timespec_check_in_range + (check_cases[i].expected, check_cases[i].observed, + check_cases[i].lower_bound, + check_cases[i].upper_bound), check_cases[i].result); + } + return 0; +} + +#include <support/test-driver.c> diff --git a/time/tst-cpuclock1.c b/time/tst-cpuclock1.c index 0120906f23..1ac611a92b 100644 --- a/time/tst-cpuclock1.c +++ b/time/tst-cpuclock1.c @@ -26,6 +26,7 @@ #include <signal.h> #include <stdint.h> #include <sys/wait.h> +#include <support/timespec.h> /* This function is intended to rack up both user and system time. */ static void @@ -155,16 +156,12 @@ do_test (void) printf ("live PID %d after sleep => %ju.%.9ju\n", child, (uintmax_t) after.tv_sec, (uintmax_t) after.tv_nsec); - struct timespec diff = { .tv_sec = after.tv_sec - before.tv_sec, - .tv_nsec = after.tv_nsec - before.tv_nsec }; - if (diff.tv_nsec < 0) - { - --diff.tv_sec; - diff.tv_nsec += 1000000000; - } - if (diff.tv_sec != 0 - || diff.tv_nsec > 600000000 - || diff.tv_nsec < 100000000) + /* The bound values are empirically defined by testing this code over high cpu + usage and different nice values. Of all the values we keep the 90th + percentile of values and use those values for our testing allowed range. */ + struct timespec diff = timespec_sub (support_timespec_normalize (after), + support_timespec_normalize (before)); + if (!support_timespec_check_in_range (sleeptime, diff, .9, 1.1)) { printf ("before - after %ju.%.9ju outside reasonable range\n", (uintmax_t) diff.tv_sec, (uintmax_t) diff.tv_nsec); @@ -194,19 +191,16 @@ do_test (void) } else { - struct timespec d = { .tv_sec = afterns.tv_sec - after.tv_sec, - .tv_nsec = afterns.tv_nsec - after.tv_nsec }; - if (d.tv_nsec < 0) - { - --d.tv_sec; - d.tv_nsec += 1000000000; - } - if (d.tv_sec > 0 - || d.tv_nsec < sleeptime.tv_nsec - || d.tv_nsec > sleeptime.tv_nsec * 2) + /* The bound values are empirically defined by testing this code over + high cpu usage and different nice values. Of all the values we keep + the 90th percentile of values and use those values for our testing + allowed range. */ + diff = timespec_sub (support_timespec_normalize (afterns), + support_timespec_normalize (after)); + if (!support_timespec_check_in_range (sleeptime, diff, .9, 1.2)) { printf ("nanosleep time %ju.%.9ju outside reasonable range\n", - (uintmax_t) d.tv_sec, (uintmax_t) d.tv_nsec); + (uintmax_t) diff.tv_sec, (uintmax_t) diff.tv_nsec); result = 1; } } @@ -240,15 +234,13 @@ do_test (void) /* Should be close to 0.6. */ printf ("dead PID %d => %ju.%.9ju\n", child, (uintmax_t) dead.tv_sec, (uintmax_t) dead.tv_nsec); - - diff.tv_sec = dead.tv_sec - after.tv_sec; - diff.tv_nsec = dead.tv_nsec - after.tv_nsec; - if (diff.tv_nsec < 0) - { - --diff.tv_sec; - diff.tv_nsec += 1000000000; - } - if (diff.tv_sec != 0 || diff.tv_nsec > 200000000) + /* The bound values are empirically defined by testing this code over high cpu + usage and different nice values. Of all the values we keep the 90th + percentile of values and use those values for our testing allowed range. */ + diff = timespec_sub (support_timespec_normalize (dead), + support_timespec_normalize (after)); + sleeptime.tv_nsec = 100000000; + if (!support_timespec_check_in_range (sleeptime, diff, .9, 1.2)) { printf ("dead - after %ju.%.9ju outside reasonable range\n", (uintmax_t) diff.tv_sec, (uintmax_t) diff.tv_nsec);