This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [gdbserver/win32] (5/11) New interrupting method : elevating the priority
- From: Pedro Alves <pedro_alves at portugalmail dot pt>
- To: gdb-patches at sourceware dot org, Lerele <lerele at champenstudios dot com>
- Date: Mon, 03 Dec 2007 02:45:54 +0000
- Subject: Re: [gdbserver/win32] (5/11) New interrupting method : elevating the priority
- References: <4737B563.4070909@portugalmail.pt> <20071201185809.GD24231@caradoc.them.org>
Daniel Jacobowitz wrote:
On Mon, Nov 12, 2007 at 02:07:31AM +0000, Pedro Alves wrote:
2007-11-12 Leo Zayas
Pedro Alves <pedro_alves@portugalmail.pt>
* 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.
I'm just going to have to trust you on this one. It looks fine.
Thank you.
I've checked the whole series in, except for this one and the next (6).
Leo, could you take a look at the attached patch? It's a
refresh against current cvs, with a minor tweak to minimize a
race in the previous version -- we were creating the threads
but not waiting for them to start.
--
Pedro Alves
2007-12-03 Leo Zayas
Pedro Alves <pedro_alves@portugalmail.pt>
* 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 | 214 +++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 204 insertions(+), 10 deletions(-)
Index: src/gdb/gdbserver/win32-low.c
===================================================================
--- src.orig/gdb/gdbserver/win32-low.c 2007-12-03 01:42:20.000000000 +0000
+++ src/gdb/gdbserver/win32-low.c 2007-12-03 01:42:32.000000000 +0000
@@ -300,6 +300,175 @@ 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)
+{
+ HANDLE* ran = data;
+ SetEvent (*ran);
+ 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;
+ HANDLE *events = 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 i;
+
+ GetProcessAffinityMask (h,
+ &old_process_affinity_mask,
+ &system_affinity_mask);
+
+ /* Set gdbserver to run on all CPUs. */
+ if (SetProcessAffinityMask != NULL)
+ SetProcessAffinityMask (h, system_affinity_mask);
+
+ /* manual reset */
+ interrupt_thread_event = CreateEvent (NULL, TRUE, FALSE, NULL);
+
+ threads = alloca (sizeof (HANDLE) * (sys_info.dwNumberOfProcessors - 1));
+ events = alloca (sizeof (HANDLE) * (sys_info.dwNumberOfProcessors - 1));
+ for (i = 0; i < sys_info.dwNumberOfProcessors - 1; i++)
+ {
+ events[i] = CreateEvent (NULL, FALSE, FALSE, NULL);
+ threads[i] = CreateThread (NULL, 0, consume_cpu_interrupt_thread,
+ &events[i], 0, NULL);
+ }
+
+ /* wait all */
+ WaitForMultipleObjects(sys_info.dwNumberOfProcessors - 1,
+ events, TRUE, INFINITE);
+
+ for (i = 0; i < sys_info.dwNumberOfProcessors - 1; i++)
+ CloseHandle (events[i]);
+ }
+
+ /* Do the work. */
+ (*func) (args);
+
+ /* Revert the priorities. */
+ if (sys_info.dwNumberOfProcessors > 1)
+ {
+ int i;
+
+ /* 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 (i = 0; i < sys_info.dwNumberOfProcessors - 1; i++)
+ CloseHandle (threads[i]);
+ 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
@@ -330,18 +499,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;
}
@@ -1313,6 +1501,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"));
@@ -1325,7 +1519,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. */