Во-первых, я прошу прощения за длину этого вопроса.
Я являюсь автором IronScheme. Недавно я много работал над выпуском достойной отладочной информации, поэтому я могу использовать "собственный" отладчик .NET.
Хотя это было частично успешно, я сталкиваюсь с некоторыми проблемами, связанными с прорезями.
Первая проблема связана с степпингом.
Из-за того, что Scheme является языком выражений, все имеет тенденцию быть завернутым в круглые скобки, в отличие от основных языков .NET, которые, как представляется, являются оператором (или строкой).
Исходный код (Схема) выглядит так:
(define (baz x)
(cond
[(null? x)
x]
[(pair? x)
(car x)]
[else
(assertion-violation #f "nooo" x)]))
Я специально изложил каждое выражение в новой строке.
Выпущенный код преобразуется в С# (через 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));
}
Как вы можете видеть, довольно просто.
Примечание. Если код был преобразован в условное выражение (?:) в С#, все это будет всего лишь одним отладочным шагом, помните об этом.
Здесь вывод 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
не применяются.
- Вызовите baz с помощью null arg, он работает правильно. (null? x), за которым следует x.
- Вызов baz with Cons arg, он работает правильно. (null? x), то (пара? x), то (автомобиль x).
- Вызовите baz с другим arg, он терпит неудачу. (null? x), то (пара? x), то (автомобиль x) тогда (утверждение-нарушение...).
При применении IgnoreSymbolStoreSequencePoints
(как рекомендуется):
- Вызовите baz с помощью null arg, он работает правильно. (null? x), за которым следует x.
- Вызов baz with Cons arg, он не работает. (null? x), то (пара? x).
- Вызовите baz с другим arg, он терпит неудачу. (null? x), то (пара? x), то (автомобиль x) тогда (утверждение-нарушение...).
Я также нашел в этом режиме, что некоторые строки (не показаны здесь) неправильно выделены, они отключены на 1.
Вот некоторые идеи, которые могут быть причиной:
- Tailcalls смущает отладчик
- Перекрывающиеся места (не показаны здесь) смущают отладчик (он очень хорошо работает при настройке точки останова)
- ????
Вторая, но также серьезная проблема - это отладчик, который в некоторых случаях не может сломать/ударить точки останова.
Единственное место, где я могу заставить отладчик правильно сломаться (и постоянно), находится в точке входа метода.
Ситуация становится немного лучше, если IgnoreSymbolStoreSequencePoints
не применяется.
Заключение
Возможно, отладчик VS просто глючит: (
Литература:
Обновление 1:
Mdbg не работает для 64-разрядных сборок. Так что это не так. У меня больше нет 32-битных машин для тестирования. Обновление: я уверен, что это не большая проблема, у кого-нибудь есть исправление? Edit: Да, глупо меня, просто запустите mdbg в командной строке x64:)
Обновление 2:
Я создал приложение С# и попытался проанализировать информацию о линии.
Мои выводы:
- После любой инструкции
brXXX
вам понадобится точка последовательности (если не действительна aka '#line hidden', испуститеnop
). - Перед любой инструкцией
brXXX
испустите '#line hidden' иnop
.
Применяя это, не исправляйте проблемы (только?).
Но добавив следующее, дает желаемый результат:)
- После
ret
испустите '#line hidden' иnop
.
Это режим, в котором 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, и все намного хуже. Возможно, это может быть неправильный код для правильной отладки. В режиме "release" все выходные данные все еще проверяются.
Обновление 5:
Теперь я решил, что моя идея выше - единственный вариант. Хотя сгенерированный код не поддается проверке, мне еще нужно найти VerificationException
. Я не знаю, какое влияние будет оказывать на конечного пользователя такой сценарий.
В качестве бонуса моя вторая проблема также будет решена.:)
Вот немного screencast того, что у меня получилось. Он попадает на точки останова, делает правильный шаг (в/из/за) и т.д. В целом, желаемый эффект.
Я, однако, все еще не принимаю это как способ сделать это. Мне это кажется слишком хриплым. У вас есть подтверждение по реальной проблеме.
Обновление 6:
Просто было изменение для проверки кода на VS2010, похоже, есть некоторые проблемы:
-
Первый вызов теперь не работает правильно. (утверждение-нарушение...). Другие случаи работают нормально.Некоторые старые коды испускали ненужные позиции. Удаленный код работает, как и ожидалось.:) - Серьезно, точки прерывания не срабатывают при втором вызове программы (использование компиляции в памяти, сборка демпинга в файл, похоже, снова делает точки останова).
Оба эти случая работают правильно в VS2008. Основное отличие заключается в том, что в VS2010 все приложение компилируется для .NET 4 и под VS2008, компилируется в .NET 2. Оба работают с 64-разрядными.
Обновление 7:
Как упоминалось, я получил mdbg под 64-разрядным. К сожалению, у него также есть проблема с точкой останова, где он не может сломаться, если я перезапущу программу (это означает, что она перекомпилируется, поэтому не использует одну и ту же сборку, но все равно использует тот же источник).
Обновление 8:
У меня зарегистрирована ошибка на сайте MS Connect относительно проблемы с точкой останова.
Обновление: исправлено
Обновление 9:
После долгих размышлений единственный способ сделать отладчик счастливым, похоже, делает SSA, поэтому каждый шаг может быть изолированным и последовательным. Однако я еще не доказал это понятие. Но это кажется логичным. Очевидно, что очистка темпов от SSA приведет к нарушению отладки, но это легко переключиться, и у них их не будет много накладных расходов.