I'm making a managed .NET debugger using MDBG sample. It works for straightforward scenarios, but has issues when method rewriting occurs. Most critical parts are yield method and async methods.
I've already asked a more general question about these problems. Now I want to focus on local variables resolution. Please consider the code:
using System;
using System.Threading.Tasks;
class C
{
public static void Main() {
var instance = new Instance();
instance.Start().Wait();
}
}
class Instance
{
public static async Task F() { for(var i=0; i<100; i++) { Console.WriteLine(i); await Task.Delay(100); } }
public async Task Start() {
var z = "test";<------- Breakpoint
var x = 10;
await F();
}
}
When debugger reaches Breakpoint I'm querying debugger to get local variables and the only variable is this
. Variables x
and z
are hoisted on generated structure and cannot be resolved directly .
So the question is: How to resolve during debug local variables in yield method and async methods?
In comments to my previous question @Brian Reichle gave me some hints how I can get mapping between existing variable and hoisted one.
Exploring SymAttribute and Roslyn source I came to conclusion that it doesn't directly store mapping between them. SymAttribute
is used to get CustomDebugInfoRecord
, which stores part of this information(Used Pdb2Xml library from Roslyn to generate it):
<method containingType="Instance+<Start>d__1" name="MoveNext">
<customDebugInfo>
<forward declaringType="C" methodName="Main" />
<hoistedLocalScopes>
<slot startOffset="0x0" endOffset="0xcc" />
<slot startOffset="0x0" endOffset="0xcc" />
</hoistedLocalScopes>
<encLocalSlotMap>
<slot kind="27" offset="0" />
<slot kind="33" offset="161" />
<slot kind="temp" />
<slot kind="temp" />
</encLocalSlotMap>
</customDebugInfo>
<sequencePoints>
<entry offset="0x0" hidden="true" document="1" />
<entry offset="0x7" hidden="true" document="1" />
<entry offset="0xe" startLine="16" startColumn="37" endLine="16" endColumn="38" document="1" />
<entry offset="0xf" startLine="17" startColumn="14" endLine="17" endColumn="29" document="1" />
<entry offset="0x1a" startLine="18" startColumn="14" endLine="18" endColumn="35" document="1" />
<entry offset="0x26" startLine="19" startColumn="14" endLine="19" endColumn="25" document="1" />
<entry offset="0x2e" startLine="19" startColumn="25" endLine="19" endColumn="46" document="1" />
<entry offset="0x3a" startLine="20" startColumn="14" endLine="20" endColumn="24" document="1" />
<entry offset="0x45" hidden="true" document="1" />
<entry offset="0xa0" hidden="true" document="1" />
<entry offset="0xb8" startLine="21" startColumn="11" endLine="21" endColumn="12" document="1" />
<entry offset="0xc0" hidden="true" document="1" />
</sequencePoints>
<asyncInfo>
<kickoffMethod declaringType="Instance" methodName="Start" />
<await yield="0x57" resume="0x72" declaringType="Instance+<Start>d__1" methodName="MoveNext" />
</asyncInfo>
</method>
So the only way I can see now to resolve hoisted variables is:
this.generatedStructureName
x
variable into <x>5__2
This approach doesn't seems right and I'm not sure if it will ever work out, but it's the only thing I can think of now. Is there any other possibility to solve this problem? How does VisualStudio tackle it?
I've created a small repo to reproduce the problem here
The Autos and Locals windows show variable values while you are debugging. The windows are only available during a debugging session. The Autos window shows variables used around the current breakpoint.
You can manually invoke this window from the Visual Studio menu, Debug | Windows | Locals, or by pressing the keyboard shortcut Ctrl + D + L: Get Mastering Visual Studio 2017 now with the O'Reilly learning platform.
If you want to continue to watch a variable, you can add it to a Watch window from a data tip. Right-click the variable in the data tip, and select Add Watch. The variable appears in the Watch window. If your Visual Studio edition supports more than one Watch window, the variable appears in Watch 1.
Well, I haven't found anyone here or on msdn forums who would enlighten me about how VS algorithm works, but I've found that SharpDevelop supports variables resolution for async methods. Surprisingly it was using similar algorithm to what I've described in my question: just parsing hoisted field names.
Related source could is available here on gitHub, if someone else will run into similar problems and will be stuck. Still, I don't consider this a good solution and hoping there is a better way to solve the issue...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With