我一直在研究针对某些典型C#构造执行的汇编代码的一些细节。在调试简单的C#代码时,我会在Visual Studio Disassembly窗口中跟踪其执行情况(这是具有完整调试信息的应用程序的发行版)。
我们有以下代码片段:
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
在 00007FFD6CFB554A 地址(实际上是对Interlocked.Increment的调用)上的调用指令存在问题,因为Visual Studio调试器只是跳过了调用并不会将执行跟随到子例程中。
我打算看看在执行Interlocked.Increment时执行什么代码。
为什么调试器不将执行跟随到调用的子例程中?
如何强制它进入该调用(已经为C#项目启用了混合调试)?
答案 0 :(得分: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优化”并将断点放置在我无法在Assembly窗口中跟踪执行的代码中时。当进入第一个调用指令时,进程终止(访问冲突)。 我不得不采用另一种方法,并在Interlocked.Increment.C#代码之前切换到Debugger.Break()调用,并附加调试器,以使其强制将.NET进程作为本机进程处理。
并且我能够跟踪我在寻找什么。但是,如果我的应用程序是直接在VS中调试启动的,那么为什么这一切都会崩溃?我想调试器没有像本地的那样附加到应用程序。但是,如果我们只关心“汇编”窗口中的指令流,为什么会很重要?
考虑到我们保持启用“抑制模块加载时的JIT优化”的原因,为什么调试器不介入并显示Interlocked.Increment例程中的代码?再一次-这些只是CPU指令。没有托管指令和本机指令,对吗?
在注释中提到Interlocked.Increment是非托管代码。由于所有内容都归结为少量的CPU指令,因此无法对其进行管理?是什么使它不受管理,为什么?它不是系统调用,也不是依赖于非托管资源的任何东西。它引用和使用的所有内容实际上都得到了管理。那为什么呢?