Когда предоставленный пример построен в режиме выпуска, а затем JIT-преобразован в 64-битный машинный код, он не содержит достаточно информации для отладчика, чтобы сопоставить точку останова с какой-либо конкретной машинной инструкцией. Вот почему отладчик никогда не останавливается в этой точке останова во время выполнения машинного кода, созданного с помощью JIT. Он просто не знает, где остановиться. Вероятно, это какое-то неправильное поведение или даже ошибка в 64-битном отладчике CLR, потому что он воспроизводится только тогда, когда он JIT-преобразован в 64-битный машинный код, но не в 32-битный машинный код.
Когда отладчик видит точку останова в вашем коде, он пытается найти машинную инструкцию в коде JIT-ed, которая соответствует местоположению, отмеченному точкой останова. Во-первых, ему нужно найти инструкцию IL, которая соответствует местоположению точки останова в вашем коде C #. Затем ему нужно найти машинную инструкцию, соответствующую команде IL. Затем он устанавливает реальную точку останова на найденной машинной инструкции и запускает выполнение метода. В вашем случае похоже, что отладчик просто игнорирует точку останова, потому что он не может сопоставить ее с конкретной машинной инструкцией.
Отладчик не может найти адрес машинной инструкции, которая следует сразу за оператором if… else. Оператор if… else и код внутри него каким-то образом вызывают такое поведение. Неважно, какое утверждение следует за if… else. Вы можете заменить оператор Console.WriteLine («2») каким-либо другим, и вы все равно сможете воспроизвести проблему.
Вы увидите, что компилятор C # генерирует блок try… catch вокруг логики, которая читает список, если вы дизассемблируете получившуюся сборку с помощью Reflector. Это задокументированная функция компилятора C #. Вы можете узнать больше об этом в Оператор foreach
Блок try… catch… finally имеет довольно агрессивный эффект на JIT-код. Он использует механизм Windows SEH под капотом и плохо переписывает ваш код. Я не могу найти ссылку на хорошую статью прямо сейчас, но уверен, что вы найдете ее там, если вам интересно.
Вот что здесь происходит. Блок try… finally внутри оператора if… else вызывает сбой отладчика. Вы можете воспроизвести свою проблему с помощью очень простого кода.
bool b = false;
if (b)
{
try
{
b = true;
}
finally
{
b = true;
}
}
else
{
b = true;
}
b = true;
Этот код не вызывает никаких внешних функций (он устраняет эффект встраивания метода, предложенного одним из ответов), и он компилируется непосредственно в IL без какого-либо дополнительного кода, добавленного компилятором C #.
Он воспроизводится только в режиме выпуска, потому что в режиме отладки компилятор выдает инструкцию IL NOP для каждой строки вашего кода C #. Инструкция IL NOP ничего не делает и напрямую компилируется в инструкцию CPU NOP JITer, который тоже ничего не делает. Полезность этой инструкции заключается в том, что она может использоваться отладчиком в качестве якоря для точек останова, даже если остальная часть кода плохо переписана JITer.
Мне удалось заставить отладчик работать правильно, поместив одну инструкцию NOP прямо перед оператором, следующим за if… else.
Вы можете узнать больше об операциях NOP и процессе сопоставления отладчика здесь Отладка Иллинойс
Вы можете попробовать использовать WinDbg и расширение SOS, чтобы изучить JIT-версию метода. Вы можете попробовать изучить машинный код, который генерирует JIT-er, и попытаться понять, почему он не может сопоставить этот машинный код с определенной строкой C #.
Вот пара ссылок об использовании WinDbg для взлома управляемого кода и получения адреса памяти метода JIT-ed. Я считаю, что вы сможете найти способ получить оттуда JIT-код для метода: Установка точки останова в WinDbg для управляемого кода, Памятка по SOS (.NET 2.0 / 3.0 / 3.5).
Вы также можете попытаться сообщить о проблеме в Microsoft. Вероятно, это ошибка отладчика CLR.
Спасибо за интересный вопрос.
person
Dennis
schedule
24.04.2011