если оператор работает некорректно - разборка мне ничего не объясняет - PullRequest
5 голосов
/ 13 января 2012

Я столкнулся с действительно странной проблемой, над которой я застрял.

Я работаю над библиотекой классов, которая подключается к веб-сервисам. Библиотека используется настольными приложениями.

В коде у меня сейчас:

Int32 errorCode32 = errorCode;
Int32 errorTimeout = 300;

if (errorCode32.Equals(errorTimeout) == false)
{
    System.Console.Out.WriteLine("aaa");
    return;
}

где errorCode равен 300. Таким образом, на первый взгляд видно, что если errorCode == 300, то код внутри оператора if не должен выполняться, поскольку он определен для выполнения только тогда, когда errorCode не равен 300 .

До сих пор все ясно, но теперь начинается все веселье.

Приложение работает, и выполняется метод с приведенным выше фрагментом кода. errorCode равен 300, ожидаемый результат состоит в том, что приложение не выполнит никакого кода внутри оператора if, поскольку весь оператор ложен. Но на самом деле приложение идет внутрь «если» и сразу же переходит к оператору «возврат». System.Console.Out ... никогда не выполняется. Если я заменю чистый оператор return на «throw new SomeException ()»

Int32 errorCode32 = errorCode;
Int32 errorTimeout = 300;

if (errorCode32.Equals(errorTimeout) == false)
{
    System.Console.Out.WriteLine("aaa");
    throw new SomeException();
}

Я получу тот же результат. Приложение переходит к оператору if (примечание: [errorCode32.Equals (errorTimeout) == false] в моем случае имеет значение false), не выполняет Console.Out ..., но выдает SomeException.

Я несколько раз перестраивал все, удалял все двоичные файлы, я даже удалял весь проект с диска и снова извлекал его из репозитория в чистую папку.

Я был так растерян, что даже разбирал код, чтобы посмотреть, что произойдет (даже если я не специалист по ассемблеру). Но результаты странные для меня.

Разобранный код следует:

    50:                 Int32 errorCode32 = errorCode;
000000e5  mov         eax,dword ptr [ebp-50h] 
000000e8  mov         dword ptr [ebp-54h],eax 
    51:                 Int32 errorTimeout = 300;
000000eb  mov         dword ptr [ebp-58h],12Ch 
    52: 
    53:                 if (errorCode32.Equals(errorTimeout) == false)
000000f2  lea         ecx,[ebp-54h] 
000000f5  mov         edx,dword ptr [ebp-58h] 
000000f8  call        699EB198 
000000fd  mov         dword ptr [ebp-68h],eax 
00000100  movzx       eax,byte ptr [ebp-68h] 
00000104  mov         dword ptr [ebp-48h],eax 
00000107  cmp         dword ptr [ebp-48h],0 
0000010b  jne         00000134 
    54:                 {
0000010d  nop              
    55:                     System.Console.Out.WriteLine("aaa");
0000010e  call        69538768 
00000113  mov         dword ptr [ebp+FFFFFF7Ch],eax 
00000119  mov         edx,dword ptr ds:[0302CE30h] 
0000011f  mov         ecx,dword ptr [ebp+FFFFFF7Ch] 
00000125  mov         eax,dword ptr [ecx] 
00000127  call        dword ptr [eax+000000D8h] 
0000012d  nop              
    56:                     return;
0000012e  nop              
0000012f  jmp         00000287 
00000134  mov         eax,dword ptr ds:[0302CE34h] 
0000013a  mov         dword ptr [ebp-6Ch],eax 
0000013d  mov         edx,5 
00000142  mov         ecx,6EDE3FBEh 
00000147  call        FA68D488 
0000014c  mov         dword ptr [ebp-70h],eax 
    57:                 }

когда я отлаживаю инструкции, я могу понять, что происходит до строки:

00000104  mov         dword ptr [ebp-48h],eax

Я ожидаю, что значение, сохраненное в eax, будет скопировано в папку "dword ptr [ebp-48h]", и приложение перейдет к следующей строке (00000107). Но этого не происходит. Когда я пытаюсь перешагнуть строку 00000104, приложение сразу переходит на строку

00000134  mov         eax,dword ptr ds:[0302CE34h]

Я не могу понять, что здесь происходит. Я пытался искать через Интернет, но я не мог найти ничего полезного. У кого-нибудь есть предложения, что может быть причиной проблемы или как ее решить?


Редактировать

Я забыл поместить информацию о том, что я использую Visual Studio 2008 и компилирую в .NET 3.5. Все обновления установлены.


Редактировать

Весь метод в C #

        private void nativeDocumentServiceWrapper_PostInvokeEvents(object sender, WebServiceInvokeEventArgs e)
        {
            Exception exception = e.Exception;
            if (exception == null)
            {
                _invokeRetries = 0;
                return;
            }

            string errorCodeString = ErrorHandler.GetErrorCodeString(exception);
            int errorCode;
            if (int.TryParse(errorCodeString, out errorCode))
            {
                e.Exception = new VaultException(errorCode, exception);
            }

            Int32 errorCode32 = errorCode;
            Int32 errorTimeout = 300;

            if (errorCode32.Equals(errorTimeout) == false)
            {
                System.Console.Out.WriteLine("aaa");
                return;
            }

            Trace.TraceWarning("Invoke failed (count: {4}) {0}.{1} #{2} error '{3}'", _moduleName, e.MethodName, _id, errorCodeString, _invokeRetries + 1);

            if (_invokeRetries > 0)
            {
                //int errorCode;
                if (int.TryParse(errorCodeString, out errorCode))
                {
                    //throw new VaultException(errorCode, exception);
                    e.Exception = new VaultException(errorCode, exception);
                }
                return;
            }

            e.Exception = null;

            // we ran into error 300 or 319
            // the solution is to log in again and re-run the command

            tryReloginAndInvokeVaultMethodAgain(e);
        }

и разобрали:

    34:             private void nativeDocumentServiceWrapper_PostInvokeEvents(object sender, WebServiceInvokeEventArgs e)
    35:             {
00000000  push        ebp  
00000001  mov         ebp,esp 
00000003  push        edi  
00000004  push        esi  
00000005  push        ebx  
00000006  sub         esp,84h 
0000000c  mov         esi,ecx 
0000000e  lea         edi,[ebp-54h] 
00000011  mov         ecx,12h 
00000016  xor         eax,eax 
00000018  rep stos    dword ptr es:[edi] 
0000001a  mov         ecx,esi 
0000001c  xor         eax,eax 
0000001e  mov         dword ptr [ebp-1Ch],eax 
00000021  mov         dword ptr [ebp-3Ch],ecx 
00000024  mov         dword ptr [ebp-40h],edx 
00000027  cmp         dword ptr ds:[01AD2DD8h],0 
0000002e  je          00000035 
00000030  call        6AB8A719 
00000035  mov         dword ptr [ebp-48h],0 
0000003c  xor         edx,edx 
0000003e  mov         dword ptr [ebp-5Ch],edx 
00000041  xor         edx,edx 
00000043  mov         dword ptr [ebp-44h],edx 
00000046  xor         edx,edx 
00000048  mov         dword ptr [ebp-4Ch],edx 
0000004b  xor         edx,edx 
0000004d  mov         dword ptr [ebp-58h],edx 
00000050  nop              
    36:                 Exception exception = e.Exception;
00000051  mov         eax,dword ptr [ebp+8] 
00000054  mov         eax,dword ptr [eax+4] 
00000057  mov         dword ptr [ebp-44h],eax 
    37:                 if (exception == null)
0000005a  cmp         dword ptr [ebp-44h],0 
0000005e  setne       al   
00000061  movzx       eax,al 
00000064  mov         dword ptr [ebp-48h],eax 
00000067  cmp         dword ptr [ebp-48h],0 
0000006b  jne         0000007F 
    38:                 {
0000006d  nop              
    39:                     _invokeRetries = 0;
0000006e  mov         eax,dword ptr [ebp-3Ch] 
00000071  xor         edx,edx 
00000073  mov         dword ptr [eax+00000088h],edx 
    40:                     return;
00000079  nop              
0000007a  jmp         00000287 
    41:                 }
    42: 
    43:                 string errorCodeString = ErrorHandler.GetErrorCodeString(exception);
0000007f  mov         ecx,dword ptr [ebp-44h] 
00000082  call        FF0827F0 
00000087  mov         dword ptr [ebp-60h],eax 
0000008a  mov         eax,dword ptr [ebp-60h] 
0000008d  mov         dword ptr [ebp-4Ch],eax 
    44:                 int errorCode;
    45:                 if (int.TryParse(errorCodeString, out errorCode))
00000090  lea         edx,[ebp-50h] 
00000093  mov         ecx,dword ptr [ebp-4Ch] 
00000096  call        694B44A8 
0000009b  mov         dword ptr [ebp-64h],eax 
0000009e  cmp         dword ptr [ebp-64h],0 
000000a2  sete        al   
000000a5  movzx       eax,al 
000000a8  mov         dword ptr [ebp-48h],eax 
000000ab  cmp         dword ptr [ebp-48h],0 
000000af  jne         000000E5 
    46:                 {
000000b1  nop              
    47:                     e.Exception = new VaultException(errorCode, exception);
000000b2  mov         ecx,5482E0Ch 
000000b7  call        FA68D364 
000000bc  mov         dword ptr [ebp+FFFFFF78h],eax 
000000c2  push        dword ptr [ebp-44h] 
000000c5  mov         edx,dword ptr [ebp-50h] 
000000c8  mov         ecx,dword ptr [ebp+FFFFFF78h] 
000000ce  call        FF07FCB0 
000000d3  mov         edx,dword ptr [ebp+8] 
000000d6  mov         eax,dword ptr [ebp+FFFFFF78h] 
000000dc  lea         edx,[edx+4] 
000000df  call        6A90E288 
    48:                 }
000000e4  nop              
    49: 
    50:                 Int32 errorCode32 = errorCode;
000000e5  mov         eax,dword ptr [ebp-50h] 
000000e8  mov         dword ptr [ebp-54h],eax 
    51:                 Int32 errorTimeout = 300;
000000eb  mov         dword ptr [ebp-58h],12Ch 
    52: 
    53:                 if (errorCode32.Equals(errorTimeout) == false)
000000f2  lea         ecx,[ebp-54h] 
000000f5  mov         edx,dword ptr [ebp-58h] 
000000f8  call        699EB198 
000000fd  mov         dword ptr [ebp-68h],eax 
00000100  movzx       eax,byte ptr [ebp-68h] 
00000104  mov         dword ptr [ebp-48h],eax 
00000107  cmp         dword ptr [ebp-48h],0 
0000010b  jne         00000134 
    54:                 {
0000010d  nop              
    55:                     System.Console.Out.WriteLine("aaa");
0000010e  call        69538768 
00000113  mov         dword ptr [ebp+FFFFFF7Ch],eax 
00000119  mov         edx,dword ptr ds:[0302CE30h] 
0000011f  mov         ecx,dword ptr [ebp+FFFFFF7Ch] 
00000125  mov         eax,dword ptr [ecx] 
00000127  call        dword ptr [eax+000000D8h] 
0000012d  nop              
    56:                     return;
0000012e  nop              
0000012f  jmp         00000287 
00000134  mov         eax,dword ptr ds:[0302CE34h] 
0000013a  mov         dword ptr [ebp-6Ch],eax 
0000013d  mov         edx,5 
00000142  mov         ecx,6EDE3FBEh 
00000147  call        FA68D488 
0000014c  mov         dword ptr [ebp-70h],eax 
    57:                 }
    58: 
    59:                 Trace.TraceWarning("Invoke failed (count: {4}) {0}.{1} #{2} error '{3}'", _moduleName, e.MethodName, _id, errorCodeString, _invokeRetries + 1);
0000014f  mov         eax,dword ptr [ebp-70h] 
00000152  mov         dword ptr [ebp-5Ch],eax 
00000155  mov         eax,dword ptr [ebp-3Ch] 
00000158  push        dword ptr [eax+00000080h] 
0000015e  mov         ecx,dword ptr [ebp-5Ch] 
00000161  xor         edx,edx 
00000163  call        6A914654 
00000168  mov         eax,dword ptr [ebp+8] 
0000016b  push        dword ptr [eax+8] 
0000016e  mov         ecx,dword ptr [ebp-5Ch] 
00000171  mov         edx,1 
00000176  call        6A914654 
0000017b  mov         ecx,6F052DA0h 
00000180  call        FA68D364 
00000185  mov         dword ptr [ebp-74h],eax 
00000188  mov         eax,dword ptr [ebp-5Ch] 
0000018b  mov         dword ptr [ebp+FFFFFF74h],eax 
00000191  mov         eax,dword ptr [ebp-3Ch] 
00000194  mov         eax,dword ptr [eax+00000084h] 
0000019a  mov         edx,dword ptr [ebp-74h] 
0000019d  mov         dword ptr [edx+4],eax 
000001a0  mov         eax,dword ptr [ebp-74h] 
000001a3  push        eax  
000001a4  mov         ecx,dword ptr [ebp+FFFFFF74h] 
000001aa  mov         edx,2 
000001af  call        6A914654 
000001b4  push        dword ptr [ebp-4Ch] 
000001b7  mov         ecx,dword ptr [ebp-5Ch] 
000001ba  mov         edx,3 
000001bf  call        6A914654 
000001c4  mov         ecx,6F052DA0h 
000001c9  call        FA68D364 
000001ce  mov         dword ptr [ebp-78h],eax 
000001d1  mov         eax,dword ptr [ebp-5Ch] 
000001d4  mov         dword ptr [ebp+FFFFFF70h],eax 
000001da  mov         eax,dword ptr [ebp-3Ch] 
000001dd  mov         eax,dword ptr [eax+00000088h] 
000001e3  inc         eax  
000001e4  mov         edx,dword ptr [ebp-78h] 
000001e7  mov         dword ptr [edx+4],eax 
000001ea  mov         eax,dword ptr [ebp-78h] 
000001ed  push        eax  
000001ee  mov         ecx,dword ptr [ebp+FFFFFF70h] 
000001f4  mov         edx,4 
000001f9  call        6A914654 
000001fe  mov         edx,dword ptr [ebp-5Ch] 
00000201  mov         ecx,dword ptr [ebp-6Ch] 
00000204  call        69039D88 
00000209  nop              
    60: 
    61:                 if (_invokeRetries > 0)
0000020a  mov         eax,dword ptr [ebp-3Ch] 
0000020d  cmp         dword ptr [eax+00000088h],0 
00000214  setle       al   
00000217  movzx       eax,al 
0000021a  mov         dword ptr [ebp-48h],eax 
0000021d  cmp         dword ptr [ebp-48h],0 
00000221  jne         00000273 
    62:                 {
00000223  nop              
    63:                     //int errorCode;
    64:                     if (int.TryParse(errorCodeString, out errorCode))
00000224  lea         edx,[ebp-50h] 
00000227  mov         ecx,dword ptr [ebp-4Ch] 
0000022a  call        694B44A8 
0000022f  mov         dword ptr [ebp-7Ch],eax 
00000232  cmp         dword ptr [ebp-7Ch],0 
00000236  sete        al   
00000239  movzx       eax,al 
0000023c  mov         dword ptr [ebp-48h],eax 
0000023f  cmp         dword ptr [ebp-48h],0 
00000243  jne         00000270 
    65:                     {
00000245  nop              
    66:                         //throw new VaultException(errorCode, exception);
    67:                         e.Exception = new VaultException(errorCode, exception);
00000246  mov         ecx,5482E0Ch 
0000024b  call        FA68D364 
00000250  mov         dword ptr [ebp-80h],eax 
00000253  push        dword ptr [ebp-44h] 
00000256  mov         edx,dword ptr [ebp-50h] 
00000259  mov         ecx,dword ptr [ebp-80h] 
0000025c  call        FF07FCB0 
00000261  mov         edx,dword ptr [ebp+8] 
00000264  mov         eax,dword ptr [ebp-80h] 
00000267  lea         edx,[edx+4] 
0000026a  call        6A90E288 
    68:                     }
0000026f  nop              
    69:                     return;
00000270  nop              
00000271  jmp         00000287 
    70:                 }
    71: 
    72:                 e.Exception = null;
00000273  mov         eax,dword ptr [ebp+8] 
00000276  xor         edx,edx 
00000278  mov         dword ptr [eax+4],edx 
    73: 
    74:                 // we ran into error 300 or 319
    75:                 // the solution is to log in again and re-run the command
    76: 
    77:                 tryReloginAndInvokeVaultMethodAgain(e);
0000027b  mov         edx,dword ptr [ebp+8] 
0000027e  mov         ecx,dword ptr [ebp-3Ch] 
00000281  call        FF0876C0 
00000286  nop              
    78:             }

Ответы [ 2 ]

2 голосов
/ 13 января 2012

У вас включена оптимизация, и компилятор объединил оператор return внутри оператора if с оператором в конце функции. Оба имеют одинаковый адрес, отладчик не может сказать, какую строку исходного кода выделить, потому что оба совпадают.

В этом нет ничего плохого. Но если вы хотите, чтобы пошаговое выполнение отладчика работало нормально, вам нужно отключить оптимизацию.

Это только одна из нескольких вещей, которые оптимизатор может сделать, чтобы запутать отладчик. Другие возможности - переупорядочить операторы, которые не имеют зависимости от данных (для оптимизации конвейера), даже разделить операторы и поместить другой оператор между ними.

0 голосов
/ 14 января 2012

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

Во-первых: код, который я представил, был предназначен для решения какой-то конкретной проблемы при использовании сторонней библиотеки. Недокументированное поведение этой библиотеки заключалось в том, что в некоторых ситуациях она вызывает исключения при некоторых условиях. Исходная проблема связана с тем, что библиотека вызвала исключение, даже если мы "обработали" ситуацию ошибки

Второе: при отладке обнаружил следующую ситуацию:

1    if (someVariable)
2    {
3        someCode();
4        someOtherCode();
5        throw someExceptionPassedFromTheThirdPartyLibrary;
6    }
7    somethingElse();

отладчик остановился на строке 1, someVariable имеет значение false, шаг за шагом, отладчик ожидает на строке 5. И здесь я сделал первое ложное предположение - это то, что мое исключение выдается отсюда.

Третье: я начал экспериментировать. «someVariable» всегда был ложным, но иногда отладчик пропускался со строки 1 до 7, а иногда с 1 до 5. Я решил использовать дизассемблер.

Четвертый: я не мог понять поведение дизассемблированного кода, и это заставило меня задуматься о том, что оператор if работает неправильно Кроме того, я нашел статью "http://stackoverflow.com/questions/6054987/if-statement-appears-to-be-evaluating-even-when-condition-evaluates-to-false" [подтвержденная ошибка JIT], которая не помогла мне в этот раз.

Хорошо, я думаю, что после некоторой дополнительной подготовки пришло время объяснить, что действительно произошло в моем коде.

Вообще в моей ситуации оператор "если" вел себя корректно. Я понял это, когда узнал немного больше об архитектуре процессора на ассемблере и модере.

    53:                 if (errorCode32.Equals(errorTimeout) == false)
000000f2  lea         ecx,[ebp-54h] 
000000f5  mov         edx,dword ptr [ebp-58h] 
000000f8  call        699EB198 
000000fd  mov         dword ptr [ebp-68h],eax 
00000100  movzx       eax,byte ptr [ebp-68h] 
00000104  mov         dword ptr [ebp-48h],eax 
00000107  cmp         dword ptr [ebp-48h],0 
0000010b  jne         00000134 
    54:                 {
0000010d  nop              
    55:                     System.Console.Out.WriteLine("aaa");
0000010e  call        69538768 
00000113  mov         dword ptr [ebp+FFFFFF7Ch],eax 
00000119  mov         edx,dword ptr ds:[0302CE30h] 
0000011f  mov         ecx,dword ptr [ebp+FFFFFF7Ch] 
00000125  mov         eax,dword ptr [ecx] 
00000127  call        dword ptr [eax+000000D8h] 
0000012d  nop              
    56:                     return;
0000012e  nop              
0000012f  jmp         00000287 
00000134  mov         eax,dword ptr ds:[0302CE34h] 
0000013a  mov         dword ptr [ebp-6Ch],eax 
0000013d  mov         edx,5 
00000142  mov         ecx,6EDE3FBEh 
00000147  call        FA68D488 
0000014c  mov         dword ptr [ebp-70h],eax 
    57:                 }

Выполнение инструкций со смещением 00000104-0000010b было оптимизировано архитектурой процессора (возможно, имеет место прогнозирование скачка или некоторые другие оптимизации ядра процессора). Поэтому вместо вызова трех инструкций процессор выполнил все за один шаг. Поскольку значения, которые должны сравниваться в 00000107, не были равны, процессор перешел на 00000134. В этот момент я не мог понять, что на самом деле здесь происходит, поэтому я предположил, что он просто выполняет команду «возврата». На самом деле все просто - инструкция «return from if» не в 00000134, как я, а в более инструкции - в 0000012f, и это просто простой безусловный переход.

Когда я понял это, я понял, что этот код ведет себя некорректно. Так что я снова перешел на C # и нашел что-то, что заставило меня очень стыдиться. Потому что, когда я снова отладил код, я обнаружил, что он ведет себя так:

1    if (someVariable)
2    {
3        someCode();
4        someOtherCode();
5        throw someExceptionPassedFromTheThirdPartyLibrary;
6    }
7    somethingElse();

точка останова на 1, F10 индикатор текущей линии на 5, нажмите F10 индикатор текущей линии на 7, нажмите F10 ... и т. д.

Итак, я понял, что отладчик входит в последнюю инструкцию «если», но не выполняет этого. Это ошибка - но не в обработке «если», а только в отладчике VS. Кроме того, ошибка не является критичной, поскольку она фактически представляет собой презентацию и не влияет на отлаживаемый код.

...