File indexing completed on 2025-05-11 08:24:21
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
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
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
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
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
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 }