#c# #assembly #visual-studio-debugging
#c# #сборка #visual-studio-debugging
Вопрос:
Я исследовал некоторые детали ассемблерного кода, который выполняется для некоторых типичных конструкций 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.Увеличение), потому что отладчик Visual Studio просто выполняет вызов и не переходит к выполнению подпрограммы.
Я намеревался посмотреть, какой код выполняется при блокировке.Выполняется увеличение.
-
Почему отладчик не выполняет выполнение в вызываемой подпрограмме?
-
Как заставить его выполнить этот вызов (для проектов C # уже включена смешанная отладка)?
Комментарии:
1. learn.microsoft.com/en-us/visualstudio/debugger/…
2. Это неуправляемый код, встроенный в среду CLR, вы не можете ни выполнить его, ни просмотреть с помощью управляемого отладчика. Требуется хитрость, чтобы обмануть отладчик. Включите неуправляемую отладку и вернитесь к точке останова. Обратите внимание на этот адрес вызова. Используйте Debug > Windows > Call Stack и дважды щелкните фрейм ntdll внизу. Это переключает механизм отладчика на неуправляемый. Теперь вы можете ввести адрес в адресное поле и просмотреть машинный код.
3. Обратите внимание, что обычно это имеет смысл делать только с оптимизированной сборкой выпуска. Инструменты> Параметры> Отладка> Общие, флажок «Подавить оптимизацию JIT» снят. Заблокировано. Increment() является встроенным, обычным генератором кода для этого встроенного, а не вызова CLR. Должна отображаться инструкция LOCK INC для переменной типа int для x86, LOCK ADD для x64.
Ответ №1:
Спасибо, Ганс, это сработало … каким-то образом 😉
Без оптимизации 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.
Хотя мне удалось достичь своей цели, у меня возникли некоторые трудности.
-
Когда отключили «Подавлять оптимизацию JIT при загрузке модуля» и установили точку останова в коде, я не мог отслеживать выполнение в окне сборки. Процесс был завершен (нарушение доступа) при выполнении команды первого вызова. Мне пришлось применить другой подход и переключиться на Debugger.Вызов Break() непосредственно перед блокировкой.Увеличьте код C # и присоедините отладчик, заставляя его обрабатывать мой процесс .NET как собственный процесс:
- запустите мое приложение без отладки
- подключите отладчик, как если бы мое приложение было встроенным
- триггер заблокирован.Увеличьте выполнение (с остановом отладчика непосредственно перед ним).
И я смог отследить то, что искал. Но почему все это завершилось сбоем, если мое приложение было запущено непосредственно с помощью отладки в VS? Я полагаю, что отладчик не был подключен к приложению, как если бы оно было встроенным. Но почему это должно иметь значение, если все, о чем мы заботимся, — это поток инструкций в окне сборки?
-
Учитывая, что мы сохраняем включенным «Подавление JIT-оптимизации при загрузке модуля», почему отладчик не выполняет вызов и не раскрывает код внутри Interlocked.Процедура увеличения? Еще раз — это всего лишь инструкции процессора. Нет управляемых и встроенных инструкций, верно?
-
Это было упомянуто в комментарии, который заблокирован.Приращение — это неуправляемый код. Каким образом это неуправляемо, поскольку все сводится к нескольким инструкциям процессора? Что делает его неуправляемым и почему? Это не системный вызов или что-либо еще, что зависит от неуправляемых ресурсов. Все, на что он ссылается и что использует, на самом деле управляется. Тогда почему?
Комментарии:
1. Привет, мы рады слышать, что ваша проблема решена, спасибо, что поделились. Пожалуйста, отметьте свой ответ как answer, и это поможет другим членам сообщества упростить поиск этой полезной информации, заранее спасибо.