http://bugs.winehq.org/show_bug.cgi?id=10478
Summary: wine server get/set thread context doesn't work correctly if simultaneously used by debugger and debuggee Product: Wine Version: CVS/GIT Platform: PC OS/Version: Linux Status: UNCONFIRMED Severity: normal Priority: P2 Component: wine-debug AssignedTo: wine-bugs@winehq.org ReportedBy: focht@gmx.net
Created an attachment (id=9209) --> (http://bugs.winehq.org/attachment.cgi?id=9209) WINEDEBUG=+tid,+server,+win trace of debugging session
Hello,
while debugging a target with some debugger I experienced severe problems with wine server get/set thread context implementation. It doesn't work correctly if the get/set thread context API is simultaneously used by both, the debugger and the debuggee to manage exception events/control execution transfer.
Attached is WINEDEBUG=+tid,+seh,+win trace (don't bother with +win channel output, was for my own purpose).
pid=0008, tid=0009 debugger main thread pid=0008, tid=000d debugger debug event loop thread
pid=000e, tid=000f debuggee main thread pid=000e, tid=0010 debuggee child thread pid=000e, tid=0011 debuggee child thread pid=000e, tid=0012 debuggee child thread
Search for "000d: get_thread_context() = ACCESS_DENIED { self=0, context={} }" in attached log and walk your way back...
--- snip --- 000f: queue_exception_event( first=1, record={context={flags=00010007,eax=7b82c455,ebx=7b8ad1e4,ecx=00000000,edx=00004000,esi=00004000,edi=00197430,ebp=0034e5d4,eip=7b841378,esp=0034e570,eflags=00200216,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=a3f67070,dr1=0012e150,dr2=0034eec0,dr3=00000000,dr6=00000006,dr7=00000200,float={00000003,0034e234,33746e49,00000032,00000000,000000f8,0034e280,0034e268,00087510,00198af0,00110014,00000001,00000318,00000000,4d430002,4d430000,00198e18,00000010,00000000,00000000,00000000,4d430003,00000000,00000000,00000000,00000000,00000000,00000000}},rec={code=e06d7363,flags=1,rec=(nil),addr=0x7b841300,params={19930520,34e670,79f9acc4}} ) 000d: *wakeup* signaled=0 cookie=0x616e24c0 0010: *sent signal* signal=10 0011: *sent signal* signal=10 0012: *sent signal* signal=10 000f: queue_exception_event() = 0 { handle=0x244 } --- snip ---
Debuggee main thread throws first chance C++ exception (queues event). Ok.
--- snip --- 0010: set_thread_context( handle=0xfffffffe, flags=00010007, suspend=1, context={flags=0001001f,eax=00000003,ebx=0000001f,ecx=613516a4,edx=00000008,esi=613516a4,edi=7ffd4000,ebp=613516b8,eip=600007f0,esp=61351670,eflags=00200293,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={ffff037f,ffff4000,ffffffff,604b434d,035f0073,0034dc34,0000007b,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,3fff8000,00000000,80000000,00003ffe,00000000,3ffe8000,00000000,80000000,00003ffe,00000000,40038000,00004000}} ) 0010: set_thread_context() = 0 { self=1 } 0011: set_thread_context( handle=0xfffffffe, flags=00010007, suspend=1, context={flags=0001001f,eax=00000003,ebx=00000023,ecx=614624a0,edx=00000008,esi=614624a0,edi=7ffd0000,ebp=614624b4,eip=600007f0,esp=6146246c,eflags=00200293,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={ffff037f,ffff0100,ffffffff,79e7a66b,06d90073,00000000,0000007b,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,3fff8000,00000000,80000000,00003ffe,00000000,3ffe8000,00000000,80000000,00003ffd,80000000,40078662,00000100}} ) 0011: set_thread_context() = 0 { self=1 } 0012: set_thread_context( handle=0xfffffffe, flags=00010007, suspend=1, context={flags=0001001f,eax=00000003,ebx=0000003a,ecx=6c101d70,edx=00000008,esi=6c101d70,edi=7ffcc000,ebp=6c101d84,eip=600007f0,esp=6c101d3c,eflags=00200293,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={ffff037f,ffff0120,ffffffff,79e7a66b,06d90073,00000000,0000007b,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,3fff8000,00000000,80000000,00003ffe,00000000,00000000,00000000,80000000,00003ffd,60000000,400785bc,00000120}} ) 0012: set_thread_context() = 0 { self=1 } --- snip ---
The debuggee child threads get SIGUSR1, suspend themselves (on purpose!). Ok.
--- snip --- 000d: wait_debug_event( get_handle=1 ) 000d: wait_debug_event() = 0 { pid=000e, tid=000f, wait=(nil), event={exception,{code=e06d7363,flags=1,rec=(nil),addr=0x7b841300,params={19930520,34e670,79f9acc4},first=1} } .. 000d: continue_debug_event( pid=000e, tid=000f, status=-2147418111 ) 000f: *wakeup* signaled=0 cookie=0x34dfd4 0010: *wakeup* signaled=258 cookie=0x7ffd77b4 0011: *wakeup* signaled=258 cookie=0x7ffd37b4 000d: continue_debug_event() = 0 .. --- snip ---
Debugger wakes up and sees first chance C++ exception, decides to pass it to debuggee (resumes execution of threads). Ok.
--- snip --- 000f: get_exception_status( handle=0x244 ) 000f: get_exception_status() = 80010001 { context={flags=00010007,eax=7b82c455,ebx=7b8ad1e4,ecx=00000000,edx=00004000,esi=00004000,edi=00197430,ebp=0034e5d4,eip=7b841378,esp=0034e570,eflags=00200216,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=a3f67070,dr1=0012e150,dr2=0034eec0,dr3=00000000,dr6=00000006,dr7=00000200,float={00000003,0034e234,33746e49,00000032,00000000,000000f8,0034e280,0034e268,00087510,00198af0,00110014,00000001,00000318,00000000,4d430002,4d430000,00198e18,00000010,00000000,00000000,00000000,4d430003,00000000,00000000,00000000,00000000,00000000,00000000}} } 0010: get_thread_context( handle=0xfffffffe, flags=00010007, suspend=1 ) 0010: get_thread_context() = 0 { self=1, context={flags=0001001f,eax=00000003,ebx=0000001f,ecx=613516a4,edx=00000008,esi=613516a4,edi=7ffd4000,ebp=613516b8,eip=600007f0,esp=61351670,eflags=00200293,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={ffff037f,ffff4000,ffffffff,604b434d,035f0073,0034dc34,0000007b,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,3fff8000,00000000,80000000,00003ffe,00000000,3ffe8000,00000000,80000000,00003ffe,00000000,40038000,00004000}} } 0011: get_thread_context( handle=0xfffffffe, flags=00010007, suspend=1 ) 0011: get_thread_context() = 0 { self=1, context={flags=0001001f,eax=00000003,ebx=00000023,ecx=614624a0,edx=00000008,esi=614624a0,edi=7ffd0000,ebp=614624b4,eip=600007f0,esp=6146246c,eflags=00200293,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={ffff037f,ffff0100,ffffffff,79e7a66b,06d90073,00000000,0000007b,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,3fff8000,00000000,80000000,00003ffe,00000000,3ffe8000,00000000,80000000,00003ffd,80000000,40078662,00000100}} } 000d: select( flags=4, cookie=0x616e24c0, signal=(nil), prev_apc=(nil), timeout=+1.0000000, result={}, handles={0xa8} ) 000d: select() = PENDING { apc_handle=(nil), timeout=1c828ffa61af642 (+1.0000000), call={APC_NONE} } 0012: get_thread_context( handle=0xfffffffe, flags=00010007, suspend=1 ) 0012: get_thread_context() = 0 { self=1, context={flags=0001001f,eax=00000003,ebx=0000003a,ecx=6c101d70,edx=00000008,esi=6c101d70,edi=7ffcc000,ebp=6c101d84,eip=600007f0,esp=6c101d3c,eflags=00200293,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={ffff037f,ffff0120,ffffffff,79e7a66b,06d90073,00000000,0000007b,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,3fff8000,00000000,80000000,00003ffe,00000000,00000000,00000000,80000000,00003ffd,60000000,400785bc,00000120}} } 000f: get_process_info( handle=0xffffffff ) 000f: get_process_info() = 0 { pid=000e, ppid=0008, exit_code=259, priority=2, affinity=1, peb=0x7ffdf000, start_time=1c828ff9f2037c6 (-10.7374150), end_time=0 } 000f: queue_exception_event( first=1, record={context={flags=00010007,eax=7b82c455,ebx=7b8ad1e4,ecx=00000000,edx=0034ee58,esi=0034ee58,edi=e0434f4d,ebp=0034ee20,eip=7b841378,esp=0034edbc,eflags=00200212,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=02d410d8,dr2=a3f67060,dr3=00000000,dr6=00110000,dr7=00000002,float={0034eaa4,00001302,00198d10,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001,0034eadc,79e783ca,00110000,00000000,79e783e6,a3f67cf4,00000001,00197430,00000000,00000008,00000000,0034eb14,7a3296dc,ffffffff,79e783e6,79e7839d,00110000}},rec={code=e0434f4d,flags=1,rec=(nil),addr=0x7b841300,params={80004002}} ) --- snip ---
Main debuggee thread SEH evaluates first chance exception and converts it internally to CLR exception, throwing again.
--- snip --- 0010: set_thread_context( handle=0xfffffffe, flags=00010007, suspend=1, context={flags=0001001f,eax=00000003,ebx=0000001f,ecx=613516a4,edx=00000008,esi=613516a4,edi=7ffd4000,ebp=613516b8,eip=600007f0,esp=61351670,eflags=00200293,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={ffff037f,ffff4000,ffffffff,604b434d,035f0073,0034dc34,0000007b,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,3fff8000,00000000,80000000,00003ffe,00000000,3ffe8000,00000000,80000000,00003ffe,00000000,40038000,00004000}} ) 0010: set_thread_context() = 0 { self=1 } 0011: set_thread_context( handle=0xfffffffe, flags=00010007, suspend=1, context={flags=0001001f,eax=00000003,ebx=00000023,ecx=614624a0,edx=00000008,esi=614624a0,edi=7ffd0000,ebp=614624b4,eip=600007f0,esp=6146246c,eflags=00200293,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={ffff037f,ffff0100,ffffffff,79e7a66b,06d90073,00000000,0000007b,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,3fff8000,00000000,80000000,00003ffe,00000000,3ffe8000,00000000,80000000,00003ffd,80000000,40078662,00000100}} ) 0011: set_thread_context() = 0 { self=1 } 0012: set_thread_context( handle=0xfffffffe, flags=00010007, suspend=1, context={flags=0001001f,eax=00000003,ebx=0000003a,ecx=6c101d70,edx=00000008,esi=6c101d70,edi=7ffcc000,ebp=6c101d84,eip=600007f0,esp=6c101d3c,eflags=00200293,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={ffff037f,ffff0120,ffffffff,79e7a66b,06d90073,00000000,0000007b,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,3fff8000,00000000,80000000,00003ffe,00000000,00000000,00000000,80000000,00003ffd,60000000,400785bc,00000120}} ) 0012: set_thread_context() = 0 { self=1 } --- snip ---
The debuggee child threads get SIGUSR1, suspend themselves again (on purpose!). Ok.
--- snip --- 000d: suspend_thread( handle=0xd4 ) 000d: suspend_thread() = 0 { count=0 } 000d: suspend_thread( handle=0xd8 ) 000d: suspend_thread() = 0 { count=0 } 000d: suspend_thread( handle=0xf8 ) 000d: suspend_thread() = 0 { count=0 } 000d: get_thread_context( handle=0xbc, flags=0001003f, suspend=0 ) 000f: *signal* signal=19 000d: get_thread_context() = 0 { self=0, context={flags=0001003f,eax=7b82c455,ebx=7b8ad1e4,ecx=00000000,edx=0034ee58,esi=0034ee58,edi=e0434f4d,ebp=0034ee20,eip=7b841378,esp=0034edbc,eflags=00200212,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={0034eaa4,00001302,00198d10,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001,0034eadc,79e783ca,00110000,00000000,79e783e6,a3f67cf4,00000001,00197430,00000000,00000008,00000000,0034eb14,7a3296dc,ffffffff,79e783e6,79e7839d,00110000}} } 000d: read_process_memory( handle=0xb8, addr=0x7b841378 ) 000f: *signal* signal=19 000d: read_process_memory() = 0 { data={8b} } 000d: resume_thread( handle=0xbc ) 000d: resume_thread() = 0 { count=1 } 000d: resume_thread( handle=0xd4 ) 000d: resume_thread() = 0 { count=1 } 000d: resume_thread( handle=0xd8 ) 000d: resume_thread() = 0 { count=1 } 000d: resume_thread( handle=0xf8 ) 000d: resume_thread() = 0 { count=1 } 000d: continue_debug_event( pid=000e, tid=000f, status=-2147418111 ) 000f: *wakeup* signaled=0 cookie=0x34e824 0010: *wakeup* signaled=258 cookie=0x7ffd77b4 0011: *wakeup* signaled=258 cookie=0x7ffd37b4 0012: *wakeup* signaled=258 cookie=0x7ffcf7b4 000d: continue_debug_event() = 0 --- snip ---
Debugger wakes up again, now seeing the CLR exception. Debugger suspends debuggee threads - although unnecessary due to debugging event before = already suspended. This exception type is unhandled, main debuggee thread receives SIGSTOP. Execution of debuggee threads resumed.
--- snip --- 000f: get_exception_status( handle=0x244 ) 000f: get_exception_status() = 80010001 { context={flags=00010007,eax=7b82c455,ebx=7b8ad1e4,ecx=00000000,edx=0034ee58,esi=0034ee58,edi=e0434f4d,ebp=0034ee20,eip=7b841378,esp=0034edbc,eflags=00200212,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1= .. 000f: *killed* exit_code=0 --- snip ---
Debuggee main thread dies. Ok.
--- snip --- 000d: wait_debug_event( get_handle=1 ) 000d: wait_debug_event() = 0 { pid=000e, tid=000f, wait=(nil), event={exit_thread,code=0} } 000d: suspend_thread( handle=0xd4 ) 000d: suspend_thread() = 0 { count=0 } 000d: suspend_thread( handle=0xd8 ) 000d: suspend_thread() = 0 { count=0 } 000d: suspend_thread( handle=0xf8 ) 000d: suspend_thread() = 0 { count=0 } 000d: get_thread_context( handle=0xbc, flags=0001003f, suspend=0 ) 000d: get_thread_context() = ACCESS_DENIED { self=0, context={} } 000d: get_thread_context( handle=0xbc, flags=0001003f, suspend=0 ) 000d: get_thread_context() = ACCESS_DENIED { self=0, context={} } 000d: get_thread_context( handle=0xbc, flags=0001003f, suspend=0 ) 000d: get_thread_context() = ACCESS_DENIED { self=0, context={} } 000d: get_thread_context( handle=0xbc, flags=0001003f, suspend=0 ) 000d: get_thread_context() = ACCESS_DENIED { self=0, context={} } 000d: get_thread_context( handle=0xbc, flags=0001003f, suspend=0 ) 000d: get_thread_context() = ACCESS_DENIED { self=0, context={} } 000d: read_process_memory( handle=0xb8, addr=(nil) ) 000d: read_process_memory() = ACCESS_DENIED { data={} } .. --- snip ---
Debugger wakes up, sees debuggee (main) thread termination event. Suspends remaining debuggee threads - although unnecessary due to debugging event before = already suspended.
Now the problem: the debugger can't get the thread context of any debuggee threads nor process memory anymore after this point. Basically dead end here...
The reason is the way server/thread.c/get_thread_context() and server/thread.c/set_thread_context() handles thread->context and thread->suspend_context data ("self" vs. "foreign" threads)
child threads "self":
set_thread_context() -> suspend == 1 -> thread->suspend_context filled get_thread_context(): req->suspend == 1 && thread == current && thread->suspend_context
--- snip --- if (thread->context == thread->suspend_context) thread->context = NULL; set_reply_data_ptr( thread->suspend_context, sizeof(CONTEXT) ); thread->suspend_context = NULL; --- snip ---
Which resets the context and suspend context data.
Next time when the debugger itself tries to query the thread context of already suspended debuggee child threads it will obviously fail:
"foreign" debugger thread:
get_thread_context() -> suspend == 0
--- snip --- else if (thread != current && !thread->context) { /* thread is not suspended, retry (if it's still running) */ if (thread->state != RUNNING) set_error( STATUS_ACCESS_DENIED ); else set_error( STATUS_PENDING ); } --- snip ---
I guess this is an oversight, a behavior not really intended?
Regards
http://bugs.winehq.org/show_bug.cgi?id=10478
--- Comment #1 from Austin English austinenglish@gmail.com 2008-06-04 14:26:54 --- Is this still an issue in 1.0-rc3?
http://bugs.winehq.org/show_bug.cgi?id=10478
Austin English austinenglish@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- Status|UNCONFIRMED |RESOLVED Resolution| |ABANDONED
--- Comment #2 from Austin English austinenglish@gmail.com 2008-12-02 05:29:31 --- Abandoned.
http://bugs.winehq.org/show_bug.cgi?id=10478
Austin English austinenglish@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- Status|RESOLVED |CLOSED
--- Comment #3 from Austin English austinenglish@gmail.com 2008-12-02 05:30:33 --- Closing.
http://bugs.winehq.org/show_bug.cgi?id=10478
Austin English austinenglish@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- Version|CVS/GIT |unspecified
http://bugs.winehq.org/show_bug.cgi?id=10478
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Version|unspecified |0.9.49.
--- Comment #4 from Anastasius Focht focht@gmx.net 2011-10-12 04:10:46 CDT --- Hello,
filling/correcting fields ... Aww, abandoned gems .. wish I provided more info at the time ;-)
Regards