all repos — mgba @ 1f899737d9825f60e6713fed4ff6d368dda32e56

mGBA Game Boy Advance Emulator

src/platform/test/fuzz-main.c (view raw)

  1/* Copyright (c) 2013-2015 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include "gba/supervisor/thread.h"
  7#include "gba/supervisor/config.h"
  8#include "gba/gba.h"
  9#include "gba/renderers/video-software.h"
 10#include "gba/serialize.h"
 11
 12#include "platform/commandline.h"
 13#include "util/string.h"
 14#include "util/vfs.h"
 15
 16#include <errno.h>
 17#include <signal.h>
 18
 19#define FUZZ_OPTIONS "F:NO:S:V:"
 20#define FUZZ_USAGE \
 21	"\nAdditional options:\n" \
 22	"  -F FRAMES        Run for the specified number of FRAMES before exiting\n" \
 23	"  -N               Disable video rendering entirely\n" \
 24	"  -O OFFSET        Offset to apply savestate overlay\n" \
 25	"  -S FILE          Load a savestate when starting the test\n" \
 26	"  -V FILE          Overlay a second savestate over the loaded savestate\n" \
 27
 28struct FuzzOpts {
 29	bool noVideo;
 30	unsigned frames;
 31	size_t overlayOffset;
 32	char* savestate;
 33	char* ssOverlay;
 34};
 35
 36static void _GBAFuzzRunloop(struct GBAThread* context, unsigned frames);
 37static void _GBAFuzzShutdown(int signal);
 38static bool _parseFuzzOpts(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg);
 39static void _loadSavestate(struct GBAThread* context);
 40
 41static struct GBAThread* _thread;
 42static bool _dispatchExiting = false;
 43static struct VFile* _savestate = 0;
 44static struct VFile* _savestateOverlay = 0;
 45static size_t _overlayOffset;
 46
 47int main(int argc, char** argv) {
 48	signal(SIGINT, _GBAFuzzShutdown);
 49
 50
 51	struct FuzzOpts fuzzOpts = { false, 0, 0, 0, 0 };
 52	struct SubParser subparser = {
 53		.usage = FUZZ_USAGE,
 54		.parse = _parseFuzzOpts,
 55		.extraOptions = FUZZ_OPTIONS,
 56		.opts = &fuzzOpts
 57	};
 58
 59	struct GBAConfig config;
 60	GBAConfigInit(&config, "fuzz");
 61	GBAConfigLoad(&config);
 62
 63	struct GBAOptions opts = {
 64		.idleOptimization = IDLE_LOOP_DETECT
 65	};
 66	GBAConfigLoadDefaults(&config, &opts);
 67
 68	struct GBAArguments args;
 69	bool parsed = parseArguments(&args, &config, argc, argv, &subparser);
 70	if (!parsed || args.showHelp) {
 71		usage(argv[0], FUZZ_USAGE);
 72		freeArguments(&args);
 73		GBAConfigFreeOpts(&opts);
 74		GBAConfigDeinit(&config);
 75		return !parsed;
 76	}
 77
 78	struct GBAVideoSoftwareRenderer renderer;
 79	renderer.outputBuffer = 0;
 80
 81	struct GBAThread context = {};
 82	_thread = &context;
 83
 84	if (!fuzzOpts.noVideo) {
 85		GBAVideoSoftwareRendererCreate(&renderer);
 86		renderer.outputBuffer = malloc(256 * 256 * 4);
 87		renderer.outputBufferStride = 256;
 88		context.renderer = &renderer.d;
 89	}
 90	if (fuzzOpts.savestate) {
 91		_savestate = VFileOpen(fuzzOpts.savestate, O_RDONLY);
 92		free(fuzzOpts.savestate);
 93	}
 94	if (fuzzOpts.ssOverlay) {
 95		_overlayOffset = fuzzOpts.overlayOffset;
 96		if (_overlayOffset < sizeof(struct GBASerializedState)) {
 97			_savestateOverlay = VFileOpen(fuzzOpts.ssOverlay, O_RDONLY);
 98		}
 99		free(fuzzOpts.ssOverlay);
100	}
101	if (_savestate) {
102		context.startCallback = _loadSavestate;
103	}
104
105	context.debugger = createDebugger(&args, &context);
106	context.overrides = GBAConfigGetOverrides(&config);
107
108	GBAConfigMap(&config, &opts);
109	opts.audioSync = false;
110	opts.videoSync = false;
111	GBAMapArgumentsToContext(&args, &context);
112	GBAMapOptionsToContext(&opts, &context);
113
114	int didStart = GBAThreadStart(&context);
115
116	if (!didStart) {
117		goto cleanup;
118	}
119	GBAThreadInterrupt(&context);
120	if (GBAThreadHasCrashed(&context)) {
121		GBAThreadJoin(&context);
122		goto cleanup;
123	}
124
125	GBAThreadContinue(&context);
126
127	_GBAFuzzRunloop(&context, fuzzOpts.frames);
128	GBAThreadJoin(&context);
129
130cleanup:
131	if (_savestate) {
132		_savestate->close(_savestate);
133	}
134	if (_savestateOverlay) {
135		_savestateOverlay->close(_savestateOverlay);
136	}
137	GBAConfigFreeOpts(&opts);
138	freeArguments(&args);
139	GBAConfigDeinit(&config);
140	free(context.debugger);
141	if (renderer.outputBuffer) {
142		free(renderer.outputBuffer);
143	}
144
145	return !didStart || GBAThreadHasCrashed(&context);
146}
147
148static void _GBAFuzzRunloop(struct GBAThread* context, unsigned duration) {
149	unsigned frames = 0;
150	while (context->state < THREAD_EXITING) {
151		if (GBASyncWaitFrameStart(&context->sync, 0)) {
152			++frames;
153		}
154		GBASyncWaitFrameEnd(&context->sync);
155		if (frames >= duration) {
156			_GBAFuzzShutdown(0);
157		}
158		if (_dispatchExiting) {
159			GBAThreadEnd(context);
160		}
161	}
162}
163
164static void _GBAFuzzShutdown(int signal) {
165	UNUSED(signal);
166	// This will come in ON the GBA thread, so we have to handle it carefully
167	_dispatchExiting = true;
168	ConditionWake(&_thread->sync.videoFrameAvailableCond);
169}
170
171static bool _parseFuzzOpts(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg) {
172	UNUSED(config);
173	struct FuzzOpts* opts = parser->opts;
174	errno = 0;
175	switch (option) {
176	case 'F':
177		opts->frames = strtoul(arg, 0, 10);
178		return !errno;
179	case 'N':
180		opts->noVideo = true;
181		return true;
182	case 'O':
183		opts->overlayOffset = strtoul(arg, 0, 10);
184		return !errno;
185	case 'S':
186		opts->savestate = strdup(arg);
187		return true;
188	case 'V':
189		opts->ssOverlay = strdup(arg);
190		return true;
191	default:
192		return false;
193	}
194}
195
196static void _loadSavestate(struct GBAThread* context) {
197	if (!_savestateOverlay) {
198		GBALoadStateNamed(context->gba, _savestate);
199	} else {
200		struct GBASerializedState* state = GBAAllocateState();
201		_savestate->read(_savestate, state, sizeof(*state));
202		_savestateOverlay->read(_savestateOverlay, (uint8_t*) state + _overlayOffset, sizeof(*state) - _overlayOffset);
203		GBADeserialize(context->gba, state);
204		GBADeallocateState(state);
205		_savestateOverlay->close(_savestateOverlay);
206		_savestateOverlay = 0;
207	}
208	_savestate->close(_savestate);
209	_savestate = 0;
210}