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.psg.left, GBA_ARM7TDMI_FREQUENCY, 0x8000);
130 blip_set_rates(context.gba->audio.psg.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}