2014-04-19 00:09:25 +09:00
|
|
|
// +build windows
|
|
|
|
|
2014-12-30 22:09:05 +09:00
|
|
|
package common
|
2014-04-19 00:09:25 +09:00
|
|
|
|
|
|
|
import (
|
2017-11-09 15:44:26 -08:00
|
|
|
"context"
|
2020-10-22 17:13:43 +03:00
|
|
|
"fmt"
|
2019-06-16 22:59:03 +02:00
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
"syscall"
|
2014-05-16 11:33:35 +09:00
|
|
|
"unsafe"
|
2017-06-02 13:51:00 -07:00
|
|
|
|
2017-11-09 15:44:26 -08:00
|
|
|
"github.com/StackExchange/wmi"
|
2017-06-02 13:51:00 -07:00
|
|
|
"golang.org/x/sys/windows"
|
2014-04-19 00:09:25 +09:00
|
|
|
)
|
|
|
|
|
2015-02-19 23:52:06 +09:00
|
|
|
// for double values
|
|
|
|
type PDH_FMT_COUNTERVALUE_DOUBLE struct {
|
|
|
|
CStatus uint32
|
|
|
|
DoubleValue float64
|
|
|
|
}
|
|
|
|
|
|
|
|
// for 64 bit integer values
|
|
|
|
type PDH_FMT_COUNTERVALUE_LARGE struct {
|
|
|
|
CStatus uint32
|
|
|
|
LargeValue int64
|
|
|
|
}
|
|
|
|
|
|
|
|
// for long values
|
|
|
|
type PDH_FMT_COUNTERVALUE_LONG struct {
|
|
|
|
CStatus uint32
|
|
|
|
LongValue int32
|
|
|
|
padding [4]byte
|
|
|
|
}
|
|
|
|
|
|
|
|
// windows system const
|
|
|
|
const (
|
|
|
|
ERROR_SUCCESS = 0
|
|
|
|
ERROR_FILE_NOT_FOUND = 2
|
|
|
|
DRIVE_REMOVABLE = 2
|
|
|
|
DRIVE_FIXED = 3
|
|
|
|
HKEY_LOCAL_MACHINE = 0x80000002
|
|
|
|
RRF_RT_REG_SZ = 0x00000002
|
|
|
|
RRF_RT_REG_DWORD = 0x00000010
|
|
|
|
PDH_FMT_LONG = 0x00000100
|
|
|
|
PDH_FMT_DOUBLE = 0x00000200
|
|
|
|
PDH_FMT_LARGE = 0x00000400
|
|
|
|
PDH_INVALID_DATA = 0xc0000bc6
|
|
|
|
PDH_INVALID_HANDLE = 0xC0000bbc
|
|
|
|
PDH_NO_DATA = 0x800007d5
|
|
|
|
)
|
|
|
|
|
2020-04-30 22:44:43 -03:00
|
|
|
const (
|
|
|
|
ProcessBasicInformation = 0
|
|
|
|
ProcessWow64Information = 26
|
|
|
|
)
|
|
|
|
|
2014-04-20 01:53:00 +09:00
|
|
|
var (
|
2018-08-18 22:47:25 +02:00
|
|
|
Modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
|
|
|
ModNt = windows.NewLazySystemDLL("ntdll.dll")
|
|
|
|
ModPdh = windows.NewLazySystemDLL("pdh.dll")
|
|
|
|
ModPsapi = windows.NewLazySystemDLL("psapi.dll")
|
2017-11-09 15:44:26 -08:00
|
|
|
|
2020-04-30 22:44:43 -03:00
|
|
|
ProcGetSystemTimes = Modkernel32.NewProc("GetSystemTimes")
|
|
|
|
ProcNtQuerySystemInformation = ModNt.NewProc("NtQuerySystemInformation")
|
|
|
|
ProcRtlGetNativeSystemInformation = ModNt.NewProc("RtlGetNativeSystemInformation")
|
|
|
|
ProcRtlNtStatusToDosError = ModNt.NewProc("RtlNtStatusToDosError")
|
|
|
|
ProcNtQueryInformationProcess = ModNt.NewProc("NtQueryInformationProcess")
|
|
|
|
ProcNtReadVirtualMemory = ModNt.NewProc("NtReadVirtualMemory")
|
|
|
|
ProcNtWow64QueryInformationProcess64 = ModNt.NewProc("NtWow64QueryInformationProcess64")
|
|
|
|
ProcNtWow64ReadVirtualMemory64 = ModNt.NewProc("NtWow64ReadVirtualMemory64")
|
|
|
|
|
2020-10-22 17:13:43 +03:00
|
|
|
PdhOpenQuery = ModPdh.NewProc("PdhOpenQuery")
|
|
|
|
PdhAddCounter = ModPdh.NewProc("PdhAddCounterW")
|
|
|
|
PdhCollectQueryData = ModPdh.NewProc("PdhCollectQueryData")
|
|
|
|
PdhGetFormattedCounterValue = ModPdh.NewProc("PdhGetFormattedCounterValue")
|
|
|
|
PdhCloseQuery = ModPdh.NewProc("PdhCloseQuery")
|
2019-06-16 22:59:03 +02:00
|
|
|
|
2020-10-22 17:13:43 +03:00
|
|
|
procQueryDosDeviceW = Modkernel32.NewProc("QueryDosDeviceW")
|
2014-04-20 01:53:00 +09:00
|
|
|
)
|
2014-04-22 22:10:13 +09:00
|
|
|
|
|
|
|
type FILETIME struct {
|
|
|
|
DwLowDateTime uint32
|
|
|
|
DwHighDateTime uint32
|
|
|
|
}
|
2014-05-16 11:33:35 +09:00
|
|
|
|
|
|
|
// borrowed from net/interface_windows.go
|
2014-11-27 10:18:15 +09:00
|
|
|
func BytePtrToString(p *uint8) string {
|
2014-05-16 11:33:35 +09:00
|
|
|
a := (*[10000]uint8)(unsafe.Pointer(p))
|
|
|
|
i := 0
|
|
|
|
for a[i] != 0 {
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
return string(a[:i])
|
|
|
|
}
|
2015-02-15 21:25:33 +09:00
|
|
|
|
2020-10-22 17:13:43 +03:00
|
|
|
// CounterInfo XXX
|
2015-02-19 23:52:06 +09:00
|
|
|
// copied from https://github.com/mackerelio/mackerel-agent/
|
|
|
|
type CounterInfo struct {
|
|
|
|
PostName string
|
|
|
|
CounterName string
|
2017-06-02 13:51:00 -07:00
|
|
|
Counter windows.Handle
|
2015-02-19 23:52:06 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
// CreateQuery XXX
|
|
|
|
// copied from https://github.com/mackerelio/mackerel-agent/
|
2017-06-02 13:51:00 -07:00
|
|
|
func CreateQuery() (windows.Handle, error) {
|
|
|
|
var query windows.Handle
|
2015-02-19 23:52:06 +09:00
|
|
|
r, _, err := PdhOpenQuery.Call(0, 0, uintptr(unsafe.Pointer(&query)))
|
|
|
|
if r != 0 {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return query, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateCounter XXX
|
2017-06-02 13:51:00 -07:00
|
|
|
func CreateCounter(query windows.Handle, pname, cname string) (*CounterInfo, error) {
|
|
|
|
var counter windows.Handle
|
2015-02-19 23:52:06 +09:00
|
|
|
r, _, err := PdhAddCounter.Call(
|
|
|
|
uintptr(query),
|
2017-06-02 13:51:00 -07:00
|
|
|
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(cname))),
|
2015-02-19 23:52:06 +09:00
|
|
|
0,
|
|
|
|
uintptr(unsafe.Pointer(&counter)))
|
|
|
|
if r != 0 {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &CounterInfo{
|
|
|
|
PostName: pname,
|
|
|
|
CounterName: cname,
|
|
|
|
Counter: counter,
|
|
|
|
}, nil
|
|
|
|
}
|
2017-11-09 15:44:26 -08:00
|
|
|
|
2020-10-22 17:13:43 +03:00
|
|
|
// GetCounterValue get counter value from handle
|
|
|
|
// adapted from https://github.com/mackerelio/mackerel-agent/
|
|
|
|
func GetCounterValue(counter windows.Handle) (float64, error) {
|
|
|
|
var value PDH_FMT_COUNTERVALUE_DOUBLE
|
|
|
|
r, _, err := PdhGetFormattedCounterValue.Call(uintptr(counter), PDH_FMT_DOUBLE, uintptr(0), uintptr(unsafe.Pointer(&value)))
|
|
|
|
if r != 0 && r != PDH_INVALID_DATA {
|
|
|
|
return 0.0, err
|
|
|
|
}
|
|
|
|
return value.DoubleValue, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ProcessorQueueLengthGenerator is struct of windows api
|
|
|
|
// adapted from https://github.com/mackerelio/mackerel-agent/
|
|
|
|
type ProcessorQueueLengthGenerator struct {
|
|
|
|
query windows.Handle
|
|
|
|
counter *CounterInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewProcessorQueueLengthGenerator is set up windows api
|
|
|
|
// adapted from https://github.com/mackerelio/mackerel-agent/
|
|
|
|
func NewProcessorQueueLengthGenerator() (*ProcessorQueueLengthGenerator, error) {
|
|
|
|
g := &ProcessorQueueLengthGenerator{0, nil}
|
|
|
|
|
|
|
|
var err error
|
|
|
|
g.query, err = CreateQuery()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
counter, err := CreateCounter(g.query, "processor_queue_length", `\System\Processor Queue Length`)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
g.counter = counter
|
|
|
|
return g, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate XXX
|
|
|
|
// adapted from https://github.com/mackerelio/mackerel-agent/
|
|
|
|
func (g *ProcessorQueueLengthGenerator) Generate() (float64, error) {
|
|
|
|
r, _, err := PdhCollectQueryData.Call(uintptr(g.query))
|
|
|
|
if r != 0 && err != nil {
|
|
|
|
if r == PDH_NO_DATA {
|
|
|
|
return 0.0, fmt.Errorf("%w: this metric has not data", err)
|
|
|
|
}
|
|
|
|
return 0.0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return GetCounterValue(g.counter.Counter)
|
|
|
|
}
|
|
|
|
|
2017-11-09 15:44:26 -08:00
|
|
|
// WMIQueryWithContext - wraps wmi.Query with a timed-out context to avoid hanging
|
|
|
|
func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, connectServerArgs ...interface{}) error {
|
2018-02-12 13:24:40 -08:00
|
|
|
if _, ok := ctx.Deadline(); !ok {
|
|
|
|
ctxTimeout, cancel := context.WithTimeout(ctx, Timeout)
|
|
|
|
defer cancel()
|
|
|
|
ctx = ctxTimeout
|
|
|
|
}
|
|
|
|
|
2017-11-09 15:44:26 -08:00
|
|
|
errChan := make(chan error, 1)
|
|
|
|
go func() {
|
|
|
|
errChan <- wmi.Query(query, dst, connectServerArgs...)
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return ctx.Err()
|
|
|
|
case err := <-errChan:
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2019-06-16 22:59:03 +02:00
|
|
|
|
|
|
|
// Convert paths using native DOS format like:
|
|
|
|
// "\Device\HarddiskVolume1\Windows\systemew\file.txt"
|
|
|
|
// into:
|
|
|
|
// "C:\Windows\systemew\file.txt"
|
|
|
|
func ConvertDOSPath(p string) string {
|
|
|
|
rawDrive := strings.Join(strings.Split(p, `\`)[:3], `\`)
|
|
|
|
|
|
|
|
for d := 'A'; d <= 'Z'; d++ {
|
|
|
|
szDeviceName := string(d) + ":"
|
|
|
|
szTarget := make([]uint16, 512)
|
|
|
|
ret, _, _ := procQueryDosDeviceW.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(szDeviceName))),
|
|
|
|
uintptr(unsafe.Pointer(&szTarget[0])),
|
|
|
|
uintptr(len(szTarget)))
|
|
|
|
if ret != 0 && windows.UTF16ToString(szTarget[:]) == rawDrive {
|
|
|
|
return filepath.Join(szDeviceName, p[len(rawDrive):])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return p
|
|
|
|
}
|