diff --git a/load/load.go b/load/load.go index 58746e7..bb996dc 100644 --- a/load/load.go +++ b/load/load.go @@ -2,8 +2,16 @@ package load import ( "encoding/json" + + "github.com/shirou/gopsutil/internal/common" ) +var invoke common.Invoker + +func init() { + invoke = common.Invoke{} +} + type LoadAvgStat struct { Load1 float64 `json:"load1"` Load5 float64 `json:"load5"` @@ -14,3 +22,14 @@ func (l LoadAvgStat) String() string { s, _ := json.Marshal(l) return string(s) } + +type MiscStat struct { + ProcsRunning int `json:"procsRunning"` + ProcsBlocked int `json:"procsBlocked"` + Ctxt int `json:"ctxt"` +} + +func (m MiscStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} diff --git a/load/load_darwin.go b/load/load_darwin.go index 0d1c7ec..a603750 100644 --- a/load/load_darwin.go +++ b/load/load_darwin.go @@ -3,7 +3,9 @@ package load import ( + "os/exec" "strconv" + "strings" "github.com/shirou/gopsutil/internal/common" ) @@ -35,3 +37,32 @@ func LoadAvg() (*LoadAvgStat, error) { 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) { + bin, err := exec.LookPath("ps") + if err != nil { + return nil, err + } + out, err := invoke.Command(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 += 1 + } else if strings.Contains(l, "U") { + // uninterruptible sleep == blocked + ret.ProcsBlocked += 1 + } + } + + return &ret, nil +} diff --git a/load/load_freebsd.go b/load/load_freebsd.go index e97c569..cd85085 100644 --- a/load/load_freebsd.go +++ b/load/load_freebsd.go @@ -3,7 +3,9 @@ package load import ( + "os/exec" "strconv" + "strings" "github.com/shirou/gopsutil/internal/common" ) @@ -35,3 +37,29 @@ func LoadAvg() (*LoadAvgStat, error) { return ret, nil } + +// Misc returnes 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) { + bin, err := exec.LookPath("ps") + if err != nil { + return nil, err + } + out, err := invoke.Command(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 += 1 + } else if strings.Contains(l, "D") { + ret.ProcsBlocked += 1 + } + } + + return &ret, nil +} diff --git a/load/load_linux.go b/load/load_linux.go index 80621c9..e2b8c56 100644 --- a/load/load_linux.go +++ b/load/load_linux.go @@ -40,3 +40,40 @@ func LoadAvg() (*LoadAvgStat, error) { return ret, nil } + + +// Misc returnes miscellaneous host-wide statistics. +// Note: the name should be changed near future. +func Misc() (*MiscStat, error) { + filename := common.HostProc("stat") + out, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + + ret := &MiscStat{} + lines := strings.Split(string(out), "\n") + for _, line := range lines { + fields := strings.Fields(line) + if len(fields) != 2 { + continue + } + v, err := strconv.ParseInt(fields[1], 10, 64) + if err != nil { + continue + } + switch fields[0] { + case "procs_running": + ret.ProcsRunning = int(v) + case "procs_blocked": + ret.ProcsBlocked = int(v) + case "ctxt": + ret.Ctxt = int(v) + default: + continue + } + + } + + return ret, nil +} diff --git a/load/load_test.go b/load/load_test.go index 39cee39..d2fa873 100644 --- a/load/load_test.go +++ b/load/load_test.go @@ -28,3 +28,27 @@ func TestLoadAvgStat_String(t *testing.T) { t.Errorf("LoadAvgStat string is invalid: %v", v) } } + +func TestMisc(t *testing.T) { + v, err := Misc() + if err != nil { + t.Errorf("error %v", err) + } + + empty := &MiscStat{} + if v == empty { + t.Errorf("error load: %v", v) + } +} + +func TestMiscStatString(t *testing.T) { + v := MiscStat{ + ProcsRunning: 1, + ProcsBlocked: 2, + Ctxt: 3, + } + e := `{"procsRunning":1,"procsBlocked":2,"ctxt":3}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("TestMiscString string is invalid: %v", v) + } +} diff --git a/load/load_windows.go b/load/load_windows.go index 65a6ba7..691d20a 100644 --- a/load/load_windows.go +++ b/load/load_windows.go @@ -11,3 +11,9 @@ func LoadAvg() (*LoadAvgStat, error) { return &ret, common.NotImplementedError } + +func Misc() (*MiscStat, error) { + ret := MiscStat{} + + return &ret, common.NotImplementedError +} diff --git a/process/process_linux.go b/process/process_linux.go index 05a75c9..1783d2f 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -8,6 +8,7 @@ import ( "fmt" "io/ioutil" "os" + "os/exec" "path/filepath" "strconv" "strings" @@ -673,7 +674,11 @@ func callLsof(arg string, pid int32) ([]string, error) { } else { cmd = []string{"-a", "-F" + arg, "-p", strconv.Itoa(int(pid))} } - out, err := invoke.Command("/usr/bin/lsof", cmd...) + lsof, err := exec.LookPath("lsof") + if err != nil { + return []string{}, err + } + out, err := invoke.Command(lsof, cmd...) if err != nil { return []string{}, err }