boxfort: Initial integration

This commit is contained in:
Snaipe 2016-08-08 15:59:13 +02:00
parent 955e982f37
commit dc5248d49c
29 changed files with 454 additions and 1140 deletions

View file

@ -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)

View file

@ -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;

View file

@ -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.

View file

@ -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

View file

@ -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_ */

View file

@ -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);
}

View file

@ -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_ */

View file

@ -33,8 +33,6 @@
#include <assert.h>
#include <signal.h>
static INLINE void nothing(void) {}
#ifdef VANILLA_WIN32
static HANDLE cr_job;
#else

View file

@ -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
}

View file

@ -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_ */

View file

@ -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);

View file

@ -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_ */

View file

@ -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) { \

View file

@ -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_ */

View file

@ -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);
}

View file

@ -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_ */

View file

@ -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));
}
}

View file

@ -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_ */

View file

@ -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 **)&param);
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());
}
}

View file

@ -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;
}

View file

@ -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_ */

View file

@ -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
View 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
View 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_ */

View file

@ -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);

View file

@ -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);
}

View file

@ -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_ */

View file

@ -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],

View file

@ -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,