From dc5248d49c14e682da99e43daae6adaed3766bb7 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Mon, 8 Aug 2016 15:59:13 +0200 Subject: [PATCH] boxfort: Initial integration --- CMakeLists.txt | 17 +- samples/parameterized.c | 2 +- samples/parameterized.cc | 3 +- src/CMakeLists.txt | 4 +- src/common.h | 7 +- src/compat/alloc.c | 129 ++++----- src/compat/alloc.h | 9 +- src/compat/kill.c | 2 - src/compat/process.c | 502 ------------------------------------ src/compat/process.h | 38 --- src/core/client.c | 29 ++- src/core/client.h | 5 +- src/core/report.c | 2 - src/core/report.h | 6 +- src/core/runner.c | 216 ++++------------ src/core/runner.h | 3 - src/core/runner_coroutine.c | 242 ++++++++++++----- src/core/runner_coroutine.h | 9 +- src/core/test.c | 19 +- src/core/worker.c | 103 -------- src/core/worker.h | 84 ------ src/entry/params.c | 4 +- src/err.c | 39 +++ src/err.h | 62 +++++ src/io/event.h | 4 - src/protocol/connect.c | 44 +--- src/protocol/connect.h | 6 +- src/protocol/messages.c | 3 - src/protocol/protocol.h | 1 + 29 files changed, 454 insertions(+), 1140 deletions(-) delete mode 100644 src/core/worker.c delete mode 100644 src/core/worker.h create mode 100644 src/err.c create mode 100644 src/err.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 018f29a..8c7c5f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,9 +29,9 @@ cr_add_subproject (csptr PATH dependencies/libcsptr cr_add_subproject (dyncall_s PATH dependencies/dyncall CMAKE IF THEORIES) if (NOT WIN32) - set (NN_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") + set (PIC_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") else () - set (NN_C_FLAGS "${CMAKE_C_FLAGS}") + set (PIC_C_FLAGS "${CMAKE_C_FLAGS}") endif () cr_add_subproject (nanomsg @@ -41,10 +41,20 @@ cr_add_subproject (nanomsg -DNN_TOOLS=OFF -DNN_STATIC_LIB=ON -DCMAKE_INSTALL_LIBDIR=lib - "-DCMAKE_C_FLAGS=${NN_C_FLAGS}" + "-DCMAKE_C_FLAGS=${PIC_C_FLAGS}" CMAKE ) +cr_add_subproject (boxfort PATH dependencies/boxfort + OPTS + -DBXF_TESTS=OFF + -DBXF_SAMPLES=OFF + -DBXF_STATIC_LIB=ON + -DBXF_FORK_RESILIENCE=OFF + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + "-DCMAKE_C_FLAGS=${PIC_C_FLAGS}" + CMAKE) + cr_add_subproject (wingetopt PATH dependencies/wingetopt CMAKE IF MSVC) include (Properties) @@ -90,6 +100,7 @@ cr_link_subproject(criterion csptr STATIC) cr_link_subproject(criterion nanomsg STATIC) cr_link_subproject(criterion dyncall_s STATIC) cr_link_subproject(criterion wingetopt STATIC) +cr_link_subproject(criterion boxfort STATIC) cr_link_libraries(criterion pthread IF NOT WIN32) cr_link_libraries(criterion rt IF HAVE_CLOCK_GETTIME) diff --git a/samples/parameterized.c b/samples/parameterized.c index 2bf1341..57ad2ee 100644 --- a/samples/parameterized.c +++ b/samples/parameterized.c @@ -40,7 +40,7 @@ ParameterizedTest(struct parameter_tuple *tup, params, multiple) { // you **MUST** use cr_malloc, cr_free, cr_realloc, and cr_calloc instead of their // unprefixed counterparts to allocate dynamic memory in parameters, otherwise -// this will crash on Windows builds of the test. +// this will crash. struct parameter_tuple_dyn { int i; diff --git a/samples/parameterized.cc b/samples/parameterized.cc index e6aaff9..61a4640 100644 --- a/samples/parameterized.cc +++ b/samples/parameterized.cc @@ -39,8 +39,7 @@ ParameterizedTest(struct parameter_tuple *tup, params, multiple) { // you **MUST** use new_obj, new_arr, delete_obj, delete_arr instead of // the new, new[], delete and delete[] operators (respectively) to allocate and -// deallocate dynamic memory in parameters, otherwise this will crash on -// Windows builds of the test. +// deallocate dynamic memory in parameters, otherwise this will crash. // the criterion::allocator allocator may be used with STL containers to // allocate objects with the functions described above. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 28361d1..0e79501 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,8 +14,6 @@ set(SOURCE_FILES src/core/runner_coroutine.c src/core/runner_coroutine.h src/core/coroutine.h - src/core/worker.c - src/core/worker.h src/core/stats.c src/core/stats.h src/core/ordered-set.c @@ -60,6 +58,8 @@ set(SOURCE_FILES src/entry/options.c src/entry/params.c src/entry/entry.c + src/err.c + src/err.h src/protocol/criterion.pb.c src/protocol/criterion.pb.h src/protocol/protocol.c diff --git a/src/common.h b/src/common.h index eac2dfa..56b57e3 100644 --- a/src/common.h +++ b/src/common.h @@ -27,6 +27,8 @@ # include # include +# include "criterion/internal/common.h" + #ifdef __GNUC__ # define INLINE __attribute__((always_inline)) inline #elif defined(_MSC_VER) @@ -37,9 +39,4 @@ # define DEF(X, Y) ((X) ? (X) : (Y)) -# define cr_panic(...) do { \ - fprintf(stderr, __VA_ARGS__); \ - abort(); \ - } while (0) - #endif /* !COMMON_H_ */ diff --git a/src/compat/alloc.c b/src/compat/alloc.c index e2737df..30621ef 100644 --- a/src/compat/alloc.c +++ b/src/compat/alloc.c @@ -21,97 +21,64 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "alloc.h" -#include "internal.h" -#include "log/logging.h" #include +#include -#ifdef VANILLA_WIN32 -HANDLE g_heap; +#include "alloc.h" +#include "err.h" -struct garbage_heap { - HANDLE handle; - struct garbage_heap *next; -}; +static bxf_arena inheritable_arena; -# define HEAP_MIN_BASE 0x08000000 +void cri_alloc_init(void) +{ + int rc = bxf_arena_init(0, BXF_ARENA_RESIZE | BXF_ARENA_IDENTITY + | BXF_ARENA_KEEPMAPPED, &inheritable_arena); -void init_inheritable_heap(void) { - struct garbage_heap *heaps = NULL; + if (rc < 0) + cr_panic("Could not initialize inheritable arena: %s", strerror(-rc)); +} - while ((void*)(g_heap = HeapCreate(0, 0, 0)) < (void*)HEAP_MIN_BASE) { - if (g_heap == NULL) - break; +void cri_alloc_term(void) +{ + bxf_arena_term(&inheritable_arena); +} - struct garbage_heap *heap = malloc(sizeof(struct garbage_heap)); - heap->handle = g_heap; - heap->next = heaps; - heaps = heap; +bxf_arena cri_alloc_getarena(void) +{ + return inheritable_arena; +} + +void *cr_malloc(size_t size) +{ + bxf_ptr ptr = bxf_arena_alloc(&inheritable_arena, size); + if (ptr < 0) { + errno = -ptr; + return NULL; } - for (struct garbage_heap *h = heaps; h != NULL; h = h->next) - HeapDestroy(h->handle); + return bxf_arena_ptr(inheritable_arena, ptr); +} - if (g_heap == (HANDLE) NULL) { - criterion_perror("Could not create the private inheritable heap.\n"); - abort(); +void *cr_calloc(size_t nmemb, size_t size) +{ + void *ptr = cr_malloc(size * nmemb); + if (ptr) + memset(ptr, 0, size * nmemb); + return ptr; +} + +void *cr_realloc(void *ptr, size_t size) +{ + bxf_ptr p = (intptr_t)ptr - (intptr_t)inheritable_arena; + bxf_ptr newptr = bxf_arena_realloc(&inheritable_arena, p, size); + if (newptr < 0) { + errno = -newptr; + return NULL; } + return bxf_arena_ptr(inheritable_arena, newptr); } -int inherit_heap(HANDLE child_process) { - PROCESS_HEAP_ENTRY entry = { .lpData = NULL }; - - while (HeapWalk(g_heap, &entry)) { - if (!(entry.wFlags & PROCESS_HEAP_REGION)) - continue; - - DWORD region_size = entry.Region.dwCommittedSize; - - if (!VirtualAllocEx(child_process, - entry.lpData, - region_size, - MEM_RESERVE | MEM_COMMIT, - PAGE_READWRITE)) - return -1; - - if (!WriteProcessMemory(child_process, - entry.lpData, - entry.lpData, - region_size, - NULL)) - return -1; - } - return 0; -} -#endif - -void *cr_malloc(size_t size) { -#ifdef VANILLA_WIN32 - return HeapAlloc(g_heap, 0, size); -#else - return malloc(size); -#endif -} - -void *cr_calloc(size_t nmemb, size_t size) { -#ifdef VANILLA_WIN32 - return HeapAlloc(g_heap, HEAP_ZERO_MEMORY, nmemb * size); -#else - return calloc(nmemb, size); -#endif -} - -void *cr_realloc(void *ptr, size_t size) { -#ifdef VANILLA_WIN32 - return HeapReAlloc(g_heap, 0, ptr, size); -#else - return realloc(ptr, size); -#endif -} - -void cr_free(void *ptr) { -#ifdef VANILLA_WIN32 - HeapFree(g_heap, 0, ptr); -#else - free(ptr); -#endif +void cr_free(void *ptr) +{ + bxf_ptr p = (intptr_t)ptr - (intptr_t)inheritable_arena; + (void) bxf_arena_free(&inheritable_arena, p); } diff --git a/src/compat/alloc.h b/src/compat/alloc.h index 27b73eb..61f9988 100644 --- a/src/compat/alloc.h +++ b/src/compat/alloc.h @@ -24,12 +24,11 @@ #ifndef COMPAT_ALLOC_H_ # define COMPAT_ALLOC_H_ +# include # include "criterion/alloc.h" -# include "posix.h" -# ifdef VANILLA_WIN32 -void init_inheritable_heap(void); -int inherit_heap(HANDLE child_process); -# endif +void cri_alloc_init(void); +void cri_alloc_term(void); +bxf_arena cri_alloc_getarena(void); #endif /* !COMPAT_ALLOC_H_ */ diff --git a/src/compat/kill.c b/src/compat/kill.c index 0719ce0..b440682 100644 --- a/src/compat/kill.c +++ b/src/compat/kill.c @@ -33,8 +33,6 @@ #include #include -static INLINE void nothing(void) {} - #ifdef VANILLA_WIN32 static HANDLE cr_job; #else diff --git a/src/compat/process.c b/src/compat/process.c index 94a8d90..1ca22dc 100644 --- a/src/compat/process.c +++ b/src/compat/process.c @@ -21,494 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include -#include -#include -#include -#include "core/worker.h" -#include "core/runner.h" -#include "protocol/protocol.h" -#include "protocol/messages.h" -#include "io/event.h" #include "process.h" #include "internal.h" -#include "pipe-internal.h" -#include "alloc.h" - -#include - -#ifdef VANILLA_WIN32 -# include -# define CREATE_SUSPENDED_(Filename, CmdLine, StartupInfo, Info) \ - CreateProcessW(Filename, \ - CmdLine, \ - NULL, \ - NULL, \ - TRUE, \ - CREATE_SUSPENDED, \ - NULL, \ - NULL, \ - &(StartupInfo), \ - &(Info)) - -# define WRITE_PROCESS_(Proc, What, Size) \ - WriteProcessMemory(Proc, &What, &What, Size, NULL); - -# ifndef STATUS_BAD_STACK -# define STATUS_BAD_STACK 0xC0000028L -# endif - -/* - * NTSTATUS specification, from ntstatus.h: - * - * > Values are 32 bit values laid out as follows: - * > - * > 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 - * > 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 - * > +---+-+-+-----------------------+-------------------------------+ - * > |Sev|C|R| Facility | Code | - * > +---+-+-+-----------------------+-------------------------------+ - * > - * > where - * > - * > Sev - is the severity code - * > - * > 00 - Success - * > 01 - Informational - * > 10 - Warning - * > 11 - Error - * > - * > C - is the Customer code flag - * > - * > R - is a reserved bit - * > - * > Facility - is the facility code - * > - * > Code - is the facility's status code - * - * We consider that all exit codes with error severity bits that cannot - * be directly translated to translate to SIGSYS. - * - */ -static int get_win_status(HANDLE handle) { - DWORD exit_code; - GetExitCodeProcess(handle, &exit_code); - unsigned int sig = 0; - switch (exit_code) { - case STATUS_FLOAT_DENORMAL_OPERAND: - case STATUS_FLOAT_DIVIDE_BY_ZERO: - case STATUS_FLOAT_INEXACT_RESULT: - case STATUS_FLOAT_INVALID_OPERATION: - case STATUS_FLOAT_OVERFLOW: - case STATUS_FLOAT_STACK_CHECK: - case STATUS_FLOAT_UNDERFLOW: - case STATUS_INTEGER_DIVIDE_BY_ZERO: - case STATUS_INTEGER_OVERFLOW: sig = SIGFPE; break; - - case STATUS_ILLEGAL_INSTRUCTION: - case STATUS_PRIVILEGED_INSTRUCTION: - case STATUS_NONCONTINUABLE_EXCEPTION: sig = SIGILL; break; - - case CR_EXCEPTION_TIMEOUT: sig = SIGPROF; break; - - case STATUS_ACCESS_VIOLATION: - case STATUS_DATATYPE_MISALIGNMENT: - case STATUS_ARRAY_BOUNDS_EXCEEDED: - case STATUS_GUARD_PAGE_VIOLATION: - case STATUS_IN_PAGE_ERROR: - case STATUS_NO_MEMORY: - case STATUS_INVALID_DISPOSITION: - case STATUS_BAD_STACK: - case STATUS_STACK_OVERFLOW: sig = SIGSEGV; break; - - case STATUS_CONTROL_C_EXIT: sig = SIGINT; break; - - default: break; - } - if (sig == 0 && exit_code & 0xC0000000) - sig = SIGSYS; - return sig ? sig : exit_code << 8; -} -#else -# include -#endif - -struct worker_context g_worker_context = {.test = NULL}; - -#ifdef VANILLA_WIN32 -unsigned long long g_ppid = 0; - -struct full_context { - struct criterion_test test; - struct criterion_test_extra_data test_data; - struct criterion_suite suite; - struct criterion_test_extra_data suite_data; - cr_worker_func func; - unsigned long long ppid; - struct test_single_param param; - HANDLE sync; - DWORD extra_size; -}; - -static TCHAR g_mapping_name[] = TEXT("WinCriterionWorker_%lu"); - -static struct full_context local_ctx; - -static CRITICAL_SECTION wait_sync; -static CONDITION_VARIABLE wait_cond; -static LONG volatile wait_threads; -#endif - -#if defined(__unix__) || defined(__APPLE__) -# ifndef __GNUC__ -# error Unsupported compiler. Use GCC or Clang under *nixes. -# endif - -static void handle_sigchld_pump(void) { - pid_t pid; - int status; - while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { - int result = WIFEXITED(status) - ? criterion_protocol_death_result_type_NORMAL - : criterion_protocol_death_result_type_CRASH; - int code = WIFEXITED(status) - ? WEXITSTATUS(status) - : WTERMSIG(status); - - criterion_protocol_msg msg = criterion_message(death, - .result = result, - .has_status = true, - .status = code, - ); - - msg.id.pid = pid; - cr_send_to_runner(&msg); - } -} - -/* - * This child reaping logic is a dirty hack to prevent deadlocks - * when the pipe is overflowing and a child terminates. - * - * (Windows doesn't have this issue as the waitpid logic is threaded by - * RegisterWaitForSingleObject) - * - * REMOVE WHEN REFACTORING I/O LAYER - */ -static pthread_t child_pump; -static volatile bool child_pump_running; - -static void *chld_pump_thread_main(void *nil) { - handle_sigchld_pump(); - while (child_pump_running) { - usleep(1000); - handle_sigchld_pump(); - } - return nil; -} -#endif - -void init_proc_compat(void) { -#ifndef VANILLA_WIN32 - child_pump_running = true; - int err = pthread_create(&child_pump, NULL, chld_pump_thread_main, NULL); - if (err) { - perror(0); - exit(1); - } -#else - g_ppid = get_process_id(); - - InitializeConditionVariable(&wait_cond); - InitializeCriticalSection(&wait_sync); - wait_threads = 0; -#endif -} - -void reset_proc_compat(void) { -#ifndef VANILLA_WIN32 - child_pump_running = false; - pthread_join(child_pump, NULL); -#else - EnterCriticalSection(&wait_sync); - while (wait_threads > 0) - SleepConditionVariableCS(&wait_cond, &wait_sync, INFINITE); - LeaveCriticalSection(&wait_sync); - - DeleteCriticalSection(&wait_sync); -#endif -} - -void free_proc_compat(void) { -#ifndef VANILLA_WIN32 - if (child_pump_running) { - reset_proc_compat(); - } -#endif -} - -#ifdef VANILLA_WIN32 -struct wait_context { - HANDLE wait_handle; - HANDLE proc_handle; -}; - -static void CALLBACK handle_child_terminated(PVOID lpParameter, - CR_UNUSED BOOLEAN TimerOrWaitFired) { - - assert(!TimerOrWaitFired); - - struct wait_context *wctx = lpParameter; - - int status = get_win_status(wctx->proc_handle); - - int64_t pid = (int64_t) GetProcessId(wctx->proc_handle); - - int result = WIFEXITED(status) - ? criterion_protocol_death_result_type_NORMAL - : criterion_protocol_death_result_type_CRASH; - int code = WIFEXITED(status) - ? WEXITSTATUS(status) - : WTERMSIG(status); - - criterion_protocol_msg msg = criterion_message(death, - .result = result, - .has_status = true, - .status = code, - ); - - msg.id.pid = pid; - cr_send_to_runner(&msg); - - if (InterlockedDecrement(&wait_threads) == 0) - WakeConditionVariable(&wait_cond); - - HANDLE whandle = wctx->wait_handle; - free(lpParameter); - UnregisterWaitEx(whandle, NULL); -} -#endif - -int resume_child(void) { -#ifdef VANILLA_WIN32 - TCHAR mapping_name[128]; - _sntprintf(mapping_name, 128, g_mapping_name, GetCurrentProcessId()); - - HANDLE sharedMem = OpenFileMapping( - FILE_MAP_ALL_ACCESS, - FALSE, - mapping_name); - - if (sharedMem == NULL) { - init_inheritable_heap(); - return 0; - } - - struct full_context *ctx = (struct full_context *) MapViewOfFile(sharedMem, - FILE_MAP_ALL_ACCESS, - 0, - 0, - sizeof (struct full_context)); - - if (ctx == NULL) { - CloseHandle(sharedMem); - exit(-1); - } - - local_ctx = *ctx; - UnmapViewOfFile(ctx); - - struct test_single_param *param = NULL; - if (local_ctx.param.size != 0) { - ctx = (struct full_context*) MapViewOfFile(sharedMem, - FILE_MAP_ALL_ACCESS, - 0, - 0, - sizeof (struct full_context) + local_ctx.extra_size); - - if (ctx == NULL) { - CloseHandle(sharedMem); - exit(-1); - } - - param = malloc(sizeof (struct test_single_param) + local_ctx.param.size); - *param = (struct test_single_param) { - .size = local_ctx.param.size, - .ptr = param + 1, - }; - memcpy(param + 1, ctx + 1, param->size); - UnmapViewOfFile(ctx); - } - - CloseHandle(sharedMem); - - g_worker_context = (struct worker_context) { - .test = &local_ctx.test, - .suite = &local_ctx.suite, - .func = local_ctx.func, - .param = param, - }; - - local_ctx.test.data = &local_ctx.test_data; - local_ctx.suite.data = &local_ctx.suite_data; - - g_ppid = local_ctx.ppid; - - SetEvent(local_ctx.sync); - - SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); - - run_worker(&g_worker_context); - free(param); - return 1; -#else - return 0; -#endif -} - -s_proc_handle *fork_process() { -#ifdef VANILLA_WIN32 - PROCESS_INFORMATION info; - STARTUPINFOW si = { .cb = sizeof (STARTUPINFOW) }; - - ZeroMemory(&info, sizeof (info)); - - SECURITY_ATTRIBUTES inherit_handle = { - .nLength = sizeof (SECURITY_ATTRIBUTES), - .bInheritHandle = TRUE - }; - - // Create the synchronization event - HANDLE sync = CreateEvent(&inherit_handle, FALSE, FALSE, NULL); - if (sync == NULL) - return (void *) -1; - - // Create the suspended child process - wchar_t filename[MAX_PATH]; - GetModuleFileNameW(NULL, filename, MAX_PATH); - - if (!CREATE_SUSPENDED_(filename, GetCommandLineW(), si, info)) - return (void *) -1; - - // Copy context over - TCHAR mapping_name[128]; - _sntprintf(mapping_name, 128, g_mapping_name, info.dwProcessId); - - DWORD mapping_size = sizeof (struct full_context); - if (g_worker_context.param) - mapping_size += g_worker_context.param->size; - - HANDLE sharedMem = CreateFileMapping( - INVALID_HANDLE_VALUE, - NULL, - PAGE_READWRITE, - 0, - mapping_size, - mapping_name); - - if (sharedMem == NULL || GetLastError() == ERROR_ALREADY_EXISTS) - return (void *) -1; - - struct full_context *ctx = (struct full_context *) MapViewOfFile(sharedMem, - FILE_MAP_ALL_ACCESS, - 0, - 0, - mapping_size); - - if (ctx == NULL) { - CloseHandle(sharedMem); - return (void *) -1; - } - - *ctx = (struct full_context) { - .test = *g_worker_context.test, - .test_data = *g_worker_context.test->data, - .suite = *g_worker_context.suite, - .func = g_worker_context.func, - .sync = sync, - .ppid = get_process_id(), - }; - - if (g_worker_context.param) { - ctx->extra_size = g_worker_context.param->size; - ctx->param = *g_worker_context.param; - memcpy(ctx + 1, g_worker_context.param->ptr, g_worker_context.param->size); - } - - if (g_worker_context.suite->data) - ctx->suite_data = *g_worker_context.suite->data; - - inherit_heap(info.hProcess); - - if (ResumeThread(info.hThread) == (DWORD) -1) - goto failure; - - // wait until the child has initialized itself - HANDLE handles[] = { info.hProcess, sync }; - DWORD wres = WaitForMultipleObjects(sizeof (handles) / sizeof (HANDLE), handles, FALSE, INFINITE); - if (wres == WAIT_OBJECT_0) - goto failure; - - CloseHandle(info.hThread); - - UnmapViewOfFile(ctx); - CloseHandle(sharedMem); - - struct wait_context *wctx = malloc(sizeof (struct wait_context)); - *wctx = (struct wait_context) { - .proc_handle = info.hProcess, - }; - - InterlockedIncrement (&wait_threads); - RegisterWaitForSingleObject( - &wctx->wait_handle, - info.hProcess, - handle_child_terminated, - wctx, - INFINITE, - WT_EXECUTELONGFUNCTION | WT_EXECUTEONLYONCE); - - s_proc_handle *handle = smalloc(sizeof (s_proc_handle)); - *handle = (s_proc_handle) { info.hProcess }; - return handle; - -failure: - UnmapViewOfFile(ctx); - CloseHandle(sharedMem); - CloseHandle(info.hThread); - CloseHandle(info.hProcess); - return (void *) -1; - -#else - pid_t pid = fork(); - if (pid == -1) - return (void *) -1; - if (pid == 0) - return NULL; - - s_proc_handle *handle = smalloc(sizeof (s_proc_handle)); - *handle = (s_proc_handle) { pid }; - return handle; -#endif -} - -s_proc_handle *get_current_process() { - s_proc_handle *handle = smalloc(sizeof (s_proc_handle)); -#ifdef VANILLA_WIN32 - *handle = (s_proc_handle) { GetCurrentProcess() }; -#else - *handle = (s_proc_handle) { getpid() }; -#endif - return handle; -} - -bool is_current_process(s_proc_handle *proc) { -#ifdef VANILLA_WIN32 - return GetProcessId(proc->handle) == GetProcessId(GetCurrentProcess()); -#else - return proc->pid == getpid(); -#endif -} unsigned long long get_process_id(void) { #ifdef VANILLA_WIN32 @@ -517,19 +31,3 @@ unsigned long long get_process_id(void) { return (unsigned long long) getpid(); #endif } - -unsigned long long get_runner_process_id(void) { -#ifdef VANILLA_WIN32 - return g_ppid; -#else - return is_runner() ? get_process_id() : (unsigned long long) getppid(); -#endif -} - -unsigned long long get_process_id_of(s_proc_handle *proc) { -#ifdef VANILLA_WIN32 - return (unsigned long long) GetProcessId(proc->handle); -#else - return (unsigned long long) proc->pid; -#endif -} diff --git a/src/compat/process.h b/src/compat/process.h index cd01b26..dad97c6 100644 --- a/src/compat/process.h +++ b/src/compat/process.h @@ -24,44 +24,6 @@ #ifndef COMPAT_PROCESS_H_ # define COMPAT_PROCESS_H_ -# include "criterion/types.h" -# include "internal.h" - -typedef void (*cr_worker_func)(struct criterion_test *, struct criterion_suite *); - -struct proc_handle { -#ifdef VANILLA_WIN32 - HANDLE handle; -#else - pid_t pid; -#endif -}; - -typedef struct proc_handle s_proc_handle; - -struct worker_context { - struct criterion_test *test; - struct criterion_suite *suite; - cr_worker_func func; - struct test_single_param *param; -}; - -extern struct worker_context g_worker_context; - -int resume_child(void); - -s_proc_handle *fork_process(); -void wait_process(s_proc_handle *handle, int *status); - -s_proc_handle *get_current_process(); -bool is_current_process(s_proc_handle *proc); - unsigned long long get_process_id(void); -unsigned long long get_runner_process_id(void); -unsigned long long get_process_id_of(s_proc_handle *proc); - -void init_proc_compat(void); -void free_proc_compat(void); -void reset_proc_compat(void); #endif /* !COMPAT_PROCESS_H_ */ diff --git a/src/core/client.c b/src/core/client.c index 78f4b63..74011f6 100644 --- a/src/core/client.c +++ b/src/core/client.c @@ -30,11 +30,10 @@ #include "criterion/options.h" #include "log/logging.h" #include "io/event.h" +#include "client.h" +#include "err.h" #include "report.h" #include "stats.h" -#include "client.h" - -static void nothing(void) {}; KHASH_MAP_INIT_INT(ht_client, struct client_ctx) KHASH_MAP_INIT_STR(ht_extern, struct client_ctx) @@ -100,19 +99,27 @@ void init_server_context(struct server_ctx *sctx, struct criterion_global_stats } void destroy_client_context(struct client_ctx *ctx) { - if (ctx->kind == WORKER) - sfree(ctx->worker); + if (ctx->kind == WORKER) { + int rc = bxf_wait(ctx->instance, BXF_FOREVER); + if (rc < 0) + cr_panic("waiting for the worker failed: %s\n", strerror(-rc)); + rc = bxf_term(ctx->instance); + if (rc < 0) + cr_panic("finalizing the worker failed: %s\n", strerror(-rc)); + } } void destroy_server_context(struct server_ctx *sctx) { khint_t k; - (void) k; struct client_ctx v; - (void) v; kh_foreach(sctx->subprocesses, k, v, { destroy_client_context(&v); }); + + (void) k; + (void) v; + kh_destroy(ht_client, sctx->subprocesses); kh_destroy(ht_extern, sctx->clients); @@ -120,11 +127,13 @@ void destroy_server_context(struct server_ctx *sctx) { sfree(sctx->extern_sstats); } -struct client_ctx *add_client_from_worker(struct server_ctx *sctx, struct client_ctx *ctx, struct worker *w) { - unsigned long long pid = get_process_id_of(w->proc); +struct client_ctx *add_client_from_worker(struct server_ctx *sctx, + struct client_ctx *ctx, bxf_instance *instance) +{ + unsigned long long pid = instance->pid; int absent; khint_t k = kh_put(ht_client, sctx->subprocesses, pid, &absent); - ctx->worker = w; + ctx->instance = instance; ctx->kind = WORKER; kh_value(sctx->subprocesses, k) = *ctx; return &kh_value(sctx->subprocesses, k); diff --git a/src/core/client.h b/src/core/client.h index 947815b..9c647af 100644 --- a/src/core/client.h +++ b/src/core/client.h @@ -25,6 +25,7 @@ # define CLIENT_H_ # include +# include // order matters here enum client_state { @@ -49,7 +50,7 @@ enum client_kind { struct client_ctx { enum client_kind kind; - struct worker *worker; + bxf_instance *instance; struct criterion_test_extra_data extern_test_data; struct criterion_test extern_test; @@ -80,7 +81,7 @@ struct client_ctx *process_client_message(struct server_ctx *ctx, const criterio void init_server_context(struct server_ctx *sctx, struct criterion_global_stats *gstats); void destroy_server_context(struct server_ctx *sctx); -struct client_ctx *add_client_from_worker(struct server_ctx *sctx, struct client_ctx *ctx, struct worker *w); +struct client_ctx *add_client_from_worker(struct server_ctx *sctx, struct client_ctx *ctx, bxf_instance *instance); void remove_client_by_pid(struct server_ctx *sctx, int pid); #endif /* !CLIENT_H_ */ diff --git a/src/core/report.c b/src/core/report.c index 6bbdf0e..fc4bdba 100644 --- a/src/core/report.c +++ b/src/core/report.c @@ -33,8 +33,6 @@ #include "config.h" #include "compat/posix.h" -static inline void nothing() {} - #define IMPL_CALL_REPORT_HOOKS(Kind) \ CR_IMPL_SECTION_LIMITS(f_report_hook, CR_HOOK_SECTION(Kind)); \ void call_report_hooks_##Kind(void *data) { \ diff --git a/src/core/report.h b/src/core/report.h index a5fcea4..68fa19a 100644 --- a/src/core/report.h +++ b/src/core/report.h @@ -46,9 +46,11 @@ DECL_CALL_REPORT_HOOKS(POST_FINI); DECL_CALL_REPORT_HOOKS(POST_SUITE); DECL_CALL_REPORT_HOOKS(POST_ALL); +static inline void nothing() {} + #define log(Type, ...) \ - log_(criterion_options.logger->log_ ## Type, __VA_ARGS__); + log_(criterion_options.logger->log_ ## Type, __VA_ARGS__) #define log_(Log, ...) \ - (Log ? Log(__VA_ARGS__) : nothing()); + (Log ? Log(__VA_ARGS__) : nothing()) #endif /* !REPORT_H_ */ diff --git a/src/core/runner.c b/src/core/runner.c index b8c1d1b..2511c02 100644 --- a/src/core/runner.c +++ b/src/core/runner.c @@ -36,41 +36,33 @@ #include "protocol/protocol.h" #include "protocol/connect.h" #include "protocol/messages.h" +#include "compat/alloc.h" #include "compat/time.h" #include "compat/posix.h" #include "compat/processor.h" #include "compat/kill.h" +#include "string/extglobmatch.h" #include "string/i18n.h" #include "io/event.h" #include "io/output.h" #include "log/logging.h" +#include "abort.h" +#include "client.h" +#include "common.h" +#include "config.h" +#include "err.h" +#include "report.h" +#include "runner.h" #include "runner_coroutine.h" #include "stats.h" -#include "runner.h" -#include "report.h" -#include "worker.h" -#include "abort.h" -#include "config.h" -#include "common.h" -#include "client.h" - -#include "string/extglobmatch.h" typedef const char *const msg_t; #ifdef ENABLE_NLS -static msg_t msg_valgrind_early_exit = N_("%1$sWarning! Criterion has detected " - "that it is running under valgrind, but the no_early_exit option is " - "explicitely disabled. Reports will not be accurate!%2$s\n"); - static msg_t msg_valgrind_jobs = N_("%1$sWarning! Criterion has detected " "that it is running under valgrind, but the number of jobs have been " "explicitely set. Reports might appear confusing!%2$s\n"); #else -static msg_t msg_valgrind_early_exit = "%sWarning! Criterion has detected " - "that it is running under valgrind, but the no_early_exit option is " - "explicitely disabled. Reports will not be accurate!%s\n"; - static msg_t msg_valgrind_jobs = "%sWarning! Criterion has detected " "that it is running under valgrind, but the number of jobs have been " "explicitely set. Reports might appear confusing!%s\n"; @@ -91,30 +83,33 @@ CR_IMPL_SECTION_LIMITS(struct criterion_suite*, cr_sts); CR_SECTION_("cr_sts") struct criterion_suite *dummy_suite = NULL; CR_SECTION_("cr_tst") struct criterion_test *dummy_test = NULL; -static INLINE void nothing(void) {} - -int cmp_suite(void *a, void *b) { +static int cmp_suite(void *a, void *b) +{ struct criterion_suite *s1 = a, *s2 = b; return strcmp(s1->name, s2->name); } -int cmp_test(void *a, void *b) { +static int cmp_test(void *a, void *b) +{ struct criterion_test *s1 = a, *s2 = b; return strcmp(s1->name, s2->name); } -static void dtor_suite_set(void *ptr, CR_UNUSED void *meta) { +static void dtor_suite_set(void *ptr, CR_UNUSED void *meta) +{ struct criterion_suite_set *s = ptr; sfree(s->tests); } -static void dtor_test_set(void *ptr, CR_UNUSED void *meta) { +static void dtor_test_set(void *ptr, CR_UNUSED void *meta) +{ struct criterion_test_set *t = ptr; sfree(t->suites); } CR_API void criterion_register_test(struct criterion_test_set *set, - struct criterion_test *test) { + struct criterion_test *test) +{ struct criterion_suite_set css = { .suite = { .name = test->category }, @@ -163,54 +158,6 @@ struct criterion_test_set *criterion_init(void) { return set; } -CR_API const struct criterion_test *criterion_current_test; -CR_API const struct criterion_suite *criterion_current_suite; - -void run_test_child(struct criterion_test *test, - struct criterion_suite *suite) { - if (!is_single_mode()) - reset_proc_compat(); - - cr_redirect_stdin(); - g_client_socket = connect_client(); - if (g_client_socket < 0) { - criterion_perror("Could not initialize the message client: %s.\n", - strerror(errno)); - abort(); - } - - // Notify the runner that the test was born - criterion_protocol_msg msg = criterion_message(birth, .name = (char *) test->name); - criterion_message_set_id(msg); - cr_send_to_runner(&msg); - -#ifndef ENABLE_VALGRIND_ERRORS - VALGRIND_ENABLE_ERROR_REPORTING; -#endif - - criterion_current_test = test; - criterion_current_suite = suite; - - if (suite->data && suite->data->timeout != 0 && test->data->timeout == 0) - setup_timeout((uint64_t) (suite->data->timeout * 1e9)); - else if (test->data->timeout != 0) - setup_timeout((uint64_t) (test->data->timeout * 1e9)); - - if (test->test) - test->test(); - -#ifndef ENABLE_VALGRIND_ERRORS - VALGRIND_DISABLE_ERROR_REPORTING; -#endif - - close_socket(g_client_socket); - - fflush(NULL); // flush all opened streams - if (criterion_options.no_early_exit) - return; - _Exit(0); -} - #define push_event(...) \ do { \ stat_push_event(ctx->stats, \ @@ -223,8 +170,6 @@ void run_test_child(struct criterion_test *test, report(CR_VA_HEAD(__VA_ARGS__), ctx->test_stats); \ } while (0) -s_pipe_handle *g_worker_pipe; - void disable_unmatching(struct criterion_test_set *set) { if (!compile_pattern(criterion_options.pattern)) { exit(3); @@ -254,9 +199,6 @@ CR_API struct criterion_test_set *criterion_initialize(void) { criterion_options.jobs = 1; } - if (resume_child()) // (windows only) resume from the fork - exit(0); - criterion_register_output_provider("tap", tap_report); criterion_register_output_provider("xml", xml_report); criterion_register_output_provider("json", json_report); @@ -277,26 +219,20 @@ CR_API void criterion_finalize(struct criterion_test_set *set) { } static struct client_ctx *spawn_next_client(struct server_ctx *sctx, ccrContext *ctx) { - struct worker *w = ctx ? run_next_test(NULL, NULL, ctx) : NULL; + struct client_ctx new_ctx; - if (!is_runner() || w == NULL) + bxf_instance *instance = cri_run_next_test(NULL, NULL, NULL, &new_ctx, ctx); + if (!instance) return NULL; - struct client_ctx new_ctx = (struct client_ctx) { - .test = w->ctx.test, - .tstats = w->ctx.test_stats, - .suite = w->ctx.suite, - .sstats = w->ctx.suite_stats, - .gstats = w->ctx.stats, - }; - - return add_client_from_worker(sctx, &new_ctx, w); + return add_client_from_worker(sctx, &new_ctx, instance); } static void run_tests_async(struct criterion_test_set *set, struct criterion_global_stats *stats, - int socket) { - + const char *url, + int socket) +{ ccrContext ctx = 0; size_t nb_workers = DEF(criterion_options.jobs, get_processor_count()); @@ -309,12 +245,10 @@ static void run_tests_async(struct criterion_test_set *set, sctx.socket = socket; // initialization of coroutine - run_next_test(set, stats, &ctx); + cri_run_next_test(set, stats, url, NULL, &ctx); for (size_t i = 0; i < nb_workers; ++i) { struct client_ctx *cctx = spawn_next_client(&sctx, &ctx); - if (!is_runner()) - goto cleanup; if (!cctx) break; @@ -338,11 +272,9 @@ static void run_tests_async(struct criterion_test_set *set, } if (cctx->kind == WORKER) { - remove_client_by_pid(&sctx, get_process_id_of(cctx->worker->proc)); + remove_client_by_pid(&sctx, cctx->instance->pid); cctx = spawn_next_client(&sctx, &ctx); - if (!is_runner()) - goto cleanup; if (cctx == NULL) --active_workers; @@ -362,105 +294,51 @@ cleanup: ccrAbort(ctx); } -static int criterion_run_all_tests_impl(struct criterion_test_set *set) { +static int criterion_run_all_tests_impl(struct criterion_test_set *set) +{ report(PRE_ALL, set); log(pre_all, set); if (RUNNING_ON_VALGRIND) { - if (!criterion_options.no_early_exit) - criterion_pimportant(CRITERION_PREFIX_DASHES, - _(msg_valgrind_early_exit), CR_FG_BOLD, CR_RESET); if (criterion_options.jobs != 1) criterion_pimportant(CRITERION_PREFIX_DASHES, _(msg_valgrind_jobs), CR_FG_BOLD, CR_RESET); } - fflush(NULL); // flush everything before forking + char url[sizeof ("ipc://criterion_.sock") + 21]; + snprintf(url, sizeof (url), "ipc://criterion_%llu.sock", get_process_id()); - int sock = bind_server(); - if (sock < 0) { - criterion_perror("Could not initialize the message server: %s.\n", - strerror(errno)); - abort(); - } + int sock = cri_proto_bind(url); + if (sock < 0) + cr_panic("Could not initialize the message server: %s.", strerror(errno)); - init_proc_compat(); + g_client_socket = cri_proto_connect(url); + if (g_client_socket < 0) + cr_panic("Could not initialize the message client: %s.", strerror(errno)); - g_client_socket = connect_client(); - if (g_client_socket < 0) { - criterion_perror("Could not initialize the message client: %s.\n", - strerror(errno)); - abort(); - } + cri_alloc_init(); struct criterion_global_stats *stats = stats_init(); - run_tests_async(set, stats, sock); - - int result = is_runner() ? stats->tests_failed == 0 : -1; - - if (!is_runner()) - goto cleanup; + run_tests_async(set, stats, url, sock); report(POST_ALL, stats); process_all_output(stats); log(post_all, stats); -cleanup: - free_proc_compat(); - if (is_runner()) { - close_socket (g_client_socket); - close_socket (sock); - } + cri_alloc_term(); + + cri_proto_close(g_client_socket); + cri_proto_close(sock); sfree(stats); - return result; + return stats->tests_failed == 0; } -CR_API int criterion_run_all_tests(struct criterion_test_set *set) { +CR_API int criterion_run_all_tests(struct criterion_test_set *set) +{ if (criterion_options.pattern) { disable_unmatching(set); } - set_runner_process(); int res = criterion_run_all_tests_impl(set); - unset_runner_process(); - - if (res == -1) { - criterion_finalize(set); - exit(0); - } - return criterion_options.always_succeed || res; } - -void run_single_test_by_name(const char *testname) { - struct criterion_test_set *set = criterion_init(); - - struct criterion_test *test = NULL; - struct criterion_suite *suite = NULL; - - FOREACH_SET(struct criterion_suite_set *s, set->suites) { - size_t tests = s->tests ? s->tests->size : 0; - if (!tests) - continue; - - FOREACH_SET(struct criterion_test *t, s->tests) { - char name[1024]; - snprintf(name, sizeof (name), "%s::%s", s->suite.name, t->name); - if (!strncmp(name, testname, 1024)) { - test = t; - suite = &s->suite; - break; - } - } - } - - if (test) { - is_extern_worker = true; - criterion_current_test = test; - criterion_current_suite = suite; - - run_test_child(test, suite); - } - - sfree(set); -} diff --git a/src/core/runner.h b/src/core/runner.h index bae1aab..fca8a70 100644 --- a/src/core/runner.h +++ b/src/core/runner.h @@ -31,7 +31,6 @@ CR_DECL_SECTION_LIMITS(struct criterion_test*, cr_tst); CR_DECL_SECTION_LIMITS(struct criterion_suite*, cr_sts); struct criterion_test_set *criterion_init(void); -void run_test_child(struct criterion_test *test, struct criterion_suite *suite); # define FOREACH_TEST_SEC(Test) \ for (struct criterion_test **Test = GET_SECTION_START(cr_tst); \ @@ -43,6 +42,4 @@ void run_test_child(struct criterion_test *test, struct criterion_suite *suite); Suite < (struct criterion_suite**) GET_SECTION_END(cr_sts); \ ++Suite) -void run_single_test_by_name(const char *testname); - #endif /* !CRITERION_RUNNER_H_ */ diff --git a/src/core/runner_coroutine.c b/src/core/runner_coroutine.c index ec7054f..212b5a6 100644 --- a/src/core/runner_coroutine.c +++ b/src/core/runner_coroutine.c @@ -24,15 +24,25 @@ #include #include #include -#include "criterion/internal/parameterized.h" -#include "log/logging.h" -#include "runner_coroutine.h" -#include "worker.h" -#include "stats.h" -#include "runner.h" -#include "report.h" +#include -static INLINE void nothing(void) {} +#include "criterion/internal/parameterized.h" +#include "criterion/redirect.h" +#include "compat/alloc.h" +#include "compat/time.h" +#include "compat/posix.h" +#include "compat/processor.h" +#include "compat/kill.h" +#include "log/logging.h" +#include "protocol/protocol.h" +#include "protocol/connect.h" +#include "protocol/messages.h" +#include "client.h" +#include "err.h" +#include "report.h" +#include "runner.h" +#include "runner_coroutine.h" +#include "stats.h" ccrBeginDefineContextType(run_next_context); @@ -47,47 +57,159 @@ ccrBeginDefineContextType(run_next_context); struct criterion_ordered_set_node *ns; struct criterion_ordered_set_node *nt; size_t i; + const char *url; ccrEndDefineContextType; -static struct worker *run_test(struct criterion_global_stats *stats, - struct criterion_suite_stats *suite_stats, - struct criterion_test_stats *test_stats, - struct test_single_param *param) { +CR_API const struct criterion_test *criterion_current_test; +CR_API const struct criterion_suite *criterion_current_suite; - struct execution_context ctx = { - .stats = sref(stats), - .test = test_stats->test, - .test_stats = sref(test_stats), - .suite = suite_stats->suite, - .suite_stats = sref(suite_stats), - .param = param, +static int run_test_child(void) +{ + +#ifndef ENABLE_VALGRIND_ERRORS + VALGRIND_DISABLE_ERROR_REPORTING; +#endif + + struct criterion_test *test; + struct criterion_suite *suite; + const char *url; + + bxf_context ctx = bxf_context_current(); + + int rc; + + rc = bxf_context_getobject(ctx, "criterion.test", (void **)&test); + if (rc > 0) + rc = bxf_context_getobject(ctx, "criterion.suite", (void **)&suite); + if (rc > 0) + rc = bxf_context_getobject(ctx, "criterion.url", (void **)&url); + + if (rc < 0) + cr_panic("Could not get the test information: %s", strerror(-rc)); + else if (!rc) + cr_panic("Could not initialize test context: property not found"); + + cr_redirect_stdin(); + g_client_socket = cri_proto_connect(url); + + if (g_client_socket < 0) + cr_panic("could not initialize the message client: %s", strerror(errno)); + + // Notify the runner that the test was born + criterion_protocol_msg msg = criterion_message(birth, .name = (char *) test->name); + criterion_message_set_id(msg); + cr_send_to_runner(&msg); + +#ifndef ENABLE_VALGRIND_ERRORS + VALGRIND_ENABLE_ERROR_REPORTING; +#endif + + criterion_current_test = test; + criterion_current_suite = suite; + + if (test->test) + test->test(); + +#ifndef ENABLE_VALGRIND_ERRORS + VALGRIND_DISABLE_ERROR_REPORTING; +#endif + + cri_proto_close(g_client_socket); + return 0; +} + +static void death_callback(bxf_instance *instance) +{ + int result = instance->status.signal + ? criterion_protocol_death_result_type_CRASH + : criterion_protocol_death_result_type_NORMAL; + int code = instance->status.signal + ? instance->status.signal + : instance->status.exit; + + criterion_protocol_msg msg = criterion_message(death, + .result = result, + .has_status = true, + .status = code, + ); + + msg.id.pid = instance->pid; + cr_send_to_runner(&msg); +} + +static bxf_instance *run_test(struct run_next_context *ctx, + struct client_ctx *client) +{ + bxf_context inst_ctx; + int rc = bxf_context_init(&inst_ctx); + + if (!rc) + rc = bxf_context_addobject(inst_ctx, "criterion.test", + ctx->test, sizeof (*ctx->test)); + if (!rc) + rc = bxf_context_addobject(inst_ctx, "criterion.suite", + &ctx->suite_set->suite, sizeof (ctx->suite_set->suite)); + + if (!rc) { + size_t len = strlen(ctx->url) + 1; + rc = bxf_context_addobject(inst_ctx, "criterion.url", ctx->url, len); + } + + if (!rc && ctx->params.params) { + void *param = (char *) ctx->params.params + ctx->i * ctx->params.size; + rc = bxf_context_addobject(inst_ctx, "criterion.param", + param, ctx->params.size); + } + + if (!rc) + rc = bxf_context_addarena(inst_ctx, cri_alloc_getarena()); + + if (rc < 0) + cr_panic("Could not initialize test context: %s", strerror(-rc)); + + struct bxf_spawn_params sp = { + .fn = run_test_child, + .callback = death_callback, + .inherit.context = inst_ctx, }; - return spawn_test_worker(&ctx, run_test_child); + +#if 0 + sp.debug.debugger = BXF_DBG_NATIVE; + sp.debug.tcp = 1234; +#endif + + if (ctx->suite_set->suite.data && ctx->suite_set->suite.data->timeout != 0) + sp.quotas.runtime = ctx->suite_set->suite.data->timeout; + if (ctx->test->data->timeout != 0) + sp.iquotas.runtime = ctx->test->data->timeout; + + bxf_instance *instance; + rc = bxf_spawn_struct(&instance, &sp); + if (rc < 0) + cr_panic("Could not spawn test instance: %s", strerror(-rc)); + + *client = (struct client_ctx) { + .test = ctx->test, + .suite = &ctx->suite_set->suite, + .gstats = ctx->stats, + .sstats = sref(ctx->suite_stats), + .tstats = test_stats_init(ctx->test), + }; + + return instance; } static INLINE bool is_disabled(struct criterion_test *t, - struct criterion_suite *s) { + struct criterion_suite *s) +{ return t->data->disabled || (s->data && s->data->disabled); } -static struct worker *cleanup_and_return_worker(struct run_next_context *ctx, - struct worker *worker) { - - sfree(ctx->test_stats); - if (!is_runner()) { - worker = NULL; - sfree(ctx->suite_stats); - if (ctx->test->data->kind_ == CR_TEST_PARAMETERIZED - && ctx->params.cleanup) - ctx->params.cleanup(&ctx->params); - } - return worker; -} - -static int skip_disabled(struct run_next_context *ctx) { +static int skip_disabled(struct run_next_context *ctx) +{ if (is_disabled(ctx->test, ctx->suite_stats->suite)) { ctx->test_stats = test_stats_init(ctx->test); stat_push_event(ctx->stats, @@ -100,11 +222,12 @@ static int skip_disabled(struct run_next_context *ctx) { return 0; } -struct worker *run_next_test(struct criterion_test_set *p_set, - struct criterion_global_stats *p_stats, - ccrContParam) { - struct worker *worker = NULL; - +bxf_instance *cri_run_next_test(struct criterion_test_set *p_set, + struct criterion_global_stats *p_stats, + const char *url, + struct client_ctx *client, + ccrContParam) +{ ccrUseNamedContext(run_next_context, ctx); ccrBegin(ctx); @@ -112,6 +235,8 @@ struct worker *run_next_test(struct criterion_test_set *p_set, do { ctx->set = p_set; ctx->stats = p_stats; + ctx->url = url; + memset(&ctx->params, 0, sizeof (ctx->params)); ccrReturn(NULL); } while (ctx->set == NULL && ctx->stats == NULL); @@ -131,42 +256,21 @@ struct worker *run_next_test(struct criterion_test_set *p_set, for (ctx->nt = ctx->suite_set->tests->first; ctx->nt; ctx->nt = ctx->nt->next) { ctx->test = (void*) (ctx->nt + 1); + if (skip_disabled(ctx)) + continue; + if (ctx->test->data->kind_ == CR_TEST_PARAMETERIZED && ctx->test->data->param_) { - if (skip_disabled(ctx)) - continue; - ctx->params = ctx->test->data->param_(); - for (ctx->i = 0; ctx->i < ctx->params.length; ++ctx->i) { - ctx->test_stats = test_stats_init(ctx->test); - - - worker = run_test(ctx->stats, - ctx->suite_stats, - ctx->test_stats, - &(struct test_single_param) { - ctx->params.size, - (char *) ctx->params.params + ctx->i * ctx->params.size - }); - - ccrReturn(cleanup_and_return_worker(ctx, worker)); - } + for (ctx->i = 0; ctx->i < ctx->params.length; ++ctx->i) + ccrReturn(run_test(ctx, client)); if (ctx->params.cleanup) ctx->params.cleanup(&ctx->params); + ctx->params.params = NULL; } else { - if (skip_disabled(ctx)) - continue; - - ctx->test_stats = test_stats_init(ctx->test); - - worker = run_test(ctx->stats, - ctx->suite_stats, - ctx->test_stats, - NULL); - - ccrReturn(cleanup_and_return_worker(ctx, worker)); + ccrReturn(run_test(ctx, client)); } } diff --git a/src/core/runner_coroutine.h b/src/core/runner_coroutine.h index 0cec861..cb27c91 100644 --- a/src/core/runner_coroutine.h +++ b/src/core/runner_coroutine.h @@ -24,10 +24,13 @@ #ifndef RUNNER_COROUTINE_H_ # define RUNNER_COROUTINE_H_ +# include # include "coroutine.h" -struct worker *run_next_test(struct criterion_test_set *p_set, - struct criterion_global_stats *p_stats, - ccrContParam); +bxf_instance *cri_run_next_test(struct criterion_test_set *p_set, + struct criterion_global_stats *p_stats, + const char *url, + struct client_ctx *client, + ccrContParam); #endif /* !RUNNER_COROUTINE_H_ */ diff --git a/src/core/test.c b/src/core/test.c index 44c8443..4ef334a 100644 --- a/src/core/test.c +++ b/src/core/test.c @@ -21,10 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include #include "criterion/internal/test.h" #include "core/abort.h" #include "core/stats.h" -#include "core/worker.h" #include "core/report.h" #include "compat/time.h" #include "protocol/protocol.h" @@ -43,8 +43,6 @@ static void send_event(int phase) { cr_send_to_runner(&msg); } -static INLINE void nothing(void) {} - void criterion_internal_test_setup(void) { const struct criterion_suite *suite = criterion_current_suite; const struct criterion_test *test = criterion_current_test; @@ -55,6 +53,19 @@ void criterion_internal_test_setup(void) { (test->data->init ? test->data->init : nothing)(); } +static void *getparam(void) +{ + void *param; + + bxf_context ctx = bxf_context_current(); + int rc = bxf_context_getobject(ctx, "criterion.param", (void **)¶m); + if (rc < 0) { + cr_log_error("Could not retrieve test parameter -- aborting."); + abort(); + } + return param; +} + void criterion_internal_test_main(void (*fn)(void)) { const struct criterion_test *test = criterion_current_test; @@ -67,7 +78,7 @@ void criterion_internal_test_main(void (*fn)(void)) { fn(); } else { void(*param_test_func)(void *) = (void(*)(void*)) fn; - param_test_func(g_worker_context.param->ptr); + param_test_func(getparam()); } } diff --git a/src/core/worker.c b/src/core/worker.c deleted file mode 100644 index b92002b..0000000 --- a/src/core/worker.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright © 2015-2016 Franklin "Snaipe" Mathieu - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include -#include -#include -#include -#include - -#include "criterion/types.h" -#include "criterion/options.h" -#include "criterion/redirect.h" -#include "protocol/protocol.h" -#include "protocol/messages.h" -#include "io/event.h" -#include "compat/posix.h" -#include "worker.h" -#include "protocol/connect.h" - -static s_proc_handle *g_current_proc; - -void set_runner_process(void) { - g_current_proc = get_current_process(); -} - -void unset_runner_process(void) { - sfree(g_current_proc); -} - -bool is_runner(void) { - return is_current_process(g_current_proc); -} - -bool is_single_mode(void) { - return g_current_proc == NULL; -} - -static void close_process(void *ptr, CR_UNUSED void *meta) { - struct worker *proc = ptr; - sfree(proc->ctx.suite_stats); - sfree(proc->ctx.test_stats); - sfree(proc->ctx.stats); - sfree(proc->proc); -} - -void run_worker(struct worker_context *ctx) { - ctx->func(ctx->test, ctx->suite); -} - -struct worker *spawn_test_worker(struct execution_context *ctx, - cr_worker_func func) { - g_worker_context = (struct worker_context) { - .test = ctx->test, - .suite = ctx->suite, - .func = func, - .param = ctx->param, - }; - - struct worker *ptr = NULL; - - s_proc_handle *proc = fork_process(); - if (proc == (void *) -1) { - criterion_perror("Could not fork the current process and start a worker: %s.\n", strerror(errno)); - abort(); - } else if (proc == NULL) { - run_worker(&g_worker_context); - sfree(ctx->test_stats); - sfree(ctx->suite_stats); - sfree(ctx->stats); - return NULL; - } - - ptr = smalloc( - .size = sizeof (struct worker), - .kind = SHARED, - .dtor = close_process); - - *ptr = (struct worker) { - .proc = proc, - .ctx = *ctx, - }; - return ptr; -} diff --git a/src/core/worker.h b/src/core/worker.h deleted file mode 100644 index 0470ec3..0000000 --- a/src/core/worker.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright © 2015-2016 Franklin "Snaipe" Mathieu - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef PROCESS_H_ -# define PROCESS_H_ - -# include -# include "criterion/types.h" -# include "compat/process.h" - -struct test_single_param { - size_t size; - void *ptr; -}; - -struct execution_context { - bool test_started; - bool normal_finish; - bool cleaned_up; - bool aborted; - struct criterion_global_stats *stats; - struct criterion_test *test; - struct criterion_test_stats *test_stats; - struct criterion_suite *suite; - struct criterion_suite_stats *suite_stats; - struct test_single_param *param; -}; - -struct worker { - int active; - s_proc_handle *proc; - struct execution_context ctx; -}; - -enum status_kind { - EXIT_STATUS, - STOPPED, - SIGNAL, -}; - -struct process_status { - enum status_kind kind; - int status; -}; - -struct worker_status { - s_proc_handle proc; - struct process_status status; -}; - -struct worker_set { - struct worker **workers; - size_t max_workers; -}; - -void run_worker(struct worker_context *ctx); -void set_runner_process(void); -void unset_runner_process(void); -bool is_runner(void); -bool is_single_mode(void); -struct process_status wait_proc(struct worker *proc); -struct worker *spawn_test_worker(struct execution_context *ctx, cr_worker_func func); - -#endif /* !PROCESS_H_ */ diff --git a/src/entry/params.c b/src/entry/params.c index 1a02a78..f642ab0 100644 --- a/src/entry/params.c +++ b/src/entry/params.c @@ -239,7 +239,6 @@ CR_API int criterion_handle_args(int argc, char *argv[], bool handle_unknown_arg case 'j': criterion_options.jobs = atou(optarg); break; case 'f': criterion_options.fail_fast = true; break; case 'S': criterion_options.short_filename = true; break; - case 's': run_single_test_by_name(optarg); return 0; case 'p': criterion_options.pattern = optarg; break; case 'q': quiet = true; break; @@ -273,6 +272,9 @@ CR_API int criterion_handle_args(int argc, char *argv[], bool handle_unknown_arg criterion_add_output(arg, path); } break; case 'w': criterion_options.wait_for_clients = true; break; + case 's': + fprintf(stderr, "--single has been removed. Use --debug instead."); + return 3; case '?': case 'c': criterion_options.crash = true; break; default : do_print_usage = handle_unknown_arg; break; diff --git a/src/err.c b/src/err.c new file mode 100644 index 0000000..e198845 --- /dev/null +++ b/src/err.c @@ -0,0 +1,39 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015-2016 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include + +#include "err.h" + +CR_NORETURN void cr_panic(const char *str, ...) +{ + va_list vl; + va_start(vl, str); + fprintf(stderr, "criterion: "); + vfprintf(stderr, str, vl); + fprintf(stderr, "\n"); + va_end(vl); + abort(); +} diff --git a/src/err.h b/src/err.h new file mode 100644 index 0000000..a82148a --- /dev/null +++ b/src/err.h @@ -0,0 +1,62 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015-2016 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef ERR_H_ +# define ERR_H_ + +# include + +# include "common.h" + +CR_FORMAT(printf, 1, 2) +CR_NORETURN void cr_panic(const char *str, ...); + +# define errno_ignore(Stmt) \ + do { \ + int err = errno; \ + Stmt; \ + errno = err; \ + } while (0) + +# define errno_return(Err, Val) \ + do { \ + errno = (Err); \ + return (Val); \ + } while (0) + +# undef assert +# define assert(Cnd) \ + do { \ + if (!(Cnd)) { \ + cr_panic("Assertion failed: %s\n\t@ %s:%d (%s)", #Cnd, __FILE__, \ + __LINE__, __func__); \ + } \ + } while (0) + +# ifdef NDEBUG +# define debug_assert(Cnd) +# else +# define debug_assert(Cnd) assert(Cnd) +# endif + +#endif /* !ERR_H_ */ diff --git a/src/io/event.h b/src/io/event.h index 741ea9a..d82c3cc 100644 --- a/src/io/event.h +++ b/src/io/event.h @@ -25,7 +25,6 @@ # define EVENT_H_ # include "criterion/event.h" -# include "core/worker.h" # include # include @@ -35,9 +34,6 @@ struct event { unsigned long long pid; int kind; void *data; - - struct worker *worker; - size_t worker_index; }; void criterion_send_assert(struct criterion_assert_stats *stats); diff --git a/src/protocol/connect.c b/src/protocol/connect.c index 5f64e51..302fd62 100644 --- a/src/protocol/connect.c +++ b/src/protocol/connect.c @@ -27,30 +27,15 @@ #include #include +#include "err.h" #include "compat/process.h" -#include "core/worker.h" - -#define URL "ipc://%scriterion_%llu.sock" - -#define errno_ignore(Stmt) do { int err = errno; Stmt; errno = err; } while (0) - -int bind_server(void) { +int cri_proto_bind(const char *url) +{ int sock = nn_socket(AF_SP, NN_REP); if (sock < 0) return -1; -#ifdef VANILLA_WIN32 - char *prefix = ""; -#else - char *prefix = "/tmp/"; -#endif - - char url[256]; - int rc = snprintf(url, sizeof (url), URL, prefix, get_process_id()); - if (rc == 256) - cr_panic("IPC url too long"); - if (nn_bind(sock, url) < 0) goto error; @@ -61,25 +46,12 @@ error: {} return -1; } -int connect_client(void) { - if (is_single_mode()) - return 0; - +int cri_proto_connect(const char *url) +{ int sock = nn_socket(AF_SP, NN_REQ); if (sock < 0) return -1; -#ifdef VANILLA_WIN32 - char *prefix = ""; -#else - char *prefix = "/tmp/"; -#endif - - char url[256]; - int rc = snprintf(url, sizeof (url), URL, prefix, get_runner_process_id()); - if (rc == 256) - cr_panic("IPC url too long"); - if (nn_connect (sock, url) < 0) goto error; @@ -90,9 +62,7 @@ error: {} return -1; } -void close_socket(int sock) { - if (is_single_mode()) - return; - +void cri_proto_close(int sock) +{ nn_close(sock); } diff --git a/src/protocol/connect.h b/src/protocol/connect.h index b9f1e26..c50d9ee 100644 --- a/src/protocol/connect.h +++ b/src/protocol/connect.h @@ -24,8 +24,8 @@ #ifndef CONNECT_H_ # define CONNECT_H_ -int connect_client(void); -int bind_server(void); -void close_socket(int sock); +int cri_proto_bind(const char *url); +int cri_proto_connect(const char *url); +void cri_proto_close(int sock); #endif /* !CONNECT_H_ */ diff --git a/src/protocol/messages.c b/src/protocol/messages.c index 4c0c3f7..788d66e 100644 --- a/src/protocol/messages.c +++ b/src/protocol/messages.c @@ -80,9 +80,6 @@ const char *message_names[] = { }; void cr_send_to_runner(const criterion_protocol_msg *message) { - if (is_single_mode()) - return; - if (write_message(g_client_socket, message) != 1) { criterion_perror("Could not write the \"%s\" message down the event pipe: %s.\n", message_names[message->data.which_value], diff --git a/src/protocol/protocol.h b/src/protocol/protocol.h index 2dea3a1..2ea0fcc 100644 --- a/src/protocol/protocol.h +++ b/src/protocol/protocol.h @@ -29,6 +29,7 @@ # include # include "criterion.pb.h" # include "criterion/internal/preprocess.h" +# include "compat/process.h" enum protocol_version { PROTOCOL_V1 = 1,