github.com/gofiber/fiber/v2@v2.47.0/internal/gopsutil/common/common_windows.go (about) 1 //go:build windows 2 // +build windows 3 4 package common 5 6 import ( 7 "context" 8 "fmt" 9 "path/filepath" 10 "strings" 11 "syscall" 12 "unsafe" 13 14 "github.com/gofiber/fiber/v2/internal/wmi" 15 "golang.org/x/sys/windows" 16 ) 17 18 // for double values 19 type PDH_FMT_COUNTERVALUE_DOUBLE struct { 20 CStatus uint32 21 DoubleValue float64 22 } 23 24 // for 64 bit integer values 25 type PDH_FMT_COUNTERVALUE_LARGE struct { 26 CStatus uint32 27 LargeValue int64 28 } 29 30 // for long values 31 type PDH_FMT_COUNTERVALUE_LONG struct { 32 CStatus uint32 33 LongValue int32 34 padding [4]byte 35 } 36 37 // windows system const 38 const ( 39 ERROR_SUCCESS = 0 40 ERROR_FILE_NOT_FOUND = 2 41 DRIVE_REMOVABLE = 2 42 DRIVE_FIXED = 3 43 HKEY_LOCAL_MACHINE = 0x80000002 44 RRF_RT_REG_SZ = 0x00000002 45 RRF_RT_REG_DWORD = 0x00000010 46 PDH_FMT_LONG = 0x00000100 47 PDH_FMT_DOUBLE = 0x00000200 48 PDH_FMT_LARGE = 0x00000400 49 PDH_INVALID_DATA = 0xc0000bc6 50 PDH_INVALID_HANDLE = 0xC0000bbc 51 PDH_NO_DATA = 0x800007d5 52 ) 53 54 const ( 55 ProcessBasicInformation = 0 56 ProcessWow64Information = 26 57 ) 58 59 var ( 60 Modkernel32 = windows.NewLazySystemDLL("kernel32.dll") 61 ModNt = windows.NewLazySystemDLL("ntdll.dll") 62 ModPdh = windows.NewLazySystemDLL("pdh.dll") 63 ModPsapi = windows.NewLazySystemDLL("psapi.dll") 64 65 ProcGetSystemTimes = Modkernel32.NewProc("GetSystemTimes") 66 ProcNtQuerySystemInformation = ModNt.NewProc("NtQuerySystemInformation") 67 ProcRtlGetNativeSystemInformation = ModNt.NewProc("RtlGetNativeSystemInformation") 68 ProcRtlNtStatusToDosError = ModNt.NewProc("RtlNtStatusToDosError") 69 ProcNtQueryInformationProcess = ModNt.NewProc("NtQueryInformationProcess") 70 ProcNtReadVirtualMemory = ModNt.NewProc("NtReadVirtualMemory") 71 ProcNtWow64QueryInformationProcess64 = ModNt.NewProc("NtWow64QueryInformationProcess64") 72 ProcNtWow64ReadVirtualMemory64 = ModNt.NewProc("NtWow64ReadVirtualMemory64") 73 74 PdhOpenQuery = ModPdh.NewProc("PdhOpenQuery") 75 PdhAddEnglishCounterW = ModPdh.NewProc("PdhAddEnglishCounterW") 76 PdhAddCounter = ModPdh.NewProc("PdhAddCounterW") 77 PdhCollectQueryData = ModPdh.NewProc("PdhCollectQueryData") 78 PdhGetFormattedCounterValue = ModPdh.NewProc("PdhGetFormattedCounterValue") 79 PdhCloseQuery = ModPdh.NewProc("PdhCloseQuery") 80 81 procQueryDosDeviceW = Modkernel32.NewProc("QueryDosDeviceW") 82 ) 83 84 type FILETIME struct { 85 DwLowDateTime uint32 86 DwHighDateTime uint32 87 } 88 89 // borrowed from net/interface_windows.go 90 func BytePtrToString(p *uint8) string { 91 a := (*[10000]uint8)(unsafe.Pointer(p)) 92 i := 0 93 for a[i] != 0 { 94 i++ 95 } 96 return string(a[:i]) 97 } 98 99 // CounterInfo 100 // copied from https://github.com/mackerelio/mackerel-agent/ 101 type CounterInfo struct { 102 PostName string 103 CounterName string 104 Counter windows.Handle 105 } 106 107 // CreateQuery XXX 108 // copied from https://github.com/mackerelio/mackerel-agent/ 109 func CreateQuery() (windows.Handle, error) { 110 var query windows.Handle 111 r, _, err := PdhOpenQuery.Call(0, 0, uintptr(unsafe.Pointer(&query))) 112 if r != 0 { 113 return 0, err 114 } 115 return query, nil 116 } 117 118 // CreateCounter XXX 119 func CreateCounter(query windows.Handle, pname, cname string) (*CounterInfo, error) { 120 var counter windows.Handle 121 r, _, err := PdhAddCounter.Call( 122 uintptr(query), 123 uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(cname))), 124 0, 125 uintptr(unsafe.Pointer(&counter))) 126 if r != 0 { 127 return nil, err 128 } 129 return &CounterInfo{ 130 PostName: pname, 131 CounterName: cname, 132 Counter: counter, 133 }, nil 134 } 135 136 // GetCounterValue get counter value from handle 137 // adapted from https://github.com/mackerelio/mackerel-agent/ 138 func GetCounterValue(counter windows.Handle) (float64, error) { 139 var value PDH_FMT_COUNTERVALUE_DOUBLE 140 r, _, err := PdhGetFormattedCounterValue.Call(uintptr(counter), PDH_FMT_DOUBLE, uintptr(0), uintptr(unsafe.Pointer(&value))) 141 if r != 0 && r != PDH_INVALID_DATA { 142 return 0.0, err 143 } 144 return value.DoubleValue, nil 145 } 146 147 type Win32PerformanceCounter struct { 148 PostName string 149 CounterName string 150 Query windows.Handle 151 Counter windows.Handle 152 } 153 154 func NewWin32PerformanceCounter(postName, counterName string) (*Win32PerformanceCounter, error) { 155 query, err := CreateQuery() 156 if err != nil { 157 return nil, err 158 } 159 var counter = Win32PerformanceCounter{ 160 Query: query, 161 PostName: postName, 162 CounterName: counterName, 163 } 164 r, _, err := PdhAddEnglishCounterW.Call( 165 uintptr(counter.Query), 166 uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(counter.CounterName))), 167 0, 168 uintptr(unsafe.Pointer(&counter.Counter)), 169 ) 170 if r != 0 { 171 return nil, err 172 } 173 return &counter, nil 174 } 175 176 func (w *Win32PerformanceCounter) GetValue() (float64, error) { 177 r, _, err := PdhCollectQueryData.Call(uintptr(w.Query)) 178 if r != 0 && err != nil { 179 if r == PDH_NO_DATA { 180 return 0.0, fmt.Errorf("%w: this counter has not data", err) 181 } 182 return 0.0, err 183 } 184 185 return GetCounterValue(w.Counter) 186 } 187 188 func ProcessorQueueLengthCounter() (*Win32PerformanceCounter, error) { 189 return NewWin32PerformanceCounter("processor_queue_length", `\System\Processor Queue Length`) 190 } 191 192 // WMIQueryWithContext - wraps wmi.Query with a timed-out context to avoid hanging 193 func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, connectServerArgs ...interface{}) error { 194 if _, ok := ctx.Deadline(); !ok { 195 ctxTimeout, cancel := context.WithTimeout(ctx, Timeout) 196 defer cancel() 197 ctx = ctxTimeout 198 } 199 200 errChan := make(chan error, 1) 201 go func() { 202 errChan <- wmi.Query(query, dst, connectServerArgs...) 203 }() 204 205 select { 206 case <-ctx.Done(): 207 return ctx.Err() 208 case err := <-errChan: 209 return err 210 } 211 } 212 213 // Convert paths using native DOS format like: 214 // 215 // "\Device\HarddiskVolume1\Windows\systemew\file.txt" 216 // 217 // into: 218 // 219 // "C:\Windows\systemew\file.txt" 220 func ConvertDOSPath(p string) string { 221 rawDrive := strings.Join(strings.Split(p, `\`)[:3], `\`) 222 223 for d := 'A'; d <= 'Z'; d++ { 224 szDeviceName := string(d) + ":" 225 szTarget := make([]uint16, 512) 226 ret, _, _ := procQueryDosDeviceW.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(szDeviceName))), 227 uintptr(unsafe.Pointer(&szTarget[0])), 228 uintptr(len(szTarget))) 229 if ret != 0 && windows.UTF16ToString(szTarget[:]) == rawDrive { 230 return filepath.Join(szDeviceName, p[len(rawDrive):]) 231 } 232 } 233 return p 234 }