all repos — mgba @ 82a0088e1ec4a1fe13f9eff96c9c72d22995013e

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/cheats.h"
  7#include "core/config.h"
  8#include "core/core.h"
  9#include "core/serialize.h"
 10#include "gb/core.h"
 11#include "gba/gba.h"
 12
 13#include "feature/commandline.h"
 14#include "util/memory.h"
 15#include "util/string.h"
 16#include "util/vfs.h"
 17
 18#include <errno.h>
 19#include <signal.h>
 20
 21#define FUZZ_OPTIONS "F:NO:S:V:"
 22#define FUZZ_USAGE \
 23	"\nAdditional options:\n" \
 24	"  -F FRAMES        Run for the specified number of FRAMES before exiting\n" \
 25	"  -N               Disable video rendering entirely\n" \
 26	"  -O OFFSET        Offset to apply savestate overlay\n" \
 27	"  -S FILE          Load a savestate when starting the test\n" \
 28	"  -V FILE          Overlay a second savestate over the loaded savestate\n" \
 29
 30struct FuzzOpts {
 31	bool noVideo;
 32	int frames;
 33	size_t overlayOffset;
 34	char* savestate;
 35	char* ssOverlay;
 36};
 37
 38static void _fuzzRunloop(struct mCore* core, int frames);
 39static void _fuzzShutdown(int signal);
 40static bool _parseFuzzOpts(struct mSubParser* parser, int option, const char* arg);
 41
 42static bool _dispatchExiting = false;
 43
 44int main(int argc, char** argv) {
 45	signal(SIGINT, _fuzzShutdown);
 46
 47	struct FuzzOpts fuzzOpts = { false, 0, 0, 0, 0 };
 48	struct mSubParser subparser = {
 49		.usage = FUZZ_USAGE,
 50		.parse = _parseFuzzOpts,
 51		.extraOptions = FUZZ_OPTIONS,
 52		.opts = &fuzzOpts
 53	};
 54
 55	struct mArguments args;
 56	bool parsed = parseArguments(&args, argc, argv, &subparser);
 57	if (!args.fname) {
 58		parsed = false;
 59	}
 60	if (!parsed || args.showHelp) {
 61		usage(argv[0], FUZZ_USAGE);
 62		return !parsed;
 63	}
 64	if (args.showVersion) {
 65		version(argv[0]);
 66		return 0;
 67	}
 68	struct mCore* core = mCoreFind(args.fname);
 69	core->init(core);
 70	mCoreInitConfig(core, "fuzz");
 71	applyArguments(&args, NULL, &core->config);
 72
 73	mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "remove");
 74
 75	void* outputBuffer;
 76	outputBuffer = 0;
 77
 78	if (!fuzzOpts.noVideo) {
 79		outputBuffer = malloc(256 * 256 * 4);
 80		core->setVideoBuffer(core, outputBuffer, 256);
 81	}
 82
 83#ifdef M_CORE_GBA
 84	if (core->platform(core) == PLATFORM_GBA) {
 85		((struct GBA*) core->board)->hardCrash = false;
 86	}
 87#endif
 88
 89#ifdef __AFL_HAVE_MANUAL_CONTROL
 90	__AFL_INIT();
 91#endif
 92	if (args.patch) {
 93		core->loadPatch(core, VFileOpen(args.patch, O_RDONLY));
 94	}
 95	mCoreLoadFile(core, args.fname);
 96
 97	struct VFile* savestate = 0;
 98	struct VFile* savestateOverlay = 0;
 99	size_t overlayOffset;
100
101	if (fuzzOpts.savestate) {
102		savestate = VFileOpen(fuzzOpts.savestate, O_RDONLY);
103		free(fuzzOpts.savestate);
104	}
105	if (fuzzOpts.ssOverlay) {
106		overlayOffset = fuzzOpts.overlayOffset;
107		if (overlayOffset < core->stateSize(core)) {
108			savestateOverlay = VFileOpen(fuzzOpts.ssOverlay, O_RDONLY);
109		}
110		free(fuzzOpts.ssOverlay);
111	}
112
113	core->reset(core);
114
115	struct mCheatDevice* device;
116	if (args.cheatsFile && (device = core->cheatDevice(core))) {
117		struct VFile* vf = VFileOpen(args.cheatsFile, O_RDONLY);
118		if (vf) {
119			mCheatDeviceClear(device);
120			mCheatParseFile(device, vf);
121			vf->close(vf);
122		}
123	}
124
125	if (savestate) {
126		if (!savestateOverlay) {
127			mCoreLoadStateNamed(core, savestate, 0);
128		} else {
129			size_t size = core->stateSize(core);
130			uint8_t* state = malloc(size);
131			savestate->read(savestate, state, size);
132			savestateOverlay->read(savestateOverlay, state + overlayOffset, size - overlayOffset);
133			core->loadState(core, state);
134			free(state);
135			savestateOverlay->close(savestateOverlay);
136			savestateOverlay = 0;
137		}
138		savestate->close(savestate);
139		savestate = 0;
140	}
141
142	blip_set_rates(core->getAudioChannel(core, 0), GBA_ARM7TDMI_FREQUENCY, 0x8000);
143	blip_set_rates(core->getAudioChannel(core, 1), GBA_ARM7TDMI_FREQUENCY, 0x8000);
144
145	_fuzzRunloop(core, fuzzOpts.frames);
146
147	core->unloadROM(core);
148
149	if (savestate) {
150		savestate->close(savestate);
151	}
152	if (savestateOverlay) {
153		savestateOverlay->close(savestateOverlay);
154	}
155
156	freeArguments(&args);
157	if (outputBuffer) {
158		free(outputBuffer);
159	}
160	core->deinit(core);
161
162	return 0;
163}
164
165static void _fuzzRunloop(struct mCore* core, int frames) {
166	do {
167		core->runFrame(core);
168		blip_clear(core->getAudioChannel(core, 0));
169		blip_clear(core->getAudioChannel(core, 1));
170	} while (core->frameCounter(core) < frames && !_dispatchExiting);
171}
172
173static void _fuzzShutdown(int signal) {
174	UNUSED(signal);
175	_dispatchExiting = true;
176}
177
178static bool _parseFuzzOpts(struct mSubParser* parser, int option, const char* arg) {
179	struct FuzzOpts* opts = parser->opts;
180	errno = 0;
181	switch (option) {
182	case 'F':
183		opts->frames = strtoul(arg, 0, 10);
184		return !errno;
185	case 'N':
186		opts->noVideo = true;
187		return true;
188	case 'O':
189		opts->overlayOffset = strtoul(arg, 0, 10);
190		return !errno;
191	case 'S':
192		opts->savestate = strdup(arg);
193		return true;
194	case 'V':
195		opts->ssOverlay = strdup(arg);
196		return true;
197	default:
198		return false;
199	}
200}