Welcome to MSDN Blogs Sign in | Join | Help

How can I debug Just My Code?

Sometimes developers want to debug just the code they wrote and not the 3rd-party code (such as framework and libraries) that’s also inside their app. This becomes particularly useful when user and non-user code call back and forth between each other.  The v2.0 CLR debugging services have a host of new features to support this, which we call “Just-My-Code” (JMC) debugging.

 

In V2.0, ICorDebug:

-         lets a debugger mark each function as either user or non-user code. It’s up to the debugger to determine what is and is not user code. Visual Studio will use hints from the project system and also assume if that a given module is non-user code if the symbols are missing. You can also use the System.Diagnostics.DebuggerNonUserCodeAttribute attribute to tell VS to mark a specific method as non-user code.

-         allows stepping operations to magically skip all non-user code.

-         provides additional exception notifications.

 

A debugger can also do additional things such as filtering non-user code from the callstack.

In this blog, I’ll demo JMC stepping and explain how to use this new functionality from ICorDebug. (I’ll blog about exceptions later)

 

A very simple example of JMC-stepping:

Here’s a trivial example of JMC-stepping. Run this as a console application in VS2005 beta1.

using System;

 

class Program

{

    static void Main()

    {

        NonUserLibraryCode(); // <-- step in here (F11 in Visual Studio)

    }

 

    // This attribute tells the debugger to mark this function as non-user code.

    [System.Diagnostics.DebuggerNonUserCode]

    static void NonUserLibraryCode()

    {

        Console.WriteLine("Before");

        UserCode();

        Console.WriteLine("After");

    }

   

    static void UserCode()

    {

        Console.WriteLine("User1"); // <-- step completes here, skipping the non-user code

    }

}

 

VS also can filter out the non-user code from the callstack. So when stopped inside UserCode(), the callstack looks like:

>          ConsoleApplication2.exe!Program.UserCode() Line 21            C#

            [External Code]           

            ConsoleApplication2.exe!Program.Main() Line 7 + 0x6 bytes   C#

            [External Code]           


In VS, JMC can be disabled from Tools | Options | Debugging, “Enable Just My Code” check box (it must be enabled for these examples to work). The callstack filtering can be toggled by right clicking the callstack to toggle “Show External Code”.

All of MDbg’s JMC support is only in extension dlls that we use for testing purposes.

 

A more real example:

The example above is very simple because everything can be determined statically. This example shows Main dynamically invoking callbacks. Some callbacks are user code and others aren’t. This is similar to the winforms case where the message loop is non-user code but some of the handlers (like button1_click) are user code. You can use JMC to step between your handlers without having to put breakpoints in each handler and without having to step through the owning message loop.

 

using System;

using System.Diagnostics;

 

class Program

{

    delegate void Callback();

 

    // This will invoke 3 callbacks. A user, non-user, and then a user one.

    // (1) Step-in here. Since Main() is non-user code, we skip straight to the first bit of user

    // code called which is UserCodeHandler1.

    [DebuggerNonUserCode]

    static void Main()

    {

        // Invoke some callbacks.

        Callback[] list = new Callback[] {

            new Callback(UserCodeHandler1), new Callback(NonUserCodeHandler), new Callback(UserCodeHandler2)

        };

        foreach (Callback fp in list)

        {

            fp();

        }

    }

   

    static void UserCodeHandler1()

    {

        Console.WriteLine("Inside my 1st handler!"); // <-- step completes here from (1)

        // (2) Do a step-in here.  Normally, a step-in at the end of a method would be a step-out.

        // But since our caller (Main) is non-user code, we don't want to stop there. So we'll run to the next bit of user code.

    }

 

    [DebuggerNonUserCode]

    static void NonUserCodeHandler()

    {

        Console.WriteLine("Inside a non-user code handler");

    }

 

    static void UserCodeHandler2()

    {

        // (3) Step-in from (2) lands here.

        Console.WriteLine("Inside my 2nd handler!"); // <-- step completes here, skipping the non-user code

    }

}

 

 

Why JMC is cool on a technical level.

JMC-stepping has some good technical accomplishments in it. It is …

1)      not limited to static anaylsis (as shown above). JMC works with everything including arbitrarily deep callstacks, multicast delegates, polymorphism / virtual functions, and events + callbacks. There are no smoke and mirrors here - you could construct arbitrarily complicated examples.

2)      ... very performant. The step operation may skip large amount of non-user code without a performance penalty.  Constrast this to trying to fake JMC by just using traditional single-step operations to skip through all non-user code (which would be unusably slow).

3)      thread-safe. You can use JMC-stepping in multi-threaded programs without any problems. (Some other solutions would break down here)

4)      very configurable. You can set JMC-status on a per-function level, and you can toggle that status at runtime.

 

JMC-stepping on the ICorDebug level:

ICorDebug implements managed stepping via ICorDebugStepper objects. All the low-level details of how managed stepping actually works are abstracted away from the debugger.

Debuggers create a JMC-stepper by calling ICorDebugStepper2::SetJMC(true). A JMC-stepper will magically skip all non-user code. All non-JMC steppers (which I’ll call “Traditional Steppers”) ignore JMC-status. Thus a debugger must explicitly request JMC else it will get the pre-JMC behavior. This allows JMC functionality to be non-breaking.

 

The CLR defaults to assuming everything is non-user code and defers all JMC decision making to the debugger via the ICorDebug API. Debuggers must mark user code by calling ICorDebug(Function|Class|Module)::SetJMCStatus(). Debuggers can use any heuristics they wish to decide what is and is not user code. So although the DebuggerNonUserCode attribute is defined in mscorlib, the CLR does not pay any attention to it. That attribute is there exclusively to provide a convenient primitive semi-standard protocol for debuggers to mark specific functions as non-user code.  A debugger could use other heuristics such as:

-         input from the project system / IDE

-         method names (eg, make all ToString() or property getters non-user code).

-         other custom attributes. Eg, a custom attribute could take a string parameters and the debugger could use that to create groups of JMC methods.

-         presence of symbols. (generally modules without symbols should be considered non-user code).

-         source-level information (since the debugger has access to the source).

 

JMC-status can be toggled at runtime. Thus a debugger could build fancy logic to toggle groups of functions at runtime. This also lets a debugger delay setting JMC status until the user first stops in that method.

 

At the ICorDebug level, JMC is orthogonal to breakpoints. Breakpoints will be hit in non-user code, but a debugger may choose to continue the debuggee anyways and not notify the user. (This is what VS does).

 

 

Published Friday, December 31, 2004 11:47 AM by jmstall

Comments

# re: How can I debug Just My Code?

Friday, December 31, 2004 1:38 PM by Daniel Moth
I haven't played with this feature yet but the obvious question is: What are the full differences between the DebuggerStepThroughAttribute and the DebuggerNonUserCode? It appears that one works at the source level while the other goes deeper, the latter should be faster than the former and that the new attribute can be turned on with a simple setting in the IDE whereas the old one has to be manually commented out. Are these statements correct and/or what other differences from a usage perspective are there?

# re: How can I debug Just My Code?

Sunday, January 02, 2005 1:34 AM by Mike Stall
Daniel -
To be clear: The CLR ignores both attributes. They're defined in mscorlib.dll so that they're available for use by debugger applications (like VS).

The semantics are very similar in both(annotating "non-user" code). Hopefully we will document the semantics very clear in MSDN when we ship v2.0.

Thus the real difference between these attributes is entirely what a debugger does with them. In Visual Studios's case:
- DebuggerStepThroughAttribute is from v1.1. VS uses traditional step operations (pre-JMC_ to skip non-user code there.
- DebuggerNonUserCode is in v2.0 and VS uses this explicitly for the new JMC functionality above.

So maybe your question boils down to comparing (1) faking JMC with traditional step operations to (2) real JMC?

- Real JMC is much faster and can handle arbitrarily complex scenarios.
- Re: turning on + off: This is entirely a feature within VS. A debugger could just as easily have added a checkbox to ignore the DebuggerStepThroughAttribute . Or it could have just as easily removed the "Enable JMC" checkbox and thus forced you to comment out the DebuggerNonUserCode attribute.

# re: How can I debug Just My Code?

Sunday, January 02, 2005 10:14 AM by Daniel Moth
Thank you, that helps. I don't see why DebuggerStepThrough does not go through the obsolescence path then, but that is an academic discussion I guess...

# Func-eval is evil

Wednesday, March 23, 2005 12:21 AM by Mike Stall's .NET Debugging Blog

# #line hidden and 0xFeeFee sequence points

Sunday, June 19, 2005 5:06 PM by Mike Stall's .NET Debugging Blog
Sometimes you may have functions that you don’t want a debugger to step into (such as 3rd-party library...

# Debug support for arbitrary state-machines

Wednesday, July 27, 2005 12:36 AM by Mike Stall's .NET Debugging Blog
I mentioned here&amp;nbsp; that if your language compiles to IL, then you get free debugging support with...

# Debug support for arbitrary state-machines

Wednesday, July 27, 2005 3:29 PM by Mike Stall's .NET Debugging Blog
I mentioned here&amp;nbsp; that if your language compiles to IL, then you get free debugging support with...

# Debug support for arbitrary state-machines

Thursday, July 28, 2005 12:02 PM by Mike Stall's .NET Debugging Blog
I mentioned here&amp;nbsp; that if your language compiles to IL, then you get free debugging support with...

# #line hidden and 0xFeeFee sequence points

Monday, August 22, 2005 4:54 PM by Mike Stall's .NET Debugging Blog
Sometimes you may have functions that you don’t want a debugger to step into (such as 3rd-party library...

# ICorDebugStepper and using ICorDebugStepper2::SetJMC

Tuesday, August 23, 2005 5:30 PM by Mike Stall's .NET Debugging Blog
We added Just-My-Code (JMC) stepping (the ability to step through just your code and skip code that's...

# Single-Stepping Into 'internalcall' CLR Methods

Thursday, September 15, 2005 4:00 PM by Stale DNA
Now I am intrigued... My previous post talked about the 'Just My Code'
option in Whidbey and how you...

# Single-Stepping Into 'internalcall' CLR Methods

Thursday, September 15, 2005 4:08 PM by Stale DNA
Now I am intrigued... My previous post talked about the 'Just My Code'
option in Whidbey and how you...

# Single-Stepping Into 'internalcall' CLR Methods

Thursday, September 15, 2005 4:10 PM by Stale DNA
Now I am intrigued... My previous post talked about the 'Just My Code'
option in Whidbey and how you...

# Single-Stepping Into 'internalcall' CLR Methods

Thursday, September 15, 2005 4:12 PM by Stale DNA
Now I am intrigued... My previous post talked about the 'Just My Code'
option in Whidbey and how you...

# Single-Stepping Into 'internalcall' CLR Methods

Friday, September 16, 2005 3:01 PM by Stale DNA
Now I am intrigued... My previous post talked about the 'Just My Code'
option in Whidbey and how you...

# Going from IL to Source

Monday, September 19, 2005 11:17 AM by Mike Stall's .NET Debugging Blog
Question from the mail bag:

I am trying to get some information on what I can do usefully with the...

# What I'm reading now (Practical .NET2 and C#2)

Tuesday, March 14, 2006 11:10 AM by Mike Stall's .NET Debugging Blog
I've been reading through &quot;Practical .NET2 and C#2&quot; (by Patrick Smacchia). I finished my previous book...

# Debugging From an ASP.NET Web Form to a Web Service using Typed DataSets

Tuesday, March 28, 2006 2:24 PM by Technical Weblog of Eric Charran
When attempting to step into a web service when a Typed DataSet is passed as a parameter to the web method...

# #line hidden vs. DebuggerNonUserCode attribute

Wednesday, July 12, 2006 7:05 PM by Mike Stall's .NET Debugging Blog
I got this great question from the mailbag:&amp;nbsp;&amp;nbsp;&amp;nbsp; &quot;[W]hat is the relation between the &quot;#...

# Mike Stall's .NET Debugging Blog : What's new in v2.0 CLR Debugging (ICorDebug)?

# Single-Stepping Into 'internalcall' CLR Methods

Tuesday, August 15, 2006 10:15 PM by Stale DNA - all things Cappy Popp
Now I am intrigued... My previous post talked about the &amp;#39;Just My Code&amp;#39; option in Whidbey and

# Debug With Reflector

Sunday, November 05, 2006 2:16 PM by TestDriven.NET by Jamie Cansdale

Below is a screencast that shows how to debug an application using the new 'Go To Reflector' functionality

# Making Catch, Rethrow more debuggable.

Monday, February 12, 2007 1:01 PM by Mike Stall's .NET Debugging Blog

I previously mentioned that catch / rethrow an exception impedes debuggability because the callstack

# Debugger talk at Compiler Lab

Friday, May 25, 2007 1:58 AM by Mike Stall's .NET Debugging Blog

The CLR team recently had a compiler dev Lab on campus in building 20, with a strong focus on dynamic

# Switching to [DebuggerNonUserCode]

Monday, September 03, 2007 1:37 PM by Mark Mullin's Professional Blog

I&rsquo;m starting the switch to using the [DebugNonUserCode] attribute instead of [DebuggerStepThrough]...

# MSDN Blog Postings &raquo; Debuggability tops MSBuild feature poll

# Debugging IronPython with my Excalibur

Saturday, April 11, 2009 5:32 AM by #.think.in

Debugging IronPython with my Excalibur

New Comments to this post are disabled
 
Page view tracker