Commit 70469d428d

xEgoist <egoist@egoistic.dev>
2023-03-22 11:57:14
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`
1 parent cc44183
Changed files (3)
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,
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,
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);