Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:21

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup RTEMSTestFrameworkImpl
0007  *
0008  * @brief This source file contains the core implementation of RTEMS Test
0009  *    Framework.
0010  */
0011 
0012 /*
0013  * Copyright (C) 2018, 2023 embedded brains GmbH & Co. KG
0014  *
0015  * Redistribution and use in source and binary forms, with or without
0016  * modification, are permitted provided that the following conditions
0017  * are met:
0018  * 1. Redistributions of source code must retain the above copyright
0019  *    notice, this list of conditions and the following disclaimer.
0020  * 2. Redistributions in binary form must reproduce the above copyright
0021  *    notice, this list of conditions and the following disclaimer in the
0022  *    documentation and/or other materials provided with the distribution.
0023  *
0024  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0025  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0026  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0027  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0028  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0029  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0030  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0031  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0032  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0033  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0034  * POSSIBILITY OF SUCH DAMAGE.
0035  */
0036 
0037 #define _GNU_SOURCE
0038 
0039 #include <rtems/test.h>
0040 
0041 #include <sys/queue.h>
0042 #include <limits.h>
0043 #include <pthread.h>
0044 #include <sched.h>
0045 #include <setjmp.h>
0046 #include <stdatomic.h>
0047 
0048 #ifdef __rtems__
0049 #include <rtems/dev/io.h>
0050 #include <rtems/score/percpu.h>
0051 #include <rtems/score/smp.h>
0052 #include <rtems/score/threadimpl.h>
0053 #include <rtems/linkersets.h>
0054 #include <rtems/version.h>
0055 #else
0056 #include "t-test-printf.h"
0057 #endif /* __rtems__ */
0058 
0059 #define T_LINE_SIZE 256
0060 
0061 typedef struct {
0062     pthread_spinlock_t lock;
0063     char *buf;
0064     unsigned int buf_mask;
0065     atomic_uint buf_head;
0066     atomic_uint buf_tail;
0067     void (*putchar)(int, void *);
0068     void *putchar_arg;
0069     T_verbosity verbosity;
0070     const T_case_context *registered_cases;
0071     const T_case_context *current_case;
0072     T_fixture_node *fixtures;
0073     T_fixture_node case_fixture;
0074     LIST_HEAD(, T_destructor) destructors;
0075     T_remark remarks;
0076     T_time case_begin_time;
0077     atomic_uint planned_steps;
0078     atomic_uint steps;
0079     atomic_uint failures;
0080     bool case_begin_context_valid;
0081     jmp_buf case_begin_context;
0082     unsigned int fixture_steps;
0083     unsigned int overall_cases;
0084     unsigned int overall_steps;
0085     unsigned int overall_failures;
0086     T_time run_begin_time;
0087 #ifdef __rtems__
0088     Thread_Control *runner_thread;
0089     const Per_CPU_Control *runner_cpu;
0090 #else
0091     bool runner_valid;
0092     pthread_t runner_thread;
0093 #endif
0094     const T_config *config;
0095 } T_context;
0096 
0097 static T_context T_instance;
0098 
0099 const T_check_context T_special = {
0100     .file = "*",
0101     .line = -1,
0102     .flags = T_CHECK_FMT | T_CHECK_QUIET
0103 };
0104 
0105 static bool
0106 T_do_is_runner(T_context *ctx)
0107 {
0108     bool is_runner;
0109 #ifdef __rtems__
0110     ISR_Level level;
0111     const Per_CPU_Control *cpu_self;
0112 #endif
0113 
0114 #ifdef __rtems__
0115     _ISR_Local_disable(level);
0116     cpu_self = _Per_CPU_Get();
0117 
0118     if (ctx->runner_thread != NULL) {
0119         is_runner = cpu_self->isr_nest_level == 0 &&
0120             _Per_CPU_Get_executing(cpu_self) == ctx->runner_thread;
0121     } else {
0122         is_runner = cpu_self == ctx->runner_cpu;
0123     }
0124 
0125     _ISR_Local_enable(level);
0126 #else
0127     is_runner = ctx->runner_valid &&
0128         pthread_equal(pthread_self(), ctx->runner_thread) != 0;
0129 #endif
0130 
0131     return is_runner;
0132 }
0133 
0134 bool T_is_runner(void)
0135 {
0136     return T_do_is_runner(&T_instance);
0137 }
0138 
0139 typedef struct {
0140     char *s;
0141     size_t n;
0142 } T_putchar_string_context;
0143 
0144 static void
0145 T_putchar_string(int c, void *arg)
0146 {
0147     T_putchar_string_context *sctx;
0148     char *s;
0149     size_t n;
0150 
0151     sctx = arg;
0152     s = sctx->s;
0153     n = sctx->n;
0154 
0155     if (n == 1) {
0156         c = '\0';
0157     }
0158 
0159     if (n > 1) {
0160         *s = (char)c;
0161         ++s;
0162         --n;
0163     }
0164 
0165     sctx->s = s;
0166     sctx->n = n;
0167 }
0168 
0169 int
0170 T_snprintf(char *s, size_t n, char const *fmt, ...)
0171 {
0172     va_list ap;
0173     int len;
0174     T_putchar_string_context sctx = {
0175         .s = s,
0176         .n = n
0177     };
0178 
0179     va_start(ap, fmt);
0180     len = _IO_Vprintf(T_putchar_string, &sctx, fmt, ap);
0181     va_end(ap);
0182 
0183     if (sctx.n > 0) {
0184         *sctx.s = '\0';
0185     }
0186 
0187     return len;
0188 }
0189 
0190 static void
0191 T_output_buffer_drain(T_context *ctx)
0192 {
0193     unsigned int head;
0194     unsigned int tail;
0195 
0196     head = atomic_load_explicit(&ctx->buf_head, memory_order_acquire);
0197     tail = atomic_load_explicit(&ctx->buf_tail, memory_order_relaxed);
0198 
0199     while (head != tail) {
0200         (*ctx->putchar)(ctx->buf[tail], ctx->putchar_arg);
0201         tail = (tail + 1) & ctx->buf_mask;
0202     }
0203 
0204     atomic_store_explicit(&ctx->buf_tail, tail, memory_order_relaxed);
0205 }
0206 
0207 static unsigned int
0208 T_output_buffer_fill(T_context *ctx, const char *buf, unsigned int len)
0209 {
0210     unsigned int head;
0211     unsigned int tail;
0212     unsigned int mask;
0213     unsigned int capacity;
0214 
0215     pthread_spin_lock(&ctx->lock);
0216     head = atomic_load_explicit(&ctx->buf_head, memory_order_relaxed);
0217     tail = atomic_load_explicit(&ctx->buf_tail, memory_order_relaxed);
0218     mask = ctx->buf_mask;
0219     capacity = (tail - head - 1) & mask;
0220 
0221     if (len <= capacity) {
0222         unsigned int todo;
0223         const char *c;
0224 
0225         todo = len;
0226         c = buf;
0227 
0228         while (todo > 0) {
0229             ctx->buf[head] = *c;
0230             head = (head + 1) & mask;
0231             --todo;
0232             ++c;
0233         }
0234 
0235         atomic_store_explicit(&ctx->buf_head, head,
0236             memory_order_release);
0237     } else {
0238         /* If it does not fit into the buffer, discard everything */
0239         len = 0;
0240     }
0241 
0242     pthread_spin_unlock(&ctx->lock);
0243     return len;
0244 }
0245 
0246 static int
0247 T_vprintf_direct(char const *fmt, va_list ap)
0248 {
0249     T_context *ctx;
0250 
0251     ctx = &T_instance;
0252     T_output_buffer_drain(ctx);
0253     return _IO_Vprintf(ctx->putchar, ctx->putchar_arg, fmt, ap);
0254 }
0255 
0256 static int
0257 T_vprintf_buffered(char const *fmt, va_list ap)
0258 {
0259     char buf[T_LINE_SIZE];
0260     T_putchar_string_context sctx = {
0261         .s = buf,
0262         .n = sizeof(buf)
0263     };
0264     unsigned int len;
0265 
0266     len = (unsigned int)_IO_Vprintf(T_putchar_string, &sctx, fmt, ap);
0267 
0268     if (len >= sizeof(buf)) {
0269         len = sizeof(buf) - 1;
0270     }
0271 
0272     return (int)T_output_buffer_fill(&T_instance, buf, len);
0273 }
0274 
0275 int
0276 T_vprintf(char const *fmt, va_list ap)
0277 {
0278     int len;
0279 
0280     if (T_is_runner()) {
0281         len = T_vprintf_direct(fmt, ap);
0282     } else {
0283         len = T_vprintf_buffered(fmt, ap);
0284     }
0285 
0286     return len;
0287 }
0288 
0289 static int
0290 T_do_puts(T_context *ctx, const char *buf, size_t len)
0291 {
0292     if (T_do_is_runner(ctx)) {
0293         size_t i;
0294 
0295         T_output_buffer_drain(ctx);
0296 
0297         for (i = 0; i < len; ++i) {
0298             (*ctx->putchar)(buf[i], ctx->putchar_arg);
0299         }
0300     } else {
0301         len = T_output_buffer_fill(ctx, buf, len);
0302     }
0303 
0304     return (int)len;
0305 }
0306 
0307 int
0308 T_puts(const char *buf, size_t len)
0309 {
0310     return T_do_puts(&T_instance, buf, len);
0311 }
0312 
0313 static int
0314 T_cpu(void)
0315 {
0316 #if defined(__rtems__)
0317     return (int)_SMP_Get_current_processor();
0318 #elif defined(__linux__)
0319     return sched_getcpu();
0320 #else
0321     return 0;
0322 #endif
0323 }
0324 
0325 size_t
0326 T_str_copy(char *dst, const char *src, size_t n)
0327 {
0328     size_t i;
0329 
0330     i = 0;
0331 
0332     while (*src != '\0' && i < n) {
0333         *dst = *src;
0334         ++dst;
0335         ++src;
0336         ++i;
0337     }
0338 
0339     return i;
0340 }
0341 
0342 #if defined(__rtems__)
0343 static size_t
0344 T_object_name_to_string(char *dst, Objects_Name name, size_t n)
0345 {
0346     uint32_t on;
0347     size_t i;
0348     int s;
0349 
0350     on = name.name_u32;
0351     i = 0;
0352 
0353     for (s = 24; s >= 0; s -= 8) {
0354         unsigned char c;
0355 
0356         c = (unsigned char)(on >> s);
0357 
0358         if (c >= '!' && c <= '~' && i < n) {
0359             *dst = (char)c;
0360             ++dst;
0361             ++i;
0362         }
0363     }
0364 
0365     return i;
0366 }
0367 
0368 static size_t
0369 T_thread_name(char *dst, const Thread_Control *th, size_t n)
0370 {
0371     if (th != NULL) {
0372         const char *name;
0373 
0374         name = th->Join_queue.Queue.name;
0375 
0376         if (name != NULL && name[0] != '\0') {
0377             return T_str_copy(dst, name, n);
0378         }
0379 
0380         return T_object_name_to_string(dst, th->Object.name, n);
0381     }
0382 
0383     return T_str_copy(dst, "?", n);
0384 }
0385 #endif
0386 
0387 static size_t
0388 T_scope(T_context *ctx, char *dst, size_t n)
0389 {
0390     T_fixture_node *node;
0391     size_t len;
0392 
0393 #if defined(__rtems__)
0394     ISR_Level level;
0395     const Per_CPU_Control *cpu_self;
0396 
0397     _ISR_Local_disable(level);
0398     cpu_self = _Per_CPU_Get();
0399 
0400     if (cpu_self->isr_nest_level == 0) {
0401         Thread_Control *executing;
0402 
0403         executing = _Per_CPU_Get_executing(cpu_self);
0404         _ISR_Local_enable(level);
0405         len = T_thread_name(dst, executing, n);
0406     } else {
0407         _ISR_Local_enable(level);
0408         len = T_str_copy(dst, "ISR", n);
0409     }
0410 
0411 #elif defined(__linux__)
0412     static __thread char name[128];
0413 
0414     if (name[0] == '\0') {
0415         pthread_getname_np(pthread_self(), name, sizeof(name));
0416     }
0417 
0418     len = T_str_copy(dst, name, n);
0419 #else
0420     len = T_str_copy(dst, "?", n);
0421 #endif
0422 
0423     dst += len;
0424     n -= len;
0425     node = &ctx->case_fixture;
0426 
0427     do {
0428         const T_fixture *fixture;
0429 
0430         fixture = node->fixture;
0431 
0432         if (fixture != NULL && fixture->scope != NULL) {
0433             size_t m;
0434 
0435             m = (*fixture->scope)(node->context, dst, n);
0436             dst += m;
0437             n -= m;
0438             len += m;
0439         }
0440 
0441         node = node->previous;
0442     } while (node != NULL);
0443 
0444     return len;
0445 }
0446 
0447 static void
0448 T_do_make_runner(T_context *ctx)
0449 {
0450 #ifdef __rtems__
0451     ISR_Level level;
0452     const Per_CPU_Control *cpu_self;
0453 
0454     _ISR_Local_disable(level);
0455     cpu_self = _Per_CPU_Get();
0456     ctx->runner_cpu = cpu_self;
0457 
0458     if (cpu_self->isr_nest_level == 0) {
0459         ctx->runner_thread = _Per_CPU_Get_executing(cpu_self);
0460     } else {
0461         ctx->runner_thread = NULL;
0462     }
0463 
0464     _ISR_Local_enable(level);
0465 #else
0466     ctx->runner_valid = true;
0467     ctx->runner_thread = pthread_self();
0468 #endif
0469 }
0470 
0471 void
0472 T_make_runner(void)
0473 {
0474     T_do_make_runner(&T_instance);
0475 }
0476 
0477 int
0478 T_printf(char const *fmt, ...)
0479 {
0480     va_list ap;
0481     int len;
0482 
0483     va_start(ap, fmt);
0484     len = T_vprintf(fmt, ap);
0485     va_end(ap);
0486 
0487     return len;
0488 }
0489 
0490 void
0491 T_log(T_verbosity verbosity, char const *fmt, ...)
0492 {
0493     T_context *ctx;
0494 
0495     ctx = &T_instance;
0496 
0497     if (ctx->verbosity >= verbosity) {
0498         va_list ap;
0499 
0500         T_printf("L:");
0501         va_start(ap, fmt);
0502         T_vprintf(fmt, ap);
0503         va_end(ap);
0504         T_printf("\n");
0505     }
0506 }
0507 
0508 static unsigned int
0509 T_fetch_add_step(T_context *ctx)
0510 {
0511     return atomic_fetch_add_explicit(&ctx->steps, 1, memory_order_relaxed);
0512 }
0513 
0514 static unsigned int
0515 T_add_failure(T_context *ctx)
0516 {
0517     return atomic_fetch_add_explicit(&ctx->failures, 1,
0518         memory_order_relaxed);
0519 }
0520 
0521 static void
0522 T_actions_forward(const T_config *config, T_event event, const char *name)
0523 {
0524     const T_action *actions;
0525     size_t n;
0526     size_t i;
0527 
0528     actions = config->actions;
0529     n = config->action_count;
0530 
0531     for (i = 0; i < n; ++i) {
0532         (*actions[i])(event, name);
0533     }
0534 }
0535 
0536 static void
0537 T_actions_backward(const T_config *config, T_event event,
0538     const char *name)
0539 {
0540     const T_action *actions;
0541     size_t n;
0542     size_t i;
0543 
0544     actions = config->actions;
0545     n = config->action_count;
0546 
0547     for (i = 0; i < n; ++i) {
0548         (*actions[n - i - 1])(event, name);
0549     }
0550 }
0551 
0552 T_NO_RETURN static void
0553 T_do_stop(T_context *ctx)
0554 {
0555     T_fixture_node *node;
0556 
0557     node = ctx->fixtures;
0558 
0559     while (node != NULL) {
0560         const T_fixture *fixture;
0561         void *node_context;
0562 
0563         fixture = node->fixture;
0564         node_context = node->context;
0565         node = node->next;
0566 
0567         if (fixture != NULL && fixture->stop != NULL) {
0568             (*fixture->stop)(node_context);
0569         }
0570     }
0571 
0572     if (T_do_is_runner(ctx) && ctx->case_begin_context_valid) {
0573         longjmp(ctx->case_begin_context, 1);
0574     } else {
0575         T_actions_backward(ctx->config, T_EVENT_CASE_STOP,
0576             T_case_name());
0577 #ifdef __GNUC__
0578         __builtin_unreachable();
0579 #endif
0580     }
0581 }
0582 
0583 T_NO_RETURN void
0584 T_stop(void)
0585 {
0586     T_do_stop(&T_instance);
0587 }
0588 
0589 void T_plan(unsigned int planned_steps)
0590 {
0591     T_context *ctx;
0592     unsigned int expected;
0593     bool success;
0594 
0595     ctx = &T_instance;
0596     expected = UINT_MAX;
0597     success = atomic_compare_exchange_strong_explicit(&ctx->planned_steps,
0598         &expected, planned_steps, memory_order_relaxed,
0599         memory_order_relaxed);
0600     T_check(&T_special, success, "planned steps (%u) already set",
0601         expected);
0602 }
0603 
0604 const T_fixture T_empty_fixture;
0605 
0606 void
0607 T_push_plan(T_fixture_node *node, unsigned int planned_steps)
0608 {
0609     T_push_fixture(node, &T_empty_fixture);
0610     T_plan(planned_steps);
0611 }
0612 
0613 void
0614 T_pop_plan(void)
0615 {
0616     T_pop_fixture();
0617 }
0618 
0619 void
0620 T_check_step(const T_check_context *t, unsigned int expected)
0621 {
0622     T_check_context tt;
0623 
0624     tt = *t;
0625     tt.flags |= T_CHECK_STEP(expected);
0626     T_check(&tt, true, "actual step is not equal to expected step (%u)",
0627         expected);
0628 }
0629 
0630 void
0631 T_case_register(T_case_context *tc)
0632 {
0633     T_context *ctx;
0634 
0635     ctx = &T_instance;
0636     tc->next = ctx->registered_cases;
0637     ctx->registered_cases = tc;
0638 }
0639 
0640 T_verbosity
0641 T_set_verbosity(T_verbosity verbosity)
0642 {
0643     T_context *ctx;
0644     T_verbosity previous;
0645 
0646     ctx = &T_instance;
0647     previous = ctx->verbosity;
0648     ctx->verbosity = verbosity;
0649 
0650     return previous;
0651 }
0652 
0653 void *
0654 T_fixture_context(void)
0655 {
0656     return T_instance.fixtures->context;
0657 }
0658 
0659 void
0660 T_set_fixture_context(void *context)
0661 {
0662     T_instance.fixtures->context = context;
0663 }
0664 
0665 const char *
0666 T_case_name(void)
0667 {
0668     const T_case_context *tc;
0669 
0670     tc = T_instance.current_case;
0671 
0672     if (tc != NULL) {
0673         return tc->name;
0674     } else {
0675         return "?";
0676     }
0677 }
0678 
0679 static const char *
0680 T_file(const T_check_context *t)
0681 {
0682     const char *file;
0683 
0684     file = strrchr(t->file, '/');
0685 
0686     if (file == NULL) {
0687         return t->file;
0688     }
0689 
0690     return file + 1;
0691 }
0692 
0693 static const char T_planned_step_fmt[] = "planned step (%u)";
0694 
0695 static void
0696 T_check_putc(int c, void *arg)
0697 {
0698     T_putchar_string_context *sctx;
0699     size_t n;
0700 
0701     sctx = arg;
0702     n = sctx->n;
0703 
0704     if (n > 0) {
0705         char *s;
0706 
0707         s = sctx->s;
0708         *s = (char)c;
0709         sctx->s = s + 1;
0710         sctx->n = n - 1;
0711     }
0712 }
0713 
0714 static void
0715 T_check_print_steps(T_context *ctx, T_putchar_string_context *sctx,
0716     unsigned int step)
0717 {
0718     T_fixture_node *node;
0719 
0720     node = &ctx->case_fixture;
0721 
0722     while (true) {
0723         node = node->previous;
0724 
0725         if (node != NULL) {
0726             _IO_Printf(T_check_putc, sctx, "%u.",
0727                 node->next_steps);
0728         } else {
0729             break;
0730         }
0731     }
0732 
0733     if (step != UINT_MAX) {
0734         _IO_Printf(T_check_putc, sctx, "%u", step);
0735     } else {
0736         T_check_putc('*', sctx);
0737     }
0738 }
0739 
0740 void
0741 T_check(const T_check_context *t, bool ok, ...)
0742 {
0743     T_context *ctx;
0744     va_list ap;
0745     char line[T_LINE_SIZE];
0746     unsigned int step;
0747     int line_number;
0748     const char *fmt;
0749 
0750     ctx = &T_instance;
0751 
0752     if ((t->flags & T_CHECK_QUIET) == 0) {
0753         step = T_fetch_add_step(ctx);
0754     } else {
0755         step = UINT_MAX;
0756     }
0757 
0758     va_start(ap, ok);
0759     line[0] = '\0';
0760     line_number = -1;
0761     fmt = NULL;
0762 
0763     if ((t->flags & T_CHECK_STEP_FLAG) != 0 &&
0764          step != T_CHECK_STEP_FROM_FLAGS(t->flags)) {
0765         T_add_failure(ctx);
0766         line[0] = 'F';
0767         line_number = t->line;
0768         fmt = T_planned_step_fmt;
0769     } else if (!ok) {
0770         T_add_failure(ctx);
0771 
0772         if (ctx->verbosity >= T_NORMAL) {
0773             line[0] = 'F';
0774             line_number = t->line;
0775 
0776             if ((t->flags & T_CHECK_FMT) != 0) {
0777                 fmt = va_arg(ap, const char *);
0778             }
0779         }
0780     } else if ((t->flags & T_CHECK_QUIET) == 0 &&
0781         ctx->verbosity >= T_VERBOSE) {
0782         line[0] = 'P';
0783         line_number = t->line;
0784     }
0785 
0786     if (line[0] != '\0') {
0787         T_putchar_string_context sctx;
0788         size_t chunk;
0789 
0790         sctx.n = sizeof(line) - 1;
0791         sctx.s = &line[1];
0792         T_check_putc(':', &sctx);
0793         T_check_print_steps(ctx, &sctx, step);
0794         _IO_Printf(T_check_putc, &sctx, ":%i:", T_cpu());
0795         chunk = T_scope(ctx, sctx.s, sctx.n);
0796         sctx.s += chunk;
0797         sctx.n -= chunk;
0798         T_check_putc(':', &sctx);
0799         chunk = T_str_copy(sctx.s, T_file(t), sctx.n);
0800         sctx.s += chunk;
0801         sctx.n -= chunk;
0802         T_check_putc(':', &sctx);
0803 
0804         if (line_number >= 0) {
0805             _IO_Printf(T_check_putc, &sctx, "%i", line_number);
0806         } else {
0807             T_check_putc('*', &sctx);
0808         }
0809 
0810         if (fmt != NULL) {
0811             if (fmt == T_planned_step_fmt) {
0812                 T_check_putc(':', &sctx);
0813                 _IO_Printf(T_check_putc, &sctx, fmt,
0814                     T_CHECK_STEP_FROM_FLAGS(t->flags));
0815             } else {
0816                 T_check_putc(':', &sctx);
0817                 _IO_Vprintf(T_check_putc, &sctx, fmt, ap);
0818             }
0819         }
0820 
0821         T_check_putc('\n', &sctx);
0822         line[sizeof(line) - 1] = '\n';
0823         T_puts(&line[0], sizeof(line) - sctx.n);
0824     }
0825 
0826     if (!ok && (t->flags & T_CHECK_STOP) != 0) {
0827         T_do_stop(ctx);
0828     }
0829 
0830     va_end(ap);
0831 }
0832 
0833 static void
0834 T_do_log(T_context *ctx, T_verbosity verbosity, char const *fmt, ...)
0835 {
0836     if (ctx->verbosity >= verbosity) {
0837         va_list ap;
0838 
0839         va_start(ap, fmt);
0840         T_vprintf(fmt, ap);
0841         va_end(ap);
0842     }
0843 }
0844 
0845 static void
0846 T_system(T_context *ctx)
0847 {
0848 #if defined(__rtems__)
0849     T_do_log(ctx, T_QUIET, "S:Platform:RTEMS\n");
0850     T_do_log(ctx, T_QUIET, "S:Compiler:" __VERSION__ "\n");
0851     T_do_log(ctx, T_QUIET, "S:Version:%s\n", rtems_version());
0852     T_do_log(ctx, T_QUIET, "S:BSP:%s\n", rtems_board_support_package());
0853     T_do_log(ctx, T_QUIET, "S:BuildLabel:%s\n", rtems_get_build_label());
0854     T_do_log(ctx, T_QUIET, "S:TargetHash:SHA256:%s\n",
0855         rtems_get_target_hash());
0856 #if RTEMS_DEBUG
0857     T_do_log(ctx, T_QUIET, "S:RTEMS_DEBUG:1\n");
0858 #else
0859     T_do_log(ctx, T_QUIET, "S:RTEMS_DEBUG:0\n");
0860 #endif
0861 #if RTEMS_MULTIPROCESSING
0862     T_do_log(ctx, T_QUIET, "S:RTEMS_MULTIPROCESSING:1\n");
0863 #else
0864     T_do_log(ctx, T_QUIET, "S:RTEMS_MULTIPROCESSING:0\n");
0865 #endif
0866 #if RTEMS_POSIX_API
0867     T_do_log(ctx, T_QUIET, "S:RTEMS_POSIX_API:1\n");
0868 #else
0869     T_do_log(ctx, T_QUIET, "S:RTEMS_POSIX_API:0\n");
0870 #endif
0871 #if RTEMS_PROFILING
0872     T_do_log(ctx, T_QUIET, "S:RTEMS_PROFILING:1\n");
0873 #else
0874     T_do_log(ctx, T_QUIET, "S:RTEMS_PROFILING:0\n");
0875 #endif
0876 #if RTEMS_SMP
0877     T_do_log(ctx, T_QUIET, "S:RTEMS_SMP:1\n");
0878 #else
0879     T_do_log(ctx, T_QUIET, "S:RTEMS_SMP:0\n");
0880 #endif
0881 #elif defined(__linux__)
0882     T_do_log(ctx, T_QUIET, "S:Platform:Linux\n");
0883     T_do_log(ctx, T_QUIET, "S:Compiler:" __VERSION__ "\n");
0884 #else
0885     T_do_log(ctx, T_QUIET, "S:Platform:POSIX\n");
0886 #ifdef __VERSION__
0887     T_do_log(ctx, T_QUIET, "S:Compiler:" __VERSION__ "\n");
0888 #endif
0889 #endif
0890 }
0891 
0892 void
0893 T_add_destructor(T_destructor *dtor, void (*destroy)(T_destructor *))
0894 {
0895     T_context *ctx;
0896 
0897     dtor->destroy = destroy;
0898     ctx = &T_instance;
0899     pthread_spin_lock(&ctx->lock);
0900     LIST_INSERT_HEAD(&ctx->destructors, dtor, node);
0901     pthread_spin_unlock(&ctx->lock);
0902 }
0903 
0904 void
0905 T_remove_destructor(T_destructor *dtor)
0906 {
0907     T_context *ctx;
0908 
0909     ctx = &T_instance;
0910     pthread_spin_lock(&ctx->lock);
0911     LIST_REMOVE(dtor, node);
0912     pthread_spin_unlock(&ctx->lock);
0913 }
0914 
0915 static void
0916 T_call_destructors(const T_context *ctx)
0917 {
0918     T_destructor *dtor;
0919 
0920 #ifdef __linux__
0921     while (!LIST_EMPTY(&ctx->destructors)) {
0922         dtor = LIST_FIRST(&ctx->destructors);
0923         LIST_REMOVE(dtor, node);
0924         (*dtor->destroy)(dtor);
0925     }
0926 #else
0927     T_destructor *tmp;
0928 
0929     LIST_FOREACH_SAFE(dtor, &ctx->destructors, node, tmp) {
0930         (*dtor->destroy)(dtor);
0931     }
0932 #endif
0933 }
0934 
0935 static void
0936 T_make_remarks(T_context *ctx)
0937 {
0938     T_remark *remark;
0939 
0940     remark = ctx->remarks.next;
0941 
0942     while (remark != &ctx->remarks) {
0943         T_remark *current;
0944 
0945         current = remark;
0946         remark = current->next;
0947         current->next = NULL;
0948         T_do_log(ctx, T_QUIET, "R:%s\n", current->remark);
0949     }
0950 }
0951 
0952 static T_context *
0953 T_do_run_initialize(const T_config *config)
0954 {
0955     T_context *ctx;
0956 
0957     ctx = &T_instance;
0958 
0959     pthread_spin_init(&ctx->lock, PTHREAD_PROCESS_PRIVATE);
0960 
0961     ctx->config = config;
0962     ctx->buf = config->buf;
0963 
0964     if (config->buf_size > 0 &&
0965         (config->buf_size & (config->buf_size - 1)) == 0) {
0966         ctx->buf_mask = config->buf_size - 1;
0967     } else {
0968         ctx->buf_mask = 0;
0969     }
0970 
0971     ctx->fixtures = &ctx->case_fixture;
0972     atomic_store_explicit(&ctx->buf_head, 0, memory_order_relaxed);
0973     ctx->buf_tail = 0;
0974     ctx->putchar = config->putchar;
0975     ctx->putchar_arg = config->putchar_arg;
0976     ctx->verbosity = config->verbosity;
0977     ctx->overall_cases = 0;
0978     ctx->overall_steps = 0;
0979     ctx->overall_failures = 0;
0980 
0981     T_do_make_runner(ctx);
0982     T_actions_forward(config, T_EVENT_RUN_INITIALIZE_EARLY, config->name);
0983     T_do_log(ctx, T_QUIET, "A:%s\n", config->name);
0984     T_system(ctx);
0985     ctx->run_begin_time = (*config->now)();
0986     T_actions_forward(config, T_EVENT_RUN_INITIALIZE_LATE, config->name);
0987 
0988     return ctx;
0989 }
0990 
0991 static void
0992 T_do_case_begin(T_context *ctx, const T_case_context *tc)
0993 {
0994     const T_config *config;
0995     const T_fixture *fixture;
0996 
0997     config = ctx->config;
0998     fixture = tc->fixture;
0999     ctx->verbosity = config->verbosity;
1000     ctx->current_case = tc;
1001     ctx->fixtures = &ctx->case_fixture;
1002     LIST_INIT(&ctx->destructors);
1003     ctx->remarks.next = &ctx->remarks;
1004     atomic_store_explicit(&ctx->planned_steps, UINT_MAX,
1005         memory_order_relaxed);
1006     atomic_store_explicit(&ctx->steps, 0, memory_order_relaxed);
1007     atomic_store_explicit(&ctx->failures, 0, memory_order_relaxed);
1008     ctx->fixture_steps = 0;
1009 
1010     T_actions_forward(config, T_EVENT_CASE_EARLY, tc->name);
1011     T_do_log(ctx, T_NORMAL, "B:%s\n", tc->name);
1012     ctx->case_begin_time = (*config->now)();
1013     T_actions_forward(config, T_EVENT_CASE_BEGIN, tc->name);
1014 
1015     if (fixture != NULL) {
1016         ctx->case_fixture.fixture = fixture;
1017         ctx->case_fixture.context = fixture->initial_context;
1018 
1019         if (fixture->setup != NULL) {
1020             (*fixture->setup)(ctx->case_fixture.context);
1021         }
1022     }
1023 }
1024 
1025 static void
1026 T_check_steps(unsigned int planned_steps, unsigned int steps,
1027     unsigned int failures)
1028 {
1029     if (planned_steps != UINT_MAX && planned_steps != steps &&
1030         failures == 0) {
1031         T_check(&T_special, false, "actual steps (%u), "
1032             "planned steps (%u)", steps, planned_steps);
1033     }
1034 }
1035 
1036 static void T_do_pop_fixture(T_context *);
1037 
1038 static void
1039 T_do_case_end(T_context *ctx, const T_case_context *tc)
1040 {
1041     const T_config *config;
1042     unsigned int planned_steps;
1043     unsigned int steps;
1044     unsigned int failures;
1045     T_time delta;
1046     T_time_string ts;
1047 
1048     while (ctx->fixtures != NULL) {
1049         T_do_pop_fixture(ctx);
1050     }
1051 
1052     T_call_destructors(ctx);
1053     config = ctx->config;
1054     T_actions_backward(config, T_EVENT_CASE_END, tc->name);
1055     T_make_remarks(ctx);
1056 
1057     planned_steps = atomic_fetch_add_explicit(&ctx->planned_steps,
1058         0, memory_order_relaxed);
1059     steps = atomic_fetch_add_explicit(&ctx->steps, 0,
1060         memory_order_relaxed);
1061     failures = atomic_fetch_add_explicit(&ctx->failures, 0,
1062         memory_order_relaxed);
1063     T_check_steps(planned_steps, steps, failures);
1064 
1065     failures = atomic_load_explicit(&ctx->failures, memory_order_relaxed);
1066     delta = (*config->now)() - ctx->case_begin_time;
1067     steps += ctx->fixture_steps;
1068     T_do_log(ctx, T_QUIET, "E:%s:N:%u:F:%u:D:%s\n",
1069         tc->name, steps, failures, T_time_to_string_us(delta, ts));
1070 
1071     ++ctx->overall_cases;
1072     ctx->overall_steps += steps;
1073     ctx->overall_failures += failures;
1074 
1075     T_actions_backward(config, T_EVENT_CASE_LATE, tc->name);
1076 }
1077 
1078 static void
1079 T_run_case(T_context *ctx, const T_case_context *tc)
1080 {
1081     if (setjmp(ctx->case_begin_context) == 0) {
1082         ctx->case_begin_context_valid = true;
1083         T_do_case_begin(ctx, tc);
1084         (*tc->body)();
1085     }
1086 
1087     ctx->case_begin_context_valid = false;
1088     T_do_case_end(ctx, tc);
1089 }
1090 
1091 static void
1092 T_do_run_all(T_context *ctx)
1093 {
1094     const T_case_context *tc;
1095 
1096     tc = ctx->registered_cases;
1097 
1098     while (tc != NULL) {
1099         T_run_case(ctx, tc);
1100         tc = tc->next;
1101     }
1102 }
1103 
1104 static bool
1105 T_do_run_finalize(T_context *ctx)
1106 {
1107     const T_config *config;
1108     T_time delta;
1109     T_time_string ts;
1110 
1111     config = ctx->config;
1112     T_actions_backward(config, T_EVENT_RUN_FINALIZE_EARLY, config->name);
1113     delta = (*config->now)() - ctx->run_begin_time;
1114     T_do_log(ctx, T_QUIET, "Z:%s:C:%u:N:%u:F:%u:D:%s\n", config->name,
1115         ctx->overall_cases, ctx->overall_steps, ctx->overall_failures,
1116         T_time_to_string_us(delta, ts));
1117     T_actions_backward(config, T_EVENT_RUN_FINALIZE_LATE, config->name);
1118 #ifdef __rtems__
1119     ctx->runner_thread = NULL;
1120     ctx->runner_cpu = NULL;
1121 #else
1122     ctx->runner_valid = false;
1123 #endif
1124     pthread_spin_destroy(&ctx->lock);
1125 
1126     return ctx->overall_failures == 0;
1127 }
1128 
1129 int
1130 T_main(const T_config *config)
1131 {
1132     T_context *ctx;
1133 
1134     ctx = T_do_run_initialize(config);
1135     T_do_run_all(ctx);
1136 
1137     return T_do_run_finalize(ctx) ? 0 : 1;
1138 }
1139 
1140 #ifdef __rtems__
1141 RTEMS_LINKER_ROSET(_T, T_case_context *);
1142 #endif /* __rtems__ */
1143 
1144 void T_register(void)
1145 {
1146 #ifdef __rtems__
1147     T_case_context * const *tc;
1148 
1149     RTEMS_LINKER_SET_FOREACH(_T, tc) {
1150         T_case_register(*tc);
1151     }
1152 #endif /* __rtems__ */
1153 }
1154 
1155 void
1156 T_run_initialize(const T_config *config)
1157 {
1158     (void)T_do_run_initialize(config);
1159 }
1160 
1161 void
1162 T_run_all(void)
1163 {
1164     T_do_run_all(&T_instance);
1165 }
1166 
1167 void
1168 T_run_by_name(const char *name)
1169 {
1170     T_context *ctx;
1171     const T_case_context *tc;
1172 
1173     ctx = &T_instance;
1174     tc = ctx->registered_cases;
1175 
1176     while (tc != NULL) {
1177         if (strcmp(tc->name, name) == 0) {
1178             T_run_case(ctx, tc);
1179             break;
1180         }
1181 
1182         tc = tc->next;
1183     }
1184 }
1185 
1186 static T_case_context default_case;
1187 
1188 void *
1189 T_case_begin(const char *name, const T_fixture *fixture)
1190 {
1191     T_case_context *tc;
1192 
1193     tc = &default_case;
1194     tc->name = name;
1195     tc->fixture = fixture;
1196     T_do_case_begin(&T_instance, tc);
1197     return T_instance.case_fixture.context;
1198 }
1199 
1200 void
1201 T_case_end(void)
1202 {
1203     T_case_context *tc;
1204 
1205     tc = &default_case;
1206     T_do_case_end(&T_instance, tc);
1207 }
1208 
1209 bool
1210 T_run_finalize(void)
1211 {
1212     return T_do_run_finalize(&T_instance);
1213 }
1214 
1215 T_time
1216 T_case_begin_time(void)
1217 {
1218     return T_instance.case_begin_time;
1219 }
1220 
1221 void
1222 T_set_putchar(T_putchar new_putchar, void *new_arg, T_putchar *old_putchar,
1223     void **old_arg)
1224 {
1225     T_context *ctx;
1226 
1227     ctx = &T_instance;
1228     *old_putchar = ctx->putchar;
1229     *old_arg = ctx->putchar_arg;
1230     ctx->putchar = new_putchar;
1231     ctx->putchar_arg = new_arg;
1232 }
1233 
1234 T_time
1235 T_now(void)
1236 {
1237     T_context *ctx;
1238     const T_config *config;
1239 
1240     ctx = &T_instance;
1241     config = ctx->config;
1242     return (*config->now)();
1243 }
1244 
1245 void *
1246 T_push_fixture(T_fixture_node *node, const T_fixture *fixture)
1247 {
1248     T_context *ctx;
1249     T_fixture_node *old;
1250     void *context;
1251 
1252     ctx = &T_instance;
1253     old = ctx->fixtures;
1254     old->previous = node;
1255     node->next = old;
1256     node->previous = NULL;
1257     node->fixture = fixture;
1258     context = fixture->initial_context;
1259     node->context = context;
1260     node->next_planned_steps = atomic_exchange_explicit(
1261         &ctx->planned_steps, UINT_MAX, memory_order_relaxed);
1262     node->next_steps = atomic_exchange_explicit(&ctx->steps, 0,
1263         memory_order_relaxed);
1264     node->failures = atomic_fetch_add_explicit(&ctx->failures, 0,
1265         memory_order_relaxed);
1266     ctx->fixtures = node;
1267 
1268     if (fixture->setup != NULL) {
1269         (*fixture->setup)(context);
1270     }
1271 
1272     return context;
1273 }
1274 
1275 static void
1276 T_do_pop_fixture(T_context *ctx)
1277 {
1278     T_fixture_node *node;
1279     const T_fixture *fixture;
1280     T_fixture_node *next;
1281 
1282     node = ctx->fixtures;
1283     next = node->next;
1284     ctx->fixtures = next;
1285     fixture = node->fixture;
1286 
1287     if (fixture != NULL && fixture->teardown != NULL) {
1288         (*fixture->teardown)(node->context);
1289     }
1290 
1291     if (next != NULL) {
1292         unsigned int planned_steps;
1293         unsigned int steps;
1294         unsigned int failures;
1295 
1296         next->previous = NULL;
1297         planned_steps = atomic_exchange_explicit(&ctx->planned_steps,
1298             node->next_planned_steps, memory_order_relaxed);
1299         steps = atomic_exchange_explicit(&ctx->steps, node->next_steps,
1300             memory_order_relaxed);
1301         failures = atomic_fetch_add_explicit(&ctx->failures, 0,
1302             memory_order_relaxed);
1303         ctx->fixture_steps += steps;
1304         T_check_steps(planned_steps, steps, node->failures - failures);
1305     }
1306 
1307     memset(node, 0, sizeof(*node));
1308 }
1309 
1310 void
1311 T_pop_fixture(void)
1312 {
1313     T_do_pop_fixture(&T_instance);
1314 }
1315 
1316 void
1317 T_add_remark(T_remark *remark)
1318 {
1319     if (remark->next == NULL) {
1320         T_context *ctx;
1321 
1322         ctx = &T_instance;
1323         remark->next = ctx->remarks.next;
1324         ctx->remarks.next = remark;
1325     }
1326 }
1327 
1328 size_t
1329 T_get_scope(const char * const * const *desc, char *buf, size_t n,
1330     const size_t *second_indices)
1331 {
1332     size_t c;
1333     size_t i;
1334 
1335     c = n;
1336     i = 0;
1337 
1338     while (true) {
1339         const char * const *desc2;
1340         size_t m;
1341 
1342         desc2 = desc[i];
1343 
1344         if (desc2 == NULL) {
1345             break;
1346         }
1347 
1348         if (c > 1) {
1349             buf[0] = '/';
1350             --c;
1351             ++buf;
1352         } else {
1353             break;
1354         }
1355 
1356         m = T_str_copy(buf, desc2[second_indices[i]], c);
1357         buf += m;
1358         c -= m;
1359         ++i;
1360     }
1361 
1362     return n - c;
1363 }
1364 
1365 #ifdef __BIGGEST_ALIGNMENT__
1366 #define T_BIGGEST_ALIGNMENT __BIGGEST_ALIGNMENT__
1367 #else
1368 #define T_BIGGEST_ALIGNMENT sizeof(long long)
1369 #endif
1370 
1371 typedef struct __attribute__((__aligned__(T_BIGGEST_ALIGNMENT))) {
1372     T_destructor base;
1373     void (*destroy)(void *);
1374 } T_malloc_destructor;
1375 
1376 static void
1377 T_malloc_destroy(T_destructor *base)
1378 {
1379     T_malloc_destructor *dtor;
1380 
1381     dtor = (T_malloc_destructor *)(uintptr_t)base;
1382 
1383     if (dtor->destroy != NULL) {
1384         (*dtor->destroy)(dtor + 1);
1385     }
1386 
1387     (*T_instance.config->deallocate)(dtor);
1388 }
1389 
1390 static void *
1391 T_do_malloc(size_t size, void (*destroy)(void *))
1392 {
1393     T_malloc_destructor *dtor;
1394     size_t new_size;
1395 
1396     new_size = sizeof(*dtor) + size;
1397     if (new_size <= size) {
1398         return NULL;
1399     }
1400 
1401     if (T_instance.config->allocate == NULL) {
1402         return NULL;
1403     }
1404 
1405     dtor = (*T_instance.config->allocate)(new_size);
1406     if (dtor != NULL) {
1407         dtor->destroy = destroy;
1408         T_add_destructor(&dtor->base, T_malloc_destroy);
1409         ++dtor;
1410     }
1411 
1412     return dtor;
1413 }
1414 
1415 void *
1416 T_malloc(size_t size)
1417 {
1418     return T_do_malloc(size, NULL);
1419 }
1420 
1421 void *
1422 T_calloc(size_t nelem, size_t elsize)
1423 {
1424     return T_zalloc(nelem * elsize, NULL);
1425 }
1426 
1427 void *
1428 T_zalloc(size_t size, void (*destroy)(void *))
1429 {
1430     void *p;
1431 
1432     p = T_do_malloc(size, destroy);
1433     if (p != NULL) {
1434         p = memset(p, 0, size);
1435     }
1436 
1437     return p;
1438 }
1439 
1440 void
1441 T_free(void *ptr)
1442 {
1443     T_malloc_destructor *dtor;
1444 
1445     dtor = ptr;
1446     --dtor;
1447     T_remove_destructor(&dtor->base);
1448     (*T_instance.config->deallocate)(dtor);
1449 }