2007-11-12 Leo Zayas Pedro Alves * win32-low.c (winapi_SetProcessPriorityBoost) (winapi_GetProcessPriorityBoost, winapi_SetProcessAffinityMask) [!_WIN32_WCE]: New typedefs. (interrupt_thread_event) [!_WIN32_WCE]: New global. (consume_cpu_interrupt_thread) [!_WIN32_WCE]: New. (realtime_execute) [!_WIN32_WCE]: New. (winapi_CeSetThreadPriority, winapi_CeGetThreadPriority) [_WIN32_WCE]: New typedefs. (realtime_execute) [_WIN32_WCE]: New. (do_continue_one_thread): New. (child_continue): If continuing with a faked breakpoint, do the thread resuming with higher priority, and don't call ContinueDebugEvent. (suspend_all_threads): New. (fake_breakpoint_event): Do the thread suspending with higher priority. --- gdb/gdbserver/win32-low.c | 198 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 188 insertions(+), 10 deletions(-) Index: src/gdb/gdbserver/win32-low.c =================================================================== --- src.orig/gdb/gdbserver/win32-low.c 2007-11-11 23:15:44.000000000 +0000 +++ src/gdb/gdbserver/win32-low.c 2007-11-11 23:15:54.000000000 +0000 @@ -267,6 +267,159 @@ do_initial_child_stuff (DWORD pid) (*the_low_target.initial_stuff) (); } +#ifndef _WIN32_WCE + +typedef BOOL WINAPI (*winapi_SetProcessPriorityBoost) (HANDLE, BOOL); +typedef BOOL WINAPI (*winapi_GetProcessPriorityBoost) (HANDLE, PBOOL); +typedef BOOL WINAPI (*winapi_SetProcessAffinityMask) (HANDLE, DWORD_PTR); + +static HANDLE interrupt_thread_event; + +/* Thread function that consumes cpu while interrupt code pauses child + threads. */ +static DWORD WINAPI +consume_cpu_interrupt_thread (LPVOID data) +{ + while (WaitForSingleObject (interrupt_thread_event, 0) != WAIT_OBJECT_0) + ; + return 0; +} + +static void +realtime_execute (void (*func) (void*), void *args) +{ + DWORD old_process_priority_class; + DWORD old_process_affinity_mask; + BOOL old_process_priority_boost; + DWORD system_affinity_mask; + HANDLE h; + SYSTEM_INFO sys_info; + HANDLE *threads = NULL; + HMODULE dll; + + winapi_SetProcessPriorityBoost SetProcessPriorityBoost; + winapi_GetProcessPriorityBoost GetProcessPriorityBoost; + winapi_SetProcessAffinityMask SetProcessAffinityMask; + + dll = GetModuleHandle (_T("KERNEL32.DLL")); + SetProcessPriorityBoost = GETPROCADDRESS (dll, SetProcessPriorityBoost); + GetProcessPriorityBoost = GETPROCADDRESS (dll, GetProcessPriorityBoost); + SetProcessAffinityMask = GETPROCADDRESS (dll, SetProcessAffinityMask); + + h = GetCurrentProcess (); + + old_process_priority_class = GetPriorityClass (h); + + /* Go real-time so we can pause child as atomically as possible. */ + SetPriorityClass (h, REALTIME_PRIORITY_CLASS); + + if (GetProcessPriorityBoost != NULL) + GetProcessPriorityBoost (h, &old_process_priority_boost); + if (SetProcessPriorityBoost != NULL) + SetProcessPriorityBoost (h, FALSE); + + GetSystemInfo (&sys_info); + + if (sys_info.dwNumberOfProcessors > 1) + { + int thr; + GetProcessAffinityMask (h, + &old_process_affinity_mask, + &system_affinity_mask); + + /* Set gdbserver to run on all CPUs. */ + if (SetProcessAffinityMask != NULL) + SetProcessAffinityMask (h, system_affinity_mask); + + interrupt_thread_event = CreateEvent (NULL, TRUE, FALSE, NULL); + + threads = alloca (sizeof (HANDLE) * (sys_info.dwNumberOfProcessors - 1)); + for (thr = 0; thr < sys_info.dwNumberOfProcessors - 1; thr++) + threads[thr] = CreateThread (NULL, 0, consume_cpu_interrupt_thread, + NULL, 0, NULL); + } + + /* Do the work. */ + (*func) (args); + + /* Revert the priorities. */ + if (sys_info.dwNumberOfProcessors > 1) + { + int thr; + + /* 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, + threads, TRUE, INFINITE); + for (thr = 0; thr < sys_info.dwNumberOfProcessors - 1; thr++) + CloseHandle (threads[thr]); + CloseHandle (interrupt_thread_event); + + if (SetProcessAffinityMask != NULL) + SetProcessAffinityMask (h, old_process_affinity_mask); + } + + /* Restore gdbserver Windows scheduling state. */ + if (SetProcessPriorityBoost != NULL) + SetProcessPriorityBoost (h, old_process_priority_boost); + SetPriorityClass (h, old_process_priority_class); +} + +#else + +typedef BOOL WINAPI (*winapi_CeSetThreadPriority) (HANDLE, int); +typedef int WINAPI (*winapi_CeGetThreadPriority) (HANDLE); + +static void +realtime_execute (void (*func) (void*), void *args) +{ + /* Windows CE (at least up to 6) does not support multi-processor. + Windows CE is a real-time operating system, with a fixed priority + scheduler. Each thread runs at particular priority level, and + the highest priority runnable thread is always the one that is + run. If there is more than one thread at that priority level, + they are run round-robin. */ + + HANDLE h = GetCurrentThread (); + int prio; + + static HMODULE dll = NULL; + static winapi_CeSetThreadPriority CeSetThreadPriority = NULL; + static winapi_CeGetThreadPriority CeGetThreadPriority = NULL; + + if (dll == NULL) + { + dll = GetModuleHandle (L"COREDLL.DLL"); + CeSetThreadPriority = GETPROCADDRESS (dll, CeSetThreadPriority); + CeGetThreadPriority = GETPROCADDRESS (dll, CeGetThreadPriority); + } + + if (CeSetThreadPriority != NULL) + { + prio = CeGetThreadPriority (h); + /* Highest priority below drivers. */ + CeSetThreadPriority (h, 153); + } + else + { + prio = GetThreadPriority (h); + SetThreadPriority (h, THREAD_PRIORITY_TIME_CRITICAL); + } + + /* Do the work. */ + (*func) (args); + + /* Revert the priority. */ + if (CeSetThreadPriority != NULL) + CeSetThreadPriority (h, prio); + else + SetThreadPriority (h, prio); +} + +#endif + /* Resume all artificially suspended threads if we are continuing execution. */ static int @@ -297,18 +450,37 @@ continue_one_thread (struct inferior_lis return 0; } +static void +do_continue_one_thread (void *args) +{ + int *thread_id_ptr = args; + find_inferior (&all_threads, continue_one_thread, thread_id_ptr); +} + static BOOL child_continue (DWORD continue_status, int thread_id) { - /* The inferior will only continue after the ContinueDebugEvent - call. */ - find_inferior (&all_threads, continue_one_thread, &thread_id); - faked_breakpoint = 0; - - if (!ContinueDebugEvent (current_event.dwProcessId, - current_event.dwThreadId, - continue_status)) - return FALSE; + if (faked_breakpoint) + { + realtime_execute (do_continue_one_thread, &thread_id); + faked_breakpoint = 0; + } + else + { + /* The inferior will only continue after the ContinueDebugEvent + call. */ + find_inferior (&all_threads, continue_one_thread, &thread_id); + + if (!ContinueDebugEvent (current_event.dwProcessId, + current_event.dwThreadId, + continue_status)) + { + DWORD err = GetLastError (); + OUTMSG (("warning: ContinueDebugEvent failed in child_continue, " + "(error %d): %s\n", (int) err, strwinerror (err))); + return FALSE; + } + } return TRUE; } @@ -1278,6 +1450,12 @@ suspend_one_thread (struct inferior_list } static void +suspend_all_threads (void *data) +{ + for_each_inferior (&all_threads, suspend_one_thread); +} + +static void fake_breakpoint_event (void) { OUTMSG2(("fake_breakpoint_event\n")); @@ -1290,7 +1468,7 @@ fake_breakpoint_event (void) current_event.u.Exception.ExceptionRecord.ExceptionCode = EXCEPTION_BREAKPOINT; - for_each_inferior (&all_threads, suspend_one_thread); + realtime_execute (suspend_all_threads, NULL); } /* Get the next event from the child. */