github.com/isyscore/isc-gobase@v1.5.3-0.20231218061332-cbc7451899e9/system/mem/mem_windows.go (about)

     1  //go:build windows
     2  
     3  package mem
     4  
     5  import (
     6  	"context"
     7  	"github.com/isyscore/isc-gobase/system/common"
     8  	"golang.org/x/sys/windows"
     9  	"sync"
    10  	"syscall"
    11  	"unsafe"
    12  )
    13  
    14  var (
    15  	procEnumPageFilesW       = common.ModPsapi.NewProc("EnumPageFilesW")
    16  	procGetNativeSystemInfo  = common.Modkernel32.NewProc("GetNativeSystemInfo")
    17  	procGetPerformanceInfo   = common.ModPsapi.NewProc("GetPerformanceInfo")
    18  	procGlobalMemoryStatusEx = common.Modkernel32.NewProc("GlobalMemoryStatusEx")
    19  )
    20  
    21  type memoryStatusEx struct {
    22  	cbSize                  uint32
    23  	dwMemoryLoad            uint32
    24  	ullTotalPhys            uint64 // in bytes
    25  	ullAvailPhys            uint64
    26  	ullTotalPageFile        uint64
    27  	ullAvailPageFile        uint64
    28  	ullTotalVirtual         uint64
    29  	ullAvailVirtual         uint64
    30  	ullAvailExtendedVirtual uint64
    31  }
    32  
    33  func VirtualMemory() (*VirtualMemoryStat, error) {
    34  	return VirtualMemoryWithContext(context.Background())
    35  }
    36  
    37  func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
    38  	var memInfo memoryStatusEx
    39  	memInfo.cbSize = uint32(unsafe.Sizeof(memInfo))
    40  	mem, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memInfo)))
    41  	if mem == 0 {
    42  		return nil, windows.GetLastError()
    43  	}
    44  
    45  	ret := &VirtualMemoryStat{
    46  		Total:       memInfo.ullTotalPhys,
    47  		Available:   memInfo.ullAvailPhys,
    48  		Free:        memInfo.ullAvailPhys,
    49  		UsedPercent: float64(memInfo.dwMemoryLoad),
    50  	}
    51  
    52  	ret.Used = ret.Total - ret.Available
    53  	return ret, nil
    54  }
    55  
    56  type performanceInformation struct {
    57  	cb                uint32
    58  	commitTotal       uint64
    59  	commitLimit       uint64
    60  	commitPeak        uint64
    61  	physicalTotal     uint64
    62  	physicalAvailable uint64
    63  	systemCache       uint64
    64  	kernelTotal       uint64
    65  	kernelPaged       uint64
    66  	kernelNonpaged    uint64
    67  	pageSize          uint64
    68  	handleCount       uint32
    69  	processCount      uint32
    70  	threadCount       uint32
    71  }
    72  
    73  func SwapMemory() (*SwapMemoryStat, error) {
    74  	return SwapMemoryWithContext(context.Background())
    75  }
    76  
    77  func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
    78  	var perfInfo performanceInformation
    79  	perfInfo.cb = uint32(unsafe.Sizeof(perfInfo))
    80  	mem, _, _ := procGetPerformanceInfo.Call(uintptr(unsafe.Pointer(&perfInfo)), uintptr(perfInfo.cb))
    81  	if mem == 0 {
    82  		return nil, windows.GetLastError()
    83  	}
    84  	tot := perfInfo.commitLimit * perfInfo.pageSize
    85  	used := perfInfo.commitTotal * perfInfo.pageSize
    86  	free := tot - used
    87  	var usedPercent float64
    88  	if tot == 0 {
    89  		usedPercent = 0
    90  	} else {
    91  		usedPercent = float64(used) / float64(tot) * 100
    92  	}
    93  	ret := &SwapMemoryStat{
    94  		Total:       tot,
    95  		Used:        used,
    96  		Free:        free,
    97  		UsedPercent: usedPercent,
    98  	}
    99  
   100  	return ret, nil
   101  }
   102  
   103  var (
   104  	pageSize     uint64
   105  	pageSizeOnce sync.Once
   106  )
   107  
   108  type systemInfo struct {
   109  	wProcessorArchitecture      uint16
   110  	wReserved                   uint16
   111  	dwPageSize                  uint32
   112  	lpMinimumApplicationAddress uintptr
   113  	lpMaximumApplicationAddress uintptr
   114  	dwActiveProcessorMask       uintptr
   115  	dwNumberOfProcessors        uint32
   116  	dwProcessorType             uint32
   117  	dwAllocationGranularity     uint32
   118  	wProcessorLevel             uint16
   119  	wProcessorRevision          uint16
   120  }
   121  
   122  // system type as defined in https://docs.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-enum_page_file_information
   123  type enumPageFileInformation struct {
   124  	cb         uint32
   125  	reserved   uint32
   126  	totalSize  uint64
   127  	totalInUse uint64
   128  	peakUsage  uint64
   129  }
   130  
   131  func SwapDevices() ([]*SwapDevice, error) {
   132  	return SwapDevicesWithContext(context.Background())
   133  }
   134  
   135  func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
   136  	pageSizeOnce.Do(func() {
   137  		var sysInfo systemInfo
   138  		_, _, _ = procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&sysInfo)))
   139  		pageSize = uint64(sysInfo.dwPageSize)
   140  	})
   141  
   142  	// the following system call invokes the supplied callback function once for each page file before returning
   143  	// see https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumpagefilesw
   144  	var swapDevices []*SwapDevice
   145  	result, _, _ := procEnumPageFilesW.Call(windows.NewCallback(pEnumPageFileCallbackW), uintptr(unsafe.Pointer(&swapDevices)))
   146  	if result == 0 {
   147  		return nil, windows.GetLastError()
   148  	}
   149  
   150  	return swapDevices, nil
   151  }
   152  
   153  // system callback as defined in https://docs.microsoft.com/en-us/windows/win32/api/psapi/nc-psapi-penum_page_file_callbackw
   154  func pEnumPageFileCallbackW(swapDevices *[]*SwapDevice, enumPageFileInfo *enumPageFileInformation, lpFilenamePtr *[syscall.MAX_LONG_PATH]uint16) *bool {
   155  	*swapDevices = append(*swapDevices, &SwapDevice{
   156  		Name:      syscall.UTF16ToString((*lpFilenamePtr)[:]),
   157  		UsedBytes: enumPageFileInfo.totalInUse * pageSize,
   158  		FreeBytes: (enumPageFileInfo.totalSize - enumPageFileInfo.totalInUse) * pageSize,
   159  	})
   160  
   161  	// return true to continue enumerating page files
   162  	ret := true
   163  	return &ret
   164  }