This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[gdbserver/win32] (5/11) New interrupting method : elevating the priority


Hi,

The previous patch that introduced the new interrupting
method, leaves some chances for the inferior to mess the
interrupting, since the operation is not atomic.  For
example by tweaking its own thread's priorities or if the
inferior is calling ResumeThread in its own threads while
gdbserver is stopping them.

Here is the patch that tweaks gdbserver's priorities
so to minimize those chances.

This can never be perfect, since, now we may be adding a bit
of extra entropy to other components of the system, especially
true in WinCE, where we may be inhibiting some other lower
priority thread to execute, even if just for a little.
For WinCE, I'm putting the ceiling on the highest application
priority, which is normally lower than what the time critical
drivers threads use.

This is mostly Leo's code.  I've splitted it from his
previous patch, fixed and cleaned a few things here and there,
added WinCE support, and regtested it on a local
i686-pc-cygwin gdbserver and against a remote arm-wince
gdbserver.

Leo, did I miss anything?

Cheers,
Pedro Alves



2007-11-12  Leo Zayas  <lerele@champenstudios@com>
	    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 |  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.  */




Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]