all repos — mgba @ 4edd7286f39fe940a890394626567211e072badb

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 "core/config.h"
  7#include "gba/context/context.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/memory.h"
 14#include "util/string.h"
 15#include "util/vfs.h"
 16
 17#include <errno.h>
 18#include <signal.h>
 19
 20#define FUZZ_OPTIONS "F:NO:S:V:"
 21#define FUZZ_USAGE \
 22	"\nAdditional options:\n" \
 23	"  -F FRAMES        Run for the specified number of FRAMES before exiting\n" \
 24	"  -N               Disable video rendering entirely\n" \
 25	"  -O OFFSET        Offset to apply savestate overlay\n" \
 26	"  -S FILE          Load a savestate when starting the test\n" \
 27	"  -V FILE          Overlay a second savestate over the loaded savestate\n" \
 28
 29struct FuzzOpts {
 30	bool noVideo;
 31	int frames;
 32	size_t overlayOffset;
 33	char* savestate;
 34	char* ssOverlay;
 35};
 36
 37static void _GBAFuzzRunloop(struct GBAContext* context, int frames);
 38static void _GBAFuzzShutdown(int signal);
 39static bool _parseFuzzOpts(struct SubParser* parser, struct mCoreConfig* config, int option, const char* arg);
 40
 41static bool _dispatchExiting = false;
 42
 43int main(int argc, char** argv) {
 44	signal(SIGINT, _GBAFuzzShutdown);
 45
 46	struct FuzzOpts fuzzOpts = { false, 0, 0, 0, 0 };
 47	struct SubParser subparser = {
 48		.usage = FUZZ_USAGE,
 49		.parse = _parseFuzzOpts,
 50		.extraOptions = FUZZ_OPTIONS,
 51		.opts = &fuzzOpts
 52	};
 53
 54	struct GBAContext context;
 55	GBAContextInit(&context, "fuzz");
 56	struct GBAOptions opts = {
 57		.idleOptimization = IDLE_LOOP_DETECT
 58	};
 59	mCoreConfigLoadDefaults(&context.config, &opts);
 60	mCoreConfigFreeOpts(&opts);
 61
 62	struct GBAArguments args;
 63	bool parsed = parseArguments(&args, &context.config, argc, argv, &subparser);
 64	if (!parsed || args.showHelp) {
 65		usage(argv[0], FUZZ_USAGE);
 66		freeArguments(&args);
 67		GBAContextDeinit(&context);
 68		return !parsed;
 69	}
 70	if (args.showVersion) {
 71		version(argv[0]);
 72		freeArguments(&args);
 73		GBAContextDeinit(&context);
 74		return 0;
 75	}
 76
 77	struct GBAVideoSoftwareRenderer renderer;
 78	renderer.outputBuffer = 0;
 79
 80	if (!fuzzOpts.noVideo) {
 81		GBAVideoSoftwareRendererCreate(&renderer);
 82		renderer.outputBuffer = malloc(256 * 256 * 4);
 83		renderer.outputBufferStride = 256;
 84		context.renderer = &renderer.d;
 85	}
 86
 87#ifdef __AFL_HAVE_MANUAL_CONTROL
 88	__AFL_INIT();
 89#endif
 90
 91	struct VFile* rom = VFileOpen(args.fname, O_RDONLY);
 92
 93	context.gba->hardCrash = false;
 94	GBAContextLoadROMFromVFile(&context, rom, 0);
 95
 96	struct VFile* savestate = 0;
 97	struct VFile* savestateOverlay = 0;
 98	size_t overlayOffset;
 99
100	GBAContextStart(&context);
101
102	if (fuzzOpts.savestate) {
103		savestate = VFileOpen(fuzzOpts.savestate, O_RDONLY);
104		free(fuzzOpts.savestate);
105	}
106	if (fuzzOpts.ssOverlay) {
107		overlayOffset = fuzzOpts.overlayOffset;
108		if (overlayOffset < sizeof(struct GBASerializedState)) {
109			savestateOverlay = VFileOpen(fuzzOpts.ssOverlay, O_RDONLY);
110		}
111		free(fuzzOpts.ssOverlay);
112	}
113	if (savestate) {
114		if (!savestateOverlay) {
115			GBALoadStateNamed(context.gba, savestate, 0);
116		} else {
117			struct GBASerializedState* state = GBAAllocateState();
118			savestate->read(savestate, state, sizeof(*state));
119			savestateOverlay->read(savestateOverlay, (uint8_t*) state + overlayOffset, sizeof(*state) - overlayOffset);
120			GBADeserialize(context.gba, state);
121			GBADeallocateState(state);
122			savestateOverlay->close(savestateOverlay);
123			savestateOverlay = 0;
124		}
125		savestate->close(savestate);
126		savestate = 0;
127	}
128
129	blip_set_rates(context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 0x8000);
130	blip_set_rates(context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 0x8000);
131
132	_GBAFuzzRunloop(&context, fuzzOpts.frames);
133
134	GBAContextStop(&context);
135	GBAContextUnloadROM(&context);
136
137	if (savestate) {
138		savestate->close(savestate);
139	}
140	if (savestateOverlay) {
141		savestateOverlay->close(savestateOverlay);
142	}
143
144	freeArguments(&args);
145	if (renderer.outputBuffer) {
146		free(renderer.outputBuffer);
147	}
148	GBAContextDeinit(&context);
149
150	return 0;
151}
152
153static void _GBAFuzzRunloop(struct GBAContext* context, int frames) {
154	do {
155		GBAContextFrame(context, 0);
156	} while (context->gba->video.frameCounter < frames && !_dispatchExiting);
157}
158
159static void _GBAFuzzShutdown(int signal) {
160	UNUSED(signal);
161	_dispatchExiting = true;
162}
163
164static bool _parseFuzzOpts(struct SubParser* parser, struct mCoreConfig* config, int option, const char* arg) {
165	UNUSED(config);
166	struct FuzzOpts* opts = parser->opts;
167	errno = 0;
168	switch (option) {
169	case 'F':
170		opts->frames = strtoul(arg, 0, 10);
171		return !errno;
172	case 'N':
173		opts->noVideo = true;
174		return true;
175	case 'O':
176		opts->overlayOffset = strtoul(arg, 0, 10);
177		return !errno;
178	case 'S':
179		opts->savestate = strdup(arg);
180		return true;
181	case 'V':
182		opts->ssOverlay = strdup(arg);
183		return true;
184	default:
185		return false;
186	}
187}