all repos — mgba @ 0cdc9ff32887e57767fac74ea12c8616025583bd

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/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 GBA* gba, int frames);
 38static void _GBAFuzzShutdown(int signal);
 39static bool _parseFuzzOpts(struct SubParser* parser, struct GBAConfig* 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 GBAConfig config;
 55	GBAConfigInit(&config, "fuzz");
 56	GBAConfigLoad(&config);
 57
 58	struct GBAOptions opts = {
 59		.idleOptimization = IDLE_LOOP_DETECT
 60	};
 61	GBAConfigLoadDefaults(&config, &opts);
 62
 63	struct GBAArguments args;
 64	bool parsed = parseArguments(&args, &config, argc, argv, &subparser);
 65	if (!parsed || args.showHelp) {
 66		usage(argv[0], FUZZ_USAGE);
 67		freeArguments(&args);
 68		GBAConfigFreeOpts(&opts);
 69		GBAConfigDeinit(&config);
 70		return !parsed;
 71	}
 72
 73	struct GBA* gba = anonymousMemoryMap(sizeof(struct GBA));
 74	struct ARMCore* cpu = anonymousMemoryMap(sizeof(struct ARMCore));
 75	struct VFile* rom = VFileOpen(args.fname, O_RDONLY);
 76
 77	GBACreate(gba);
 78	ARMSetComponents(cpu, &gba->d, 0, 0);
 79	ARMInit(cpu);
 80	gba->sync = 0;
 81	gba->hardCrash = false;
 82
 83	GBALoadROM(gba, rom, 0, 0);
 84	ARMReset(cpu);
 85
 86	struct GBACartridgeOverride override;
 87	const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom;
 88	memcpy(override.id, &cart->id, sizeof(override.id));
 89	if (GBAOverrideFind(GBAConfigGetOverrides(&config), &override)) {
 90		GBAOverrideApply(gba, &override);
 91	}
 92
 93	struct GBAVideoSoftwareRenderer renderer;
 94	renderer.outputBuffer = 0;
 95
 96	struct VFile* savestate = 0;
 97	struct VFile* savestateOverlay = 0;
 98	size_t overlayOffset;
 99
100	if (!fuzzOpts.noVideo) {
101		GBAVideoSoftwareRendererCreate(&renderer);
102		renderer.outputBuffer = malloc(256 * 256 * 4);
103		renderer.outputBufferStride = 256;
104		GBAVideoAssociateRenderer(&gba->video, &renderer.d);
105	}
106
107	if (fuzzOpts.savestate) {
108		savestate = VFileOpen(fuzzOpts.savestate, O_RDONLY);
109		free(fuzzOpts.savestate);
110	}
111	if (fuzzOpts.ssOverlay) {
112		overlayOffset = fuzzOpts.overlayOffset;
113		if (overlayOffset < sizeof(struct GBASerializedState)) {
114			savestateOverlay = VFileOpen(fuzzOpts.ssOverlay, O_RDONLY);
115		}
116		free(fuzzOpts.ssOverlay);
117	}
118	if (savestate) {
119		if (!savestateOverlay) {
120			GBALoadStateNamed(gba, savestate);
121		} else {
122			struct GBASerializedState* state = GBAAllocateState();
123			savestate->read(savestate, state, sizeof(*state));
124			savestateOverlay->read(savestateOverlay, (uint8_t*) state + overlayOffset, sizeof(*state) - overlayOffset);
125			GBADeserialize(gba, state);
126			GBADeallocateState(state);
127			savestateOverlay->close(savestateOverlay);
128			savestateOverlay = 0;
129		}
130		savestate->close(savestate);
131		savestate = 0;
132	}
133
134	GBAConfigMap(&config, &opts);
135
136	blip_set_rates(gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 0x8000);
137	blip_set_rates(gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 0x8000);
138
139	_GBAFuzzRunloop(gba, fuzzOpts.frames);
140
141	if (savestate) {
142		savestate->close(savestate);
143	}
144	if (savestateOverlay) {
145		savestateOverlay->close(savestateOverlay);
146	}
147	GBAConfigFreeOpts(&opts);
148	freeArguments(&args);
149	GBAConfigDeinit(&config);
150	if (renderer.outputBuffer) {
151		free(renderer.outputBuffer);
152	}
153
154	return 0;
155}
156
157static void _GBAFuzzRunloop(struct GBA* gba, int frames) {
158	do {
159		ARMRunLoop(gba->cpu);
160	} while (gba->video.frameCounter < frames && !_dispatchExiting);
161}
162
163static void _GBAFuzzShutdown(int signal) {
164	UNUSED(signal);
165	_dispatchExiting = true;
166}
167
168static bool _parseFuzzOpts(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg) {
169	UNUSED(config);
170	struct FuzzOpts* opts = parser->opts;
171	errno = 0;
172	switch (option) {
173	case 'F':
174		opts->frames = strtoul(arg, 0, 10);
175		return !errno;
176	case 'N':
177		opts->noVideo = true;
178		return true;
179	case 'O':
180		opts->overlayOffset = strtoul(arg, 0, 10);
181		return !errno;
182	case 'S':
183		opts->savestate = strdup(arg);
184		return true;
185	case 'V':
186		opts->ssOverlay = strdup(arg);
187		return true;
188	default:
189		return false;
190	}
191}