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_SDL ON CACHE BOOL "Build SDL frontend")
|
||||||
set(BUILD_LIBRETRO OFF CACHE BOOL "Build libretro core")
|
set(BUILD_LIBRETRO OFF CACHE BOOL "Build libretro core")
|
||||||
set(BUILD_PERF OFF CACHE BOOL "Build performance profiling tool")
|
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_STATIC OFF CACHE BOOL "Build a static library")
|
||||||
set(BUILD_SHARED ON CACHE BOOL "Build a shared library")
|
set(BUILD_SHARED ON CACHE BOOL "Build a shared library")
|
||||||
set(BUILD_GL ON CACHE STRING "Build with OpenGL")
|
set(BUILD_GL ON CACHE STRING "Build with OpenGL")
|
||||||
|
@ -435,7 +436,7 @@ if(BUILD_QT)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(BUILD_PERF)
|
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)
|
if(UNIX AND NOT APPLE)
|
||||||
list(APPEND PERF_LIB rt)
|
list(APPEND PERF_LIB rt)
|
||||||
endif()
|
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)
|
install(FILES ${CMAKE_SOURCE_DIR}/tools/perf.py DESTINATION "${CMAKE_INSTALL_LIBDIR}/${BINARY_NAME}" COMPONENT ${BINARY_NAME}-perf)
|
||||||
endif()
|
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
|
# Packaging
|
||||||
set(CPACK_PACKAGE_VERSION ${VERSION_STRING})
|
set(CPACK_PACKAGE_VERSION ${VERSION_STRING})
|
||||||
set(CPACK_PACKAGE_VERSION_MAJOR ${LIB_VERSION_MAJOR})
|
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 " SDL (${SDL_VERSION}): ${BUILD_SDL}")
|
||||||
message(STATUS " Libretro core: ${BUILD_LIBRETRO}")
|
message(STATUS " Libretro core: ${BUILD_LIBRETRO}")
|
||||||
message(STATUS " Profiling: ${BUILD_PERF}")
|
message(STATUS " Profiling: ${BUILD_PERF}")
|
||||||
|
message(STATUS " Test harness: ${BUILD_TEST}")
|
||||||
message(STATUS "Library summary:")
|
message(STATUS "Library summary:")
|
||||||
message(STATUS " Static: ${BUILD_STATIC}")
|
message(STATUS " Static: ${BUILD_STATIC}")
|
||||||
message(STATUS " Shared: ${BUILD_SHARED}")
|
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