Welcome to MSDN Blogs Sign in | Join | Help

Implications of using a helper thread for debugging

What it means?

I mentioned in a previous post (http://blogs.msdn.com/jmstall/archive/2004/10/10/240452.aspx) that the CLR debugging services is an “in-process model” which means it has a helper thread running in the same process as the EE which provides debugging information at runtime. Contrast this to an “out-of-process” model where the debugger operates completely from a separate process with little cooperation from debuggee.

The helper thread spends most of its life sitting in a message loop waiting for requests from the debugger. When it gets a request, it can directly access the CLR’s data structures to compute an answer for the request, and then it sends a response back to the debugger.

 

The good?

Having a helper thread and an in-process model has several advantages:

-         Maintainability: The helper thread can reuse the same code that the rest of the EE uses to manipulate the data structures. It’s also considerably easier to update EE data structures from a helper thread than from out-of-process.

-         Performance:  The helper thread is running in the same process as the EE, so there’s less overhead compared to obtaining the same information from out of process.

-         Cooperating with the GC – it’s easier to synchronize with the Garbage Collector (e.g., take locks, manipulate synchronization objects) from within the same process.

 

The bad?

Having a helper-thread model has many disadvantages:

1)      Can’t debug managed dumps (mini/heap/full). The helper thread requires code to run in the debuggee. Code can only run in a live process, not in a dump. Thus the helper thread is unavailable for dump-debugging.

2)      Makes interop-debugging very problematic. Interop-debugging becomes much less stable and some scenarios become completely broken. This is because interop debugging combines native debugging (out-of-process model) with managed debugging (in-process model), and the two don’t mix. This is the top reason why interop debugging in VS 2002 / 2003 is both very slow and prone to deadlock. (GreggM gave a nice overview of the problems here at http://blogs.msdn.com/greggm/archive/2004/01/23/62455.aspx)

3)      Larger Heisenberg effect. Even when it looks like the debuggee is stopped (at a breakpoint, for example), the helper thread is still running inside of the debuggee to service managed requests. This can become problematic when trying to debug stress runs.

4)      Less stable. If the debuggee is sufficiently corrupted, the functionality needed by the helper thread may not work properly. For example:

a.       A memory corruption in the debuggee may break key data structures needed by the helper thread.

b.      Debugging out-of-memory scenarios can be compromised because the helper thread may not have enough memory to execute.

c.       There are many random corner-case scenarios to affect threads running in a process. For example, If the helper thread gets suspended for whatever reason, the debugger will hang. Also, the helper will run arbitrary code for all dllmain routines in the debuggee. If any of those dllmain routines call managed code, then there won’t be a helper thread available to help debug the managed code running on the real helper.

d.      If the helper blocks on anything (or calls anything, like an OS api that block on something), the debugger will deadlock.

5)      Extra thread in every managed app, even when you’re not debugging. If we lazily launch the helper thread only once we start debugging then the helper thread would still be missing in the attach scenario.

6)      Can’t debug the runtime itself (e.g., mscorwks.dll). For example, you can’t step-in from managed code into mscorwks because you can’t debug any code in the runtime that the helper thread would need to run. Since the runtime is unmanaged code, this is really a subset of interop-debugging. See here for details.

 

 

Why did the CLR choose an inproc model?

There’s a long list of problems with the inproc model, so why did we do it that way?  It was actually a pretty contested decision with some very very strong resistance. Some of the reasons for going with inproc were:

-         Hindsight is 20/20. This decision was made over 6 years ago, and the key scenarios where inproc breaks down weren’t nearly as important at that time. Dump debugging wasn’t nearly as popular. Interop-debugging wasn’t even on the table yet.

-         Other similar debugging services, such as script debugging, were successfully inproc.

-         There were serious concerns about the maintainability of an out-of-proc solution.

 

Published Wednesday, October 13, 2004 1:18 PM by jmstall

Comments

# re: Implications of using a helper thread for debugging

Monday, October 18, 2004 2:14 PM by Dmitriy Zaslavskiy
Are there any changes planned in this regard.
In Longhorn or even before where most (for different values of the work most) processes will be managed an extra thread per processes will add up?

# re: Implications of using a helper thread for debugging

Monday, October 18, 2004 3:10 PM by Mike Stall
Definitely an issue. Now I know 2 wrongs don't make a right, but fwiw, Managed apps already spin up extra threads. For eg, Every app has a finalizer thread too. And certain interesting activities will also create extra threads under the covers.

For the debugger's thread: The challenge is to delay-create the helper thread without breaking the attach scenario.
We'd like to use something like kernel32!CreateRemoteThread, but that doesn't always work (such as creating across sessions).

Unfortunately, I can't yet make any official comment of what it will be when we ship v2.0.

# re: Implications of using a helper thread for debugging

Tuesday, October 19, 2004 11:12 AM by Dmitriy Zaslavskiy
>> Managed apps already spin up extra threads.
Sure, timers come to mind. But I would consider all or most of those uses in need of an optimization. For example Chris Brumme discussed the use of thread pool to schedule finalizers.

What about a fault injection technique. So that external debugger does something similar to GC to find a safe point and inject an exception which when CLR would handle would start debugging thread.

I do realize it's much easier to give a free advise than to implement ;)

# All The News That Is Fit To Print (Oct 19, 2004)

Tuesday, October 19, 2004 10:48 PM by OdeToCode Link Blog

# re: Implications of using a helper thread for debugging

Thursday, October 21, 2004 1:58 AM by Mike Stall
The dilemma is finding a technique that is *guaranteed* to work in all situations (even the goofy ones), else you'll hit cases where your app crashes and you can't attach to it.

Your raise some interesting points.
The fault injection requires that we be guaranteed to find a thread that we can hijack. This may not be a given if threads are all out in the system calls or if the thread has its own handlers in place that we can't intercept.

There's also an issue of reliability. If our technique for creating the thread remotely is too random, then it may introduce too many corner cases and we may not be able to test them all sufficiently to be confident it really works properly. I'll spend the rest of my life chasing down random "attach failed to create the helper thread" bugs!

# How do Managed Breakpoints work?

Tuesday, December 28, 2004 10:26 PM by Mike Stall's .NET Debugging Blog

# Special threads in CLR

Tuesday, July 12, 2005 2:32 PM by Yun Jin's WebLog
Question: How many threads does a typical managed process have when it just starts to run?  ...

# How many threads does a typical managed process have when it just starts to run?

Thursday, August 18, 2005 12:33 PM by C#, VS Deployment and all geek talk

# Special threads in CLR

Sunday, August 28, 2005 3:44 PM by Yun Jin's WebLog
Question: How many threads does a typical managed process have when it just starts to run?  ...

# Out-of-band events and Mixed-mode debugging

Tuesday, September 13, 2005 11:10 AM by Mike Stall's .NET Debugging Blog
Interop-debugging splits all debug events into "In-band (IB) " and "Out-of-band (OOB)". Inband events...

# ICorDebug reentrancy

Monday, September 19, 2005 2:50 AM by Mike Stall's .NET Debugging Blog
ICorDebug (ICD) in managed-only debugging mode does not need to be a reentrant API. In other words, you...

# What to expect when you Attach, Async-Break

Tuesday, March 21, 2006 12:14 PM by Mike Stall's .NET Debugging Blog
Don't assume that if you have a thread doing a spin-wait, that you can attach / asynchronously-break...

# Caveats about managed JIT-debugging

Monday, July 24, 2006 11:15 AM by Mike Stall's .NET Debugging Blog
I received some questions in the mailbag about what Debugger.Launch actually does. Debugger.Launch the...

# Trivia about Managed debugging and Exit Process

Wednesday, May 09, 2007 11:52 PM by Mike Stall's .NET Debugging Blog

Process Shutdown is evil , as Raymond Chen recently blogged about in wonderful detail. This prompts me

New Comments to this post are disabled
 
Page view tracker