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 (25)

  1. Yosi Taguri says:

    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

  2. J says:

    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

  3. smidgeonsoft says:

    If you check out my debugger at (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.

  4. 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.

  5. I would definitely appreciate the ability to debug at the IL-level (without doing any prep work before hand). I don’t debug all that often but when I do I often end up in either the framework or third party code looking at the x86. This isn’t too bad, but IL would be much easier 🙂

    BTW, I’m very happy that the IL <–> Native mapping will always be available on Whidbey. My users often complain about the lack of line number information in the stack traces that IKVM (JVM for .NET) produces.

  6. James Austin says:

    I find that stepping through the x86 code, with .NET reflector showing the decompilation of the same function adequate.

  7. Mike,

    I don’t really buy the argument that having the IL be slightly "out of touch" with the native code makes tracing through IL impossible or useless. After all, isn’t that what we already do when tracing through C++ code compiled with optimizations ? Sure, the line pointer can jump around a bit but it can still be very useful.

    As it stands, the disassembly of the jitted code provided by VS is even less useful to me than what I get when tracing through a native DLL for which I only have symbols (or even just entry points). Stepping through it is like advancing blindfolded – I usually end up just watching for changes in the call stack. Believe me, these are not ideal conditions for tracking down a bug deep down in .NET’s XSLT processor… though I did find a few.

    My wildest dreams involve being able to integrate Reflector within the VS debugger. Imagine a world where IL methods are decompiled on the fly and the debugger allows you to step through the generated C#. It _is_ technically feasible, right ?

  8. IL debugging is very useful when doing interop stuff. Happily VS.NET supports it a little (see comment above by Yosi Taguri).

  9. Mike Stall says:

    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.

  10. 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! 😉

  11. 你覺得目前C#/VB.Net的能力有限,想發揮.Net平台上100%的威力,或是你單純是個練功狂,就是想探索.Net底層機制,那麼 Microsoft Intermediate Language (MSIL)將是你最好的選擇。
    <br><br /><a href="">繼續閱讀…</a>

  12. Rick Byers says:

    In my last post&amp;nbsp;I gave an overview of the DebuggableAttribute, what values the C# compiler gives…

  13. Question from the mail bag:

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

  14. I mentioned in the recent dev lab that you can debug at the IL level . I demoed two ways to do this,

  15. Waver says:



  16. Rick Byers says:

    In my last post I gave an overview of the DebuggableAttribute, what values the C# compiler gives it,