From cc44183787adde8b83f9373dd53250f154058dc4 Mon Sep 17 00:00:00 2001 From: xEgoist Date: Tue, 21 Mar 2023 09:03:50 -0500 Subject: [PATCH 1/4] Implemented getMaxRss for Windows In Windows, the equivalent to maxrss is PeakWorkingSetSize which is found in PROCESS_MEMORY_COUNTERS in bytes. Currently, this is done by calling `GetProcessMemoryInfo` in kernel32. --- lib/std/child_process.zig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 3748ca687768..72d7be4451da 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -99,12 +99,20 @@ pub const ChildProcess = struct { return null; } }, + .windows => { + if (rus.rusage) |ru| { + return ru.PeakWorkingSetSize; + } else { + return null; + } + }, else => return null, } } const rusage_init = switch (builtin.os.tag) { .linux => @as(?std.os.rusage, null), + .windows => @as(?windows.PROCESS_MEMORY_COUNTERS, null), else => {}, }; }; @@ -364,6 +372,13 @@ pub const ChildProcess = struct { } }); + if (self.request_resource_usage_statistics) { + var pmc: windows.PROCESS_MEMORY_COUNTERS = undefined; + if (windows.kernel32.K32GetProcessMemoryInfo(self.id, &pmc, @sizeOf(windows.PROCESS_MEMORY_COUNTERS)) != 0) { + self.resource_usage_statistics.rusage = pmc; + } + } + os.close(self.id); os.close(self.thread_handle); self.cleanupStreams(); From 70469d428dd94693295b320a975b1ddf904dda3b Mon Sep 17 00:00:00 2001 From: xEgoist Date: Wed, 22 Mar 2023 05:57:14 -0500 Subject: [PATCH 2/4] Implemented Zig wrapper for `GetProcessMemoryInfo` `GetProcessMemoryInfo` is implemented using `NtQueryInformationProcess` with `ProcessVmCounters` to obtain `VM_COUNTERS`. The structs, enum definitions are found in `winternl.h` or `ntddk.h` in the latest WDK. This should give the same results as using `K32GetProcessMemoryInfo` --- lib/std/child_process.zig | 6 ++-- lib/std/os/windows.zig | 45 ++++++++++++++++++++++++++ lib/std/os/windows/ntdll.zig | 63 ++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 3 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 72d7be4451da..13fd40d982c2 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -137,6 +137,7 @@ pub const ChildProcess = struct { os.SetIdError || os.ChangeCurDirError || windows.CreateProcessError || + windows.GetProcessMemoryInfoError || windows.WaitForSingleObjectError; pub const Term = union(enum) { @@ -374,9 +375,8 @@ pub const ChildProcess = struct { if (self.request_resource_usage_statistics) { var pmc: windows.PROCESS_MEMORY_COUNTERS = undefined; - if (windows.kernel32.K32GetProcessMemoryInfo(self.id, &pmc, @sizeOf(windows.PROCESS_MEMORY_COUNTERS)) != 0) { - self.resource_usage_statistics.rusage = pmc; - } + try windows.GetProcessMemoryInfo(self.id, &pmc); + self.resource_usage_statistics.rusage = pmc; } os.close(self.id); diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index b385f681949d..27f2ddb31606 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -3947,6 +3947,20 @@ pub const PSAPI_WS_WATCH_INFORMATION = extern struct { FaultingVa: LPVOID, }; +pub const VM_COUNTERS = extern struct { + PeakVirtualSize: SIZE_T, + VirtualSize: SIZE_T, + PageFaultCount: ULONG, + PeakWorkingSetSize: SIZE_T, + WorkingSetSize: SIZE_T, + QuotaPeakPagedPoolUsage: SIZE_T, + QuotaPagedPoolUsage: SIZE_T, + QuotaPeakNonPagedPoolUsage: SIZE_T, + QuotaNonPagedPoolUsage: SIZE_T, + PagefileUsage: SIZE_T, + PeakPagefileUsage: SIZE_T, +}; + pub const PROCESS_MEMORY_COUNTERS = extern struct { cb: DWORD, PageFaultCount: DWORD, @@ -3974,6 +3988,37 @@ pub const PROCESS_MEMORY_COUNTERS_EX = extern struct { PrivateUsage: SIZE_T, }; +pub const GetProcessMemoryInfoError = error{ + AccessDenied, + InvalidHandle, + Unexpected, +}; + +pub fn GetProcessMemoryInfo(hProcess: HANDLE, out: *PROCESS_MEMORY_COUNTERS) GetProcessMemoryInfoError!void { + var vmc: VM_COUNTERS = undefined; + const rc = ntdll.NtQueryInformationProcess(hProcess, .ProcessVmCounters, &vmc, @sizeOf(VM_COUNTERS), null); + switch (rc) { + .SUCCESS => { + out.* = PROCESS_MEMORY_COUNTERS{ + .cb = @sizeOf(PROCESS_MEMORY_COUNTERS), + .PageFaultCount = vmc.PageFaultCount, + .PeakWorkingSetSize = vmc.PeakWorkingSetSize, + .WorkingSetSize = vmc.WorkingSetSize, + .QuotaPeakPagedPoolUsage = vmc.QuotaPeakPagedPoolUsage, + .QuotaPagedPoolUsage = vmc.QuotaPagedPoolUsage, + .QuotaPeakNonPagedPoolUsage = vmc.QuotaPeakNonPagedPoolUsage, + .QuotaNonPagedPoolUsage = vmc.QuotaNonPagedPoolUsage, + .PagefileUsage = vmc.PagefileUsage, + .PeakPagefileUsage = vmc.PeakPagefileUsage, + }; + }, + .ACCESS_DENIED => return error.AccessDenied, + .INVALID_HANDLE => return error.InvalidHandle, + .INVALID_PARAMETER => unreachable, + else => return unexpectedStatus(rc), + } +} + pub const PERFORMANCE_INFORMATION = extern struct { cb: DWORD, CommitTotal: SIZE_T, diff --git a/lib/std/os/windows/ntdll.zig b/lib/std/os/windows/ntdll.zig index 58cba356a23a..3916e7d17899 100644 --- a/lib/std/os/windows/ntdll.zig +++ b/lib/std/os/windows/ntdll.zig @@ -32,6 +32,69 @@ const RUNTIME_FUNCTION = windows.RUNTIME_FUNCTION; const KNONVOLATILE_CONTEXT_POINTERS = windows.KNONVOLATILE_CONTEXT_POINTERS; const EXCEPTION_ROUTINE = windows.EXCEPTION_ROUTINE; +pub const PROCESSINFOCLASS = enum(c_int) { + ProcessBasicInformation, + ProcessQuotaLimits, + ProcessIoCounters, + ProcessVmCounters, + ProcessTimes, + ProcessBasePriority, + ProcessRaisePriority, + ProcessDebugPort, + ProcessExceptionPort, + ProcessAccessToken, + ProcessLdtInformation, + ProcessLdtSize, + ProcessDefaultHardErrorMode, + ProcessIoPortHandlers, + ProcessPooledUsageAndLimits, + ProcessWorkingSetWatch, + ProcessUserModeIOPL, + ProcessEnableAlignmentFaultFixup, + ProcessPriorityClass, + ProcessWx86Information, + ProcessHandleCount, + ProcessAffinityMask, + ProcessPriorityBoost, + ProcessDeviceMap, + ProcessSessionInformation, + ProcessForegroundInformation, + ProcessWow64Information, + ProcessImageFileName, + ProcessLUIDDeviceMapsEnabled, + ProcessBreakOnTermination, + ProcessDebugObjectHandle, + ProcessDebugFlags, + ProcessHandleTracing, + ProcessIoPriority, + ProcessExecuteFlags, + ProcessTlsInformation, + ProcessCookie, + ProcessImageInformation, + ProcessCycleTime, + ProcessPagePriority, + ProcessInstrumentationCallback, + ProcessThreadStackAllocation, + ProcessWorkingSetWatchEx, + ProcessImageFileNameWin32, + ProcessImageFileMapping, + ProcessAffinityUpdateMode, + ProcessMemoryAllocationMode, + ProcessGroupInformation, + ProcessTokenVirtualizationEnabled, + ProcessConsoleHostProcess, + ProcessWindowInformation, + MaxProcessInfoClass +}; + +pub extern "ntdll" fn NtQueryInformationProcess( + ProcessHandle: HANDLE, + ProcessInformationClass: PROCESSINFOCLASS, + ProcessInformation: *anyopaque, + ProcessInformationLength: ULONG, + ReturnLength: ?*ULONG, +) callconv(WINAPI) NTSTATUS; + pub const THREADINFOCLASS = enum(c_int) { ThreadBasicInformation, ThreadTimes, From 2f5af6c9721a125f3308b55f5a8059d62e5d2ec7 Mon Sep 17 00:00:00 2001 From: xEgoist Date: Thu, 23 Mar 2023 06:13:26 -0500 Subject: [PATCH 3/4] Refactored GetProcessMemoryInfo to return `VM_COUNTERS` This change allows the function to return the process memory info directly instead of copying the result of the underlying Nt function. --- lib/std/child_process.zig | 6 ++---- lib/std/os/windows.zig | 17 ++--------------- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 13fd40d982c2..ca447b068df7 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -112,7 +112,7 @@ pub const ChildProcess = struct { const rusage_init = switch (builtin.os.tag) { .linux => @as(?std.os.rusage, null), - .windows => @as(?windows.PROCESS_MEMORY_COUNTERS, null), + .windows => @as(?windows.VM_COUNTERS, null), else => {}, }; }; @@ -374,9 +374,7 @@ pub const ChildProcess = struct { }); if (self.request_resource_usage_statistics) { - var pmc: windows.PROCESS_MEMORY_COUNTERS = undefined; - try windows.GetProcessMemoryInfo(self.id, &pmc); - self.resource_usage_statistics.rusage = pmc; + self.resource_usage_statistics.rusage = try windows.GetProcessMemoryInfo(self.id); } os.close(self.id); diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 27f2ddb31606..fe0a68a13a06 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -3994,24 +3994,11 @@ pub const GetProcessMemoryInfoError = error{ Unexpected, }; -pub fn GetProcessMemoryInfo(hProcess: HANDLE, out: *PROCESS_MEMORY_COUNTERS) GetProcessMemoryInfoError!void { +pub fn GetProcessMemoryInfo(hProcess: HANDLE) GetProcessMemoryInfoError!VM_COUNTERS { var vmc: VM_COUNTERS = undefined; const rc = ntdll.NtQueryInformationProcess(hProcess, .ProcessVmCounters, &vmc, @sizeOf(VM_COUNTERS), null); switch (rc) { - .SUCCESS => { - out.* = PROCESS_MEMORY_COUNTERS{ - .cb = @sizeOf(PROCESS_MEMORY_COUNTERS), - .PageFaultCount = vmc.PageFaultCount, - .PeakWorkingSetSize = vmc.PeakWorkingSetSize, - .WorkingSetSize = vmc.WorkingSetSize, - .QuotaPeakPagedPoolUsage = vmc.QuotaPeakPagedPoolUsage, - .QuotaPagedPoolUsage = vmc.QuotaPagedPoolUsage, - .QuotaPeakNonPagedPoolUsage = vmc.QuotaPeakNonPagedPoolUsage, - .QuotaNonPagedPoolUsage = vmc.QuotaNonPagedPoolUsage, - .PagefileUsage = vmc.PagefileUsage, - .PeakPagefileUsage = vmc.PeakPagefileUsage, - }; - }, + .SUCCESS => return vmc, .ACCESS_DENIED => return error.AccessDenied, .INVALID_HANDLE => return error.InvalidHandle, .INVALID_PARAMETER => unreachable, From 8f4548dd69de3a1c1f5c14cdf6b2e7052ea5079f Mon Sep 17 00:00:00 2001 From: xEgoist Date: Thu, 23 Mar 2023 06:17:05 -0500 Subject: [PATCH 4/4] fmt: lib/std/os/windows/ntdll.zig --- lib/std/os/windows/ntdll.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/os/windows/ntdll.zig b/lib/std/os/windows/ntdll.zig index 3916e7d17899..429b36039e5d 100644 --- a/lib/std/os/windows/ntdll.zig +++ b/lib/std/os/windows/ntdll.zig @@ -84,7 +84,7 @@ pub const PROCESSINFOCLASS = enum(c_int) { ProcessTokenVirtualizationEnabled, ProcessConsoleHostProcess, ProcessWindowInformation, - MaxProcessInfoClass + MaxProcessInfoClass, }; pub extern "ntdll" fn NtQueryInformationProcess(