ChangeLog: 2007-11-04 Leo Zayas * win32-low.c [NEW_INT]: New macro to allow conditional compilation with new or old interrupt code. * win32-low.c [NEW_INT] (interrupt_requested): New variable to indicate remote interrupt request. * win32-low.c [NEW_INT] (interrupt_thread_event): New variable to store interrupt code secondary gdbserver thread event. * win32-low.c [NEW_INT] (winapi_SetProcessPriorityBoost): New API typedef [NEW_INT] (winapi_GetProcessPriorityBoost): New API typedef [NEW_INT] (winapi_SetProcessAffinityMask): New API typedef [!NEW_INT] (winapi_DebugBreakProcess): Old typedef being replaced [!NEW_INT] (winapi_GenerateConsoleCtrlEvent): Old typedef being replaced * win32-low.c (thread_rec): set suspend_count to our internal suspends only. * win32-low.c (continue_one_thread): fix indentation. * win32-low.c [NEW_INT] (consume_cpu_interrupt_thread): Thread function that consumes cpu, and exits upon signal. * win32-low.c [NEW_INT] (pause_child_inferior_thread): Pause a single thread. (resume_child_inferior_thread): Resume a single thread. * win32-low.c [NEW_INT] (synthetic_child_interrupt): New function to pause or resume all child threads. * win32-low.c (child_continue) [NEW_INT]: Resume the child process threads. * win32-low.c (get_child_debug_event) [NEW_INT]: Pause child process threads upon interrupt request, and return signal. * win32-low.c (win32_request_interrupt) [NEW_INT]: Set interrupt request flag. (win32_request_interrupt) [!NEW_INT]: Keep old code around. diff -u src_orig/gdb/gdbserver/win32-low.c src/gdb/gdbserver/win32-low.c --- src_orig/gdb/gdbserver/win32-low.c 2007-09-04 00:17:27.000000000 +0200 +++ src/gdb/gdbserver/win32-low.c 2007-11-04 00:54:53.218750000 +0100 @@ -37,6 +37,8 @@ #include #endif +#define NEW_INT 1 + #define LOG 0 #define OUTMSG(X) do { printf X; fflush (stdout); } while (0) @@ -64,6 +66,11 @@ int using_threads = 1; +#if NEW_INT +static int interrupt_requested = 0; +static HANDLE interrupt_thread_event = NULL; +#endif + /* Globals. */ static HANDLE current_process_handle = NULL; static DWORD current_process_id = 0; @@ -76,8 +83,14 @@ typedef BOOL WINAPI (*winapi_DebugActiveProcessStop) (DWORD dwProcessId); typedef BOOL WINAPI (*winapi_DebugSetProcessKillOnExit) (BOOL KillOnExit); +#if NEW_INT +typedef BOOL WINAPI (*winapi_SetProcessPriorityBoost) (HANDLE, BOOL); +typedef BOOL WINAPI (*winapi_GetProcessPriorityBoost) (HANDLE, PBOOL); +typedef BOOL WINAPI (*winapi_SetProcessAffinityMask) (HANDLE, DWORD_PTR); +#else typedef BOOL WINAPI (*winapi_DebugBreakProcess) (HANDLE); typedef BOOL WINAPI (*winapi_GenerateConsoleCtrlEvent) (DWORD, DWORD); +#endif static DWORD main_thread_id = 0; @@ -92,7 +105,7 @@ return th->tid; } -/* Find a thread record given a thread id. If GET_CONTEXT is set then +/* Find a thread record given a thread id. If get_context is set then also retrieve the context for this thread. */ static win32_thread_info * thread_rec (DWORD id, int get_context) @@ -108,7 +121,13 @@ if (!th->suspend_count && get_context) { if (id != current_event.dwThreadId) - th->suspend_count = SuspendThread (th->h) + 1; + { + /* Set suspend count to *our* internal thread suspends, so that we + do not interfere with already thread suspend state. */ + th->suspend_count++; + SuspendThread (th->h); + /* th->suspend_count = SuspendThread (th->h) + 1; */ + } (*the_low_target.get_thread_context) (th, ¤t_event); } @@ -265,24 +284,178 @@ && th->suspend_count) { if (th->context.ContextFlags) - { - (*the_low_target.set_thread_context) (th, ¤t_event); - th->context.ContextFlags = 0; - } + { + (*the_low_target.set_thread_context) (th, ¤t_event); + th->context.ContextFlags = 0; + } for (i = 0; i < th->suspend_count; i++) - (void) ResumeThread (th->h); + (void) ResumeThread (th->h); th->suspend_count = 0; } return 0; } +/* thread function that consumes cpu while interrupt code pauses + child threads. */ +#if NEW_INT +static DWORD WINAPI +consume_cpu_interrupt_thread (LPVOID data) +{ + HANDLE *event = (HANDLE *) data; + while (WaitForSingleObject (interrupt_thread_event, 0) != WAIT_OBJECT_0) + { + } + if (event != 0) + SetEvent (*event); + return 0; +} +#endif + +/* Pause child thread. */ +#if NEW_INT +static void pause_child_inferior_thread (struct inferior_list_entry *inf) +{ + struct thread_info *thr = (struct thread_info *) inf; + + if (thr != NULL) + { + win32_thread_info *th = inferior_target_data (thr); + + /* Pause and get context from thread if necessary. */ + int tid = th->tid; + thread_rec (tid, 1); + } +} +#endif + +/* Resume child thread. */ +#if NEW_INT +static void resume_child_inferior_thread (struct inferior_list_entry *inf) +{ + struct thread_info *thr = (struct thread_info *) inf; + + if (thr != NULL) + { + win32_thread_info *th = inferior_target_data (thr); + + /* Resume thread and set context if necessary. */ + int tid = th->tid; + continue_one_thread (inf, &tid); + } +} +#endif + +/* Pause/resume all child threads. This function attempts to perform the + required operation in a more portable manner across Windows versions and also + bypass some problems with GenerateConcoleCtrlEvent and DebugBreakProcess + functions. */ +#if NEW_INT +static void +synthetic_child_interrupt (int pause) +{ + DWORD old_process_priority_class; + DWORD old_process_affinity_mask; + BOOL old_process_priority_boost; + DWORD system_affinity_mask; + HANDLE current_process_handle; + SYSTEM_INFO sys_info; + int thr; + + winapi_SetProcessPriorityBoost SetProcessPriorityBoost; + winapi_GetProcessPriorityBoost GetProcessPriorityBoost; + winapi_SetProcessAffinityMask SetProcessAffinityMask; + +#ifdef _WIN32_WCE + HMODULE dll = GetModuleHandle (_T("COREDLL.DLL")); +#else + HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL")); +#endif + + SetProcessPriorityBoost = GETPROCADDRESS (dll, SetProcessPriorityBoost); + GetProcessPriorityBoost = GETPROCADDRESS (dll, GetProcessPriorityBoost); + SetProcessAffinityMask = GETPROCADDRESS (dll, SetProcessAffinityMask); + + /* As we are going to play around with gdbserver scheduling, use system + reported current process, in case gdbserver own data gets corrupted. */ + current_process_handle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, + GetCurrentProcessId ()); + + old_process_priority_class = GetPriorityClass (current_process_handle); + + /* Go real-time so we can pause child as atomically as possible. */ + SetPriorityClass (current_process_handle, REALTIME_PRIORITY_CLASS); + + if (GetProcessPriorityBoost != NULL) + GetProcessPriorityBoost (current_process_handle, + &old_process_priority_boost); + if (SetProcessPriorityBoost != NULL) + SetProcessPriorityBoost (current_process_handle, FALSE); + + GetProcessAffinityMask (current_process_handle, + &old_process_affinity_mask, + &system_affinity_mask); + + /* Set gdbserver to run on all CPUs. */ + if (SetProcessAffinityMask != NULL) + SetProcessAffinityMask (current_process_handle, system_affinity_mask); + + GetSystemInfo (&sys_info); + + interrupt_thread_event = CreateEvent(NULL, TRUE, FALSE, NULL); + + HANDLE *events; + events = malloc (sizeof (HANDLE) * (sys_info.dwNumberOfProcessors - 1)); + for (thr = 0; thr < sys_info.dwNumberOfProcessors - 1; thr++) + { + events[thr] = CreateEvent (NULL, TRUE, FALSE, NULL); + CreateThread(NULL, 0, consume_cpu_interrupt_thread, events+thr, 0, NULL); + } + + /* Now pause/resume child threads one by one. */ + if (pause) + for_each_inferior (&all_threads, pause_child_inferior_thread); + else + for_each_inferior (&all_threads, resume_child_inferior_thread); + + /* Once paused, signal gdbserver secondary cpu-consumption threads + to terminate, wait for them to do so, and gracefully + free all synchronization objects. */ + SetEvent (interrupt_thread_event); + WaitForMultipleObjects (sys_info.dwNumberOfProcessors - 1, + events, TRUE, INFINITE); + for (thr = 0; thr < sys_info.dwNumberOfProcessors - 1; thr++) + CloseHandle (events[thr]); + free (events); + CloseHandle (interrupt_thread_event); + + /* Restore gdbserver Windows scheduling state. */ + if (SetProcessPriorityBoost != NULL) + SetProcessPriorityBoost (current_process_handle, + old_process_priority_boost); + if (SetProcessAffinityMask != NULL) + SetProcessAffinityMask (current_process_handle, old_process_affinity_mask); + SetPriorityClass (current_process_handle, old_process_priority_class); + + FreeLibrary (dll); +} +#endif + static BOOL child_continue (DWORD continue_status, int thread_id) { BOOL res; +#if NEW_INT + if (interrupt_requested) + { + interrupt_requested = 0; + synthetic_child_interrupt(0); + return TRUE; + } +#endif + res = ContinueDebugEvent (current_event.dwProcessId, current_event.dwThreadId, continue_status); if (res) @@ -1243,6 +1416,33 @@ last_sig = TARGET_SIGNAL_0; ourstatus->kind = TARGET_WAITKIND_SPURIOUS; + +#if NEW_INT + /* Remote interrupt has been requested. In this case return a signal + * the client gdb can understand as the child being stopped. + * As we are checking this first, any pending OS exceptions arised + * between interrupt request and next call to WaitForDebugEvent should + * get stacked and pending. */ + if (interrupt_requested) + { + current_inferior = + (struct thread_info *) find_inferior_id (&all_threads, + main_thread_id); + ourstatus->kind = TARGET_WAITKIND_STOPPED; + ourstatus->value.sig = TARGET_SIGNAL_INT; + + /* Clear current event data, as this could interfere with thread_rec. */ + current_event.dwThreadId = 0; + current_event.dwProcessId = current_process_id; + + /* Simulate child stop. Basically give gdbserver process maximum priority + * so that child doesn't get a chance to run, then pause one by one all + * child threads, and finally return new signaled state. */ + synthetic_child_interrupt(1); + + return; + } +#endif /* Keep the wait time low enough for confortable remote interruption, but high enough so gdbserver doesn't become a bottleneck. */ @@ -1445,7 +1645,8 @@ called through read_inferior_memory, which handles breakpoint shadowing. Read LEN bytes at MEMADDR into a buffer at MYADDR. */ static int -win32_read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) +win32_read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, + int len) { return child_xfer_memory (memaddr, (char *) myaddr, len, 0, 0) != len; } @@ -1455,8 +1656,9 @@ Write LEN bytes from the buffer at MYADDR to MEMADDR. Returns 0 on success and errno on failure. */ static int -win32_write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr, - int len) +win32_write_inferior_memory (CORE_ADDR memaddr, + const unsigned char *myaddr, + int len) { return child_xfer_memory (memaddr, (char *) myaddr, len, 1, 0) != len; } @@ -1465,6 +1667,12 @@ static void win32_request_interrupt (void) { +#if NEW_INT + + interrupt_requested = 1; + +#else + winapi_DebugBreakProcess DebugBreakProcess; winapi_GenerateConsoleCtrlEvent GenerateConsoleCtrlEvent; @@ -1492,6 +1700,8 @@ return; OUTMSG (("Could not interrupt process.\n")); + +#endif } static const char *