From 70a3c91dc51667fc4fb4eeeffc7e0e95990be604 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Sun, 15 Mar 2015 16:00:21 +0100 Subject: [PATCH 01/14] Refactored code to prepare for windows integration --- Makefile.am | 1 + include/criterion/event.h | 3 +- src/event.c | 16 ++++---- src/event.h | 2 +- src/posix-compat.c | 82 +++++++++++++++++++++++++++++++++++++++ src/posix-compat.h | 35 +++++++++++++++++ src/process.c | 49 ++++++++++++----------- src/process.h | 3 +- src/runner.c | 7 ++-- 9 files changed, 162 insertions(+), 36 deletions(-) create mode 100644 src/posix-compat.c create mode 100644 src/posix-compat.h diff --git a/Makefile.am b/Makefile.am index e880bad..482175a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -52,6 +52,7 @@ libcriterion_la_SOURCES = \ src/timer.c \ src/timer.h \ src/ordered-set.c \ + src/posix-compat.c \ src/main.c TARGET = $(PACKAGE)-$(VERSION) diff --git a/include/criterion/event.h b/include/criterion/event.h index 75f4a7c..99c85c8 100644 --- a/include/criterion/event.h +++ b/include/criterion/event.h @@ -25,8 +25,9 @@ # define CRITERION_EVENT_H_ # include +# include -extern int EVENT_PIPE; +extern FILE *g_event_pipe; void send_event(int kind, void *data, size_t size); diff --git a/src/event.c b/src/event.c index 339c477..a6a8b3d 100644 --- a/src/event.c +++ b/src/event.c @@ -22,36 +22,37 @@ * THE SOFTWARE. */ -#include +#include #include #include "criterion/stats.h" +#include "criterion/common.h" #include "criterion/hooks.h" #include "event.h" -int EVENT_PIPE = -1; +FILE *g_event_pipe = NULL; void destroy_event(void *ptr, UNUSED void *meta) { struct event *ev = ptr; free(ev->data); } -struct event *read_event(int fd) { +struct event *read_event(FILE *f) { unsigned kind; - if (read(fd, &kind, sizeof (unsigned)) < (ssize_t) sizeof (unsigned)) + if (fread(&kind, sizeof (unsigned), 1, f) == 0) return NULL; switch (kind) { case ASSERT: { const size_t assert_size = sizeof (struct criterion_assert_stats); unsigned char *buf = malloc(assert_size); - if (read(fd, buf, assert_size) < (ssize_t) assert_size) + if (fread(buf, assert_size, 1, f) == 0) return NULL; return unique_ptr(struct event, { .kind = kind, .data = buf }, destroy_event); } case POST_TEST: { double *elapsed_time = malloc(sizeof (double)); - if (read(fd, elapsed_time, sizeof (double)) < (ssize_t) sizeof (double)) + if (fread(elapsed_time, sizeof (double), 1, f) == 0) return NULL; return unique_ptr(struct event, { .kind = kind, .data = elapsed_time }, destroy_event); @@ -65,5 +66,6 @@ void send_event(int kind, void *data, size_t size) { unsigned char buf[sizeof (int) + size]; memcpy(buf, &kind, sizeof (int)); memcpy(buf + sizeof (int), data, size); - write(EVENT_PIPE, buf, sizeof (int) + size); + if (fwrite(buf, sizeof (int) + size, 1, g_event_pipe) == 0) + abort(); } diff --git a/src/event.h b/src/event.h index 405df73..fc8ce4f 100644 --- a/src/event.h +++ b/src/event.h @@ -31,6 +31,6 @@ struct event { void *data; }; -struct event *read_event(int fd); +struct event *read_event(FILE *f); #endif /* !EVENT_H_ */ diff --git a/src/posix-compat.c b/src/posix-compat.c new file mode 100644 index 0000000..46e26e4 --- /dev/null +++ b/src/posix-compat.c @@ -0,0 +1,82 @@ +#include "posix-compat.h" + +#ifdef _WIN32 +# include +#else +# include +# include +# include +# include +#endif + +#include + +struct proc_handle { +#ifdef _WIN32 + // TODO +#else + pid_t pid; +#endif +}; + +struct pipe_handle { +#ifdef _WIN32 + // TODO +#else + int fds[2]; +#endif +}; + +s_proc_handle *fork_process() { +#ifdef _WIN32 + // TODO +#else + pid_t pid = fork(); + if (pid == -1) + return (void *) -1; + if (pid == 0) + return NULL; + return unique_ptr(s_proc_handle, { pid }); +#endif +} + +void wait_process(s_proc_handle *handle, int *status) { +#ifdef _WIN32 + // TODO +#else + waitpid(handle->pid, status, 0); +#endif +} + +FILE *pipe_in(s_pipe_handle *p) { + close(p->fds[1]); + FILE *in = fdopen(p->fds[0], "r"); + setvbuf(in, NULL, _IONBF, 0); + return in; +} + +FILE *pipe_out(s_pipe_handle *p) { + close(p->fds[0]); + FILE *out = fdopen(p->fds[1], "w"); + setvbuf(out, NULL, _IONBF, 0); + return out; +} + +s_pipe_handle *stdpipe() { +#ifdef _WIN32 + // TODO +#else + int fds[2] = { -1, -1 }; + if (pipe(fds) == -1) + return NULL; + return unique_ptr(s_pipe_handle, {{ fds[0], fds[1] }}); +#endif +} + +s_proc_handle *get_current_process() { + return unique_ptr(s_proc_handle, { getpid() }); +} + +bool is_current_process(s_proc_handle *proc) { + return proc->pid == getpid(); +} diff --git a/src/posix-compat.h b/src/posix-compat.h new file mode 100644 index 0000000..83567f8 --- /dev/null +++ b/src/posix-compat.h @@ -0,0 +1,35 @@ +#ifndef POSIX_COMPAT_H_ +# define POSIX_COMPAT_H_ + +# define _POSIX_SOURCE 1 +# include +# undef _POSIX_SOURCE + +# ifdef _WIN32 +# define WEXITSTATUS(Status) (((Status) & 0xFF00) >> 8) +# define WTERMSIG(Status) ((Status) & 0x7F) +# define WIFEXITED(Status) (WTERMSIG(Status) == 0) +# define WIFSIGNALED(Status) (((signed char) (WTERMSIG(Status) + 1) >> 1) > 0) +# else +# include +# endif + +#include + +struct proc_handle; +typedef struct proc_handle s_proc_handle; + +struct pipe_handle; +typedef struct pipe_handle s_pipe_handle; + +s_pipe_handle *stdpipe(); +FILE *pipe_in(s_pipe_handle *p); +FILE *pipe_out(s_pipe_handle *p); + +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); + +#endif /* !POSIX_COMPAT_H_ */ diff --git a/src/process.c b/src/process.c index 0d5689c..c0ebd76 100644 --- a/src/process.c +++ b/src/process.c @@ -22,33 +22,38 @@ * THE SOFTWARE. */ #include -#include -#include -#include +#include #include #include "criterion/types.h" #include "criterion/options.h" #include "process.h" #include "event.h" +#include "posix-compat.h" struct process { - pid_t pid; - int in; + s_proc_handle *proc; + FILE *in; }; -static pid_t g_runner_pid; +static s_proc_handle *g_current_proc; -void set_runner_pid(void) { - g_runner_pid = getpid(); +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 g_runner_pid == getpid(); + return is_current_process(g_current_proc); } static void close_process(void *ptr, UNUSED void *meta) { - close(((struct process *) ptr)->in); + struct process *proc = ptr; + fclose(proc->in); + sfree(proc->proc); } struct event *worker_read_event(struct process *proc) { @@ -58,35 +63,33 @@ struct event *worker_read_event(struct process *proc) { struct process *spawn_test_worker(struct criterion_test *test, struct criterion_suite *suite, void (*func)(struct criterion_test *, struct criterion_suite *)) { - int fds[2]; - if (pipe(fds) == -1) + smart s_pipe_handle *pipe = stdpipe(); + if (pipe == NULL) abort(); - pid_t pid = fork(); - if (pid == -1) { + s_proc_handle *proc = fork_process(); + if (proc == (void *) -1) { return NULL; - } else if (!pid) { - close(STDIN_FILENO); - close(fds[0]); - EVENT_PIPE = fds[1]; + } else if (proc == NULL) { + fclose(stdin); + g_event_pipe = pipe_out(pipe); func(test, suite); - close(fds[1]); + fclose(g_event_pipe); fflush(NULL); // flush all opened streams if (criterion_options.no_early_exit) return NULL; else - _exit(0); + _Exit(0); } - close(fds[1]); - return unique_ptr(struct process, { .pid = pid, .in = fds[0] }, close_process); + return unique_ptr(struct process, { .proc = proc, .in = pipe_in(pipe) }, close_process); } struct process_status wait_proc(struct process *proc) { int status; - waitpid(proc->pid, &status, 0); + wait_process(proc->proc, &status); if (WIFEXITED(status)) return (struct process_status) { .kind = EXIT_STATUS, .status = WEXITSTATUS(status) }; diff --git a/src/process.h b/src/process.h index 02f480e..679c17f 100644 --- a/src/process.h +++ b/src/process.h @@ -39,7 +39,8 @@ struct process_status { int status; }; -void set_runner_pid(void); +void set_runner_process(void); +void unset_runner_process(void); bool is_runner(void); struct process_status wait_proc(struct process *proc); struct process *spawn_test_worker(struct criterion_test *test, diff --git a/src/runner.c b/src/runner.c index c4a535e..8f07b22 100644 --- a/src/runner.c +++ b/src/runner.c @@ -23,7 +23,6 @@ */ #include #include -#include #include #include "criterion/criterion.h" #include "criterion/options.h" @@ -203,7 +202,6 @@ static int criterion_run_all_tests_impl(void) { smart struct criterion_test_set *set = criterion_init(); report(PRE_ALL, set); - set_runner_pid(); smart struct criterion_global_stats *stats = stats_init(); map_tests(set, stats, run_test); @@ -216,9 +214,12 @@ static int criterion_run_all_tests_impl(void) { } int criterion_run_all_tests(void) { + set_runner_process(); int res = criterion_run_all_tests_impl(); + unset_runner_process(); + if (res == -1) // if this is the test worker terminating - _exit(0); + _Exit(0); return criterion_options.always_succeed || res; } From 130244d2cadde4a1fa84a558332b03f3f52ea96d Mon Sep 17 00:00:00 2001 From: Snaipe Date: Wed, 25 Mar 2015 00:22:33 +0100 Subject: [PATCH 02/14] Implemented pipe on the windows side --- src/posix-compat.c | 33 +++++++++++++++++++++++++++++++-- src/posix-compat.h | 10 ++++++++-- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/posix-compat.c b/src/posix-compat.c index 46e26e4..7d7f0e4 100644 --- a/src/posix-compat.c +++ b/src/posix-compat.c @@ -1,7 +1,11 @@ #include "posix-compat.h" #ifdef _WIN32 +# define VC_EXTRALEAN +# define WIN32_LEAN_AND_MEAN # include +# include +# include #else # include # include @@ -21,7 +25,7 @@ struct proc_handle { struct pipe_handle { #ifdef _WIN32 - // TODO + HANDLE fhs[2]; #else int fds[2]; #endif @@ -49,22 +53,47 @@ void wait_process(s_proc_handle *handle, int *status) { } FILE *pipe_in(s_pipe_handle *p) { +#ifdef _WIN32 + CloseHandle(p->fhs[1]); + int fd = _open_osfhandle(p->fhs[0], _O_RDONLY); + if (fd == -1) + return NULL; + FILE *in = _fdopen(, "r"); +#else close(p->fds[1]); FILE *in = fdopen(p->fds[0], "r"); +#endif + if (!in) + return NULL; + setvbuf(in, NULL, _IONBF, 0); return in; } FILE *pipe_out(s_pipe_handle *p) { +#ifdef _WIN32 + CloseHandle(p->fhs[0]); + int fd = _open_osfhandle(p->fhs[1], _O_WRONLY); + if (fd == -1) + return NULL; + FILE *out = _fdopen(fd, "w"); +#else close(p->fds[0]); FILE *out = fdopen(p->fds[1], "w"); +#endif + if (!out) + return NULL; + setvbuf(out, NULL, _IONBF, 0); return out; } s_pipe_handle *stdpipe() { #ifdef _WIN32 - // TODO + HANDLE fhs[2]; + if (!CreatePipe(fhs, fhs + 1, NULL, 0)) + return NULL; + return unique_ptr(s_pipe_handle, {{ fhs[0], fhs[1] }}); #else int fds[2] = { -1, -1 }; if (pipe(fds) == -1) diff --git a/src/posix-compat.h b/src/posix-compat.h index 83567f8..8f53627 100644 --- a/src/posix-compat.h +++ b/src/posix-compat.h @@ -1,9 +1,15 @@ #ifndef POSIX_COMPAT_H_ # define POSIX_COMPAT_H_ -# define _POSIX_SOURCE 1 +# if !defined(_POSIX_SOURCE) +# define _POSIX_SOURCE 1 +# define TMP_POSIX +# endif # include -# undef _POSIX_SOURCE +# ifdef TMP_POSIX +# undef _POSIX_SOURCE +# undef TMP_POSIX +# endif # ifdef _WIN32 # define WEXITSTATUS(Status) (((Status) & 0xFF00) >> 8) From 7a74617884eb9263b1f41988e2eae9c7ccb82159 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Wed, 25 Mar 2015 12:06:47 +0100 Subject: [PATCH 03/14] Made appveyor use mingw-gcc --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 5ef46fa..e899727 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -52,7 +52,7 @@ configuration: Release install: - "%CYG_BASH% -lc 'cd $APPVEYOR_BUILD_FOLDER; ./autogen.sh'" - - '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0 Date: Wed, 25 Mar 2015 17:17:28 +0100 Subject: [PATCH 04/14] Added WIP fork()'ing --- src/posix-compat.c | 90 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 3 deletions(-) diff --git a/src/posix-compat.c b/src/posix-compat.c index 7d7f0e4..236f4f8 100644 --- a/src/posix-compat.c +++ b/src/posix-compat.c @@ -6,6 +6,26 @@ # include # include # include +# include +# include + +# define CREATE_SUSPENDED(Filename, CmdLine, StartupInfo, Info) \ + CreateProcessW(Filename, \ + CmdLine(), \ + NULL, \ + NULL, \ + TRUE, \ + CREATE_SUSPENDED, \ + NULL, \ + NULL, \ + &(StartupInfo), \ + &(Info)) + +# ifdef _WIN64 +# define Register(Reg, Context) ((Context).R ## Reg) +# else +# define Register(Reg, Context) ((Context).E ## Reg) +# endif #else # include # include @@ -17,7 +37,7 @@ struct proc_handle { #ifdef _WIN32 - // TODO + HANDLE handle; #else pid_t pid; #endif @@ -31,9 +51,69 @@ struct pipe_handle { #endif }; +#ifdef _WIN32 +# define CONTEXT_INIT {CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT} +struct stack_info { + char *ptr; + char *base; + size_t size; +}; + +static inline get_stack_info(struct stack_info *stack) { + CONTEXT context = CONTEXT_INIT; + MEMORY_BASIC_INFORMATION mbi; + + GetThreadContext(GetCurrentThread(), &context); + stack->ptr = (char *) Register(sp, context); + + VirtualQuery(stack->ptr, &mbi, sizeof (mbi)); + stack->base = mbi->BaseAddress; + stack->size = mbi->RegionSize; +} + +static g_jmp; + +static void resume_child(void) { + longjmp(g_jmp, 1); +} +#endif + s_proc_handle *fork_process() { #ifdef _WIN32 - // TODO + PROCESS_INFORMATION info; + STARTUPINFOW si = { .cb = sizeof (STARTUPINFOW) }; + + ZeroMemory(&info, sizeof (info)); + + CONTEXT context = {CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT}; + + // Initialize longjmp buffer + if (setjmp(g_jmp)) + return NULL; // we are the child, return + + // Create the suspended child process + wchar_t filename[MAX_PATH]; + GetModuleFileNameW(NULL, filename, MAX_PATH); + + if (!CREATE_SUSPENDED(filename, GetCommandLineW(), si, info)) + return NULL; + + // Set child's instruction pointer to resume_child + GetThreadContext(info.hThread, &context); + Register(ip, context) = resume_child; + SetThreadContext(info.hThread, &context); + + // Copy stack + struct stack_info stack; + get_stack_info(&stack); + + VirtualAllocEx(info.hProcess, stack->base, stack->size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + WriteProcessMemory(info.hProcess, stack->ptr, stack->ptr, stack->base + stack->size - stack->ptr, NULL); + + ResumeThread(info.hThread); + CloseHandle(info.hThread); + + return unique_ptr(s_proc_handle, { info.hProcess }); #else pid_t pid = fork(); if (pid == -1) @@ -46,7 +126,11 @@ s_proc_handle *fork_process() { void wait_process(s_proc_handle *handle, int *status) { #ifdef _WIN32 - // TODO + WaitForSingleObject(handle->handle, INFINITE); + DWORD exit_code; + GetExitCodeProcess(handle->handle, &exit_code); + CloseHandle(handle->handle); + *status = exit_code << 8; #else waitpid(handle->pid, status, 0); #endif From cde5fed6d4032bce69556be499eb54f51da85854 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Wed, 25 Mar 2015 18:48:28 +0100 Subject: [PATCH 05/14] Updated libcsptr for mingw 4.7 support --- dependencies/csptr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/csptr b/dependencies/csptr index 15b825f..1c96aae 160000 --- a/dependencies/csptr +++ b/dependencies/csptr @@ -1 +1 @@ -Subproject commit 15b825ffb8ffe7309bf524659eb900d6bb1d7c04 +Subproject commit 1c96aaec456287dc63eeb31443f409b5476afa0e From e9f0dfb572c893823efbb9d2ff726f19f7df18a7 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Wed, 25 Mar 2015 19:58:51 +0100 Subject: [PATCH 06/14] Added fnmatch conditional for Windows portability --- configure.ac | 2 ++ src/main.c | 6 ++++++ src/report.c | 8 +++++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index bddf447..e8de7d2 100644 --- a/configure.ac +++ b/configure.ac @@ -20,6 +20,8 @@ AC_PROG_LN_S AC_PROG_MAKE_SET AC_SUBST([LIBTOOL_DEPS]) +AC_FUNC_FNMATCH + AC_ARG_ENABLE([gcov], [AS_HELP_STRING([--enable-gcov], [Compile the project with converage enabled])], diff --git a/src/main.c b/src/main.c index 467e49d..4ee1c76 100644 --- a/src/main.c +++ b/src/main.c @@ -112,7 +112,9 @@ int main(int argc, char *argv[]) { {"list", no_argument, 0, 'l'}, {"ascii", no_argument, 0, 'k'}, {"fail-fast", no_argument, 0, 'f'}, +#ifdef HAVE_FNMATCH {"pattern", required_argument, 0, 'p'}, +#endif {"always-succeed", no_argument, 0, 'y'}, {"no-early-exit", no_argument, 0, 'z'}, {0, 0, 0, 0 } @@ -124,7 +126,9 @@ int main(int argc, char *argv[]) { .fail_fast = !strcmp("1", getenv("CRITERION_FAIL_FAST") ?: "0"), .use_ascii = !strcmp("1", getenv("CRITERION_USE_ASCII") ?: "0"), .logging_threshold = atoi(getenv("CRITERION_VERBOSITY_LEVEL") ?: "2"), +#ifdef HAVE_FNMATCH .pattern = getenv("CRITERION_TEST_PATTERN"), +#endif .output_provider = NORMAL_LOGGING, }; @@ -140,7 +144,9 @@ int main(int argc, char *argv[]) { case 'z': criterion_options.no_early_exit = true; break; case 'k': criterion_options.use_ascii = true; break; case 'f': criterion_options.fail_fast = true; break; +#ifdef HAVE_FNMATCH case 'p': criterion_options.pattern = optarg; break; +#endif case 't': use_tap = true; break; case 'l': do_list_tests = true; break; case 'v': do_print_version = true; break; diff --git a/src/report.c b/src/report.c index 5c70807..542f813 100644 --- a/src/report.c +++ b/src/report.c @@ -24,13 +24,17 @@ #define _GNU_SOURCE #include #include -#include #include "criterion/types.h" #include "criterion/stats.h" #include "criterion/logging.h" #include "criterion/options.h" #include "criterion/ordered-set.h" #include "report.h" +#include "config.h" + +#ifdef HAVE_FNMATCH +#include +#endif #define IMPL_CALL_REPORT_HOOKS(Kind) \ IMPL_SECTION_LIMITS(f_report_hook, crit_ ## Kind); \ @@ -53,6 +57,7 @@ __attribute__((always_inline)) static inline void nothing() {} IMPL_REPORT_HOOK(PRE_ALL)(struct criterion_test_set *set) { +#ifdef HAVE_FNMATCH if (criterion_options.pattern) { FOREACH_SET(struct criterion_suite_set *s, set->suites) { if ((s->suite.data && s->suite.data->disabled) || !s->tests) @@ -64,6 +69,7 @@ IMPL_REPORT_HOOK(PRE_ALL)(struct criterion_test_set *set) { } } } +#endif log(pre_all, set); } From bbb107e8da85608b68c9bcfc97901ebf10999d15 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Wed, 25 Mar 2015 20:23:06 +0100 Subject: [PATCH 07/14] Fixed some issues on windows --- src/posix-compat.c | 53 +++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/posix-compat.c b/src/posix-compat.c index 236f4f8..7c8b8ea 100644 --- a/src/posix-compat.c +++ b/src/posix-compat.c @@ -8,17 +8,18 @@ # include # include # include +# include -# define CREATE_SUSPENDED(Filename, CmdLine, StartupInfo, Info) \ - CreateProcessW(Filename, \ - CmdLine(), \ - NULL, \ - NULL, \ - TRUE, \ - CREATE_SUSPENDED, \ - NULL, \ - NULL, \ - &(StartupInfo), \ +# define CREATE_SUSPENDED_(Filename, CmdLine, StartupInfo, Info) \ + CreateProcessW(Filename, \ + CmdLine, \ + NULL, \ + NULL, \ + TRUE, \ + CREATE_SUSPENDED, \ + NULL, \ + NULL, \ + &(StartupInfo), \ &(Info)) # ifdef _WIN64 @@ -52,14 +53,14 @@ struct pipe_handle { }; #ifdef _WIN32 -# define CONTEXT_INIT {CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT} +# define CONTEXT_INIT { .ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT } struct stack_info { char *ptr; char *base; size_t size; }; -static inline get_stack_info(struct stack_info *stack) { +static inline void get_stack_info(struct stack_info *stack) { CONTEXT context = CONTEXT_INIT; MEMORY_BASIC_INFORMATION mbi; @@ -67,8 +68,8 @@ static inline get_stack_info(struct stack_info *stack) { stack->ptr = (char *) Register(sp, context); VirtualQuery(stack->ptr, &mbi, sizeof (mbi)); - stack->base = mbi->BaseAddress; - stack->size = mbi->RegionSize; + stack->base = mbi.BaseAddress; + stack->size = mbi.RegionSize; } static g_jmp; @@ -85,7 +86,7 @@ s_proc_handle *fork_process() { ZeroMemory(&info, sizeof (info)); - CONTEXT context = {CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT}; + CONTEXT context = CONTEXT_INIT; // Initialize longjmp buffer if (setjmp(g_jmp)) @@ -95,20 +96,20 @@ s_proc_handle *fork_process() { wchar_t filename[MAX_PATH]; GetModuleFileNameW(NULL, filename, MAX_PATH); - if (!CREATE_SUSPENDED(filename, GetCommandLineW(), si, info)) + if (!CREATE_SUSPENDED_(filename, GetCommandLineW(), si, info)) return NULL; // Set child's instruction pointer to resume_child GetThreadContext(info.hThread, &context); - Register(ip, context) = resume_child; + Register(ip, context) = (intptr_t) resume_child; SetThreadContext(info.hThread, &context); // Copy stack struct stack_info stack; get_stack_info(&stack); - VirtualAllocEx(info.hProcess, stack->base, stack->size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); - WriteProcessMemory(info.hProcess, stack->ptr, stack->ptr, stack->base + stack->size - stack->ptr, NULL); + VirtualAllocEx(info.hProcess, stack.base, stack.size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + WriteProcessMemory(info.hProcess, stack.ptr, stack.ptr, stack.base + stack.size - stack.ptr, NULL); ResumeThread(info.hThread); CloseHandle(info.hThread); @@ -139,10 +140,10 @@ void wait_process(s_proc_handle *handle, int *status) { FILE *pipe_in(s_pipe_handle *p) { #ifdef _WIN32 CloseHandle(p->fhs[1]); - int fd = _open_osfhandle(p->fhs[0], _O_RDONLY); + int fd = _open_osfhandle((intptr_t) p->fhs[0], _O_RDONLY); if (fd == -1) return NULL; - FILE *in = _fdopen(, "r"); + FILE *in = _fdopen(fd, "r"); #else close(p->fds[1]); FILE *in = fdopen(p->fds[0], "r"); @@ -157,7 +158,7 @@ FILE *pipe_in(s_pipe_handle *p) { FILE *pipe_out(s_pipe_handle *p) { #ifdef _WIN32 CloseHandle(p->fhs[0]); - int fd = _open_osfhandle(p->fhs[1], _O_WRONLY); + int fd = _open_osfhandle((intptr_t) p->fhs[1], _O_WRONLY); if (fd == -1) return NULL; FILE *out = _fdopen(fd, "w"); @@ -187,9 +188,17 @@ s_pipe_handle *stdpipe() { } s_proc_handle *get_current_process() { +#ifdef _WIN32 + return unique_ptr(s_proc_handle, { GetCurrentProcess() }); +#else return unique_ptr(s_proc_handle, { getpid() }); +#endif } bool is_current_process(s_proc_handle *proc) { +#ifdef _WIN32 + return GetProcessId(proc->handle) == GetProcessId(GetCurrentProcess()); +#else return proc->pid == getpid(); +#endif } From 419d80639f4138b76aaed7a78e67c15b5901990e Mon Sep 17 00:00:00 2001 From: Snaipe Date: Wed, 25 Mar 2015 20:24:41 +0100 Subject: [PATCH 08/14] Added missing jmp_buf type --- src/posix-compat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/posix-compat.c b/src/posix-compat.c index 7c8b8ea..636c8a5 100644 --- a/src/posix-compat.c +++ b/src/posix-compat.c @@ -72,7 +72,7 @@ static inline void get_stack_info(struct stack_info *stack) { stack->size = mbi.RegionSize; } -static g_jmp; +static jmp_buf g_jmp; static void resume_child(void) { longjmp(g_jmp, 1); From f652d174fe3e5339a3a472e40b0358e6bc3a569b Mon Sep 17 00:00:00 2001 From: Snaipe Date: Thu, 26 Mar 2015 14:49:09 +0100 Subject: [PATCH 09/14] Also copy the heap --- src/posix-compat.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/posix-compat.c b/src/posix-compat.c index 636c8a5..7a5d809 100644 --- a/src/posix-compat.c +++ b/src/posix-compat.c @@ -54,13 +54,13 @@ struct pipe_handle { #ifdef _WIN32 # define CONTEXT_INIT { .ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT } -struct stack_info { +struct region_info { char *ptr; char *base; size_t size; }; -static inline void get_stack_info(struct stack_info *stack) { +static inline void get_memory_info(struct region_info *stack, struct region_info *heap) { CONTEXT context = CONTEXT_INIT; MEMORY_BASIC_INFORMATION mbi; @@ -70,6 +70,13 @@ static inline void get_stack_info(struct stack_info *stack) { VirtualQuery(stack->ptr, &mbi, sizeof (mbi)); stack->base = mbi.BaseAddress; stack->size = mbi.RegionSize; + + void *ptr = malloc(1); + VirtualQuery(ptr, &mbi, sizeof (mbi)); + heap->ptr = NULL; + heap->base = mbi.BaseAddress; + heap->size = mbi.RegionSize; + free(ptr); } static jmp_buf g_jmp; @@ -104,12 +111,19 @@ s_proc_handle *fork_process() { Register(ip, context) = (intptr_t) resume_child; SetThreadContext(info.hThread, &context); - // Copy stack - struct stack_info stack; - get_stack_info(&stack); + // Copy stack, heap + struct region_info stack; + struct region_info heap; + get_memory_info(&stack, &heap); VirtualAllocEx(info.hProcess, stack.base, stack.size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); - WriteProcessMemory(info.hProcess, stack.ptr, stack.ptr, stack.base + stack.size - stack.ptr, NULL); + WriteProcessMemory(info.hProcess, stack.base, stack.base, stack.size, NULL); + + VirtualAllocEx(info.hProcess, heap.base, heap.size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + WriteProcessMemory(info.hProcess, heap.base, heap.base, heap.size, NULL); + + // Copy jmp_buf + WriteProcessMemory(info.hProcess, &g_jmp, &g_jmp, sizeof (jmp_buf), NULL); ResumeThread(info.hThread); CloseHandle(info.hThread); From 243ed3a8b83ed1419a6c4093ad323b1a38827a68 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Thu, 26 Mar 2015 22:18:43 +0100 Subject: [PATCH 10/14] Trying a safer approach for a fork() alternative --- include/criterion/types.h | 12 +++--- src/posix-compat.c | 91 +++++++++++++++------------------------ src/posix-compat.h | 13 +++++- src/process.c | 34 +++++++++------ src/process.h | 2 + src/runner.c | 4 ++ 6 files changed, 81 insertions(+), 75 deletions(-) diff --git a/include/criterion/types.h b/include/criterion/types.h index 3a4672f..6e7dae2 100644 --- a/include/criterion/types.h +++ b/include/criterion/types.h @@ -29,9 +29,9 @@ struct criterion_test_extra_data { int sentinel_; - const char *const identifier_; - const char *const file_; - const unsigned line_; + const char *identifier_; + const char *file_; + unsigned line_; void (*init)(void); void (*fini)(void); int signal; @@ -44,12 +44,14 @@ struct criterion_test { const char *name; const char *category; void (*test)(void); - struct criterion_test_extra_data *const data; + struct criterion_test_extra_data *data; }; struct criterion_suite { const char *name; - struct criterion_test_extra_data *const data; + struct criterion_test_extra_data *data; }; +typedef void (*f_worker_func)(struct criterion_test *, struct criterion_suite *); + #endif /* !CRITERION_TYPES_H_ */ diff --git a/src/posix-compat.c b/src/posix-compat.c index 7a5d809..01e4527 100644 --- a/src/posix-compat.c +++ b/src/posix-compat.c @@ -1,4 +1,5 @@ #include "posix-compat.h" +#include "process.h" #ifdef _WIN32 # define VC_EXTRALEAN @@ -7,7 +8,6 @@ # include # include # include -# include # include # define CREATE_SUSPENDED_(Filename, CmdLine, StartupInfo, Info) \ @@ -22,11 +22,7 @@ &(StartupInfo), \ &(Info)) -# ifdef _WIN64 -# define Register(Reg, Context) ((Context).R ## Reg) -# else -# define Register(Reg, Context) ((Context).E ## Reg) -# endif +# define CONTEXT_INIT { .ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT } #else # include # include @@ -52,40 +48,24 @@ struct pipe_handle { #endif }; +struct worker_context g_worker_context = {.test = NULL}; + #ifdef _WIN32 -# define CONTEXT_INIT { .ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT } -struct region_info { - char *ptr; - char *base; - size_t size; -}; - -static inline void get_memory_info(struct region_info *stack, struct region_info *heap) { - CONTEXT context = CONTEXT_INIT; - MEMORY_BASIC_INFORMATION mbi; - - GetThreadContext(GetCurrentThread(), &context); - stack->ptr = (char *) Register(sp, context); - - VirtualQuery(stack->ptr, &mbi, sizeof (mbi)); - stack->base = mbi.BaseAddress; - stack->size = mbi.RegionSize; - - void *ptr = malloc(1); - VirtualQuery(ptr, &mbi, sizeof (mbi)); - heap->ptr = NULL; - heap->base = mbi.BaseAddress; - heap->size = mbi.RegionSize; - free(ptr); -} - -static jmp_buf g_jmp; - -static void resume_child(void) { - longjmp(g_jmp, 1); -} +static struct criterion_test child_test; +static struct criterion_test_extra_data child_test_data; +static struct criterion_suite child_suite; +static struct criterion_test_extra_data child_suite_data; +static struct pipe_handle child_pipe; #endif +int resume_child(void) { + if (g_worker_context.test) { + run_worker(&g_worker_context); + return 1; + } + return 0; +} + s_proc_handle *fork_process() { #ifdef _WIN32 PROCESS_INFORMATION info; @@ -95,35 +75,32 @@ s_proc_handle *fork_process() { CONTEXT context = CONTEXT_INIT; - // Initialize longjmp buffer - if (setjmp(g_jmp)) - return NULL; // we are the child, return - // Create the suspended child process wchar_t filename[MAX_PATH]; GetModuleFileNameW(NULL, filename, MAX_PATH); if (!CREATE_SUSPENDED_(filename, GetCommandLineW(), si, info)) - return NULL; + return (void *) -1; - // Set child's instruction pointer to resume_child - GetThreadContext(info.hThread, &context); - Register(ip, context) = (intptr_t) resume_child; - SetThreadContext(info.hThread, &context); + // Copy context over + f_worker_func child_func = g_worker_context.func; - // Copy stack, heap - struct region_info stack; - struct region_info heap; - get_memory_info(&stack, &heap); + child_test = *g_worker_context.test; + child_test_data = *g_worker_context.test->data; + child_suite = *g_worker_context.suite; + child_suite_data = *g_worker_context.suite->data; + child_pipe = *g_worker_context.pipe; - VirtualAllocEx(info.hProcess, stack.base, stack.size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); - WriteProcessMemory(info.hProcess, stack.base, stack.base, stack.size, NULL); + g_worker_context = (struct worker_context) { &child_test, &child_suite, child_func, &child_pipe }; + child_test.data = &child_test_data; + child_suite.data = &child_suite_data; - VirtualAllocEx(info.hProcess, heap.base, heap.size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); - WriteProcessMemory(info.hProcess, heap.base, heap.base, heap.size, NULL); - - // Copy jmp_buf - WriteProcessMemory(info.hProcess, &g_jmp, &g_jmp, sizeof (jmp_buf), NULL); + WriteProcessMemory(info.hProcess, &child_test, &child_test, sizeof (child_test), NULL); + WriteProcessMemory(info.hProcess, &child_test_data, &child_test_data, sizeof (child_test_data), NULL); + WriteProcessMemory(info.hProcess, &child_suite, &child_suite, sizeof (child_suite), NULL); + WriteProcessMemory(info.hProcess, &child_suite_data, &child_suite_data, sizeof (child_suite_data), NULL); + WriteProcessMemory(info.hProcess, &child_pipe, &child_pipe, sizeof (child_pipe), NULL); + WriteProcessMemory(info.hProcess, &g_worker_context, &g_worker_context, sizeof (struct worker_context), NULL); ResumeThread(info.hThread); CloseHandle(info.hThread); diff --git a/src/posix-compat.h b/src/posix-compat.h index 8f53627..3c32e9c 100644 --- a/src/posix-compat.h +++ b/src/posix-compat.h @@ -20,7 +20,7 @@ # include # endif -#include +#include struct proc_handle; typedef struct proc_handle s_proc_handle; @@ -28,6 +28,17 @@ typedef struct proc_handle s_proc_handle; struct pipe_handle; typedef struct pipe_handle s_pipe_handle; +struct worker_context { + struct criterion_test *test; + struct criterion_suite *suite; + f_worker_func func; + struct pipe_handle *pipe; +}; + +extern struct worker_context g_worker_context; + +int resume_child(void); + s_pipe_handle *stdpipe(); FILE *pipe_in(s_pipe_handle *p); FILE *pipe_out(s_pipe_handle *p); diff --git a/src/process.c b/src/process.c index c0ebd76..83afc73 100644 --- a/src/process.c +++ b/src/process.c @@ -60,28 +60,38 @@ struct event *worker_read_event(struct process *proc) { return read_event(proc->in); } +void run_worker(struct worker_context *ctx) { + fclose(stdin); + g_event_pipe = pipe_out(ctx->pipe); + + ctx->func(ctx->test, ctx->suite); + fclose(g_event_pipe); + + fflush(NULL); // flush all opened streams + if (criterion_options.no_early_exit) + return; + _Exit(0); +} + struct process *spawn_test_worker(struct criterion_test *test, struct criterion_suite *suite, - void (*func)(struct criterion_test *, struct criterion_suite *)) { + f_worker_func func) { smart s_pipe_handle *pipe = stdpipe(); if (pipe == NULL) abort(); + g_worker_context = (struct worker_context) { + .test = test, + .suite = suite, + .func = func, + .pipe = pipe + }; s_proc_handle *proc = fork_process(); if (proc == (void *) -1) { return NULL; } else if (proc == NULL) { - fclose(stdin); - g_event_pipe = pipe_out(pipe); - - func(test, suite); - fclose(g_event_pipe); - - fflush(NULL); // flush all opened streams - if (criterion_options.no_early_exit) - return NULL; - else - _Exit(0); + run_worker(&g_worker_context); + return NULL; } return unique_ptr(struct process, { .proc = proc, .in = pipe_in(pipe) }, close_process); diff --git a/src/process.h b/src/process.h index 679c17f..5e0a6c3 100644 --- a/src/process.h +++ b/src/process.h @@ -25,6 +25,7 @@ # define PROCESS_H_ # include +# include "posix-compat.h" struct process; @@ -39,6 +40,7 @@ struct process_status { int status; }; +void run_worker(struct worker_context *ctx); void set_runner_process(void); void unset_runner_process(void); bool is_runner(void); diff --git a/src/runner.c b/src/runner.c index 8f07b22..8c84b1e 100644 --- a/src/runner.c +++ b/src/runner.c @@ -33,6 +33,7 @@ #include "event.h" #include "process.h" #include "timer.h" +#include "posix-compat.h" IMPL_SECTION_LIMITS(struct criterion_test, criterion_tests); IMPL_SECTION_LIMITS(struct criterion_suite, crit_suites); @@ -199,6 +200,9 @@ static void run_test(struct criterion_global_stats *stats, } static int criterion_run_all_tests_impl(void) { + if (resume_child()) // (windows only) resume from the fork + return -1; + smart struct criterion_test_set *set = criterion_init(); report(PRE_ALL, set); From 07a5d9a6004543dbd812f797d3a1156130342820 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Fri, 27 Mar 2015 07:39:33 -0700 Subject: [PATCH 11/14] Made the pipe handles inheritable --- src/posix-compat.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/posix-compat.c b/src/posix-compat.c index 01e4527..ec1e315 100644 --- a/src/posix-compat.c +++ b/src/posix-compat.c @@ -22,7 +22,6 @@ &(StartupInfo), \ &(Info)) -# define CONTEXT_INIT { .ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT } #else # include # include @@ -73,8 +72,6 @@ s_proc_handle *fork_process() { ZeroMemory(&info, sizeof (info)); - CONTEXT context = CONTEXT_INIT; - // Create the suspended child process wchar_t filename[MAX_PATH]; GetModuleFileNameW(NULL, filename, MAX_PATH); @@ -85,20 +82,23 @@ s_proc_handle *fork_process() { // Copy context over f_worker_func child_func = g_worker_context.func; - child_test = *g_worker_context.test; - child_test_data = *g_worker_context.test->data; - child_suite = *g_worker_context.suite; - child_suite_data = *g_worker_context.suite->data; - child_pipe = *g_worker_context.pipe; + child_test = *g_worker_context.test; + child_test_data = *g_worker_context.test->data; + child_suite = *g_worker_context.suite; + child_pipe = *g_worker_context.pipe; g_worker_context = (struct worker_context) { &child_test, &child_suite, child_func, &child_pipe }; child_test.data = &child_test_data; - child_suite.data = &child_suite_data; + + if (g_worker_context.suite->data) { + child_suite_data = *g_worker_context.suite->data; + child_suite.data = &child_suite_data; + WriteProcessMemory(info.hProcess, &child_suite_data, &child_suite_data, sizeof (child_suite_data), NULL); + } WriteProcessMemory(info.hProcess, &child_test, &child_test, sizeof (child_test), NULL); WriteProcessMemory(info.hProcess, &child_test_data, &child_test_data, sizeof (child_test_data), NULL); WriteProcessMemory(info.hProcess, &child_suite, &child_suite, sizeof (child_suite), NULL); - WriteProcessMemory(info.hProcess, &child_suite_data, &child_suite_data, sizeof (child_suite_data), NULL); WriteProcessMemory(info.hProcess, &child_pipe, &child_pipe, sizeof (child_pipe), NULL); WriteProcessMemory(info.hProcess, &g_worker_context, &g_worker_context, sizeof (struct worker_context), NULL); @@ -167,7 +167,8 @@ FILE *pipe_out(s_pipe_handle *p) { s_pipe_handle *stdpipe() { #ifdef _WIN32 HANDLE fhs[2]; - if (!CreatePipe(fhs, fhs + 1, NULL, 0)) + SECURITY_ATTRIBUTES attr = { .nLength = sizeof (SECURITY_ATTRIBUTES), .bInheritHandle = TRUE }; + if (!CreatePipe(fhs, fhs + 1, &attr, 0)) return NULL; return unique_ptr(s_pipe_handle, {{ fhs[0], fhs[1] }}); #else From 9374693b66c18e6ce4359859976ca06d2b85860e Mon Sep 17 00:00:00 2001 From: Snaipe Date: Fri, 27 Mar 2015 16:21:03 +0100 Subject: [PATCH 12/14] Added conditional usage display for --pattern --- src/main.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main.c b/src/main.c index 4ee1c76..832a28b 100644 --- a/src/main.c +++ b/src/main.c @@ -33,6 +33,14 @@ # define VERSION_MSG "Tests compiled with Criterion v" VERSION "\n" +#ifdef HAVE_FNMATCH +# define PATTERN_USAGE \ + " --pattern [PATTERN]: run tests matching the " \ + "given pattern\n" +#else +# define PATTERN_USAGE +#endif + # define USAGE \ VERSION_MSG "\n" \ "usage: %s OPTIONS\n" \ @@ -44,8 +52,7 @@ " -f or --fail-fast: exit after the first failure\n" \ " --ascii: don't use fancy unicode symbols " \ "or colors in the output\n" \ - " --pattern [PATTERN]: run tests matching the " \ - "given pattern\n" \ + PATTERN_USAGE \ " --tap: enables TAP formatting\n" \ " --always-succeed: always exit with 0\n" \ " --no-early-exit: do not exit the test worker " \ From 7cab7632168475f2dbd2698b819265685e84f6aa Mon Sep 17 00:00:00 2001 From: Snaipe Date: Fri, 27 Mar 2015 16:21:30 +0100 Subject: [PATCH 13/14] Made appveyor builds work again --- appveyor.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index e899727..fe3ad27 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,6 +14,7 @@ init: -P mingw-gcc-core \ -P mingw-pthreads \ -P mingw-w32api \ + -P mingw64-i686-gcc-core \ -P libtool \ -P make \ -P python \ @@ -52,7 +53,7 @@ configuration: Release install: - "%CYG_BASH% -lc 'cd $APPVEYOR_BUILD_FOLDER; ./autogen.sh'" - - '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0 Date: Fri, 27 Mar 2015 19:29:34 +0100 Subject: [PATCH 14/14] Only test criterion, not csptr --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index fe3ad27..cc59949 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -59,7 +59,7 @@ build_script: - '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0