Module: xenomai-gch Branch: for-head Commit: 193aa2abeb6b73a933e49a1c711f0094419a3666 URL: http://git.xenomai.org/?p=xenomai-gch.git;a=commit;h=193aa2abeb6b73a933e49a1c711f0094419a3666
Author: Gilles Chanteperdrix <gilles.chanteperd...@xenomai.org> Date: Tue Dec 1 23:21:24 2009 +0100 testsuite: add user-space real-time signals unit test --- configure.in | 1 + include/Makefile.am | 2 +- include/testing/sigtest_syscall.h | 16 ++ ksrc/drivers/testing/Config.in | 2 + ksrc/drivers/testing/Kconfig | 10 +- ksrc/drivers/testing/Makefile | 8 +- ksrc/drivers/testing/sigtest_module.c | 137 ++++++++++++++ src/testsuite/Makefile.am | 2 +- src/testsuite/sigtest/Makefile.am | 29 +++ src/testsuite/sigtest/runinfo.in | 1 + src/testsuite/sigtest/sigtest.c | 315 +++++++++++++++++++++++++++++++++ 11 files changed, 518 insertions(+), 5 deletions(-) diff --git a/configure.in b/configure.in index d0d9a1c..37d1ad2 100644 --- a/configure.in +++ b/configure.in @@ -912,6 +912,7 @@ AC_CONFIG_FILES([ \ src/testsuite/clocktest/Makefile \ src/testsuite/klatency/Makefile \ src/testsuite/unit/Makefile \ + src/testsuite/sigtest/Makefile \ src/utils/Makefile \ src/utils/can/Makefile \ src/utils/analogy/Makefile \ diff --git a/include/Makefile.am b/include/Makefile.am index 04811a2..e882713 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -41,4 +41,4 @@ uninstall-local: dist-hook: find $(distdir)/compat -name .svn -depth -exec rm -fr \{\} \; -EXTRA_DIST = compat +EXTRA_DIST = compat testing diff --git a/include/testing/sigtest_syscall.h b/include/testing/sigtest_syscall.h new file mode 100644 index 0000000..b260cc5 --- /dev/null +++ b/include/testing/sigtest_syscall.h @@ -0,0 +1,16 @@ +#ifndef SIGTEST_SYSCALL_H +#define SIGTEST_SYSCALL_H + +#include <asm/xenomai/syscall.h> + +#define SIGTEST_SKIN_MAGIC 0x53494754 + +#define __NR_sigtest_queue 0 /* sigtest_queue(int *, size_t) */ +#define __NR_sigtest_wait_pri 1 /* sigtest_wait_pri(void) */ +#define __NR_sigtest_wait_sec 2 /* sigtest_wait_sec(void) */ + +struct sigtest_siginfo { + int sig_nr; +}; + +#endif /* SIGTEST_SYSCALL_H */ diff --git a/ksrc/drivers/testing/Config.in b/ksrc/drivers/testing/Config.in index 41c2efa..01fe460 100644 --- a/ksrc/drivers/testing/Config.in +++ b/ksrc/drivers/testing/Config.in @@ -13,4 +13,6 @@ dep_tristate 'Context switches test driver' CONFIG_XENO_DRIVERS_SWITCHTEST $CONF dep_tristate 'Kernel-only latency measurement module' CONFIG_XENO_DRIVERS_KLATENCY $XENO_DRIVERS_TIMERBENCH +dep_tristate 'User-space real-time signals testing module' CONFIG_XENO_SKIN_SIGTEST + endmenu diff --git a/ksrc/drivers/testing/Kconfig b/ksrc/drivers/testing/Kconfig index 17c6ad6..c219fb9 100644 --- a/ksrc/drivers/testing/Kconfig +++ b/ksrc/drivers/testing/Kconfig @@ -1,6 +1,6 @@ menu "Testing drivers" -config XENO_KLATENCY_MODULE +config XENO_TESTING_MODULE depends on MODULES def_tristate m @@ -13,7 +13,7 @@ config XENO_DRIVERS_TIMERBENCH See testsuite/latency for a possible front-end. config XENO_DRIVERS_KLATENCY - depends on XENO_DRIVERS_TIMERBENCH && XENO_KLATENCY_MODULE + depends on XENO_DRIVERS_TIMERBENCH && XENO_TESTING_MODULE tristate "Kernel-only latency measurement module" help Kernel module for kernel-only latency measurement. @@ -34,4 +34,10 @@ config XENO_DRIVERS_SWITCHTEST Kernel-based driver for unit testing context switches and FPU switches. +config XENO_SKIN_SIGTEST + depends on XENO_TESTING_MODULE + tristate "User-space real-time signals testing module" + help + Elementary skin for unit testing user-space real-time signals. + endmenu diff --git a/ksrc/drivers/testing/Makefile b/ksrc/drivers/testing/Makefile index e8ce95a..db9150e 100644 --- a/ksrc/drivers/testing/Makefile +++ b/ksrc/drivers/testing/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_XENO_DRIVERS_TIMERBENCH) += xeno_timerbench.o obj-$(CONFIG_XENO_DRIVERS_IRQBENCH) += xeno_irqbench.o obj-$(CONFIG_XENO_DRIVERS_SWITCHTEST) += xeno_switchtest.o obj-$(CONFIG_XENO_DRIVERS_KLATENCY) += xeno_klat.o +obj-$(CONFIG_XENO_SKIN_SIGTEST) += xeno_sigtest.o xeno_timerbench-y := timerbench.o @@ -17,6 +18,8 @@ xeno_switchtest-y := switchtest.o xeno_klat-y := klat.o +xeno_sigtest-y := sigtest_module.o + EXTRA_CFLAGS += -D__IN_XENOMAI__ -Iinclude/xenomai else @@ -29,6 +32,7 @@ obj-$(CONFIG_XENO_DRIVERS_TIMERBENCH) += xeno_timerbench.o obj-$(CONFIG_XENO_DRIVERS_IRQBENCH) += xeno_irqbench.o obj-$(CONFIG_XENO_DRIVERS_SWITCHTEST) += xeno_switchtest.o obj-$(CONFIG_XENO_DRIVERS_KLATENCY) += xeno_klat.o +obj-$(CONFIG_XENO_SKIN_SIGTEST) += xeno_sigtest.o xeno_timerbench-objs := timerbench.o xeno_irqbench-objs := irqbench.o @@ -37,8 +41,10 @@ xeno_switchtest-objs := switchtest.o xeno_klat-objs := klat.o +xeno_sigtest-objs := sigtest_module.o + export-objs := $(xeno_timerbench-objs) $(xeno_irqbench-objs) \ - $(xeno_switchtest-objs) $(xeno_klat-objs) + $(xeno_switchtest-objs) $(xeno_klat-objs) $(xeno_sigtest-objs) EXTRA_CFLAGS += -D__IN_XENOMAI__ -I$(TOPDIR)/include/xenomai -I$(TOPDIR)/include/xenomai/compat diff --git a/ksrc/drivers/testing/sigtest_module.c b/ksrc/drivers/testing/sigtest_module.c new file mode 100644 index 0000000..e4235f1 --- /dev/null +++ b/ksrc/drivers/testing/sigtest_module.c @@ -0,0 +1,137 @@ +#include <nucleus/timebase.h> +#include <nucleus/timer.h> +#include <nucleus/shadow.h> +#include <nucleus/thread.h> +#include <nucleus/heap.h> +#include <nucleus/pod.h> +#include <nucleus/types.h> +#include <testing/sigtest_syscall.h> + +static int muxid; +static xntbase_t *tbase; + +static int *sigs, next_sig; +static size_t nr_sigs; +static xnthread_t *target; +static xntimer_t sigtest_timer; + +MODULE_DESCRIPTION("signals testing interface"); +MODULE_AUTHOR("gilles.chanteperd...@xenomai.org"); +MODULE_LICENSE("GPL"); + +static void sigtest_timer_handler(xntimer_t *timer) +{ + xnshadow_mark_sig(target, muxid); + /* xnpod_schedule called later. */ +} + +static int __sigtest_queue(struct pt_regs *regs) +{ + target = xnshadow_thread(current); + nr_sigs = (size_t)__xn_reg_arg2(regs); + sigs = xnmalloc(sizeof(*sigs) * nr_sigs); + next_sig = 0; + + if (__xn_copy_from_user(sigs, (void __user *)__xn_reg_arg1(regs), + sizeof(*sigs) * nr_sigs)) { + xnfree(sigs); + return -EFAULT; + } + + xntimer_set_sched(&sigtest_timer, xnpod_current_sched()); + xntimer_start(&sigtest_timer, 10000000, 0, 0); + + return 0; +} + +static int __sigtest_wait_pri(struct pt_regs *regs) +{ + xnthread_t *thread = xnshadow_thread(current); + xnticks_t ticks = xntbase_ns2ticks(tbase, 20000000); + xnpod_suspend_thread(thread, XNDELAY, ticks, XN_RELATIVE, NULL); + if (xnthread_test_info(thread, XNBREAK)) + return -EINTR; + + return 0; +} + +static int __sigtest_wait_sec(struct pt_regs *regs) +{ + schedule_timeout_interruptible(20 * HZ / 1000 + 1); + if (signal_pending(current)) + return -EINTR; + return 0; +} + +static xnsysent_t __systab[] = { + [__NR_sigtest_queue] = {&__sigtest_queue, __xn_exec_any}, + [__NR_sigtest_wait_pri] = {&__sigtest_wait_pri, __xn_exec_primary}, + [__NR_sigtest_wait_sec] = {&__sigtest_wait_sec, __xn_exec_secondary}, +}; + +static int sigtest_unqueue(xnthread_t *thread, union xnsiginfo __user *si) +{ + struct sigtest_siginfo __user *mysi = (struct sigtest_siginfo __user *)si; + int status = sigs[next_sig]; + + __xn_put_user(next_sig, &mysi->sig_nr); + if (++next_sig == nr_sigs) { + spl_t s; + + xnfree(sigs); + xnlock_get_irqsave(&nklock, s); + xnshadow_clear_sig(thread, muxid); + xnlock_put_irqrestore(&nklock, s); + } + return status; +} + +static struct xnskin_props __props = { + .name = "sigtest", + .magic = SIGTEST_SKIN_MAGIC, + .nrcalls = ARRAY_SIZE(__systab), + .systab = __systab, + .eventcb = NULL, + .sig_unqueue = sigtest_unqueue, + .timebasep = &tbase, + .module = THIS_MODULE +}; + +int SKIN_INIT(sigtest) +{ + int err; + + xnprintf("starting sigtest services\n"); + + err = xnpod_init(); + if (err) + goto fail; + + err = xntbase_alloc("sigtest", 0, 0, &tbase); + if (err) + goto fail_shutdown_pod; + + muxid = xnshadow_register_interface(&__props); + if (muxid < 0) { + err = muxid; + fail_shutdown_pod: + xnpod_shutdown(err); + fail: + return err; + } + + xntimer_init(&sigtest_timer, tbase, sigtest_timer_handler); + + return 0; +} + +void SKIN_EXIT(sigtest) +{ + xnprintf("stopping sigtest services\n"); + xntimer_destroy(&sigtest_timer); + xnshadow_unregister_interface(muxid); + xntbase_free(tbase); + xnpod_shutdown(XNPOD_NORMAL_EXIT); +} +module_init(__sigtest_skin_init); +module_exit(__sigtest_skin_exit); diff --git a/src/testsuite/Makefile.am b/src/testsuite/Makefile.am index 0f533c2..70ab3ce 100644 --- a/src/testsuite/Makefile.am +++ b/src/testsuite/Makefile.am @@ -1 +1 @@ -SUBDIRS = latency cyclic switchtest irqbench clocktest klatency unit +SUBDIRS = latency cyclic switchtest irqbench clocktest klatency unit sigtest diff --git a/src/testsuite/sigtest/Makefile.am b/src/testsuite/sigtest/Makefile.am new file mode 100644 index 0000000..9d7a342 --- /dev/null +++ b/src/testsuite/sigtest/Makefile.am @@ -0,0 +1,29 @@ +testdir = $(exec_prefix)/share/xenomai/testsuite/sigtest + +CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC) + +bin_PROGRAMS = sigtest + +sigtest_SOURCES = sigtest.c + +sigtest_CPPFLAGS = -I$(top_srcdir)/include/posix $(XENO_USER_CFLAGS) -g -I$(top_srcdir)/include + +sigtest_LDFLAGS = $(XENO_POSIX_WRAPPERS) $(XENO_USER_LDFLAGS) -rdynamic + +sigtest_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 "#!/bin/sh" > $(DESTDIR)$(testdir)/run + @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 diff --git a/src/testsuite/sigtest/runinfo.in b/src/testsuite/sigtest/runinfo.in new file mode 100644 index 0000000..1d89013 --- /dev/null +++ b/src/testsuite/sigtest/runinfo.in @@ -0,0 +1 @@ +sigtest:posix+rtdm+sigtest:!...@exec_prefix@/bin/sigtest;popall:control_c diff --git a/src/testsuite/sigtest/sigtest.c b/src/testsuite/sigtest/sigtest.c new file mode 100644 index 0000000..77de76b --- /dev/null +++ b/src/testsuite/sigtest/sigtest.c @@ -0,0 +1,315 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <unistd.h> +#include <sys/mman.h> +#ifdef __GLIBC__ +#include <execinfo.h> +#endif /* __GLIBC__ */ + +#include <asm-generic/xenomai/bits/bind.h> +#include <asm-generic/bits/mlock_alert.h> +#include <asm-generic/bits/sigshadow.h> +#include <testing/sigtest_syscall.h> + +#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) + +static int shifted_muxid; + +int sigtest_queue(int *retvals, size_t nr) +{ + return XENOMAI_SKINCALL2(shifted_muxid, + __NR_sigtest_queue, retvals, nr); +} + +int sigtest_wait_pri(void) +{ + return XENOMAI_SKINCALL0(shifted_muxid, __NR_sigtest_wait_pri); +} + +int sigtest_wait_sec(void) +{ + return XENOMAI_SKINCALL0(shifted_muxid, __NR_sigtest_wait_sec); +} + +static xnsighandler *mysh; + +void sigtest_handler(union xnsiginfo *gen_si) +{ + mysh(gen_si); +} + +__attribute__((constructor)) void __init_sigtest_interface(void) +{ + int muxid; + + muxid = xeno_bind_skin(SIGTEST_SKIN_MAGIC, "SIGTEST", "xeno_sigtest", sigtest_handler); + + shifted_muxid = __xn_mux_shifted_id(muxid); +} + +static volatile unsigned seen; +static int cascade_res; + +void mark_seen(union xnsiginfo *gen_si) +{ + struct sigtest_siginfo *si = (struct sigtest_siginfo *)gen_si; + seen |= (1 << si->sig_nr); +} + +void mark_seen_2(int sig) +{ + seen |= 2; +} + +void mark_seen_2_bt(int sig) +{ +#ifdef __GLIBC__ + void *buf[200]; + int nelems = backtrace(buf, sizeof(buf)/sizeof(buf[0])); + fputs("\n>>>>>>>>>>>>>>>>>>>>> Please " + "check that the following backtrace looks correct:\n", stderr); + backtrace_symbols_fd(buf, nelems, 2); + fputs("<<<<<<<<<<<<<<<<<<<<< End of backtrace\n\n", stderr); +#endif /* __GLIBC__ */ + seen |= 2; +} + +void cascade_pri(union xnsiginfo *gen_si __attribute__((unused))) +{ + cascade_res = sigtest_wait_pri() == -EINTR ? -EINTR : cascade_res; +} + +void cascade_sec(union xnsiginfo *gen_si __attribute__((unused))) +{ + cascade_res = sigtest_wait_sec() == -EINTR ? -EINTR : cascade_res; +} + +static unsigned failed, success; + +#define test_assert(expr) \ + ({ \ + if (expr) { \ + ++success; \ + fprintf(stderr, #expr ": success.\n"); \ + } else { \ + ++failed; \ + fprintf(stderr, #expr " failed\n"); \ + } \ + }) + +#define check(expr, expected) \ + ({ \ + int rc = (expr); \ + if (rc == (expected)) { \ + ++success; \ + fprintf(stderr, #expr ": success.\n"); \ + } else { \ + ++failed; \ + fprintf(stderr, #expr " failed: %d\n", -rc); \ + } \ + }) + +void *cancel_with_signals(void *cookie) +{ + int *run = (int *)cookie; + int one_restart[] = { -ERESTART, }; + struct timespec ts; + + pthread_set_name_np(pthread_self(), "cancel_with_signals"); + + check(sigtest_queue(one_restart, ARRAY_SIZE(one_restart)), 0); + ts.tv_sec = 0; + ts.tv_nsec = 20000000; + __real_nanosleep(&ts, NULL); /* Wait for the signals to be + * delivered. */ + *run = 1; + pthread_exit(NULL); +} + +struct cond { + pthread_mutex_t mx; + pthread_cond_t cnd; + int val; +}; + +void *dual_signals(void *cookie) +{ + struct cond *c = (struct cond *)cookie; + int one_restart[] = { -ERESTART, }; + + pthread_set_name_np(pthread_self(), "dual_signals"); + + check(sigtest_queue(one_restart, ARRAY_SIZE(one_restart)), 0); + pthread_mutex_lock(&c->mx); + c->val = 1; + pthread_cond_signal(&c->cnd); + while (c->val != 2) + check(pthread_cond_wait(&c->cnd, &c->mx), 0); + c->val = 3; + pthread_cond_signal(&c->cnd); + pthread_mutex_unlock(&c->mx); + pthread_exit(NULL); +} + +void *dual_signals2(void *cookie) +{ + int one_restart[] = { -ERESTART, }; + + pthread_set_name_np(pthread_self(), "dual_signals"); + check(sigtest_queue(one_restart, ARRAY_SIZE(one_restart)), 0); + check(sigtest_wait_sec(), 0); + test_assert(seen == 3); + pthread_exit(NULL); +} + +int main(void) +{ + mlockall(MCL_CURRENT | MCL_FUTURE); + + int one_restart[] = { -ERESTART, }; + mysh = mark_seen; + seen = 0; + check(sigtest_queue(one_restart, ARRAY_SIZE(one_restart)), 0); + check(sigtest_wait_pri(), 0); + test_assert(seen == 1); + + seen = 0; + check(sigtest_queue(one_restart, ARRAY_SIZE(one_restart)), 0); + check(sigtest_wait_sec(), 0); + test_assert(seen == 1); + + int one_intr[] = { -EINTR, }; + seen = 0; + check(sigtest_queue(one_intr, ARRAY_SIZE(one_intr)), 0); + check(sigtest_wait_pri(), -EINTR); + test_assert(seen == 1); + + seen = 0; + check(sigtest_queue(one_intr, ARRAY_SIZE(one_intr)), 0); + check(sigtest_wait_sec(), 0); /* Signal does not interrupt + * secondary-mode syscall */ + test_assert(seen == 1); + + int sixteen_restart[] = { [0 ... 15] = -ERESTART, }; + seen = 0; + check(sigtest_queue(sixteen_restart, ARRAY_SIZE(sixteen_restart)), 0); + check(sigtest_wait_pri(), 0); + test_assert(seen == ((1 << 16) - 1)); + + seen = 0; + check(sigtest_queue(sixteen_restart, ARRAY_SIZE(sixteen_restart)), 0); + check(sigtest_wait_sec(), 0); + test_assert(seen == ((1 << 16) - 1)); + + int middle_intr[] = { [0 ... 7] = -ERESTART, [8] = -EINTR, [9 ... 15] = -ERESTART, + }; + seen = 0; + check(sigtest_queue(middle_intr, ARRAY_SIZE(middle_intr)), 0); + check(sigtest_wait_pri(), -EINTR); + test_assert(seen == ((1 << 16) - 1)); + + seen = 0; + check(sigtest_queue(middle_intr, ARRAY_SIZE(middle_intr)), 0); + check(sigtest_wait_sec(), 0); /* Signal does not interrupt + * secondary-mode syscall */ + test_assert(seen == ((1 << 16) - 1)); + + int seventeen_restart[] = { [0 ... 16] = -ERESTART }; + mysh = cascade_pri; + cascade_res = ~0; + check(sigtest_queue(seventeen_restart, ARRAY_SIZE(seventeen_restart)), 0); + check(sigtest_wait_pri(), 0); + test_assert(cascade_res == ~0); + + cascade_res = ~0; + check(sigtest_queue(seventeen_restart, ARRAY_SIZE(seventeen_restart)), 0); + check(sigtest_wait_sec(), 0); + test_assert(cascade_res == ~0); + + int seventeen_intr[] = { [0 ... 15] = -ERESTART, [16] = -EINTR }; + cascade_res = ~0; + check(sigtest_queue(seventeen_intr, ARRAY_SIZE(seventeen_intr)), 0); + check(sigtest_wait_pri(), 0); + test_assert(cascade_res == -EINTR); + + cascade_res = ~0; + check(sigtest_queue(seventeen_intr, ARRAY_SIZE(seventeen_intr)), 0); + check(sigtest_wait_sec(), 0); + test_assert(cascade_res == -EINTR); + + /* Cascade secondary mode call. */ + mysh = cascade_sec; + cascade_res = ~0; + check(sigtest_queue(seventeen_restart, ARRAY_SIZE(seventeen_restart)), 0); + check(sigtest_wait_pri(), 0); + test_assert(cascade_res == ~0); + + cascade_res = ~0; + check(sigtest_queue(seventeen_restart, ARRAY_SIZE(seventeen_restart)), 0); + check(sigtest_wait_sec(), 0); + test_assert(cascade_res == ~0); + + cascade_res = ~0; + check(sigtest_queue(seventeen_intr, ARRAY_SIZE(seventeen_intr)), 0); + check(sigtest_wait_pri(), 0); + test_assert(cascade_res == ~0); + + cascade_res = ~0; + check(sigtest_queue(seventeen_intr, ARRAY_SIZE(seventeen_intr)), 0); + check(sigtest_wait_sec(), 0); + test_assert(cascade_res == ~0); + + /* Try and destroy a thread with pending signals. They should + be discarded. */ + pthread_t tid; + int run = 0; + mysh = mark_seen; + seen = 0; + pthread_create(&tid, NULL, cancel_with_signals, &run); + pthread_join(tid, NULL); + test_assert(run == 1 && seen == 0); + + /* Try and mix linux signals and xeno signals (this test does + not work as expected, but turns out to be a good test for + pthread_cond_wait and signals, so, keep it). */ + struct timespec ts; + struct cond c; + mysh = mark_seen; + seen = 0; + pthread_mutex_init(&c.mx, NULL); + pthread_cond_init(&c.cnd, NULL); + c.val = 0; + signal(SIGUSR1, mark_seen_2); + pthread_create(&tid, NULL, dual_signals, &c); + check(pthread_mutex_lock(&c.mx), 0); + while (c.val != 1) + check(pthread_cond_wait(&c.cnd, &c.mx), 0); + ts.tv_sec = 0; + ts.tv_nsec = 20000000; + nanosleep(&ts, NULL); + c.val = 2; + /* thread received the xeno signals, now send the linux signal */ + pthread_kill(tid, SIGUSR1); + pthread_cond_signal(&c.cnd); /* Now, wake-up. */ + while (c.val != 3) + check(pthread_cond_wait(&c.cnd, &c.mx), 0); + pthread_mutex_unlock(&c.mx); + test_assert(seen == 3); + pthread_join(tid, NULL); + + /* Try and mix linux signals and xeno signals. Take 2. */ + signal(SIGUSR1, mark_seen_2_bt); + seen = 0; + pthread_create(&tid, NULL, dual_signals2, NULL); + ts.tv_sec = 0; + ts.tv_nsec = 15000000; + nanosleep(&ts, NULL); + pthread_kill(tid, SIGUSR1); + pthread_join(tid, NULL); + + fprintf(stderr, "Failed %u/%u\n", failed, success + failed); + sleep(1); + exit(failed ? EXIT_FAILURE : EXIT_SUCCESS); +} _______________________________________________ Xenomai-git mailing list Xenomai-git@gna.org https://mail.gna.org/listinfo/xenomai-git