blob: 0ca8ff2fc7789c6fbf5c11146b2d966328f39dff [file] [log] [blame]
/*
* Copyright (C) Miroslav Lichvar 2015
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* Test the system adjtime() function. Check the range of supported offset,
support for readonly operation, and slew rate with different update
intervals and offsets. */
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
static int
diff_tv(struct timeval *tv1, struct timeval *tv2)
{
return 1000000 * (tv1->tv_sec - tv2->tv_sec) + (tv1->tv_usec - tv2->tv_usec);
}
static struct timeval
usec_to_tv(int usec)
{
struct timeval tv;
tv.tv_sec = usec / 1000000;
tv.tv_usec = usec % 1000000;
return tv;
}
static int
try_adjtime(struct timeval *new, struct timeval *old)
{
int r;
r = adjtime(new, old);
if (r)
printf("adjtime() failed : %s ", strerror(errno));
return r;
}
static void
reset_adjtime(void)
{
struct timeval tv;
tv = usec_to_tv(0);
try_adjtime(&tv, NULL);
}
static void
test_range(void)
{
struct timeval tv;
int i;
printf("range:\n");
for (i = 0; i < sizeof (time_t) * 8; i++) {
tv.tv_usec = 0;
tv.tv_sec = (1ULL << i) - 1;
printf("%20lld s : ", (long long)tv.tv_sec);
printf("%s\n", !try_adjtime(&tv, NULL) ? "ok" : "");
tv.tv_sec = ~tv.tv_sec;
printf("%20lld s : ", (long long)tv.tv_sec);
printf("%s\n", !try_adjtime(&tv, NULL) ? "ok" : "");
}
}
static void
test_readonly(void)
{
struct timeval tv1, tv2;
int i, r;
printf("readonly:\n");
for (i = 0; i <= 20; i++) {
tv1 = usec_to_tv(1 << i);
printf("%9d us : ", 1 << i);
try_adjtime(&tv1, NULL);
r = !try_adjtime(NULL, &tv2) && !diff_tv(&tv1, &tv2);
printf("%s\n", r ? "ok" : "fail");
}
}
static void
test_readwrite(void)
{
struct timeval tv1, tv2, tv3;
int i, r;
printf("readwrite:\n");
for (i = 0; i <= 20; i++) {
tv1 = usec_to_tv(1 << i);
tv3 = usec_to_tv(0);
printf("%9d us : ", 1 << i);
try_adjtime(&tv1, NULL);
r = !try_adjtime(&tv3, &tv2) && !diff_tv(&tv1, &tv2);
printf("%s\n", r ? "ok" : "fail");
}
}
static void
xusleep(int usec)
{
struct timeval tv;
tv = usec_to_tv(usec);
select(0, NULL, NULL, NULL, &tv);
}
static void
test_slew(void)
{
struct timeval tv1, tv2, tv3;
int i, j, k, diff, min, has_min;
printf("slew:\n");
for (i = 9; i <= 20; i++) {
printf("%9d us : ", 1 << i);
for (j = 4; j <= 20; j += 4) {
for (min = has_min = 0, k = 4; k < 16; k += 2) {
tv1 = usec_to_tv(1 << j);
tv3 = usec_to_tv(0);
xusleep(1 << i);
reset_adjtime();
xusleep(1 << i);
if (try_adjtime(&tv1, NULL))
continue;
xusleep(1 << i);
if (try_adjtime(&tv3, &tv2))
continue;
diff = diff_tv(&tv1, &tv2);
if (!has_min || min > diff) {
min = diff;
has_min = 1;
}
}
if (!has_min)
continue;
printf(" %5d (%d)", min, 1 << j);
fflush(stdout);
}
printf("\n");
}
}
int
main()
{
test_range();
test_readonly();
test_readwrite();
test_slew();
reset_adjtime();
return 0;
}