Added new Windows option -watchdog, which creates a watchdog thread

that forcibly kills the application after a certain number of seconds.
Primarily intended to be used in regression test runs to handle the
occasional hung game.
This commit is contained in:
Aaron Giles 2008-04-20 22:29:29 +00:00
parent 682265e480
commit e0533ec7c3
2 changed files with 59 additions and 4 deletions

View File

@ -65,7 +65,6 @@ typedef BOOL (WINAPI *av_revert_mm_thread_characteristics_ptr)(HANDLE AvrtHandle
int _CRT_glob = 0; int _CRT_glob = 0;
//============================================================ //============================================================
// LOCAL VARIABLES // LOCAL VARIABLES
//============================================================ //============================================================
@ -79,6 +78,10 @@ static av_revert_mm_thread_characteristics_ptr av_revert_mm_thread_characteristi
static char mapfile_name[MAX_PATH]; static char mapfile_name[MAX_PATH];
static LPTOP_LEVEL_EXCEPTION_FILTER pass_thru_filter; static LPTOP_LEVEL_EXCEPTION_FILTER pass_thru_filter;
static HANDLE watchdog_exit_event;
static HANDLE watchdog_thread;
#ifndef MESS #ifndef MESS
static const TCHAR helpfile[] = TEXT("docs\\windows.txt"); static const TCHAR helpfile[] = TEXT("docs\\windows.txt");
#else #else
@ -101,6 +104,7 @@ static void osd_exit(running_machine *machine);
static void soft_link_functions(void); static void soft_link_functions(void);
static int is_double_click_start(int argc); static int is_double_click_start(int argc);
static DWORD WINAPI watchdog_thread_entry(LPVOID lpParameter);
static LONG CALLBACK exception_filter(struct _EXCEPTION_POINTERS *info); static LONG CALLBACK exception_filter(struct _EXCEPTION_POINTERS *info);
static const char *lookup_symbol(UINT32 address); static const char *lookup_symbol(UINT32 address);
static int get_code_base_size(UINT32 *base, UINT32 *size); static int get_code_base_size(UINT32 *base, UINT32 *size);
@ -120,6 +124,7 @@ const options_entry mame_win_options[] =
// debugging options // debugging options
{ NULL, NULL, OPTION_HEADER, "WINDOWS DEBUGGING OPTIONS" }, { NULL, NULL, OPTION_HEADER, "WINDOWS DEBUGGING OPTIONS" },
{ "oslog", "0", OPTION_BOOLEAN, "output error.log data to the system debugger" }, { "oslog", "0", OPTION_BOOLEAN, "output error.log data to the system debugger" },
{ "watchdog;wdog", "0", 0, "force the program to terminate if no updates within specified number of seconds" },
// performance options // performance options
{ NULL, NULL, OPTION_HEADER, "WINDOWS PERFORMANCE OPTIONS" }, { NULL, NULL, OPTION_HEADER, "WINDOWS PERFORMANCE OPTIONS" },
@ -239,9 +244,10 @@ int main(int argc, char **argv)
// soft-link optional functions // soft-link optional functions
soft_link_functions(); soft_link_functions();
// compute the name of the mapfile
strcpy(mapfile_name, argv[0]); strcpy(mapfile_name, argv[0]);
ext = strchr(mapfile_name, '.'); ext = strchr(mapfile_name, '.');
if (ext) if (ext != NULL)
strcpy(ext, ".map"); strcpy(ext, ".map");
else else
strcat(mapfile_name, ".map"); strcat(mapfile_name, ".map");
@ -267,6 +273,8 @@ static void output_oslog(running_machine *machine, const char *buffer)
void osd_init(running_machine *machine) void osd_init(running_machine *machine)
{ {
int watchdog = options_get_int(mame_options(), WINOPTION_WATCHDOG);
// thread priority // thread priority
if (!options_get_bool(mame_options(), OPTION_DEBUG)) if (!options_get_bool(mame_options(), OPTION_DEBUG))
SetThreadPriority(GetCurrentThread(), options_get_int(mame_options(), WINOPTION_PRIORITY)); SetThreadPriority(GetCurrentThread(), options_get_int(mame_options(), WINOPTION_PRIORITY));
@ -295,11 +303,33 @@ void osd_init(running_machine *machine)
// mm_task = (*av_set_mm_thread_characteristics)(TEXT("Playback"), &task_index); // mm_task = (*av_set_mm_thread_characteristics)(TEXT("Playback"), &task_index);
start_profiler(); start_profiler();
// if a watchdog thread is requested, create one
if (watchdog != 0)
{
watchdog_exit_event = CreateEvent(NULL, TRUE, FALSE, NULL);
assert_always(watchdog_exit_event != NULL, "Failed to create watchdog event");
watchdog_thread = CreateThread(NULL, 0, watchdog_thread_entry, (LPVOID)watchdog, 0, NULL);
assert_always(watchdog_thread != NULL, "Failed to create watchdog thread");
}
} }
//============================================================
// osd_exit
//============================================================
static void osd_exit(running_machine *machine) static void osd_exit(running_machine *machine)
{ {
// take down the watchdog thread if it exists
if (watchdog_thread != NULL)
{
SetEvent(watchdog_exit_event);
WaitForSingleObject(watchdog_thread, INFINITE);
watchdog_exit_event = NULL;
watchdog_thread = NULL;
}
stop_profiler(); stop_profiler();
// turn off our multimedia tasks // turn off our multimedia tasks
@ -360,6 +390,22 @@ static int is_double_click_start(int argc)
} }
//============================================================
// watchdog_thread_entry
//============================================================
static DWORD WINAPI watchdog_thread_entry(LPVOID lpParameter)
{
DWORD timeout = (int)(FPTR)lpParameter * 1000;
DWORD wait_result;
wait_result = WaitForSingleObject(watchdog_exit_event, timeout);
if (wait_result == WAIT_TIMEOUT)
TerminateProcess(GetCurrentProcess(), -1);
return 0;
}
//============================================================ //============================================================
// exception_filter // exception_filter
//============================================================ //============================================================
@ -550,13 +596,14 @@ static const char *lookup_symbol(UINT32 address)
// parse the file, looking for map entries // parse the file, looking for map entries
while (fgets(line, sizeof(line) - 1, map)) while (fgets(line, sizeof(line) - 1, map))
if (!strncmp(line, " 0x", 18)) if (strncmp(line, " 0x", 18) == 0)
if (sscanf(line, " 0x%08x %s", &addr, symbol) == 2) if (sscanf(line, " 0x%08x %s", &addr, symbol) == 2)
if (addr <= address && addr > best_addr) if (addr <= address && addr > best_addr)
{ {
best_addr = addr; best_addr = addr;
strcpy(best_symbol, symbol); strcpy(best_symbol, symbol);
} }
fclose(map);
// create the final result // create the final result
if (address - best_addr > 0x10000) if (address - best_addr > 0x10000)
@ -575,6 +622,7 @@ static int get_code_base_size(UINT32 *base, UINT32 *size)
{ {
FILE * map = fopen(mapfile_name, "r"); FILE * map = fopen(mapfile_name, "r");
char line[1024]; char line[1024];
int result = 0;
// if no file, return nothing // if no file, return nothing
if (map == NULL) if (map == NULL)
@ -584,8 +632,12 @@ static int get_code_base_size(UINT32 *base, UINT32 *size)
while (fgets(line, sizeof(line) - 1, map)) while (fgets(line, sizeof(line) - 1, map))
if (!strncmp(line, ".text 0x", 18)) if (!strncmp(line, ".text 0x", 18))
if (sscanf(line, ".text 0x%08x 0x%x", base, size) == 2) if (sscanf(line, ".text 0x%08x 0x%x", base, size) == 2)
return 1; {
result = 1;
break;
}
fclose(map);
return 0; return 0;
} }
@ -625,6 +677,8 @@ static HANDLE profiler_thread;
static DWORD profiler_thread_id; static DWORD profiler_thread_id;
static volatile UINT8 profiler_thread_exit; static volatile UINT8 profiler_thread_exit;
//============================================================ //============================================================
// compare_base // compare_base
// compare_hits -- qsort callbacks to sort on // compare_hits -- qsort callbacks to sort on

View File

@ -48,6 +48,7 @@
#define WINOPTION_AUDIO_LATENCY "audio_latency" #define WINOPTION_AUDIO_LATENCY "audio_latency"
#define WINOPTION_PRIORITY "priority" #define WINOPTION_PRIORITY "priority"
#define WINOPTION_MULTITHREADING "multithreading" #define WINOPTION_MULTITHREADING "multithreading"
#define WINOPTION_WATCHDOG "watchdog"
#define WINOPTION_OSLOG "oslog" #define WINOPTION_OSLOG "oslog"