Fuzzer improvements.
phase_of_moon and friday_13th determined using rn2() instead of local time if fuzzing. Don't reseed using init_random() if fuzzing. Allow set_random to be called outside of hacklib. rn2_on_display_rng uses rn2 if fuzzing so that we have a single source of random that we can ensure is reproducible. Implement rul() that returns a random unsigned long. Fix bug in fuzzer handling of ntposkey which would cause us to use unitialized values for x and y. Added command line arguments to allow auto starting and stopping of fuzzer. Add a logging facility for the fuzzer to use to record activity. Added some scripts used to automate fuzzer testing on windows.
This commit is contained in:
@@ -462,6 +462,10 @@ struct ptr_array {
|
||||
};
|
||||
typedef struct ptr_array ptr_array_t;
|
||||
|
||||
/* logging verbosity levels */
|
||||
#define LOG_MINIMAL 0
|
||||
#define LOG_VERBOSE 1
|
||||
|
||||
#undef E
|
||||
|
||||
#endif /* DECL_H */
|
||||
|
||||
@@ -27,6 +27,13 @@ E void NDECL(newgame);
|
||||
E void FDECL(welcome, (BOOLEAN_P));
|
||||
E time_t NDECL(get_realtime);
|
||||
E int FDECL(argcheck, (int, char **, enum earlyarg));
|
||||
E void NDECL(fuzzer_start);
|
||||
E void NDECL(fuzzer_stop);
|
||||
E void NDECL(fuzzer_toggle);
|
||||
E void VDECL(fuzzer_log, (int, const char *, ...)) PRINTF_F(2, 3);
|
||||
E void NDECL(fuzzer_check);
|
||||
E void NDECL(fuzzer_auto_start);
|
||||
E boolean FDECL(fuzzer_msg_history, (const char *));
|
||||
|
||||
/* ### apply.c ### */
|
||||
|
||||
@@ -950,6 +957,7 @@ E boolean
|
||||
FDECL(fuzzymatch, (const char *, const char *, const char *, BOOLEAN_P));
|
||||
E void FDECL(init_random, (int FDECL((*fn), (int))));
|
||||
E void FDECL(reseed_random, (int FDECL((*fn), (int))));
|
||||
E void FDECL(set_random, (unsigned long, int FDECL((*fn), (int))));
|
||||
E time_t NDECL(getnow);
|
||||
E int NDECL(getyear);
|
||||
#if 0
|
||||
@@ -2161,6 +2169,7 @@ E int FDECL(rnd, (int));
|
||||
E int FDECL(d, (int, int));
|
||||
E int FDECL(rne, (int));
|
||||
E int FDECL(rnz, (int));
|
||||
E unsigned long NDECL(rul);
|
||||
|
||||
/* ### role.c ### */
|
||||
|
||||
|
||||
@@ -447,6 +447,10 @@ struct instance_flags {
|
||||
chosen_windowport[], but do not switch to
|
||||
it in the midst of options processing */
|
||||
boolean obsolete; /* obsolete options can point at this, it isn't used */
|
||||
boolean fuzzer_auto_start; /* start fuzzer automatically */
|
||||
int fuzzer_stop_and_save; /* move when fuzzer stops and saves game */
|
||||
boolean fuzzer_saving; /* fuzzer is saving game */
|
||||
int verbose_logging_start; /* move when verbose fuzzer logging starts */
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
131
src/allmain.c
131
src/allmain.c
@@ -436,6 +436,8 @@ boolean resuming;
|
||||
#ifdef MAIL
|
||||
ckmailstatus();
|
||||
#endif
|
||||
fuzzer_check();
|
||||
|
||||
rhack((char *) 0);
|
||||
}
|
||||
if (u.utotype) /* change dungeon level */
|
||||
@@ -583,6 +585,8 @@ newgame()
|
||||
{
|
||||
int i;
|
||||
|
||||
fuzzer_auto_start();
|
||||
|
||||
#ifdef MFLOPPY
|
||||
gameDiskPrompt();
|
||||
#endif
|
||||
@@ -922,4 +926,131 @@ const char *opts;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
static FILE * g_fuzzer_log_file = NULL;
|
||||
static int g_fuzzer_log_level = LOG_MINIMAL;
|
||||
|
||||
/* fuzzer_start() starts the fuzzer opening the fuzzer log file */
|
||||
void
|
||||
fuzzer_start()
|
||||
{
|
||||
if (!iflags.debug_fuzzer) {
|
||||
const char * fq_replay;
|
||||
|
||||
iflags.debug_fuzzer = TRUE;
|
||||
iflags.fuzzer_auto_start = FALSE;
|
||||
|
||||
nhassert(g_fuzzer_log_file == NULL);
|
||||
fq_replay = fqname("fuzzer.log", SAVEPREFIX, 0);
|
||||
|
||||
g_fuzzer_log_file = fopen(fq_replay, "w");
|
||||
}
|
||||
}
|
||||
|
||||
/* fuzzer_stop() stops the fuzzer and close the fuzzer log file */
|
||||
void
|
||||
fuzzer_stop()
|
||||
{
|
||||
if (iflags.debug_fuzzer) {
|
||||
if(g_fuzzer_log_file != NULL) {
|
||||
fclose(g_fuzzer_log_file);
|
||||
g_fuzzer_log_file = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* fuzzer_toggle() toggles fuzzer state */
|
||||
void
|
||||
fuzzer_toggle()
|
||||
{
|
||||
if (iflags.debug_fuzzer)
|
||||
fuzzer_stop();
|
||||
else
|
||||
fuzzer_start();
|
||||
}
|
||||
|
||||
/* fuzzer_log() is used to place messages in the file 'fuzzer.log'. This
|
||||
* log is the primary tool for monitoring fuzzer activity and tracking down
|
||||
* issues that the fuzzer is able to reproduce.
|
||||
*/
|
||||
void
|
||||
fuzzer_log
|
||||
VA_DECL2(int, level, const char *, str)
|
||||
{
|
||||
VA_START(str);
|
||||
VA_INIT(str, char *);
|
||||
|
||||
if (!g_fuzzer_log_file)
|
||||
return;
|
||||
|
||||
if (iflags.verbose_logging_start != 0 && moves >= iflags.verbose_logging_start)
|
||||
g_fuzzer_log_level = LOG_VERBOSE;
|
||||
|
||||
if (level <= g_fuzzer_log_level)
|
||||
Vfprintf(g_fuzzer_log_file, str, VA_ARGS);
|
||||
|
||||
VA_END();
|
||||
}
|
||||
|
||||
/* fuzzer_check() is called prior to rhack(0) to allow the fuzzer to
|
||||
* check if it should stop and to allow it to reseed the game.
|
||||
*/
|
||||
void
|
||||
fuzzer_check()
|
||||
{
|
||||
if (iflags.debug_fuzzer)
|
||||
{
|
||||
if (moves >= iflags.fuzzer_stop_and_save) {
|
||||
iflags.fuzzer_saving = TRUE;
|
||||
dosave0();
|
||||
exit_nhwindows("Goodbye from the fuzzer...");
|
||||
fuzzer_stop();
|
||||
nh_terminate(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
unsigned long seed = rul();
|
||||
set_random(seed, rn2);
|
||||
fuzzer_log(LOG_MINIMAL, "SEED:%ld:%lu\n", moves, seed);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* fuzzer_auto_start is called when creating a new game to allow
|
||||
* the fuzzer to start itself.
|
||||
*/
|
||||
void
|
||||
fuzzer_auto_start()
|
||||
{
|
||||
if (iflags.fuzzer_auto_start) {
|
||||
nhassert(!iflags.debug_fuzzer);
|
||||
fuzzer_start();
|
||||
unsigned long seed = rul();
|
||||
set_random(seed, rn2);
|
||||
fuzzer_log(LOG_MINIMAL, "START:%ld:%lu\n", moves, seed);
|
||||
}
|
||||
}
|
||||
|
||||
/* fuzzer_msg_history is called during save file recovery to allow
|
||||
* the fuzzer to snoop the messages being recovered. The fuzzer
|
||||
* saves a seed as a message in save files and this is the mechanism
|
||||
* used to recover that seed if the fuzzer is being auto started.
|
||||
*/
|
||||
boolean
|
||||
fuzzer_msg_history(msg)
|
||||
const char * msg;
|
||||
{
|
||||
long saved_moves;
|
||||
unsigned long saved_seed;
|
||||
if (sscanf(msg, "SEED:%ld:%lu", &saved_moves, &saved_seed) == 2) {
|
||||
nhassert(saved_moves == moves);
|
||||
if (iflags.fuzzer_auto_start) {
|
||||
fuzzer_start();
|
||||
set_random(saved_seed, rn2);
|
||||
fuzzer_log(LOG_MINIMAL, "START:%ld:%lu\n", moves, saved_seed);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
/*allmain.c*/
|
||||
|
||||
@@ -854,7 +854,7 @@ STATIC_DCL struct tm *NDECL(getlt);
|
||||
/* Sets the seed for the random number generator */
|
||||
#ifdef USE_ISAAC64
|
||||
|
||||
static void
|
||||
void
|
||||
set_random(seed, fn)
|
||||
unsigned long seed;
|
||||
int FDECL((*fn), (int));
|
||||
@@ -865,7 +865,7 @@ int FDECL((*fn), (int));
|
||||
#else /* USE_ISAAC64 */
|
||||
|
||||
/*ARGSUSED*/
|
||||
static void
|
||||
void
|
||||
set_random(seed, fn)
|
||||
unsigned long seed;
|
||||
int FDECL((*fn), (int)) UNUSED;
|
||||
@@ -917,7 +917,7 @@ int FDECL((*fn), (int));
|
||||
{
|
||||
/* only reseed if we are certain that the seed generation is unguessable
|
||||
* by the players. */
|
||||
if (has_strong_rngseed)
|
||||
if (has_strong_rngseed && !iflags.debug_fuzzer)
|
||||
init_random(fn);
|
||||
}
|
||||
|
||||
@@ -1108,6 +1108,9 @@ phase_of_the_moon() /* 0-7, with 0: new, 4: full */
|
||||
register struct tm *lt = getlt();
|
||||
register int epact, diy, goldn;
|
||||
|
||||
if(iflags.debug_fuzzer)
|
||||
return rn2(8);
|
||||
|
||||
diy = lt->tm_yday;
|
||||
goldn = (lt->tm_year % 19) + 1;
|
||||
epact = (11 * goldn + 18) % 30;
|
||||
@@ -1122,6 +1125,9 @@ friday_13th()
|
||||
{
|
||||
register struct tm *lt = getlt();
|
||||
|
||||
if(iflags.debug_fuzzer)
|
||||
return rn2(30);
|
||||
|
||||
/* tm_wday (day of week; 0==Sunday) == 5 => Friday */
|
||||
return (boolean) (lt->tm_wday == 5 && lt->tm_mday == 13);
|
||||
}
|
||||
|
||||
@@ -1231,6 +1231,8 @@ register int fd;
|
||||
panic("restore_msghistory: msg too big (%d)", msgsize);
|
||||
mread(fd, (genericptr_t) msg, msgsize);
|
||||
msg[msgsize] = '\0';
|
||||
if(fuzzer_msg_history(msg))
|
||||
continue;
|
||||
putmsghistory(msg, TRUE);
|
||||
++msgcount;
|
||||
}
|
||||
|
||||
29
src/rnd.c
29
src/rnd.c
@@ -56,10 +56,21 @@ int FDECL((*fn), (int));
|
||||
(int) sizeof seed);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
rul()
|
||||
{
|
||||
unsigned long value;
|
||||
|
||||
value = (unsigned long) isaac64_next_uint64(&rnglist[CORE].rng_state);
|
||||
fuzzer_log(LOG_VERBOSE, "RANDOM:%llu\n", value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int
|
||||
RND(int x)
|
||||
{
|
||||
return (isaac64_next_uint64(&rnglist[CORE].rng_state) % x);
|
||||
return (rul() % x);
|
||||
}
|
||||
|
||||
/* 0 <= rn2(x) < x, but on a different sequence from the "main" rn2;
|
||||
@@ -69,6 +80,8 @@ int
|
||||
rn2_on_display_rng(x)
|
||||
register int x;
|
||||
{
|
||||
if (iflags.debug_fuzzer)
|
||||
return rn2(x);
|
||||
return (isaac64_next_uint64(&rnglist[DISP].rng_state) % x);
|
||||
}
|
||||
|
||||
@@ -94,6 +107,20 @@ register int x;
|
||||
seed *= 2739110765;
|
||||
return (int)((seed >> 16) % (unsigned)x);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
rul()
|
||||
{
|
||||
#if defined(LINT) && defined(UNIX)
|
||||
return (unsigned long) rand();
|
||||
#else /* LINT */
|
||||
#if defined(UNIX) || defined(RANDOM)
|
||||
return (unsigned long) Rand();
|
||||
#else
|
||||
return (unsigned long) (Rand() >> 3);
|
||||
#endif /* defined(UNIX) || defined(RANDOM) */
|
||||
#endif /* LINT */
|
||||
}
|
||||
#endif /* USE_ISAAC64 */
|
||||
|
||||
/* 0 <= rn2(x) < x */
|
||||
|
||||
14
src/save.c
14
src/save.c
@@ -139,7 +139,7 @@ dosave0()
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
HUP if (iflags.window_inited) {
|
||||
HUP if (!iflags.debug_fuzzer && iflags.window_inited) {
|
||||
nh_uncompress(fq_save);
|
||||
fd = open_savefile();
|
||||
if (fd > 0) {
|
||||
@@ -1238,6 +1238,18 @@ int fd, mode;
|
||||
bwrite(fd, (genericptr_t) msg, msglen);
|
||||
++msgcount;
|
||||
}
|
||||
/* If the fuzzer is stopping and saving, save a seed as a message.
|
||||
In 3.7, we will modify the save file format and save the seed
|
||||
directly in the saved game state. */
|
||||
if (iflags.fuzzer_saving) {
|
||||
char message[BUFSIZ];
|
||||
unsigned long seed = rul();
|
||||
sprintf(message, "SEED:%ld:%lu", moves, seed);
|
||||
fuzzer_log(LOG_MINIMAL, "STOP:%ld:%lu\n", moves, seed);
|
||||
msglen = strlen(message);
|
||||
bwrite(fd, (genericptr_t) &msglen, sizeof msglen);
|
||||
bwrite(fd, (genericptr_t) message, msglen);
|
||||
}
|
||||
bwrite(fd, (genericptr_t) &minusone, sizeof (int));
|
||||
}
|
||||
debugpline1("Stored %d messages into savefile.", msgcount);
|
||||
|
||||
@@ -475,8 +475,14 @@ int *x, *y, *mod;
|
||||
coord cc;
|
||||
DWORD count;
|
||||
really_move_cursor();
|
||||
if (iflags.debug_fuzzer)
|
||||
return randomkey();
|
||||
if (iflags.debug_fuzzer) {
|
||||
int poskey = randomkey();
|
||||
if (poskey == 0) {
|
||||
*x = rn2(console.width);
|
||||
*y = rn2(console.height);
|
||||
}
|
||||
return poskey;
|
||||
}
|
||||
ch = (program_state.done_hup)
|
||||
? '\033'
|
||||
: keyboard_handler.pCheckInput(
|
||||
|
||||
@@ -336,7 +336,7 @@ attempt_restore:
|
||||
resuming = TRUE; /* not starting new game */
|
||||
if (discover)
|
||||
You("are in non-scoring discovery mode.");
|
||||
if (discover || wizard) {
|
||||
if ((discover || wizard) && !iflags.fuzzer_auto_start) {
|
||||
if (yn("Do you want to keep the save file?") == 'n')
|
||||
(void) delete_savefile();
|
||||
else {
|
||||
@@ -461,6 +461,23 @@ char *argv[];
|
||||
case 'X':
|
||||
discover = TRUE, wizard = FALSE;
|
||||
break;
|
||||
case 'F':
|
||||
{
|
||||
iflags.fuzzer_auto_start = 1;
|
||||
|
||||
if (argc > 1 && argv[1][0] != '-') {
|
||||
argc--;
|
||||
argv++;
|
||||
iflags.fuzzer_stop_and_save = atoi(*argv);
|
||||
|
||||
if (argc > 1 && argv[1][0] != '-') {
|
||||
argc--;
|
||||
argv++;
|
||||
iflags.verbose_logging_start = atoi(*argv);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
#ifdef NEWS
|
||||
case 'n':
|
||||
iflags.news = FALSE;
|
||||
|
||||
8
win/win32/scripts/fuzzer/clean.bat
Normal file
8
win/win32/scripts/fuzzer/clean.bat
Normal file
@@ -0,0 +1,8 @@
|
||||
set BIN_DIR=..\..\..\..\bin\Debug\Win32
|
||||
|
||||
set FUZZER_LOG=%BIN_DIR%\fuzzer.log
|
||||
set FUZZER_DIR=%BIN_DIR%\fuzzer
|
||||
|
||||
if exist %BIN_DIR%\%USERNAME%* del %BIN_DIR%\%USERNAME%*
|
||||
if exist %FUZZER_LOG% del %FUZZER_LOG%
|
||||
|
||||
23
win/win32/scripts/fuzzer/longtest.bat
Normal file
23
win/win32/scripts/fuzzer/longtest.bat
Normal file
@@ -0,0 +1,23 @@
|
||||
echo off
|
||||
|
||||
SETLOCAL ENABLEEXTENSIONS
|
||||
SETLOCAL ENABLEDELAYEDEXPANSION
|
||||
|
||||
set STEP_SIZE=5000
|
||||
set FINAL_MOVE=500000
|
||||
set START_MOVE=5000
|
||||
|
||||
for /L %%i in (%START_MOVE%, %STEP_SIZE%, %FINAL_MOVE%) do (
|
||||
|
||||
call runtill.bat %%i
|
||||
|
||||
if ERRORLEVEL 1 (
|
||||
echo FAILED getting running to %%i.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
echo SUCCESS.
|
||||
|
||||
|
||||
7
win/win32/scripts/fuzzer/restore.bat
Normal file
7
win/win32/scripts/fuzzer/restore.bat
Normal file
@@ -0,0 +1,7 @@
|
||||
call clean.bat
|
||||
|
||||
set BIN_DIR=..\..\..\..\bin\Debug\Win32
|
||||
set SAVED_GAME=%USERNAME%-wizard.NetHack-saved-game
|
||||
set FUZZER_DIR=%BIN_DIR%\fuzzer
|
||||
|
||||
copy %FUZZER_DIR%\%SAVED_GAME% %BIN_DIR%\%SAVED_GAME%
|
||||
73
win/win32/scripts/fuzzer/runtill.bat
Normal file
73
win/win32/scripts/fuzzer/runtill.bat
Normal file
@@ -0,0 +1,73 @@
|
||||
REM
|
||||
REM runtill target_move
|
||||
REM
|
||||
echo off
|
||||
|
||||
SETLOCAL ENABLEEXTENSIONS
|
||||
SETLOCAL ENABLEDELAYEDEXPANSION
|
||||
|
||||
set TARGET_MOVE=%1
|
||||
|
||||
if %TARGET_MOVE% == "" (
|
||||
echo Usage:runtill target_move
|
||||
goto :eof
|
||||
)
|
||||
|
||||
set BIN_DIR=..\..\..\..\bin\Debug\Win32
|
||||
set SAVED_GAME=%USERNAME%-wizard.NetHack-saved-game
|
||||
set LOG_FILE=%BIN_DIR%\runtil.log
|
||||
set FUZZER_LOG=%BIN_DIR%\fuzzer.log
|
||||
set FUZZER_DIR=%BIN_DIR%\fuzzer
|
||||
set BASELINE=%FUZZER_DIR%\fuzzer.log
|
||||
|
||||
if not exist %FUZZER_DIR% mkdir %FUZZER_DIR%
|
||||
|
||||
call clean.bat
|
||||
|
||||
if not exist %FUZZER_DIR%\%SAVED_GAME% (
|
||||
%BIN_DIR%\nethack -D -F 0
|
||||
|
||||
copy %BIN_DIR%\%SAVED_GAME% %FUZZER_DIR%
|
||||
)
|
||||
|
||||
call restore.bat
|
||||
|
||||
%BIN_DIR%\nethack -D -F %TARGET_MOVE%
|
||||
|
||||
move %BIN_DIR%\*.snap %BIN_DIR%\snapshots
|
||||
copy %FUZZER_LOG% %BASELINE%
|
||||
|
||||
for /f "tokens=2,3 delims=: usebackq" %%i in (`findstr /c:START %BASELINE%`) do (
|
||||
set START_SEED=%%j
|
||||
set START_MOVE=%%i
|
||||
)
|
||||
|
||||
for /f "tokens=2,3 delims=: usebackq" %%i in (`findstr /c:STOP %BASELINE%`) do (
|
||||
set STOP_SEED=%%j
|
||||
set STOP_MOVE=%%i
|
||||
)
|
||||
|
||||
if !STOP_MOVE! LSS %TARGET_MOVE% (
|
||||
cls
|
||||
echo FAILED: Failed to reach target move. !STOP_MOVE! is not GTE %TARGET_MOVE%.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
call restore.bat
|
||||
|
||||
%BIN_DIR%\nethack -D -F %TARGET_MOVE%
|
||||
|
||||
fc %FUZZER_LOG% %BASELINE%
|
||||
|
||||
if ERRORLEVEL 1 (
|
||||
cls
|
||||
echo FAILED: Unable to reproduce same timeline
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
del /q %FUZZER_DIR%\%SAVED_GAME%
|
||||
|
||||
copy %BIN_DIR%\%SAVED_GAME% %FUZZER_DIR%
|
||||
|
||||
echo !START_MOVE! to !STOP_MOVE!.
|
||||
echo SUCCESS.
|
||||
Reference in New Issue
Block a user