| /* |
| * 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; |
| } |