Visual Studio调试器不会进入“调用”指令

时间:2019-03-02 15:49:44

标签: c# assembly visual-studio-debugging

我一直在研究针对某些典型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时执行什么代码。

  1. 为什么调试器不将执行跟随到调用的子例程中?

  2. 如何强制它进入该调用(已经为C#项目启用了混合调试)?

1 个答案:

答案 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返回增量值。

尽管我已经设法实现了自己的目标,但我还是遇到了一些困难。

  1. 当关闭“在模块加载时禁止JIT优化”并将断点放置在我无法在Assembly窗口中跟踪执行的代码中时。当进入第一个调用指令时,进程终止(访问冲突)。 我不得不采用另一种方法,并在Interlocked.Increment.C#代码之前切换到Debugger.Break()调用,并附加调试器,以使其强制将.NET进程作为本机进程处理。

    • 无需调试即可启动我的应用
    • 附加调试器,就像我的应用是本地的
    • 触发互锁。执行增量(调试器在其之前中断)。

并且我能够跟踪我在寻找什么。但是,如果我的应用程序是直接在VS中调试启动的,那么为什么这一切都会崩溃?我想调试器没有像本地的那样附加到应用程序。但是,如果我们只关心“汇编”窗口中的指令流,为什么会很重要?

  1. 考虑到我们保持启用“抑制模块加载时的JIT优化”的原因,为什么调试器不介入并显示Interlocked.Increment例程中的代码?再一次-这些只是CPU指令。没有托管指令和本机指令,对吗?

  2. 在注释中提到Interlocked.Increment是非托管代码。由于所有内容都归结为少量的CPU指令,因此无法对其进行管理?是什么使它不受管理,为什么?它不是系统调用,也不是依赖于非托管资源的任何东西。它引用和使用的所有内容实际上都得到了管理。那为什么呢?