Skip to content

Commit

Permalink
Add one minute load avg for monitor. (#1530)
Browse files Browse the repository at this point in the history
Fix CI/CD errors.

Fix Windows.

Fix Windows.

Fix golint error.
  • Loading branch information
efectn committed Sep 22, 2021
1 parent 0ad677e commit c0c1467
Show file tree
Hide file tree
Showing 14 changed files with 676 additions and 3 deletions.
59 changes: 59 additions & 0 deletions internal/gopsutil/common/common_windows.go
@@ -1,9 +1,11 @@
//go:build windows
// +build windows

package common

import (
"context"
"fmt"
"path/filepath"
"strings"
"syscall"
Expand Down Expand Up @@ -70,6 +72,7 @@ var (
ProcNtWow64ReadVirtualMemory64 = ModNt.NewProc("NtWow64ReadVirtualMemory64")

PdhOpenQuery = ModPdh.NewProc("PdhOpenQuery")
PdhAddEnglishCounterW = ModPdh.NewProc("PdhAddEnglishCounterW")
PdhAddCounter = ModPdh.NewProc("PdhAddCounterW")
PdhCollectQueryData = ModPdh.NewProc("PdhCollectQueryData")
PdhGetFormattedCounterValue = ModPdh.NewProc("PdhGetFormattedCounterValue")
Expand Down Expand Up @@ -130,6 +133,62 @@ 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
}

type Win32PerformanceCounter struct {
PostName string
CounterName string
Query windows.Handle
Counter windows.Handle
}

func NewWin32PerformanceCounter(postName, counterName string) (*Win32PerformanceCounter, error) {
query, err := CreateQuery()
if err != nil {
return nil, err
}
var counter = Win32PerformanceCounter{
Query: query,
PostName: postName,
CounterName: counterName,
}
r, _, err := PdhAddEnglishCounterW.Call(
uintptr(counter.Query),
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(counter.CounterName))),
0,
uintptr(unsafe.Pointer(&counter.Counter)),
)
if r != 0 {
return nil, err
}
return &counter, nil
}

func (w *Win32PerformanceCounter) GetValue() (float64, error) {
r, _, err := PdhCollectQueryData.Call(uintptr(w.Query))
if r != 0 && err != nil {
if r == PDH_NO_DATA {
return 0.0, fmt.Errorf("%w: this counter has not data", err)
}
return 0.0, err
}

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
func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, connectServerArgs ...interface{}) error {
if _, ok := ctx.Deadline(); !ok {
Expand Down
31 changes: 31 additions & 0 deletions internal/gopsutil/load/load.go
@@ -0,0 +1,31 @@
package load

import (
"encoding/json"
)

//var invoke common.Invoker = common.Invoke{}

type AvgStat struct {
Load1 float64 `json:"load1"`
Load5 float64 `json:"load5"`
Load15 float64 `json:"load15"`
}

func (l AvgStat) String() string {
s, _ := json.Marshal(l)
return string(s)
}

type MiscStat struct {
ProcsTotal int64 `json:"procsTotal"`
ProcsCreated int64 `json:"procsCreated"`
ProcsRunning int64 `json:"procsRunning"`
ProcsBlocked int64 `json:"procsBlocked"`
Ctxt int64 `json:"ctxt"`
}

func (m MiscStat) String() string {
s, _ := json.Marshal(m)
return string(s)
}
80 changes: 80 additions & 0 deletions internal/gopsutil/load/load_bsd.go
@@ -0,0 +1,80 @@
// +build freebsd openbsd

package load

import (
"context"
"os/exec"
"strings"
"unsafe"

"golang.org/x/sys/unix"
)

func Avg() (*AvgStat, error) {
return AvgWithContext(context.Background())
}

func AvgWithContext(ctx context.Context) (*AvgStat, error) {
// This SysctlRaw method borrowed from
// https://github.com/prometheus/node_exporter/blob/master/collector/loadavg_freebsd.go
type loadavg struct {
load [3]uint32
scale int
}
b, err := unix.SysctlRaw("vm.loadavg")
if err != nil {
return nil, err
}
load := *(*loadavg)(unsafe.Pointer((&b[0])))
scale := float64(load.scale)
ret := &AvgStat{
Load1: float64(load.load[0]) / scale,
Load5: float64(load.load[1]) / scale,
Load15: float64(load.load[2]) / scale,
}

return ret, nil
}

type forkstat struct {
forks int
vforks int
__tforks int
}

// Misc returns miscellaneous host-wide statistics.
// darwin use ps command to get process running/blocked count.
// Almost same as Darwin implementation, but state is different.
func Misc() (*MiscStat, error) {
return MiscWithContext(context.Background())
}

func MiscWithContext(ctx context.Context) (*MiscStat, error) {
bin, err := exec.LookPath("ps")
if err != nil {
return nil, err
}
out, err := invoke.CommandWithContext(ctx, bin, "axo", "state")
if err != nil {
return nil, err
}
lines := strings.Split(string(out), "\n")

ret := MiscStat{}
for _, l := range lines {
if strings.Contains(l, "R") {
ret.ProcsRunning++
} else if strings.Contains(l, "D") {
ret.ProcsBlocked++
}
}

f, err := getForkStat()
if err != nil {
return nil, err
}
ret.ProcsCreated = f.forks

return &ret, nil
}
71 changes: 71 additions & 0 deletions internal/gopsutil/load/load_darwin.go
@@ -0,0 +1,71 @@
// +build darwin

package load

import (
"context"
"os/exec"
"strings"
"unsafe"

"golang.org/x/sys/unix"
)

func Avg() (*AvgStat, error) {
return AvgWithContext(context.Background())
}

func AvgWithContext(ctx context.Context) (*AvgStat, error) {
// This SysctlRaw method borrowed from
// https://github.com/prometheus/node_exporter/blob/master/collector/loadavg_freebsd.go
// this implementation is common with BSDs
type loadavg struct {
load [3]uint32
scale int
}
b, err := unix.SysctlRaw("vm.loadavg")
if err != nil {
return nil, err
}
load := *(*loadavg)(unsafe.Pointer((&b[0])))
scale := float64(load.scale)
ret := &AvgStat{
Load1: float64(load.load[0]) / scale,
Load5: float64(load.load[1]) / scale,
Load15: float64(load.load[2]) / scale,
}

return ret, nil
}

// Misc returnes miscellaneous host-wide statistics.
// darwin use ps command to get process running/blocked count.
// Almost same as FreeBSD implementation, but state is different.
// U means 'Uninterruptible Sleep'.
func Misc() (*MiscStat, error) {
return MiscWithContext(context.Background())
}

func MiscWithContext(ctx context.Context) (*MiscStat, error) {
bin, err := exec.LookPath("ps")
if err != nil {
return nil, err
}
out, err := invoke.CommandWithContext(ctx, bin, "axo", "state")
if err != nil {
return nil, err
}
lines := strings.Split(string(out), "\n")

ret := MiscStat{}
for _, l := range lines {
if strings.Contains(l, "R") {
ret.ProcsRunning++
} else if strings.Contains(l, "U") {
// uninterruptible sleep == blocked
ret.ProcsBlocked++
}
}

return &ret, nil
}
25 changes: 25 additions & 0 deletions internal/gopsutil/load/load_fallback.go
@@ -0,0 +1,25 @@
// +build !darwin,!linux,!freebsd,!openbsd,!windows,!solaris

package load

import (
"context"

"github.com/shirou/gopsutil/internal/common"
)

func Avg() (*AvgStat, error) {
return AvgWithContext(context.Background())
}

func AvgWithContext(ctx context.Context) (*AvgStat, error) {
return nil, common.ErrNotImplementedError
}

func Misc() (*MiscStat, error) {
return MiscWithContext(context.Background())
}

func MiscWithContext(ctx context.Context) (*MiscStat, error) {
return nil, common.ErrNotImplementedError
}
7 changes: 7 additions & 0 deletions internal/gopsutil/load/load_freebsd.go
@@ -0,0 +1,7 @@
// +build freebsd

package load

func getForkStat() (forkstat, error) {
return forkstat{}, nil
}

0 comments on commit c0c1467

Please sign in to comment.