Debugging IL

Managed applications are compiled to IL (Intermediate Language) and then our JIT (Just-In-Time compiler) can compile it to native code so that the CPU can execute it. (The alternative is interpreting the IL directly, which has horrible performance characteristics and is unsupported by the current CLR implementations).

People commonly ask, "Why can't I debug the IL?"..

The answer: The CPU is executing native code and not IL, and any loss of fidelity from IL-->Native will confuse the debugger. For example, suppose you have the source statement “x = y”. That may get compiled to the following IL:

  IL_0004: ldloc.1

  IL_0005: stloc.0

 

That may then get compiled to the following native code (x86 here, if x + y are enregistered).

mov eax, ebx

Thus the 2 IL instructions are represented by only a single native instruction. Thus you could never step over just IL_0004. We deal with this IL-->Native conversion by specifying sequence points, which are groups of IL instructions that the debugger effectively considers to be atomic. The compilers (C#, VB.Net, etc) either explicitly specify Sequence points or tell the JIT to infer sequence points based off certain patterns in the IL. Sequence Points also determine the granularity of the IL-->Native map. More sequence points provide better fidelity, but may restrict the jit's ability to optimize. The PDBs associate sequence points with source-lines, and a good mapping here ensures a sane source-level debugging experience. Thus for source-level debugging, the "sweet spot" is to have just enough sequence points to map the source-lines.

Now if that's not an issue, you can debug the IL ... at least from the ICorDebug perspective. In fact, ICorDebug is abstracted at the level of debugging IL. 

  1. Most ICorDebug functionality operates on the IL level. For example, breakpoints can be set at IL offsets and stackframes can report IL offsets.
  2. The ICorDebug API exposes the IL-codebytes for functions (via ICorDebugCode),
  3. Translating IL codebytes to text is trivial. It's much easier than disassembling x86. The IL encodings are public and I'm guessing that the source to ILDasm is available on rotor, and there's probably lots of IL disassemblers out there.
  4. Our API also exposes the IL <--> Native mapping (also via ICorDebugCode::GetILToNativeMapping). So although the CPU is really executing native code, you can translate back to the IL. In v2.0, these mappings are always available (regardless of ini / config files).

In fact, MDbg can do "IL-debugging" (at least with the right extensions), and internally, we find it very useful for our internal test purposes. Also, you could always use IL-dasm to get the IL from a high-level language (eg, C#) and then re-ILasm to get different sequence points granularity. 
[Update: 11/8/05] As proof of all this, we've added IL-debugging support into the MDbg gui.

Now all that said, Visual Studio does not expose "IL debugging". This is a great example of how the low level API (ICorDebug) has a different perspective than the high-level end-user tool (VS).  From the CLR perspective, the problem is solved - though I recognize that doesn't really help out VS's end users. I think they figure that already being able to debug on both the source-level and native-code level was sufficient; the IL-->Native problem mentioned above would make the feature problematic, and that there wasn't high enough demand / benefit for this feature to justify the costs. I'm curious: how important do you consider the ability to debug at the IL-level?

Comments

  • Anonymous
    October 02, 2004
    To debug c# code in msil you can do the following:
    1. Compile c3 as you use too.
    2. Disassemble using ildasm (look also on the /source flag)
    3. compile it again using the ilasm /pdb
    and you are set to debug it.

    once you have a matching pdb for your exe you can debug what ever you want

  • Anonymous
    October 02, 2004
    Being able to step through IL would, for me at least, be a much easier way of understanding how IL works than reading the specs

  • Anonymous
    October 03, 2004
    If you check out my debugger at www.smidgeonsoft.com (PEBrowse Interactive), you will find a debugger that debugs IL at the assembly language level with few problems. My debugger is a native-only debugger and does not use the ICorDebug interfaces at all (although it does use the unmanaged metadata APIs). As to why this is useful, try stepping through interop calls in VS.NET, and then try using PEBrowse Interactive. The plumbing in interop calls is nicely exposed with PI. There are additional instances where having access to the JIT-compiled code is important especially when your managed program is stepping into MSCORWKS.DLL, etc.

  • Anonymous
    October 03, 2004
    I've asked for IL debugging from Visual Studio mainly to be able to step through the framework code. Actually, this could be accomplished if I could just successfully get all the framework libraries assembled with ILASM /debug.

  • Anonymous
    October 03, 2004
    The comment has been removed

  • Anonymous
    October 05, 2004
    I find that stepping through the x86 code, with .NET reflector showing the decompilation of the same function adequate.

  • Anonymous
    October 05, 2004
    The comment has been removed

  • Anonymous
    October 05, 2004
    IL debugging is very useful when doing interop stuff. Happily VS.NET supports it a little (see comment above by Yosi Taguri).

  • Anonymous
    October 05, 2004
    Jonathan -
    You're totally right that you can view the "out of touch" thing as just another optimization. Nobody said it was impossible, and it doesn't make it useless (certainly would be better than nothing). My original point was: that issue aside, our API does support IL debugging, it's just that VS doesn't take advantage of it.

    Hooking up Reflector (or any IL-->source decompiler) to a debugger would be very cool.
    It's not only feasible, it's very very possible! The ICorDebug API certainly doesn't stop you from doing this - it's just a matter of adding a feature to your debugger of choice.

    In fact, I believe you could actually do this w/ MDbg because of it's great extensibility model (check out the ILdasm extension that comes with the sample - it's 90% of the way there). Anybody want to try this?

    I don't understand VS's extension interfaces enough to know if this is possible to hook up in VS - I strongly suspect it's not.

  • Anonymous
    October 17, 2004
    It would be particularly useful when debugging generated code. Round tripping has saved my skin a good number of times, but it does have its limitations. Debugging IL is one of those things I wouldn't use every day, but in those situations where it is used it can save a huge amount of time. Give me debugging IL rather than E&C any day! ;)

  • Anonymous
    October 19, 2004
    if

  • Anonymous
    February 21, 2005
    你覺得目前C#/VB.Net的能力有限,想發揮.Net平台上100%的威力,或是你單純是個練功狂,就是想探索.Net底層機制,那麼 Microsoft Intermediate Language (MSIL)將是你最好的選擇。<br><br /><a href="http://www.techblog.idv.tw/Blog/zion/archive/3782.aspx">繼續閱讀...</a>

  • Anonymous
    September 08, 2005
    In my last post&amp;nbsp;I gave an overview of the DebuggableAttribute, what values the C# compiler gives...

  • Anonymous
    September 19, 2005
    Question from the mail bag:

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

  • Anonymous
    May 27, 2007
    I mentioned in the recent dev lab that you can debug at the IL level . I demoed two ways to do this,

  • Anonymous
    March 31, 2008
    TooltoallowinlineILinC#/VB.Net C#doesn

  • Anonymous
    October 27, 2008
    In my last post I gave an overview of the DebuggableAttribute, what values the C# compiler gives it,

  • Anonymous
    May 09, 2009
    PingBack from http://dotnetdiscoverer.com/?p=34