Отладчик Visual Studio не входит в команду «вызова» - PullRequest
0 голосов
/ 02 марта 2019

Я исследовал некоторые детали ассемблерного кода, который выполняется для некоторых типичных конструкций C #.При отладке простого кода C # я следую за его выполнением в окне дизассемблирования Visual Studio (это сборка приложения с полной информацией отладки).

У нас есть следующий фрагмент кода:

return Interlocked.Increment(ref _boxedInt);

00007FFD6CFB5522  sub         esp,30h  
00007FFD6CFB5525  lea         rbp,[rsp+30h]  
00007FFD6CFB552A  mov         qword ptr [rbp+10h],rcx  
00007FFD6CFB552E  cmp         dword ptr [7FFD6C166370h],0  
00007FFD6CFB5535  je          00007FFD6CFB553C  
00007FFD6CFB5537  call        00007FFDCBCFFCC0  
00007FFD6CFB553C  mov         rcx,qword ptr [rbp+10h]  
00007FFD6CFB5540  cmp         dword ptr [rcx],ecx  
00007FFD6CFB5542  mov         rcx,qword ptr [rbp+10h]  
00007FFD6CFB5546  add         rcx,8  
00007FFD6CFB554A  call        00007FFDCA6624B0  
00007FFD6CFB554F  mov         dword ptr [rbp-4],eax  
00007FFD6CFB5552  mov         eax,dword ptr [rbp-4]  
00007FFD6CFB5555  lea         rsp,[rbp]  
00007FFD6CFB5559  pop         rbp  
00007FFD6CFB555A  ret  

Проблема с инструкцией call по адресу 00007FFD6CFB554A (которая находится вна самом деле вызов Interlocked.Increment), поскольку отладчик Visual Studio просто переходит по вызову и не отслеживает выполнение в подпрограмме.

Я хотел посмотреть, какой код выполняется, когда Interlocked.IncrementВыполнено.

  1. Почему отладчик не следует за выполнением в вызываемой подпрограмме?

  2. Как заставить его войти в этот вызов (естьуже смешанная отладка включена для проектов C #)?

1 Ответ

0 голосов
/ 02 марта 2019

Спасибо, Ганс, это сработало ... как-то;)

Без JIT-оптимизации это выглядит так:

00007FFDCA6624B0  nop         dword ptr [rax+rax]  
00007FFDCA6624B5  mov         eax,1  
00007FFDCA6624BA  lock xadd   dword ptr [rcx],eax  
00007FFDCA6624BE  inc         eax  
00007FFDCA6624C0  ret  

С JIT-оптимизацией все намного сложнее.Это всего лишь фрагмент кода, структура которого трудно охватить, но она есть:

00007FFD6CD7219F  lea         rax,[rsi+8]  
00007FFD6CD721A3  mov         edx,1  
00007FFD6CD721A8  lock xadd   dword ptr [rax],edx  
00007FFD6CD721AC  lea         eax,[rdx+1]  

Похоже, он возвращает увеличенное значение в EAX.

Хотя мне удалось достичьмоя цель у меня были некоторые трудности.

  1. Когда было отключено «Подавить оптимизацию JIT при загрузке модуля» и поместить точку останова в код, я не смог отследить выполнение в окне сборки.Процесс был прерван (нарушение прав доступа) при входе в команду первого вызова.Мне пришлось воспользоваться другим подходом и переключиться на вызов Debugger.Break () непосредственно перед Interlocked.Increment в коде C # и подключить отладчик, заставив его обрабатывать мой .NET-процесс как собственный процесс:

    • startмое приложение без отладки
    • подключить отладчик так, как если бы мое приложение было собственным
    • Триггер Interlocked.Increment выполнение (с прерыванием отладчика непосредственно перед ним).

И я смог отследить то, что искал.Но почему все это вылетело, если мое приложение было запущено непосредственно с отладкой в ​​VS?Я полагаю, что отладчик не был присоединен к приложению, как если бы он был нативным.Но почему это имеет значение, если все, что нас волнует, это поток инструкций в окне сборки?

Учитывая, что мы включили «Подавить оптимизацию JIT при загрузке модуля», почему отладчик не входит в вызов и не раскрывает код внутри процедуры Interlocked.Increment?Еще раз - это всего лишь инструкции процессора.Нет никаких управляемых и собственных инструкций, верно?

В комментарии было упомянуто, что Interlocked.Increment является неуправляемым кодом.Каким образом это неуправляемо, так как все сводится к горстке инструкций процессора?Что делает его неуправляемым и почему?Это не системный вызов или что-либо, что зависит от неуправляемых ресурсов.Все, на что он ссылается и использует, фактически управляется.Тогда почему?

...