测量过程峰值内存使用情况

时间:2013-03-22 15:55:13

标签: winapi

我正在开发一个基准测试工具,除其他外,它还测量执行操作的外部进程使用的时间和内存。我最感兴趣的是峰值可分页内存大小(a.k.a. PageFileBytesPeak性能计数器/ Process.PeakPagedMemorySize64 /峰值专用字节)。这是一个.NET项目,因此最好使用纯.NET解决方案,但这很可能是不可能的。

这里的问题是我不知道进程退出之前的峰值内存使用情况。当它不再存在时,我无法读取该过程的性能计数器。所以我可以在进程运行时轮询它。

然而,这不是优选的,因为我经常轮询我将干扰完成其工作所需的时间,如果轮询太少,结果将不准确(该过程很可能会触及它退出之前的峰值内存使用率)。所以我希望有一些方法能够可靠地做到这一点,这比我到目前为止提出的解决方案要少得多:

  1. 将DLL注入进程,通过DLL_PROCESS_DETACH上的IPC机制报告值。
  2. 目标进程中的Patch / Hook ExitProcess,在执行真正的ExitProcess之前通过IPC机制报告值。
  3. 假装是一个调试器,在EXIT_PROCESS_DEBUG_EVENT上测量值(在调用ContinueDebugEvent之前,内核不会清理该进程)。

2 个答案:

答案 0 :(得分:1)

读取现存的PerfMon计数器应该是一个非常低的开销操作,尤其是。对于你想要使用的系统计数器,因为计数器通常(可能总是?不确定)使用共享内存块(映射文件)实现。

我将使用运行时可配置的时间间隔实现轮询,如果您发现这会严重影响您的应用程序,则只采用更复杂的技术。如果你想首先检查一下,请设置PerfMon以监控感兴趣的计数器,看看在可用的刷新间隔运行时是否会杀死你的应用程序。

答案 1 :(得分:1)

事实证明,只要你有一个活动句柄,GetProcessMemoryInfo即使在进程退出后也能正常工作。如果您碰巧需要,虚拟内存使用情况不可用。

唯一需要注意的是,值的大小取决于调用它的进程的位数,因此如果32位进程测量64位进程的内存使用情况,则值可能会溢出。

示例:

[DllImport("psapi.dll", SetLastError=true)]
static extern bool GetProcessMemoryInfo(IntPtr hProcess, out PROCESS_MEMORY_COUNTERS counters, int size);

[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_MEMORY_COUNTERS
{
    public uint cb;
    public uint PageFaultCount;
    public UIntPtr PeakWorkingSetSize;
    public UIntPtr WorkingSetSize;
    public UIntPtr QuotaPeakPagedPoolUsage;
    public UIntPtr QuotaPagedPoolUsage;
    public UIntPtr QuotaPeakNonPagedPoolUsage;
    public UIntPtr QuotaNonPagedPoolUsage;
    public UIntPtr PagefileUsage;
    public UIntPtr PeakPagefileUsage;
}

public long BenchmarkProcessMemoryUsage(string fileName, string arguments)
{
    ProcessStartInfo startInfo = new ProcessStartInfo(fileName, arguments);
    startInfo.UseShellExecute = false;
    Process process = Process.Start();
    process.WaitForExit();
    PROCESS_MEMORY_COUNTERS counters;
    if (!GetProcessMemoryInfo(process.Handle, out counters, Marshal.SizeOf(typeof(PROCESS_MEMORY_COUNTERS))))
        throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
    return (long)counters.PeakPagefileUsage;
}