ltrzesniewski / InlineIL.Fody

Inject arbitrary IL code at compile time.
MIT License
240 stars 17 forks source link

Generate debug info #2

Closed Rohansi closed 6 years ago

Rohansi commented 6 years ago

It might be useful having sequence points for each of the IL intrinsics to make finding the relevant disassembly a bit easier.

ltrzesniewski commented 6 years ago

I'm not sure how the debugger would react to such sequence points - many of them such as argument pushes before a call don't map to actual assembler instructions. But I could still try to add them to see if the debugger can cope.

I don't understand what you mean by "finding the relevant disassembly a bit easier", could you explain what you're trying to do and how sequence points would help with that?

Rohansi commented 6 years ago

The disassembly window shows line(s) that correspond to the C# code so you can see what the JIT generates. For regular source level debugging this wouldn't be very useful, IMO.

ltrzesniewski commented 6 years ago

Ok, I see what you mean. I'll take a look, although I won't guarantee it'll be useful in this case either.

Rohansi commented 6 years ago

Yeah I'm not 100% sure it would work properly but it should and it would be perfect if it does.

On Jul 22, 2018 7:53 AM, Lucas Trzesniewski notifications@github.com wrote:

Ok, I see what you mean. I'll take a look, although I won't guarantee it'll be useful in this case either.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/ltrzesniewski/InlineIL.Fody/issues/2#issuecomment-406860157, or mute the threadhttps://github.com/notifications/unsubscribe-auth/ABQjzNbM5aGeY3b82N5egG-_upo_hn2aks5uJGe4gaJpZM4VZ9dM.

ltrzesniewski commented 6 years ago

Well, this is promising (before/after):

image

But I need to make 100% sure this does not have any influence on the JIT codegen in release builds before shipping it. I'd prefer to live without debug info than ending up with less optimized code.

ltrzesniewski commented 6 years ago

Some info (mostly relevant notes to myself for later reference):

MSDN:

Sequence points are used to indicate locations in the Microsoft intermediate language (MSIL) code that a debugger user will expect to be able to refer to uniquely, such as for setting a breakpoint. The JIT compiler ensures it does not compile the MSIL at two different sequence points into a single native instruction. By default, the JIT compiler examines the symbol store in the program database (PDB) file for a list of additional sequence points. However, loading the PDB file requires that the file be available and has a negative performance impact. Starting with version 2.0, compilers can emit "implicit sequence points" in the MSIL code stream through the use of MSIL "nop" instructions. Such compilers should set the IgnoreSymbolStoreSequencePoints flag to notify the common language runtime to not load the PDB file.

DebuggingModes.IgnoreSymbolStoreSequencePoints:

Since they're only needed for debugging scenarios, the location of sequence points, and their mapping back to source locations, are stored in the PDB file. Unfortunately this means that the JIT compiler has to open the PDB file and read this data any time it compiles a method with optimizations disabled (if JIT optimizations are enabled, then it doesn't care about sequence points in order to get the best codegen possible).

Debugging IL:

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.

BOTR - CLR ABI:

The VM assumes that anytime a thread is stopped, it must be at a GC safe point, or the current frame is non-resumable (i.e. a throw that will never be caught in the same frame). Thus effectively all methods with EH must be fully interruptible (or at a minimum all try bodies). Currently the GC info appears to support mixing of partially interruptible and fully-interruptible regions within the same method, but no JIT uses this, so use at your own risk.

The debugger always wants to stop at GC safe points, and thus debuggable code should be fully interruptible to maximize the places where the debugger can safely stop. If the JIT creates non-interruptible regions within fully interruptible code, the code should ensure that each sequence point begins on an interruptible instruction.

AMD64/JIT64 only: The JIT will add an interruptible NOP if needed.


Given all the info above, it may be safer to generate sequence points only when explicitly enabled through an option in FodyWeavers.xml. Or maybe I could always generate them in debug builds just so the debugger doesn't "jump around". I need to think about that.

Rohansi commented 6 years ago

Having an option for it would be perfect. Since it doesn't affect codegen unless a debugger is attached I'm undecided on what the default setting should be.

ltrzesniewski commented 6 years ago

I think I'll default it to true on debug builds, and false on release. I haven't had time to turn the POC into a fully-fledged feature yet ,and I still have to make sure the debugger behaves well by testing several cases.

ltrzesniewski commented 6 years ago

This is now available in v0.7.0.

Sequence points are generated by default in debug builds, but this behavior is configurable through a new SequencePoints="True|False|Debug|Release" attribute on the <InlineIL /> tag in FodyWeavers.xml.

I also made IL locals declared with IL.DeclareLocals show up in the debugger's Locals window - this is always enabled. VS also lets you reference them in the immediate window, which is pretty cool 🙂

The new Fody version also uncovered a case where invalid metadata was emitted. That is also fixed.

I haven't tested this as much as I wanted to, so feel free to send feedback if anything doesn't behave as expected @Rohansi.