c# - ICorProfilerCallback2 : CLR profiler does not log all Leave calls

标签 c# .net windows performance clr-profiling-api

我正在尝试编写一个记录进程中所有 .Net 方法调用的分析器。目标是使其具有高性能,并在内存中保留最后 5-10 分钟(固定缓冲区,循环覆盖旧信息),直到用户触发将该信息写入磁盘。预期用途是追踪很少重现的性能问题。

我从 https://github.com/appneta/SimpleCLRProfiler 的 SimpleCLRProfiler 项目开始.分析器使用 .Net 分析的 ICorProfilerCallback2 回调接口(interface)。我让它在我的环境中编译和工作(Win 8.1、.Net 4.5、VS2012)。但是,我注意到有时记录了 Enter 调用的 Leave 调用会丢失。 Console.WriteLine 调用示例(我将 DbgView 的输出减少到理解所需的最低限度):

Line 1481: Entering System.Console.WriteLine
Line 1483: Entering SyncTextWriter.WriteLine
Line 1485: Entering System.IO.TextWriter.WriteLine
Line 1537: Leaving SyncTextWriter.WriteLine

两个进入调用没有对应的离开调用。经过分析的 .Net 代码如下所示:

Console.WriteLine("Hello, Simple Profiler!");

相关的 SimpleCLRProfiler 方法是:

HRESULT CSimpleProfiler::registerGlobalCallbacks() 
{
   HRESULT hr = profilerInfo3->SetEnterLeaveFunctionHooks3WithInfo(
      (FunctionEnter3WithInfo*)MethodEntered3, 
      (FunctionEnter3WithInfo*)MethodLeft3, 
      (FunctionEnter3WithInfo*)MethodTailcall3);

   if (FAILED(hr))
      Trace_f(L"Failed to register global callbacks (%s)", _com_error(hr).ErrorMessage());

   return S_OK;
}

void CSimpleProfiler::OnEnterWithInfo(FunctionID functionId, COR_PRF_ELT_INFO eltInfo)
{
    MethodInfo info;
   HRESULT hr = info.Create(profilerInfo3, functionId);
   if (FAILED(hr)) 
      Trace_f(L"Enter() failed to create MethodInfo object (%s)", _com_error(hr).ErrorMessage());

   Trace_f(L"[%p] [%d] Entering %s.%s", functionId, GetCurrentThreadId(), info.className.c_str(), info.methodName.c_str());
}

void CSimpleProfiler::OnLeaveWithInfo(FunctionID functionId, COR_PRF_ELT_INFO eltInfo)
{
   MethodInfo info;
   HRESULT hr = info.Create(profilerInfo3, functionId);
   if (FAILED(hr)) 
      Trace_f(L"Enter() failed to create MethodInfo object (%s)", _com_error(hr).ErrorMessage());

   Trace_f(L"[%p] [%d] Leaving %s.%s", functionId, GetCurrentThreadId(), info.className.c_str(), info.methodName.c_str());
}

有没有人知道为什么 .Net Profiler 不会对所有离开方法执行离开调用?顺便说一句,我检查过 OnLeaveMethod 不会由于异常等原因在任何跟踪之前意外退出。它没有。

谢谢,克里斯托夫

最佳答案

因为 stakx 似乎不会回到我的问题来提供官方答案(并获得荣誉)所以我会为他做: 正如 stakx 所暗示的那样,我没有记录尾调用。事实上,我什至没有意识到这个概念,所以我完全忽略了那个钩子(Hook)方法(它是有线的但空的)。我在这里找到了对尾调用的很好解释:David Broman's CLR Profiling API Blog: Enter, Leave, Tailcall Hooks Part 2: Tall tales of tail calls .

我引用上面的链接:

Tail calling is a compiler optimization that saves execution of instructions and saves reads and writes of stack memory. When the last thing a function does is call another function (and other conditions are favorable), the compiler may consider implementing that call as a tail call, instead of a regular call.

考虑这段代码:

static public void Main() {
    Helper();
}

static public void Helper() {
    One();
    Three();
}

static public void Three() {
    ...
}

当调用方法 Three 时,如果没有尾调用优化,堆栈将如下所示。

Three
Helper
Main

通过尾调用优化,堆栈看起来像这样:

Three
Main

因此在调用 Three 之前,由于优化,方法 Helper 已经从堆栈中弹出,结果堆栈上少了一个方法(less内存使用)以及一些执行和内存写入操作被保存。

关于c# - ICorProfilerCallback2 : CLR profiler does not log all Leave calls,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30950929/

相关文章:

.net - 类似于 .NET Framework 2.0 的 MEF(托管可扩展性框架)

c++ - Windows 控制台代码页 866

c# - 计算字符串中数组中字符的出现次数?

c# - 在 Visual Studio C# 中以 MS Access 数据库形式获取 OLE(位图)对象,我的代码有什么问题?

c# - 如何从 dll 中找到网站的根路径?

c# - MVC3 多 View 起始页

c# - NLog CPU 性能问题

c# - 将字节数组发送到 WCF 服务

c++ - 写入 .ini 文件 - SimpleIni SetValue 没有做任何事情,尽管看起来成功了

windows - 如何解决 TPrintDialog 不保存设置?