mirror of https://github.com/mgba-emu/mgba.git
Test: Add fuzzing harness and move perf-main into test folder
This commit is contained in:
parent
3b61005f2e
commit
4d79fd7324
|
@ -18,6 +18,7 @@ set(BUILD_QT ON CACHE BOOL "Build Qt frontend")
|
|||
set(BUILD_SDL ON CACHE BOOL "Build SDL frontend")
|
||||
set(BUILD_LIBRETRO OFF CACHE BOOL "Build libretro core")
|
||||
set(BUILD_PERF OFF CACHE BOOL "Build performance profiling tool")
|
||||
set(BUILD_TEST OFF CACHE BOOL "Build testing harness")
|
||||
set(BUILD_STATIC OFF CACHE BOOL "Build a static library")
|
||||
set(BUILD_SHARED ON CACHE BOOL "Build a shared library")
|
||||
set(BUILD_GL ON CACHE STRING "Build with OpenGL")
|
||||
|
@ -435,7 +436,7 @@ if(BUILD_QT)
|
|||
endif()
|
||||
|
||||
if(BUILD_PERF)
|
||||
set(PERF_SRC ${CMAKE_SOURCE_DIR}/src/platform/perf-main.c)
|
||||
set(PERF_SRC ${CMAKE_SOURCE_DIR}/src/platform/test/perf-main.c)
|
||||
if(UNIX AND NOT APPLE)
|
||||
list(APPEND PERF_LIB rt)
|
||||
endif()
|
||||
|
@ -446,6 +447,12 @@ if(BUILD_PERF)
|
|||
install(FILES ${CMAKE_SOURCE_DIR}/tools/perf.py DESTINATION "${CMAKE_INSTALL_LIBDIR}/${BINARY_NAME}" COMPONENT ${BINARY_NAME}-perf)
|
||||
endif()
|
||||
|
||||
if(BUILD_TEST)
|
||||
add_executable(${BINARY_NAME}-fuzz ${CMAKE_SOURCE_DIR}/src/platform/test/fuzz-main.c)
|
||||
target_link_libraries(${BINARY_NAME}-fuzz ${BINARY_NAME})
|
||||
install(TARGETS ${BINARY_NAME}-fuzz DESTINATION bin COMPONENT ${BINARY_NAME}-test)
|
||||
endif()
|
||||
|
||||
# Packaging
|
||||
set(CPACK_PACKAGE_VERSION ${VERSION_STRING})
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR ${LIB_VERSION_MAJOR})
|
||||
|
@ -483,6 +490,7 @@ message(STATUS " Qt: ${BUILD_QT}")
|
|||
message(STATUS " SDL (${SDL_VERSION}): ${BUILD_SDL}")
|
||||
message(STATUS " Libretro core: ${BUILD_LIBRETRO}")
|
||||
message(STATUS " Profiling: ${BUILD_PERF}")
|
||||
message(STATUS " Test harness: ${BUILD_TEST}")
|
||||
message(STATUS "Library summary:")
|
||||
message(STATUS " Static: ${BUILD_STATIC}")
|
||||
message(STATUS " Shared: ${BUILD_SHARED}")
|
||||
|
|
|
@ -0,0 +1,210 @@
|
|||
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "gba/supervisor/thread.h"
|
||||
#include "gba/supervisor/config.h"
|
||||
#include "gba/gba.h"
|
||||
#include "gba/renderers/video-software.h"
|
||||
#include "gba/serialize.h"
|
||||
|
||||
#include "platform/commandline.h"
|
||||
#include "util/string.h"
|
||||
#include "util/vfs.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define FUZZ_OPTIONS "F:NO:S:V:"
|
||||
#define FUZZ_USAGE \
|
||||
"\nAdditional options:\n" \
|
||||
" -F FRAMES Run for the specified number of FRAMES before exiting\n" \
|
||||
" -N Disable video rendering entirely\n" \
|
||||
" -O OFFSET Offset to apply savestate overlay\n" \
|
||||
" -S FILE Load a savestate when starting the test\n" \
|
||||
" -V FILE Overlay a second savestate over the loaded savestate\n" \
|
||||
|
||||
struct FuzzOpts {
|
||||
bool noVideo;
|
||||
unsigned frames;
|
||||
size_t overlayOffset;
|
||||
char* savestate;
|
||||
char* ssOverlay;
|
||||
};
|
||||
|
||||
static void _GBAFuzzRunloop(struct GBAThread* context, unsigned frames);
|
||||
static void _GBAFuzzShutdown(int signal);
|
||||
static bool _parseFuzzOpts(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg);
|
||||
static void _loadSavestate(struct GBAThread* context);
|
||||
|
||||
static struct GBAThread* _thread;
|
||||
static bool _dispatchExiting = false;
|
||||
static struct VFile* _savestate = 0;
|
||||
static struct VFile* _savestateOverlay = 0;
|
||||
static size_t _overlayOffset;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
signal(SIGINT, _GBAFuzzShutdown);
|
||||
|
||||
|
||||
struct FuzzOpts fuzzOpts = { false, 0, 0, 0, 0 };
|
||||
struct SubParser subparser = {
|
||||
.usage = FUZZ_USAGE,
|
||||
.parse = _parseFuzzOpts,
|
||||
.extraOptions = FUZZ_OPTIONS,
|
||||
.opts = &fuzzOpts
|
||||
};
|
||||
|
||||
struct GBAConfig config;
|
||||
GBAConfigInit(&config, "fuzz");
|
||||
GBAConfigLoad(&config);
|
||||
|
||||
struct GBAOptions opts = {
|
||||
.idleOptimization = IDLE_LOOP_DETECT
|
||||
};
|
||||
GBAConfigLoadDefaults(&config, &opts);
|
||||
|
||||
struct GBAArguments args;
|
||||
bool parsed = parseArguments(&args, &config, argc, argv, &subparser);
|
||||
if (!parsed || args.showHelp) {
|
||||
usage(argv[0], FUZZ_USAGE);
|
||||
freeArguments(&args);
|
||||
GBAConfigFreeOpts(&opts);
|
||||
GBAConfigDeinit(&config);
|
||||
return !parsed;
|
||||
}
|
||||
|
||||
struct GBAVideoSoftwareRenderer renderer;
|
||||
renderer.outputBuffer = 0;
|
||||
|
||||
struct GBAThread context = {};
|
||||
_thread = &context;
|
||||
|
||||
if (!fuzzOpts.noVideo) {
|
||||
GBAVideoSoftwareRendererCreate(&renderer);
|
||||
renderer.outputBuffer = malloc(256 * 256 * 4);
|
||||
renderer.outputBufferStride = 256;
|
||||
context.renderer = &renderer.d;
|
||||
}
|
||||
if (fuzzOpts.savestate) {
|
||||
_savestate = VFileOpen(fuzzOpts.savestate, O_RDONLY);
|
||||
free(fuzzOpts.savestate);
|
||||
}
|
||||
if (fuzzOpts.ssOverlay) {
|
||||
_overlayOffset = fuzzOpts.overlayOffset;
|
||||
if (_overlayOffset < sizeof(struct GBASerializedState)) {
|
||||
_savestateOverlay = VFileOpen(fuzzOpts.ssOverlay, O_RDONLY);
|
||||
}
|
||||
free(fuzzOpts.ssOverlay);
|
||||
}
|
||||
if (_savestate) {
|
||||
context.startCallback = _loadSavestate;
|
||||
}
|
||||
|
||||
context.debugger = createDebugger(&args, &context);
|
||||
context.overrides = GBAConfigGetOverrides(&config);
|
||||
|
||||
GBAConfigMap(&config, &opts);
|
||||
opts.audioSync = false;
|
||||
opts.videoSync = false;
|
||||
GBAMapArgumentsToContext(&args, &context);
|
||||
GBAMapOptionsToContext(&opts, &context);
|
||||
|
||||
int didStart = GBAThreadStart(&context);
|
||||
|
||||
if (!didStart) {
|
||||
goto cleanup;
|
||||
}
|
||||
GBAThreadInterrupt(&context);
|
||||
if (GBAThreadHasCrashed(&context)) {
|
||||
GBAThreadJoin(&context);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
GBAThreadContinue(&context);
|
||||
|
||||
_GBAFuzzRunloop(&context, fuzzOpts.frames);
|
||||
GBAThreadJoin(&context);
|
||||
|
||||
cleanup:
|
||||
if (_savestate) {
|
||||
_savestate->close(_savestate);
|
||||
}
|
||||
if (_savestateOverlay) {
|
||||
_savestateOverlay->close(_savestateOverlay);
|
||||
}
|
||||
GBAConfigFreeOpts(&opts);
|
||||
freeArguments(&args);
|
||||
GBAConfigDeinit(&config);
|
||||
free(context.debugger);
|
||||
if (renderer.outputBuffer) {
|
||||
free(renderer.outputBuffer);
|
||||
}
|
||||
|
||||
return !didStart || GBAThreadHasCrashed(&context);
|
||||
}
|
||||
|
||||
static void _GBAFuzzRunloop(struct GBAThread* context, unsigned duration) {
|
||||
unsigned frames = 0;
|
||||
while (context->state < THREAD_EXITING) {
|
||||
if (GBASyncWaitFrameStart(&context->sync, 0)) {
|
||||
++frames;
|
||||
}
|
||||
GBASyncWaitFrameEnd(&context->sync);
|
||||
if (frames >= duration) {
|
||||
_GBAFuzzShutdown(0);
|
||||
}
|
||||
if (_dispatchExiting) {
|
||||
GBAThreadEnd(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _GBAFuzzShutdown(int signal) {
|
||||
UNUSED(signal);
|
||||
// This will come in ON the GBA thread, so we have to handle it carefully
|
||||
_dispatchExiting = true;
|
||||
ConditionWake(&_thread->sync.videoFrameAvailableCond);
|
||||
}
|
||||
|
||||
static bool _parseFuzzOpts(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg) {
|
||||
UNUSED(config);
|
||||
struct FuzzOpts* opts = parser->opts;
|
||||
errno = 0;
|
||||
switch (option) {
|
||||
case 'F':
|
||||
opts->frames = strtoul(arg, 0, 10);
|
||||
return !errno;
|
||||
case 'N':
|
||||
opts->noVideo = true;
|
||||
return true;
|
||||
case 'O':
|
||||
opts->overlayOffset = strtoul(arg, 0, 10);
|
||||
return !errno;
|
||||
case 'S':
|
||||
opts->savestate = strdup(arg);
|
||||
return true;
|
||||
case 'V':
|
||||
opts->ssOverlay = strdup(arg);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void _loadSavestate(struct GBAThread* context) {
|
||||
if (!_savestateOverlay) {
|
||||
GBALoadStateNamed(context->gba, _savestate);
|
||||
} else {
|
||||
struct GBASerializedState* state = GBAAllocateState();
|
||||
_savestate->read(_savestate, state, sizeof(*state));
|
||||
_savestateOverlay->read(_savestateOverlay, (uint8_t*) state + _overlayOffset, sizeof(*state) - _overlayOffset);
|
||||
GBADeserialize(context->gba, state);
|
||||
GBADeallocateState(state);
|
||||
_savestateOverlay->close(_savestateOverlay);
|
||||
_savestateOverlay = 0;
|
||||
}
|
||||
_savestate->close(_savestate);
|
||||
_savestate = 0;
|
||||
}
|
Loading…
Reference in New Issue