mem_windows.go 4.7 KB

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