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  }