#line hidden and 0xFeeFee sequence points

Sometimes you may have functions that you don’t want a debugger to step into (such as 3rd-party library code or even your own helper functions like certain getters or ToString calls).  I blogged here about debugging “Just-my-code” on a per-function basis. You can also mark regions within a given function that you want the debugger to skip over.

In C#, you can use the ‘#line hidden’ directive to mark a region of code as not exposed to the debugger.

Eg:
        int y = 0; // <-- start here, press F10
#line hidden
y++; // This code will be stepped over
y++;
#line default
Console.WriteLine(y); // <-- and you land here

So a debugger would skip over the region between the ‘#line hidden’ and ‘#line default’.

How is this useful?
So the above example is pretty contrived. Real examples show up with generating code for various loop structures. For example, here’s how a basic for loop gets generated in C#:
        for (int i = 0; i < 5; i++)
{
Console.WriteLine(i);
}
Compiles to this code:
.locals init ([0] int32 i,
[1] bool CS$4$0000)

//000009: for (int i = 0; i < 5; i++)
IL_0002: ldc.i4.0
IL_0003: stloc.0
<no source line>
IL_0004: br.s IL_0013

//000010: {
IL_0006: nop
//000011: Console.WriteLine(i);
IL_0007: ldloc.0
IL_0008: call void [mscorlib]System.Console::WriteLine(int32)
IL_000d: nop
//000012: }
IL_000e: nop
//000009: for (int i = 0; i < 5; i++ )
IL_000f: ldloc.0
IL_0010: ldc.i4.1
IL_0011: add
IL_0012: stloc.0

//000009: for (int i = 0; i < 5; i++)
IL_0013: ldloc.0
IL_0014: ldc.i4.5
IL_0015: clt
IL_0017: stloc.1

<no source line>
IL_0018: ldloc.1
IL_0019: brtrue.s IL_0006

//000013: }
IL_001b: nop

So how does it work?
The debugging information in a PDB (program database) maps each region of IL back to its source file.  Thus when you do a source-level step in a debugger (eg, press F10), a debugger can use this map to figure out what range of IL you want to step over. (You can then pass these IL ranges into ICorDebugStepper::Step).
Debuggers + compilers have a common agreement that if a line maps 0xFeeFee, then the debugger will skip over it.  So the ‘#line hidden’ directive tells the C# compiler to map all following lines to a 0xFeeFee line number, instead of the real line number.

Just for kicks, you can verify this. 0xFeeFee = 16707566 in decimal. You could use that instead of the #line hidden directive (since CSC doesn’t stop you).
    int y = 0; // F10 here
#line 16707566 // decimal equivalent of 0xFeeFee
y++; // This statement is skipped
#line default // restores
y++;
You could also verify by using the CorSym API to dump the sequence points (there are various Mdbg extensions to do this).

Random caveats:
The 0xFeeFee sequence points is entirely an protocol between the compiler and debuggers. It has no ICorDebug level support.
Any compiler targeting managed code can take advantage of FeeFee sequence points. C# exposes this via the #line directives, which makes it convenient for code-generators targeting C# instead of raw IL. ILasm exposes this via it’s ‘.line’ directive.

Any debugger can support 0xFeeFee sequence points. V1.1 Cordbg did not support them, but Mdbg, VS, and Cordbg V2.0 (in beta 1) all do. My suggested way to add debugger support for FeeFee is to have the debugger find all FeeFee ranges in the current method and then add those to the step ranges passed into ICorDebugStepper::Step (that’s how we added it to Cordbg).

Another application:
It turns out you can use 0xFeeFee sequence points combined with JMC to do some pretty interesting things. Stay tuned…
[uppdate] One usage is for adding debug support to arbitrary state machines.