From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 31870 invoked by alias); 12 Nov 2007 02:07:40 -0000 Received: (qmail 31682 invoked by uid 22791); 12 Nov 2007 02:07:39 -0000 X-Spam-Check-By: sourceware.org Received: from mu-out-0910.google.com (HELO mu-out-0910.google.com) (209.85.134.188) by sourceware.org (qpsmtpd/0.31) with ESMTP; Mon, 12 Nov 2007 02:07:35 +0000 Received: by mu-out-0910.google.com with SMTP id g7so1333701muf for ; Sun, 11 Nov 2007 18:07:32 -0800 (PST) Received: by 10.86.33.10 with SMTP id g10mr4131633fgg.1194833252019; Sun, 11 Nov 2007 18:07:32 -0800 (PST) Received: from ?192.168.0.4? ( [62.169.107.97]) by mx.google.com with ESMTPS id 33sm5848540nfu.2007.11.11.18.07.28 (version=TLSv1/SSLv3 cipher=RC4-MD5); Sun, 11 Nov 2007 18:07:30 -0800 (PST) Message-ID: <4737B563.4070909@portugalmail.pt> Date: Mon, 12 Nov 2007 02:07:00 -0000 From: Pedro Alves User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-BR; rv:1.8.1.6) Gecko/20070728 Thunderbird/2.0.0.6 Mnenhy/0.7.5.0 MIME-Version: 1.0 To: gdb-patches@sourceware.org, Lerele Subject: [gdbserver/win32] (5/11) New interrupting method : elevating the priority Content-Type: multipart/mixed; boundary="------------090502060303060505010407" X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2007-11/txt/msg00218.txt.bz2 This is a multi-part message in MIME format. --------------090502060303060505010407 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Content-length: 1069 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 --------------090502060303060505010407 Content-Type: text/x-diff; name="gdbserver_priority.diff" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gdbserver_priority.diff" Content-length: 8021 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. */ --------------090502060303060505010407--