Во-первых, я прошу прощения за длину этого вопроса.
Я автор IronScheme . В последнее время я усердно работал над выдачей приличной отладочной информации, чтобы я мог использовать «нативный» отладчик .NET.
Хотя это было частично успешно, я сталкиваюсь с некоторыми проблемами при прорезывании зубов.
Первая проблема связана со степпингом.
Поскольку Scheme является языком выражений, все имеет тенденцию заключаться в круглые скобки, в отличие от основных языков .NET, которые, кажется, основаны на операторах (или строках).
Исходный код (схема) выглядит так:
(define (baz x)
(cond
[(null? x)
x]
[(pair? x)
(car x)]
[else
(assertion-violation #f "nooo" x)]))
Я специально выложил каждое выражение на новую строку.
Излучаемый код преобразуется в C # (через ILSpy) выглядит следующим образом:
public static object ::baz(object x)
{
if (x == null)
{
return x;
}
if (x is Cons)
{
return Builtins.Car(x);
}
return #.ironscheme.exceptions::assertion-violation+(
RuntimeHelpers.False, "nooo", Builtins.List(x));
}
Как видите, довольно просто.
Примечание: если код был преобразован в условное выражение (? :) в C #, все это было бы просто одним шагом отладки, имейте это в виду.
Вот вывод IL с номерами источника и строки:
.method public static object '::baz'(object x) cil managed
{
// Code size 56 (0x38)
.maxstack 6
.line 15,15 : 1,2 ''
//000014:
//000015: (define (baz x)
IL_0000: nop
.line 17,17 : 6,15 ''
//000016: (cond
//000017: [(null? x)
IL_0001: ldarg.0
IL_0002: brtrue IL_0009
.line 18,18 : 7,8 ''
//000018: x]
IL_0007: ldarg.0
IL_0008: ret
.line 19,19 : 6,15 ''
//000019: [(pair? x)
.line 19,19 : 6,15 ''
IL_0009: ldarg.0
IL_000a: isinst [IronScheme]IronScheme.Runtime.Cons
IL_000f: ldnull
IL_0010: cgt.un
IL_0012: brfalse IL_0020
IL_0017: ldarg.0
.line 20,20 : 7,14 ''
//000020: (car x)]
IL_0018: tail.
IL_001a: call object [IronScheme]IronScheme.Runtime.Builtins::Car(object)
IL_001f: ret
IL_0020: ldsfld object
[Microsoft.Scripting]Microsoft.Scripting.RuntimeHelpers::False
IL_0025: ldstr "nooo"
IL_002a: ldarg.0
IL_002b: call object [IronScheme]IronScheme.Runtime.Builtins::List(object)
.line 22,22 : 7,40 ''
//000021: [else
//000022: (assertion-violation #f "nooo" x)]))
IL_0030: tail.
IL_0032: call object [ironscheme.boot]#::
'ironscheme.exceptions::assertion-violation+'(object,object,object)
IL_0037: ret
} // end of method 'eval-core(033)'::'::baz'
Примечание. Чтобы не дать отладчику просто выделить весь метод, я делаю точку входа метода шириной всего в 1 столбец.
Как видите, каждое выражение корректно отображается на строку.
Теперь проблема со степпингом (протестирована на VS2010, но такая же / похожая проблема на VS2008):
Эти с IgnoreSymbolStoreSequencePoints
не применяются.
- Вызовите базу с нулевым аргументом, он работает правильно. (null? x), за которым следует x.
- Позвоните в базу с Cons аргумент, он работает правильно. (null? x), затем (pair? x), затем (car x).
- Позвонить в базу с другим аргументом, это не удастся. (null? x) затем (pair? x) затем (car x) затем (нарушение-утверждение ...).
При применении IgnoreSymbolStoreSequencePoints
(как рекомендуется):
- Вызовите базу с нулевым аргументом, он работает правильно. (null? x), за которым следует x.
- Позвонить в базу с Cons аргумент, это не удается. (null? x) затем (pair? x).
- Позвонить в базу с другим аргументом, это не удастся. (null? x) затем (pair? x) затем (car x) затем (нарушение-утверждение ...).
Я также нахожу в этом режиме, что некоторые строки (не показаны здесь) неправильно выделены, они выключены на 1.
Вот некоторые идеи, которые могут быть причинами:
- Tailcalls смущает отладчик
- Перекрывающиеся местоположения (здесь не показаны) сбивают с толку отладчик (это очень хорошо работает при установке точки останова)
- ????
Вторая, но также серьезная проблема заключается в том, что отладчик в некоторых случаях не может сломать / достичь точки останова.
Единственное место, где я могу заставить отладчик работать корректно (и последовательно), это точка входа метода.
Ситуация становится немного лучше, когда IgnoreSymbolStoreSequencePoints
не применяется.
Вывод
Возможно, отладчик VS просто глючит :(
Ссылки:
Обновление 1:
Mdbg не работает для 64-битных сборок. Так что это вне. У меня больше нет 32-битных машин для тестирования. Обновление: я уверен, что это не большая проблема, у кого-нибудь есть исправление? Изменить: Да, глупый я, просто запустите mdbg в командной строке x64 :)
Обновление 2:
Я создал приложение на C # и попытался проанализировать информацию о линии.
Мои выводы:
- После любой
brXXX
инструкции вам нужно указать точку последовательности (если она недействительна, то есть «#line hidden», выведите anop
). - Перед любой
brXXX
инструкцией выведите «#line hidden» и anop
.
Применение этого, однако, не решает проблемы (в одиночку?).
Но добавление следующего дает желаемый результат :)
- После
ret
этого выделите «#line hidden» и anop
.
Это использует режим, где IgnoreSymbolStoreSequencePoints
не применяется. При применении некоторые шаги все еще пропускаются :(
Вот выход IL, когда выше был применен:
.method public static object '::baz'(object x) cil managed
{
// Code size 63 (0x3f)
.maxstack 6
.line 15,15 : 1,2 ''
IL_0000: nop
.line 17,17 : 6,15 ''
IL_0001: ldarg.0
.line 16707566,16707566 : 0,0 ''
IL_0002: nop
IL_0003: brtrue IL_000c
.line 16707566,16707566 : 0,0 ''
IL_0008: nop
.line 18,18 : 7,8 ''
IL_0009: ldarg.0
IL_000a: ret
.line 16707566,16707566 : 0,0 ''
IL_000b: nop
.line 19,19 : 6,15 ''
.line 19,19 : 6,15 ''
IL_000c: ldarg.0
IL_000d: isinst [IronScheme]IronScheme.Runtime.Cons
IL_0012: ldnull
IL_0013: cgt.un
.line 16707566,16707566 : 0,0 ''
IL_0015: nop
IL_0016: brfalse IL_0026
.line 16707566,16707566 : 0,0 ''
IL_001b: nop
IL_001c: ldarg.0
.line 20,20 : 7,14 ''
IL_001d: tail.
IL_001f: call object [IronScheme]IronScheme.Runtime.Builtins::Car(object)
IL_0024: ret
.line 16707566,16707566 : 0,0 ''
IL_0025: nop
IL_0026: ldsfld object
[Microsoft.Scripting]Microsoft.Scripting.RuntimeHelpers::False
IL_002b: ldstr "nooo"
IL_0030: ldarg.0
IL_0031: call object [IronScheme]IronScheme.Runtime.Builtins::List(object)
.line 22,22 : 7,40 ''
IL_0036: tail.
IL_0038: call object [ironscheme.boot]#::
'ironscheme.exceptions::assertion-violation+'(object,object,object)
IL_003d: ret
.line 16707566,16707566 : 0,0 ''
IL_003e: nop
} // end of method 'eval-core(033)'::'::baz'
Обновление 3:
Проблема с вышеприведенным «полувлажом». Peverify сообщает об ошибках на всех методах из-за nop
после ret
. Я не понимаю проблемы на самом деле. Как можно nop
перерыв проверки после ret
. Это как мертвый код (за исключением того, что это даже не код) ... О, хорошо, эксперименты продолжаются.
Обновление 4:
Вернувшись домой, удалил «непроверяемый» код, работающий на VS2008, и все стало намного хуже. Возможно, запуск непроверяемого кода для правильной отладки может быть ответом. В режиме «релиз» все выходные данные будут проверяемыми.
Обновление 5:
Теперь я решил, что моя вышеупомянутая идея - единственный жизнеспособный вариант на данный момент. Хотя сгенерированный код не поддается проверке, я еще не нашел ни одного VerificationException
. Я не знаю, как этот сценарий повлияет на конечного пользователя.
В качестве бонуса, моя вторая проблема также была решена. :)
Вот небольшая заставка о том, чем я закончил. Он попадает в точки останова, делает правильный шаг (вход / выход / овер) и т. Д. В общем, желаемый эффект.
Я, однако, все еще не принимаю это как способ сделать это. Мне это кажется слишком хакерским. Было бы неплохо получить подтверждение о реальной проблеме.
Обновление 6:
Только что было внесено изменение для тестирования кода на VS2010, кажется, есть некоторые проблемы:
Первый звонок теперь не выполняется правильно. (нарушение-утверждение ...) ударил. В других случаях работает нормально.Какой-то старый код выдавал ненужные позиции. Убрал код, работает как положено. :)- А если серьезно, то при втором вызове программы точки останова завершаются неудачно (при компиляции в памяти выгрузка сборки в файл, кажется, снова делает точки останова счастливыми).
Оба эти случая работают правильно под VS2008. Основное отличие состоит в том, что под VS2010 все приложение компилируется для .NET 4, а под VS2008 компилируется в .NET 2. Оба работают под управлением 64-разрядных систем.
Обновление 7:
Как уже упоминалось, я получил mdbg под 64-битной. К сожалению, у него также есть проблема с точкой останова, из-за которой он не может выйти из строя, если я перезапущу программу (это означает, что она перекомпилируется, поэтому не использует ту же сборку, но все еще использует тот же исходный код).
Обновление 8:
Я подал ошибку на сайте MS Connect в отношении проблемы с точкой останова.
Обновление: исправлено
Обновление 9:
После долгих раздумий кажется, что единственный способ порадовать отладчик - это использовать SSA, поэтому каждый шаг может быть изолированным и последовательным. Мне еще предстоит доказать это понятие, хотя. Но это кажется логичным. Очевидно, что очистка временного кода от SSA нарушит отладку, но это легко переключить, и оставить их без особых накладных расходов.
nop
s, степпинг не удался (я проверю это снова, чтобы быть уверенным). Это жертва, я думаю, мне придется пойти на это. Это не то, что VS может даже работать без прав администратора :) Кстати, используя Reflection.Emit через DLR (очень взломанный ранний разветвленный).