mem_windows.go 4.7 KB

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