Problem in strace -p pid
Sun May 26 18:18:00 GMT 2002
[Sorry: This is rather a long message]
I asked last week about how to get 'strace -p pid' working, since it wasn't
working for me. I've done some fishing about in the source and found that
there seems to be a race condition (on w2k at least) where the child is not
yet being debugged (according to IsDebuggerPresent() for example) despite
the DebugActiveProcess() call having completed in strace.exe.
I've got a fix that seems to work (signalling the target process at a
suitable later moment) but I thought I'd just set out my logic here for
others to check if they could. This could also form part of a
"how-strace-works.txt", on the off-chance I've got it right of course . . .
Anyhow, here's my account of how strace works when its attaching to a
1) strace.exe attachs to the target process for debugging (via
2) Then it sends it the magic signal __SIGSTRACE (via
cygwin_internal(CW_STRACE_TOGGLE, pid) in external.cc).
3) This signal causes the target process to enter strace.hello() (via the
special handling in wait_sig() in sigproc.cc).
4) strace::hello() builds a magic string containing the address of its
`active' flag as a hexadecimal string and sends this back to strace.exe (via
OutputDebugString () using the special marker value
5) strace.exe picks up this magic string (in handle_output_debug_string())
and sets the target process's `active' flag (via WriteProcessMemory() with
the address of the `active' flag that it has extracted from the debug
6) Now that the `strace.active' flag is set, the target process generates
the messages for strace.exe by successive calls to OutputDebugString().
Synchronization in all of this occurs since, when a process is being
debugged, calls to OutputDebugString() do not return until the debugger has
received the corresponding debug event (via WaitForDebugEvent()) and allowed
the target to continue processing (via ContinueDebugEvent()).
[* Caveat: I'm unclear that this is *exactly* what happens, as there seems
to be some degree of buffering involved, but it's close enough for the
current discusion, so far as I can tell. *]
There is unfortunately a race, since (on w2k at least) the target process is
not being debugged by the time it receives the magic __SIGSTRACE signal.
Thus the OutputDebugString() call in strace::hello() does nothing. (To quote
the SDK: "If the application has no debugger, the system debugger displays
the string. If the application has no debugger and the system debugger is
not active, OutputDebugString does nothing.")
So strace.exe never receives the magic message and never toggles the
`active' flag. Silence reigns.
[* Note that other debug events *are* queued up even if no debugger is
attached. For example, as soon as strace.exe attaches to the target process
it gets a stream of CREATE_THREAD and LOAD_DLL events (amongst others) that
I assume are the same as it would get if it started the target itself; that
is, they are the messages generated during application launch. See the
earlier caveat too in this context. *]
My "fix" for this infelicity is for strace.exe to re-signal the target
process if it gets into the situation that there are no pending debug events
(i.e., the call to WaitForDebugEvent() times out) and it has yet to receive
the magic _STRACE_INTERFACE_ACTIVATE_ADDR message.
And that works for me (i.e. I can now get back to what I was trying to do in
the first place). But does anyone out there know better? If so, please give
me at least a little hint.
While I'm at it, I was considering making some extensions to strace.exe: for
example, to (optionally) display the other debug messages so that you could
trace DLLs being loaded and unloaded, for example. Is anyone interested in
this? or is there some other preferred mechanism?
Anyhow, I'm off to bed now (and tomorrow I'll post off my copyright
assignment form so I can then submit a patch, in case it comes to that).
More information about the Cygwin-developers