add load v2

tidied up common/windows
added load benchmark for load_test
cpu percent is used as load if queue is zero
pull/977/head
AtakanColak 5 years ago
parent 24e6d6d350
commit 2c98f69e11

@ -142,44 +142,49 @@ func GetCounterValue(counter windows.Handle) (float64, error) {
return value.DoubleValue, nil return value.DoubleValue, nil
} }
// ProcessorQueueLengthGenerator is struct of windows api type Win32PerformanceCounter struct {
// adapted from https://github.com/mackerelio/mackerel-agent/ PostName string
type ProcessorQueueLengthGenerator struct { CounterName string
query windows.Handle Query windows.Handle
counter *CounterInfo Counter windows.Handle
} }
// NewProcessorQueueLengthGenerator is set up windows api func NewWin32PerformanceCounter(postName, counterName string) (*Win32PerformanceCounter, error) {
// adapted from https://github.com/mackerelio/mackerel-agent/ query, err := CreateQuery()
func NewProcessorQueueLengthGenerator() (*ProcessorQueueLengthGenerator, error) {
g := &ProcessorQueueLengthGenerator{0, nil}
var err error
g.query, err = CreateQuery()
if err != nil { if err != nil {
return nil, err return nil, err
} }
var counter = Win32PerformanceCounter{
counter, err := CreateCounter(g.query, "processor_queue_length", `\System\Processor Queue Length`) Query: query,
if err != nil { PostName: postName,
CounterName: counterName,
}
r, _, err := PdhAddCounter.Call(
uintptr(counter.Query),
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(counter.CounterName))),
0,
uintptr(unsafe.Pointer(&counter.Counter)),
)
if r != 0 {
return nil, err return nil, err
} }
g.counter = counter return &counter, nil
return g, nil
} }
// Generate XXX func (w *Win32PerformanceCounter) GetValue() (float64, error) {
// adapted from https://github.com/mackerelio/mackerel-agent/ r, _, err := PdhCollectQueryData.Call(uintptr(w.Query))
func (g *ProcessorQueueLengthGenerator) Generate() (float64, error) {
r, _, err := PdhCollectQueryData.Call(uintptr(g.query))
if r != 0 && err != nil { if r != 0 && err != nil {
if r == PDH_NO_DATA { if r == PDH_NO_DATA {
return 0.0, fmt.Errorf("%w: this metric has not data", err) return 0.0, fmt.Errorf("%w: this counter has not data", err)
} }
return 0.0, err return 0.0, err
} }
return GetCounterValue(g.counter.Counter) return GetCounterValue(w.Counter)
}
func ProcessorQueueLengthCounter() (*Win32PerformanceCounter, error) {
return NewWin32PerformanceCounter("processor_queue_length", `\System\Processor Queue Length`)
} }
// WMIQueryWithContext - wraps wmi.Query with a timed-out context to avoid hanging // WMIQueryWithContext - wraps wmi.Query with a timed-out context to avoid hanging

@ -7,7 +7,7 @@ import (
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
) )
func skipIfNotImplementedErr(t *testing.T, err error) { func skipIfNotImplementedErr(t testing.TB, err error) {
if err == common.ErrNotImplementedError { if err == common.ErrNotImplementedError {
t.Skip("not implemented") t.Skip("not implemented")
} }
@ -27,23 +27,6 @@ func TestLoad(t *testing.T) {
t.Log(v) 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) { func TestLoadAvgStat_String(t *testing.T) {
v := AvgStat{ v := AvgStat{
Load1: 10.1, Load1: 10.1,
@ -84,3 +67,28 @@ func TestMiscStatString(t *testing.T) {
} }
t.Log(e) t.Log(e)
} }
func BenchmarkLoad(b *testing.B) {
loadAvg := func(t testing.TB) {
v, err := Avg()
skipIfNotImplementedErr(t, err)
if err != nil {
t.Errorf("error %v", err)
}
empty := &AvgStat{}
if v == empty {
t.Errorf("error load: %v", v)
}
}
b.Run("FirstCall", func(b *testing.B) {
loadAvg(b)
})
b.Run("SubsequentCalls", func(b *testing.B) {
for i := 0; i < b.N; i++ {
loadAvg(b)
}
})
}

@ -4,9 +4,11 @@ package load
import ( import (
"context" "context"
"log"
"sync" "sync"
"time" "time"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
) )
@ -31,30 +33,51 @@ func loadAvgGoroutine() {
) )
var ( var (
interval = 5 * time.Second sleepInterval time.Duration = 5 * time.Second
currentLoad float64
) )
generator, err := common.NewProcessorQueueLengthGenerator() counter, err := common.ProcessorQueueLengthCounter()
if err != nil { loadErr = err
loadAvgMutex.Lock() if err != nil || counter == nil {
loadErr = err
loadAvgMutex.Unlock() loadAvgMutex.Unlock()
log.Println("unexpected processor queue length counter error, please file an issue on github")
return return
} }
for { for {
time.Sleep(interval) currentLoad, loadErr = counter.GetValue()
if generator == nil { if loadErr != nil {
return goto SKIP
} }
currentLoad, err := generator.Generate() // comment following block if you want load to be 0 as long as process queue is zero
loadAvgMutex.Lock() {
loadErr = err if currentLoad == 0.0 {
if err == nil { percent, err := cpu.Percent(0, false)
loadAvg1M = loadAvg1M*loadAvgFactor1F + currentLoad*(1.0-loadAvgFactor1F) if err == nil {
loadAvg5M = loadAvg5M*loadAvgFactor5F + currentLoad*(1.0-loadAvgFactor5F) currentLoad = percent[0] / 100
loadAvg15M = loadAvg15M*loadAvgFactor15F + currentLoad*(1.0-loadAvgFactor15F) // load averages are also given some amount of the currentLoad
// maybe they shouldnt?
if loadAvg1M == 0 {
loadAvg1M = currentLoad
}
if loadAvg5M == 0 {
loadAvg5M = currentLoad / 2
}
if loadAvg15M == 0 {
loadAvg15M = currentLoad / 3
}
}
}
} }
loadAvg1M = loadAvg1M*loadAvgFactor1F + currentLoad*(1.0-loadAvgFactor1F)
loadAvg5M = loadAvg5M*loadAvgFactor5F + currentLoad*(1.0-loadAvgFactor5F)
loadAvg15M = loadAvg15M*loadAvgFactor15F + currentLoad*(1.0-loadAvgFactor15F)
SKIP:
loadAvgMutex.Unlock() loadAvgMutex.Unlock()
time.Sleep(sleepInterval)
loadAvgMutex.Lock()
} }
} }
@ -63,7 +86,10 @@ func Avg() (*AvgStat, error) {
} }
func AvgWithContext(ctx context.Context) (*AvgStat, error) { func AvgWithContext(ctx context.Context) (*AvgStat, error) {
loadAvgGoroutineOnce.Do(func() { go loadAvgGoroutine() }) loadAvgGoroutineOnce.Do(func() {
loadAvgMutex.Lock()
go loadAvgGoroutine()
})
loadAvgMutex.RLock() loadAvgMutex.RLock()
defer loadAvgMutex.RUnlock() defer loadAvgMutex.RUnlock()
ret := AvgStat{ ret := AvgStat{

Loading…
Cancel
Save