dnSpyEx / dnSpy

Unofficial revival of the well known .NET debugger and assembly editor, dnSpy
GNU General Public License v3.0
6.95k stars 456 forks source link

Improve debug info generation and handling #81

Open ElektroKill opened 2 years ago

ElektroKill commented 2 years ago

In order for the debugger to function properly, the ILSpy decompiler has been modified to generate debug info which tells us what IL code corresponds to what text in the text view as well as some other information that may be useful for the debugger. This debug info generation works well for regular code but over the past few days of extensive testing, I noticed many edge cases where strange, unusual, and unexpected behavior can occur.

  1. When the "Show hidden decompiler generated code" option is enabled in the settings, adding breakpoints to code in methods that utilize state machines (yield return and async code) stops working properly. the "Edit IL Instructions" option from the context menu disappears too. The cause of this is the deduplication of debug infos that takes place after decompilation. Each debug info has a method attached to it. In the case of state machine methods the "kickoff" method (the one which instantiates the state machine and is visible to the user at all times) as well as the state machine MoveNext method both have debug infos which point to the MoveNext method. This causes the first debug info to be removed during deduplication. During the decompilation process, the MoveNext method is inlined into the "kickoff" method, and hence the debug info points to the MoveNext method. Code responsible for deduplication: https://github.com/dnSpyEx/dnSpy/blob/823f416f83d77318a93635350fcdf4a38f3a42a7/dnSpy/dnSpy/Decompiler/MethodDebugService.cs#L78-L83 This lack of debug info for the decompiled async method (the "kickoff" method) causes the inability to set breakpoints.
  2. Debug Info for yield return state machines is not ideal. There are currently several problems. First of all there is no debug info provided for yield break; statements making it impossible to set a breakpoint on them. The next problem is a bit more complex. When a method which uses yield return and has a finally handler is compiled, a separate method is inserted into the state machine containing the code which was inside the finally handler. The decompiler currently handles this but the inlined statements have IL offsets that are from the finally method and when the debug info for them is generated it applies the offsets to the current decompiled method and not the finally method causing an incorrect breakpoint to be set. Example: image image This second problem is much harder to fix as it requires changes to the debug info code to allow a debug info for a specific method to specify from which method each statement is.
  3. Debug Info generation is currently broken/not implemented for query expressions and LINQ expression trees.

This issue will take some time to fix as it will require changes to the debug info system to make everything work smoothly and as expected.

ElektroKill commented 2 years ago

In commit 9f776f9, I added support for setting breakpoints on yield break; statements for all 3 types of yield-return state machines. This does not always work but it's a step in the right direction.

ElektroKill commented 2 years ago

In commit https://github.com/dnSpyEx/dnSpy/commit/d6bf16f96cf03abdc9c3e580fba4a5ce19ddb4ad, I fixed debug info generation for inlined finally blocks in yield return state machines. This means point number 2 from the original issue has now been fully resolved.

BadRyuner commented 1 year ago

As an improvement to "debug info" it would be cool to put a breakpoint wherever the stack length is greater than zero. The example in the picture, where in "pseudo code" stack is greater than 0 (i.e. all variables generated by decompiler are dummy) you cannot put breakpoint. image

ElektroKill commented 1 year ago

As an improvement to "debug info" it would be cool to put a breakpoint wherever the stack length is greater than zero. The example in the picture, where in "pseudo code" stack is greater than 0 (i.e. all variables generated by decompiler are dummy) you cannot put breakpoint. image

This is not a limitation of dnSpy or it’s debug info system. It’s a limitation of the .NET debugging APIs which we can’t overcome.