简体   繁体   English

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

[英]Visual Studio debugger does not step into a "call" instruction

I have been researching some details of the assembly code which is executed for some typical C# constructs.我一直在研究为一些典型的 C# 构造执行的汇编代码的一些细节。 When debugging simple C# code I follow its execution in the Visual Studio Disassembly window (it is a release build of the app with full debug info).在调试简单的 C# 代码时,我会在 Visual Studio 反汇编窗口中跟踪它的执行情况(它是具有完整调试信息的应用程序的发布版本)。

We have a following fragment of code:我们有以下代码片段:

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  

There is a problem with a call instruction at 00007FFD6CFB554A address (which is in fact a call to Interlocked.Increment) because Visual Studio Debugger simply steps over the call and does not follow the execution into the subroutine. 00007FFD6CFB554A地址处的调用指令(实际上是对 Interlocked.Increment 的调用)存在问题,因为 Visual Studio Debugger 只是单步执行调用,并没有跟随执行进入子例程。

It was my intention to have a look what code is executed when Interlocked.Increment is executed.我的目的是看看在执行 Interlocked.Increment 时执行了什么代码。

  1. Why debugger does not follow the execution into a called subroutine?为什么调试器不跟随执行进入被调用的子程序?

  2. How to force it to step into that call (there is already mixed debugging enabled for the C# projects)?如何强制它进入该调用(已经为 C# 项目启用了混合调试)?

Thanks Hans it worked... somehow ;)谢谢汉斯,它起作用了……不知何故;)

Without JIT optimization it looks like:如果没有 JIT 优化,它看起来像:

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

With JIT optimization everything is much more complicated.使用 JIT 优化,一切都变得更加复杂。 It is just a piece of code which structure is hard to embrace but it is there:它只是一段代码,其结构很难接受,但它就在那里:

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

It looks that it returns incremented value in eax.看起来它在 eax 中返回递增的值。

Although I've managed to achieve my goal I have had some difficulties.虽然我已经设法实现了我的目标,但我遇到了一些困难。

  1. When turned off "Suppress JIT optimization on module load" and placed a breakpoint in the code I could not track execution in Assembly window.当关闭“在模块加载时抑制 JIT 优化”并在代码中放置断点时,我无法在程序集窗口中跟踪执行。 Process was terminated (access violation) when stepped into first call instruction.进入第一个调用指令时,进程终止(访问冲突)。 I had to take a different approach and switch to Debugger.Break() call just before Interlocked.Increment in C# code and attach debugger forcing it to handle my .NET process as a native process:我不得不采取不同的方法并在 C# 代码中的 Interlocked.Increment 之前切换到 Debugger.Break() 调用,并附加调试器强制它作为本机进程处理我的 .NET 进程:

    • start my app without debug无需调试即可启动我的应用程序
    • attach debugger as if my app was native附加调试器,就像我的应用程序是原生的一样
    • trigger Interlocked.Increment execution (with debugger break just before it).触发 Interlocked.Increment 执行(在它之前有调试器中断)。

And I was able to track what I was looking for.我能够跟踪我正在寻找的东西。 But why did it all crash if my app was started directly with debugging in VS?但是,如果我的应用程序是通过 VS 中的调试直接启动的,为什么这一切都崩溃了? I suppose that debugger was not attached to the app as if it was native.我想调试器没有附加到应用程序上,就好像它是原生的一样。 But why would it matter if all we care about is a stream of instructions in Assembly window?但是,如果我们只关心“汇编”窗口中的指令流,那为什么会很重要呢?

  1. Considering we keep "Suppress JIT optimization on module load" enabled why does debugger not step into the call and reveal the code inside Interlocked.Increment routine?考虑到我们一直启用“在模块加载时抑制 JIT 优化”,为什么调试器不进入调用并显示 Interlocked.Increment 例程中的代码? Once again - these are just CPU instructions.再一次 - 这些只是 CPU 指令。 There are no managed and native instructions, right?没有托管和本机指令,对吗?

  2. It was mentioned in the comment that Interlocked.Increment is unmanaged code.评论中提到 Interlocked.Increment 是非托管代码。 In which way it is unmanaged since all boils down to a handful CPU instructions?由于一切都归结为少数 CPU 指令,因此它以何种方式不受管理? What makes it unmanaged and why?是什么使它不受管理,为什么? It is not a system call or anything that depends on unmanaged resources.它不是系统调用或任何依赖于非托管资源的东西。 Everything it refers to and uses is in fact managed.它所指和使用的一切实际上都是被管理的。 Then why?那么为什么?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM