add load to windows

pull/977/head
AtakanColak 5 years ago
parent 3202231bcd
commit 24e6d6d350

@ -4,6 +4,7 @@ package common
import (
"context"
"fmt"
"path/filepath"
"strings"
"syscall"
@ -69,13 +70,13 @@ var (
ProcNtWow64QueryInformationProcess64 = ModNt.NewProc("NtWow64QueryInformationProcess64")
ProcNtWow64ReadVirtualMemory64 = ModNt.NewProc("NtWow64ReadVirtualMemory64")
PdhOpenQuery = ModPdh.NewProc("PdhOpenQuery")
PdhAddCounter = ModPdh.NewProc("PdhAddCounterW")
PdhCollectQueryData = ModPdh.NewProc("PdhCollectQueryData")
PdhGetFormattedCounterValue = ModPdh.NewProc("PdhGetFormattedCounterValue")
PdhCloseQuery = ModPdh.NewProc("PdhCloseQuery")
PdhOpenQuery = ModPdh.NewProc("PdhOpenQuery")
PdhAddCounter = ModPdh.NewProc("PdhAddCounterW")
PdhCollectQueryData = ModPdh.NewProc("PdhCollectQueryData")
PdhGetFormattedCounterValue = ModPdh.NewProc("PdhGetFormattedCounterValue")
PdhCloseQuery = ModPdh.NewProc("PdhCloseQuery")
procQueryDosDeviceW = Modkernel32.NewProc("QueryDosDeviceW")
procQueryDosDeviceW = Modkernel32.NewProc("QueryDosDeviceW")
)
type FILETIME struct {
@ -93,7 +94,7 @@ func BytePtrToString(p *uint8) string {
return string(a[:i])
}
// CounterInfo
// CounterInfo XXX
// copied from https://github.com/mackerelio/mackerel-agent/
type CounterInfo struct {
PostName string
@ -130,6 +131,57 @@ func CreateCounter(query windows.Handle, pname, cname string) (*CounterInfo, err
}, nil
}
// 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)
}
// WMIQueryWithContext - wraps wmi.Query with a timed-out context to avoid hanging
func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, connectServerArgs ...interface{}) error {
if _, ok := ctx.Deadline(); !ok {

@ -27,6 +27,23 @@ func TestLoad(t *testing.T) {
t.Log(v)
}
// Commented out to not to slow down CI
// Do conduct heavy cpu load on he computer to observe change
// func TestLoadWithInterval(t *testing.T) {
// interval := 5
// iteration := 110 / interval
// for i := 0; i < iteration; i++ {
// v, err := Avg()
// skipIfNotImplementedErr(t, err)
// if err != nil {
// t.Errorf("error %v", err)
// }
// t.Log(v)
// time.Sleep(time.Duration(interval) * time.Second)
// }
// }
func TestLoadAvgStat_String(t *testing.T) {
v := AvgStat{
Load1: 10.1,

@ -4,18 +4,75 @@ package load
import (
"context"
"sync"
"time"
"github.com/shirou/gopsutil/internal/common"
)
var (
loadErr error
loadAvg1M float64 = 0.0
loadAvg5M float64 = 0.0
loadAvg15M float64 = 0.0
loadAvgMutex sync.RWMutex
loadAvgGoroutineOnce sync.Once
)
// loadAvgGoroutine updates avg data by fetching current load by interval
// TODO register callback rather than this
// see https://psutil.readthedocs.io/en/latest/#psutil.getloadavg
// code https://github.com/giampaolo/psutil/blob/master/psutil/arch/windows/wmi.c
func loadAvgGoroutine() {
const (
loadAvgFactor1F = 0.9200444146293232478931553241
loadAvgFactor5F = 0.9834714538216174894737477501
loadAvgFactor15F = 0.9944598480048967508795473394
)
var (
interval = 5 * time.Second
)
generator, err := common.NewProcessorQueueLengthGenerator()
if err != nil {
loadAvgMutex.Lock()
loadErr = err
loadAvgMutex.Unlock()
return
}
for {
time.Sleep(interval)
if generator == nil {
return
}
currentLoad, err := generator.Generate()
loadAvgMutex.Lock()
loadErr = err
if err == nil {
loadAvg1M = loadAvg1M*loadAvgFactor1F + currentLoad*(1.0-loadAvgFactor1F)
loadAvg5M = loadAvg5M*loadAvgFactor5F + currentLoad*(1.0-loadAvgFactor5F)
loadAvg15M = loadAvg15M*loadAvgFactor15F + currentLoad*(1.0-loadAvgFactor15F)
}
loadAvgMutex.Unlock()
}
}
func Avg() (*AvgStat, error) {
return AvgWithContext(context.Background())
}
func AvgWithContext(ctx context.Context) (*AvgStat, error) {
ret := AvgStat{}
loadAvgGoroutineOnce.Do(func() { go loadAvgGoroutine() })
loadAvgMutex.RLock()
defer loadAvgMutex.RUnlock()
ret := AvgStat{
Load1: loadAvg1M,
Load5: loadAvg5M,
Load15: loadAvg15M,
}
return &ret, common.ErrNotImplementedError
return &ret, loadErr
}
func Misc() (*MiscStat, error) {

Loading…
Cancel
Save