boxfort: Initial integration
This commit is contained in:
parent
955e982f37
commit
dc5248d49c
29 changed files with 454 additions and 1140 deletions
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<T> allocator may be used with STL containers to
|
||||
// allocate objects with the functions described above.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
# include <stdio.h>
|
||||
# include <stdlib.h>
|
||||
|
||||
# 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_ */
|
||||
|
|
|
@ -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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
|
|
@ -24,12 +24,11 @@
|
|||
#ifndef COMPAT_ALLOC_H_
|
||||
# define COMPAT_ALLOC_H_
|
||||
|
||||
# include <boxfort.h>
|
||||
# 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_ */
|
||||
|
|
|
@ -33,8 +33,6 @@
|
|||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
|
||||
static INLINE void nothing(void) {}
|
||||
|
||||
#ifdef VANILLA_WIN32
|
||||
static HANDLE cr_job;
|
||||
#else
|
||||
|
|
|
@ -21,494 +21,8 @@
|
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <csptr/smalloc.h>
|
||||
#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 <signal.h>
|
||||
|
||||
#ifdef VANILLA_WIN32
|
||||
# include <tchar.h>
|
||||
# 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 <pthread.h>
|
||||
#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
|
||||
}
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
# define CLIENT_H_
|
||||
|
||||
# include <khash.h>
|
||||
# include <boxfort.h>
|
||||
|
||||
// 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_ */
|
||||
|
|
|
@ -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) { \
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -24,15 +24,25 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <csptr/smalloc.h>
|
||||
#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 <valgrind/valgrind.h>
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,10 +24,13 @@
|
|||
#ifndef RUNNER_COROUTINE_H_
|
||||
# define RUNNER_COROUTINE_H_
|
||||
|
||||
# include <boxfort.h>
|
||||
# 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_ */
|
||||
|
|
|
@ -21,10 +21,10 @@
|
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <boxfort.h>
|
||||
#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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright © 2015-2016 Franklin "Snaipe" Mathieu <http://snai.pe/>
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
#include <csptr/smalloc.h>
|
||||
#include <nanomsg/nn.h>
|
||||
|
||||
#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;
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright © 2015-2016 Franklin "Snaipe" Mathieu <http://snai.pe/>
|
||||
*
|
||||
* 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 <stdbool.h>
|
||||
# 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_ */
|
|
@ -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;
|
||||
|
|
39
src/err.c
Normal file
39
src/err.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright © 2015-2016 Franklin "Snaipe" Mathieu <http://snai.pe/>
|
||||
*
|
||||
* 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 <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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();
|
||||
}
|
62
src/err.h
Normal file
62
src/err.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright © 2015-2016 Franklin "Snaipe" Mathieu <http://snai.pe/>
|
||||
*
|
||||
* 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 <errno.h>
|
||||
|
||||
# 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_ */
|
|
@ -25,7 +25,6 @@
|
|||
# define EVENT_H_
|
||||
|
||||
# include "criterion/event.h"
|
||||
# include "core/worker.h"
|
||||
# include <stdio.h>
|
||||
# include <pb.h>
|
||||
|
||||
|
@ -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);
|
||||
|
|
|
@ -27,30 +27,15 @@
|
|||
#include <nanomsg/reqrep.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
# include <pb_decode.h>
|
||||
# include "criterion.pb.h"
|
||||
# include "criterion/internal/preprocess.h"
|
||||
# include "compat/process.h"
|
||||
|
||||
enum protocol_version {
|
||||
PROTOCOL_V1 = 1,
|
||||
|
|
Loading…
Add table
Reference in a new issue