Я думаю, что моя команда обнаружила ошибку в 64-битном компиляторе, могут ли другие подтвердить или сказать, почему это правильно?

У меня есть простой пример чистой комнаты с этой возможной ошибкой.

   static void Main(string[] args)
    {
        bool MyFalse = false;

        if (MyFalse)
        {
            throw new Exception();
        }
        try
        {
            int i = 0;
        }
        catch (Exception e)
        {
            Console.Write(e);
        }

        Console.Read();
    }

Если скомпилировано в x64 или AnyCPU (если в VS2012 установлено 32bit, то если вы поставили точку останова в блоке if, она всегда будет удалена.

Мы пробовали его в VS2012, VS2010 и VS2008, и все они запускали блок if при компиляции в 64-битном режиме, но в 32-битном режиме он не запускает блок if.

Мы рассмотрели IL для 32-битных и 64-битных версий, и они выглядят одинаково.

Мы нашли это в производственном коде, потому что блок if был запущен, и исключение было выбрано независимо от значения переменной boolean, хотя в простом примере мы не можем генерировать исключение, это происходит в производственном коде. Дел >

Так как это происходит в производственном коде, это не просто проблема отладчика.

Очень странное поведение, но, похоже, не работает с любым кодом в блоке if. Разработчик прыгнул с пистолета, предполагая, что это был исключение, которое он видел.

(Вся отладка находится в режиме отладки - производство находится в выпуске)

Если бросок закомментирован - блок if не достигнут.

Ответы

Ответ 1

Хорошо, я это вижу. Это действительно не так в сборке Debug для 64-разрядного отладчика. Ключ состоит в том, чтобы точно установить точку останова в инструкции if(), а затем начать шагать. Это будет выглядеть так, как выполняется инструкция throw. Но это на самом деле не происходит, фактическое выполнение кода является правильным.

Чтобы узнать, что происходит, сделайте это шаг в строке оператора throw. Затем используйте Debug + Disassembly, чтобы увидеть, где он находится. На моей машине это выглядит так:

       if (MyFalse)
00000040  movzx       ecx,byte ptr [rbp+8] 
00000044  xor         eax,eax 
00000046  test        ecx,ecx 
00000048  sete        al 
0000004b  mov         dword ptr [rbp+1Ch],eax 
0000004e  movzx       eax,byte ptr [rbp+1Ch] 
00000052  mov         byte ptr [rbp+18h],al 
00000055  movzx       eax,byte ptr [rbp+18h] 
00000059  test        eax,eax 
0000005b  jne         0000000000000088            // <=== Note this jump
        {
0000005d  nop 
            throw new Exception();
0000005e  lea         rcx,[5B848928h] 
00000065  call        000000005F65E9E0 
0000006a  mov         qword ptr [rbp+20h],rax 
0000006e  mov         rax,qword ptr [rbp+20h] 
00000072  mov         qword ptr [rbp+28h],rax 
00000076  mov         rcx,qword ptr [rbp+28h] 
0000007a  call        000000005BE4A5D0 
0000007f  mov         rcx,qword ptr [rbp+28h] 
00000083  call        000000005F73E36C 
00000088  nop                                     // <=== yellow arrow here
        }
        try
        {
00000089  nop 
            int i = 0;

Вы даже можете увидеть это по тому, как отладчик группирует инструкции машинного кода с инструкциями С#. Обратите внимание, что отладчик запутался в NOP по адресу 0088. Он считает, что он принадлежит составной формуле if(). Поэтому он помещает желтую подсветку внутри блока. Но программа фактически взяла прыжок по адресу 005b и пропустила оператор throw (адреса от 005e до 0083).

Не так уверен, где возложить вину на это, нельзя винить компилятор С# или файл PDB, поскольку он работает правильно в 32-битном режиме. Он пахнет проблемой дрожания, примечательно, что джиттер x86 не генерирует команду NOP. Вы также можете указать, что джиттер должен был сгенерировать инструкцию JNE для перехода к адресу 0089. Это всего лишь догадки, вы можете получить реальный ответ на сайте connect.microsoft.com

Просто соблюдайте эту причуду, пока вы не услышите ответ, или все мы получим обновление в пакете обновления. Код на самом деле выполняется правильно, поэтому вы страдаете только слабым случаем недоумения.

Ответ 2

В оптимизированном коде нет строгой корреляции между MSIL и собственным машинным кодом и исходным кодом. Это приводит к тому, что отладчик иногда выделяет код, отличный от того, что выполняется, выделяет одну и ту же строку несколько раз, в то время как один шаг, или помещает точки останова в местах, отличных от того, где вы планировали.

Это фундаментальная проблема с отладкой оптимизированного кода и представляет собой несоответствия в формате отладочной информации. В компиляторе или отладчике нет ошибок. Возможно, вам придется прибегнуть к отладке в режиме разборки.