The ROme OpTimistic Simulator  3.0.0
A General-Purpose Multithreaded Parallel/Distributed Simulation Platform
test.c
Go to the documentation of this file.
1 
11 #include <test.h>
12 
13 #include <arch/thread.h>
14 
15 #include <limits.h>
16 #include <memory.h>
17 #include <stdarg.h>
18 #include <stdatomic.h>
19 #include <stdbool.h>
20 #include <stdlib.h>
21 
22 #ifndef ROOTSIM_TEST_NAME
23 #define ROOTSIM_TEST_NAME "rs_test"
24 #endif
25 
26 static char *t_out_buf;
27 static size_t t_out_buf_size;
28 static size_t t_out_wrote;
29 
30 static char **test_argv;
31 
32 __attribute__((weak)) lp_id_t n_lps;
33 #ifdef ROOTSIM_MPI
34 __attribute__((weak)) nid_t nid;
35 __attribute__((weak)) nid_t n_nodes = 1;
36 #endif
37 __attribute__((weak)) rid_t n_threads;
38 __attribute__((weak)) __thread rid_t rid;
39 
40 __attribute__((weak)) int log_level;
41 
42 __attribute__((weak))
43 void _log_log(int level, const char *file, unsigned line, const char *fmt, ...)
44 {
45  (void) level;
46  (void) file;
47  (void) line;
48  (void) fmt;
49 }
50 
51 int main(int argc, char **argv);
52 
59 static int init_arguments(int *argc_p, char ***argv_p)
60 {
61  int argc = 0;
63  while (test_config.test_arguments[argc]) {
64  ++argc;
65  }
66  }
67  ++argc;
68 
69  char **argv = malloc(sizeof(*argv) * (argc + 1));
70  if(argv == NULL)
71  return -1;
72 
73  argv[0] = ROOTSIM_TEST_NAME;
74 
76  memcpy(&argv[1], test_config.test_arguments,
77  sizeof(*argv) * argc);
78  } else {
79  argv[1] = NULL;
80  }
81 
82  *argc_p = argc;
83  *argv_p = argv;
84  return 0;
85 }
86 
90 static void test_atexit(void)
91 {
92  free(test_argv);
93  free(t_out_buf);
94 }
95 
99 __attribute__((constructor))
100 void main_wrapper(void)
101 {
102  int test_argc = 0;
103 
104  puts("Starting " ROOTSIM_TEST_NAME " test");
105 
107 
108  atexit(test_atexit);
109 
110  if (init_arguments(&test_argc, &test_argv) == -1)
112 
113  t_out_buf_size = 1;
114  t_out_buf = malloc(t_out_buf_size);
115  if (t_out_buf == NULL)
117 
118  int test_ret = main(test_argc, test_argv);
119  if (test_ret)
120  exit(test_ret);
121 
122  if (t_out_wrote < test_config.expected_output_size) {
123  puts("Test failed: output is shorter than the expected one");
124  exit(-1);
125  }
126 
127  puts("Successfully run " ROOTSIM_TEST_NAME " test");
128  exit(0);
129 }
130 
131 static int test_printf_internal(const char *restrict fmt, va_list args)
132 {
133  va_list args_cpy;
134  va_copy(args_cpy, args);
135  size_t p_size = vsnprintf(t_out_buf, t_out_buf_size, fmt, args_cpy);
136  va_end(args_cpy);
137 
138  if (t_out_wrote + p_size > test_config.expected_output_size) {
139  puts("Test failed: output is longer than the expected one");
140  exit(-1);
141  }
142 
143  if (p_size >= t_out_buf_size) {
144  do {
145  t_out_buf_size *= 2;
146  } while(p_size >= t_out_buf_size);
147 
148  free(t_out_buf);
149  t_out_buf = malloc(t_out_buf_size);
150  if (t_out_buf == NULL)
152 
153  vsnprintf(t_out_buf, t_out_buf_size, fmt, args);
154  }
155 
156  if (memcmp(t_out_buf, test_config.expected_output + t_out_wrote, p_size)) {
157  printf("Test failed: output is different from the expected one %zu\n", t_out_wrote);
158  exit(-1);
159  }
160  t_out_wrote += p_size;
161 
162  return p_size;
163 }
164 
169 int test_printf(const char *restrict fmt, ...)
170 {
171  va_list args;
172  va_start(args, fmt);
173  int ret = test_printf_internal(fmt, args);
174  va_end(args);
175  return ret;
176 }
177 
184 int test_printf_pr(const char *restrict fmt, ...)
185 {
186  va_list args;
187  va_start(args, fmt);
188  int ret = test_printf_internal(fmt, args);
189  va_end(args);
190  return ret;
191 }
192 
201 {
202  static atomic_uint b_in, b_out, b_cr;
203 
204  unsigned i;
205  unsigned count = test_config.threads_count;
206  unsigned max_in_before_reset = (UINT_MAX / 2) - (UINT_MAX / 2) % count;
207  do {
208  i = atomic_fetch_add_explicit(
209  &b_in, 1U, memory_order_acq_rel) + 1;
210  } while (__builtin_expect(i > max_in_before_reset, 0));
211 
212  unsigned cr = atomic_load_explicit(&b_cr, memory_order_relaxed);
213 
214  bool leader = i == cr + count;
215  if (leader) {
216  atomic_store_explicit(&b_cr, cr + count, memory_order_release);
217  } else {
218  while (i > cr) {
219  cr = atomic_load_explicit(&b_cr, memory_order_relaxed);
220  }
221  }
222  atomic_thread_fence(memory_order_acquire);
223 
224  unsigned o = atomic_fetch_add_explicit(&b_out, 1, memory_order_release) + 1;
225  if (__builtin_expect(o == max_in_before_reset, 0)) {
226  atomic_thread_fence(memory_order_acquire);
227  atomic_store_explicit(&b_cr, 0, memory_order_relaxed);
228  atomic_store_explicit(&b_out, 0, memory_order_relaxed);
229  atomic_store_explicit(&b_in, 0, memory_order_release);
230  }
231  return leader;
232 }
TEST_BAD_FAIL_EXIT_CODE
#define TEST_BAD_FAIL_EXIT_CODE
The exit code of tests when something fails horribly.
Definition: test.h:19
main_wrapper
void main_wrapper(void)
The test wrapper which allows to intervene before the actual main()
Definition: test.c:100
rid_t
unsigned rid_t
Used to identify in a node the computing resources (threads at the moment)
Definition: core.h:77
n_lps
lp_id_t n_lps
The total number of LPs in the simulation.
Definition: test.c:32
nid_t
int nid_t
Used to identify MPI nodes in a distributed environment.
Definition: core.h:79
test_printf_pr
int test_printf_pr(const char *restrict fmt,...)
Registers a formatted string to compare against the expected output.
Definition: test.c:184
_log_log
void _log_log(int level, const char *file, unsigned line, const char *fmt,...)
Logs a message. For internal use: log_log() should be used instead.
Definition: test.c:43
test_config
A complete test configuration.
Definition: test.h:22
main
int main(int argc, char **argv)
The main entry point of the custom compiler.
Definition: compiler.c:62
test_atexit
static void test_atexit(void)
The exit handler, to exit cleanly even in case of errors.
Definition: test.c:90
test_thread_barrier
bool test_thread_barrier(void)
Synchronizes threads on a barrier.
Definition: test.c:200
test.h
Test framework header.
test_config::expected_output
const char * expected_output
The expected output from the whole sequence of test_printf() calls.
Definition: test.h:35
rid
__thread rid_t rid
The identifier of the thread.
Definition: test.c:38
nid
nid_t nid
The node identifier of the node.
Definition: test.c:34
test_printf
int test_printf(const char *restrict fmt,...)
Registers a formatted string to compare against the expected output.
Definition: test.c:169
n_threads
rid_t n_threads
The total number of MPI nodes in the simulation.
Definition: test.c:37
test_config::expected_output_size
size_t expected_output_size
The expected output size of the full sequence of test_printf() calls.
Definition: test.h:37
lp_id_t
uint64_t lp_id_t
Used to uniquely identify LPs in the simulation.
Definition: core.h:75
test_config::test_arguments
const char ** test_arguments
The command line arguments passed to the wrapped main function.
Definition: test.h:39
test_config::threads_count
unsigned threads_count
test_fnc is executed with that many cores
Definition: test.h:33
thread.h
Generic architecture management facilities.
log_level
int log_level
The minimum log level of the messages to display.
Definition: test.c:40
init_arguments
static int init_arguments(int *argc_p, char ***argv_p)
Initializes ISO C compliant argc and argv from the test configuration.
Definition: test.c:59