Hi,

here comes a new testsuite tool to add the required [1] clock validation
against unsynchronised TSCs on SMP boxes. It can also be used to detect
certain classes of broken clocks (TSCs...) on UP and to measure the
drift against the Linux clock (gettimeofday). The latter is interesting
to check the potential impact of new tsc-to-ns conversion functions and
- once we provide such a feature - the synchronisation of Xenomai's time
base with Linux (or other clocks).

Feedback welcome!

Once everyone is fine with it, I would like to apply it to trunk. IMO we
should also add this test to the xeno_test script (if someone feels like
providing an add-on patch, don't hesitate).

Jan


[1] https://mail.gna.org/public/xenomai-core/2007-04/msg00022.html
---
 configure.in                        |    1 
 src/testsuite/Makefile.am           |    2 
 src/testsuite/clocktest/Makefile.am |   26 ++++
 src/testsuite/clocktest/clocktest.c |  230 ++++++++++++++++++++++++++++++++++++
 src/testsuite/clocktest/runinfo.in  |    1 
 5 files changed, 259 insertions(+), 1 deletion(-)

Index: xenomai/configure.in
===================================================================
--- xenomai.orig/configure.in
+++ xenomai/configure.in
@@ -641,6 +641,7 @@ AC_CONFIG_FILES([ \
                src/testsuite/cyclic/Makefile \
                src/testsuite/switchtest/Makefile \
        src/testsuite/irqbench/Makefile \
+       src/testsuite/clocktest/Makefile \
        src/utils/Makefile \
        src/utils/can/Makefile \
                include/Makefile \
Index: xenomai/src/testsuite/Makefile.am
===================================================================
--- xenomai.orig/src/testsuite/Makefile.am
+++ xenomai/src/testsuite/Makefile.am
@@ -1 +1 @@
-SUBDIRS = latency switchbench cyclic switchtest irqbench
+SUBDIRS = latency switchbench cyclic switchtest irqbench clocktest
Index: xenomai/src/testsuite/clocktest/Makefile.am
===================================================================
--- /dev/null
+++ xenomai/src/testsuite/clocktest/Makefile.am
@@ -0,0 +1,26 @@
+testdir = $(exec_prefix)/share/xenomai/testsuite/clocktest
+
+bin_PROGRAMS = clocktest
+
+clocktest_SOURCES = clocktest.c
+
+clocktest_CPPFLAGS = -I$(top_srcdir)/include/posix $(XENO_USER_CFLAGS) 
-I$(top_srcdir)/include
+
+clocktest_LDFLAGS = $(XENO_POSIX_WRAPPERS) $(XENO_USER_LDFLAGS)
+
+clocktest_LDADD = \
+       ../../skins/posix/libpthread_rt.la -lpthread -lrt
+
+install-data-local:
+       $(mkinstalldirs) $(DESTDIR)$(testdir)
+       @sed -e's,@exec_prefix\@,$(exec_prefix),g' $(srcdir)/runinfo.in > 
$(DESTDIR)$(testdir)/.runinfo
+       @echo "\$${DESTDIR}$(exec_prefix)/bin/xeno-load \`dirname \$$0\` \$$*" 
> $(DESTDIR)$(testdir)/run
+       @chmod +x $(DESTDIR)$(testdir)/run
+
+uninstall-local:
+       $(RM) $(DESTDIR)$(testdir)/.runinfo $(DESTDIR)$(testdir)/run
+
+run: all
+       @$(top_srcdir)/scripts/xeno-load --verbose
+
+EXTRA_DIST = runinfo.in
Index: xenomai/src/testsuite/clocktest/clocktest.c
===================================================================
--- /dev/null
+++ xenomai/src/testsuite/clocktest/clocktest.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2007 Jan Kiszka <[EMAIL PROTECTED]>.
+ *
+ * Xenomai is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Xenomai 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 Xenomai; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+#ifndef HAVE_RECENT_SETAFFINITY
+#ifdef HAVE_OLD_SETAFFINITY
+#define sched_setaffinity(pid, len, mask)      sched_setaffinity(pid, mask)
+#else /* !HAVE_OLD_SETAFFINITY */
+#ifndef __cpu_set_t_defined
+typedef unsigned long cpu_set_t;
+#endif
+#define sched_setaffinity(pid, len, mask)      do { } while (0)
+#define CPU_ZERO(set)                          memset(set, 0, sizeof(*set))
+#define CPU_SET(n, set)                                do { } while (0)
+#endif /* !HAVE_OLD_SETAFFINITY */
+#endif /* !HAVE_RECENT_SETAFFINITY */
+
+pthread_spinlock_t lock;
+unsigned long long last_common = 0;
+clockid_t clock_id = CLOCK_REALTIME;
+
+struct per_cpu_data {
+    unsigned long long first_tod, first_clock;
+    int first_round;
+    long long offset;
+    double drift;
+    unsigned long warps;
+    unsigned long long max_warp;
+    pthread_t thread;
+} *per_cpu_data;
+
+static inline unsigned long long read_clock(clockid_t clock_id)
+{
+    struct timespec ts;
+
+    clock_gettime(clock_id, &ts);
+    return ts.tv_nsec + ts.tv_sec * 1000000000ULL;
+}
+
+static inline unsigned long long read_reference_clock(void)
+{
+    struct timeval tv;
+
+    gettimeofday(&tv, NULL);
+    return tv.tv_usec * 1000ULL + tv.tv_sec * 1000000000ULL;
+}
+
+void check_reference(struct per_cpu_data *per_cpu_data)
+{
+    unsigned long long clock_val[10], tod_val[10];
+    long long delta, min_delta;
+    int i, idx;
+
+    for (i = 0; i < 10; i++) {
+        tod_val[i] = read_reference_clock();
+        clock_val[i] = read_clock(clock_id);
+    }
+
+    min_delta = tod_val[1] - tod_val[0];
+    idx = 1;
+
+    for (i = 2; i < 10; i++) {
+        delta = tod_val[i] - tod_val[i-1];
+        if (delta < min_delta) {
+            min_delta = delta;
+            idx = i;
+        }
+    }
+
+    if (per_cpu_data->first_round) {
+        per_cpu_data->first_round = 0;
+
+        per_cpu_data->first_tod = tod_val[idx];
+        per_cpu_data->first_clock = clock_val[idx];
+    } else
+        per_cpu_data->drift =
+            (clock_val[idx] - per_cpu_data->first_clock) /
+            (double)(tod_val[idx] - per_cpu_data->first_tod) - 1;
+
+    per_cpu_data->offset = clock_val[idx] - tod_val[idx];
+}
+
+void check_time_warps(struct per_cpu_data *per_cpu_data)
+{
+    int i;
+    unsigned long long last, now;
+    long long incr;
+
+    for (i = 0; i < 100; i++) {
+        pthread_spin_lock(&lock);
+        now = read_clock(clock_id);
+        last = last_common;
+        last_common = now;
+        pthread_spin_unlock(&lock);
+
+        incr = now - last;
+        if (incr < 0) {
+            per_cpu_data->warps++;
+            if (incr > per_cpu_data->max_warp)
+                per_cpu_data->max_warp = incr;
+        }
+    }
+}
+
+void *cpu_thread(void *arg)
+{
+    int cpuid = (long)arg;
+    struct sched_param param = { .sched_priority = 1 };
+    struct timespec delay = { 0, 0 };
+    cpu_set_t cpu_set;
+
+    srandom(read_reference_clock());
+
+    CPU_ZERO(&cpu_set);
+    CPU_SET(cpuid, &cpu_set);
+    sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
+    pthread_setschedparam(pthread_self(), SCHED_FIFO, &param);
+
+    while (1) {
+        check_reference(&per_cpu_data[cpuid]);
+
+        check_time_warps(&per_cpu_data[cpuid]);
+
+        delay.tv_nsec = 1000000 + random() * 4000000;
+        nanosleep(&delay, NULL);
+    }
+}
+
+void sighand(int signal)
+{
+    exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+    int cpus = sysconf(_SC_NPROCESSORS_ONLN);
+    int i;
+    char c;
+
+    while ((c = getopt(argc,argv,"C:T:")) != EOF)
+        switch (c) {
+            case 'C':
+                clock_id = atoi(optarg);
+                break;
+
+            case 'T':
+                alarm(atoi(optarg));
+                break;
+
+            default:
+                fprintf(stderr, "usage: clocktest [options]\n"
+                        "  [-C <clock_id>]              # tested clock, 
default=%d (CLOCK_REALTIME)\n"
+                        "  [-T <test_duration_seconds>] # default=0, so ^C to 
end\n",
+                        CLOCK_REALTIME);
+                exit(2);
+        }
+
+    mlockall(MCL_CURRENT | MCL_FUTURE);
+
+    signal(SIGALRM, sighand);
+
+    pthread_spin_init(&lock, 0);
+
+    per_cpu_data = malloc(sizeof(*per_cpu_data) * cpus);
+    if (!per_cpu_data) {
+        fprintf(stderr, "%s\n", strerror(ENOMEM));
+        exit(1);
+    }
+    memset(per_cpu_data, 0, sizeof(*per_cpu_data) * cpus);
+
+    for (i = 0; i < cpus; i++) {
+        per_cpu_data[i].first_round = 1;
+        pthread_create(&per_cpu_data[i].thread, NULL, cpu_thread,
+                    (void *)i);
+    }
+
+    printf("== Tested clock: %d (", clock_id);
+    switch (clock_id) {
+        case CLOCK_REALTIME:
+            printf("CLOCK_REALTIME");
+            break;
+
+        case CLOCK_MONOTONIC:
+            printf("CLOCK_MONOTONIC");
+            break;
+
+        default:
+            printf("<unknown>");
+            break;
+    }
+    printf(")\nCPU      ToD offset [us] ToD drift [us/s]      warps max delta 
[us]\n"
+              "--- -------------------- ---------------- ---------- 
--------------\n");
+
+    while (1) {
+        for (i = 0; i < cpus; i++)
+            printf("%3d %20.1f %16.3f %10lu %14.1f\n",
+                   i,
+                   per_cpu_data[i].offset/1000.0,
+                   per_cpu_data[i].drift * 1000000.0,
+                   per_cpu_data[i].warps,
+                   per_cpu_data[i].max_warp/1000.0);
+        usleep(250000);
+        printf("\033[%dA", cpus);
+    }
+}
Index: xenomai/src/testsuite/clocktest/runinfo.in
===================================================================
--- /dev/null
+++ xenomai/src/testsuite/clocktest/runinfo.in
@@ -0,0 +1 @@
+clocktest:posix:[EMAIL PROTECTED]@/bin/clocktest;popall:control_c

Attachment: signature.asc
Description: OpenPGP digital signature

_______________________________________________
Xenomai-core mailing list
Xenomai-core@gna.org
https://mail.gna.org/listinfo/xenomai-core

Reply via email to