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]

Re: [PATCH] Fix "PC register is not available" issue


On 04/08/2014 12:32 PM, Pedro Alves wrote:
> On 04/07/2014 07:24 PM, Eli Zaretskii wrote:
>>> Date: Mon, 07 Apr 2014 18:09:16 +0100
>>> From: Pedro Alves <palves@redhat.com>
>>> CC: brobecker@adacore.com, gdb-patches@sourceware.org
>>>
>>>> Funnily enough, I cannot get GDBserver to emit similar warnings in the
>>>> same situation.  I don't understand the reasons for that, since the
>>>> code is very similar, and with a single exception, we do check the
>>>> return values of calls to GetThreadContext, SetThreadContext, and
>>>> SuspendThread in GDBserver.  But the fact remains that no warnings
>>>> about these threads are ever seen when debugging remotely.  I do see
>>>> the extra threads under GDBserver as well.
>>>
>>> GDBserver's warnings are guarded by 'if (debug_threads)' (see OUTMSG2).
>>
>> But the warnings I was talking about are output with OUTMSG, which
>> doesn't depend on debug_threads.  Here's an example:
>>
>>   static void
>>   suspend_one_thread (struct inferior_list_entry *entry)
>>   {
>>     struct thread_info *thread = (struct thread_info *) entry;
>>     win32_thread_info *th = inferior_target_data (thread);
>>
>>     if (!th->suspended)
>>       {
>> 	if (SuspendThread (th->h) == (DWORD) -1)
>> 	  {
>> 	    DWORD err = GetLastError ();
>> 	    OUTMSG (("warning: SuspendThread failed in suspend_one_thread, "
>> 		     "(error %d): %s\n", (int) err, strwinerror (err)));
>>
>> Did I miss something?
> 
> No, it was I who missed that.  I found a Windows machine and am taking
> a look.

D**n, I had forgotten how slow Windows is...


OK, so I looked for a Windows machine to try it.  I can definitely
reproduce the warning with gdbserver, using the testcase from PR14018.
Like:

$ cat test.c
#include <windows.h>

int main()
{    
    while (1)
        DebugBreakProcess(GetCurrentProcess());
}

$ gcc win32_suspend_issue.c -o win32_suspend_issue.exe -g3 -O0
$
$ ./gdb -data-directory=data-directory --args ./win32_suspend_issue.exe
[snip]
Reading symbols from ./win32_suspend_issue.exe...done.
(gdb) tbreak main
Temporary breakpoint 1 at 0x4010ba: file win32_suspend_issue.c, line 6.
(gdb) tar remote :9999
Remote debugging using :9999
0x770c1004 in ntdll!RtlUpcaseUnicodeToOemN () from /cygdrive/c/Windows/SysWOW64/ntdll.dll
(gdb) c
Continuing.

Temporary breakpoint 1, main () at win32_suspend_issue.c:6
6               DebugBreakProcess(GetCurrentProcess());
(gdb) b 6
Breakpoint 2 at 0x4010ba: file win32_suspend_issue.c, line 6.
(gdb) commands 
Type commands for breakpoint(s) 2, one per line.
End with a line saying just "end".
>continue
>end
(gdb) handle SIGTRAP nostop
SIGTRAP is used by the debugger.
Are you sure you want to change it? (y or n) y
Signal        Stop      Print   Pass to program Description
SIGTRAP       No        Yes     No              Trace/breakpoint trap
(gdb) set pagination off 
(gdb) c

Breakpoint 2, main () at win32_suspend_issue.c:6
6               DebugBreakProcess(GetCurrentProcess());
[New Thread 3156]

Program received signal SIGTRAP, Trace/breakpoint trap.

Breakpoint 2, main () at win32_suspend_issue.c:6
6               DebugBreakProcess(GetCurrentProcess());

Breakpoint 2, main () at win32_suspend_issue.c:6
6               DebugBreakProcess(GetCurrentProcess());

Breakpoint 2, main () at win32_suspend_issue.c:6
6               DebugBreakProcess(GetCurrentProcess());

Breakpoint 2, main () at win32_suspend_issue.c:6
6               DebugBreakProcess(GetCurrentProcess());

Breakpoint 2, main () at win32_suspend_issue.c:6
6               DebugBreakProcess(GetCurrentProcess());

Breakpoint 2, main () at win32_suspend_issue.c:6
6               DebugBreakProcess(GetCurrentProcess());

Breakpoint 2, main () at win32_suspend_issue.c:6
6               DebugBreakProcess(GetCurrentProcess());
[New Thread 3524]

Program received signal SIGTRAP, Trace/breakpoint trap.

[snip lots of the same, forever]


While on the gdbserver terminal:

$ gdbserver/gdbserver.exe :9999 ./win32_suspend_issue.exe
Process ./win32_suspend_issue.exe created; pid = 5888
Listening on port 9999
Remote debugging from host 127.0.0.1
warning: SuspendThread failed in thread_rec, (error 5): Acesso negado.
warning: SuspendThread failed in thread_rec, (error 5): Acesso negado.
warning: SuspendThread failed in thread_rec, (error 5): Acesso negado.
warning: SuspendThread failed in thread_rec, (error 5): Acesso negado.
warning: SuspendThread failed in thread_rec, (error 5): Acesso negado.
warning: SuspendThread failed in thread_rec, (error 5): Acesso negado.
warning: SuspendThread failed in thread_rec, (error 5): Acesso negado.
warning: SuspendThread failed in thread_rec, (error 5): Acesso negado.
...

Now, unlike in the GDB case,  gdbserver reads registers
from the thread anyway, so we don't see the "PC register is not
available" error.  So I went a step further, and patched gdbserver
like this, to force it to error out on the subsequent resume,
if previously suspending the thread failed:

---
diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index bc2506c..ea3d70b 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -177,8 +177,11 @@ thread_rec (ptid_t ptid, int get_context)
 	  if (SuspendThread (th->h) == (DWORD) -1)
 	    {
 	      DWORD err = GetLastError ();
-	      OUTMSG (("warning: SuspendThread failed in thread_rec, "
-		       "(error %d): %s\n", (int) err, strwinerror (err)));
+	      OUTMSG (("warning: SuspendThread failed in thread_rec(%s), "
+		       "(error %d): %s\n",
+		       target_pid_to_str (ptid),
+		       (int) err, strwinerror (err)));
+	      th->suspended = -1;
 	    }
 	  else
 	    th->suspended = 1;
@@ -420,8 +423,15 @@ continue_one_thread (struct inferior_list_entry *this_thread, void *id_ptr)
   int thread_id = * (int *) id_ptr;
   win32_thread_info *th = inferior_target_data (thread);
 
-  if ((thread_id == -1 || thread_id == th->tid)
-      && th->suspended)
+  if ((thread_id == -1 || thread_id == th->tid))
+    {
+      if (th->suspended == -1)
+	{
+	  th->suspended = 1;
+	  error ("SuspendThread had failed for thread %d.\n", (int) th->tid);
+	}
+
+      if (th->suspended)
     {
       if (th->context.ContextFlags)
 	{
@@ -437,6 +447,7 @@ continue_one_thread (struct inferior_list_entry *this_thread, void *id_ptr)
 	}
       th->suspended = 0;
     }
+    }
 
   return 0;
 }


That allows us letting the test case run free, and when
SuspendThread fails, "continue" fails, and we can then
get the backtrace of the failing thread.

Re-running the test under the patched gdbserver, I
now get:

$ ./gdbserver.exe :9999 ../win32_suspend_issue.exe
Process ../win32_suspend_issue.exe created; pid = 5564
Listening on port 9999
Remote debugging from host 127.0.0.1
warning: SuspendThread failed in thread_rec(LWP 5564.2428), (error 5): Acesso negado.
SuspendThread had failed for thread 2428.

And on the GDB side, we see:

(gdb) info threads 
[New Thread 4216]
[New Thread 1096]
[New Thread 5412]
[New Thread 3248]
  Id   Target Id         Frame 
  10   Thread 3248       0x770301b4 in ntdll!KiIntSystemCall () from /cygdrive/c/Windows/SysWOW64/ntdll.dll
  9    Thread 5412       0x770301b4 in ntdll!KiIntSystemCall () from /cygdrive/c/Windows/SysWOW64/ntdll.dll
  8    Thread 1096       0x770301b4 in ntdll!KiIntSystemCall () from /cygdrive/c/Windows/SysWOW64/ntdll.dll
  7    Thread 4216       0x7703f905 in ntdll!RtlUpcaseUnicodeChar () from /cygdrive/c/Windows/SysWOW64/ntdll.dll
  6    Thread 2428       0x77040096 in ntdll!RtlDosApplyFileIsolationRedirection_Ustr () from /cygdrive/c/Windows/SysWOW64/ntdll.dll
* 1    Thread 4756       0x004010bf in main () at /home/pedro/host-pedro/win32_suspend_issue.c:6
(gdb) t 6
[Switching to thread 6 (Thread 2428)]
#0  0x77040096 in ntdll!RtlDosApplyFileIsolationRedirection_Ustr () from /cygdrive/c/Windows/SysWOW64/ntdll.dll
(gdb) bt
#0  0x77040096 in ntdll!RtlDosApplyFileIsolationRedirection_Ustr () from /cygdrive/c/Windows/SysWOW64/ntdll.dll
#1  0x77040096 in ntdll!RtlDosApplyFileIsolationRedirection_Ustr () from /cygdrive/c/Windows/SysWOW64/ntdll.dll
#2  0x77076795 in ntdll!RtlDeleteAtomFromAtomTable () from /cygdrive/c/Windows/SysWOW64/ntdll.dll
#3  0x00000000 in ?? ()


Now, that backtrace is unfortunately doesn't look complete,
as I'm using a 32-bit gdb/gdbserver here, but I'm going to guess
RtlDeleteAtomFromAtomTable is being called during thread
termination.  Searching the web for that function at least finds
this backtrace, at
<http://www.windowsbbs.com/windows-xp/59014-help-stop-errors.html>:

STACK_TEXT:  
b857275c 804fe507 0000008e c0000005 bf802ade nt!KeBugCheckEx+0x1b
b8572b24 80541075 b8572b40 00000000 b8572b94 nt!KiDispatchException+0x3b1
b8572b8c 80541026 b8572c18 bf802ade badb0d00 nt!CommonDispatchException+0x4d
b8572ba8 805dbbc3 bc63da10 bc630000 0000057b nt!Kei386EoiHelper+0x18a
b8572c18 bf8c0bbb e47b808c 86475f18 e4867568 nt!RtlDeleteAtomFromAtomTable+0xcf
b8572c2c bf8209f6 e47b8008 85466da8 00000000 win32k!DestroyProcessesClasses+0x18
b8572c54 bf80f0b6 00000001 b8572c7c bf80f17a win32k!xxxDestroyThreadInfo+0x21d
b8572c60 bf80f17a 85466da8 00000001 00000000 win32k!UserThreadCallout+0x4b
b8572c7c 805d0c80 85466da8 00000001 85466da8 win32k!W32pThreadCallout+0x3d
b8572d08 805d1098 00000000 85466da8 00000000 nt!PspExitThread+0x3cc
b8572d28 805d1273 85466da8 00000000 b8572d64 nt!PspTerminateThreadByPointer+0x52
b8572d54 8054060c 00000000 00000000 0006fee4 nt!NtTerminateProcess+0x105
b8572d54 7c90eb94 00000000 00000000 0006fee4 nt!KiFastCallEntry+0xfc
0006fde4 7c90e89a 7c81cd96 ffffffff 00000000 ntdll!KiFastSystemCallRet
0006fde8 7c81cd96 ffffffff 00000000 00000000 ntdll!ZwTerminateProcess+0xc
0006fee4 7c81cdee 00000000 77e8f3b0 ffffffff kernel32!_ExitProcess+0x62
0006fef8 77c39d45 00000000 0006ff14 77c39e78 kernel32!ExitProcess+0x14
0006ff04 77c39e78 00000000 00000000 0006ff28 msvcrt!__crtExitProcess+0x32
0006ff14 77c39e90 00000000 00000000 00000002 msvcrt!_cinit+0xee
0006ff28 01019cf6 00000000 00fdcd5c 7c90e1fe msvcrt!exit+0x12
0006ffc0 7c816fd7 00fdcd5c 7c90e1fe 7ffdf000 wordpad!wWinMainCRTStartup+0x1a9
0006fff0 00000000 01019b4d 00000000 78746341 kernel32!BaseProcessStart+0x23

(shows RtlDeleteAtomFromAtomTable being called from within NtTerminateProcess.)


My next step was then to apply a similar patch on the GDB
side.  Like so:

diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index fe40c4d..6a0c861 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -312,9 +312,10 @@ thread_rec (DWORD id, int get_context)
 		    warning (_("SuspendThread (tid=0x%x) failed."
 			       " (winerr %u)"),
 			     (unsigned) id, (unsigned) err);
-		    return NULL;
+		    th->suspended = -2;
 		  }
-		th->suspended = 1;
+		else
+		  th->suspended = 1;
 	      }
 	    else if (get_context < 0)
 	      th->suspended = -1;
@@ -1200,6 +1201,12 @@ windows_continue (DWORD continue_status, int id)
     if ((id == -1 || id == (int) th->id)
 	&& th->suspended)
       {
+	if (th->suspended == -2)
+	  {
+	    th->suspended = 1;
+	    error ("SuspendThread had failed.\n");
+	  }
+
 	if (debug_registers_changed)
 	  {
 	    th->context.ContextFlags |= CONTEXT_DEBUG_REGISTERS;



That is, again, if SuspendThread fails, GDB still fetches registers,
like in your patch, but in addition, prevent resuming (once)
so we can get the failing thread's backtrace.

Still using the test case from the PR, I see, with the native
gdb:

Program received signal SIGTRAP, Trace/breakpoint trap.
warning: SuspendThread (tid=0x1318) failed. (winerr 5)

Breakpoint 4, main () at /home/pedro/win32_suspend_issue.c:6
6       in /home/pedro/win32_suspend_issue.c
0x0000000000401514 in main () at /home/pedro/win32_suspend_issue.c:6
6       in /home/pedro/win32_suspend_issue.c
SuspendThread had failed.


And tid=0x1318 is inside ZwTerminateThread:

(gdb) info threads
  Id   Target Id         Frame 
  123  Thread 5316.0x1318 0x0000000076e90aea in ntdll!ZwTerminateThread () from C:\Windows\SYSTEM32\ntdll.dll
* 1    Thread 5316.0x1004 0x0000000000401514 in main () at /home/pedro/win32_suspend_issue.c:6
(gdb) t 123
[Switching to thread 123 (Thread 5316.0x1318)]
#0  0x0000000076e90aea in ntdll!ZwTerminateThread () from C:\Windows\SYSTEM32\ntdll.dll
(gdb) bt
#0  0x0000000076e90aea in ntdll!ZwTerminateThread () from C:\Windows\SYSTEM32\ntdll.dll
#1  0x0000000076e85c78 in ntdll!RtlExitUserThread () from C:\Windows\SYSTEM32\ntdll.dll
#2  0x0000000076f37a61 in ntdll!DbgUiRemoteBreakin () from C:\Windows\SYSTEM32\ntdll.dll
#3  0x0000000076d3651d in KERNEL32!BaseThreadInitThunk () from C:\Windows\system32\kernel32.dll
#4  0x0000000076e6ba01 in ntdll!RtlUserThreadStart () from C:\Windows\SYSTEM32\ntdll.dll
#5  0x0000000000000000 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb)


With the native debugger, the fail always happens in 
ZwTerminateThread (and I tried both 64-bit and 32-bit native
gdbs), while with gdbserver the fail always happens in
RtlDosApplyFileIsolationRedirection.  My currently hypothesis for this is
that gdbserver affects timings differently then when running under
gdb (remote protocol traffic, etc.).

(Oh this is a Windows 7 box, btw)

Then I tried simplifying the test case, and remove
remote thread injection from the picture (DebugBreakProcess
uses remote thread injection, and I don't know whether
that's special in terms of SuspendThread permissions, or whether
that's somehow short circuited in terms of breaking
GetCurrentProcess()), with this alternative test case:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include <windows.h>

static DWORD WINAPI
thread_entry (void *arg)
{
  return 0;
}

int
main ()
{
  DWORD threadId;

	
  while (1)
    {
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
    }
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

And sourcing this GDB script:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
start
tb main
continue

break 17
command
	continue
end

set pagination off

continue
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

And sure enough, eventually, I get:

(...)
[Thread 11432.0x1208 exited with code 0]
[Thread 11432.0x813c exited with code 0]
[New Thread 11432.0x8290]
[Thread 11432.0x8290 exited with code 0]
[New Thread 11432.0x78d4]
[Thread 11432.0x78d4 exited with code 0]
[New Thread 11432.0x6ed8]
[Thread 11432.0x6ed8 exited with code 0]

Breakpoint 1, main () at win32_spawn_threads.c:17
17      in win32_spawn_threads.c
[New Thread 11432.0x8334]
warning: SuspendThread (tid=0x8334) failed. (winerr 5)
0x0000000000401520 in main () at win32_spawn_threads.c:17
17      in win32_spawn_threads.c
./win32_spawn_threads.gdb:14: Error in sourced command file:
SuspendThread had failed.

(gdb) info threads
  Id   Target Id         Frame 
  6870 Thread 11432.0x8334 0x0000000076e90aea in ntdll!ZwTerminateThread () from C:\Windows\SYSTEM32\ntdll.dll
  6863 Thread 11432.0x7030 0x0000000076e905fa in ntdll!ZwWaitForSingleObject () from C:\Windows\SYSTEM32\ntdll.dll
  6862 Thread 11432.0x8294 0x0000000076e905fa in ntdll!ZwWaitForSingleObject () from C:\Windows\SYSTEM32\ntdll.dll
...
(gdb) t 6870
[Switching to thread 6870 (Thread 11432.0x8334)]
#0  0x0000000076e90aea in ntdll!ZwTerminateThread () from C:\Windows\SYSTEM32\ntdll.dll
(gdb) bt
#0  0x0000000076e90aea in ntdll!ZwTerminateThread () from C:\Windows\SYSTEM32\ntdll.dll
#1  0x0000000076e85c78 in ntdll!RtlExitUserThread () from C:\Windows\SYSTEM32\ntdll.dll
#2  0x0000000076d36525 in KERNEL32!BaseThreadInitThunk () from C:\Windows\system32\kernel32.dll
#3  0x0000000076e6ba01 in ntdll!RtlUserThreadStart () from C:\Windows\SYSTEM32\ntdll.dll
#4  0x0000000000000000 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb)


and in 32-bit mode, the equivalent:

[New Thread 13192.0x8248]
[New Thread 13192.0x82d0]
[New Thread 13192.0x718c]
[New Thread 13192.0x7214]
[New Thread 13192.0x8238]
warning: SuspendThread (tid=0x8238) failed. (winerr 5)

Breakpoint 3, main () at win32_spawn_threads.c:17
17      in win32_spawn_threads.c
0x00401585 in main () at win32_spawn_threads.c:17
17      in win32_spawn_threads.c
./win32_spawn_threads.gdb:14: Error in sourced command file:
SuspendThread had failed.

(gdb) info threads
  Id   Target Id         Frame 
  635  Thread 13192.0x8238 0x77040096 in ntdll!ZwTerminateThread () from C:\Windows\SysWOW64\ntdll.dll
  634  Thread 13192.0x7214 0x7703f8d1 in ntdll!ZwWaitForSingleObject () from C:\Windows\SysWOW64\ntdll.dll
  633  Thread 13192.0x718c 0x770301b4 in ntdll!RtlUserThreadStart () from C:\Windows\SysWOW64\ntdll.dll
  632  Thread 13192.0x82d0 0x7703f8d1 in ntdll!ZwWaitForSingleObject () from C:\Windows\SysWOW64\ntdll.dll
  631  Thread 13192.0x8248 0x7703f8d1 in ntdll!ZwWaitForSingleObject () from C:\Windows\SysWOW64\ntdll.dll
  630  Thread 13192.0x8258 0x7703f8d1 in ntdll!ZwWaitForSingleObject () from C:\Windows\SysWOW64\ntdll.dll
  629  Thread 13192.0x82a0 0x7703f8d1 in ntdll!ZwWaitForSingleObject () from C:\Windows\SysWOW64\ntdll.dll
  628  Thread 13192.0x6544 0x7703f8d1 in ntdll!ZwWaitForSingleObject () from C:\Windows\SysWOW64\ntdll.dll
  627  Thread 13192.0x8280 0x770301b4 in ntdll!RtlUserThreadStart () from C:\Windows\SysWOW64\ntdll.dll
  616  Thread 13192.0x71a4 0x7703f8d1 in ntdll!ZwWaitForSingleObject () from C:\Windows\SysWOW64\ntdll.dll
  590  Thread 13192.0x8298 0x770301b4 in ntdll!RtlUserThreadStart () from C:\Windows\SysWOW64\ntdll.dll
  578  Thread 13192.0x2ba0 0x7703f8d1 in ntdll!ZwWaitForSingleObject () from C:\Windows\SysWOW64\ntdll.dll
  574  Thread 13192.0x2560 0x7703f8d1 in ntdll!ZwWaitForSingleObject () from C:\Windows\SysWOW64\ntdll.dll
  558  Thread 13192.0x6a94 0x7703f8d1 in ntdll!ZwWaitForSingleObject () from C:\Windows\SysWOW64\ntdll.dll
  557  Thread 13192.0x15dc 0x7703f8d1 in ntdll!ZwWaitForSingleObject () from C:\Windows\SysWOW64\ntdll.dll
  544  Thread 13192.0x2d90 0x7703f9d9 in ntdll!ZwSetEvent () from C:\Windows\SysWOW64\ntdll.dll
  519  Thread 13192.0x118c 0x7703f8d1 in ntdll!ZwWaitForSingleObject () from C:\Windows\SysWOW64\ntdll.dll
* 1    Thread 13192.0x3390 0x00401585 in main () at win32_spawn_threads.c:17
(gdb) t 635
[Switching to thread 635 (Thread 13192.0x8238)]
#0  0x77040096 in ntdll!ZwTerminateThread () from C:\Windows\SysWOW64\ntdll.dll
(gdb) bt
#0  0x77040096 in ntdll!ZwTerminateThread () from C:\Windows\SysWOW64\ntdll.dll
#1  0x77040096 in ntdll!ZwTerminateThread () from C:\Windows\SysWOW64\ntdll.dll
#2  0x77076795 in ntdll!RtlExitUserThread () from C:\Windows\SysWOW64\ntdll.dll
#3  0x76903371 in KERNEL32!BaseThreadInitThunk () from C:\Windows\syswow64\kernel32.dll
#4  0x7705bf32 in ntdll!RtlInitializeExceptionChain () from C:\Windows\SysWOW64\ntdll.dll
#5  0x7705bf05 in ntdll!RtlInitializeExceptionChain () from C:\Windows\SysWOW64\ntdll.dll
#6  0x00000000 in ?? ()
(gdb) 


So at least this test case shows that the issue very much looks
like indeed caused by trying to suspend threads that are more dead
than alive.  I'd be very curious to see the backtrace you get
for the failing thread in your test case (I guess emacs?).

+		    /* We get Access Denied (5) when trying to suspend
+		       threads that Windows started on behalf of the
+		       debuggee, usually when those threads are just
+		       about to exit.  */
+		    if (err != ERROR_ACCESS_DENIED)

I've shown above that whether it was Windows or the program
itself that started the threads is irrelevant, it'd be good to 
reword this comment.

-- 
Pedro Alves

>From d935c6d43e035c15ba64c1e8094943ac63d8e711 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Tue, 8 Apr 2014 11:37:26 +0100
Subject: [PATCH] Force the next resume to fail when SuspendThread fails

---
 gdb/gdbserver/win32-low.c | 19 +++++++++++++++----
 gdb/windows-nat.c         | 11 +++++++++--
 2 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index bc2506c..ea3d70b 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -177,8 +177,11 @@ thread_rec (ptid_t ptid, int get_context)
 	  if (SuspendThread (th->h) == (DWORD) -1)
 	    {
 	      DWORD err = GetLastError ();
-	      OUTMSG (("warning: SuspendThread failed in thread_rec, "
-		       "(error %d): %s\n", (int) err, strwinerror (err)));
+	      OUTMSG (("warning: SuspendThread failed in thread_rec(%s), "
+		       "(error %d): %s\n",
+		       target_pid_to_str (ptid),
+		       (int) err, strwinerror (err)));
+	      th->suspended = -1;
 	    }
 	  else
 	    th->suspended = 1;
@@ -420,8 +423,15 @@ continue_one_thread (struct inferior_list_entry *this_thread, void *id_ptr)
   int thread_id = * (int *) id_ptr;
   win32_thread_info *th = inferior_target_data (thread);
 
-  if ((thread_id == -1 || thread_id == th->tid)
-      && th->suspended)
+  if ((thread_id == -1 || thread_id == th->tid))
+    {
+      if (th->suspended == -1)
+	{
+	  th->suspended = 1;
+	  error ("SuspendThread had failed for thread %d.\n", (int) th->tid);
+	}
+
+      if (th->suspended)
     {
       if (th->context.ContextFlags)
 	{
@@ -437,6 +447,7 @@ continue_one_thread (struct inferior_list_entry *this_thread, void *id_ptr)
 	}
       th->suspended = 0;
     }
+    }
 
   return 0;
 }
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index fe40c4d..6a0c861 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -312,9 +312,10 @@ thread_rec (DWORD id, int get_context)
 		    warning (_("SuspendThread (tid=0x%x) failed."
 			       " (winerr %u)"),
 			     (unsigned) id, (unsigned) err);
-		    return NULL;
+		    th->suspended = -2;
 		  }
-		th->suspended = 1;
+		else
+		  th->suspended = 1;
 	      }
 	    else if (get_context < 0)
 	      th->suspended = -1;
@@ -1200,6 +1201,12 @@ windows_continue (DWORD continue_status, int id)
     if ((id == -1 || id == (int) th->id)
 	&& th->suspended)
       {
+	if (th->suspended == -2)
+	  {
+	    th->suspended = 1;
+	    error ("SuspendThread had failed.\n");
+	  }
+
 	if (debug_registers_changed)
 	  {
 	    th->context.ContextFlags |= CONTEXT_DEBUG_REGISTERS;
-- 
1.7.11.7

#include <windows.h>

static DWORD WINAPI
thread_entry (void *arg)
{
  return 0;
}

int
main ()
{
  DWORD threadId;

	
  while (1)
    {
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
      CreateThread (NULL, 0, thread_entry, NULL, 0, &threadId);
    }
}

Attachment: win32_spawn_threads.gdb
Description: Text document

#include <windows.h>

int main()
{    
    while (1)
        DebugBreakProcess(GetCurrentProcess());
}

Attachment: win32_suspend_issue.gdb
Description: Text document


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