Управляемый отладчик.NET и асинхронные / ожидающие методы.
Я делаю управляемый.NET отладчик, используя пример MDBG.
Рассмотрим простой пример асинхронности:
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: }
При отладке этого кода с помощью моего отладчика я сталкиваюсь с двумя основными проблемами:
- Имена локальных переменных изменены (CS$4$0000, CS$0$0001 и т. Д.) Или отсутствуют (невозможно найти obj в локальных переменных отладчика)
Степпинг ведет себя непредсказуемо:a) Переход по строке 3 и т. Д. Обычно следует перейти к строке 4 после ожидания завершения оценки. Но вместо этого отладчик переходит на строку 13 и продолжает оттуда. Поведение StepOver на видео
б) Для входа в строку 3 и т. д. нужно просто наступить на каждую строку: строка 3 -> строка 12 -> строка 13(подождать некоторое время) -> строка 14 -> строка 15 -> строка 4. Но вместо этого после перехода в строке 13, где я ожидаю, что отладчик будет ожидать результата оценки, переход по какой-то причине продолжается на строку 3. После этого отладчик ожидает результата и выполнение продолжается, как и ожидалось. Поведение StepIn на видео
c) Если в ожидании ответа запланирована какая-либо другая работа, отладчик переключается на этот код. Например, если есть некоторый таймер, который истек во время ожидания ответа, оценка после строки 13 продолжается для этого кода таймера. Вместо этого, как и в Visual Studio, я бы ожидал, что отладчик будет придерживаться текущей области видимости и не покинет ее, пока он не будет выполнен полностью. Параллельное поведение на видео
Частично я понимаю источник этих проблем: компилятор создает конечный автомат, который представлен вложенной структурой, где логика инкапсулирована в методе MoveNext. Это, по крайней мере, объясняет мне, почему степпинг не работает, как я ожидал бы для случаев а) и б). Когда я вхожу в некоторый код без символов (и у меня нет символов для кода, сгенерированного компилятором), я делаю один или несколько шагов, чтобы добраться до некоторого моего кода. Это решение, которое @Brian Reichle предложил в этом связанном вопросе
Что касается изменения имен локальных переменных, я думал, что это происходит из-за разлива стека (глава "Что происходит"). Но, анализируя мою сборку с помощью ILDASM, я не нашел ничего сохраненного в поле t__stack сгенерированной структуры. Поэтому я не догадываюсь, почему имена переменных не сохраняются для асинхронных методов.
Тем не менее VisualStudio как-то избегает всех этих проблем.
Итак, как управляемый отладчик.net должен обрабатывать пошаговые и локальные переменные в сценарии async / await?
За этим стоит много кода реализации, но я не уверен, какую часть было бы целесообразно показать...