forked from Imagelibrary/rtems
@@ -1837,6 +1837,18 @@ librtemstest_a_SOURCES += libtest/testbusy.c
|
||||
librtemstest_a_SOURCES += libtest/testextension.c
|
||||
librtemstest_a_SOURCES += libtest/testparallel.c
|
||||
librtemstest_a_SOURCES += libtest/testwrappers.c
|
||||
librtemstest_a_SOURCES += libtest/t-test.c
|
||||
librtemstest_a_SOURCES += libtest/t-test-checks.c
|
||||
librtemstest_a_SOURCES += libtest/t-test-checks-eno.c
|
||||
librtemstest_a_SOURCES += libtest/t-test-checks-psx.c
|
||||
librtemstest_a_SOURCES += libtest/t-test-hash-sha256.c
|
||||
librtemstest_a_SOURCES += libtest/t-test-malloc.c
|
||||
librtemstest_a_SOURCES += libtest/t-test-rtems.c
|
||||
librtemstest_a_SOURCES += libtest/t-test-rtems-fds.c
|
||||
librtemstest_a_SOURCES += libtest/t-test-rtems-heap.c
|
||||
librtemstest_a_SOURCES += libtest/t-test-rtems-measure.c
|
||||
librtemstest_a_SOURCES += libtest/t-test-rtems-objs.c
|
||||
librtemstest_a_SOURCES += libtest/t-test-time.c
|
||||
|
||||
project_lib_LIBRARIES += libftpd.a
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ include_HEADERS += include/poll.h
|
||||
include_HEADERS += include/rtems.h
|
||||
include_HEADERS += include/sha256.h
|
||||
include_HEADERS += include/sha512.h
|
||||
include_HEADERS += include/t.h
|
||||
include_HEADERS += include/xz.h
|
||||
include_HEADERS += include/zconf.h
|
||||
include_HEADERS += include/zlib.h
|
||||
|
||||
2377
cpukit/include/t.h
Normal file
2377
cpukit/include/t.h
Normal file
File diff suppressed because it is too large
Load Diff
145
cpukit/libtest/t-test-checks-eno.c
Normal file
145
cpukit/libtest/t-test-checks-eno.c
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (C) 2018 embedded brains GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <t.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#define T_STRERROR_CASE(eno) case eno: return #eno
|
||||
|
||||
const char *T_strerror(int eno)
|
||||
{
|
||||
switch (eno) {
|
||||
case 0:
|
||||
return "0";
|
||||
T_STRERROR_CASE(E2BIG);
|
||||
T_STRERROR_CASE(EACCES);
|
||||
T_STRERROR_CASE(EADDRINUSE);
|
||||
T_STRERROR_CASE(EADDRNOTAVAIL);
|
||||
T_STRERROR_CASE(EAFNOSUPPORT);
|
||||
T_STRERROR_CASE(EAGAIN);
|
||||
T_STRERROR_CASE(EALREADY);
|
||||
T_STRERROR_CASE(EBADF);
|
||||
T_STRERROR_CASE(EBADMSG);
|
||||
T_STRERROR_CASE(EBUSY);
|
||||
T_STRERROR_CASE(ECANCELED);
|
||||
T_STRERROR_CASE(ECHILD);
|
||||
T_STRERROR_CASE(ECONNABORTED);
|
||||
T_STRERROR_CASE(ECONNREFUSED);
|
||||
T_STRERROR_CASE(ECONNRESET);
|
||||
T_STRERROR_CASE(EDEADLK);
|
||||
T_STRERROR_CASE(EDESTADDRREQ);
|
||||
T_STRERROR_CASE(EDOM);
|
||||
T_STRERROR_CASE(EDQUOT);
|
||||
T_STRERROR_CASE(EEXIST);
|
||||
T_STRERROR_CASE(EFAULT);
|
||||
T_STRERROR_CASE(EFBIG);
|
||||
T_STRERROR_CASE(EHOSTDOWN);
|
||||
T_STRERROR_CASE(EHOSTUNREACH);
|
||||
T_STRERROR_CASE(EIDRM);
|
||||
T_STRERROR_CASE(EILSEQ);
|
||||
T_STRERROR_CASE(EINPROGRESS);
|
||||
T_STRERROR_CASE(EINTR);
|
||||
T_STRERROR_CASE(EINVAL);
|
||||
T_STRERROR_CASE(EIO);
|
||||
T_STRERROR_CASE(EISCONN);
|
||||
T_STRERROR_CASE(EISDIR);
|
||||
T_STRERROR_CASE(ELOOP);
|
||||
T_STRERROR_CASE(EMFILE);
|
||||
T_STRERROR_CASE(EMLINK);
|
||||
T_STRERROR_CASE(EMSGSIZE);
|
||||
T_STRERROR_CASE(EMULTIHOP);
|
||||
T_STRERROR_CASE(ENAMETOOLONG);
|
||||
T_STRERROR_CASE(ENETDOWN);
|
||||
T_STRERROR_CASE(ENETRESET);
|
||||
T_STRERROR_CASE(ENETUNREACH);
|
||||
T_STRERROR_CASE(ENFILE);
|
||||
T_STRERROR_CASE(ENOBUFS);
|
||||
#ifdef ENODATA
|
||||
T_STRERROR_CASE(ENODATA);
|
||||
#endif
|
||||
T_STRERROR_CASE(ENODEV);
|
||||
T_STRERROR_CASE(ENOENT);
|
||||
T_STRERROR_CASE(ENOEXEC);
|
||||
T_STRERROR_CASE(ENOLCK);
|
||||
T_STRERROR_CASE(ENOLINK);
|
||||
T_STRERROR_CASE(ENOMEM);
|
||||
T_STRERROR_CASE(ENOMSG);
|
||||
T_STRERROR_CASE(ENOPROTOOPT);
|
||||
T_STRERROR_CASE(ENOSPC);
|
||||
#ifdef ENOSR
|
||||
T_STRERROR_CASE(ENOSR);
|
||||
#endif
|
||||
#ifdef ENOSTR
|
||||
T_STRERROR_CASE(ENOSTR);
|
||||
#endif
|
||||
T_STRERROR_CASE(ENOSYS);
|
||||
T_STRERROR_CASE(ENOTCONN);
|
||||
T_STRERROR_CASE(ENOTDIR);
|
||||
T_STRERROR_CASE(ENOTEMPTY);
|
||||
T_STRERROR_CASE(ENOTRECOVERABLE);
|
||||
T_STRERROR_CASE(ENOTSOCK);
|
||||
#if ENOTSUP != EOPNOTSUPP
|
||||
T_STRERROR_CASE(ENOTSUP);
|
||||
#endif
|
||||
T_STRERROR_CASE(ENOTTY);
|
||||
T_STRERROR_CASE(ENXIO);
|
||||
T_STRERROR_CASE(EOPNOTSUPP);
|
||||
T_STRERROR_CASE(EOVERFLOW);
|
||||
T_STRERROR_CASE(EOWNERDEAD);
|
||||
T_STRERROR_CASE(EPERM);
|
||||
T_STRERROR_CASE(EPFNOSUPPORT);
|
||||
T_STRERROR_CASE(EPIPE);
|
||||
T_STRERROR_CASE(EPROTO);
|
||||
T_STRERROR_CASE(EPROTONOSUPPORT);
|
||||
T_STRERROR_CASE(EPROTOTYPE);
|
||||
T_STRERROR_CASE(ERANGE);
|
||||
T_STRERROR_CASE(EROFS);
|
||||
T_STRERROR_CASE(ESPIPE);
|
||||
T_STRERROR_CASE(ESRCH);
|
||||
T_STRERROR_CASE(ESTALE);
|
||||
#ifdef ETIME
|
||||
T_STRERROR_CASE(ETIME);
|
||||
#endif
|
||||
T_STRERROR_CASE(ETIMEDOUT);
|
||||
T_STRERROR_CASE(ETOOMANYREFS);
|
||||
T_STRERROR_CASE(ETXTBSY);
|
||||
T_STRERROR_CASE(EXDEV);
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
void T_check_eno(int a, const T_check_context *t, int e)
|
||||
{
|
||||
T_check_true(a == e, t, "%s == %s", T_strerror(a), T_strerror(e));
|
||||
}
|
||||
|
||||
void T_check_eno_success(int a, const T_check_context *t)
|
||||
{
|
||||
T_check_eno(a, t, 0);
|
||||
}
|
||||
17
cpukit/libtest/t-test-checks-psx.c
Normal file
17
cpukit/libtest/t-test-checks-psx.c
Normal file
@@ -0,0 +1,17 @@
|
||||
#include <t.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
void T_check_psx_error(int a, const T_check_context *t, int eeno)
|
||||
{
|
||||
int aeno;
|
||||
|
||||
aeno = errno;
|
||||
T_check_true(a == -1 && aeno == eeno, t, "%i == -1, %s == %s", a,
|
||||
T_strerror(aeno), T_strerror(eeno));
|
||||
}
|
||||
|
||||
void T_check_psx_success(int a, const T_check_context *t)
|
||||
{
|
||||
T_check_true(a == 0, t, "%i == 0, %s", a, T_strerror(errno));
|
||||
}
|
||||
328
cpukit/libtest/t-test-checks.c
Normal file
328
cpukit/libtest/t-test-checks.c
Normal file
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (C) 2018 embedded brains GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <t.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
void
|
||||
T_check_eq_ptr(const void *a, const T_check_context_msg *t, const void *e)
|
||||
{
|
||||
T_check_true(a == e, &t->base, "%s", t->msg);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_ne_ptr(const void *a, const T_check_context_msg *t, const void *e)
|
||||
{
|
||||
T_check_true(a != e, &t->base, "%s", t->msg);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_null(const void *a, const T_check_context_msg *t)
|
||||
{
|
||||
T_check_true(a == NULL, &t->base, "%s == NULL", t->msg);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_not_null(const void *a, const T_check_context_msg *t)
|
||||
{
|
||||
T_check_true(a != NULL, &t->base, "%s != NULL", t->msg);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_eq_mem(const void *a, const T_check_context_msg *t, const void *e,
|
||||
size_t n)
|
||||
{
|
||||
T_check_true(memcmp(a, e, n) == 0, &t->base, "%s", t->msg);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_ne_mem(const void *a, const T_check_context_msg *t, const void *e,
|
||||
size_t n)
|
||||
{
|
||||
T_check_true(memcmp(a, e, n) != 0, &t->base, "%s", t->msg);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_eq_str(const char *a, const T_check_context *t, const char *e)
|
||||
{
|
||||
T_check_true(strcmp(a, e) == 0, t, "\"%s\" == \"%s\"", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_ne_str(const char *a, const T_check_context *t, const char *e)
|
||||
{
|
||||
T_check_true(strcmp(a, e) != 0, t, "\"%s\" != \"%s\"", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_eq_nstr(const char *a, const T_check_context *t, const char *e, size_t n)
|
||||
{
|
||||
T_check_true(strncmp(a, e, n) == 0, t, "\"%.*s\" == \"%.*s\"", (int)n, a,
|
||||
(int)n, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_ne_nstr(const char *a, const T_check_context *t, const char *e, size_t n)
|
||||
{
|
||||
T_check_true(strncmp(a, e, n) != 0, t, "\"%.*s\" != \"%.*s\"", (int)n, a,
|
||||
(int)n, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_eq_char(char a, const T_check_context *t, char e)
|
||||
{
|
||||
T_check_true(a == e, t, "'%c' == '%c'", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_ne_char(char a, const T_check_context *t, char e)
|
||||
{
|
||||
T_check_true(a != e, t, "'%c' != '%c'", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_eq_int(int a, const T_check_context *t, int e)
|
||||
{
|
||||
T_check_true(a == e, t, "%i == %i", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_ne_int(int a, const T_check_context *t, int e)
|
||||
{
|
||||
T_check_true(a != e, t, "%i != %i", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_ge_int(int a, const T_check_context *t, int e)
|
||||
{
|
||||
T_check_true(a >= e, t, "%i >= %i", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_gt_int(int a, const T_check_context *t, int e)
|
||||
{
|
||||
T_check_true(a > e, t, "%i > %i", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_le_int(int a, const T_check_context *t, int e)
|
||||
{
|
||||
T_check_true(a <= e, t, "%i <= %i", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_lt_int(int a, const T_check_context *t, int e)
|
||||
{
|
||||
T_check_true(a < e, t, "%i < %i", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_eq_uint(unsigned int a, const T_check_context *t, unsigned int e)
|
||||
{
|
||||
T_check_true(a == e, t, "%u == %u", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_ne_uint(unsigned int a, const T_check_context *t, unsigned int e)
|
||||
{
|
||||
T_check_true(a != e, t, "%u != %u", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_ge_uint(unsigned int a, const T_check_context *t, unsigned int e)
|
||||
{
|
||||
T_check_true(a >= e, t, "%u >= %u", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_gt_uint(unsigned int a, const T_check_context *t, unsigned int e)
|
||||
{
|
||||
T_check_true(a > e, t, "%u > %u", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_le_uint(unsigned int a, const T_check_context *t, unsigned int e)
|
||||
{
|
||||
T_check_true(a <= e, t, "%u <= %u", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_lt_uint(unsigned int a, const T_check_context *t, unsigned int e)
|
||||
{
|
||||
T_check_true(a < e, t, "%u < %u", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_eq_long(long a, const T_check_context *t, long e)
|
||||
{
|
||||
T_check_true(a == e, t, "%li == %li", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_ne_long(long a, const T_check_context *t, long e)
|
||||
{
|
||||
T_check_true(a != e, t, "%li != %li", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_ge_long(long a, const T_check_context *t, long e)
|
||||
{
|
||||
T_check_true(a >= e, t, "%li >= %li", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_gt_long(long a, const T_check_context *t, long e)
|
||||
{
|
||||
T_check_true(a > e, t, "%li > %li", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_le_long(long a, const T_check_context *t, long e)
|
||||
{
|
||||
T_check_true(a <= e, t, "%li <= %li", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_lt_long(long a, const T_check_context *t, long e)
|
||||
{
|
||||
T_check_true(a < e, t, "%li < %li", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_eq_ulong(unsigned long a, const T_check_context *t, unsigned long e)
|
||||
{
|
||||
T_check_true(a == e, t, "%lu == %lu", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_ne_ulong(unsigned long a, const T_check_context *t, unsigned long e)
|
||||
{
|
||||
T_check_true(a != e, t, "%lu != %lu", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_ge_ulong(unsigned long a, const T_check_context *t, unsigned long e)
|
||||
{
|
||||
T_check_true(a >= e, t, "%lu >= %lu", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_gt_ulong(unsigned long a, const T_check_context *t, unsigned long e)
|
||||
{
|
||||
T_check_true(a > e, t, "%lu > %lu", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_le_ulong(unsigned long a, const T_check_context *t, unsigned long e)
|
||||
{
|
||||
T_check_true(a <= e, t, "%lu <= %lu", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_lt_ulong(unsigned long a, const T_check_context *t, unsigned long e)
|
||||
{
|
||||
T_check_true(a < e, t, "%lu < %lu", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_eq_ll(long long a, const T_check_context *t, long long e)
|
||||
{
|
||||
T_check_true(a == e, t, "%lli == %lli", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_ne_ll(long long a, const T_check_context *t, long long e)
|
||||
{
|
||||
T_check_true(a != e, t, "%lli != %lli", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_ge_ll(long long a, const T_check_context *t, long long e)
|
||||
{
|
||||
T_check_true(a >= e, t, "%lli >= %lli", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_gt_ll(long long a, const T_check_context *t, long long e)
|
||||
{
|
||||
T_check_true(a > e, t, "%lli > %lli", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_le_ll(long long a, const T_check_context *t, long long e)
|
||||
{
|
||||
T_check_true(a <= e, t, "%lli <= %lli", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_lt_ll(long long a, const T_check_context *t, long long e)
|
||||
{
|
||||
T_check_true(a < e, t, "%lli < %lli", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_eq_ull(unsigned long long a, const T_check_context *t,
|
||||
unsigned long long e)
|
||||
{
|
||||
T_check_true(a == e, t, "%llu == %llu", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_ne_ull(unsigned long long a, const T_check_context *t,
|
||||
unsigned long long e)
|
||||
{
|
||||
T_check_true(a != e, t, "%llu != %llu", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_ge_ull(unsigned long long a, const T_check_context *t,
|
||||
unsigned long long e)
|
||||
{
|
||||
T_check_true(a >= e, t, "%llu >= %llu", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_gt_ull(unsigned long long a, const T_check_context *t,
|
||||
unsigned long long e)
|
||||
{
|
||||
T_check_true(a > e, t, "%llu > %llu", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_le_ull(unsigned long long a, const T_check_context *t,
|
||||
unsigned long long e)
|
||||
{
|
||||
T_check_true(a <= e, t, "%llu <= %llu", a, e);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_lt_ull(unsigned long long a, const T_check_context *t,
|
||||
unsigned long long e)
|
||||
{
|
||||
T_check_true(a < e, t, "%llu < %llu", a, e);
|
||||
}
|
||||
100
cpukit/libtest/t-test-hash-sha256.c
Normal file
100
cpukit/libtest/t-test-hash-sha256.c
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (C) 2019 embedded brains GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <t.h>
|
||||
|
||||
#if defined(__rtems__)
|
||||
#include <sha256.h>
|
||||
#else
|
||||
#include <openssl/sha.h>
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
SHA256_CTX sha256;
|
||||
T_putchar putchar;
|
||||
void *putchar_arg;
|
||||
} T_report_hash_sha256_context;
|
||||
|
||||
static T_report_hash_sha256_context T_report_hash_sha256_instance;
|
||||
|
||||
static void
|
||||
T_report_hash_sha256_putchar(int c, void *arg)
|
||||
{
|
||||
T_report_hash_sha256_context *ctx;
|
||||
char cc;
|
||||
|
||||
ctx = arg;
|
||||
cc = (char)c;
|
||||
SHA256_Update(&ctx->sha256, &cc, sizeof(cc));
|
||||
(*ctx->putchar)(c, ctx->putchar_arg);
|
||||
}
|
||||
|
||||
static void
|
||||
T_report_hash_sha256_initialize(void)
|
||||
{
|
||||
T_report_hash_sha256_context *ctx;
|
||||
|
||||
ctx = &T_report_hash_sha256_instance;
|
||||
SHA256_Init(&ctx->sha256);
|
||||
T_set_putchar(T_report_hash_sha256_putchar, ctx, &ctx->putchar,
|
||||
&ctx->putchar_arg);
|
||||
}
|
||||
|
||||
static void
|
||||
T_report_hash_sha256_finalize(void)
|
||||
{
|
||||
T_report_hash_sha256_context *ctx;
|
||||
unsigned char hash[32];
|
||||
size_t i;
|
||||
|
||||
ctx = &T_report_hash_sha256_instance;
|
||||
SHA256_Final(hash, &ctx->sha256);
|
||||
T_printf("Y:ReportHash:SHA256:");
|
||||
|
||||
for (i = 0; i < 32; ++i) {
|
||||
T_printf("%02x", hash[i]);
|
||||
}
|
||||
|
||||
T_printf("\n");
|
||||
}
|
||||
|
||||
void
|
||||
T_report_hash_sha256(T_event event, const char *name)
|
||||
{
|
||||
(void)name;
|
||||
|
||||
switch (event) {
|
||||
case T_EVENT_RUN_INITIALIZE:
|
||||
T_report_hash_sha256_initialize();
|
||||
break;
|
||||
case T_EVENT_RUN_FINALIZE:
|
||||
T_report_hash_sha256_finalize();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
112
cpukit/libtest/t-test-malloc.c
Normal file
112
cpukit/libtest/t-test-malloc.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (C) 2018 embedded brains GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <t.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __BIGGEST_ALIGNMENT__
|
||||
#define T_BIGGEST_ALIGNMENT __BIGGEST_ALIGNMENT__
|
||||
#else
|
||||
#define T_BIGGEST_ALIGNMENT sizeof(long long)
|
||||
#endif
|
||||
|
||||
typedef struct __attribute__((__aligned__(T_BIGGEST_ALIGNMENT))) {
|
||||
T_destructor base;
|
||||
void (*destroy)(void *);
|
||||
} T_malloc_destructor;
|
||||
|
||||
static void
|
||||
T_malloc_destroy(T_destructor *base)
|
||||
{
|
||||
T_malloc_destructor *dtor;
|
||||
|
||||
dtor = (T_malloc_destructor *)(uintptr_t)base;
|
||||
|
||||
if (dtor->destroy != NULL) {
|
||||
(*dtor->destroy)(dtor + 1);
|
||||
}
|
||||
|
||||
free(dtor);
|
||||
}
|
||||
|
||||
static void *
|
||||
T_do_malloc(size_t size, void (*destroy)(void *))
|
||||
{
|
||||
T_malloc_destructor *dtor;
|
||||
size_t new_size;
|
||||
|
||||
new_size = sizeof(*dtor) + size;
|
||||
if (new_size <= size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dtor = malloc(new_size);
|
||||
if (dtor != NULL) {
|
||||
dtor->destroy = destroy;
|
||||
T_add_destructor(&dtor->base, T_malloc_destroy);
|
||||
++dtor;
|
||||
}
|
||||
|
||||
return dtor;
|
||||
}
|
||||
|
||||
void *
|
||||
T_malloc(size_t size)
|
||||
{
|
||||
return T_do_malloc(size, NULL);
|
||||
}
|
||||
|
||||
void *
|
||||
T_calloc(size_t nelem, size_t elsize)
|
||||
{
|
||||
return T_zalloc(nelem * elsize, NULL);
|
||||
}
|
||||
|
||||
void *
|
||||
T_zalloc(size_t size, void (*destroy)(void *))
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = T_do_malloc(size, destroy);
|
||||
if (p != NULL) {
|
||||
p = memset(p, 0, size);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
T_free(void *ptr)
|
||||
{
|
||||
T_malloc_destructor *dtor;
|
||||
|
||||
dtor = ptr;
|
||||
--dtor;
|
||||
T_remove_destructor(&dtor->base);
|
||||
free(dtor);
|
||||
}
|
||||
83
cpukit/libtest/t-test-rtems-fds.c
Normal file
83
cpukit/libtest/t-test-rtems-fds.c
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (C) 2018 embedded brains GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <t.h>
|
||||
|
||||
#include <rtems/libio_.h>
|
||||
|
||||
static int T_open_fds;
|
||||
|
||||
static int
|
||||
T_count_open_fds(void)
|
||||
{
|
||||
int free_count;
|
||||
rtems_libio_t *iop;
|
||||
|
||||
free_count = 0;
|
||||
rtems_libio_lock();
|
||||
|
||||
iop = rtems_libio_iop_free_head;
|
||||
while (iop != NULL) {
|
||||
++free_count;
|
||||
iop = iop->data1;
|
||||
}
|
||||
|
||||
rtems_libio_unlock();
|
||||
return (int)rtems_libio_number_iops - free_count;
|
||||
}
|
||||
|
||||
static void
|
||||
T_check_open_fds(void)
|
||||
{
|
||||
int open_fds;
|
||||
int delta;
|
||||
|
||||
open_fds = T_count_open_fds();
|
||||
delta = open_fds - T_open_fds;
|
||||
|
||||
if (delta != 0) {
|
||||
T_open_fds = open_fds;
|
||||
T_check_true(NULL, false, "file descriptor leak (%+i)", delta);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
T_check_file_descriptors(T_event event, const char *name)
|
||||
{
|
||||
(void)name;
|
||||
|
||||
switch (event) {
|
||||
case T_EVENT_RUN_INITIALIZE:
|
||||
T_open_fds = T_count_open_fds();
|
||||
break;
|
||||
case T_EVENT_CASE_END:
|
||||
T_check_open_fds();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
112
cpukit/libtest/t-test-rtems-heap.c
Normal file
112
cpukit/libtest/t-test-rtems-heap.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (C) 2018 embedded brains GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <t.h>
|
||||
|
||||
#include <rtems/score/heapimpl.h>
|
||||
#include <rtems/score/wkspace.h>
|
||||
#include <rtems/malloc.h>
|
||||
|
||||
typedef struct {
|
||||
Heap_Information_block heap_info;
|
||||
Heap_Information_block workspace_info;
|
||||
} T_resource_heap_context;
|
||||
|
||||
static T_resource_heap_context T_resource_heap_instance;
|
||||
|
||||
static void
|
||||
T_get_heap_info(Heap_Control *heap, Heap_Information_block *info)
|
||||
{
|
||||
_Heap_Get_information(heap, info);
|
||||
memset(&info->Stats, 0, sizeof(info->Stats));
|
||||
}
|
||||
|
||||
static void
|
||||
T_heap_run_initialize(void)
|
||||
{
|
||||
T_resource_heap_context *ctx;
|
||||
|
||||
ctx = &T_resource_heap_instance;
|
||||
T_get_heap_info(&_Workspace_Area, &ctx->workspace_info);
|
||||
|
||||
if (!rtems_configuration_get_unified_work_area()) {
|
||||
T_get_heap_info(RTEMS_Malloc_Heap, &ctx->heap_info);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
T_heap_case_end(void)
|
||||
{
|
||||
T_resource_heap_context *ctx;
|
||||
Heap_Information_block info;
|
||||
bool ok;
|
||||
|
||||
ctx = &T_resource_heap_instance;
|
||||
|
||||
T_get_heap_info(&_Workspace_Area, &info);
|
||||
ok = memcmp(&info, &ctx->workspace_info, sizeof(info)) == 0;
|
||||
|
||||
if (!ok) {
|
||||
const char *where;
|
||||
|
||||
if (rtems_configuration_get_unified_work_area()) {
|
||||
where = "workspace or heap";
|
||||
} else {
|
||||
where = "workspace";
|
||||
}
|
||||
|
||||
T_check_true(ok, NULL, "memory leak in %s", where);
|
||||
memcpy(&ctx->workspace_info, &info, sizeof(info));
|
||||
}
|
||||
|
||||
if (!rtems_configuration_get_unified_work_area()) {
|
||||
T_get_heap_info(RTEMS_Malloc_Heap, &info);
|
||||
ok = memcmp(&info, &ctx->heap_info, sizeof(info)) == 0;
|
||||
|
||||
if (!ok) {
|
||||
T_check_true(ok, NULL, "memory leak in heap");
|
||||
memcpy(&ctx->heap_info, &info, sizeof(info));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
T_check_heap(T_event event, const char *name)
|
||||
{
|
||||
(void)name;
|
||||
|
||||
switch (event) {
|
||||
case T_EVENT_RUN_INITIALIZE:
|
||||
T_heap_run_initialize();
|
||||
break;
|
||||
case T_EVENT_CASE_END:
|
||||
T_heap_case_end();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
837
cpukit/libtest/t-test-rtems-measure.c
Normal file
837
cpukit/libtest/t-test-rtems-measure.c
Normal file
@@ -0,0 +1,837 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (C) 2018 embedded brains GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <t.h>
|
||||
|
||||
#include <alloca.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <rtems.h>
|
||||
|
||||
#define WAKEUP_EVENT RTEMS_EVENT_0
|
||||
|
||||
typedef struct {
|
||||
struct T_measure_runtime_context *master;
|
||||
rtems_id id;
|
||||
volatile unsigned int *chunk;
|
||||
} load_context;
|
||||
|
||||
struct T_measure_runtime_context {
|
||||
T_destructor destructor;
|
||||
size_t sample_count;
|
||||
T_ticks *samples;
|
||||
size_t cache_line_size;
|
||||
size_t chunk_size;
|
||||
volatile unsigned int *chunk;
|
||||
rtems_id runner;
|
||||
uint32_t load_count;
|
||||
load_context *load_contexts;
|
||||
};
|
||||
|
||||
static unsigned int
|
||||
dirty_data_cache(volatile unsigned int *chunk, size_t chunk_size,
|
||||
size_t cache_line_size, unsigned int token)
|
||||
{
|
||||
size_t m;
|
||||
size_t k;
|
||||
size_t i;
|
||||
|
||||
m = chunk_size / sizeof(chunk[0]);
|
||||
k = cache_line_size / sizeof(chunk[0]);
|
||||
|
||||
for (i = 0; i < m; i += k) {
|
||||
chunk[i] = i + token;
|
||||
}
|
||||
|
||||
return i + token;
|
||||
}
|
||||
|
||||
static void
|
||||
wait_for_worker(void)
|
||||
{
|
||||
rtems_event_set events;
|
||||
|
||||
(void)rtems_event_receive(WAKEUP_EVENT, RTEMS_EVENT_ALL | RTEMS_WAIT,
|
||||
RTEMS_NO_TIMEOUT, &events);
|
||||
}
|
||||
|
||||
static void
|
||||
wakeup_master(const T_measure_runtime_context *ctx)
|
||||
{
|
||||
(void)rtems_event_send(ctx->runner, WAKEUP_EVENT);
|
||||
}
|
||||
|
||||
static void
|
||||
suspend_worker(const load_context *lctx)
|
||||
{
|
||||
(void)rtems_task_suspend(lctx->id);
|
||||
}
|
||||
|
||||
static void
|
||||
restart_worker(const load_context *lctx)
|
||||
{
|
||||
(void)rtems_task_restart(lctx->id, (rtems_task_argument)lctx);
|
||||
wait_for_worker();
|
||||
}
|
||||
|
||||
static void
|
||||
load_worker(rtems_task_argument arg)
|
||||
{
|
||||
const load_context *lctx;
|
||||
T_measure_runtime_context *ctx;
|
||||
unsigned int token;
|
||||
volatile unsigned int *chunk;
|
||||
size_t chunk_size;
|
||||
size_t cache_line_size;
|
||||
|
||||
lctx = (const load_context *)arg;
|
||||
ctx = lctx->master;
|
||||
chunk = lctx->chunk;
|
||||
chunk_size = ctx->chunk_size;
|
||||
cache_line_size = ctx->cache_line_size;
|
||||
token = (unsigned int)rtems_get_current_processor();
|
||||
|
||||
token = dirty_data_cache(chunk, chunk_size, cache_line_size, token);
|
||||
wakeup_master(ctx);
|
||||
|
||||
while (true) {
|
||||
token = dirty_data_cache(chunk, chunk_size, cache_line_size,
|
||||
token);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_worker(const T_measure_runtime_context *ctx)
|
||||
{
|
||||
uint32_t load;
|
||||
|
||||
for (load = 0; load < ctx->load_count; ++load) {
|
||||
const load_context *lctx;
|
||||
|
||||
lctx = &ctx->load_contexts[load];
|
||||
|
||||
if (lctx->chunk != ctx->chunk) {
|
||||
free(RTEMS_DEVOLATILE(unsigned int *, lctx->chunk));
|
||||
}
|
||||
|
||||
|
||||
if (lctx->id != 0) {
|
||||
rtems_task_delete(lctx->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
destroy(T_destructor *dtor)
|
||||
{
|
||||
T_measure_runtime_context *ctx;
|
||||
|
||||
ctx = (T_measure_runtime_context *)dtor;
|
||||
destroy_worker(ctx);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
static void *
|
||||
add_offset(const volatile void *p, uintptr_t o)
|
||||
{
|
||||
return (void *)((uintptr_t)p + o);
|
||||
}
|
||||
|
||||
static void *
|
||||
align_up(const volatile void *p, uintptr_t a)
|
||||
{
|
||||
return (void *)(((uintptr_t)p + a - 1) & ~(a - 1));
|
||||
}
|
||||
|
||||
T_measure_runtime_context *
|
||||
T_measure_runtime_create(const T_measure_runtime_config *config)
|
||||
{
|
||||
T_measure_runtime_context *ctx;
|
||||
size_t sample_size;
|
||||
size_t cache_line_size;
|
||||
size_t chunk_size;
|
||||
size_t load_size;
|
||||
uint32_t load_count;
|
||||
bool success;
|
||||
uint32_t i;
|
||||
#ifdef RTEMS_SMP
|
||||
cpu_set_t cpu;
|
||||
#endif
|
||||
|
||||
sample_size = config->sample_count * sizeof(ctx->samples[0]);
|
||||
|
||||
cache_line_size = rtems_cache_get_data_line_size();
|
||||
|
||||
if (cache_line_size == 0) {
|
||||
cache_line_size = 8;
|
||||
}
|
||||
|
||||
chunk_size = rtems_cache_get_data_cache_size(0);
|
||||
|
||||
if (chunk_size == 0) {
|
||||
chunk_size = cache_line_size;
|
||||
}
|
||||
|
||||
chunk_size *= 2;
|
||||
|
||||
load_count = rtems_get_processor_count();
|
||||
load_size = load_count * sizeof(ctx->load_contexts[0]);
|
||||
|
||||
ctx = malloc(sizeof(*ctx) + sample_size + load_size + chunk_size +
|
||||
2 * cache_line_size);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->sample_count = config->sample_count;
|
||||
ctx->samples = add_offset(ctx, sizeof(*ctx));
|
||||
ctx->cache_line_size = cache_line_size;
|
||||
ctx->chunk_size = chunk_size;
|
||||
ctx->chunk = add_offset(ctx->samples, sample_size);
|
||||
ctx->runner = rtems_task_self();
|
||||
ctx->load_count = load_count;
|
||||
ctx->load_contexts = add_offset(ctx->chunk, chunk_size);
|
||||
ctx->samples = align_up(ctx->samples, cache_line_size);
|
||||
ctx->chunk = align_up(ctx->chunk, cache_line_size);
|
||||
|
||||
memset(ctx->load_contexts, 0, load_size);
|
||||
success = true;
|
||||
|
||||
for (i = 0; i < load_count; ++i) {
|
||||
rtems_status_code sc;
|
||||
rtems_id id;
|
||||
load_context *lctx;
|
||||
#ifdef RTEMS_SMP
|
||||
rtems_task_priority priority;
|
||||
rtems_id scheduler;
|
||||
|
||||
sc = rtems_scheduler_ident_by_processor(i, &scheduler);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
sc = rtems_task_create(rtems_build_name('L', 'O', 'A', 'D'),
|
||||
RTEMS_MAXIMUM_PRIORITY - 1, RTEMS_MINIMUM_STACK_SIZE,
|
||||
RTEMS_DEFAULT_MODES, RTEMS_DEFAULT_ATTRIBUTES, &id);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
lctx = &ctx->load_contexts[i];
|
||||
|
||||
lctx->master = ctx;
|
||||
lctx->id = id;
|
||||
|
||||
lctx->chunk = malloc(chunk_size);
|
||||
if (lctx->chunk == NULL) {
|
||||
lctx->chunk = ctx->chunk;
|
||||
}
|
||||
|
||||
#ifdef RTEMS_SMP
|
||||
(void)rtems_scheduler_get_maximum_priority(scheduler, &priority);
|
||||
(void)rtems_task_set_scheduler(id, scheduler, priority - 1);
|
||||
|
||||
CPU_ZERO(&cpu);
|
||||
CPU_SET((int)i, &cpu);
|
||||
(void)rtems_task_set_affinity(id, sizeof(cpu), &cpu);
|
||||
#endif
|
||||
|
||||
(void)rtems_task_start(id, load_worker,
|
||||
(rtems_task_argument)lctx);
|
||||
|
||||
wait_for_worker();
|
||||
suspend_worker(lctx);
|
||||
}
|
||||
|
||||
if (success) {
|
||||
#ifdef RTEMS_SMP
|
||||
CPU_ZERO(&cpu);
|
||||
CPU_SET(0, &cpu);
|
||||
(void)rtems_task_set_affinity(RTEMS_SELF, sizeof(cpu), &cpu);
|
||||
#endif
|
||||
} else {
|
||||
destroy(&ctx->destructor);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
T_add_destructor(&ctx->destructor, destroy);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static int
|
||||
cmp(const void *ap, const void *bp)
|
||||
{
|
||||
T_ticks a;
|
||||
T_ticks b;
|
||||
|
||||
a = *(const T_ticks *)ap;
|
||||
b = *(const T_ticks *)bp;
|
||||
|
||||
if (a < b) {
|
||||
return -1;
|
||||
} else if (a > b) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
measure_variant_begin(const char *name, const char *variant)
|
||||
{
|
||||
T_printf("M:B:%s\n", name);
|
||||
T_printf("M:V:%s\n", variant);
|
||||
}
|
||||
|
||||
static T_time
|
||||
accumulate(const T_ticks *samples, size_t sample_count)
|
||||
{
|
||||
T_time a;
|
||||
size_t i;
|
||||
|
||||
a = 0;
|
||||
|
||||
for (i = 0; i < sample_count; ++i) {
|
||||
a += T_ticks_to_time(samples[i]);
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
static T_ticks
|
||||
median_absolute_deviation(T_ticks *samples, size_t sample_count)
|
||||
{
|
||||
T_ticks median;
|
||||
size_t i;
|
||||
|
||||
median = samples[sample_count / 2];
|
||||
|
||||
for (i = 0; i < sample_count / 2; ++i) {
|
||||
samples[i] = median - samples[i];
|
||||
}
|
||||
|
||||
for (; i < sample_count; ++i) {
|
||||
samples[i] = samples[i] - median;
|
||||
}
|
||||
|
||||
qsort(samples, sample_count, sizeof(samples[0]), cmp);
|
||||
return samples[sample_count / 2];
|
||||
}
|
||||
|
||||
static void
|
||||
report_sorted_samples(const T_measure_runtime_context *ctx)
|
||||
{
|
||||
size_t sample_count;
|
||||
const T_ticks *samples;
|
||||
T_time_string ts;
|
||||
T_ticks last;
|
||||
T_ticks v;
|
||||
size_t count;
|
||||
size_t i;
|
||||
|
||||
sample_count = ctx->sample_count;
|
||||
samples = ctx->samples;
|
||||
last = 0;
|
||||
--last;
|
||||
count = 0;
|
||||
|
||||
for (i = 0; i < sample_count; ++i) {
|
||||
v = samples[i];
|
||||
++count;
|
||||
|
||||
if (v != last) {
|
||||
uint32_t sa;
|
||||
uint32_t sb;
|
||||
uint32_t nsa;
|
||||
uint32_t nsb;
|
||||
T_time t;
|
||||
|
||||
T_time_to_seconds_and_nanoseconds(T_ticks_to_time(last),
|
||||
&sa, &nsa);
|
||||
t = T_ticks_to_time(v);
|
||||
T_time_to_seconds_and_nanoseconds(t, &sb, &nsb);
|
||||
|
||||
if (sa != sb || nsa != nsb) {
|
||||
T_printf("M:S:%zu:%s\n", count,
|
||||
T_time_to_string_ns(t, ts));
|
||||
count = 0;
|
||||
}
|
||||
|
||||
last = v;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
T_printf("M:S:%zu:%s\n", count,
|
||||
T_ticks_to_string_ns(last, ts));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
measure_variant_end(const T_measure_runtime_context *ctx,
|
||||
const T_measure_runtime_request *req, T_time begin)
|
||||
{
|
||||
size_t sample_count;
|
||||
T_ticks *samples;
|
||||
T_time_string ts;
|
||||
T_time d;
|
||||
T_ticks v;
|
||||
T_time a;
|
||||
|
||||
sample_count = ctx->sample_count;
|
||||
samples = ctx->samples;
|
||||
d = T_now() - begin;
|
||||
a = accumulate(samples, sample_count);
|
||||
qsort(samples, sample_count, sizeof(samples[0]), cmp);
|
||||
T_printf("M:N:%zu\n", sample_count);
|
||||
|
||||
if ((req->flags & T_MEASURE_RUNTIME_REPORT_SAMPLES) != 0) {
|
||||
report_sorted_samples(ctx);
|
||||
}
|
||||
|
||||
v = samples[0];
|
||||
T_printf("M:MI:%s\n", T_ticks_to_string_ns(v, ts));
|
||||
v = samples[(1 * sample_count) / 100];
|
||||
T_printf("M:P1:%s\n", T_ticks_to_string_ns(v, ts));
|
||||
v = samples[(1 * sample_count) / 4];
|
||||
T_printf("M:Q1:%s\n", T_ticks_to_string_ns(v, ts));
|
||||
v = samples[sample_count / 2];
|
||||
T_printf("M:Q2:%s\n", T_ticks_to_string_ns(v, ts));
|
||||
v = samples[(3 * sample_count) / 4];
|
||||
T_printf("M:Q3:%s\n", T_ticks_to_string_ns(v, ts));
|
||||
v = samples[(99 * sample_count) / 100];
|
||||
T_printf("M:P99:%s\n", T_ticks_to_string_ns(v, ts));
|
||||
v = samples[sample_count - 1];
|
||||
T_printf("M:MX:%s\n", T_ticks_to_string_ns(v, ts));
|
||||
v = median_absolute_deviation(samples, sample_count);
|
||||
T_printf("M:MAD:%s\n", T_ticks_to_string_ns(v, ts));
|
||||
T_printf("M:D:%s\n", T_time_to_string_ns(a, ts));
|
||||
T_printf("M:E:%s:D:%s\n", req->name, T_time_to_string_ns(d, ts));
|
||||
}
|
||||
|
||||
static void
|
||||
fill_data_cache(volatile unsigned int *chunk, size_t chunk_size,
|
||||
size_t cache_line_size)
|
||||
{
|
||||
size_t m;
|
||||
size_t k;
|
||||
size_t i;
|
||||
|
||||
m = chunk_size / sizeof(chunk[0]);
|
||||
k = cache_line_size / sizeof(chunk[0]);
|
||||
|
||||
for (i = 0; i < m; i += k) {
|
||||
chunk[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dirty_call(void (*body)(void *), void *arg)
|
||||
{
|
||||
void *space;
|
||||
|
||||
/* Ensure that we use an untouched stack area */
|
||||
space = alloca(1024);
|
||||
RTEMS_OBFUSCATE_VARIABLE(space);
|
||||
|
||||
(*body)(arg);
|
||||
}
|
||||
|
||||
static void
|
||||
setup(const T_measure_runtime_request *req, void *arg)
|
||||
{
|
||||
if (req->setup != NULL) {
|
||||
(*req->setup)(arg);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
teardown(const T_measure_runtime_request *req, void *arg, T_ticks *delta,
|
||||
uint32_t tic, uint32_t toc, unsigned int retry,
|
||||
unsigned int maximum_retries)
|
||||
{
|
||||
if (req->teardown == NULL) {
|
||||
return tic == toc || retry >= maximum_retries;
|
||||
}
|
||||
|
||||
return (*req->teardown)(arg, delta, tic, toc, retry);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
get_maximum_retries(const T_measure_runtime_request *req)
|
||||
{
|
||||
return (req->flags & T_MEASURE_RUNTIME_ALLOW_CLOCK_ISR) != 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
static void
|
||||
measure_valid_cache(T_measure_runtime_context *ctx,
|
||||
const T_measure_runtime_request *req)
|
||||
{
|
||||
size_t sample_count;
|
||||
T_ticks *samples;
|
||||
void (*body)(void *);
|
||||
void *arg;
|
||||
size_t i;
|
||||
T_time begin;
|
||||
|
||||
measure_variant_begin(req->name, "ValidCache");
|
||||
begin = T_now();
|
||||
sample_count = ctx->sample_count;
|
||||
samples = ctx->samples;
|
||||
body = req->body;
|
||||
arg = req->arg;
|
||||
|
||||
for (i = 0; i < sample_count; ++i) {
|
||||
unsigned int maximum_retries;
|
||||
unsigned int retry;
|
||||
|
||||
maximum_retries = get_maximum_retries(req);
|
||||
retry = 0;
|
||||
|
||||
while (true) {
|
||||
rtems_interval tic;
|
||||
rtems_interval toc;
|
||||
T_ticks t0;
|
||||
T_ticks t1;
|
||||
|
||||
setup(req, arg);
|
||||
fill_data_cache(ctx->chunk, ctx->chunk_size,
|
||||
ctx->cache_line_size);
|
||||
|
||||
tic = rtems_clock_get_ticks_since_boot();
|
||||
t0 = T_tick();
|
||||
(*body)(arg);
|
||||
t1 = T_tick();
|
||||
toc = rtems_clock_get_ticks_since_boot();
|
||||
samples[i] = t1 - t0;
|
||||
|
||||
if (teardown(req, arg, &samples[i], tic, toc, retry,
|
||||
maximum_retries)) {
|
||||
break;
|
||||
}
|
||||
|
||||
++retry;
|
||||
}
|
||||
}
|
||||
|
||||
measure_variant_end(ctx, req, begin);
|
||||
}
|
||||
|
||||
static void
|
||||
measure_hot_cache(T_measure_runtime_context *ctx,
|
||||
const T_measure_runtime_request *req)
|
||||
{
|
||||
size_t sample_count;
|
||||
T_ticks *samples;
|
||||
void (*body)(void *);
|
||||
void *arg;
|
||||
size_t i;
|
||||
T_time begin;
|
||||
|
||||
measure_variant_begin(req->name, "HotCache");
|
||||
begin = T_now();
|
||||
sample_count = ctx->sample_count;
|
||||
samples = ctx->samples;
|
||||
body = req->body;
|
||||
arg = req->arg;
|
||||
|
||||
for (i = 0; i < sample_count; ++i) {
|
||||
unsigned int maximum_retries;
|
||||
unsigned int retry;
|
||||
|
||||
maximum_retries = get_maximum_retries(req);
|
||||
retry = 0;
|
||||
|
||||
while (true) {
|
||||
rtems_interval tic;
|
||||
rtems_interval toc;
|
||||
T_ticks t0;
|
||||
T_ticks t1;
|
||||
|
||||
setup(req, arg);
|
||||
|
||||
tic = rtems_clock_get_ticks_since_boot();
|
||||
t0 = T_tick();
|
||||
(*body)(arg);
|
||||
t1 = T_tick();
|
||||
toc = rtems_clock_get_ticks_since_boot();
|
||||
samples[i] = t1 - t0;
|
||||
|
||||
(void)teardown(req, arg, &samples[i], tic, toc, retry,
|
||||
0);
|
||||
setup(req, arg);
|
||||
|
||||
tic = rtems_clock_get_ticks_since_boot();
|
||||
t0 = T_tick();
|
||||
(*body)(arg);
|
||||
t1 = T_tick();
|
||||
toc = rtems_clock_get_ticks_since_boot();
|
||||
samples[i] = t1 - t0;
|
||||
|
||||
if (teardown(req, arg, &samples[i], tic, toc, retry,
|
||||
maximum_retries)) {
|
||||
break;
|
||||
}
|
||||
|
||||
++retry;
|
||||
}
|
||||
}
|
||||
|
||||
measure_variant_end(ctx, req, begin);
|
||||
}
|
||||
|
||||
static void
|
||||
measure_dirty_cache(T_measure_runtime_context *ctx,
|
||||
const T_measure_runtime_request *req)
|
||||
{
|
||||
size_t sample_count;
|
||||
T_ticks *samples;
|
||||
void (*body)(void *);
|
||||
void *arg;
|
||||
size_t i;
|
||||
T_time begin;
|
||||
size_t token;
|
||||
|
||||
measure_variant_begin(req->name, "DirtyCache");
|
||||
begin = T_now();
|
||||
sample_count = ctx->sample_count;
|
||||
samples = ctx->samples;
|
||||
body = req->body;
|
||||
arg = req->arg;
|
||||
token = 0;
|
||||
|
||||
for (i = 0; i < sample_count; ++i) {
|
||||
unsigned int maximum_retries;
|
||||
unsigned int retry;
|
||||
|
||||
maximum_retries = get_maximum_retries(req);
|
||||
retry = 0;
|
||||
|
||||
while (true) {
|
||||
rtems_interval tic;
|
||||
rtems_interval toc;
|
||||
T_ticks t0;
|
||||
T_ticks t1;
|
||||
|
||||
setup(req, arg);
|
||||
token = dirty_data_cache(ctx->chunk, ctx->chunk_size,
|
||||
ctx->cache_line_size, token);
|
||||
rtems_cache_invalidate_entire_instruction();
|
||||
|
||||
tic = rtems_clock_get_ticks_since_boot();
|
||||
t0 = T_tick();
|
||||
dirty_call(body, arg);
|
||||
t1 = T_tick();
|
||||
toc = rtems_clock_get_ticks_since_boot();
|
||||
samples[i] = t1 - t0;
|
||||
|
||||
if (teardown(req, arg, &samples[i], tic, toc, retry,
|
||||
maximum_retries)) {
|
||||
break;
|
||||
}
|
||||
|
||||
++retry;
|
||||
}
|
||||
}
|
||||
|
||||
measure_variant_end(ctx, req, begin);
|
||||
}
|
||||
|
||||
#ifdef __sparc__
|
||||
/*
|
||||
* Use recursive function calls to wake sure that we cause window overflow
|
||||
* traps in the body. Try to make it hard for the compiler to optimize the
|
||||
* recursive function away.
|
||||
*/
|
||||
static T_ticks
|
||||
recursive_load_call(void (*body)(void *), void *arg, int n)
|
||||
{
|
||||
T_ticks delta;
|
||||
|
||||
RTEMS_OBFUSCATE_VARIABLE(n);
|
||||
|
||||
if (n > 0) {
|
||||
delta = recursive_load_call(body, arg, n - 1);
|
||||
} else {
|
||||
T_ticks t0;
|
||||
T_ticks t1;
|
||||
|
||||
t0 = T_tick();
|
||||
dirty_call(body, arg);
|
||||
t1 = T_tick();
|
||||
|
||||
delta = t1 - t0;
|
||||
}
|
||||
|
||||
RTEMS_OBFUSCATE_VARIABLE(delta);
|
||||
return delta;
|
||||
}
|
||||
#else
|
||||
static T_ticks
|
||||
load_call(void (*body)(void *), void *arg)
|
||||
{
|
||||
T_ticks t0;
|
||||
T_ticks t1;
|
||||
|
||||
t0 = T_tick();
|
||||
dirty_call(body, arg);
|
||||
t1 = T_tick();
|
||||
|
||||
return t1 - t0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
measure_load_variant(T_measure_runtime_context *ctx,
|
||||
const T_measure_runtime_request *req,
|
||||
const load_context *lctx, uint32_t load)
|
||||
{
|
||||
size_t sample_count;
|
||||
T_ticks *samples;
|
||||
void (*body)(void *);
|
||||
void *arg;
|
||||
size_t i;
|
||||
T_time begin;
|
||||
size_t token;
|
||||
|
||||
measure_variant_begin(req->name, "Load");
|
||||
T_printf("M:L:%" PRIu32 "\n", load + 1);
|
||||
begin = T_now();
|
||||
sample_count = ctx->sample_count;
|
||||
samples = ctx->samples;
|
||||
body = req->body;
|
||||
arg = req->arg;
|
||||
token = 0;
|
||||
|
||||
restart_worker(lctx);
|
||||
|
||||
for (i = 0; i < sample_count; ++i) {
|
||||
unsigned int maximum_retries;
|
||||
unsigned int retry;
|
||||
|
||||
maximum_retries = get_maximum_retries(req);
|
||||
retry = 0;
|
||||
|
||||
while (true) {
|
||||
rtems_interval tic;
|
||||
rtems_interval toc;
|
||||
T_ticks delta;
|
||||
|
||||
setup(req, arg);
|
||||
token = dirty_data_cache(ctx->chunk, ctx->chunk_size,
|
||||
ctx->cache_line_size, token);
|
||||
rtems_cache_invalidate_entire_instruction();
|
||||
|
||||
tic = rtems_clock_get_ticks_since_boot();
|
||||
#ifdef __sparc__
|
||||
delta = recursive_load_call(body, arg,
|
||||
SPARC_NUMBER_OF_REGISTER_WINDOWS - 3);
|
||||
#else
|
||||
delta = load_call(body, arg);
|
||||
#endif
|
||||
toc = rtems_clock_get_ticks_since_boot();
|
||||
samples[i] = delta;
|
||||
|
||||
if (teardown(req, arg, &samples[i], tic, toc, retry,
|
||||
maximum_retries)) {
|
||||
break;
|
||||
}
|
||||
|
||||
++retry;
|
||||
}
|
||||
}
|
||||
|
||||
measure_variant_end(ctx, req, begin);
|
||||
}
|
||||
|
||||
static void
|
||||
measure_load(T_measure_runtime_context *ctx,
|
||||
const T_measure_runtime_request *req)
|
||||
{
|
||||
const load_context *lctx;
|
||||
uint32_t load;
|
||||
|
||||
#ifdef RTEMS_SMP
|
||||
for (load = 0; load < ctx->load_count - 1; ++load) {
|
||||
lctx = &ctx->load_contexts[load];
|
||||
|
||||
if (lctx->id != 0) {
|
||||
if ((req->flags &
|
||||
T_MEASURE_RUNTIME_DISABLE_MINOR_LOAD) == 0) {
|
||||
measure_load_variant(ctx, req, lctx, load);
|
||||
} else {
|
||||
restart_worker(lctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((req->flags & T_MEASURE_RUNTIME_DISABLE_MAX_LOAD) == 0) {
|
||||
load = ctx->load_count - 1;
|
||||
lctx = &ctx->load_contexts[load];
|
||||
|
||||
if (lctx->id != 0) {
|
||||
measure_load_variant(ctx, req, lctx, load);
|
||||
}
|
||||
}
|
||||
|
||||
for (load = 0; load < ctx->load_count; ++load) {
|
||||
lctx = &ctx->load_contexts[load];
|
||||
|
||||
if (lctx->id != 0) {
|
||||
suspend_worker(lctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
T_measure_runtime(T_measure_runtime_context *ctx,
|
||||
const T_measure_runtime_request *req)
|
||||
{
|
||||
/*
|
||||
* Do ValidCache variant before HotCache to get a good overall cache
|
||||
* state for the HotCache variant.
|
||||
*/
|
||||
if ((req->flags & T_MEASURE_RUNTIME_DISABLE_VALID_CACHE) == 0) {
|
||||
measure_valid_cache(ctx, req);
|
||||
}
|
||||
|
||||
if ((req->flags & T_MEASURE_RUNTIME_DISABLE_HOT_CACHE) == 0) {
|
||||
measure_hot_cache(ctx, req);
|
||||
}
|
||||
|
||||
if ((req->flags & T_MEASURE_RUNTIME_DISABLE_DIRTY_CACHE) == 0) {
|
||||
measure_dirty_cache(ctx, req);
|
||||
}
|
||||
|
||||
measure_load(ctx, req);
|
||||
}
|
||||
447
cpukit/libtest/t-test-rtems-objs.c
Normal file
447
cpukit/libtest/t-test-rtems-objs.c
Normal file
@@ -0,0 +1,447 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (C) 2018 embedded brains GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#undef __STRICT_ANSI__
|
||||
|
||||
#include <t.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <rtems/score/objectimpl.h>
|
||||
#include <rtems/score/threadimpl.h>
|
||||
#include <rtems/posix/keyimpl.h>
|
||||
|
||||
static Objects_Maximum
|
||||
T_objects_count(Objects_APIs api, uint16_t cls)
|
||||
{
|
||||
const Objects_Information *information;
|
||||
Objects_Maximum count;
|
||||
|
||||
information = _Objects_Get_information(api, cls);
|
||||
|
||||
_RTEMS_Lock_allocator();
|
||||
|
||||
if (information != NULL) {
|
||||
count = _Objects_Active_count(information);
|
||||
} else {
|
||||
count = 0;
|
||||
}
|
||||
|
||||
_RTEMS_Unlock_allocator();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void
|
||||
T_objects_check(Objects_APIs api, uint16_t cls,
|
||||
Objects_Maximum *expected, const char *name)
|
||||
{
|
||||
Objects_Maximum count;
|
||||
int32_t delta;
|
||||
|
||||
count = T_objects_count(api, cls);
|
||||
delta = (int32_t)count - (int32_t)*expected;
|
||||
|
||||
if (delta != 0) {
|
||||
*expected = count;
|
||||
T_check_true(NULL, false, "%s leak (%" PRIi32 ")", name, delta);
|
||||
}
|
||||
}
|
||||
|
||||
static Objects_Maximum T_barrier_count;
|
||||
|
||||
static void
|
||||
T_rtems_barriers_run_initialize(void)
|
||||
{
|
||||
T_barrier_count = T_objects_count(OBJECTS_CLASSIC_API,
|
||||
OBJECTS_RTEMS_BARRIERS);
|
||||
}
|
||||
|
||||
static void
|
||||
T_rtems_barriers_case_end(void)
|
||||
{
|
||||
T_objects_check(OBJECTS_CLASSIC_API, OBJECTS_RTEMS_BARRIERS,
|
||||
&T_barrier_count, "RTEMS barrier");
|
||||
}
|
||||
|
||||
void
|
||||
T_check_rtems_barriers(T_event event, const char *name)
|
||||
{
|
||||
(void)name;
|
||||
|
||||
switch (event) {
|
||||
case T_EVENT_RUN_INITIALIZE:
|
||||
T_rtems_barriers_run_initialize();
|
||||
break;
|
||||
case T_EVENT_CASE_END:
|
||||
T_rtems_barriers_case_end();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static Objects_Maximum T_extension_count;
|
||||
|
||||
static void
|
||||
T_rtems_extensions_run_initialize(void)
|
||||
{
|
||||
T_extension_count = T_objects_count(OBJECTS_CLASSIC_API,
|
||||
OBJECTS_RTEMS_EXTENSIONS);
|
||||
}
|
||||
|
||||
static void
|
||||
T_rtems_extensions_case_end(void)
|
||||
{
|
||||
T_objects_check(OBJECTS_CLASSIC_API, OBJECTS_RTEMS_EXTENSIONS,
|
||||
&T_extension_count, "RTEMS extension");
|
||||
}
|
||||
|
||||
void
|
||||
T_check_rtems_extensions(T_event event, const char *name)
|
||||
{
|
||||
(void)name;
|
||||
|
||||
switch (event) {
|
||||
case T_EVENT_RUN_INITIALIZE:
|
||||
T_rtems_extensions_run_initialize();
|
||||
break;
|
||||
case T_EVENT_CASE_END:
|
||||
T_rtems_extensions_case_end();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static Objects_Maximum T_mq_count;
|
||||
|
||||
static void
|
||||
T_rtems_message_queues_run_initialize(void)
|
||||
{
|
||||
T_mq_count = T_objects_count(OBJECTS_CLASSIC_API,
|
||||
OBJECTS_RTEMS_MESSAGE_QUEUES);
|
||||
}
|
||||
|
||||
static void
|
||||
T_rtems_message_queues_case_end(void)
|
||||
{
|
||||
T_objects_check(OBJECTS_CLASSIC_API, OBJECTS_RTEMS_MESSAGE_QUEUES,
|
||||
&T_mq_count, "RTEMS message queue");
|
||||
}
|
||||
|
||||
void
|
||||
T_check_rtems_message_queues(T_event event, const char *name)
|
||||
{
|
||||
(void)name;
|
||||
|
||||
switch (event) {
|
||||
case T_EVENT_RUN_INITIALIZE:
|
||||
T_rtems_message_queues_run_initialize();
|
||||
break;
|
||||
case T_EVENT_CASE_END:
|
||||
T_rtems_message_queues_case_end();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static Objects_Maximum T_part_count;
|
||||
|
||||
static void
|
||||
T_rtems_partitions_run_initialize(void)
|
||||
{
|
||||
T_part_count = T_objects_count(OBJECTS_CLASSIC_API,
|
||||
OBJECTS_RTEMS_PARTITIONS);
|
||||
}
|
||||
|
||||
static void
|
||||
T_rtems_partitions_case_end(void)
|
||||
{
|
||||
T_objects_check(OBJECTS_CLASSIC_API, OBJECTS_RTEMS_PARTITIONS,
|
||||
&T_part_count, "RTEMS partition");
|
||||
}
|
||||
|
||||
void
|
||||
T_check_rtems_partitions(T_event event, const char *name)
|
||||
{
|
||||
(void)name;
|
||||
|
||||
switch (event) {
|
||||
case T_EVENT_RUN_INITIALIZE:
|
||||
T_rtems_partitions_run_initialize();
|
||||
break;
|
||||
case T_EVENT_CASE_END:
|
||||
T_rtems_partitions_case_end();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static Objects_Maximum T_period_count;
|
||||
|
||||
static void
|
||||
T_rtems_periods_run_initialize(void)
|
||||
{
|
||||
T_period_count = T_objects_count(OBJECTS_CLASSIC_API,
|
||||
OBJECTS_RTEMS_PERIODS);
|
||||
}
|
||||
|
||||
static void
|
||||
T_rtems_periods_case_end(void)
|
||||
{
|
||||
T_objects_check(OBJECTS_CLASSIC_API, OBJECTS_RTEMS_PERIODS,
|
||||
&T_period_count, "RTEMS period");
|
||||
}
|
||||
|
||||
void
|
||||
T_check_rtems_periods(T_event event, const char *name)
|
||||
{
|
||||
(void)name;
|
||||
|
||||
switch (event) {
|
||||
case T_EVENT_RUN_INITIALIZE:
|
||||
T_rtems_periods_run_initialize();
|
||||
break;
|
||||
case T_EVENT_CASE_END:
|
||||
T_rtems_periods_case_end();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static Objects_Maximum T_region_count;
|
||||
|
||||
static void
|
||||
T_rtems_regions_run_initialize(void)
|
||||
{
|
||||
T_region_count = T_objects_count(OBJECTS_CLASSIC_API,
|
||||
OBJECTS_RTEMS_REGIONS);
|
||||
}
|
||||
|
||||
static void
|
||||
T_rtems_regions_case_end(void)
|
||||
{
|
||||
T_objects_check(OBJECTS_CLASSIC_API, OBJECTS_RTEMS_REGIONS,
|
||||
&T_region_count, "RTEMS region");
|
||||
}
|
||||
|
||||
void
|
||||
T_check_rtems_regions(T_event event, const char *name)
|
||||
{
|
||||
(void)name;
|
||||
|
||||
switch (event) {
|
||||
case T_EVENT_RUN_INITIALIZE:
|
||||
T_rtems_regions_run_initialize();
|
||||
break;
|
||||
case T_EVENT_CASE_END:
|
||||
T_rtems_regions_case_end();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static Objects_Maximum T_sema_count;
|
||||
|
||||
static void
|
||||
T_rtems_semaphores_run_initialize(void)
|
||||
{
|
||||
T_sema_count = T_objects_count(OBJECTS_CLASSIC_API,
|
||||
OBJECTS_RTEMS_SEMAPHORES);
|
||||
}
|
||||
|
||||
static void
|
||||
T_rtems_semaphores_case_end(void)
|
||||
{
|
||||
T_objects_check(OBJECTS_CLASSIC_API, OBJECTS_RTEMS_SEMAPHORES,
|
||||
&T_sema_count, "RTEMS semaphore");
|
||||
}
|
||||
|
||||
void
|
||||
T_check_rtems_semaphores(T_event event, const char *name)
|
||||
{
|
||||
(void)name;
|
||||
|
||||
switch (event) {
|
||||
case T_EVENT_RUN_INITIALIZE:
|
||||
T_rtems_semaphores_run_initialize();
|
||||
break;
|
||||
case T_EVENT_CASE_END:
|
||||
T_rtems_semaphores_case_end();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static Objects_Maximum T_task_count;
|
||||
|
||||
static void
|
||||
T_rtems_tasks_run_initialize(void)
|
||||
{
|
||||
T_task_count = T_objects_count(OBJECTS_CLASSIC_API,
|
||||
OBJECTS_RTEMS_TASKS);
|
||||
}
|
||||
|
||||
static void
|
||||
T_rtems_tasks_case_end(void)
|
||||
{
|
||||
_RTEMS_Lock_allocator();
|
||||
_Thread_Kill_zombies();
|
||||
_RTEMS_Unlock_allocator();
|
||||
|
||||
T_objects_check(OBJECTS_CLASSIC_API, OBJECTS_RTEMS_TASKS,
|
||||
&T_task_count, "RTEMS task");
|
||||
}
|
||||
|
||||
void
|
||||
T_check_rtems_tasks(T_event event, const char *name)
|
||||
{
|
||||
(void)name;
|
||||
|
||||
switch (event) {
|
||||
case T_EVENT_RUN_INITIALIZE:
|
||||
T_rtems_tasks_run_initialize();
|
||||
break;
|
||||
case T_EVENT_CASE_END:
|
||||
T_rtems_tasks_case_end();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static Objects_Maximum T_timer_count;
|
||||
|
||||
static void
|
||||
T_rtems_timers_run_initialize(void)
|
||||
{
|
||||
T_timer_count = T_objects_count(OBJECTS_CLASSIC_API,
|
||||
OBJECTS_RTEMS_TIMERS);
|
||||
}
|
||||
|
||||
static void
|
||||
T_rtems_timers_case_end(void)
|
||||
{
|
||||
T_objects_check(OBJECTS_CLASSIC_API, OBJECTS_RTEMS_TIMERS,
|
||||
&T_timer_count, "RTEMS timer");
|
||||
}
|
||||
|
||||
void
|
||||
T_check_rtems_timers(T_event event, const char *name)
|
||||
{
|
||||
(void)name;
|
||||
|
||||
switch (event) {
|
||||
case T_EVENT_RUN_INITIALIZE:
|
||||
T_rtems_timers_run_initialize();
|
||||
break;
|
||||
case T_EVENT_CASE_END:
|
||||
T_rtems_timers_case_end();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static Objects_Maximum T_posix_key_count;
|
||||
|
||||
static ssize_t T_posix_key_value_count;
|
||||
|
||||
static POSIX_Keys_Control *
|
||||
T_get_next_posix_key(Objects_Id *id)
|
||||
{
|
||||
return (POSIX_Keys_Control *)
|
||||
_Objects_Get_next(*id, &_POSIX_Keys_Information, id);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
T_get_active_posix_key_value_pairs(void)
|
||||
{
|
||||
ssize_t count;
|
||||
Objects_Id id;
|
||||
POSIX_Keys_Control *the_key;
|
||||
|
||||
count = 0;
|
||||
id = OBJECTS_ID_INITIAL_INDEX;
|
||||
|
||||
while ((the_key = T_get_next_posix_key(&id)) != NULL ) {
|
||||
count += (ssize_t)
|
||||
_Chain_Node_count_unprotected(&the_key->Key_value_pairs);
|
||||
_Objects_Allocator_unlock();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void
|
||||
T_posix_keys_run_initialize(void)
|
||||
{
|
||||
T_posix_key_count = T_objects_count(OBJECTS_POSIX_API,
|
||||
OBJECTS_POSIX_KEYS);
|
||||
T_posix_key_value_count = T_get_active_posix_key_value_pairs();
|
||||
}
|
||||
|
||||
static void
|
||||
T_posix_keys_case_end(void)
|
||||
{
|
||||
ssize_t count;
|
||||
ssize_t delta;
|
||||
|
||||
T_objects_check(OBJECTS_POSIX_API, OBJECTS_POSIX_KEYS,
|
||||
&T_posix_key_count, "POSIX key");
|
||||
|
||||
count = T_get_active_posix_key_value_pairs();
|
||||
delta = count - T_posix_key_value_count;
|
||||
|
||||
if (delta != 0) {
|
||||
T_posix_key_value_count = count;
|
||||
T_check_true(NULL, false, "POSIX key value pair leak (%zi)", delta);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
T_check_posix_keys(T_event event, const char *name)
|
||||
{
|
||||
(void)name;
|
||||
|
||||
switch (event) {
|
||||
case T_EVENT_RUN_INITIALIZE:
|
||||
T_posix_keys_run_initialize();
|
||||
break;
|
||||
case T_EVENT_CASE_END:
|
||||
T_posix_keys_case_end();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
51
cpukit/libtest/t-test-rtems.c
Normal file
51
cpukit/libtest/t-test-rtems.c
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (C) 2018 embedded brains GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <t.h>
|
||||
|
||||
#include <rtems.h>
|
||||
#include <rtems/bspIo.h>
|
||||
|
||||
void
|
||||
T_putchar_default(int c, void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
rtems_putc((char)c);
|
||||
}
|
||||
|
||||
void
|
||||
T_check_rsc(uint32_t a, const T_check_context *t, uint32_t e)
|
||||
{
|
||||
T_check_true(a == e, t, "%s == %s", rtems_status_text(a),
|
||||
rtems_status_text(e));
|
||||
}
|
||||
|
||||
void
|
||||
T_check_rsc_success(uint32_t a, const T_check_context *t)
|
||||
{
|
||||
T_check_rsc(a, t, RTEMS_SUCCESSFUL);
|
||||
}
|
||||
233
cpukit/libtest/t-test-time.c
Normal file
233
cpukit/libtest/t-test-time.c
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (C) 2018 embedded brains GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#undef __STRICT_ANSI__
|
||||
|
||||
#include <t.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __rtems__
|
||||
#include <rtems/counter.h>
|
||||
#include <rtems/score/timecounter.h>
|
||||
#endif
|
||||
|
||||
#ifdef __rtems__
|
||||
static T_time
|
||||
round_sbt(T_time time)
|
||||
{
|
||||
/*
|
||||
* One 1ns consists of 4.30 fractions of 1/2**32. Round up close to
|
||||
* the middle. This turns the conversion mapping of struct timespec to
|
||||
* sbintime_t and back into the identity function.
|
||||
*/
|
||||
return time + 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
const char *
|
||||
T_time_to_string_ns(T_time time, T_time_string string)
|
||||
{
|
||||
uint32_t s;
|
||||
uint32_t f;
|
||||
|
||||
#ifdef __rtems__
|
||||
time = round_sbt(time);
|
||||
s = (uint32_t)(time >> 32);
|
||||
f = (uint32_t)(((uint64_t)1000000000 * (uint32_t)time) >> 32);
|
||||
#else
|
||||
s = (uint32_t)(time / 1000000000);
|
||||
f = (uint32_t)(time % 1000000000);
|
||||
#endif
|
||||
|
||||
(void)T_snprintf(string, sizeof(T_time_string),
|
||||
"%" PRIu32 ".%09" PRIu32, s, f);
|
||||
return string;
|
||||
}
|
||||
|
||||
const char *
|
||||
T_time_to_string_us(T_time time, T_time_string string)
|
||||
{
|
||||
uint32_t s;
|
||||
uint32_t f;
|
||||
|
||||
#ifdef __rtems__
|
||||
time = round_sbt(time);
|
||||
s = (uint32_t)(time >> 32);
|
||||
f = (uint32_t)(((uint64_t)1000000 * (uint32_t)time) >> 32);
|
||||
#else
|
||||
time /= 1000;
|
||||
s = (uint32_t)(time / 1000000);
|
||||
f = (uint32_t)(time % 1000000);
|
||||
#endif
|
||||
|
||||
(void)T_snprintf(string, sizeof(T_time_string),
|
||||
"%" PRIu32 ".%06" PRIu32, s, f);
|
||||
return string;
|
||||
}
|
||||
|
||||
const char *
|
||||
T_time_to_string_ms(T_time time, T_time_string string)
|
||||
{
|
||||
uint32_t s;
|
||||
uint32_t f;
|
||||
|
||||
#ifdef __rtems__
|
||||
time = round_sbt(time);
|
||||
s = (uint32_t)(time >> 32);
|
||||
f = (uint32_t)(((uint64_t)1000 * (uint32_t)time) >> 32);
|
||||
#else
|
||||
time /= 1000000;
|
||||
s = (uint32_t)(time / 1000);
|
||||
f = (uint32_t)(time % 1000);
|
||||
#endif
|
||||
|
||||
(void)T_snprintf(string, sizeof(T_time_string),
|
||||
"%" PRIu32 ".%03" PRIu32, s, f);
|
||||
return string;
|
||||
}
|
||||
|
||||
const char *
|
||||
T_time_to_string_s(T_time time, T_time_string string)
|
||||
{
|
||||
uint32_t s;
|
||||
|
||||
#ifdef __rtems__
|
||||
time = round_sbt(time);
|
||||
s = (uint32_t)(time >> 32);
|
||||
#else
|
||||
s = (uint32_t)(time / 1000000000);
|
||||
#endif
|
||||
|
||||
(void)T_snprintf(string, sizeof(T_time_string), "%" PRIu32, s);
|
||||
return string;
|
||||
}
|
||||
|
||||
const char *
|
||||
T_ticks_to_string_ns(T_ticks ticks, T_time_string string)
|
||||
{
|
||||
return T_time_to_string_ns(T_ticks_to_time(ticks), string);
|
||||
}
|
||||
|
||||
const char *
|
||||
T_ticks_to_string_us(T_ticks ticks, T_time_string string)
|
||||
{
|
||||
return T_time_to_string_us(T_ticks_to_time(ticks), string);
|
||||
}
|
||||
|
||||
const char *
|
||||
T_ticks_to_string_ms(T_ticks ticks, T_time_string string)
|
||||
{
|
||||
return T_time_to_string_ms(T_ticks_to_time(ticks), string);
|
||||
}
|
||||
|
||||
const char *
|
||||
T_ticks_to_string_s(T_ticks ticks, T_time_string string)
|
||||
{
|
||||
return T_time_to_string_s(T_ticks_to_time(ticks), string);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
T_ticks_to_time(T_ticks ticks)
|
||||
{
|
||||
#ifdef __rtems__
|
||||
return (uint64_t)rtems_counter_ticks_to_sbintime(ticks);
|
||||
#else
|
||||
return ticks;
|
||||
#endif
|
||||
}
|
||||
|
||||
T_ticks
|
||||
T_time_to_ticks(T_time time)
|
||||
{
|
||||
#ifdef __rtems__
|
||||
return rtems_counter_sbintime_to_ticks((sbintime_t)time);
|
||||
#else
|
||||
return time;
|
||||
#endif
|
||||
}
|
||||
|
||||
T_time
|
||||
T_seconds_and_nanoseconds_to_time(uint32_t s, uint32_t ns)
|
||||
{
|
||||
#ifdef __rtems__
|
||||
struct timespec ts;
|
||||
|
||||
ts.tv_sec = s;
|
||||
ts.tv_nsec = (long)ns;
|
||||
return (T_time)tstosbt(ts);
|
||||
#else
|
||||
return (T_time)s * (T_time)1000000000 + (T_time)ns;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
T_time_to_seconds_and_nanoseconds(T_time time, uint32_t *s, uint32_t *ns)
|
||||
{
|
||||
#ifdef __rtems__
|
||||
time = round_sbt(time);
|
||||
*s = (uint32_t)(time >> 32);
|
||||
*ns = (uint32_t)(((uint64_t)1000000000 * (uint32_t)time) >> 32);
|
||||
#else
|
||||
*s = (uint32_t)(time / 1000000000);
|
||||
*ns = (uint32_t)(time % 1000000000);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef __rtems__
|
||||
T_time
|
||||
T_now(void)
|
||||
{
|
||||
struct timespec tp;
|
||||
|
||||
(void)clock_gettime(CLOCK_MONOTONIC, &tp);
|
||||
return (T_time)tp.tv_sec * (T_time)1000000000 + (T_time)tp.tv_nsec;
|
||||
}
|
||||
|
||||
T_ticks
|
||||
T_tick(void)
|
||||
{
|
||||
return T_now();
|
||||
}
|
||||
#endif
|
||||
|
||||
static atomic_uint dummy_time;
|
||||
|
||||
T_time
|
||||
T_now_dummy(void)
|
||||
{
|
||||
return atomic_fetch_add_explicit(&dummy_time, 1, memory_order_relaxed);
|
||||
}
|
||||
|
||||
T_time
|
||||
T_now_via_tick(void)
|
||||
{
|
||||
return T_ticks_to_time(T_tick());
|
||||
}
|
||||
888
cpukit/libtest/t-test.c
Normal file
888
cpukit/libtest/t-test.c
Normal file
@@ -0,0 +1,888 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (C) 2018, 2019 embedded brains GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <t.h>
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
#ifdef __rtems__
|
||||
#include <rtems/score/io.h>
|
||||
#include <rtems/score/percpu.h>
|
||||
#include <rtems/score/smp.h>
|
||||
#include <rtems/score/threadimpl.h>
|
||||
#include <rtems/linkersets.h>
|
||||
#include <rtems/version.h>
|
||||
#else
|
||||
#include "t-test-printf.h"
|
||||
#endif /* __rtems__ */
|
||||
|
||||
#define T_LINE_SIZE 120
|
||||
|
||||
#define T_SCOPE_SIZE 5
|
||||
|
||||
typedef struct {
|
||||
pthread_spinlock_t lock;
|
||||
void (*putchar)(int, void *);
|
||||
void *putchar_arg;
|
||||
T_verbosity verbosity;
|
||||
const T_case_context *registered_cases;
|
||||
const T_case_context *current_case;
|
||||
void *fixture_context;
|
||||
LIST_HEAD(, T_destructor) destructors;
|
||||
T_time case_begin_time;
|
||||
atomic_uint planned_steps;
|
||||
atomic_uint steps;
|
||||
atomic_uint failures;
|
||||
jmp_buf case_begin_context;
|
||||
unsigned int overall_cases;
|
||||
unsigned int overall_steps;
|
||||
unsigned int overall_failures;
|
||||
T_time run_begin_time;
|
||||
#ifdef __rtems__
|
||||
Thread_Control *runner_thread;
|
||||
const Per_CPU_Control *runner_cpu;
|
||||
#else
|
||||
bool runner_valid;
|
||||
pthread_t runner_thread;
|
||||
#endif
|
||||
const T_config *config;
|
||||
} T_context;
|
||||
|
||||
static T_context T_instance;
|
||||
|
||||
static int
|
||||
T_do_vprintf(T_context *ctx, char const *fmt, va_list ap)
|
||||
{
|
||||
return _IO_Vprintf(ctx->putchar, ctx->putchar_arg, fmt, ap);
|
||||
}
|
||||
|
||||
int
|
||||
T_vprintf(char const *fmt, va_list ap)
|
||||
{
|
||||
return T_do_vprintf(&T_instance, fmt, ap);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char *s;
|
||||
size_t n;
|
||||
} T_putchar_string_context;
|
||||
|
||||
static void
|
||||
T_putchar_string(int c, void *arg)
|
||||
{
|
||||
T_putchar_string_context *ctx;
|
||||
char *s;
|
||||
size_t n;
|
||||
|
||||
ctx = arg;
|
||||
s = ctx->s;
|
||||
n = ctx->n;
|
||||
|
||||
if (n == 1) {
|
||||
c = '\0';
|
||||
}
|
||||
|
||||
if (n > 1) {
|
||||
*s = (char)c;
|
||||
++s;
|
||||
--n;
|
||||
}
|
||||
|
||||
ctx->s = s;
|
||||
ctx->n = n;
|
||||
}
|
||||
|
||||
int
|
||||
T_snprintf(char *s, size_t n, char const *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int len;
|
||||
T_putchar_string_context ctx = {
|
||||
.s = s,
|
||||
.n = n
|
||||
};
|
||||
|
||||
va_start(ap, fmt);
|
||||
len = _IO_Vprintf(T_putchar_string, &ctx, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (ctx.n > 0) {
|
||||
*ctx.s = '\0';
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int
|
||||
T_cpu(void)
|
||||
{
|
||||
#if defined(__rtems__)
|
||||
return (int)_SMP_Get_current_processor();
|
||||
#elif defined(__linux__)
|
||||
return sched_getcpu();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__rtems__)
|
||||
static const char *
|
||||
T_object_name_to_string(Objects_Name name, char *buf)
|
||||
{
|
||||
uint32_t on;
|
||||
size_t i;
|
||||
int s;
|
||||
|
||||
on = name.name_u32;
|
||||
i = 0;
|
||||
|
||||
for (s = 24; s >= 0; s -= 8) {
|
||||
unsigned char c;
|
||||
|
||||
c = (unsigned char)(on >> s);
|
||||
|
||||
if (c >= '!' && c <= '~') {
|
||||
buf[i] = (char)c;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
buf[i] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
static const char *
|
||||
T_thread_name(const Thread_Control *th, char *buf)
|
||||
{
|
||||
if (th != NULL) {
|
||||
const char *name;
|
||||
|
||||
name = th->Join_queue.Queue.name;
|
||||
|
||||
if (name != NULL && name[0] != '\0') {
|
||||
return name;
|
||||
} else {
|
||||
return T_object_name_to_string(th->Object.name, buf);
|
||||
}
|
||||
} else {
|
||||
buf[0] = '?';
|
||||
buf[1] = '\0';
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char *
|
||||
T_scope(char *buf)
|
||||
{
|
||||
const char *r;
|
||||
|
||||
#if defined(__rtems__)
|
||||
ISR_Level level;
|
||||
const Per_CPU_Control *cpu_self;
|
||||
|
||||
_ISR_Local_disable(level);
|
||||
cpu_self = _Per_CPU_Get();
|
||||
|
||||
if (cpu_self->isr_nest_level == 0) {
|
||||
Thread_Control *executing;
|
||||
|
||||
executing = _Per_CPU_Get_executing(cpu_self);
|
||||
_ISR_Local_enable(level);
|
||||
r = T_thread_name(executing, buf);
|
||||
} else {
|
||||
_ISR_Local_enable(level);
|
||||
buf[0] = 'I';
|
||||
buf[1] = 'S';
|
||||
buf[2] = 'R';
|
||||
buf[3] = '\0';
|
||||
r = buf;
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
static __thread char name[128];
|
||||
|
||||
(void)buf;
|
||||
|
||||
if (name[0] == '\0') {
|
||||
pthread_getname_np(pthread_self(), name, sizeof(name));
|
||||
}
|
||||
|
||||
r = &name[0];
|
||||
#else
|
||||
buf[0] = '?';
|
||||
buf[1] = '\0';
|
||||
r = buf;
|
||||
#endif
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void
|
||||
T_set_runner(T_context *ctx)
|
||||
{
|
||||
#ifdef __rtems__
|
||||
ISR_Level level;
|
||||
const Per_CPU_Control *cpu_self;
|
||||
|
||||
_ISR_Local_disable(level);
|
||||
cpu_self = _Per_CPU_Get();
|
||||
ctx->runner_cpu = cpu_self;
|
||||
|
||||
if (cpu_self->isr_nest_level == 0) {
|
||||
ctx->runner_thread = _Per_CPU_Get_executing(cpu_self);
|
||||
} else {
|
||||
ctx->runner_thread = NULL;
|
||||
}
|
||||
|
||||
_ISR_Local_enable(level);
|
||||
#else
|
||||
ctx->runner_valid = true;
|
||||
ctx->runner_thread = pthread_self();
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
T_printf(char const *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int len;
|
||||
|
||||
va_start(ap, fmt);
|
||||
len = T_vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void
|
||||
T_log(T_verbosity verbosity, char const *fmt, ...)
|
||||
{
|
||||
T_context *ctx;
|
||||
|
||||
ctx = &T_instance;
|
||||
|
||||
if (ctx->verbosity >= verbosity) {
|
||||
va_list ap;
|
||||
|
||||
T_printf("L:");
|
||||
va_start(ap, fmt);
|
||||
T_vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
T_printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
T_fetch_add_step(T_context *ctx)
|
||||
{
|
||||
return atomic_fetch_add_explicit(&ctx->steps, 1, memory_order_relaxed);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
T_add_failure(T_context *ctx)
|
||||
{
|
||||
return atomic_fetch_add_explicit(&ctx->failures, 1,
|
||||
memory_order_relaxed);
|
||||
}
|
||||
|
||||
static void
|
||||
T_stop(T_context *ctx)
|
||||
{
|
||||
const T_case_context *tc;
|
||||
|
||||
tc = ctx->current_case;
|
||||
|
||||
if (tc != NULL) {
|
||||
const T_fixture *fixture;
|
||||
|
||||
fixture = tc->fixture;
|
||||
|
||||
if (fixture != NULL && fixture->stop != NULL) {
|
||||
(*fixture->stop)(ctx->fixture_context);
|
||||
}
|
||||
}
|
||||
|
||||
longjmp(ctx->case_begin_context, 1);
|
||||
}
|
||||
|
||||
void T_plan(unsigned int planned_steps)
|
||||
{
|
||||
T_context *ctx;
|
||||
unsigned int expected;
|
||||
bool success;
|
||||
|
||||
ctx = &T_instance;
|
||||
expected = UINT_MAX;
|
||||
success = atomic_compare_exchange_strong_explicit(&ctx->planned_steps,
|
||||
&expected, planned_steps, memory_order_relaxed,
|
||||
memory_order_relaxed);
|
||||
T_check_true(success, NULL, "planned steps (%u) already set", expected);
|
||||
}
|
||||
|
||||
void
|
||||
T_case_register(T_case_context *tc)
|
||||
{
|
||||
T_context *ctx;
|
||||
|
||||
ctx = &T_instance;
|
||||
tc->next = ctx->registered_cases;
|
||||
ctx->registered_cases = tc;
|
||||
}
|
||||
|
||||
T_verbosity
|
||||
T_set_verbosity(T_verbosity verbosity)
|
||||
{
|
||||
T_context *ctx;
|
||||
T_verbosity previous;
|
||||
|
||||
ctx = &T_instance;
|
||||
previous = ctx->verbosity;
|
||||
ctx->verbosity = verbosity;
|
||||
|
||||
return previous;
|
||||
}
|
||||
|
||||
void *
|
||||
T_fixture_context(void)
|
||||
{
|
||||
return T_instance.fixture_context;
|
||||
}
|
||||
|
||||
void
|
||||
T_set_fixture_context(void *context)
|
||||
{
|
||||
T_instance.fixture_context = context;
|
||||
}
|
||||
|
||||
const char *
|
||||
T_case_name(void)
|
||||
{
|
||||
const T_case_context *tc;
|
||||
|
||||
tc = T_instance.current_case;
|
||||
|
||||
if (tc != NULL) {
|
||||
return tc->name;
|
||||
} else {
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
T_check_true(bool ok, const T_check_context *t, const char *fmt, ...)
|
||||
{
|
||||
T_context *ctx;
|
||||
va_list ap;
|
||||
char scope[T_SCOPE_SIZE];
|
||||
|
||||
ctx = &T_instance;
|
||||
|
||||
if (t != NULL) {
|
||||
unsigned int step;
|
||||
|
||||
if ((t->flags & T_CHECK_QUIET) == 0) {
|
||||
step = T_fetch_add_step(ctx);
|
||||
} else {
|
||||
step = UINT_MAX;
|
||||
}
|
||||
|
||||
if ((t->flags & T_CHECK_STEP_FLAG) != 0 &&
|
||||
step != T_CHECK_STEP_FROM_FLAGS(t->flags)) {
|
||||
T_add_failure(ctx);
|
||||
T_printf("F:%u:%i:%s:%s:%i:planned step (%u)\n", step,
|
||||
T_cpu(), T_scope(scope), t->file, t->line,
|
||||
T_CHECK_STEP_FROM_FLAGS(t->flags));
|
||||
} else if (!ok) {
|
||||
T_add_failure(ctx);
|
||||
|
||||
if (ctx->verbosity >= T_NORMAL) {
|
||||
if ((t->flags & T_CHECK_QUIET) == 0) {
|
||||
T_printf("F:%u:%i:%s:%s:%i:",
|
||||
step, T_cpu(), T_scope(scope),
|
||||
t->file, t->line);
|
||||
} else {
|
||||
T_printf("F:*:%i:%s:%s:%i:", T_cpu(),
|
||||
T_scope(scope), t->file, t->line);
|
||||
}
|
||||
|
||||
va_start(ap, fmt);
|
||||
T_vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
T_printf("\n");
|
||||
}
|
||||
|
||||
if ((t->flags & T_CHECK_STOP) != 0) {
|
||||
T_stop(ctx);
|
||||
}
|
||||
} else if ((t->flags & T_CHECK_QUIET) == 0 &&
|
||||
ctx->verbosity >= T_VERBOSE) {
|
||||
T_printf("P:%u:%i:%s:%s:%i\n", step, T_cpu(),
|
||||
T_scope(scope), t->file, t->line);
|
||||
}
|
||||
} else if (!ok) {
|
||||
T_add_failure(ctx);
|
||||
|
||||
T_printf("F:*:%i:%s:*:*:", T_cpu(), T_scope(scope));
|
||||
|
||||
va_start(ap, fmt);
|
||||
T_vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
T_printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
T_do_log(T_context *ctx, T_verbosity verbosity, char const *fmt, ...)
|
||||
{
|
||||
if (ctx->verbosity >= verbosity) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
T_vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
T_system(T_context *ctx)
|
||||
{
|
||||
#if defined(__rtems__)
|
||||
T_do_log(ctx, T_QUIET, "S:Platform:RTEMS\n");
|
||||
T_do_log(ctx, T_QUIET, "S:Compiler:" __VERSION__ "\n");
|
||||
T_do_log(ctx, T_QUIET, "S:Version:%s\n", rtems_version());
|
||||
T_do_log(ctx, T_QUIET, "S:BSP:%s\n", rtems_board_support_package());
|
||||
#if RTEMS_DEBUG
|
||||
T_do_log(ctx, T_QUIET, "S:RTEMS_DEBUG:1\n");
|
||||
#else
|
||||
T_do_log(ctx, T_QUIET, "S:RTEMS_DEBUG:0\n");
|
||||
#endif
|
||||
#if RTEMS_MULTIPROCESSING
|
||||
T_do_log(ctx, T_QUIET, "S:RTEMS_MULTIPROCESSING:1\n");
|
||||
#else
|
||||
T_do_log(ctx, T_QUIET, "S:RTEMS_MULTIPROCESSING:0\n");
|
||||
#endif
|
||||
#if RTEMS_POSIX_API
|
||||
T_do_log(ctx, T_QUIET, "S:RTEMS_POSIX_API:1\n");
|
||||
#else
|
||||
T_do_log(ctx, T_QUIET, "S:RTEMS_POSIX_API:0\n");
|
||||
#endif
|
||||
#if RTEMS_PROFILING
|
||||
T_do_log(ctx, T_QUIET, "S:RTEMS_PROFILING:1\n");
|
||||
#else
|
||||
T_do_log(ctx, T_QUIET, "S:RTEMS_PROFILING:0\n");
|
||||
#endif
|
||||
#if RTEMS_SMP
|
||||
T_do_log(ctx, T_QUIET, "S:RTEMS_SMP:1\n");
|
||||
#else
|
||||
T_do_log(ctx, T_QUIET, "S:RTEMS_SMP:0\n");
|
||||
#endif
|
||||
#elif defined(__linux__)
|
||||
T_do_log(ctx, T_QUIET, "S:Platform:Linux\n");
|
||||
T_do_log(ctx, T_QUIET, "S:Compiler:" __VERSION__ "\n");
|
||||
#else
|
||||
T_do_log(ctx, T_QUIET, "S:Platform:POSIX\n");
|
||||
#ifdef __VERSION__
|
||||
T_do_log(ctx, T_QUIET, "S:Compiler:" __VERSION__ "\n");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
T_add_destructor(T_destructor *dtor, void (*destroy)(T_destructor *))
|
||||
{
|
||||
T_context *ctx;
|
||||
|
||||
dtor->destroy = destroy;
|
||||
ctx = &T_instance;
|
||||
pthread_spin_lock(&ctx->lock);
|
||||
LIST_INSERT_HEAD(&ctx->destructors, dtor, node);
|
||||
pthread_spin_unlock(&ctx->lock);
|
||||
}
|
||||
|
||||
void
|
||||
T_remove_destructor(T_destructor *dtor)
|
||||
{
|
||||
T_context *ctx;
|
||||
|
||||
ctx = &T_instance;
|
||||
pthread_spin_lock(&ctx->lock);
|
||||
LIST_REMOVE(dtor, node);
|
||||
pthread_spin_unlock(&ctx->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
T_call_destructors(const T_context *ctx)
|
||||
{
|
||||
T_destructor *dtor;
|
||||
|
||||
#ifdef __linux__
|
||||
while (!LIST_EMPTY(&ctx->destructors)) {
|
||||
dtor = LIST_FIRST(&ctx->destructors);
|
||||
LIST_REMOVE(dtor, node);
|
||||
(*dtor->destroy)(dtor);
|
||||
}
|
||||
#else
|
||||
T_destructor *tmp;
|
||||
|
||||
LIST_FOREACH_SAFE(dtor, &ctx->destructors, node, tmp) {
|
||||
(*dtor->destroy)(dtor);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
T_call_actions_forward(const T_config *config, T_event event, const char *name)
|
||||
{
|
||||
const T_action *actions;
|
||||
size_t n;
|
||||
size_t i;
|
||||
|
||||
actions = config->actions;
|
||||
n = config->action_count;
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
(*actions[i])(event, name);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
T_call_actions_backward(const T_config *config, T_event event,
|
||||
const char *name)
|
||||
{
|
||||
const T_action *actions;
|
||||
size_t n;
|
||||
size_t i;
|
||||
|
||||
actions = config->actions;
|
||||
n = config->action_count;
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
(*actions[n - i - 1])(event, name);
|
||||
}
|
||||
}
|
||||
|
||||
static T_context *
|
||||
T_do_run_initialize(const T_config *config)
|
||||
{
|
||||
T_context *ctx;
|
||||
|
||||
ctx = &T_instance;
|
||||
|
||||
pthread_spin_init(&ctx->lock, PTHREAD_PROCESS_PRIVATE);
|
||||
|
||||
ctx->config = config;
|
||||
ctx->putchar = config->putchar;
|
||||
ctx->putchar_arg = config->putchar_arg;
|
||||
ctx->verbosity = config->verbosity;
|
||||
atomic_store_explicit(&ctx->steps, 0, memory_order_relaxed);
|
||||
atomic_store_explicit(&ctx->failures, 0, memory_order_relaxed);
|
||||
ctx->overall_cases = 0;
|
||||
ctx->overall_steps = 0;
|
||||
ctx->overall_failures = 0;
|
||||
|
||||
T_set_runner(ctx);
|
||||
T_call_actions_forward(config, T_EVENT_RUN_INITIALIZE, config->name);
|
||||
T_do_log(ctx, T_QUIET, "A:%s\n", config->name);
|
||||
T_system(ctx);
|
||||
ctx->run_begin_time = (*config->now)();
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void
|
||||
T_do_case_begin(T_context *ctx, const T_case_context *tc)
|
||||
{
|
||||
const T_config *config;
|
||||
const T_fixture *fixture;
|
||||
|
||||
config = ctx->config;
|
||||
fixture = tc->fixture;
|
||||
ctx->verbosity = config->verbosity;
|
||||
ctx->current_case = tc;
|
||||
LIST_INIT(&ctx->destructors);
|
||||
atomic_store_explicit(&ctx->planned_steps, UINT_MAX,
|
||||
memory_order_relaxed);
|
||||
atomic_store_explicit(&ctx->steps, 0, memory_order_relaxed);
|
||||
atomic_store_explicit(&ctx->failures, 0, memory_order_relaxed);
|
||||
|
||||
T_call_actions_forward(config, T_EVENT_CASE_EARLY, tc->name);
|
||||
T_do_log(ctx, T_NORMAL, "B:%s\n", tc->name);
|
||||
ctx->case_begin_time = (*config->now)();
|
||||
T_call_actions_forward(config, T_EVENT_CASE_BEGIN, tc->name);
|
||||
|
||||
if (fixture != NULL) {
|
||||
ctx->fixture_context = fixture->initial_context;
|
||||
|
||||
if (fixture->setup != NULL) {
|
||||
(*fixture->setup)(ctx->fixture_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
T_do_case_end(T_context *ctx, const T_case_context *tc)
|
||||
{
|
||||
const T_config *config;
|
||||
const T_fixture *fixture;
|
||||
unsigned int planned_steps;
|
||||
unsigned int steps;
|
||||
unsigned int failures;
|
||||
T_time delta;
|
||||
T_time_string ts;
|
||||
|
||||
config = ctx->config;
|
||||
fixture = tc->fixture;
|
||||
|
||||
if (fixture != NULL && fixture->teardown != NULL) {
|
||||
(*fixture->teardown)(ctx->fixture_context);
|
||||
}
|
||||
|
||||
T_call_destructors(ctx);
|
||||
T_call_actions_backward(config, T_EVENT_CASE_END, tc->name);
|
||||
|
||||
planned_steps = atomic_fetch_add_explicit(&ctx->planned_steps,
|
||||
0, memory_order_relaxed);
|
||||
steps = atomic_fetch_add_explicit(&ctx->steps, 0,
|
||||
memory_order_relaxed);
|
||||
failures = atomic_fetch_add_explicit(&ctx->failures, 0,
|
||||
memory_order_relaxed);
|
||||
|
||||
if (planned_steps != UINT_MAX && planned_steps != steps &&
|
||||
failures == 0) {
|
||||
++failures;
|
||||
|
||||
if (ctx->verbosity >= T_NORMAL) {
|
||||
char scope[T_SCOPE_SIZE];
|
||||
|
||||
T_printf("F:*:%i:%s:*:*:actual steps (%u), "
|
||||
"planned steps (%u)\n", T_cpu(),
|
||||
T_scope(scope), steps, planned_steps);
|
||||
}
|
||||
}
|
||||
|
||||
delta = (*config->now)() - ctx->case_begin_time;
|
||||
T_do_log(ctx, T_QUIET, "E:%s:N:%u:F:%u:D:%s\n",
|
||||
tc->name, steps, failures, T_time_to_string_us(delta, ts));
|
||||
|
||||
++ctx->overall_cases;
|
||||
ctx->overall_steps += steps;
|
||||
ctx->overall_failures += failures;
|
||||
|
||||
T_call_actions_backward(config, T_EVENT_CASE_LATE, tc->name);
|
||||
}
|
||||
|
||||
static void
|
||||
T_run_case(T_context *ctx, const T_case_context *tc)
|
||||
{
|
||||
T_do_case_begin(ctx, tc);
|
||||
|
||||
if (setjmp(ctx->case_begin_context) == 0) {
|
||||
(*tc->body)();
|
||||
}
|
||||
|
||||
T_do_case_end(ctx, tc);
|
||||
}
|
||||
|
||||
static void
|
||||
T_do_run_all(T_context *ctx)
|
||||
{
|
||||
const T_case_context *tc;
|
||||
|
||||
tc = ctx->registered_cases;
|
||||
|
||||
while (tc != NULL) {
|
||||
T_run_case(ctx, tc);
|
||||
tc = tc->next;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
T_do_run_finalize(T_context *ctx)
|
||||
{
|
||||
const T_config *config;
|
||||
T_time delta;
|
||||
T_time_string ts;
|
||||
|
||||
config = ctx->config;
|
||||
delta = (*config->now)() - ctx->run_begin_time;
|
||||
T_do_log(ctx, T_QUIET, "Z:%s:C:%u:N:%u:F:%u:D:%s\n", config->name,
|
||||
ctx->overall_cases, ctx->overall_steps, ctx->overall_failures,
|
||||
T_time_to_string_us(delta, ts));
|
||||
T_call_actions_backward(config, T_EVENT_RUN_FINALIZE, config->name);
|
||||
#ifdef __rtems__
|
||||
ctx->runner_thread = NULL;
|
||||
ctx->runner_cpu = NULL;
|
||||
#else
|
||||
ctx->runner_valid = false;
|
||||
#endif
|
||||
pthread_spin_destroy(&ctx->lock);
|
||||
|
||||
return ctx->overall_failures == 0;
|
||||
}
|
||||
|
||||
int
|
||||
T_main(const T_config *config)
|
||||
{
|
||||
T_context *ctx;
|
||||
|
||||
ctx = T_do_run_initialize(config);
|
||||
T_do_run_all(ctx);
|
||||
|
||||
return T_do_run_finalize(ctx) ? 0 : 1;
|
||||
}
|
||||
|
||||
bool T_is_runner(void)
|
||||
{
|
||||
T_context *ctx;
|
||||
bool is_runner;
|
||||
#ifdef __rtems__
|
||||
ISR_Level level;
|
||||
const Per_CPU_Control *cpu_self;
|
||||
#endif
|
||||
|
||||
ctx = &T_instance;
|
||||
#ifdef __rtems__
|
||||
_ISR_Local_disable(level);
|
||||
cpu_self = _Per_CPU_Get();
|
||||
|
||||
if (ctx->runner_thread != NULL) {
|
||||
is_runner = cpu_self->isr_nest_level == 0 &&
|
||||
_Per_CPU_Get_executing(cpu_self) == ctx->runner_thread;
|
||||
} else {
|
||||
is_runner = cpu_self == ctx->runner_cpu;
|
||||
}
|
||||
|
||||
_ISR_Local_enable(level);
|
||||
#else
|
||||
is_runner = ctx->runner_valid &&
|
||||
pthread_equal(pthread_self(), ctx->runner_thread) != 0;
|
||||
#endif
|
||||
|
||||
return is_runner;
|
||||
}
|
||||
|
||||
#ifdef __rtems__
|
||||
RTEMS_LINKER_ROSET(_T, T_case_context *);
|
||||
#endif /* __rtems__ */
|
||||
|
||||
void T_register(void)
|
||||
{
|
||||
#ifdef __rtems__
|
||||
T_case_context * const *tc;
|
||||
|
||||
RTEMS_LINKER_SET_FOREACH(_T, tc) {
|
||||
T_case_register(*tc);
|
||||
}
|
||||
#endif /* __rtems__ */
|
||||
}
|
||||
|
||||
void
|
||||
T_run_initialize(const T_config *config)
|
||||
{
|
||||
(void)T_do_run_initialize(config);
|
||||
}
|
||||
|
||||
void
|
||||
T_run_all(void)
|
||||
{
|
||||
T_do_run_all(&T_instance);
|
||||
}
|
||||
|
||||
void
|
||||
T_run_by_name(const char *name)
|
||||
{
|
||||
T_context *ctx;
|
||||
const T_case_context *tc;
|
||||
|
||||
ctx = &T_instance;
|
||||
tc = ctx->registered_cases;
|
||||
|
||||
while (tc != NULL) {
|
||||
if (strcmp(tc->name, name) == 0) {
|
||||
T_run_case(ctx, tc);
|
||||
break;
|
||||
}
|
||||
|
||||
tc = tc->next;
|
||||
}
|
||||
}
|
||||
|
||||
static T_case_context default_case;
|
||||
|
||||
void
|
||||
T_case_begin(const char *name, const T_fixture *fixture)
|
||||
{
|
||||
T_case_context *tc;
|
||||
|
||||
tc = &default_case;
|
||||
tc->name = name;
|
||||
tc->fixture = fixture;
|
||||
T_do_case_begin(&T_instance, tc);
|
||||
}
|
||||
|
||||
void
|
||||
T_case_end(void)
|
||||
{
|
||||
T_case_context *tc;
|
||||
|
||||
tc = &default_case;
|
||||
T_do_case_end(&T_instance, tc);
|
||||
}
|
||||
|
||||
bool
|
||||
T_run_finalize(void)
|
||||
{
|
||||
return T_do_run_finalize(&T_instance);
|
||||
}
|
||||
|
||||
T_time
|
||||
T_case_begin_time(void)
|
||||
{
|
||||
return T_instance.case_begin_time;
|
||||
}
|
||||
|
||||
void
|
||||
T_set_putchar(T_putchar new_putchar, void *new_arg, T_putchar *old_putchar,
|
||||
void **old_arg)
|
||||
{
|
||||
T_context *ctx;
|
||||
|
||||
ctx = &T_instance;
|
||||
*old_putchar = ctx->putchar;
|
||||
*old_arg = ctx->putchar_arg;
|
||||
ctx->putchar = new_putchar;
|
||||
ctx->putchar_arg = new_arg;
|
||||
}
|
||||
Reference in New Issue
Block a user