2

我正在使用 MDBG 示例制作托管的 .NET 调试器。

考虑一些简单的异步示例:

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:         }

用我的调试器调试这段代码我遇到了两个主要问题:

  1. 局部变量名称已更改(CS$4$0000、CS$0$0001 等)或丢失(在调试器的局部变量中找不到 obj)
  2. 步进行为不可预测: a) 越过第 3 行等通常应在等待评估完成后转到第 4 行。但调试器改为跳转到第 13 行并从那里继续执行。视频上的 StepOver 行为

    b) Stepping In第 3 行等等应该只踩在每一行:第 3 行 -> 第 12 行 -> 第 13 行(暂停一段时间) -> 第 14 行 -> 第 15 行 -> 第 4 行。但是在步进之后在第 13 行,我希望调试器等待评估结果,由于某种原因,步进继续到第 3 行。之后调试器等待结果并按预期继续执行。视频上的 StepIn 行为

    c)如果在等待响应时安排了其他一些工作,调试器将切换到该代码。例如,如果在等待响应期间有某个计时器超时,则在第 13 行之后继续对该计时器代码进行评估。相反,就像 Visual Studio 一样,我希望调试器能够坚持它的当前范围,并且在它完全执行之前不要离开它。视频上的并行行为

我部分理解了这些问题的根源:编译器创建了一个由嵌套结构表示的状态机,其中逻辑封装在 MoveNext 方法中。这至少解释了为什么步进不像我对案例 a) 和 b) 所期望的那样工作。当我进入一些没有符号的代码时(并且我没有编译器生成的代码的符号),我正在执行一个或多个步骤来获取我的一些代码。这是@Brian Reichle 在这个相关问题中建议的解决方案

关于局部变量名称的更改,我认为这是由于Stack Spilling("What'staking" chapter)而发生的。但是使用 ILDASM 分析我的程序集时,我没有发现任何保存到生成结构的 t__stack 字段中的内容。所以我猜不到为什么变量名没有为异步方法保留。

尽管如此,VisualStudio 还是以某种方式避免了所有这些问题。

那么托管的 .net 调试器应该如何处理异步/等待场景中的步进和局部变量解析呢?

这背后有很多实现代码,但我不确定显示哪一部分是合理的......

4

0 回答 0