调用堆栈中的 InlinedCallFrame
有时在托管调用堆栈中,尽管有任何方法调用,我都会得到 InlinedCallFrame。这究竟意味着什么?
0024e6dc 6fe1e38f Microsoft.Win32.SystemEvents.OnUserPreferenceChanged(Int32, IntPtr, IntPtr)
0024e6fc 6fa64c29 Microsoft.Win32.SystemEvents.WindowProc(IntPtr, Int32, IntPtr, IntPtr)
0024e700 000a1104 [InlinedCallFrame: 0024e700]
0024e8d8 6e378d5e System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32, Int32, Int32)
0024e974 6e3789c7 System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)
0024e9c8 6e378811 System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)
Sometimes in managed calls stack, inspite any method call I get InlinedCallFrame. What does this exactly mean?
0024e6dc 6fe1e38f Microsoft.Win32.SystemEvents.OnUserPreferenceChanged(Int32, IntPtr, IntPtr)
0024e6fc 6fa64c29 Microsoft.Win32.SystemEvents.WindowProc(IntPtr, Int32, IntPtr, IntPtr)
0024e700 000a1104 [InlinedCallFrame: 0024e700]
0024e8d8 6e378d5e System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32, Int32, Int32)
0024e974 6e3789c7 System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)
0024e9c8 6e378811 System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
上下文:
在计算的早期,没有异常处理,也没有垃圾收集。所以对于堆栈的可步行性并没有硬性要求。因此,一般来说,栈是不可行走的。
为了支持异常、垃圾收集和调试,CLR 必须确保所有托管帧都是可步行的。为此,JIT 生成所描述的代码,以便堆栈遍历器知道如何展开各个托管帧。
有时,托管代码需要调用一些用 C++ 编写的代码。这可能是因为我们正在执行 PInvoke,但也可能是其他各种原因,例如,当我们需要执行 GC 时,或者我们正在执行反射等时。在这些情况下,堆栈上会包含一些 C++ 代码它,并且不能保证 C++ 代码生成可步行的堆栈帧,因此我们遇到了问题。
解决方案:
这就是我们拥有这些 Frame 对象的原因。这些 Frame 对象旨在解决该问题。
Frame 对象在堆栈上分配,它是一个链表,因此每个 Frame 都可以有一个父 Frame(如果没有,则为
FRAME_TOP
),并且可以通过线程静态。它们一起形成一个与实际堆栈并排的单独链表。有了这两个,现在我们可以并排行走它们,并按地址将它们交错。请记住,当我们进行函数调用时,堆栈指针值会减少。因此,从被调用者到调用者,堆栈指针应该始终增加,因此我们确切地知道如何交错它们。更有趣的是,这些 Frame 对象负责跳过 C++ 帧,因此我们确切地知道如何遍历托管帧的整个堆栈。
自 x64 以来,平台应用程序二进制接口的设计使得堆栈始终是可步行的,至少对于 Windows 而言是这样。因此,技术上不再需要帮助堆栈遍历器。但由于必须支持 x86,而且我们可以跳过本机帧,这是一件好事,因此保留了这个单独的 Frame 对象链。
从技术上讲,
!clrstack
将它们像其他行一样显示为一行,这有点令人困惑。该行不代表函数调用。相反,它是一个恰好位于堆栈上的特殊对象,就像您的参数或局部变量一样。对于 .NET 运行时开发人员来说,调试堆栈遍历器中出现的问题可能很有用,但对于编写 .NET 代码的开发人员来说几乎毫无用处。欢迎在 此处了解有关 CLR 堆栈遍历的更多信息
Context:
In the early days of computing, there is no exception handling and there is no garbage collection. So there isn't a hard requirement for stacks to be walkable. Therefore, in general, the stack is not walkable.
To support exception, garbage collection, and debugging, the CLR has to make sure that all managed frames are walkable. To do that, the JIT generates code that is described so that the stack walker knows how to unwind the individual managed frames.
Once in a while, managed code needs to call into some code written in C++. It could be because we are doing a PInvoke, but it could also be various other reasons, for example, when we need to do a GC, or we are doing reflection, etc. In those cases, the stack will have some C++ code on it, and there is no guarantee that c++ code produces stack frames that are walkable, so we have a problem.
Solution:
That's why we have these Frame objects. These Frame objects are meant to solve that problem.
A Frame object is allocated on the stack, it is a linked list so every Frame can have a parent Frame (or
FRAME_TOP
if we don't have one), and the top Frame can be accessed through a thread static. Together, these form a separate linked list that is side-by-side with the actual stack.With the two, now we can walk them side by side, and interleave them by their addresses. Remember stack pointer values decrease as we make function calls. So from the callee to caller, the stack pointer should always increase, so we know exactly how to interleave them. More interestingly, these Frame objects are responsible for skipping through the c++ frames, so we know exactly how to walk the whole stack for managed frames.
Ever since x64, the platform application binary interface is designed so that the stack is always walkable, at least for Windows. So helping the stack walker is technically no longer needed. But since x86 has to be supported, and it is a nice thing to have that we can skip through the native frames, this separate chain of Frame objects stayed.
Technically, it is a bit confusing that
!clrstack
show them there as a row just like the others. That line does NOT represent a function call. Instead, it is a special object that happens to be on the stack, just like your arguments or locals. It might be useful for a .NET runtime developer to debug what's gone wrong in the stack walker, but it is almost useless for developers writing .NET code.Feel free to learn more about CLR stack walking here