问题
I'm making a managed .NET debugger using MDBG sample.
Consider some simple async example:
1: private async void OnClick(EventArgs args){
2: var obj = new SomeClass();
3: bool res = await StaticHelper.DoSomeAsyncStuff();
4: if(res){
5: Debug.WriteLine("result is True");
6: }
7: else{
8: Debug.WriteLine("result is False");
9: }
10: someField = obj.Name + "is:" + res.ToString();
11: }
12: public static async Task<bool> DoSomeAsyncStuff(){
13: await Task.Delay(5000);
14: return true;
15: }
Debugging this code with my debugger I encounter 2 major issues:
- Local variables names are changed(CS$4$0000, CS$0$0001, ect) or are missing (cannot find obj in debugger's local variables)
Stepping behaves unpredictably : a) Stepping Over line 3 and so on should normally go to line 4 after waiting for evaluation to complete. But instead debugger jumps to line 13 and continues stepping from there. StepOver behaviour on video
b) Stepping In on line 3 and so on should just step on each line: line 3 -> line 12 -> line 13(hang for a while) -> line 14 -> line 15 -> line 4. But instead after stepping in on line 13, where I would expect debugger to wait for evaluation result, stepping continues to line 3 for some reason. After that debugger waits for result and execution continues as expected. StepIn behaviour on video
c) If there is some other work scheduled while awaiting the response, debugger switches to that code. For example, if there is some timer that elapsed during await of response, evaluation after line 13 continues on that timer code. Instead, like visual studio, I would expect debugger to stick with it's current scope and not leave it until it's executed completely. Parallel behaviour on video
Partially I understand the source of these problems: compiler creates a state machine which is represented by a nested struct, where logic is encapsulated inside MoveNext method. That at least explains me why stepping is not working as I would expect for case a) and b). When I'm stepping in some code without symbols(and I don't have symbols for code generated by compiler), I'm making one or more steps to get to some code of mine. It's solution that @Brian Reichle suggested in this related question
Regarding the local variable names change I thought it's happening because of Stack Spilling("What's happening" chapter). But analysing my assembly with ILDASM I haven't found anything saved to the t__stack field of the generated structure. So I'm out of guesses why the variable names are not persisted for async methods.
Nevertheless VisualStudio has all these problems avoided somehow.
So how managed .net debugger should handle stepping and local variable resolution in async/await scenario?
There is a lot of implementation code behind this, but I'm not sure which part would be reasonable to show...
来源:https://stackoverflow.com/questions/39078291/managed-net-debugger-and-async-await-methods