// +build windows package mem import ( "context" "sync" "syscall" "unsafe" "github.com/shirou/gopsutil/v3/internal/common" "golang.org/x/sys/windows" ) var ( procEnumPageFilesW = common.ModPsapi.NewProc("EnumPageFilesW") procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo") procGetPerformanceInfo = common.ModPsapi.NewProc("GetPerformanceInfo") procGlobalMemoryStatusEx = common.Modkernel32.NewProc("GlobalMemoryStatusEx") ) type memoryStatusEx struct { cbSize uint32 dwMemoryLoad uint32 ullTotalPhys uint64 // in bytes ullAvailPhys uint64 ullTotalPageFile uint64 ullAvailPageFile uint64 ullTotalVirtual uint64 ullAvailVirtual uint64 ullAvailExtendedVirtual uint64 } func VirtualMemory() (*VirtualMemoryStat, error) { return VirtualMemoryWithContext(context.Background()) } func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { var memInfo memoryStatusEx memInfo.cbSize = uint32(unsafe.Sizeof(memInfo)) mem, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memInfo))) if mem == 0 { return nil, windows.GetLastError() } ret := &VirtualMemoryStat{ Total: memInfo.ullTotalPhys, Available: memInfo.ullAvailPhys, Free: memInfo.ullAvailPhys, UsedPercent: float64(memInfo.dwMemoryLoad), } ret.Used = ret.Total - ret.Available return ret, nil } type performanceInformation struct { cb uint32 commitTotal uint64 commitLimit uint64 commitPeak uint64 physicalTotal uint64 physicalAvailable uint64 systemCache uint64 kernelTotal uint64 kernelPaged uint64 kernelNonpaged uint64 pageSize uint64 handleCount uint32 processCount uint32 threadCount uint32 } func SwapMemory() (*SwapMemoryStat, error) { return SwapMemoryWithContext(context.Background()) } func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { var perfInfo performanceInformation perfInfo.cb = uint32(unsafe.Sizeof(perfInfo)) mem, _, _ := procGetPerformanceInfo.Call(uintptr(unsafe.Pointer(&perfInfo)), uintptr(perfInfo.cb)) if mem == 0 { return nil, windows.GetLastError() } tot := perfInfo.commitLimit * perfInfo.pageSize used := perfInfo.commitTotal * perfInfo.pageSize free := tot - used var usedPercent float64 if tot == 0 { usedPercent = 0 } else { usedPercent = float64(used) / float64(tot) * 100 } ret := &SwapMemoryStat{ Total: tot, Used: used, Free: free, UsedPercent: usedPercent, } return ret, nil } var ( pageSize uint64 pageSizeOnce sync.Once ) type systemInfo struct { wProcessorArchitecture uint16 wReserved uint16 dwPageSize uint32 lpMinimumApplicationAddress uintptr lpMaximumApplicationAddress uintptr dwActiveProcessorMask uintptr dwNumberOfProcessors uint32 dwProcessorType uint32 dwAllocationGranularity uint32 wProcessorLevel uint16 wProcessorRevision uint16 } // system type as defined in https://docs.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-enum_page_file_information type enumPageFileInformation struct { cb uint32 reserved uint32 totalSize uint64 totalInUse uint64 peakUsage uint64 } func SwapDevices() ([]*SwapDevice, error) { return SwapDevicesWithContext(context.Background()) } func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) { pageSizeOnce.Do(func() { var sysInfo systemInfo procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&sysInfo))) pageSize = uint64(sysInfo.dwPageSize) }) // the following system call invokes the supplied callback function once for each page file before returning // see https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumpagefilesw var swapDevices []*SwapDevice result, _, _ := procEnumPageFilesW.Call(windows.NewCallback(pEnumPageFileCallbackW), uintptr(unsafe.Pointer(&swapDevices))) if result == 0 { return nil, windows.GetLastError() } return swapDevices, nil } // system callback as defined in https://docs.microsoft.com/en-us/windows/win32/api/psapi/nc-psapi-penum_page_file_callbackw func pEnumPageFileCallbackW(swapDevices *[]*SwapDevice, enumPageFileInfo *enumPageFileInformation, lpFilenamePtr *[syscall.MAX_LONG_PATH]uint16) *bool { *swapDevices = append(*swapDevices, &SwapDevice{ Name: syscall.UTF16ToString((*lpFilenamePtr)[:]), UsedBytes: enumPageFileInfo.totalInUse * pageSize, FreeBytes: (enumPageFileInfo.totalSize - enumPageFileInfo.totalInUse) * pageSize, }) // return true to continue enumerating page files ret := true return &ret }