This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[RFC] New gdbserver Win32 interrupt code
- From: Lerele <lerele at champenstudios dot com>
- To: gdb-patches at sourceware dot org
- Date: Sun, 04 Nov 2007 02:28:21 +0100
- Subject: [RFC] New gdbserver Win32 interrupt code
Hello,
Sorry for the delay, didn't get the time to continue with gdbserver
Win32 coding.
Some time ago I suggested a method to better support remote interrupt of
debugged process, in this thread:
http://sourceware.org/ml/gdb-patches/2007-03/msg00026.html
The attached patch does just what I described there, with all the
benefits I discussed back then.
I have tried to code the bits with WinCE in mind by copying how it's
already done for WinCE (LoadLibrary basically), but I have not compiled
nor tested it under Windows CE. However, it does work fine with Cygwin.
Also I fixed what I think is a slight bug in thread suspend/resume in
win32-low.c, and something that is also related with this new interrupt
thread pause/resume functionality.
The thread_rec function was setting the gdbserver internal suspend_count
to the number of suspends a thread actually has, as reported by Windows.
However I think this is erroneous to keep this way because a thread may
already be paused by the application being debugged, and as such when
gdbserver pauses/resumes threads, it could wake up a child thread that
should actually stay paused.
I have also kept old functionality with the macro NEW_INT in case
someone needs/want to fallback to the old code. This can also be removed
of course.
Any thoughts about this new functionality?
Hope patch is Ok.
Regards,
Leo.
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 <sys/cygwin.h>
#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 *