Criterion/src/theories.c

242 lines
8.5 KiB
C

/*
* The MIT License (MIT)
*
* Copyright © 2015 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 <stdbool.h>
#include <stdarg.h>
#include <setjmp.h>
#include <dyncall.h>
#include <assert.h>
#include <limits.h>
#include "criterion/theories.h"
#include "abort.h"
struct criterion_theory_context {
DCCallVM* vm;
};
void cr_theory_push_arg(struct criterion_theory_context *ctx, bool is_float, size_t size, void *ptr) {
if (is_float) {
if (size == sizeof (float)) {
dcArgFloat(ctx->vm, *(float*)ptr);
} else if (size == sizeof (double)) {
dcArgDouble(ctx->vm, *(double*)ptr);
} else if (size == sizeof (long double)) {
dcArgDouble(ctx->vm, *(long double*)ptr);
}
} else {
if (size == sizeof (char)) {
dcArgChar(ctx->vm, *(char*)ptr);
} else if (size == sizeof (short)) {
dcArgShort(ctx->vm, *(short*)ptr);
} else if (size == sizeof (int)) {
dcArgInt(ctx->vm, *(int*)ptr);
} else if (size == sizeof (bool)) {
dcArgBool(ctx->vm, *(bool*)ptr);
#if INT_MAX < LONG_MAX
} else if (size == sizeof (long)) {
dcArgLong(ctx->vm, *(long*)ptr);
#endif
#if LONG_MAX < LLONG_MAX
} else if (size == sizeof (long long)) {
dcArgLongLong(ctx->vm, *(long long*)ptr);
#endif
} else if (size == sizeof (void*)) {
dcArgPointer(ctx->vm, *(void**)ptr);
} else {
dcArgPointer(ctx->vm, ptr);
}
}
}
struct criterion_theory_context* cr_theory_init(void) {
struct criterion_theory_context* ctx = malloc(sizeof (struct criterion_theory_context));
ctx->vm = dcNewCallVM(4096);
dcMode(ctx->vm, DC_CALL_C_DEFAULT);
return ctx;
}
void cr_theory_free(struct criterion_theory_context *ctx) {
dcFree(ctx->vm);
}
static jmp_buf theory_jmp;
void cr_theory_abort(void) {
longjmp(theory_jmp, 1);
}
int cr_theory_mark(void) {
return setjmp(theory_jmp);
}
void cr_theory_reset(struct criterion_theory_context *ctx) {
dcReset(ctx->vm);
}
void cr_theory_call(struct criterion_theory_context *ctx, void (*fnptr)(void)) {
dcCallVoid(ctx->vm, (DCpointer) fnptr);
}
static bool contains_word(const char *str, const char *pattern, size_t sz) {
char *res = strstr(str, pattern);
return res
&& (res == str || (res > str && res[-1] == ' '))
&& (!res[sz - 1] || res[sz - 1] == ' ');
}
static bool is_string(const char *name) {
return !strcmp(name, "char*")
|| !strcmp(name, "char *")
|| !strcmp(name, "const char*")
|| !strcmp(name, "const char *")
|| !strcmp(name, "char const *")
|| !strcmp(name, "char const*")
|| !strcmp(name, "char[]")
|| !strcmp(name, "char []")
|| !strcmp(name, "const char[]")
|| !strcmp(name, "const char []")
|| !strcmp(name, "char const[]")
|| !strcmp(name, "char const []");
}
static bool is_float(const char *name) {
return contains_word(name, "float", sizeof ("float"))
|| contains_word(name, "double", sizeof ("double"));
}
static bool is_unsigned_int(const char *name) {
return contains_word(name, "unsigned", sizeof ("unsigned"))
|| !strncmp(name, "uint", 4);
}
static bool is_bool(const char *name) {
return contains_word(name, "bool", sizeof ("bool"))
|| contains_word(name, "_Bool", sizeof ("_Bool"));
}
static void format_arg(char (*arg)[1024], struct criterion_datapoints *dp, void *data) {
if (is_float(dp->name)) {
if (dp->size == sizeof (float)) {
snprintf(*arg, sizeof (*arg) - 1, "%gf", *(float*) data);
} else if (dp->size == sizeof (double)) {
snprintf(*arg, sizeof (*arg) - 1, "%g", *(double*) data);
} else if (dp->size == sizeof (long double)) {
snprintf(*arg, sizeof (*arg) - 1, "%gl", (double) *(long double*) data);
}
} else {
if (is_string(dp->name)) {
snprintf(*arg, sizeof (*arg) - 1, "\"%s\"", *(char**) data);
} else if (dp->size == sizeof (bool) && is_bool(dp->name)) {
snprintf(*arg, sizeof (*arg) - 1, "%s", (*(bool*) data) ? "true" : "false");
} else if (dp->size == sizeof (char)) {
snprintf(*arg, sizeof (*arg) - 1, "'%c'", *(char*) data);
} else if (dp->size == sizeof (short)) {
const char *fmt = is_unsigned_int(dp->name) ? "%hu" : "%hd";
snprintf(*arg, sizeof (*arg) - 1, fmt, *(short*) data);
} else if (dp->size == sizeof (int)) {
const char *fmt = is_unsigned_int(dp->name) ? "%u" : "%d";
snprintf(*arg, sizeof (*arg) - 1, fmt, *(int*) data);
} else if (dp->size == sizeof (void*) && strstr(dp->name, "*")) {
snprintf(*arg, sizeof (*arg) - 1, "%p", *(void**) data);
#if INT_MAX < LONG_MAX
} else if (dp->size == sizeof (long)) {
const char *fmt = is_unsigned_int(dp->name) ? "%lulu" : "%ldl";
snprintf(*arg, sizeof (*arg) - 1, fmt, *(long*) data);
#endif
#if LONG_MAX < LLONG_MAX
} else if (dp->size == sizeof (long long)) {
const char *fmt = is_unsigned_int(dp->name) ? "%llullu" : "%lldll";
snprintf(*arg, sizeof (*arg) - 1, fmt, *(long long*) data);
#endif
} else {
snprintf(*arg, sizeof (*arg) - 1, "%s", "<np>");
}
}
}
static void concat_arg(char (*msg)[4096], struct criterion_datapoints *dps, size_t *indices, size_t i) {
void *data = ((char*) dps[i].arr) + dps[i].size * indices[i];
char arg[1024];
format_arg(&arg, dps + i, data);
strncat(*msg, arg, sizeof (*msg) - 1);
}
void cr_theory_main(struct criterion_datapoints *dps, size_t datapoints, void (*fnptr)(void)) {
struct criterion_theory_context *ctx = cr_theory_init();
size_t *indices = malloc(sizeof (size_t) * datapoints);
memset(indices, 0, datapoints * sizeof (size_t));
bool has_next = true;
while (has_next) {
if (!cr_theory_mark()) {
cr_theory_reset(ctx);
for (size_t i = 0; i < datapoints; ++i) {
bool is_float = contains_word(dps[i].name, "float", sizeof ("float"))
|| contains_word(dps[i].name, "double", sizeof ("double"));
cr_theory_push_arg(ctx,
is_float,
dps[i].size,
((char*) dps[i].arr) + dps[i].size * indices[i]);
}
jmp_buf backup;
memcpy(backup, g_pre_test, sizeof (jmp_buf));
if (!setjmp(g_pre_test)) {
cr_theory_call(ctx, fnptr);
} else {
struct {
size_t len;
char msg[4096];
} result = { .len = 0 };
for (size_t i = 0; i < datapoints - 1; ++i) {
concat_arg(&result.msg, dps, indices, i);
strncat(result.msg, ", ", sizeof (result.msg) - 1);
}
concat_arg(&result.msg, dps, indices, datapoints - 1);
result.len = strlen(result.msg) + 1;
send_event(THEORY_FAIL, &result, result.len + sizeof (size_t));
}
memcpy(g_pre_test, backup, sizeof (jmp_buf));
}
for (size_t i = 0; i < datapoints; ++i) {
if (indices[i] == dps[i].len - 1) {
indices[i] = 0;
has_next = i != (datapoints - 1);
} else {
++indices[i];
break;
}
}
}
free(indices);
cr_theory_free(ctx);
}