From 1cae57aee9257edbd45d2c0085e0cb24992f9fa4 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Thu, 28 Apr 2016 16:06:01 -0700 Subject: [PATCH 01/69] Add the Stats api to driverhandle --- client/driver/docker.go | 13 +++++++++++++ client/driver/driver.go | 2 ++ client/driver/exec.go | 4 ++++ client/driver/executor/executor.go | 1 + client/driver/executor/executor_basic.go | 9 ++++++++- client/driver/executor/executor_linux.go | 11 +++++++++++ client/driver/executor_plugin.go | 15 +++++++++++++++ client/driver/java.go | 4 ++++ client/driver/qemu.go | 4 ++++ client/driver/raw_exec.go | 4 ++++ client/driver/rkt.go | 4 ++++ client/driver/structs/structs.go | 8 ++++++++ 12 files changed, 78 insertions(+), 1 deletion(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index b101dbcbd5d..59ba2da13ae 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -908,6 +908,19 @@ func (h *DockerHandle) Kill() error { return nil } +func (h *DockerHandle) Stats() (*cstructs.TaskResourceUsage, error) { + stats := make(chan *docker.Stats) + done := make(chan bool) + statsOpts := docker.StatsOptions{ID: h.containerID, Done: done, Stats: stats, Stream: false, Timeout: 2 * time.Second} + if err := h.client.Stats(statsOpts); err != nil { + return nil, err + } + containerStats := <-stats + close(done) + resourceUsage := cstructs.TaskResourceUsage{MemoryStats: &cstructs.MemoryStats{RSS: containerStats.MemoryStats.Stats.Rss}} + return &resourceUsage, nil +} + func (h *DockerHandle) run() { // Wait for it... exitCode, err := h.client.WaitContainer(h.containerID) diff --git a/client/driver/driver.go b/client/driver/driver.go index 68b3b46b9fa..1040ca094e6 100644 --- a/client/driver/driver.go +++ b/client/driver/driver.go @@ -113,6 +113,8 @@ type DriverHandle interface { // Kill is used to stop the task Kill() error + + Stats() (*cstructs.TaskResourceUsage, error) } // ExecContext is shared between drivers within an allocation diff --git a/client/driver/exec.go b/client/driver/exec.go index e5264c6fcd1..77456b028a5 100644 --- a/client/driver/exec.go +++ b/client/driver/exec.go @@ -280,6 +280,10 @@ func (h *execHandle) Kill() error { } } +func (h *execHandle) Stats() (*cstructs.TaskResourceUsage, error) { + return h.executor.Stats() +} + func (h *execHandle) run() { ps, err := h.executor.Wait() close(h.doneCh) diff --git a/client/driver/executor/executor.go b/client/driver/executor/executor.go index c95437dcac7..8d7304fda3c 100644 --- a/client/driver/executor/executor.go +++ b/client/driver/executor/executor.go @@ -38,6 +38,7 @@ type Executor interface { SyncServices(ctx *ConsulContext) error DeregisterServices() error Version() (*ExecutorVersion, error) + Stats() (*cstructs.TaskResourceUsage, error) } // ConsulContext holds context to configure the consul client and run checks diff --git a/client/driver/executor/executor_basic.go b/client/driver/executor/executor_basic.go index db2479d82cb..65558aa9153 100644 --- a/client/driver/executor/executor_basic.go +++ b/client/driver/executor/executor_basic.go @@ -2,7 +2,10 @@ package executor -import cgroupConfig "github.com/opencontainers/runc/libcontainer/configs" +import ( + cstructs "github.com/hashicorp/nomad/client/driver/structs" + cgroupConfig "github.com/opencontainers/runc/libcontainer/configs" +) func (e *UniversalExecutor) configureChroot() error { return nil @@ -27,3 +30,7 @@ func (e *UniversalExecutor) applyLimits(pid int) error { func (e *UniversalExecutor) configureIsolation() error { return nil } + +func (e *UniversalExecutor) Stats() (*cstructs.TaskResourceUsage, error) { + return nil, nil +} diff --git a/client/driver/executor/executor_linux.go b/client/driver/executor/executor_linux.go index c7f3b5b7dd8..9858e00fe91 100644 --- a/client/driver/executor/executor_linux.go +++ b/client/driver/executor/executor_linux.go @@ -15,6 +15,7 @@ import ( cgroupConfig "github.com/opencontainers/runc/libcontainer/configs" "github.com/hashicorp/nomad/client/allocdir" + cstructs "github.com/hashicorp/nomad/client/driver/structs" "github.com/hashicorp/nomad/nomad/structs" ) @@ -116,6 +117,16 @@ func (e *UniversalExecutor) configureCgroups(resources *structs.Resources) error return nil } +func (e *UniversalExecutor) Stats() (*cstructs.TaskResourceUsage, error) { + manager := getCgroupManager(e.groups, e.cgPaths) + stats, err := manager.GetStats() + if err != nil { + return nil, err + } + e.logger.Printf("DIPTANU stats %#v", stats.MemoryStats.Stats) + return &cstructs.TaskResourceUsage{}, nil +} + // runAs takes a user id as a string and looks up the user, and sets the command // to execute as that user. func (e *UniversalExecutor) runAs(userid string) error { diff --git a/client/driver/executor_plugin.go b/client/driver/executor_plugin.go index decb359e3c0..a4edf2da70e 100644 --- a/client/driver/executor_plugin.go +++ b/client/driver/executor_plugin.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/go-plugin" "github.com/hashicorp/nomad/client/driver/executor" + cstructs "github.com/hashicorp/nomad/client/driver/structs" "github.com/hashicorp/nomad/nomad/structs" ) @@ -88,6 +89,12 @@ func (e *ExecutorRPC) Version() (*executor.ExecutorVersion, error) { return &version, err } +func (e *ExecutorRPC) Stats() (*cstructs.TaskResourceUsage, error) { + var resourceUsage cstructs.TaskResourceUsage + err := e.client.Call("Plugin.Stats", new(interface{}), &resourceUsage) + return &resourceUsage, err +} + type ExecutorRPCServer struct { Impl executor.Executor logger *log.Logger @@ -149,6 +156,14 @@ func (e *ExecutorRPCServer) Version(args interface{}, version *executor.Executor return err } +func (e *ExecutorRPCServer) Stats(args interface{}, resourceUsage *cstructs.TaskResourceUsage) error { + ru, err := e.Impl.Stats() + if ru != nil { + *resourceUsage = *ru + } + return err +} + type ExecutorPlugin struct { logger *log.Logger Impl *ExecutorRPCServer diff --git a/client/driver/java.go b/client/driver/java.go index acd2289a469..f18bdf8425c 100644 --- a/client/driver/java.go +++ b/client/driver/java.go @@ -380,6 +380,10 @@ func (h *javaHandle) Kill() error { } } +func (h *javaHandle) Stats() (*cstructs.TaskResourceUsage, error) { + return h.executor.Stats() +} + func (h *javaHandle) run() { ps, err := h.executor.Wait() close(h.doneCh) diff --git a/client/driver/qemu.go b/client/driver/qemu.go index aad12f77fa5..719ce41e7dd 100644 --- a/client/driver/qemu.go +++ b/client/driver/qemu.go @@ -375,6 +375,10 @@ func (h *qemuHandle) Kill() error { } } +func (h *qemuHandle) Stats() (*cstructs.TaskResourceUsage, error) { + return h.executor.Stats() +} + func (h *qemuHandle) run() { ps, err := h.executor.Wait() if ps.ExitCode == 0 && err != nil { diff --git a/client/driver/raw_exec.go b/client/driver/raw_exec.go index 8a71a73c48c..2aa0695ccde 100644 --- a/client/driver/raw_exec.go +++ b/client/driver/raw_exec.go @@ -277,6 +277,10 @@ func (h *rawExecHandle) Kill() error { } } +func (h *rawExecHandle) Stats() (*cstructs.TaskResourceUsage, error) { + return h.executor.Stats() +} + func (h *rawExecHandle) run() { ps, err := h.executor.Wait() close(h.doneCh) diff --git a/client/driver/rkt.go b/client/driver/rkt.go index 7aa6f8d001c..137c643d762 100644 --- a/client/driver/rkt.go +++ b/client/driver/rkt.go @@ -399,6 +399,10 @@ func (h *rktHandle) Kill() error { } } +func (h *rktHandle) Stats() (*cstructs.TaskResourceUsage, error) { + return nil, fmt.Errorf("stats not implemented for rkt") +} + func (h *rktHandle) run() { ps, err := h.executor.Wait() close(h.doneCh) diff --git a/client/driver/structs/structs.go b/client/driver/structs/structs.go index 9059df6ecd5..8cecb9f7a29 100644 --- a/client/driver/structs/structs.go +++ b/client/driver/structs/structs.go @@ -84,3 +84,11 @@ type CheckResult struct { // Err is the error that a check returned Err error } + +type MemoryStats struct { + RSS uint64 +} + +type TaskResourceUsage struct { + MemoryStats *MemoryStats +} From 01e0ae7753f99df2d7a17e31a99dd812050a7a89 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Fri, 29 Apr 2016 11:06:19 -0700 Subject: [PATCH 02/69] Added a client API to display resource usage of an allocation --- client/alloc_runner.go | 18 ++++++++++++ client/client.go | 17 ++++++++++++ client/driver/executor/executor_linux.go | 5 ++-- client/driver/structs/structs.go | 3 +- client/task_runner.go | 35 ++++++++++++++++++++++++ command/agent/http.go | 1 + command/agent/stats_endpoint.go | 20 ++++++++++++++ 7 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 command/agent/stats_endpoint.go diff --git a/client/alloc_runner.go b/client/alloc_runner.go index 3a0300b11ae..2e3ced2db4e 100644 --- a/client/alloc_runner.go +++ b/client/alloc_runner.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/nomad/client/allocdir" "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/client/driver" + cstructs "github.com/hashicorp/nomad/client/driver/structs" "github.com/hashicorp/nomad/nomad/structs" ) @@ -471,6 +472,23 @@ func (r *AllocRunner) Update(update *structs.Allocation) { } } +func (r *AllocRunner) ResourceUsage() map[string]*cstructs.TaskResourceUsage { + res := make(map[string]*cstructs.TaskResourceUsage) + for task, tr := range r.tasks { + res[task] = tr.ResourceUsage() + } + return res +} + +func (r *AllocRunner) ResourceUsageOfTask(task string) (*cstructs.TaskResourceUsage, error) { + tr, ok := r.tasks[task] + if !ok { + return nil, fmt.Errorf("task %q not running", task) + } + + return tr.ResourceUsage(), nil +} + // shouldUpdate takes the AllocModifyIndex of an allocation sent from the server and // checks if the current running allocation is behind and should be updated. func (r *AllocRunner) shouldUpdate(serverIndex uint64) bool { diff --git a/client/client.go b/client/client.go index 8b753573b73..8928bb1eb6f 100644 --- a/client/client.go +++ b/client/client.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/client/consul" "github.com/hashicorp/nomad/client/driver" + cstructs "github.com/hashicorp/nomad/client/driver/structs" "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/nomad" "github.com/hashicorp/nomad/nomad/structs" @@ -394,6 +395,22 @@ func (c *Client) Node() *structs.Node { return c.config.Node } +func (c *Client) ResourceUsageOfAlloc(alloc string) (map[string]*cstructs.TaskResourceUsage, error) { + ar, ok := c.allocs[alloc] + if !ok { + return nil, fmt.Errorf("allocation: %q not running on this client", alloc) + } + return ar.ResourceUsage(), nil +} + +func (c *Client) ResourceUsageOfTask(alloc string, task string) (*cstructs.TaskResourceUsage, error) { + ar, ok := c.allocs[alloc] + if !ok { + return nil, fmt.Errorf("allocation: %q not running on this client", alloc) + } + return ar.ResourceUsageOfTask(task) +} + // GetAllocFS returns the AllocFS interface for the alloc dir of an allocation func (c *Client) GetAllocFS(allocID string) (allocdir.AllocDirFS, error) { ar, ok := c.allocs[allocID] diff --git a/client/driver/executor/executor_linux.go b/client/driver/executor/executor_linux.go index 9858e00fe91..66ed63ff2df 100644 --- a/client/driver/executor/executor_linux.go +++ b/client/driver/executor/executor_linux.go @@ -123,8 +123,9 @@ func (e *UniversalExecutor) Stats() (*cstructs.TaskResourceUsage, error) { if err != nil { return nil, err } - e.logger.Printf("DIPTANU stats %#v", stats.MemoryStats.Stats) - return &cstructs.TaskResourceUsage{}, nil + rss := stats.MemoryStats.Stats["rss"] + cache := stats.MemoryStats.Stats["cache"] + return &cstructs.TaskResourceUsage{MemoryStats: &cstructs.MemoryStats{RSS: rss, Cache: cache}}, nil } // runAs takes a user id as a string and looks up the user, and sets the command diff --git a/client/driver/structs/structs.go b/client/driver/structs/structs.go index 8cecb9f7a29..dbc1f6f9a15 100644 --- a/client/driver/structs/structs.go +++ b/client/driver/structs/structs.go @@ -86,7 +86,8 @@ type CheckResult struct { } type MemoryStats struct { - RSS uint64 + RSS uint64 + Cache uint64 } type TaskResourceUsage struct { diff --git a/client/task_runner.go b/client/task_runner.go index 7a54ced55f4..7e6175c83c1 100644 --- a/client/task_runner.go +++ b/client/task_runner.go @@ -43,6 +43,10 @@ type TaskRunner struct { alloc *structs.Allocation restartTracker *RestartTracker + resourceUsage *cstructs.TaskResourceUsage + resourceUsageLock sync.RWMutex + stopResourceMonitorCh chan struct{} + task *structs.Task taskEnv *env.TaskEnvironment updateCh chan *structs.Allocation @@ -338,6 +342,9 @@ func (r *TaskRunner) run() { panic("nil wait") } + // Stop monitoring resource usage + close(r.stopResourceMonitorCh) + // Log whether the task was successful or not. r.restartTracker.SetWaitResult(waitRes) r.setState(structs.TaskStateDead, r.waitErrorToEvent(waitRes)) @@ -433,9 +440,37 @@ func (r *TaskRunner) startTask() error { r.handleLock.Lock() r.handle = handle r.handleLock.Unlock() + r.stopResourceMonitorCh = make(chan struct{}) + go r.monitorUsage() return nil } +func (r *TaskRunner) monitorUsage() { + for { + next := time.NewTimer(1 * time.Second) + select { + case <-next.C: + ru, err := r.handle.Stats() + if err != nil { + r.logger.Printf("[DEBUG] client.taskrunner: error fetching stats of task %v: %v", r.task.Name, err) + } + r.resourceUsageLock.RLock() + r.resourceUsage = ru + r.resourceUsageLock.RUnlock() + next.Reset(1 * time.Second) + case <-r.stopResourceMonitorCh: + next.Stop() + return + } + } +} + +func (r *TaskRunner) ResourceUsage() *cstructs.TaskResourceUsage { + r.resourceUsageLock.RLock() + defer r.resourceUsageLock.RUnlock() + return r.resourceUsage +} + // handleUpdate takes an updated allocation and updates internal state to // reflect the new config for the task. func (r *TaskRunner) handleUpdate(update *structs.Allocation) error { diff --git a/command/agent/http.go b/command/agent/http.go index 168021185b4..8db323373b8 100644 --- a/command/agent/http.go +++ b/command/agent/http.go @@ -114,6 +114,7 @@ func (s *HTTPServer) registerHandlers(enableDebug bool) { s.mux.HandleFunc("/v1/evaluation/", s.wrap(s.EvalSpecificRequest)) s.mux.HandleFunc("/v1/client/fs/", s.wrap(s.FsRequest)) + s.mux.HandleFunc("/v1/client/stats/", s.wrap(s.StatsRequest)) s.mux.HandleFunc("/v1/agent/self", s.wrap(s.AgentSelfRequest)) s.mux.HandleFunc("/v1/agent/join", s.wrap(s.AgentJoinRequest)) diff --git a/command/agent/stats_endpoint.go b/command/agent/stats_endpoint.go new file mode 100644 index 00000000000..594b9710942 --- /dev/null +++ b/command/agent/stats_endpoint.go @@ -0,0 +1,20 @@ +package agent + +import ( + "fmt" + "net/http" +) + +func (s *HTTPServer) StatsRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { + if s.agent.client == nil { + return nil, clientNotRunning + } + var allocID, task string + if allocID = req.URL.Query().Get("allocation"); allocID == "" { + return nil, fmt.Errorf("provide a valid alloc id") + } + if task = req.URL.Query().Get("task"); task != "" { + return s.agent.client.ResourceUsageOfTask(allocID, task) + } + return s.agent.client.ResourceUsageOfAlloc(allocID) +} From a485a3802974994cf7ae7100bee56f0d1238cd9e Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Fri, 29 Apr 2016 11:40:37 -0700 Subject: [PATCH 03/69] Added cpu stats --- client/driver/executor/executor_linux.go | 26 +++++++++++++++++++++++- client/driver/structs/structs.go | 6 ++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/client/driver/executor/executor_linux.go b/client/driver/executor/executor_linux.go index 66ed63ff2df..b3721d62ebe 100644 --- a/client/driver/executor/executor_linux.go +++ b/client/driver/executor/executor_linux.go @@ -13,6 +13,7 @@ import ( "github.com/opencontainers/runc/libcontainer/cgroups" cgroupFs "github.com/opencontainers/runc/libcontainer/cgroups/fs" cgroupConfig "github.com/opencontainers/runc/libcontainer/configs" + "github.com/opencontainers/runc/libcontainer/system" "github.com/hashicorp/nomad/client/allocdir" cstructs "github.com/hashicorp/nomad/client/driver/structs" @@ -20,6 +21,7 @@ import ( ) var ( + // A mapping of directories on the host OS to attempt to embed inside each // task's chroot. chrootEnv = map[string]string{ @@ -32,6 +34,10 @@ var ( "/sbin": "/sbin", "/usr": "/usr", } + + clockTicks = uint64(system.GetClockTicks()) + + nanosecondsInSecond = uint64(1000000000) ) // configureIsolation configures chroot and creates cgroups @@ -123,9 +129,27 @@ func (e *UniversalExecutor) Stats() (*cstructs.TaskResourceUsage, error) { if err != nil { return nil, err } + + // Memory Related Stats rss := stats.MemoryStats.Stats["rss"] cache := stats.MemoryStats.Stats["cache"] - return &cstructs.TaskResourceUsage{MemoryStats: &cstructs.MemoryStats{RSS: rss, Cache: cache}}, nil + ms := &cstructs.MemoryStats{ + RSS: rss, + Cache: cache, + } + + // CPU Related Stats + userModeTime := stats.CpuStats.CpuUsage.UsageInUsermode + kernelModeTime := stats.CpuStats.CpuUsage.UsageInKernelmode + + umTicks := (userModeTime * clockTicks) / nanosecondsInSecond + kmTicks := (kernelModeTime * clockTicks) / nanosecondsInSecond + + cs := &cstructs.CpuUsage{ + SystemMode: kmTicks, + UserMode: umTicks, + } + return &cstructs.TaskResourceUsage{MemoryStats: ms, CpuStats: cs}, nil } // runAs takes a user id as a string and looks up the user, and sets the command diff --git a/client/driver/structs/structs.go b/client/driver/structs/structs.go index dbc1f6f9a15..8f55087ac4a 100644 --- a/client/driver/structs/structs.go +++ b/client/driver/structs/structs.go @@ -90,6 +90,12 @@ type MemoryStats struct { Cache uint64 } +type CpuUsage struct { + SystemMode uint64 + UserMode uint64 +} + type TaskResourceUsage struct { MemoryStats *MemoryStats + CpuStats *CpuUsage } From 50250b145559616aceeccd8dfe225646334d47c3 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Fri, 29 Apr 2016 13:03:02 -0700 Subject: [PATCH 04/69] Added the nomad stats command --- api/allocations.go | 49 ++++++++ api/tasks.go | 21 ++++ client/driver/executor/executor_linux.go | 16 ++- client/driver/structs/structs.go | 14 ++- command/alloc_status.go | 20 ++- command/stats.go | 149 +++++++++++++++++++++++ commands.go | 5 + 7 files changed, 262 insertions(+), 12 deletions(-) create mode 100644 command/stats.go diff --git a/api/allocations.go b/api/allocations.go index 6badd3f69b5..765a349fcf1 100644 --- a/api/allocations.go +++ b/api/allocations.go @@ -1,6 +1,11 @@ package api import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" "sort" "time" ) @@ -40,6 +45,50 @@ func (a *Allocations) Info(allocID string, q *QueryOptions) (*Allocation, *Query return &resp, qm, nil } +func (a *Allocations) Stats(alloc *Allocation, q *QueryOptions) (map[string]*TaskResourceUsage, error) { + node, _, err := a.client.Nodes().Info(alloc.NodeID, &QueryOptions{}) + if err != nil { + return nil, err + } + if node.HTTPAddr == "" { + return nil, fmt.Errorf("http addr of the node where alloc %q is running is not advertised", alloc.ID) + } + u := &url.URL{ + Scheme: "http", + Host: node.HTTPAddr, + Path: "/v1/client/stats/", + } + v := url.Values{} + v.Set("allocation", alloc.ID) + u.RawQuery = v.Encode() + req := &http.Request{ + Method: "GET", + URL: u, + } + c := http.Client{} + resp, err := c.Do(req) + if err != nil { + return nil, err + } + if resp.StatusCode != 200 { + return nil, a.getErrorMsg(resp) + } + decoder := json.NewDecoder(resp.Body) + var stats map[string]*TaskResourceUsage + if err := decoder.Decode(&stats); err != nil { + return nil, err + } + return stats, nil +} + +func (a *Allocations) getErrorMsg(resp *http.Response) error { + if errMsg, err := ioutil.ReadAll(resp.Body); err == nil { + return fmt.Errorf(string(errMsg)) + } else { + return err + } +} + // Allocation is used for serialization of allocations. type Allocation struct { ID string diff --git a/api/tasks.go b/api/tasks.go index e0fd67bac4f..1a80212fd24 100644 --- a/api/tasks.go +++ b/api/tasks.go @@ -4,6 +4,27 @@ import ( "time" ) +type MemoryStats struct { + RSS uint64 + Cache uint64 + Swap uint64 + MaxUsage uint64 + KernelUsage uint64 + KernelMaxUsage uint64 +} + +type CpuUsage struct { + SystemMode uint64 + UserMode uint64 + ThrottledPeriods uint64 + ThrottledTime uint64 +} + +type TaskResourceUsage struct { + MemoryStats *MemoryStats + CpuStats *CpuUsage +} + // RestartPolicy defines how the Nomad client restarts // tasks in a taskgroup when they fail type RestartPolicy struct { diff --git a/client/driver/executor/executor_linux.go b/client/driver/executor/executor_linux.go index b3721d62ebe..bb170af556c 100644 --- a/client/driver/executor/executor_linux.go +++ b/client/driver/executor/executor_linux.go @@ -131,11 +131,17 @@ func (e *UniversalExecutor) Stats() (*cstructs.TaskResourceUsage, error) { } // Memory Related Stats + swap := stats.MemoryStats.SwapUsage + maxUsage := stats.MemoryStats.Usage.MaxUsage rss := stats.MemoryStats.Stats["rss"] cache := stats.MemoryStats.Stats["cache"] ms := &cstructs.MemoryStats{ - RSS: rss, - Cache: cache, + RSS: rss, + Cache: cache, + Swap: swap.Usage, + MaxUsage: maxUsage, + KernelUsage: stats.MemoryStats.KernelUsage.Usage, + KernelMaxUsage: stats.MemoryStats.KernelUsage.MaxUsage, } // CPU Related Stats @@ -146,8 +152,10 @@ func (e *UniversalExecutor) Stats() (*cstructs.TaskResourceUsage, error) { kmTicks := (kernelModeTime * clockTicks) / nanosecondsInSecond cs := &cstructs.CpuUsage{ - SystemMode: kmTicks, - UserMode: umTicks, + SystemMode: kmTicks, + UserMode: umTicks, + ThrottledPeriods: stats.CpuStats.ThrottlingData.ThrottledPeriods, + ThrottledTime: stats.CpuStats.ThrottlingData.ThrottledTime, } return &cstructs.TaskResourceUsage{MemoryStats: ms, CpuStats: cs}, nil } diff --git a/client/driver/structs/structs.go b/client/driver/structs/structs.go index 8f55087ac4a..5ec4f211d58 100644 --- a/client/driver/structs/structs.go +++ b/client/driver/structs/structs.go @@ -86,13 +86,19 @@ type CheckResult struct { } type MemoryStats struct { - RSS uint64 - Cache uint64 + RSS uint64 + Cache uint64 + Swap uint64 + MaxUsage uint64 + KernelUsage uint64 + KernelMaxUsage uint64 } type CpuUsage struct { - SystemMode uint64 - UserMode uint64 + SystemMode uint64 + UserMode uint64 + ThrottledPeriods uint64 + ThrottledTime uint64 } type TaskResourceUsage struct { diff --git a/command/alloc_status.go b/command/alloc_status.go index 700a175469c..6f4eeec3c1e 100644 --- a/command/alloc_status.go +++ b/command/alloc_status.go @@ -3,6 +3,7 @@ package command import ( "fmt" "sort" + "strconv" "strings" "time" @@ -119,6 +120,11 @@ func (c *AllocStatusCommand) Run(args []string) int { return 1 } + stats, err := client.Allocations().Stats(alloc, nil) + if err != nil { + c.Ui.Error(fmt.Sprintf("couldn't retreive stats: %v", err)) + } + // Format the allocation data basic := []string{ fmt.Sprintf("ID|%s", limit(alloc.ID, length)), @@ -140,7 +146,7 @@ func (c *AllocStatusCommand) Run(args []string) int { c.Ui.Output(formatKV(basic)) if !short { - c.taskResources(alloc) + c.taskResources(alloc, stats) } // Print the state of each task. @@ -302,7 +308,7 @@ func (c *AllocStatusCommand) allocResources(alloc *api.Allocation) { } // taskResources prints out the tasks current resource usage -func (c *AllocStatusCommand) taskResources(alloc *api.Allocation) { +func (c *AllocStatusCommand) taskResources(alloc *api.Allocation, stats map[string]*api.TaskResourceUsage) { if len(alloc.TaskResources) == 0 { return } @@ -338,9 +344,15 @@ func (c *AllocStatusCommand) taskResources(alloc *api.Allocation) { if len(addr) > 0 { firstAddr = addr[0] } + cpuUsage := strconv.Itoa(resource.CPU) + memUsage := strconv.Itoa(resource.MemoryMB) + if ru, ok := stats[task]; ok { + cpuUsage = fmt.Sprintf("%v/%v", (ru.CpuStats.SystemMode + ru.CpuStats.UserMode), resource.CPU) + memUsage = fmt.Sprintf("%v/%v", ru.MemoryStats.RSS/(1024*1024), resource.MemoryMB) + } resourcesOutput = append(resourcesOutput, fmt.Sprintf("%v|%v|%v|%v|%v", - resource.CPU, - resource.MemoryMB, + cpuUsage, + memUsage, resource.DiskMB, resource.IOPS, firstAddr)) diff --git a/command/stats.go b/command/stats.go new file mode 100644 index 00000000000..3d395515b5d --- /dev/null +++ b/command/stats.go @@ -0,0 +1,149 @@ +package command + +import ( + "fmt" + "github.com/hashicorp/nomad/api" + "strings" +) + +type StatsCommand struct { + Meta +} + +func (f *StatsCommand) Help() string { + return "Dispalys stats of an allocation or a task running on a nomad client" +} + +func (f *StatsCommand) Synopsis() string { + return "Dispalys stats of an allocation or a task running on a nomad client" +} + +func (f *StatsCommand) Run(args []string) int { + var verbose bool + flags := f.Meta.FlagSet("fs-list", FlagSetClient) + flags.BoolVar(&verbose, "verbose", false, "") + flags.Usage = func() { f.Ui.Output(f.Help()) } + + if err := flags.Parse(args); err != nil { + return 1 + } + + args = flags.Args() + if len(args) < 1 { + f.Ui.Error("allocation id is a required parameter") + return 1 + } + client, err := f.Meta.Client() + if err != nil { + f.Ui.Error(fmt.Sprintf("Error initializing client: %v", err)) + return 1 + } + + var allocID, task string + allocID = strings.TrimSpace(args[0]) + if len(args) == 2 { + task = strings.TrimSpace(args[1]) + } + + // Truncate the id unless full length is requested + length := shortId + if verbose { + length = fullId + } + + // Query the allocation info + if len(allocID) == 1 { + f.Ui.Error(fmt.Sprintf("Alloc ID must contain at least two characters.")) + return 1 + } + if len(allocID)%2 == 1 { + // Identifiers must be of even length, so we strip off the last byte + // to provide a consistent user experience. + allocID = allocID[:len(allocID)-1] + } + + allocs, _, err := client.Allocations().PrefixList(allocID) + if err != nil { + f.Ui.Error(fmt.Sprintf("Error querying allocation: %v", err)) + return 1 + } + if len(allocs) == 0 { + f.Ui.Error(fmt.Sprintf("No allocation(s) with prefix or id %q found", allocID)) + return 1 + } + if len(allocs) > 1 { + // Format the allocs + out := make([]string, len(allocs)+1) + out[0] = "ID|Eval ID|Job ID|Task Group|Desired Status|Client Status" + for i, alloc := range allocs { + out[i+1] = fmt.Sprintf("%s|%s|%s|%s|%s|%s", + limit(alloc.ID, length), + limit(alloc.EvalID, length), + alloc.JobID, + alloc.TaskGroup, + alloc.DesiredStatus, + alloc.ClientStatus, + ) + } + f.Ui.Output(fmt.Sprintf("Prefix matched multiple allocations\n\n%s", formatList(out))) + return 0 + } + // Prefix lookup matched a single allocation + alloc, _, err := client.Allocations().Info(allocs[0].ID, nil) + if err != nil { + f.Ui.Error(fmt.Sprintf("Error querying allocation: %s", err)) + return 1 + } + + stats, err := client.Allocations().Stats(alloc, nil) + if err != nil { + f.Ui.Error(fmt.Sprintf("unable to get stats: %v", err)) + return 1 + } + if task == "" { + f.printAllocResourceUsage(alloc, stats) + } else { + f.printTaskResourceUsage(task, stats) + } + return 0 +} + +func (f *StatsCommand) printTaskResourceUsage(task string, resourceUsage map[string]*api.TaskResourceUsage) { + tu, ok := resourceUsage[task] + if !ok { + return + } + f.Ui.Output(fmt.Sprintf("===> Task: %q", task)) + f.Ui.Output("Memory Stats") + out := make([]string, 2) + out[0] = "RSS|Cache|Swap|Max Usage|Kernel Usage|KernelMaxUsage" + out[1] = fmt.Sprintf("%v|%v|%v|%v|%v|%v", + f.inMB(tu.MemoryStats.RSS), + f.inMB(tu.MemoryStats.Cache), + f.inMB(tu.MemoryStats.Swap), + f.inMB(tu.MemoryStats.MaxUsage), + f.inMB(tu.MemoryStats.KernelUsage), + f.inMB(tu.MemoryStats.KernelMaxUsage), + ) + f.Ui.Output(formatList(out)) + + f.Ui.Output("") + + f.Ui.Output("CPU Stats") + out = make([]string, 2) + out[0] = "Kernel Mode|User Mode|Throttled Periods|Throttled Time" + out[1] = fmt.Sprintf("%v|%v|%v|%v", tu.CpuStats.SystemMode, tu.CpuStats.UserMode, + tu.CpuStats.ThrottledPeriods, tu.CpuStats.ThrottledTime) + f.Ui.Output(formatList(out)) +} + +func (f *StatsCommand) printAllocResourceUsage(alloc *api.Allocation, resourceUsage map[string]*api.TaskResourceUsage) { + f.Ui.Output(fmt.Sprintf("Resource Usage of Tasks running in Allocation %q", alloc.ID)) + for task, _ := range alloc.TaskStates { + f.printTaskResourceUsage(task, resourceUsage) + } +} + +func (f *StatsCommand) inMB(bytes uint64) uint64 { + return bytes +} diff --git a/commands.go b/commands.go index b402f429bc8..023a74d7d23 100644 --- a/commands.go +++ b/commands.go @@ -121,6 +121,11 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory { Meta: meta, }, nil }, + "stats": func() (cli.Command, error) { + return &command.StatsCommand{ + Meta: meta, + }, nil + }, "status": func() (cli.Command, error) { return &command.StatusCommand{ Meta: meta, From eda53e3bcb1ce81c76c1fc0ab2d9ef2ee3c6a4f7 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Mon, 9 May 2016 06:35:53 -0700 Subject: [PATCH 05/69] Added a ringbuff datastructure --- client/stats/ring.go | 45 +++++++++++++++++++++ client/stats/ring_test.go | 83 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 client/stats/ring.go create mode 100644 client/stats/ring_test.go diff --git a/client/stats/ring.go b/client/stats/ring.go new file mode 100644 index 00000000000..d20dd895cb2 --- /dev/null +++ b/client/stats/ring.go @@ -0,0 +1,45 @@ +package stats + +import ( + "fmt" +) + +var ( + defaultCap = 60 +) + +type RingBuff struct { + head int + buff []interface{} +} + +func NewRingBuff(capacity int) (*RingBuff, error) { + if capacity < 1 { + return nil, fmt.Errorf("can not create a ring buffer with capacity: %v", capacity) + } + return &RingBuff{buff: make([]interface{}, capacity), head: -1}, nil +} + +func (r *RingBuff) Enqueue(value interface{}) { + r.head += 1 + if r.head == len(r.buff) { + r.head = 0 + } + r.buff[r.head] = value +} + +func (r *RingBuff) Peek() interface{} { + return r.buff[r.head] +} + +func (r *RingBuff) Values() []interface{} { + if r.head == len(r.buff)-1 { + vals := make([]interface{}, len(r.buff)) + copy(vals, r.buff) + return vals + } + + slice1 := r.buff[r.head+1:] + slice2 := r.buff[:r.head+1] + return append(slice1, slice2...) +} diff --git a/client/stats/ring_test.go b/client/stats/ring_test.go new file mode 100644 index 00000000000..11e4028e28c --- /dev/null +++ b/client/stats/ring_test.go @@ -0,0 +1,83 @@ +package stats + +import ( + "testing" +) + +func TestRingBuffInvalid(t *testing.T) { + if _, err := NewRingBuff(0); err == nil { + t.Fatalf("expected err") + } +} + +func TestRingBuffEnqueue(t *testing.T) { + rb, err := NewRingBuff(3) + if err != nil { + t.Fatalf("err: %v", err) + } + + rb.Enqueue(1) + rb.Enqueue(2) + rb.Enqueue(3) + if val := rb.Peek(); val != 3 { + t.Fatalf("expected: %v, actual: %v", 3, val) + } + + rb.Enqueue(4) + rb.Enqueue(5) + if val := rb.Peek(); val != 5 { + t.Fatalf("expected: %v, actual: %v", 5, val) + } +} + +func TestRingBuffValues(t *testing.T) { + rb, err := NewRingBuff(3) + if err != nil { + t.Fatalf("err: %v", err) + } + rb.Enqueue(1) + rb.Enqueue(2) + rb.Enqueue(3) + rb.Enqueue(4) + + expected := []interface{}{2, 3, 4} + if !sliceEq(expected, rb.Values()) { + t.Fatalf("expected: %v, actual: %v", expected, rb.Values()) + } + + rb.Enqueue(5) + expected = []interface{}{3, 4, 5} + if !sliceEq(expected, rb.Values()) { + t.Fatalf("expected: %v, actual: %v", expected, rb.Values()) + } + + rb.Enqueue(6) + expected = []interface{}{4, 5, 6} + if !sliceEq(expected, rb.Values()) { + t.Fatalf("expected: %v, actual: %v", expected, rb.Values()) + } + +} + +func sliceEq(slice1, slice2 []interface{}) bool { + + if slice1 == nil && slice2 == nil { + return true + } + + if slice1 == nil || slice2 == nil { + return false + } + + if len(slice1) != len(slice2) { + return false + } + + for i := range slice1 { + if slice1[i] != slice2[i] { + return false + } + } + + return true +} From f390261cd002dc6a213aeac96f8568c52d5c8e28 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Mon, 9 May 2016 07:57:26 -0700 Subject: [PATCH 06/69] Reporting time series of stats --- client/alloc_runner.go | 20 +++++++++---- client/client.go | 13 ++------ client/driver/executor/executor_linux.go | 3 +- client/driver/structs/structs.go | 1 + client/task_runner.go | 38 ++++++++++++++++++++++-- command/agent/stats_endpoint.go | 16 ++++++++-- 6 files changed, 68 insertions(+), 23 deletions(-) diff --git a/client/alloc_runner.go b/client/alloc_runner.go index 2e3ced2db4e..073d383158b 100644 --- a/client/alloc_runner.go +++ b/client/alloc_runner.go @@ -12,7 +12,6 @@ import ( "github.com/hashicorp/nomad/client/allocdir" "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/client/driver" - cstructs "github.com/hashicorp/nomad/client/driver/structs" "github.com/hashicorp/nomad/nomad/structs" ) @@ -29,6 +28,11 @@ const ( // AllocStateUpdater is used to update the status of an allocation type AllocStateUpdater func(alloc *structs.Allocation) +type AllocStatsReporter interface { + AllocStats() map[string]TaskStatsReporter + TaskStats(task string) (TaskStatsReporter, error) +} + // AllocRunner is used to wrap an allocation and provide the execution context. type AllocRunner struct { config *config.Config @@ -472,21 +476,25 @@ func (r *AllocRunner) Update(update *structs.Allocation) { } } -func (r *AllocRunner) ResourceUsage() map[string]*cstructs.TaskResourceUsage { - res := make(map[string]*cstructs.TaskResourceUsage) +func (r *AllocRunner) StatsReporter() AllocStatsReporter { + return r +} + +func (r *AllocRunner) AllocStats() map[string]TaskStatsReporter { + res := make(map[string]TaskStatsReporter) for task, tr := range r.tasks { - res[task] = tr.ResourceUsage() + res[task] = tr.StatsReporter() } return res } -func (r *AllocRunner) ResourceUsageOfTask(task string) (*cstructs.TaskResourceUsage, error) { +func (r *AllocRunner) TaskStats(task string) (TaskStatsReporter, error) { tr, ok := r.tasks[task] if !ok { return nil, fmt.Errorf("task %q not running", task) } - return tr.ResourceUsage(), nil + return tr.StatsReporter(), nil } // shouldUpdate takes the AllocModifyIndex of an allocation sent from the server and diff --git a/client/client.go b/client/client.go index 8928bb1eb6f..6e60e6bd8cf 100644 --- a/client/client.go +++ b/client/client.go @@ -17,7 +17,6 @@ import ( "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/client/consul" "github.com/hashicorp/nomad/client/driver" - cstructs "github.com/hashicorp/nomad/client/driver/structs" "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/nomad" "github.com/hashicorp/nomad/nomad/structs" @@ -395,20 +394,12 @@ func (c *Client) Node() *structs.Node { return c.config.Node } -func (c *Client) ResourceUsageOfAlloc(alloc string) (map[string]*cstructs.TaskResourceUsage, error) { +func (c *Client) AllocStats(alloc string) (AllocStatsReporter, error) { ar, ok := c.allocs[alloc] if !ok { return nil, fmt.Errorf("allocation: %q not running on this client", alloc) } - return ar.ResourceUsage(), nil -} - -func (c *Client) ResourceUsageOfTask(alloc string, task string) (*cstructs.TaskResourceUsage, error) { - ar, ok := c.allocs[alloc] - if !ok { - return nil, fmt.Errorf("allocation: %q not running on this client", alloc) - } - return ar.ResourceUsageOfTask(task) + return ar.StatsReporter(), nil } // GetAllocFS returns the AllocFS interface for the alloc dir of an allocation diff --git a/client/driver/executor/executor_linux.go b/client/driver/executor/executor_linux.go index bb170af556c..2aa92fe3188 100644 --- a/client/driver/executor/executor_linux.go +++ b/client/driver/executor/executor_linux.go @@ -8,6 +8,7 @@ import ( "strconv" "strings" "syscall" + "time" "github.com/hashicorp/go-multierror" "github.com/opencontainers/runc/libcontainer/cgroups" @@ -157,7 +158,7 @@ func (e *UniversalExecutor) Stats() (*cstructs.TaskResourceUsage, error) { ThrottledPeriods: stats.CpuStats.ThrottlingData.ThrottledPeriods, ThrottledTime: stats.CpuStats.ThrottlingData.ThrottledTime, } - return &cstructs.TaskResourceUsage{MemoryStats: ms, CpuStats: cs}, nil + return &cstructs.TaskResourceUsage{MemoryStats: ms, CpuStats: cs, Timestamp: time.Now()}, nil } // runAs takes a user id as a string and looks up the user, and sets the command diff --git a/client/driver/structs/structs.go b/client/driver/structs/structs.go index 5ec4f211d58..89e0ed40b05 100644 --- a/client/driver/structs/structs.go +++ b/client/driver/structs/structs.go @@ -104,4 +104,5 @@ type CpuUsage struct { type TaskResourceUsage struct { MemoryStats *MemoryStats CpuStats *CpuUsage + Timestamp time.Time } diff --git a/client/task_runner.go b/client/task_runner.go index 7e6175c83c1..307b85cd4d8 100644 --- a/client/task_runner.go +++ b/client/task_runner.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/client/driver" "github.com/hashicorp/nomad/client/getter" + "github.com/hashicorp/nomad/client/stats" "github.com/hashicorp/nomad/nomad/structs" "github.com/hashicorp/nomad/client/driver/env" @@ -34,6 +35,11 @@ const ( killFailureLimit = 5 ) +type TaskStatsReporter interface { + ResourceUsage() *cstructs.TaskResourceUsage + ResourceUsageTS() []*cstructs.TaskResourceUsage +} + // TaskRunner is used to wrap a task within an allocation and provide the execution context. type TaskRunner struct { config *config.Config @@ -43,7 +49,7 @@ type TaskRunner struct { alloc *structs.Allocation restartTracker *RestartTracker - resourceUsage *cstructs.TaskResourceUsage + resourceUsage *stats.RingBuff resourceUsageLock sync.RWMutex stopResourceMonitorCh chan struct{} @@ -90,11 +96,18 @@ func NewTaskRunner(logger *log.Logger, config *config.Config, } restartTracker := newRestartTracker(tg.RestartPolicy, alloc.Job.Type) + resourceUsage, err := stats.NewRingBuff(60) + if err != nil { + logger.Printf("[ERR] client: can't create resource usage buffer: %v", err) + return nil + } + tc := &TaskRunner{ config: config, updater: updater, logger: logger, restartTracker: restartTracker, + resourceUsage: resourceUsage, ctx: ctx, alloc: alloc, task: task, @@ -455,7 +468,7 @@ func (r *TaskRunner) monitorUsage() { r.logger.Printf("[DEBUG] client.taskrunner: error fetching stats of task %v: %v", r.task.Name, err) } r.resourceUsageLock.RLock() - r.resourceUsage = ru + r.resourceUsage.Enqueue(ru) r.resourceUsageLock.RUnlock() next.Reset(1 * time.Second) case <-r.stopResourceMonitorCh: @@ -465,10 +478,29 @@ func (r *TaskRunner) monitorUsage() { } } +func (r *TaskRunner) StatsReporter() TaskStatsReporter { + return r +} + func (r *TaskRunner) ResourceUsage() *cstructs.TaskResourceUsage { r.resourceUsageLock.RLock() defer r.resourceUsageLock.RUnlock() - return r.resourceUsage + val := r.resourceUsage.Peek() + if val != nil { + return val.(*cstructs.TaskResourceUsage) + } + return nil +} + +func (r *TaskRunner) ResourceUsageTS() []*cstructs.TaskResourceUsage { + r.resourceUsageLock.RLock() + defer r.resourceUsageLock.RUnlock() + values := r.resourceUsage.Values() + ts := make([]*cstructs.TaskResourceUsage, len(values)) + for index, val := range values { + ts[index] = val.(*cstructs.TaskResourceUsage) + } + return ts } // handleUpdate takes an updated allocation and updates internal state to diff --git a/command/agent/stats_endpoint.go b/command/agent/stats_endpoint.go index 594b9710942..66c64217fa9 100644 --- a/command/agent/stats_endpoint.go +++ b/command/agent/stats_endpoint.go @@ -13,8 +13,20 @@ func (s *HTTPServer) StatsRequest(resp http.ResponseWriter, req *http.Request) ( if allocID = req.URL.Query().Get("allocation"); allocID == "" { return nil, fmt.Errorf("provide a valid alloc id") } + statsReporter, err := s.agent.client.AllocStats(allocID) + if err != nil { + return nil, err + } if task = req.URL.Query().Get("task"); task != "" { - return s.agent.client.ResourceUsageOfTask(allocID, task) + taskStatsReporter, err := statsReporter.TaskStats(task) + if err != nil { + return nil, err + } + return taskStatsReporter.ResourceUsage(), nil + } + res := make(map[string]interface{}) + for task, sr := range statsReporter.AllocStats() { + res[task] = sr.ResourceUsage() } - return s.agent.client.ResourceUsageOfAlloc(allocID) + return res, nil } From 445b181fecfca4e29643bd0bed23dd64bd044356 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Mon, 9 May 2016 08:19:04 -0700 Subject: [PATCH 07/69] Updated gopsutil --- client/fingerprint/cpu.go | 2 +- client/fingerprint/host.go | 2 +- vendor/github.com/shirou/gopsutil/cpu/cpu.go | 35 +- .../shirou/gopsutil/cpu/cpu_darwin.go | 17 +- .../shirou/gopsutil/cpu/cpu_darwin_cgo.go | 12 +- .../shirou/gopsutil/cpu/cpu_darwin_nocgo.go | 8 +- .../shirou/gopsutil/cpu/cpu_freebsd.go | 20 +- .../shirou/gopsutil/cpu/cpu_linux.go | 34 +- .../shirou/gopsutil/cpu/cpu_unix.go | 10 +- .../shirou/gopsutil/cpu/cpu_windows.go | 20 +- .../github.com/shirou/gopsutil/host/host.go | 14 +- .../shirou/gopsutil/host/host_darwin.go | 25 +- .../shirou/gopsutil/host/host_darwin_amd64.go | 2 +- .../shirou/gopsutil/host/host_freebsd.go | 26 +- .../shirou/gopsutil/host/host_freebsd_386.go | 43 ++ .../gopsutil/host/host_freebsd_amd64.go | 7 +- .../shirou/gopsutil/host/host_linux.go | 70 +-- .../shirou/gopsutil/host/host_linux_386.go | 7 +- .../shirou/gopsutil/host/host_linux_amd64.go | 14 +- .../shirou/gopsutil/host/host_linux_arm.go | 19 +- .../shirou/gopsutil/host/host_linux_arm64.go | 43 ++ .../gopsutil/host/host_linux_ppc64le.go | 43 ++ .../shirou/gopsutil/host/host_windows.go | 8 +- .../shirou/gopsutil/host/types_freebsd.go | 1 + .../shirou/gopsutil/host/types_linux.go | 9 +- .../{process => internal/common}/binary.go | 4 +- .../shirou/gopsutil/internal/common/common.go | 44 +- .../gopsutil/internal/common/common_darwin.go | 12 +- .../internal/common/common_freebsd.go | 16 +- vendor/github.com/shirou/gopsutil/mem/mem.go | 50 +- .../shirou/gopsutil/mem/mem_darwin.go | 102 +--- .../shirou/gopsutil/mem/mem_darwin_cgo.go | 53 ++ .../shirou/gopsutil/mem/mem_darwin_nocgo.go | 88 ++++ .../shirou/gopsutil/mem/mem_freebsd.go | 14 +- .../shirou/gopsutil/mem/mem_linux.go | 2 +- .../shirou/gopsutil/mem/mem_windows.go | 4 +- vendor/github.com/shirou/gopsutil/net/net.go | 76 +-- .../shirou/gopsutil/net/net_darwin.go | 25 +- .../shirou/gopsutil/net/net_freebsd.go | 25 +- .../shirou/gopsutil/net/net_linux.go | 456 +++++++++++++++++- .../shirou/gopsutil/net/net_unix.go | 12 +- .../shirou/gopsutil/net/net_windows.go | 29 +- .../shirou/gopsutil/process/process.go | 72 +-- .../shirou/gopsutil/process/process_darwin.go | 96 ++-- .../gopsutil/process/process_darwin_amd64.go | 4 +- .../gopsutil/process/process_freebsd.go | 137 ++++-- .../gopsutil/process/process_freebsd_386.go | 284 ++++++----- .../gopsutil/process/process_freebsd_amd64.go | 285 ++++++----- .../shirou/gopsutil/process/process_linux.go | 203 +++++--- .../shirou/gopsutil/process/process_posix.go | 19 +- .../gopsutil/process/process_windows.go | 87 ++-- .../shirou/gopsutil/process/types_freebsd.go | 95 ++++ vendor/vendor.json | 24 +- 53 files changed, 2004 insertions(+), 805 deletions(-) create mode 100644 vendor/github.com/shirou/gopsutil/host/host_freebsd_386.go create mode 100644 vendor/github.com/shirou/gopsutil/host/host_linux_arm64.go create mode 100644 vendor/github.com/shirou/gopsutil/host/host_linux_ppc64le.go rename vendor/github.com/shirou/gopsutil/{process => internal/common}/binary.go (99%) create mode 100644 vendor/github.com/shirou/gopsutil/mem/mem_darwin_cgo.go create mode 100644 vendor/github.com/shirou/gopsutil/mem/mem_darwin_nocgo.go create mode 100644 vendor/github.com/shirou/gopsutil/process/types_freebsd.go diff --git a/client/fingerprint/cpu.go b/client/fingerprint/cpu.go index bcd09f507df..57c59a6f93c 100644 --- a/client/fingerprint/cpu.go +++ b/client/fingerprint/cpu.go @@ -22,7 +22,7 @@ func NewCPUFingerprint(logger *log.Logger) Fingerprint { } func (f *CPUFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { - cpuInfo, err := cpu.CPUInfo() + cpuInfo, err := cpu.Info() if err != nil { f.logger.Println("[WARN] Error reading CPU information:", err) return false, err diff --git a/client/fingerprint/host.go b/client/fingerprint/host.go index 87acac63c97..d735c05cb31 100644 --- a/client/fingerprint/host.go +++ b/client/fingerprint/host.go @@ -25,7 +25,7 @@ func NewHostFingerprint(logger *log.Logger) Fingerprint { } func (f *HostFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { - hostInfo, err := host.HostInfo() + hostInfo, err := host.Info() if err != nil { f.logger.Println("[WARN] Error retrieving host information: ", err) return false, err diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu.go b/vendor/github.com/shirou/gopsutil/cpu/cpu.go index fb23a39c291..71535094d48 100644 --- a/vendor/github.com/shirou/gopsutil/cpu/cpu.go +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu.go @@ -7,7 +7,7 @@ import ( "strings" ) -type CPUTimesStat struct { +type TimesStat struct { CPU string `json:"cpu"` User float64 `json:"user"` System float64 `json:"system"` @@ -18,33 +18,33 @@ type CPUTimesStat struct { Softirq float64 `json:"softirq"` Steal float64 `json:"steal"` Guest float64 `json:"guest"` - GuestNice float64 `json:"guest_nice"` + GuestNice float64 `json:"guestNice"` Stolen float64 `json:"stolen"` } -type CPUInfoStat struct { +type InfoStat struct { CPU int32 `json:"cpu"` - VendorID string `json:"vendor_id"` + VendorID string `json:"vendorId"` Family string `json:"family"` Model string `json:"model"` Stepping int32 `json:"stepping"` - PhysicalID string `json:"physical_id"` - CoreID string `json:"core_id"` + PhysicalID string `json:"physicalId"` + CoreID string `json:"coreId"` Cores int32 `json:"cores"` - ModelName string `json:"model_name"` + ModelName string `json:"modelName"` Mhz float64 `json:"mhz"` - CacheSize int32 `json:"cache_size"` + CacheSize int32 `json:"cacheSize"` Flags []string `json:"flags"` } -var lastCPUTimes []CPUTimesStat -var lastPerCPUTimes []CPUTimesStat +var lastCPUTimes []TimesStat +var lastPerCPUTimes []TimesStat -func CPUCounts(logical bool) (int, error) { +func Counts(logical bool) (int, error) { return runtime.NumCPU(), nil } -func (c CPUTimesStat) String() string { +func (c TimesStat) String() string { v := []string{ `"cpu":"` + c.CPU + `"`, `"user":` + strconv.FormatFloat(c.User, 'f', 1, 64), @@ -56,14 +56,21 @@ func (c CPUTimesStat) String() string { `"softirq":` + strconv.FormatFloat(c.Softirq, 'f', 1, 64), `"steal":` + strconv.FormatFloat(c.Steal, 'f', 1, 64), `"guest":` + strconv.FormatFloat(c.Guest, 'f', 1, 64), - `"guest_nice":` + strconv.FormatFloat(c.GuestNice, 'f', 1, 64), + `"guestNice":` + strconv.FormatFloat(c.GuestNice, 'f', 1, 64), `"stolen":` + strconv.FormatFloat(c.Stolen, 'f', 1, 64), } return `{` + strings.Join(v, ",") + `}` } -func (c CPUInfoStat) String() string { +// Total returns the total number of seconds in a CPUTimesStat +func (c TimesStat) Total() float64 { + total := c.User + c.System + c.Nice + c.Iowait + c.Irq + c.Softirq + c.Steal + + c.Guest + c.GuestNice + c.Idle + c.Stolen + return total +} + +func (c InfoStat) String() string { s, _ := json.Marshal(c) return string(s) } diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin.go index 4066b651501..fbb74a821f1 100644 --- a/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin.go +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin.go @@ -21,7 +21,7 @@ const ( // default value. from time.h var ClocksPerSec = float64(128) -func CPUTimes(percpu bool) ([]CPUTimesStat, error) { +func Times(percpu bool) ([]TimesStat, error) { if percpu { return perCPUTimes() } @@ -30,15 +30,18 @@ func CPUTimes(percpu bool) ([]CPUTimesStat, error) { } // Returns only one CPUInfoStat on FreeBSD -func CPUInfo() ([]CPUInfoStat, error) { - var ret []CPUInfoStat - - out, err := exec.Command("/usr/sbin/sysctl", "machdep.cpu").Output() +func Info() ([]InfoStat, error) { + var ret []InfoStat + sysctl, err := exec.LookPath("/usr/sbin/sysctl") + if err != nil { + return ret, err + } + out, err := exec.Command(sysctl, "machdep.cpu").Output() if err != nil { return ret, err } - c := CPUInfoStat{} + c := InfoStat{} for _, line := range strings.Split(string(out), "\n") { values := strings.Fields(line) if len(values) < 1 { @@ -87,7 +90,7 @@ func CPUInfo() ([]CPUInfoStat, error) { // Use the rated frequency of the CPU. This is a static value and does not // account for low power or Turbo Boost modes. - out, err = exec.Command("/usr/sbin/sysctl", "hw.cpufrequency").Output() + out, err = exec.Command(sysctl, "hw.cpufrequency").Output() if err != nil { return ret, err } diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin_cgo.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin_cgo.go index 29732172aa6..ee59fefc06a 100644 --- a/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin_cgo.go +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin_cgo.go @@ -25,7 +25,7 @@ import ( // these CPU times for darwin is borrowed from influxdb/telegraf. -func perCPUTimes() ([]CPUTimesStat, error) { +func perCPUTimes() ([]TimesStat, error) { var ( count C.mach_msg_type_number_t cpuload *C.processor_cpu_load_info_data_t @@ -59,7 +59,7 @@ func perCPUTimes() ([]CPUTimesStat, error) { bbuf := bytes.NewBuffer(buf) - var ret []CPUTimesStat + var ret []TimesStat for i := 0; i < int(ncpu); i++ { err := binary.Read(bbuf, binary.LittleEndian, &cpu_ticks) @@ -67,7 +67,7 @@ func perCPUTimes() ([]CPUTimesStat, error) { return nil, err } - c := CPUTimesStat{ + c := TimesStat{ CPU: fmt.Sprintf("cpu%d", i), User: float64(cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec, System: float64(cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec, @@ -81,7 +81,7 @@ func perCPUTimes() ([]CPUTimesStat, error) { return ret, nil } -func allCPUTimes() ([]CPUTimesStat, error) { +func allCPUTimes() ([]TimesStat, error) { var count C.mach_msg_type_number_t = C.HOST_CPU_LOAD_INFO_COUNT var cpuload C.host_cpu_load_info_data_t @@ -94,7 +94,7 @@ func allCPUTimes() ([]CPUTimesStat, error) { return nil, fmt.Errorf("host_statistics error=%d", status) } - c := CPUTimesStat{ + c := TimesStat{ CPU: "cpu-total", User: float64(cpuload.cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec, System: float64(cpuload.cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec, @@ -102,6 +102,6 @@ func allCPUTimes() ([]CPUTimesStat, error) { Idle: float64(cpuload.cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec, } - return []CPUTimesStat{c}, nil + return []TimesStat{c}, nil } diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin_nocgo.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin_nocgo.go index 0b7d1f999f9..242b4a8e799 100644 --- a/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin_nocgo.go +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_darwin_nocgo.go @@ -5,10 +5,10 @@ package cpu import "github.com/shirou/gopsutil/internal/common" -func perCPUTimes() ([]CPUTimesStat, error) { - return []CPUTimesStat{}, common.NotImplementedError +func perCPUTimes() ([]TimesStat, error) { + return []TimesStat{}, common.ErrNotImplementedError } -func allCPUTimes() ([]CPUTimesStat, error) { - return []CPUTimesStat{}, common.NotImplementedError +func allCPUTimes() ([]TimesStat, error) { + return []TimesStat{}, common.ErrNotImplementedError } diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd.go index 5df67b3d4af..ce1adf3c16d 100644 --- a/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd.go +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_freebsd.go @@ -25,7 +25,11 @@ const ( var ClocksPerSec = float64(128) func init() { - out, err := exec.Command("/usr/bin/getconf", "CLK_TCK").Output() + getconf, err := exec.LookPath("/usr/bin/getconf") + if err != nil { + return + } + out, err := exec.Command(getconf, "CLK_TCK").Output() // ignore errors if err == nil { i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) @@ -35,14 +39,14 @@ func init() { } } -func CPUTimes(percpu bool) ([]CPUTimesStat, error) { - var ret []CPUTimesStat +func Times(percpu bool) ([]TimesStat, error) { + var ret []TimesStat var sysctlCall string var ncpu int if percpu { sysctlCall = "kern.cp_times" - ncpu, _ = CPUCounts(true) + ncpu, _ = Counts(true) } else { sysctlCall = "kern.cp_time" ncpu = 1 @@ -76,7 +80,7 @@ func CPUTimes(percpu bool) ([]CPUTimesStat, error) { return ret, err } - c := CPUTimesStat{ + c := TimesStat{ User: float64(user / ClocksPerSec), Nice: float64(nice / ClocksPerSec), System: float64(sys / ClocksPerSec), @@ -96,13 +100,13 @@ func CPUTimes(percpu bool) ([]CPUTimesStat, error) { } // Returns only one CPUInfoStat on FreeBSD -func CPUInfo() ([]CPUInfoStat, error) { +func Info() ([]InfoStat, error) { filename := "/var/run/dmesg.boot" lines, _ := common.ReadLines(filename) - var ret []CPUInfoStat + var ret []InfoStat - c := CPUInfoStat{} + c := InfoStat{} for _, line := range lines { if matches := regexp.MustCompile(`CPU:\s+(.+) \(([\d.]+).+\)`).FindStringSubmatch(line); matches != nil { c.ModelName = matches[1] diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_linux.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_linux.go index 8dbbd8536ca..975b75c0725 100644 --- a/vendor/github.com/shirou/gopsutil/cpu/cpu_linux.go +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_linux.go @@ -15,7 +15,11 @@ import ( var cpu_tick = float64(100) func init() { - out, err := exec.Command("/usr/bin/getconf", "CLK_TCK").Output() + getconf, err := exec.LookPath("/usr/bin/getconf") + if err != nil { + return + } + out, err := exec.Command(getconf, "CLK_TCK").Output() // ignore errors if err == nil { i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) @@ -25,7 +29,7 @@ func init() { } } -func CPUTimes(percpu bool) ([]CPUTimesStat, error) { +func Times(percpu bool) ([]TimesStat, error) { filename := common.HostProc("stat") var lines = []string{} if percpu { @@ -37,13 +41,13 @@ func CPUTimes(percpu bool) ([]CPUTimesStat, error) { break } lines = append(lines, line) - startIdx += 1 + startIdx++ } } else { lines, _ = common.ReadLinesOffsetN(filename, 0, 1) } - ret := make([]CPUTimesStat, 0, len(lines)) + ret := make([]TimesStat, 0, len(lines)) for _, line := range lines { ct, err := parseStatLine(line) @@ -56,13 +60,13 @@ func CPUTimes(percpu bool) ([]CPUTimesStat, error) { return ret, nil } -func sysCpuPath(cpu int32, relPath string) string { +func sysCPUPath(cpu int32, relPath string) string { return common.HostSys(fmt.Sprintf("devices/system/cpu/cpu%d", cpu), relPath) } -func finishCPUInfo(c *CPUInfoStat) error { +func finishCPUInfo(c *InfoStat) error { if c.Mhz == 0 { - lines, err := common.ReadLines(sysCpuPath(c.CPU, "cpufreq/cpuinfo_max_freq")) + lines, err := common.ReadLines(sysCPUPath(c.CPU, "cpufreq/cpuinfo_max_freq")) if err == nil { value, err := strconv.ParseFloat(lines[0], 64) if err != nil { @@ -72,7 +76,7 @@ func finishCPUInfo(c *CPUInfoStat) error { } } if len(c.CoreID) == 0 { - lines, err := common.ReadLines(sysCpuPath(c.CPU, "topology/core_id")) + lines, err := common.ReadLines(sysCPUPath(c.CPU, "topology/coreId")) if err == nil { c.CoreID = lines[0] } @@ -87,13 +91,13 @@ func finishCPUInfo(c *CPUInfoStat) error { // Sockets often come with many physical CPU cores. // For example a single socket board with two cores each with HT will // return 4 CPUInfoStat structs on Linux and the "Cores" field set to 1. -func CPUInfo() ([]CPUInfoStat, error) { +func Info() ([]InfoStat, error) { filename := common.HostProc("cpuinfo") lines, _ := common.ReadLines(filename) - var ret []CPUInfoStat + var ret []InfoStat - c := CPUInfoStat{CPU: -1, Cores: 1} + c := InfoStat{CPU: -1, Cores: 1} for _, line := range lines { fields := strings.Split(line, ":") if len(fields) < 2 { @@ -111,13 +115,13 @@ func CPUInfo() ([]CPUInfoStat, error) { } ret = append(ret, c) } - c = CPUInfoStat{Cores: 1} + c = InfoStat{Cores: 1} t, err := strconv.ParseInt(value, 10, 64) if err != nil { return ret, err } c.CPU = int32(t) - case "vendor_id": + case "vendorId", "vendor_id": c.VendorID = value case "cpu family": c.Family = value @@ -163,7 +167,7 @@ func CPUInfo() ([]CPUInfoStat, error) { return ret, nil } -func parseStatLine(line string) (*CPUTimesStat, error) { +func parseStatLine(line string) (*TimesStat, error) { fields := strings.Fields(line) if strings.HasPrefix(fields[0], "cpu") == false { @@ -204,7 +208,7 @@ func parseStatLine(line string) (*CPUTimesStat, error) { return nil, err } - ct := &CPUTimesStat{ + ct := &TimesStat{ CPU: cpu, User: float64(user) / cpu_tick, Nice: float64(nice) / cpu_tick, diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_unix.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_unix.go index bab79d8f4e8..9f1ea4d772a 100644 --- a/vendor/github.com/shirou/gopsutil/cpu/cpu_unix.go +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_unix.go @@ -7,14 +7,14 @@ import ( "time" ) -func CPUPercent(interval time.Duration, percpu bool) ([]float64, error) { - getAllBusy := func(t CPUTimesStat) (float64, float64) { +func Percent(interval time.Duration, percpu bool) ([]float64, error) { + getAllBusy := func(t TimesStat) (float64, float64) { busy := t.User + t.System + t.Nice + t.Iowait + t.Irq + t.Softirq + t.Steal + t.Guest + t.GuestNice + t.Stolen return busy + t.Idle, busy } - calculate := func(t1, t2 CPUTimesStat) float64 { + calculate := func(t1, t2 TimesStat) float64 { t1All, t1Busy := getAllBusy(t1) t2All, t2Busy := getAllBusy(t2) @@ -28,7 +28,7 @@ func CPUPercent(interval time.Duration, percpu bool) ([]float64, error) { } // Get CPU usage at the start of the interval. - cpuTimes1, err := CPUTimes(percpu) + cpuTimes1, err := Times(percpu) if err != nil { return nil, err } @@ -38,7 +38,7 @@ func CPUPercent(interval time.Duration, percpu bool) ([]float64, error) { } // And at the end of the interval. - cpuTimes2, err := CPUTimes(percpu) + cpuTimes2, err := Times(percpu) if err != nil { return nil, err } diff --git a/vendor/github.com/shirou/gopsutil/cpu/cpu_windows.go b/vendor/github.com/shirou/gopsutil/cpu/cpu_windows.go index 408d6a686aa..fbd25e6057d 100644 --- a/vendor/github.com/shirou/gopsutil/cpu/cpu_windows.go +++ b/vendor/github.com/shirou/gopsutil/cpu/cpu_windows.go @@ -19,14 +19,14 @@ type Win32_Processor struct { Manufacturer string Name string NumberOfLogicalProcessors uint32 - ProcessorId *string + ProcessorID *string Stepping *string MaxClockSpeed uint32 } // TODO: Get percpu -func CPUTimes(percpu bool) ([]CPUTimesStat, error) { - var ret []CPUTimesStat +func Times(percpu bool) ([]TimesStat, error) { + var ret []TimesStat var lpIdleTime common.FILETIME var lpKernelTime common.FILETIME @@ -46,7 +46,7 @@ func CPUTimes(percpu bool) ([]CPUTimesStat, error) { kernel := ((HIT * float64(lpKernelTime.DwHighDateTime)) + (LOT * float64(lpKernelTime.DwLowDateTime))) system := (kernel - idle) - ret = append(ret, CPUTimesStat{ + ret = append(ret, TimesStat{ Idle: float64(idle), User: float64(user), System: float64(system), @@ -54,8 +54,8 @@ func CPUTimes(percpu bool) ([]CPUTimesStat, error) { return ret, nil } -func CPUInfo() ([]CPUInfoStat, error) { - var ret []CPUInfoStat +func Info() ([]InfoStat, error) { + var ret []InfoStat var dst []Win32_Processor q := wmi.CreateQuery(&dst, "") err := wmi.Query(q, &dst) @@ -66,11 +66,11 @@ func CPUInfo() ([]CPUInfoStat, error) { var procID string for i, l := range dst { procID = "" - if l.ProcessorId != nil { - procID = *l.ProcessorId + if l.ProcessorID != nil { + procID = *l.ProcessorID } - cpu := CPUInfoStat{ + cpu := InfoStat{ CPU: int32(i), Family: fmt.Sprintf("%d", l.Family), VendorID: l.Manufacturer, @@ -86,7 +86,7 @@ func CPUInfo() ([]CPUInfoStat, error) { return ret, nil } -func CPUPercent(interval time.Duration, percpu bool) ([]float64, error) { +func Percent(interval time.Duration, percpu bool) ([]float64, error) { var ret []float64 var dst []Win32_Processor q := wmi.CreateQuery(&dst, "") diff --git a/vendor/github.com/shirou/gopsutil/host/host.go b/vendor/github.com/shirou/gopsutil/host/host.go index 241ceb23e37..150eb60a7d3 100644 --- a/vendor/github.com/shirou/gopsutil/host/host.go +++ b/vendor/github.com/shirou/gopsutil/host/host.go @@ -6,17 +6,17 @@ import ( // A HostInfoStat describes the host status. // This is not in the psutil but it useful. -type HostInfoStat struct { +type InfoStat struct { Hostname string `json:"hostname"` Uptime uint64 `json:"uptime"` - BootTime uint64 `json:"boot_time"` + BootTime uint64 `json:"bootTime"` Procs uint64 `json:"procs"` // number of processes OS string `json:"os"` // ex: freebsd, linux Platform string `json:"platform"` // ex: ubuntu, linuxmint - PlatformFamily string `json:"platform_family"` // ex: debian, rhel - PlatformVersion string `json:"platform_version"` - VirtualizationSystem string `json:"virtualization_system"` - VirtualizationRole string `json:"virtualization_role"` // guest or host + PlatformFamily string `json:"platformFamily"` // ex: debian, rhel + PlatformVersion string `json:"platformVersion"` + VirtualizationSystem string `json:"virtualizationSystem"` + VirtualizationRole string `json:"virtualizationRole"` // guest or host } @@ -27,7 +27,7 @@ type UserStat struct { Started int `json:"started"` } -func (h HostInfoStat) String() string { +func (h InfoStat) String() string { s, _ := json.Marshal(h) return string(s) } diff --git a/vendor/github.com/shirou/gopsutil/host/host_darwin.go b/vendor/github.com/shirou/gopsutil/host/host_darwin.go index 069c6fa37d7..f4a8c36a26f 100644 --- a/vendor/github.com/shirou/gopsutil/host/host_darwin.go +++ b/vendor/github.com/shirou/gopsutil/host/host_darwin.go @@ -17,8 +17,11 @@ import ( "github.com/shirou/gopsutil/internal/common" ) -func HostInfo() (*HostInfoStat, error) { - ret := &HostInfoStat{ +// from utmpx.h +const USER_PROCESS = 7 + +func Info() (*InfoStat, error) { + ret := &InfoStat{ OS: runtime.GOOS, PlatformFamily: "darwin", } @@ -28,13 +31,13 @@ func HostInfo() (*HostInfoStat, error) { ret.Hostname = hostname } - platform, family, version, err := GetPlatformInformation() + platform, family, version, err := PlatformInformation() if err == nil { ret.Platform = platform ret.PlatformFamily = family ret.PlatformVersion = version } - system, role, err := GetVirtualization() + system, role, err := Virtualization() if err == nil { ret.VirtualizationSystem = system ret.VirtualizationRole = role @@ -103,7 +106,7 @@ func Users() ([]UserStat, error) { if err != nil { continue } - if u.Type != 7 { // skip if not USERPROCESS + if u.Type != USER_PROCESS { continue } user := UserStat{ @@ -119,17 +122,21 @@ func Users() ([]UserStat, error) { } -func GetPlatformInformation() (string, string, string, error) { +func PlatformInformation() (string, string, string, error) { platform := "" family := "" version := "" - out, err := exec.Command("uname", "-s").Output() + uname, err := exec.LookPath("uname") + if err != nil { + return "", "", "", err + } + out, err := exec.Command(uname, "-s").Output() if err == nil { platform = strings.ToLower(strings.TrimSpace(string(out))) } - out, err = exec.Command("uname", "-r").Output() + out, err = exec.Command(uname, "-r").Output() if err == nil { version = strings.ToLower(strings.TrimSpace(string(out))) } @@ -137,7 +144,7 @@ func GetPlatformInformation() (string, string, string, error) { return platform, family, version, nil } -func GetVirtualization() (string, string, error) { +func Virtualization() (string, string, error) { system := "" role := "" diff --git a/vendor/github.com/shirou/gopsutil/host/host_darwin_amd64.go b/vendor/github.com/shirou/gopsutil/host/host_darwin_amd64.go index 3ea52d527ee..c3596f9f5e9 100644 --- a/vendor/github.com/shirou/gopsutil/host/host_darwin_amd64.go +++ b/vendor/github.com/shirou/gopsutil/host/host_darwin_amd64.go @@ -5,7 +5,7 @@ package host type Utmpx struct { User [256]int8 - Id [4]int8 + ID [4]int8 Line [32]int8 Pid int32 Type int16 diff --git a/vendor/github.com/shirou/gopsutil/host/host_freebsd.go b/vendor/github.com/shirou/gopsutil/host/host_freebsd.go index 4a151fd84fd..aeb1b45a5c8 100644 --- a/vendor/github.com/shirou/gopsutil/host/host_freebsd.go +++ b/vendor/github.com/shirou/gopsutil/host/host_freebsd.go @@ -23,8 +23,8 @@ const ( UTHostSize = 16 ) -func HostInfo() (*HostInfoStat, error) { - ret := &HostInfoStat{ +func Info() (*InfoStat, error) { + ret := &InfoStat{ OS: runtime.GOOS, PlatformFamily: "freebsd", } @@ -34,13 +34,13 @@ func HostInfo() (*HostInfoStat, error) { ret.Hostname = hostname } - platform, family, version, err := GetPlatformInformation() + platform, family, version, err := PlatformInformation() if err == nil { ret.Platform = platform ret.PlatformFamily = family ret.PlatformVersion = version } - system, role, err := GetVirtualization() + system, role, err := Virtualization() if err == nil { ret.VirtualizationSystem = system ret.VirtualizationRole = role @@ -101,13 +101,11 @@ func Users() ([]UserStat, error) { return ret, err } - u := Utmpx{} - entrySize := int(unsafe.Sizeof(u)) - 3 - entrySize = 197 // TODO: why should 197 + entrySize := sizeOfUtmpx count := len(buf) / entrySize for i := 0; i < count; i++ { - b := buf[i*entrySize : i*entrySize+entrySize] + b := buf[i*sizeOfUtmpx : (i+1)*sizeOfUtmpx] var u Utmpx br := bytes.NewReader(b) err := binary.Read(br, binary.LittleEndian, &u) @@ -129,17 +127,21 @@ func Users() ([]UserStat, error) { } -func GetPlatformInformation() (string, string, string, error) { +func PlatformInformation() (string, string, string, error) { platform := "" family := "" version := "" + uname, err := exec.LookPath("uname") + if err != nil { + return "", "", "", err + } - out, err := exec.Command("uname", "-s").Output() + out, err := exec.Command(uname, "-s").Output() if err == nil { platform = strings.ToLower(strings.TrimSpace(string(out))) } - out, err = exec.Command("uname", "-r").Output() + out, err = exec.Command(uname, "-r").Output() if err == nil { version = strings.ToLower(strings.TrimSpace(string(out))) } @@ -147,7 +149,7 @@ func GetPlatformInformation() (string, string, string, error) { return platform, family, version, nil } -func GetVirtualization() (string, string, error) { +func Virtualization() (string, string, error) { system := "" role := "" diff --git a/vendor/github.com/shirou/gopsutil/host/host_freebsd_386.go b/vendor/github.com/shirou/gopsutil/host/host_freebsd_386.go new file mode 100644 index 00000000000..7f06d8f8e25 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/host/host_freebsd_386.go @@ -0,0 +1,43 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package host + +const ( + sizeofPtr = 0x4 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x4 + sizeofLongLong = 0x8 + sizeOfUtmpx = 197 // TODO why should 197 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int32 + _C_long_long int64 +) + +type Utmp struct { + Line [8]int8 + Name [16]int8 + Host [16]int8 + Time int32 +} + +type Utmpx struct { + Type int16 + Tv Timeval + Id [8]int8 + Pid int32 + User [32]int8 + Line [16]int8 + Host [125]int8 + // X__ut_spare [64]int8 +} + +type Timeval struct { + Sec [4]byte + Usec [3]byte +} diff --git a/vendor/github.com/shirou/gopsutil/host/host_freebsd_amd64.go b/vendor/github.com/shirou/gopsutil/host/host_freebsd_amd64.go index 46be58688e3..3f015f0fb51 100644 --- a/vendor/github.com/shirou/gopsutil/host/host_freebsd_amd64.go +++ b/vendor/github.com/shirou/gopsutil/host/host_freebsd_amd64.go @@ -9,6 +9,7 @@ const ( sizeofInt = 0x4 sizeofLong = 0x8 sizeofLongLong = 0x8 + sizeOfUtmpx = 197 // TODO: why should 197, not 0x118 ) type ( @@ -24,6 +25,7 @@ type Utmp struct { Host [16]int8 Time int32 } + type Utmpx struct { Type int16 Tv Timeval @@ -32,9 +34,10 @@ type Utmpx struct { User [32]int8 Line [16]int8 Host [125]int8 - // Host [128]int8 - // X__ut_spare [64]int8 + // Host [128]int8 + // X__ut_spare [64]int8 } + type Timeval struct { Sec [4]byte Usec [3]byte diff --git a/vendor/github.com/shirou/gopsutil/host/host_linux.go b/vendor/github.com/shirou/gopsutil/host/host_linux.go index 91b153074df..14d09357d2a 100644 --- a/vendor/github.com/shirou/gopsutil/host/host_linux.go +++ b/vendor/github.com/shirou/gopsutil/host/host_linux.go @@ -14,7 +14,6 @@ import ( "strconv" "strings" "time" - "unsafe" "github.com/shirou/gopsutil/internal/common" ) @@ -26,8 +25,11 @@ type LSB struct { Description string } -func HostInfo() (*HostInfoStat, error) { - ret := &HostInfoStat{ +// from utmp.h +const USER_PROCESS = 7 + +func Info() (*InfoStat, error) { + ret := &InfoStat{ OS: runtime.GOOS, } @@ -36,13 +38,13 @@ func HostInfo() (*HostInfoStat, error) { ret.Hostname = hostname } - platform, family, version, err := GetPlatformInformation() + platform, family, version, err := PlatformInformation() if err == nil { ret.Platform = platform ret.PlatformFamily = family ret.PlatformVersion = version } - system, role, err := GetVirtualization() + system, role, err := Virtualization() if err == nil { ret.VirtualizationSystem = system ret.VirtualizationRole = role @@ -105,14 +107,12 @@ func Users() ([]UserStat, error) { return nil, err } - u := utmp{} - entrySize := int(unsafe.Sizeof(u)) - count := len(buf) / entrySize + count := len(buf) / sizeOfUtmp ret := make([]UserStat, 0, count) for i := 0; i < count; i++ { - b := buf[i*entrySize : i*entrySize+entrySize] + b := buf[i*sizeOfUtmp : (i+1)*sizeOfUtmp] var u utmp br := bytes.NewReader(b) @@ -120,11 +120,14 @@ func Users() ([]UserStat, error) { if err != nil { continue } + if u.Type != USER_PROCESS { + continue + } user := UserStat{ User: common.IntToString(u.User[:]), Terminal: common.IntToString(u.Line[:]), Host: common.IntToString(u.Host[:]), - Started: int(u.Tv.TvSec), + Started: int(u.Tv.Sec), } ret = append(ret, user) } @@ -135,8 +138,8 @@ func Users() ([]UserStat, error) { func getLSB() (*LSB, error) { ret := &LSB{} - if common.PathExists("/etc/lsb-release") { - contents, err := common.ReadLines("/etc/lsb-release") + if common.PathExists(common.HostEtc("lsb-release")) { + contents, err := common.ReadLines(common.HostEtc("lsb-release")) if err != nil { return ret, err // return empty } @@ -157,7 +160,11 @@ func getLSB() (*LSB, error) { } } } else if common.PathExists("/usr/bin/lsb_release") { - out, err := exec.Command("/usr/bin/lsb_release").Output() + lsb_release, err := exec.LookPath("/usr/bin/lsb_release") + if err != nil { + return ret, err + } + out, err := exec.Command(lsb_release).Output() if err != nil { return ret, err } @@ -183,26 +190,27 @@ func getLSB() (*LSB, error) { return ret, nil } -func GetPlatformInformation() (platform string, family string, version string, err error) { +func PlatformInformation() (platform string, family string, version string, err error) { lsb, err := getLSB() if err != nil { lsb = &LSB{} } - if common.PathExists("/etc/oracle-release") { + if common.PathExists(common.HostEtc("oracle-release")) { platform = "oracle" - contents, err := common.ReadLines("/etc/oracle-release") + contents, err := common.ReadLines(common.HostEtc("oracle-release")) if err == nil { version = getRedhatishVersion(contents) } - } else if common.PathExists("/etc/enterprise-release") { + + } else if common.PathExists(common.HostEtc("enterprise-release")) { platform = "oracle" - contents, err := common.ReadLines("/etc/enterprise-release") + contents, err := common.ReadLines(common.HostEtc("enterprise-release")) if err == nil { version = getRedhatishVersion(contents) } - } else if common.PathExists("/etc/debian_version") { + } else if common.PathExists(common.HostEtc("debian_version")) { if lsb.ID == "Ubuntu" { platform = "ubuntu" version = lsb.Release @@ -215,39 +223,39 @@ func GetPlatformInformation() (platform string, family string, version string, e } else { platform = "debian" } - contents, err := common.ReadLines("/etc/debian_version") + contents, err := common.ReadLines(common.HostEtc("debian_version")) if err == nil { version = contents[0] } } - } else if common.PathExists("/etc/redhat-release") { - contents, err := common.ReadLines("/etc/redhat-release") + } else if common.PathExists(common.HostEtc("redhat-release")) { + contents, err := common.ReadLines(common.HostEtc("redhat-release")) if err == nil { version = getRedhatishVersion(contents) platform = getRedhatishPlatform(contents) } - } else if common.PathExists("/etc/system-release") { - contents, err := common.ReadLines("/etc/system-release") + } else if common.PathExists(common.HostEtc("system-release")) { + contents, err := common.ReadLines(common.HostEtc("system-release")) if err == nil { version = getRedhatishVersion(contents) platform = getRedhatishPlatform(contents) } - } else if common.PathExists("/etc/gentoo-release") { + } else if common.PathExists(common.HostEtc("gentoo-release")) { platform = "gentoo" - contents, err := common.ReadLines("/etc/gentoo-release") + contents, err := common.ReadLines(common.HostEtc("gentoo-release")) if err == nil { version = getRedhatishVersion(contents) } - } else if common.PathExists("/etc/SuSE-release") { - contents, err := common.ReadLines("/etc/SuSE-release") + } else if common.PathExists(common.HostEtc("SuSE-release")) { + contents, err := common.ReadLines(common.HostEtc("SuSE-release")) if err == nil { version = getSuseVersion(contents) platform = getSusePlatform(contents) } // TODO: slackware detecion - } else if common.PathExists("/etc/arch-release") { + } else if common.PathExists(common.HostEtc("arch-release")) { platform = "arch" - // TODO: exherbo detection + version = lsb.Release } else if lsb.ID == "RedHat" { platform = "redhat" version = lsb.Release @@ -331,7 +339,7 @@ func getSusePlatform(contents []string) string { return "suse" } -func GetVirtualization() (string, string, error) { +func Virtualization() (string, string, error) { var system string var role string diff --git a/vendor/github.com/shirou/gopsutil/host/host_linux_386.go b/vendor/github.com/shirou/gopsutil/host/host_linux_386.go index fb6d7a0f616..79b5cb5d380 100644 --- a/vendor/github.com/shirou/gopsutil/host/host_linux_386.go +++ b/vendor/github.com/shirou/gopsutil/host/host_linux_386.go @@ -11,6 +11,7 @@ const ( sizeofInt = 0x4 sizeofLong = 0x4 sizeofLongLong = 0x8 + sizeOfUtmp = 0x180 ) type ( @@ -25,7 +26,7 @@ type utmp struct { Pad_cgo_0 [2]byte Pid int32 Line [32]int8 - Id [4]int8 + ID [4]int8 User [32]int8 Host [256]int8 Exit exit_status @@ -39,6 +40,6 @@ type exit_status struct { Exit int16 } type UtTv struct { - TvSec int32 - TvUsec int32 + Sec int32 + Usec int32 } diff --git a/vendor/github.com/shirou/gopsutil/host/host_linux_amd64.go b/vendor/github.com/shirou/gopsutil/host/host_linux_amd64.go index b04fc17e38c..9a69652f510 100644 --- a/vendor/github.com/shirou/gopsutil/host/host_linux_amd64.go +++ b/vendor/github.com/shirou/gopsutil/host/host_linux_amd64.go @@ -9,6 +9,7 @@ const ( sizeofInt = 0x4 sizeofLong = 0x8 sizeofLongLong = 0x8 + sizeOfUtmp = 0x180 ) type ( @@ -28,7 +29,7 @@ type utmp struct { Host [256]int8 Exit exit_status Session int32 - Tv UtTv + Tv _Ctype_struct___0 Addr_v6 [4]int32 X__glibc_reserved [20]int8 } @@ -36,7 +37,12 @@ type exit_status struct { Termination int16 Exit int16 } -type UtTv struct { - TvSec int32 - TvUsec int32 +type timeval struct { + Sec int64 + Usec int64 +} + +type _Ctype_struct___0 struct { + Sec int32 + Usec int32 } diff --git a/vendor/github.com/shirou/gopsutil/host/host_linux_arm.go b/vendor/github.com/shirou/gopsutil/host/host_linux_arm.go index 329e530f710..e2cf4485096 100644 --- a/vendor/github.com/shirou/gopsutil/host/host_linux_arm.go +++ b/vendor/github.com/shirou/gopsutil/host/host_linux_arm.go @@ -1,20 +1,21 @@ -// +build linux -// +build arm +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_linux.go | sed "s/uint8/int8/g" package host const ( - sizeofPtr = 0x8 + sizeofPtr = 0x4 sizeofShort = 0x2 sizeofInt = 0x4 - sizeofLong = 0x8 + sizeofLong = 0x4 sizeofLongLong = 0x8 + sizeOfUtmp = 0x180 ) type ( _C_short int16 _C_int int32 - _C_long int64 + _C_long int32 _C_long_long int64 ) @@ -28,7 +29,7 @@ type utmp struct { Host [256]int8 Exit exit_status Session int32 - Tv UtTv + Tv timeval Addr_v6 [4]int32 X__glibc_reserved [20]int8 } @@ -36,7 +37,7 @@ type exit_status struct { Termination int16 Exit int16 } -type UtTv struct { - TvSec int32 - TvUsec int32 +type timeval struct { + Sec int32 + Usec int32 } diff --git a/vendor/github.com/shirou/gopsutil/host/host_linux_arm64.go b/vendor/github.com/shirou/gopsutil/host/host_linux_arm64.go new file mode 100644 index 00000000000..37dbe5c8c30 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/host/host_linux_arm64.go @@ -0,0 +1,43 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_linux.go + +package host + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 + sizeOfUtmp = 0x180 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type utmp struct { + Type int16 + Pad_cgo_0 [2]byte + Pid int32 + Line [32]int8 + Id [4]int8 + User [32]int8 + Host [256]int8 + Exit exit_status + Session int32 + Tv timeval + Addr_v6 [4]int32 + X__glibc_reserved [20]int8 +} +type exit_status struct { + Termination int16 + Exit int16 +} +type timeval struct { + Sec int64 + Usec int64 +} diff --git a/vendor/github.com/shirou/gopsutil/host/host_linux_ppc64le.go b/vendor/github.com/shirou/gopsutil/host/host_linux_ppc64le.go new file mode 100644 index 00000000000..37dbe5c8c30 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/host/host_linux_ppc64le.go @@ -0,0 +1,43 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_linux.go + +package host + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 + sizeOfUtmp = 0x180 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type utmp struct { + Type int16 + Pad_cgo_0 [2]byte + Pid int32 + Line [32]int8 + Id [4]int8 + User [32]int8 + Host [256]int8 + Exit exit_status + Session int32 + Tv timeval + Addr_v6 [4]int32 + X__glibc_reserved [20]int8 +} +type exit_status struct { + Termination int16 + Exit int16 +} +type timeval struct { + Sec int64 + Usec int64 +} diff --git a/vendor/github.com/shirou/gopsutil/host/host_windows.go b/vendor/github.com/shirou/gopsutil/host/host_windows.go index 13890bf2b24..29f900c6e27 100644 --- a/vendor/github.com/shirou/gopsutil/host/host_windows.go +++ b/vendor/github.com/shirou/gopsutil/host/host_windows.go @@ -28,8 +28,8 @@ type Win32_OperatingSystem struct { LastBootUpTime time.Time } -func HostInfo() (*HostInfoStat, error) { - ret := &HostInfoStat{ +func Info() (*InfoStat, error) { + ret := &InfoStat{ OS: runtime.GOOS, } @@ -38,7 +38,7 @@ func HostInfo() (*HostInfoStat, error) { ret.Hostname = hostname } - platform, family, version, err := GetPlatformInformation() + platform, family, version, err := PlatformInformation() if err == nil { ret.Platform = platform ret.PlatformFamily = family @@ -100,7 +100,7 @@ func Uptime() (uint64, error) { return uptime(boot), nil } -func GetPlatformInformation() (platform string, family string, version string, err error) { +func PlatformInformation() (platform string, family string, version string, err error) { if osInfo == nil { _, err = GetOSInfo() if err != nil { diff --git a/vendor/github.com/shirou/gopsutil/host/types_freebsd.go b/vendor/github.com/shirou/gopsutil/host/types_freebsd.go index 113b22eeaf3..e70677f6813 100644 --- a/vendor/github.com/shirou/gopsutil/host/types_freebsd.go +++ b/vendor/github.com/shirou/gopsutil/host/types_freebsd.go @@ -27,6 +27,7 @@ const ( sizeofInt = C.sizeof_int sizeofLong = C.sizeof_long sizeofLongLong = C.sizeof_longlong + sizeOfUtmpx = C.sizeof_struct_utmpx ) // Basic types diff --git a/vendor/github.com/shirou/gopsutil/host/types_linux.go b/vendor/github.com/shirou/gopsutil/host/types_linux.go index 928545515c3..8adecb6cfa9 100644 --- a/vendor/github.com/shirou/gopsutil/host/types_linux.go +++ b/vendor/github.com/shirou/gopsutil/host/types_linux.go @@ -7,12 +7,11 @@ Input to cgo -godefs. package host /* -#define KERNEL #include #include enum { - sizeofPtr = sizeof(void*), + sizeofPtr = sizeof(void*), }; */ @@ -26,6 +25,7 @@ const ( sizeofInt = C.sizeof_int sizeofLong = C.sizeof_long sizeofLongLong = C.sizeof_longlong + sizeOfUtmp = C.sizeof_struct_utmp ) // Basic types @@ -39,7 +39,4 @@ type ( type utmp C.struct_utmp type exit_status C.struct_exit_status -type UtTv struct { - TvSec int32 - TvUsec int32 -} +type timeval C.struct_timeval diff --git a/vendor/github.com/shirou/gopsutil/process/binary.go b/vendor/github.com/shirou/gopsutil/internal/common/binary.go similarity index 99% rename from vendor/github.com/shirou/gopsutil/process/binary.go rename to vendor/github.com/shirou/gopsutil/internal/common/binary.go index 8891f0f14c7..9b5dc55b49a 100644 --- a/vendor/github.com/shirou/gopsutil/process/binary.go +++ b/vendor/github.com/shirou/gopsutil/internal/common/binary.go @@ -1,3 +1,5 @@ +package common + // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -19,8 +21,6 @@ // high-performance serialization, especially for large data structures, // should look at more advanced solutions such as the encoding/gob // package or protocol buffers. -package process - import ( "errors" "io" diff --git a/vendor/github.com/shirou/gopsutil/internal/common/common.go b/vendor/github.com/shirou/gopsutil/internal/common/common.go index 3449cdd0e8a..e190f4d13b3 100644 --- a/vendor/github.com/shirou/gopsutil/internal/common/common.go +++ b/vendor/github.com/shirou/gopsutil/internal/common/common.go @@ -1,11 +1,11 @@ +package common + // // gopsutil is a port of psutil(http://pythonhosted.org/psutil/). // This covers these architectures. // - linux (amd64, arm) // - freebsd (amd64) // - windows (amd64) -package common - import ( "bufio" "errors" @@ -59,12 +59,11 @@ func (i FakeInvoke) Command(name string, arg ...string) ([]byte, error) { } if PathExists(fpath) { return ioutil.ReadFile(fpath) - } else { - return exec.Command(name, arg...).Output() } + return exec.Command(name, arg...).Output() } -var NotImplementedError = errors.New("not implemented yet") +var ErrNotImplementedError = errors.New("not implemented yet") // ReadLines reads contents from a file and splits them by new lines. // A convenience wrapper to ReadLinesOffsetN(filename, 0, -1). @@ -118,6 +117,23 @@ func IntToString(orig []int8) string { return string(ret[0:size]) } +func UintToString(orig []uint8) string { + ret := make([]byte, len(orig)) + size := -1 + for i, o := range orig { + if o == 0 { + size = i + break + } + ret[i] = byte(o) + } + if size == -1 { + size = len(orig) + } + + return string(ret[0:size]) +} + func ByteToString(orig []byte) string { n := -1 l := -1 @@ -186,7 +202,7 @@ func mustParseFloat64(val string) float64 { return vv } -// StringsHas checks the target string slice containes src or not +// StringsHas checks the target string slice contains src or not func StringsHas(target []string, src string) bool { for _, t := range target { if strings.TrimSpace(t) == src { @@ -206,6 +222,16 @@ func StringsContains(target []string, src string) bool { return false } +// IntContains checks the src in any int of the target int slice. +func IntContains(target []int, src int) bool { + for _, t := range target { + if src == t { + return true + } + } + return false +} + // get struct attributes. // This method is used only for debugging platform dependent code. func attributes(m interface{}) map[string]reflect.Type { @@ -236,7 +262,7 @@ func PathExists(filename string) bool { return false } -//GetEnv retreives the environment variable key. If it does not exist it returns the default. +//GetEnv retrieves the environment variable key. If it does not exist it returns the default. func GetEnv(key string, dfault string, combineWith ...string) string { value := os.Getenv(key) if value == "" { @@ -264,3 +290,7 @@ func HostProc(combineWith ...string) string { func HostSys(combineWith ...string) string { return GetEnv("HOST_SYS", "/sys", combineWith...) } + +func HostEtc(combineWith ...string) string { + return GetEnv("HOST_ETC", "/etc", combineWith...) +} diff --git a/vendor/github.com/shirou/gopsutil/internal/common/common_darwin.go b/vendor/github.com/shirou/gopsutil/internal/common/common_darwin.go index 7d6f3c69269..2e1552aeee3 100644 --- a/vendor/github.com/shirou/gopsutil/internal/common/common_darwin.go +++ b/vendor/github.com/shirou/gopsutil/internal/common/common_darwin.go @@ -3,6 +3,7 @@ package common import ( + "os" "os/exec" "strings" "syscall" @@ -10,7 +11,16 @@ import ( ) func DoSysctrl(mib string) ([]string, error) { - out, err := exec.Command("/usr/sbin/sysctl", "-n", mib).Output() + err := os.Setenv("LC_ALL", "C") + if err != nil { + return []string{}, err + } + + sysctl, err := exec.LookPath("/usr/sbin/sysctl") + if err != nil { + return []string{}, err + } + out, err := exec.Command(sysctl, "-n", mib).Output() if err != nil { return []string{}, err } diff --git a/vendor/github.com/shirou/gopsutil/internal/common/common_freebsd.go b/vendor/github.com/shirou/gopsutil/internal/common/common_freebsd.go index 8ccd40e90e9..dfdcebd8d7d 100644 --- a/vendor/github.com/shirou/gopsutil/internal/common/common_freebsd.go +++ b/vendor/github.com/shirou/gopsutil/internal/common/common_freebsd.go @@ -3,6 +3,7 @@ package common import ( + "os" "os/exec" "strings" "syscall" @@ -10,7 +11,15 @@ import ( ) func DoSysctrl(mib string) ([]string, error) { - out, err := exec.Command("/sbin/sysctl", "-n", mib).Output() + err := os.Setenv("LC_ALL", "C") + if err != nil { + return []string{}, err + } + sysctl, err := exec.LookPath("/sbin/sysctl") + if err != nil { + return []string{}, err + } + out, err := exec.Command(sysctl, "-n", mib).Output() if err != nil { return []string{}, err } @@ -22,13 +31,14 @@ func DoSysctrl(mib string) ([]string, error) { } func CallSyscall(mib []int32) ([]byte, uint64, error) { + mibptr := unsafe.Pointer(&mib[0]) miblen := uint64(len(mib)) // get required buffer size length := uint64(0) _, _, err := syscall.Syscall6( syscall.SYS___SYSCTL, - uintptr(unsafe.Pointer(&mib[0])), + uintptr(mibptr), uintptr(miblen), 0, uintptr(unsafe.Pointer(&length)), @@ -46,7 +56,7 @@ func CallSyscall(mib []int32) ([]byte, uint64, error) { buf := make([]byte, length) _, _, err = syscall.Syscall6( syscall.SYS___SYSCTL, - uintptr(unsafe.Pointer(&mib[0])), + uintptr(mibptr), uintptr(miblen), uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&length)), diff --git a/vendor/github.com/shirou/gopsutil/mem/mem.go b/vendor/github.com/shirou/gopsutil/mem/mem.go index 67f8741e7c9..5f122d11b15 100644 --- a/vendor/github.com/shirou/gopsutil/mem/mem.go +++ b/vendor/github.com/shirou/gopsutil/mem/mem.go @@ -4,25 +4,51 @@ import ( "encoding/json" ) +// Memory usage statistics. Total, Available and Used contain numbers of bytes +// for human consumption. +// +// The other fields in this struct contain kernel specific values. type VirtualMemoryStat struct { - Total uint64 `json:"total"` - Available uint64 `json:"available"` - Used uint64 `json:"used"` - UsedPercent float64 `json:"used_percent"` - Free uint64 `json:"free"` - Active uint64 `json:"active"` - Inactive uint64 `json:"inactive"` - Buffers uint64 `json:"buffers"` - Cached uint64 `json:"cached"` - Wired uint64 `json:"wired"` - Shared uint64 `json:"shared"` + // Total amount of RAM on this system + Total uint64 `json:"total"` + + // RAM available for programs to allocate + // + // This value is computed from the kernel specific values. + Available uint64 `json:"available"` + + // RAM used by programs + // + // This value is computed from the kernel specific values. + Used uint64 `json:"used"` + + // Percentage of RAM used by programs + // + // This value is computed from the kernel specific values. + UsedPercent float64 `json:"usedPercent"` + + // This is the kernel's notion of free memory; RAM chips whose bits nobody + // cares about the value of right now. For a human consumable number, + // Available is what you really want. + Free uint64 `json:"free"` + + // OS X / BSD specific numbers: + // http://www.macyourself.com/2010/02/17/what-is-free-wired-active-and-inactive-system-memory-ram/ + Active uint64 `json:"active"` + Inactive uint64 `json:"inactive"` + Wired uint64 `json:"wired"` + + // Linux specific numbers + // https://www.centos.org/docs/5/html/5.1/Deployment_Guide/s2-proc-meminfo.html + Buffers uint64 `json:"buffers"` + Cached uint64 `json:"cached"` } type SwapMemoryStat struct { Total uint64 `json:"total"` Used uint64 `json:"used"` Free uint64 `json:"free"` - UsedPercent float64 `json:"used_percent"` + UsedPercent float64 `json:"usedPercent"` Sin uint64 `json:"sin"` Sout uint64 `json:"sout"` } diff --git a/vendor/github.com/shirou/gopsutil/mem/mem_darwin.go b/vendor/github.com/shirou/gopsutil/mem/mem_darwin.go index ba4dbd826a7..922b05cb4f6 100644 --- a/vendor/github.com/shirou/gopsutil/mem/mem_darwin.go +++ b/vendor/github.com/shirou/gopsutil/mem/mem_darwin.go @@ -3,111 +3,27 @@ package mem import ( - "os/exec" + "encoding/binary" "strconv" "strings" + "syscall" "github.com/shirou/gopsutil/internal/common" ) -func getPageSize() (uint64, error) { - out, err := exec.Command("pagesize").Output() +func getHwMemsize() (uint64, error) { + totalString, err := syscall.Sysctl("hw.memsize") if err != nil { return 0, err } - o := strings.TrimSpace(string(out)) - p, err := strconv.ParseUint(o, 10, 64) - if err != nil { - return 0, err - } - return p, nil -} - -// Runs vm_stat and returns Free and inactive pages -func getVmStat(pagesize uint64, vms *VirtualMemoryStat) error { - out, err := exec.Command("vm_stat").Output() - if err != nil { - return err - } - return parseVmStat(string(out), pagesize, vms) -} - -func parseVmStat(out string, pagesize uint64, vms *VirtualMemoryStat) error { - var err error - lines := strings.Split(out, "\n") - for _, line := range lines { - fields := strings.Split(line, ":") - if len(fields) < 2 { - continue - } - key := strings.TrimSpace(fields[0]) - value := strings.Trim(fields[1], " .") - switch key { - case "Pages free": - free, e := strconv.ParseUint(value, 10, 64) - if e != nil { - err = e - } - vms.Free = free * pagesize - case "Pages inactive": - inactive, e := strconv.ParseUint(value, 10, 64) - if e != nil { - err = e - } - vms.Cached += inactive * pagesize - vms.Inactive = inactive * pagesize - case "Pages active": - active, e := strconv.ParseUint(value, 10, 64) - if e != nil { - err = e - } - vms.Active = active * pagesize - case "Pages wired down": - wired, e := strconv.ParseUint(value, 10, 64) - if e != nil { - err = e - } - vms.Wired = wired * pagesize - case "Pages purgeable": - purgeable, e := strconv.ParseUint(value, 10, 64) - if e != nil { - err = e - } - vms.Cached += purgeable * pagesize - } - } - return err -} + // syscall.sysctl() helpfully assumes the result is a null-terminated string and + // removes the last byte of the result if it's 0 :/ + totalString += "\x00" -// VirtualMemory returns VirtualmemoryStat. -func VirtualMemory() (*VirtualMemoryStat, error) { - ret := &VirtualMemoryStat{} + total := uint64(binary.LittleEndian.Uint64([]byte(totalString))) - p, err := getPageSize() - if err != nil { - return nil, err - } - t, err := common.DoSysctrl("hw.memsize") - if err != nil { - return nil, err - } - total, err := strconv.ParseUint(t[0], 10, 64) - if err != nil { - return nil, err - } - err = getVmStat(p, ret) - if err != nil { - return nil, err - } - - ret.Available = ret.Free + ret.Cached - ret.Total = total - - ret.Used = ret.Total - ret.Free - ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0 - - return ret, nil + return total, nil } // SwapMemory returns swapinfo. diff --git a/vendor/github.com/shirou/gopsutil/mem/mem_darwin_cgo.go b/vendor/github.com/shirou/gopsutil/mem/mem_darwin_cgo.go new file mode 100644 index 00000000000..461631976e8 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/mem/mem_darwin_cgo.go @@ -0,0 +1,53 @@ +// +build darwin +// +build cgo + +package mem + +/* +#include +*/ +import "C" + +import ( + "fmt" + "syscall" + "unsafe" +) + +// VirtualMemory returns VirtualmemoryStat. +func VirtualMemory() (*VirtualMemoryStat, error) { + count := C.mach_msg_type_number_t(C.HOST_VM_INFO_COUNT) + var vmstat C.vm_statistics_data_t + + status := C.host_statistics(C.host_t(C.mach_host_self()), + C.HOST_VM_INFO, + C.host_info_t(unsafe.Pointer(&vmstat)), + &count) + + if status != C.KERN_SUCCESS { + return nil, fmt.Errorf("host_statistics error=%d", status) + } + + pageSize := uint64(syscall.Getpagesize()) + total, err := getHwMemsize() + if err != nil { + return nil, err + } + totalCount := C.natural_t(total / pageSize) + + availableCount := vmstat.inactive_count + vmstat.free_count + usedPercent := 100 * float64(totalCount-availableCount) / float64(totalCount) + + usedCount := totalCount - availableCount + + return &VirtualMemoryStat{ + Total: total, + Available: pageSize * uint64(availableCount), + Used: pageSize * uint64(usedCount), + UsedPercent: usedPercent, + Free: pageSize * uint64(vmstat.free_count), + Active: pageSize * uint64(vmstat.active_count), + Inactive: pageSize * uint64(vmstat.inactive_count), + Wired: pageSize * uint64(vmstat.wire_count), + }, nil +} diff --git a/vendor/github.com/shirou/gopsutil/mem/mem_darwin_nocgo.go b/vendor/github.com/shirou/gopsutil/mem/mem_darwin_nocgo.go new file mode 100644 index 00000000000..7094802d9a1 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/mem/mem_darwin_nocgo.go @@ -0,0 +1,88 @@ +// +build darwin +// +build !cgo + +package mem + +import ( + "os/exec" + "strconv" + "strings" + "syscall" +) + +// Runs vm_stat and returns Free and inactive pages +func getVMStat(vms *VirtualMemoryStat) error { + vm_stat, err := exec.LookPath("vm_stat") + if err != nil { + return err + } + out, err := exec.Command(vm_stat).Output() + if err != nil { + return err + } + return parseVMStat(string(out), vms) +} + +func parseVMStat(out string, vms *VirtualMemoryStat) error { + var err error + + lines := strings.Split(out, "\n") + pagesize := uint64(syscall.Getpagesize()) + for _, line := range lines { + fields := strings.Split(line, ":") + if len(fields) < 2 { + continue + } + key := strings.TrimSpace(fields[0]) + value := strings.Trim(fields[1], " .") + switch key { + case "Pages free": + free, e := strconv.ParseUint(value, 10, 64) + if e != nil { + err = e + } + vms.Free = free * pagesize + case "Pages inactive": + inactive, e := strconv.ParseUint(value, 10, 64) + if e != nil { + err = e + } + vms.Inactive = inactive * pagesize + case "Pages active": + active, e := strconv.ParseUint(value, 10, 64) + if e != nil { + err = e + } + vms.Active = active * pagesize + case "Pages wired down": + wired, e := strconv.ParseUint(value, 10, 64) + if e != nil { + err = e + } + vms.Wired = wired * pagesize + } + } + return err +} + +// VirtualMemory returns VirtualmemoryStat. +func VirtualMemory() (*VirtualMemoryStat, error) { + ret := &VirtualMemoryStat{} + + total, err := getHwMemsize() + if err != nil { + return nil, err + } + err = getVMStat(ret) + if err != nil { + return nil, err + } + + ret.Available = ret.Free + ret.Inactive + ret.Total = total + + ret.Used = ret.Total - ret.Available + ret.UsedPercent = 100 * float64(ret.Used) / float64(ret.Total) + + return ret, nil +} diff --git a/vendor/github.com/shirou/gopsutil/mem/mem_freebsd.go b/vendor/github.com/shirou/gopsutil/mem/mem_freebsd.go index 0cb33abc41f..71940576748 100644 --- a/vendor/github.com/shirou/gopsutil/mem/mem_freebsd.go +++ b/vendor/github.com/shirou/gopsutil/mem/mem_freebsd.go @@ -3,10 +3,11 @@ package mem import ( + "errors" "os/exec" "strconv" "strings" - "errors" + "github.com/shirou/gopsutil/internal/common" ) @@ -78,8 +79,8 @@ func VirtualMemory() (*VirtualMemoryStat, error) { } ret.Available = ret.Inactive + ret.Cached + ret.Free - ret.Used = ret.Active + ret.Wired + ret.Cached - ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0 + ret.Used = ret.Total - ret.Available + ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0 return ret, nil } @@ -87,7 +88,12 @@ func VirtualMemory() (*VirtualMemoryStat, error) { // Return swapinfo // FreeBSD can have multiple swap devices. but use only first device func SwapMemory() (*SwapMemoryStat, error) { - out, err := exec.Command("swapinfo").Output() + swapinfo, err := exec.LookPath("swapinfo") + if err != nil { + return nil, err + } + + out, err := exec.Command(swapinfo).Output() if err != nil { return nil, err } diff --git a/vendor/github.com/shirou/gopsutil/mem/mem_linux.go b/vendor/github.com/shirou/gopsutil/mem/mem_linux.go index fc9226219d7..899da83dffb 100644 --- a/vendor/github.com/shirou/gopsutil/mem/mem_linux.go +++ b/vendor/github.com/shirou/gopsutil/mem/mem_linux.go @@ -51,7 +51,7 @@ func VirtualMemory() (*VirtualMemoryStat, error) { if !memavail { ret.Available = ret.Free + ret.Buffers + ret.Cached } - ret.Used = ret.Total - ret.Free + ret.Used = ret.Total - ret.Available ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0 return ret, nil diff --git a/vendor/github.com/shirou/gopsutil/mem/mem_windows.go b/vendor/github.com/shirou/gopsutil/mem/mem_windows.go index 2696da58e97..045af49e39d 100644 --- a/vendor/github.com/shirou/gopsutil/mem/mem_windows.go +++ b/vendor/github.com/shirou/gopsutil/mem/mem_windows.go @@ -13,7 +13,7 @@ var ( procGlobalMemoryStatusEx = common.Modkernel32.NewProc("GlobalMemoryStatusEx") ) -type MEMORYSTATUSEX struct { +type memoryStatusEx struct { cbSize uint32 dwMemoryLoad uint32 ullTotalPhys uint64 // in bytes @@ -26,7 +26,7 @@ type MEMORYSTATUSEX struct { } func VirtualMemory() (*VirtualMemoryStat, error) { - var memInfo MEMORYSTATUSEX + var memInfo memoryStatusEx memInfo.cbSize = uint32(unsafe.Sizeof(memInfo)) mem, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memInfo))) if mem == 0 { diff --git a/vendor/github.com/shirou/gopsutil/net/net.go b/vendor/github.com/shirou/gopsutil/net/net.go index 66d5991bad2..60f1069c856 100644 --- a/vendor/github.com/shirou/gopsutil/net/net.go +++ b/vendor/github.com/shirou/gopsutil/net/net.go @@ -17,12 +17,12 @@ func init() { invoke = common.Invoke{} } -type NetIOCountersStat struct { +type IOCountersStat struct { Name string `json:"name"` // interface name - BytesSent uint64 `json:"bytes_sent"` // number of bytes sent - BytesRecv uint64 `json:"bytes_recv"` // number of bytes received - PacketsSent uint64 `json:"packets_sent"` // number of packets sent - PacketsRecv uint64 `json:"packets_recv"` // number of packets received + BytesSent uint64 `json:"bytesSent"` // number of bytes sent + BytesRecv uint64 `json:"bytesRecv"` // number of bytes received + PacketsSent uint64 `json:"packetsSent"` // number of packets sent + PacketsRecv uint64 `json:"packetsRecv"` // number of packets received Errin uint64 `json:"errin"` // total number of errors while receiving Errout uint64 `json:"errout"` // total number of errors while sending Dropin uint64 `json:"dropin"` // total number of incoming packets which were dropped @@ -35,7 +35,7 @@ type Addr struct { Port uint32 `json:"port"` } -type NetConnectionStat struct { +type ConnectionStat struct { Fd uint32 `json:"fd"` Family uint32 `json:"family"` Type uint32 `json:"type"` @@ -46,27 +46,27 @@ type NetConnectionStat struct { } // System wide stats about different network protocols -type NetProtoCountersStat struct { +type ProtoCountersStat struct { Protocol string `json:"protocol"` Stats map[string]int64 `json:"stats"` } // NetInterfaceAddr is designed for represent interface addresses -type NetInterfaceAddr struct { +type InterfaceAddr struct { Addr string `json:"addr"` } -type NetInterfaceStat struct { - MTU int `json:"mtu"` // maximum transmission unit - Name string `json:"name"` // e.g., "en0", "lo0", "eth0.100" - HardwareAddr string `json:"hardwareaddr"` // IEEE MAC-48, EUI-48 and EUI-64 form - Flags []string `json:"flags"` // e.g., FlagUp, FlagLoopback, FlagMulticast - Addrs []NetInterfaceAddr `json:"addrs"` +type InterfaceStat struct { + MTU int `json:"mtu"` // maximum transmission unit + Name string `json:"name"` // e.g., "en0", "lo0", "eth0.100" + HardwareAddr string `json:"hardwareaddr"` // IEEE MAC-48, EUI-48 and EUI-64 form + Flags []string `json:"flags"` // e.g., FlagUp, FlagLoopback, FlagMulticast + Addrs []InterfaceAddr `json:"addrs"` } -type NetFilterStat struct { - ConnTrackCount int64 `json:"conntrack_count"` - ConnTrackMax int64 `json:"conntrack_max"` +type FilterStat struct { + ConnTrackCount int64 `json:"conntrackCount"` + ConnTrackMax int64 `json:"conntrackMax"` } var constMap = map[string]int{ @@ -76,17 +76,17 @@ var constMap = map[string]int{ "IPv6": syscall.AF_INET6, } -func (n NetIOCountersStat) String() string { +func (n IOCountersStat) String() string { s, _ := json.Marshal(n) return string(s) } -func (n NetConnectionStat) String() string { +func (n ConnectionStat) String() string { s, _ := json.Marshal(n) return string(s) } -func (n NetProtoCountersStat) String() string { +func (n ProtoCountersStat) String() string { s, _ := json.Marshal(n) return string(s) } @@ -96,22 +96,22 @@ func (a Addr) String() string { return string(s) } -func (n NetInterfaceStat) String() string { +func (n InterfaceStat) String() string { s, _ := json.Marshal(n) return string(s) } -func (n NetInterfaceAddr) String() string { +func (n InterfaceAddr) String() string { s, _ := json.Marshal(n) return string(s) } -func NetInterfaces() ([]NetInterfaceStat, error) { +func Interfaces() ([]InterfaceStat, error) { is, err := net.Interfaces() if err != nil { return nil, err } - ret := make([]NetInterfaceStat, 0, len(is)) + ret := make([]InterfaceStat, 0, len(is)) for _, ifi := range is { var flags []string @@ -131,7 +131,7 @@ func NetInterfaces() ([]NetInterfaceStat, error) { flags = append(flags, "multicast") } - r := NetInterfaceStat{ + r := InterfaceStat{ Name: ifi.Name, MTU: ifi.MTU, HardwareAddr: ifi.HardwareAddr.String(), @@ -139,9 +139,9 @@ func NetInterfaces() ([]NetInterfaceStat, error) { } addrs, err := ifi.Addrs() if err == nil { - r.Addrs = make([]NetInterfaceAddr, 0, len(addrs)) + r.Addrs = make([]InterfaceAddr, 0, len(addrs)) for _, addr := range addrs { - r.Addrs = append(r.Addrs, NetInterfaceAddr{ + r.Addrs = append(r.Addrs, InterfaceAddr{ Addr: addr.String(), }) } @@ -153,8 +153,8 @@ func NetInterfaces() ([]NetInterfaceStat, error) { return ret, nil } -func getNetIOCountersAll(n []NetIOCountersStat) ([]NetIOCountersStat, error) { - r := NetIOCountersStat{ +func getIOCountersAll(n []IOCountersStat) ([]IOCountersStat, error) { + r := IOCountersStat{ Name: "all", } for _, nic := range n { @@ -168,38 +168,38 @@ func getNetIOCountersAll(n []NetIOCountersStat) ([]NetIOCountersStat, error) { r.Dropout += nic.Dropout } - return []NetIOCountersStat{r}, nil + return []IOCountersStat{r}, nil } -func parseNetLine(line string) (NetConnectionStat, error) { +func parseNetLine(line string) (ConnectionStat, error) { f := strings.Fields(line) if len(f) < 9 { - return NetConnectionStat{}, fmt.Errorf("wrong line,%s", line) + return ConnectionStat{}, fmt.Errorf("wrong line,%s", line) } pid, err := strconv.Atoi(f[1]) if err != nil { - return NetConnectionStat{}, err + return ConnectionStat{}, err } fd, err := strconv.Atoi(strings.Trim(f[3], "u")) if err != nil { - return NetConnectionStat{}, fmt.Errorf("unknown fd, %s", f[3]) + return ConnectionStat{}, fmt.Errorf("unknown fd, %s", f[3]) } netFamily, ok := constMap[f[4]] if !ok { - return NetConnectionStat{}, fmt.Errorf("unknown family, %s", f[4]) + return ConnectionStat{}, fmt.Errorf("unknown family, %s", f[4]) } netType, ok := constMap[f[7]] if !ok { - return NetConnectionStat{}, fmt.Errorf("unknown type, %s", f[7]) + return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[7]) } laddr, raddr, err := parseNetAddr(f[8]) if err != nil { - return NetConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s", f[8]) + return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s", f[8]) } - n := NetConnectionStat{ + n := ConnectionStat{ Fd: uint32(fd), Family: uint32(netFamily), Type: uint32(netType), diff --git a/vendor/github.com/shirou/gopsutil/net/net_darwin.go b/vendor/github.com/shirou/gopsutil/net/net_darwin.go index 547286b31ef..4fa358a38c9 100644 --- a/vendor/github.com/shirou/gopsutil/net/net_darwin.go +++ b/vendor/github.com/shirou/gopsutil/net/net_darwin.go @@ -16,14 +16,18 @@ import ( // lo0 16384 869107 0 169411755 869107 0 169411755 0 0 // lo0 16384 ::1/128 ::1 869107 - 169411755 869107 - 169411755 - - // lo0 16384 127 127.0.0.1 869107 - 169411755 869107 - 169411755 - - -func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { - out, err := exec.Command("/usr/sbin/netstat", "-ibdnW").Output() +func IOCounters(pernic bool) ([]IOCountersStat, error) { + netstat, err := exec.LookPath("/usr/sbin/netstat") + if err != nil { + return nil, err + } + out, err := exec.Command(netstat, "-ibdnW").Output() if err != nil { return nil, err } lines := strings.Split(string(out), "\n") - ret := make([]NetIOCountersStat, 0, len(lines)-1) + ret := make([]IOCountersStat, 0, len(lines)-1) exists := make([]string, 0, len(ret)) for _, line := range lines { @@ -70,7 +74,7 @@ func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { parsed = append(parsed, t) } - n := NetIOCountersStat{ + n := IOCountersStat{ Name: values[0], PacketsRecv: parsed[0], Errin: parsed[1], @@ -86,16 +90,25 @@ func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { } if pernic == false { - return getNetIOCountersAll(ret) + return getIOCountersAll(ret) } return ret, nil } +// NetIOCountersByFile is an method which is added just a compatibility for linux. +func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { + return IOCounters(pernic) +} + +func FilterCounters() ([]FilterStat, error) { + return nil, errors.New("NetFilterCounters not implemented for darwin") +} + // NetProtoCounters returns network statistics for the entire system // If protocols is empty then all protocols are returned, otherwise // just the protocols in the list are returned. // Not Implemented for Darwin -func NetProtoCounters(protocols []string) ([]NetProtoCountersStat, error) { +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { return nil, errors.New("NetProtoCounters not implemented for darwin") } diff --git a/vendor/github.com/shirou/gopsutil/net/net_freebsd.go b/vendor/github.com/shirou/gopsutil/net/net_freebsd.go index 1b0375cebc7..3a67b4af4a5 100644 --- a/vendor/github.com/shirou/gopsutil/net/net_freebsd.go +++ b/vendor/github.com/shirou/gopsutil/net/net_freebsd.go @@ -11,14 +11,18 @@ import ( "github.com/shirou/gopsutil/internal/common" ) -func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { - out, err := exec.Command("/usr/bin/netstat", "-ibdnW").Output() +func IOCounters(pernic bool) ([]IOCountersStat, error) { + netstat, err := exec.LookPath("/usr/bin/netstat") + if err != nil { + return nil, err + } + out, err := exec.Command(netstat, "-ibdnW").Output() if err != nil { return nil, err } lines := strings.Split(string(out), "\n") - ret := make([]NetIOCountersStat, 0, len(lines)-1) + ret := make([]IOCountersStat, 0, len(lines)-1) exists := make([]string, 0, len(ret)) for _, line := range lines { @@ -65,7 +69,7 @@ func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { parsed = append(parsed, t) } - n := NetIOCountersStat{ + n := IOCountersStat{ Name: values[0], PacketsRecv: parsed[0], Errin: parsed[1], @@ -80,16 +84,25 @@ func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { } if pernic == false { - return getNetIOCountersAll(ret) + return getIOCountersAll(ret) } return ret, nil } +// NetIOCountersByFile is an method which is added just a compatibility for linux. +func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { + return IOCounters(pernic) +} + +func FilterCounters() ([]FilterStat, error) { + return nil, errors.New("NetFilterCounters not implemented for freebsd") +} + // NetProtoCounters returns network statistics for the entire system // If protocols is empty then all protocols are returned, otherwise // just the protocols in the list are returned. // Not Implemented for FreeBSD -func NetProtoCounters(protocols []string) ([]NetProtoCountersStat, error) { +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { return nil, errors.New("NetProtoCounters not implemented for freebsd") } diff --git a/vendor/github.com/shirou/gopsutil/net/net_linux.go b/vendor/github.com/shirou/gopsutil/net/net_linux.go index ef06814d4fd..e0247714fea 100644 --- a/vendor/github.com/shirou/gopsutil/net/net_linux.go +++ b/vendor/github.com/shirou/gopsutil/net/net_linux.go @@ -3,9 +3,15 @@ package net import ( + "encoding/hex" "errors" + "fmt" + "io/ioutil" + "net" + "os" "strconv" "strings" + "syscall" "github.com/shirou/gopsutil/internal/common" ) @@ -15,8 +21,12 @@ import ( // return only sum of all information (which name is 'all'). If true, // every network interface installed on the system is returned // separately. -func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { +func IOCounters(pernic bool) ([]IOCountersStat, error) { filename := common.HostProc("net/dev") + return IOCountersByFile(pernic, filename) +} + +func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { lines, err := common.ReadLines(filename) if err != nil { return nil, err @@ -24,7 +34,7 @@ func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { statlen := len(lines) - 1 - ret := make([]NetIOCountersStat, 0, statlen) + ret := make([]IOCountersStat, 0, statlen) for _, line := range lines[2:] { parts := strings.SplitN(line, ":", 2) @@ -70,7 +80,7 @@ func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { return ret, err } - nic := NetIOCountersStat{ + nic := IOCountersStat{ Name: interfaceName, BytesRecv: bytesRecv, PacketsRecv: packetsRecv, @@ -85,7 +95,7 @@ func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { } if pernic == false { - return getNetIOCountersAll(ret) + return getIOCountersAll(ret) } return ret, nil @@ -105,12 +115,12 @@ var netProtocols = []string{ // just the protocols in the list are returned. // Available protocols: // ip,icmp,icmpmsg,tcp,udp,udplite -func NetProtoCounters(protocols []string) ([]NetProtoCountersStat, error) { +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { if len(protocols) == 0 { protocols = netProtocols } - stats := make([]NetProtoCountersStat, 0, len(protocols)) + stats := make([]ProtoCountersStat, 0, len(protocols)) protos := make(map[string]bool, len(protocols)) for _, p := range protocols { protos[p] = true @@ -145,7 +155,7 @@ func NetProtoCounters(protocols []string) ([]NetProtoCountersStat, error) { if len(statNames) != len(statValues) { return nil, errors.New(filename + " is not fomatted correctly, expected same number of columns.") } - stat := NetProtoCountersStat{ + stat := ProtoCountersStat{ Protocol: proto, Stats: make(map[string]int64, len(statNames)), } @@ -164,23 +174,23 @@ func NetProtoCounters(protocols []string) ([]NetProtoCountersStat, error) { // NetFilterCounters returns iptables conntrack statistics // the currently in use conntrack count and the max. // If the file does not exist or is invalid it will return nil. -func NetFilterCounters() ([]NetFilterStat, error) { - countfile := common.HostProc("sys/net/netfilter/nf_conntrack_count") - maxfile := common.HostProc("sys/net/netfilter/nf_conntrack_max") +func FilterCounters() ([]FilterStat, error) { + countfile := common.HostProc("sys/net/netfilter/nf_conntrackCount") + maxfile := common.HostProc("sys/net/netfilter/nf_conntrackMax") count, err := common.ReadInts(countfile) if err != nil { return nil, err } - stats := make([]NetFilterStat, 0, 1) - + stats := make([]FilterStat, 0, 1) + max, err := common.ReadInts(maxfile) if err != nil { return nil, err } - payload := NetFilterStat{ + payload := FilterStat{ ConnTrackCount: count[0], ConnTrackMax: max[0], } @@ -188,3 +198,423 @@ func NetFilterCounters() ([]NetFilterStat, error) { stats = append(stats, payload) return stats, nil } + +// http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h +var TCPStatuses = map[string]string{ + "01": "ESTABLISHED", + "02": "SYN_SENT", + "03": "SYN_RECV", + "04": "FIN_WAIT1", + "05": "FIN_WAIT2", + "06": "TIME_WAIT", + "07": "CLOSE", + "08": "CLOSE_WAIT", + "09": "LAST_ACK", + "0A": "LISTEN", + "0B": "CLOSING", +} + +type netConnectionKindType struct { + family uint32 + sockType uint32 + filename string +} + +var kindTCP4 = netConnectionKindType{ + family: syscall.AF_INET, + sockType: syscall.SOCK_STREAM, + filename: "tcp", +} +var kindTCP6 = netConnectionKindType{ + family: syscall.AF_INET6, + sockType: syscall.SOCK_STREAM, + filename: "tcp6", +} +var kindUDP4 = netConnectionKindType{ + family: syscall.AF_INET, + sockType: syscall.SOCK_DGRAM, + filename: "udp", +} +var kindUDP6 = netConnectionKindType{ + family: syscall.AF_INET6, + sockType: syscall.SOCK_DGRAM, + filename: "udp6", +} +var kindUNIX = netConnectionKindType{ + family: syscall.AF_UNIX, + filename: "unix", +} + +var netConnectionKindMap = map[string][]netConnectionKindType{ + "all": []netConnectionKindType{kindTCP4, kindTCP6, kindUDP4, kindUDP6, kindUNIX}, + "tcp": []netConnectionKindType{kindTCP4, kindTCP6}, + "tcp4": []netConnectionKindType{kindTCP4}, + "tcp6": []netConnectionKindType{kindTCP6}, + "udp": []netConnectionKindType{kindUDP4, kindUDP6}, + "udp4": []netConnectionKindType{kindUDP4}, + "udp6": []netConnectionKindType{kindUDP6}, + "unix": []netConnectionKindType{kindUNIX}, + "inet": []netConnectionKindType{kindTCP4, kindTCP6, kindUDP4, kindUDP6}, + "inet4": []netConnectionKindType{kindTCP4, kindUDP4}, + "inet6": []netConnectionKindType{kindTCP6, kindUDP6}, +} + +type inodeMap struct { + pid int32 + fd uint32 +} + +type connTmp struct { + fd uint32 + family uint32 + sockType uint32 + laddr Addr + raddr Addr + status string + pid int32 + boundPid int32 + path string +} + +// Return a list of network connections opened. +func Connections(kind string) ([]ConnectionStat, error) { + return ConnectionsPid(kind, 0) +} + +// Return a list of network connections opened by a process. +func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) { + tmap, ok := netConnectionKindMap[kind] + if !ok { + return nil, fmt.Errorf("invalid kind, %s", kind) + } + root := common.HostProc() + var err error + var inodes map[string][]inodeMap + if pid == 0 { + inodes, err = getProcInodesAll(root) + } else { + inodes, err = getProcInodes(root, pid) + if len(inodes) == 0 { + // no connection for the pid + return []ConnectionStat{}, nil + } + } + if err != nil { + return nil, fmt.Errorf("cound not get pid(s), %d", pid) + } + + dupCheckMap := make(map[string]bool) + var ret []ConnectionStat + + for _, t := range tmap { + var path string + var ls []connTmp + path = fmt.Sprintf("%s/net/%s", root, t.filename) + switch t.family { + case syscall.AF_INET: + fallthrough + case syscall.AF_INET6: + ls, err = processInet(path, t, inodes, pid) + case syscall.AF_UNIX: + ls, err = processUnix(path, t, inodes, pid) + } + if err != nil { + return nil, err + } + for _, c := range ls { + conn := ConnectionStat{ + Fd: c.fd, + Family: c.family, + Type: c.sockType, + Laddr: c.laddr, + Raddr: c.raddr, + Status: c.status, + Pid: c.pid, + } + if c.pid == 0 { + conn.Pid = c.boundPid + } else { + conn.Pid = c.pid + } + // check duplicate using JSON format + json := conn.String() + _, exists := dupCheckMap[json] + if !exists { + ret = append(ret, conn) + dupCheckMap[json] = true + } + } + + } + + return ret, nil +} + +// getProcInodes returnes fd of the pid. +func getProcInodes(root string, pid int32) (map[string][]inodeMap, error) { + ret := make(map[string][]inodeMap) + + dir := fmt.Sprintf("%s/%d/fd", root, pid) + files, err := ioutil.ReadDir(dir) + if err != nil { + return ret, nil + } + for _, fd := range files { + inodePath := fmt.Sprintf("%s/%d/fd/%s", root, pid, fd.Name()) + + inode, err := os.Readlink(inodePath) + if err != nil { + continue + } + if !strings.HasPrefix(inode, "socket:[") { + continue + } + // the process is using a socket + l := len(inode) + inode = inode[8 : l-1] + _, ok := ret[inode] + if !ok { + ret[inode] = make([]inodeMap, 0) + } + fd, err := strconv.Atoi(fd.Name()) + if err != nil { + continue + } + + i := inodeMap{ + pid: pid, + fd: uint32(fd), + } + ret[inode] = append(ret[inode], i) + } + return ret, nil +} + +// Pids retunres all pids. +// Note: this is a copy of process_linux.Pids() +// FIXME: Import process occures import cycle. +// move to common made other platform breaking. Need consider. +func Pids() ([]int32, error) { + var ret []int32 + + d, err := os.Open(common.HostProc()) + if err != nil { + return nil, err + } + defer d.Close() + + fnames, err := d.Readdirnames(-1) + if err != nil { + return nil, err + } + for _, fname := range fnames { + pid, err := strconv.ParseInt(fname, 10, 32) + if err != nil { + // if not numeric name, just skip + continue + } + ret = append(ret, int32(pid)) + } + + return ret, nil +} + +func getProcInodesAll(root string) (map[string][]inodeMap, error) { + pids, err := Pids() + if err != nil { + return nil, err + } + ret := make(map[string][]inodeMap) + + for _, pid := range pids { + t, err := getProcInodes(root, pid) + if err != nil { + return ret, err + } + if len(t) == 0 { + continue + } + // TODO: update ret. + ret = updateMap(ret, t) + } + return ret, nil +} + +// decodeAddress decode addresse represents addr in proc/net/* +// ex: +// "0500000A:0016" -> "10.0.0.5", 22 +// "0085002452100113070057A13F025401:0035" -> "2400:8500:1301:1052:a157:7:154:23f", 53 +func decodeAddress(family uint32, src string) (Addr, error) { + t := strings.Split(src, ":") + if len(t) != 2 { + return Addr{}, fmt.Errorf("does not contain port, %s", src) + } + addr := t[0] + port, err := strconv.ParseInt("0x"+t[1], 0, 64) + if err != nil { + return Addr{}, fmt.Errorf("invalid port, %s", src) + } + decoded, err := hex.DecodeString(addr) + if err != nil { + return Addr{}, fmt.Errorf("decode error, %s", err) + } + var ip net.IP + // Assumes this is little_endian + if family == syscall.AF_INET { + ip = net.IP(Reverse(decoded)) + } else { // IPv6 + ip, err = parseIPv6HexString(decoded) + if err != nil { + return Addr{}, err + } + } + return Addr{ + IP: ip.String(), + Port: uint32(port), + }, nil +} + +// Reverse reverses array of bytes. +func Reverse(s []byte) []byte { + for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// parseIPv6HexString parse array of bytes to IPv6 string +func parseIPv6HexString(src []byte) (net.IP, error) { + if len(src) != 16 { + return nil, fmt.Errorf("invalid IPv6 string") + } + + buf := make([]byte, 0, 16) + for i := 0; i < len(src); i += 4 { + r := Reverse(src[i : i+4]) + buf = append(buf, r...) + } + return net.IP(buf), nil +} + +func processInet(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) { + + if strings.HasSuffix(file, "6") && !common.PathExists(file) { + // IPv6 not supported, return empty. + return []connTmp{}, nil + } + lines, err := common.ReadLines(file) + if err != nil { + return nil, err + } + var ret []connTmp + // skip first line + for _, line := range lines[1:] { + l := strings.Fields(line) + if len(l) < 10 { + continue + } + laddr := l[1] + raddr := l[2] + status := l[3] + inode := l[9] + pid := int32(0) + fd := uint32(0) + i, exists := inodes[inode] + if exists { + pid = i[0].pid + fd = i[0].fd + } + if filterPid > 0 && filterPid != pid { + continue + } + if kind.sockType == syscall.SOCK_STREAM { + status = TCPStatuses[status] + } else { + status = "NONE" + } + la, err := decodeAddress(kind.family, laddr) + if err != nil { + continue + } + ra, err := decodeAddress(kind.family, raddr) + if err != nil { + continue + } + + ret = append(ret, connTmp{ + fd: fd, + family: kind.family, + sockType: kind.sockType, + laddr: la, + raddr: ra, + status: status, + pid: pid, + }) + } + + return ret, nil +} + +func processUnix(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) { + lines, err := common.ReadLines(file) + if err != nil { + return nil, err + } + + var ret []connTmp + // skip first line + for _, line := range lines[1:] { + tokens := strings.Fields(line) + if len(tokens) < 6 { + continue + } + st, err := strconv.Atoi(tokens[4]) + if err != nil { + return nil, err + } + + inode := tokens[6] + + var pairs []inodeMap + pairs, exists := inodes[inode] + if !exists { + pairs = []inodeMap{ + inodeMap{}, + } + } + for _, pair := range pairs { + if filterPid > 0 && filterPid != pair.pid { + continue + } + var path string + if len(tokens) == 8 { + path = tokens[len(tokens)-1] + } + ret = append(ret, connTmp{ + fd: pair.fd, + family: kind.family, + sockType: uint32(st), + laddr: Addr{ + IP: path, + }, + pid: pair.pid, + status: "NONE", + path: path, + }) + } + } + + return ret, nil +} + +func updateMap(src map[string][]inodeMap, add map[string][]inodeMap) map[string][]inodeMap { + for key, value := range add { + a, exists := src[key] + if !exists { + src[key] = value + continue + } + src[key] = append(a, value...) + } + return src +} diff --git a/vendor/github.com/shirou/gopsutil/net/net_unix.go b/vendor/github.com/shirou/gopsutil/net/net_unix.go index fe2f1eb4dd4..45de6b17deb 100644 --- a/vendor/github.com/shirou/gopsutil/net/net_unix.go +++ b/vendor/github.com/shirou/gopsutil/net/net_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd darwin +// +build freebsd darwin package net @@ -9,13 +9,13 @@ import ( ) // Return a list of network connections opened. -func NetConnections(kind string) ([]NetConnectionStat, error) { - return NetConnectionsPid(kind, 0) +func Connections(kind string) ([]ConnectionStat, error) { + return ConnectionsPid(kind, 0) } // Return a list of network connections opened by a process. -func NetConnectionsPid(kind string, pid int32) ([]NetConnectionStat, error) { - var ret []NetConnectionStat +func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) { + var ret []ConnectionStat args := []string{"-i"} switch strings.ToLower(kind) { @@ -44,7 +44,7 @@ func NetConnectionsPid(kind string, pid int32) ([]NetConnectionStat, error) { case "udp6": args = append(args, "6udp") case "unix": - return ret, common.NotImplementedError + return ret, common.ErrNotImplementedError } r, err := common.CallLsof(invoke, pid, args...) diff --git a/vendor/github.com/shirou/gopsutil/net/net_windows.go b/vendor/github.com/shirou/gopsutil/net/net_windows.go index 33aceb6576a..f125260c4d5 100644 --- a/vendor/github.com/shirou/gopsutil/net/net_windows.go +++ b/vendor/github.com/shirou/gopsutil/net/net_windows.go @@ -14,8 +14,8 @@ import ( var ( modiphlpapi = syscall.NewLazyDLL("iphlpapi.dll") - procGetExtendedTcpTable = modiphlpapi.NewProc("GetExtendedTcpTable") - procGetExtendedUdpTable = modiphlpapi.NewProc("GetExtendedUdpTable") + procGetExtendedTCPTable = modiphlpapi.NewProc("GetExtendedTcpTable") + procGetExtendedUDPTable = modiphlpapi.NewProc("GetExtendedUdpTable") ) const ( @@ -30,7 +30,7 @@ const ( TCPTableOwnerModuleAll ) -func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { +func IOCounters(pernic bool) ([]IOCountersStat, error) { ifs, err := net.Interfaces() if err != nil { return nil, err @@ -40,13 +40,13 @@ func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { if err != nil { return nil, err } - var ret []NetIOCountersStat + var ret []IOCountersStat for _, ifi := range ifs { name := ifi.Name for ; ai != nil; ai = ai.Next { name = common.BytePtrToString(&ai.Description[0]) - c := NetIOCountersStat{ + c := IOCountersStat{ Name: name, } @@ -69,16 +69,21 @@ func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { } if pernic == false { - return getNetIOCountersAll(ret) + return getIOCountersAll(ret) } return ret, nil } +// NetIOCountersByFile is an method which is added just a compatibility for linux. +func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { + return IOCounters(pernic) +} + // Return a list of network connections opened by a process -func NetConnections(kind string) ([]NetConnectionStat, error) { - var ret []NetConnectionStat +func Connections(kind string) ([]ConnectionStat, error) { + var ret []ConnectionStat - return ret, common.NotImplementedError + return ret, common.ErrNotImplementedError } // borrowed from src/pkg/net/interface_windows.go @@ -98,10 +103,14 @@ func getAdapterList() (*syscall.IpAdapterInfo, error) { return a, nil } +func FilterCounters() ([]FilterStat, error) { + return nil, errors.New("NetFilterCounters not implemented for windows") +} + // NetProtoCounters returns network statistics for the entire system // If protocols is empty then all protocols are returned, otherwise // just the protocols in the list are returned. // Not Implemented for Windows -func NetProtoCounters(protocols []string) ([]NetProtoCountersStat, error) { +func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { return nil, errors.New("NetProtoCounters not implemented for windows") } diff --git a/vendor/github.com/shirou/gopsutil/process/process.go b/vendor/github.com/shirou/gopsutil/process/process.go index 0526f78a7d3..4b69224340e 100644 --- a/vendor/github.com/shirou/gopsutil/process/process.go +++ b/vendor/github.com/shirou/gopsutil/process/process.go @@ -7,6 +7,7 @@ import ( "github.com/shirou/gopsutil/cpu" "github.com/shirou/gopsutil/internal/common" + "github.com/shirou/gopsutil/mem" ) var invoke common.Invoker @@ -19,13 +20,14 @@ type Process struct { Pid int32 `json:"pid"` name string status string + parent int32 numCtxSwitches *NumCtxSwitchesStat uids []int32 gids []int32 numThreads int32 memInfo *MemoryInfoStat - lastCPUTimes *cpu.CPUTimesStat + lastCPUTimes *cpu.TimesStat lastCPUTime time.Time } @@ -47,10 +49,10 @@ type RlimitStat struct { } type IOCountersStat struct { - ReadCount uint64 `json:"read_count"` - WriteCount uint64 `json:"write_count"` - ReadBytes uint64 `json:"read_bytes"` - WriteBytes uint64 `json:"write_bytes"` + ReadCount uint64 `json:"readCount"` + WriteCount uint64 `json:"writeCount"` + ReadBytes uint64 `json:"readBytes"` + WriteBytes uint64 `json:"writeBytes"` } type NumCtxSwitchesStat struct { @@ -105,45 +107,61 @@ func PidExists(pid int32) (bool, error) { // If interval is 0, return difference from last call(non-blocking). // If interval > 0, wait interval sec and return diffrence between start and end. -func (p *Process) CPUPercent(interval time.Duration) (float64, error) { - numcpu := runtime.NumCPU() - calculate := func(t1, t2 *cpu.CPUTimesStat, delta float64) float64 { - if delta == 0 { - return 0 - } - delta_proc := (t2.User - t1.User) + (t2.System - t1.System) - overall_percent := ((delta_proc / delta) * 100) * float64(numcpu) - return overall_percent - } - - cpuTimes, err := p.CPUTimes() +func (p *Process) Percent(interval time.Duration) (float64, error) { + cpuTimes, err := p.Times() if err != nil { return 0, err } + now := time.Now() if interval > 0 { p.lastCPUTimes = cpuTimes - p.lastCPUTime = time.Now() + p.lastCPUTime = now time.Sleep(interval) - cpuTimes, err = p.CPUTimes() + cpuTimes, err = p.Times() + now = time.Now() if err != nil { return 0, err } } else { if p.lastCPUTimes == nil { // invoked first time - p.lastCPUTimes, err = p.CPUTimes() - if err != nil { - return 0, err - } - p.lastCPUTime = time.Now() + p.lastCPUTimes = cpuTimes + p.lastCPUTime = now return 0, nil } } - delta := (time.Now().Sub(p.lastCPUTime).Seconds()) * float64(numcpu) - ret := calculate(p.lastCPUTimes, cpuTimes, float64(delta)) + numcpu := runtime.NumCPU() + delta := (now.Sub(p.lastCPUTime).Seconds()) * float64(numcpu) + ret := calculatePercent(p.lastCPUTimes, cpuTimes, delta, numcpu) p.lastCPUTimes = cpuTimes - p.lastCPUTime = time.Now() + p.lastCPUTime = now return ret, nil } + +func calculatePercent(t1, t2 *cpu.TimesStat, delta float64, numcpu int) float64 { + if delta == 0 { + return 0 + } + delta_proc := t2.Total() - t1.Total() + overall_percent := ((delta_proc / delta) * 100) * float64(numcpu) + return overall_percent +} + +// MemoryPercent returns how many percent of the total RAM this process uses +func (p *Process) MemoryPercent() (float32, error) { + machineMemory, err := mem.VirtualMemory() + if err != nil { + return 0, err + } + total := machineMemory.Total + + processMemory, err := p.MemoryInfo() + if err != nil { + return 0, err + } + used := processMemory.RSS + + return (100 * float32(used) / float32(total)), nil +} diff --git a/vendor/github.com/shirou/gopsutil/process/process_darwin.go b/vendor/github.com/shirou/gopsutil/process/process_darwin.go index 01cb66cc87a..cea0803e6dc 100644 --- a/vendor/github.com/shirou/gopsutil/process/process_darwin.go +++ b/vendor/github.com/shirou/gopsutil/process/process_darwin.go @@ -4,7 +4,9 @@ package process import ( "bytes" + "encoding/binary" "fmt" + "os/exec" "strconv" "strings" "syscall" @@ -77,8 +79,11 @@ func (p *Process) Name() (string, error) { return common.IntToString(k.Proc.P_comm[:]), nil } func (p *Process) Exe() (string, error) { - return "", common.NotImplementedError + return "", common.ErrNotImplementedError } + +// Cmdline returns the command line arguments of the process as a string with +// each argument separated by 0x20 ascii character. func (p *Process) Cmdline() (string, error) { r, err := callPs("command", p.Pid, false) if err != nil { @@ -86,11 +91,24 @@ func (p *Process) Cmdline() (string, error) { } return strings.Join(r[0], " "), err } + +// CmdlineSlice returns the command line arguments of the process as a slice with each +// element being an argument. Because of current deficiencies in the way that the command +// line arguments are found, single arguments that have spaces in the will actually be +// reported as two separate items. In order to do something better CGO would be needed +// to use the native darwin functions. +func (p *Process) CmdlineSlice() ([]string, error) { + r, err := callPs("command", p.Pid, false) + if err != nil { + return nil, err + } + return r[0], err +} func (p *Process) CreateTime() (int64, error) { - return 0, common.NotImplementedError + return 0, common.ErrNotImplementedError } func (p *Process) Cwd() (string, error) { - return "", common.NotImplementedError + return "", common.ErrNotImplementedError } func (p *Process) Parent() (*Process, error) { rr, err := common.CallLsof(invoke, p.Pid, "-FR") @@ -124,11 +142,10 @@ func (p *Process) Uids() ([]int32, error) { return nil, err } - uids := make([]int32, 0, 3) - - uids = append(uids, int32(k.Eproc.Pcred.P_ruid), int32(k.Eproc.Ucred.Uid), int32(k.Eproc.Pcred.P_svuid)) + // See: http://unix.superglobalmegacorp.com/Net2/newsrc/sys/ucred.h.html + userEffectiveUID := int32(k.Eproc.Ucred.UID) - return uids, nil + return []int32{userEffectiveUID}, nil } func (p *Process) Gids() ([]int32, error) { k, err := p.getKProc() @@ -142,7 +159,7 @@ func (p *Process) Gids() ([]int32, error) { return gids, nil } func (p *Process) Terminal() (string, error) { - return "", common.NotImplementedError + return "", common.ErrNotImplementedError /* k, err := p.getKProc() if err != nil { @@ -166,20 +183,20 @@ func (p *Process) Nice() (int32, error) { return int32(k.Proc.P_nice), nil } func (p *Process) IOnice() (int32, error) { - return 0, common.NotImplementedError + return 0, common.ErrNotImplementedError } func (p *Process) Rlimit() ([]RlimitStat, error) { var rlimit []RlimitStat - return rlimit, common.NotImplementedError + return rlimit, common.ErrNotImplementedError } func (p *Process) IOCounters() (*IOCountersStat, error) { - return nil, common.NotImplementedError + return nil, common.ErrNotImplementedError } func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { - return nil, common.NotImplementedError + return nil, common.ErrNotImplementedError } func (p *Process) NumFDs() (int32, error) { - return 0, common.NotImplementedError + return 0, common.ErrNotImplementedError } func (p *Process) NumThreads() (int32, error) { r, err := callPs("utime,stime", p.Pid, true) @@ -190,10 +207,10 @@ func (p *Process) NumThreads() (int32, error) { } func (p *Process) Threads() (map[string]string, error) { ret := make(map[string]string, 0) - return ret, common.NotImplementedError + return ret, common.ErrNotImplementedError } -func convertCpuTimes(s string) (ret float64, err error) { +func convertCPUTimes(s string) (ret float64, err error) { var t int var _tmp string if strings.Contains(s, ":") { @@ -218,23 +235,23 @@ func convertCpuTimes(s string) (ret float64, err error) { t += h return float64(t) / ClockTicks, nil } -func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) { +func (p *Process) Times() (*cpu.TimesStat, error) { r, err := callPs("utime,stime", p.Pid, false) if err != nil { return nil, err } - utime, err := convertCpuTimes(r[0][0]) + utime, err := convertCPUTimes(r[0][0]) if err != nil { return nil, err } - stime, err := convertCpuTimes(r[0][1]) + stime, err := convertCPUTimes(r[0][1]) if err != nil { return nil, err } - ret := &cpu.CPUTimesStat{ + ret := &cpu.TimesStat{ CPU: "cpu", User: utime, System: stime, @@ -242,7 +259,7 @@ func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) { return ret, nil } func (p *Process) CPUAffinity() ([]int32, error) { - return nil, common.NotImplementedError + return nil, common.ErrNotImplementedError } func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { r, err := callPs("rss,vsize,pagein", p.Pid, false) @@ -271,10 +288,7 @@ func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { return ret, nil } func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { - return nil, common.NotImplementedError -} -func (p *Process) MemoryPercent() (float32, error) { - return 0, common.NotImplementedError + return nil, common.ErrNotImplementedError } func (p *Process) Children() ([]*Process, error) { @@ -294,24 +308,23 @@ func (p *Process) Children() ([]*Process, error) { } func (p *Process) OpenFiles() ([]OpenFilesStat, error) { - return nil, common.NotImplementedError + return nil, common.ErrNotImplementedError } -func (p *Process) Connections() ([]net.NetConnectionStat, error) { - return net.NetConnectionsPid("all", p.Pid) +func (p *Process) Connections() ([]net.ConnectionStat, error) { + return net.ConnectionsPid("all", p.Pid) +} + +func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) { + return nil, common.ErrNotImplementedError } func (p *Process) IsRunning() (bool, error) { - return true, common.NotImplementedError + return true, common.ErrNotImplementedError } func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { var ret []MemoryMapsStat - return &ret, common.NotImplementedError -} - -func copyParams(k *KinfoProc, p *Process) error { - - return nil + return &ret, common.ErrNotImplementedError } func processes() ([]Process, error) { @@ -346,8 +359,6 @@ func processes() ([]Process, error) { if err != nil { continue } - copyParams(&k, p) - results = append(results, *p) } @@ -358,7 +369,7 @@ func parseKinfoProc(buf []byte) (KinfoProc, error) { var k KinfoProc br := bytes.NewReader(buf) - err := Read(br, LittleEndian, &k) + err := common.Read(br, binary.LittleEndian, &k) if err != nil { return k, err } @@ -366,6 +377,8 @@ func parseKinfoProc(buf []byte) (KinfoProc, error) { return k, nil } +// Returns a proc as defined here: +// http://unix.superglobalmegacorp.com/Net2/newsrc/sys/kinfo_proc.h.html func (p *Process) getKProc() (*KinfoProc, error) { mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid} procK := KinfoProc{} @@ -401,15 +414,20 @@ func NewProcess(pid int32) (*Process, error) { // And splited by Space. Caller have responsibility to manage. // If passed arg pid is 0, get information from all process. func callPs(arg string, pid int32, threadOption bool) ([][]string, error) { + bin, err := exec.LookPath("ps") + if err != nil { + return [][]string{}, err + } + var cmd []string if pid == 0 { // will get from all processes. - cmd = []string{"-x", "-o", arg} + cmd = []string{"-ax", "-o", arg} } else if threadOption { cmd = []string{"-x", "-o", arg, "-M", "-p", strconv.Itoa(int(pid))} } else { cmd = []string{"-x", "-o", arg, "-p", strconv.Itoa(int(pid))} } - out, err := invoke.Command("/bin/ps", cmd...) + out, err := invoke.Command(bin, cmd...) if err != nil { return [][]string{}, err } diff --git a/vendor/github.com/shirou/gopsutil/process/process_darwin_amd64.go b/vendor/github.com/shirou/gopsutil/process/process_darwin_amd64.go index 7ac7bdd6bb7..f8e922385b9 100644 --- a/vendor/github.com/shirou/gopsutil/process/process_darwin_amd64.go +++ b/vendor/github.com/shirou/gopsutil/process/process_darwin_amd64.go @@ -101,7 +101,7 @@ type ucred struct { type Uucred struct { Ref int32 - Uid uint32 + UID uint32 Ngroups int16 Pad_cgo_0 [2]byte Groups [16]uint32 @@ -197,7 +197,7 @@ type Au_session struct { } type Posix_cred struct { - Uid uint32 + UID uint32 Ruid uint32 Svuid uint32 Ngroups int16 diff --git a/vendor/github.com/shirou/gopsutil/process/process_freebsd.go b/vendor/github.com/shirou/gopsutil/process/process_freebsd.go index ecd89204c40..5362128e6e5 100644 --- a/vendor/github.com/shirou/gopsutil/process/process_freebsd.go +++ b/vendor/github.com/shirou/gopsutil/process/process_freebsd.go @@ -5,7 +5,6 @@ package process import ( "bytes" "encoding/binary" - "unsafe" "strings" "syscall" @@ -41,7 +40,7 @@ func (p *Process) Ppid() (int32, error) { return 0, err } - return k.KiPpid, nil + return k.Ppid, nil } func (p *Process) Name() (string, error) { k, err := p.getKProc() @@ -49,11 +48,12 @@ func (p *Process) Name() (string, error) { return "", err } - return string(k.KiComm[:]), nil + return common.IntToString(k.Comm[:]), nil } func (p *Process) Exe() (string, error) { - return "", common.NotImplementedError + return "", common.ErrNotImplementedError } + func (p *Process) Cmdline() (string, error) { mib := []int32{CTLKern, KernProc, KernProcArgs, p.Pid} buf, _, err := common.CallSyscall(mib) @@ -69,22 +69,60 @@ func (p *Process) Cmdline() (string, error) { return strings.Join(ret, " "), nil } + +func (p *Process) CmdlineSlice() ([]string, error) { + mib := []int32{CTLKern, KernProc, KernProcArgs, p.Pid} + buf, _, err := common.CallSyscall(mib) + if err != nil { + return nil, err + } + if len(buf) == 0 { + return nil, nil + } + if buf[len(buf)-1] == 0 { + buf = buf[:len(buf)-1] + } + parts := bytes.Split(buf, []byte{0}) + var strParts []string + for _, p := range parts { + strParts = append(strParts, string(p)) + } + + return strParts, nil +} func (p *Process) CreateTime() (int64, error) { - return 0, common.NotImplementedError + return 0, common.ErrNotImplementedError } func (p *Process) Cwd() (string, error) { - return "", common.NotImplementedError + return "", common.ErrNotImplementedError } func (p *Process) Parent() (*Process, error) { - return p, common.NotImplementedError + return p, common.ErrNotImplementedError } func (p *Process) Status() (string, error) { k, err := p.getKProc() if err != nil { return "", err } + var s string + switch k.Stat { + case SIDL: + s = "I" + case SRUN: + s = "R" + case SSLEEP: + s = "S" + case SSTOP: + s = "T" + case SZOMB: + s = "Z" + case SWAIT: + s = "W" + case SLOCK: + s = "L" + } - return string(k.KiStat[:]), nil + return s, nil } func (p *Process) Uids() ([]int32, error) { k, err := p.getKProc() @@ -94,7 +132,7 @@ func (p *Process) Uids() ([]int32, error) { uids := make([]int32, 0, 3) - uids = append(uids, int32(k.KiRuid), int32(k.KiUID), int32(k.KiSvuid)) + uids = append(uids, int32(k.Ruid), int32(k.Uid), int32(k.Svuid)) return uids, nil } @@ -105,7 +143,7 @@ func (p *Process) Gids() ([]int32, error) { } gids := make([]int32, 0, 3) - gids = append(gids, int32(k.KiRgid), int32(k.KiNgroups[0]), int32(k.KiSvuid)) + gids = append(gids, int32(k.Rgid), int32(k.Ngroups), int32(k.Svgid)) return gids, nil } @@ -115,7 +153,7 @@ func (p *Process) Terminal() (string, error) { return "", err } - ttyNr := uint64(k.KiTdev) + ttyNr := uint64(k.Tdev) termmap, err := getTerminalMap() if err != nil { @@ -125,14 +163,18 @@ func (p *Process) Terminal() (string, error) { return termmap[ttyNr], nil } func (p *Process) Nice() (int32, error) { - return 0, common.NotImplementedError + k, err := p.getKProc() + if err != nil { + return 0, err + } + return int32(k.Nice), nil } func (p *Process) IOnice() (int32, error) { - return 0, common.NotImplementedError + return 0, common.ErrNotImplementedError } func (p *Process) Rlimit() ([]RlimitStat, error) { var rlimit []RlimitStat - return rlimit, common.NotImplementedError + return rlimit, common.ErrNotImplementedError } func (p *Process) IOCounters() (*IOCountersStat, error) { k, err := p.getKProc() @@ -140,15 +182,15 @@ func (p *Process) IOCounters() (*IOCountersStat, error) { return nil, err } return &IOCountersStat{ - ReadCount: uint64(k.KiRusage.Inblock), - WriteCount: uint64(k.KiRusage.Oublock), + ReadCount: uint64(k.Rusage.Inblock), + WriteCount: uint64(k.Rusage.Oublock), }, nil } func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { - return nil, common.NotImplementedError + return nil, common.ErrNotImplementedError } func (p *Process) NumFDs() (int32, error) { - return 0, common.NotImplementedError + return 0, common.ErrNotImplementedError } func (p *Process) NumThreads() (int32, error) { k, err := p.getKProc() @@ -156,25 +198,25 @@ func (p *Process) NumThreads() (int32, error) { return 0, err } - return k.KiNumthreads, nil + return k.Numthreads, nil } func (p *Process) Threads() (map[string]string, error) { ret := make(map[string]string, 0) - return ret, common.NotImplementedError + return ret, common.ErrNotImplementedError } -func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) { +func (p *Process) Times() (*cpu.TimesStat, error) { k, err := p.getKProc() if err != nil { return nil, err } - return &cpu.CPUTimesStat{ + return &cpu.TimesStat{ CPU: "cpu", - User: float64(k.KiRusage.Utime.Sec) + float64(k.KiRusage.Utime.Usec)/1000000, - System: float64(k.KiRusage.Stime.Sec) + float64(k.KiRusage.Stime.Usec)/1000000, + User: float64(k.Rusage.Utime.Sec) + float64(k.Rusage.Utime.Usec)/1000000, + System: float64(k.Rusage.Stime.Sec) + float64(k.Rusage.Stime.Usec)/1000000, }, nil } func (p *Process) CPUAffinity() ([]int32, error) { - return nil, common.NotImplementedError + return nil, common.ErrNotImplementedError } func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { k, err := p.getKProc() @@ -185,18 +227,15 @@ func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { if err != nil { return nil, err } - pageSize := binary.LittleEndian.Uint16([]byte(v)) + pageSize := common.LittleEndian.Uint16([]byte(v)) return &MemoryInfoStat{ - RSS: uint64(k.KiRssize) * uint64(pageSize), - VMS: uint64(k.KiSize), + RSS: uint64(k.Rssize) * uint64(pageSize), + VMS: uint64(k.Size), }, nil } func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { - return nil, common.NotImplementedError -} -func (p *Process) MemoryPercent() (float32, error) { - return 0, common.NotImplementedError + return nil, common.ErrNotImplementedError } func (p *Process) Children() ([]*Process, error) { @@ -216,24 +255,23 @@ func (p *Process) Children() ([]*Process, error) { } func (p *Process) OpenFiles() ([]OpenFilesStat, error) { - return nil, common.NotImplementedError + return nil, common.ErrNotImplementedError } -func (p *Process) Connections() ([]net.NetConnectionStat, error) { - return nil, common.NotImplementedError +func (p *Process) Connections() ([]net.ConnectionStat, error) { + return nil, common.ErrNotImplementedError +} + +func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) { + return nil, common.ErrNotImplementedError } func (p *Process) IsRunning() (bool, error) { - return true, common.NotImplementedError + return true, common.ErrNotImplementedError } func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { var ret []MemoryMapsStat - return &ret, common.NotImplementedError -} - -func copyParams(k *KinfoProc, p *Process) error { - - return nil + return &ret, common.ErrNotImplementedError } func processes() ([]Process, error) { @@ -246,22 +284,19 @@ func processes() ([]Process, error) { } // get kinfo_proc size - k := KinfoProc{} - procinfoLen := int(unsafe.Sizeof(k)) - count := int(length / uint64(procinfoLen)) + count := int(length / uint64(sizeOfKinfoProc)) // parse buf to procs for i := 0; i < count; i++ { - b := buf[i*procinfoLen : i*procinfoLen+procinfoLen] + b := buf[i*sizeOfKinfoProc : (i+1)*sizeOfKinfoProc] k, err := parseKinfoProc(b) if err != nil { continue } - p, err := NewProcess(int32(k.KiPid)) + p, err := NewProcess(int32(k.Pid)) if err != nil { continue } - copyParams(&k, p) results = append(results, *p) } @@ -272,7 +307,7 @@ func processes() ([]Process, error) { func parseKinfoProc(buf []byte) (KinfoProc, error) { var k KinfoProc br := bytes.NewReader(buf) - err := binary.Read(br, binary.LittleEndian, &k) + err := common.Read(br, binary.LittleEndian, &k) return k, err } @@ -283,8 +318,7 @@ func (p *Process) getKProc() (*KinfoProc, error) { if err != nil { return nil, err } - procK := KinfoProc{} - if length != uint64(unsafe.Sizeof(procK)) { + if length != sizeOfKinfoProc { return nil, err } @@ -292,7 +326,6 @@ func (p *Process) getKProc() (*KinfoProc, error) { if err != nil { return nil, err } - return &k, nil } diff --git a/vendor/github.com/shirou/gopsutil/process/process_freebsd_386.go b/vendor/github.com/shirou/gopsutil/process/process_freebsd_386.go index 6b3bdfc726f..05e96d0f035 100644 --- a/vendor/github.com/shirou/gopsutil/process/process_freebsd_386.go +++ b/vendor/github.com/shirou/gopsutil/process/process_freebsd_386.go @@ -1,126 +1,192 @@ -// +build freebsd -// +build 386 +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go package process -// copied from sys/sysctl.h const ( - CTLKern = 1 // "high kernel": proc, limits - KernProc = 14 // struct: process entries - KernProcPID = 1 // by process id - KernProcProc = 8 // only return procs - KernProcPathname = 12 // path to executable - KernProcArgs = 7 // get/set arguments/proctitle + CTLKern = 1 + KernProc = 14 + KernProcPID = 1 + KernProcProc = 8 + KernProcPathname = 12 + KernProcArgs = 7 +) + +const ( + sizeofPtr = 0x4 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x4 + sizeofLongLong = 0x8 +) + +const ( + sizeOfKinfoVmentry = 0x488 + sizeOfKinfoProc = 0x300 +) + +const ( + SIDL = 1 + SRUN = 2 + SSLEEP = 3 + SSTOP = 4 + SZOMB = 5 + SWAIT = 6 + SLOCK = 7 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int32 + _C_long_long int64 ) type Timespec struct { - Sec int32 - Nsec int32 + Sec int32 + Nsec int32 } type Timeval struct { - Sec int32 - Usec int32 + Sec int32 + Usec int32 } type Rusage struct { - Utime Timeval - Stime Timeval - Maxrss int32 - Ixrss int32 - Idrss int32 - Isrss int32 - Minflt int32 - Majflt int32 - Nswap int32 - Inblock int32 - Oublock int32 - Msgsnd int32 - Msgrcv int32 - Nsignals int32 - Nvcsw int32 - Nivcsw int32 + Utime Timeval + Stime Timeval + Maxrss int32 + Ixrss int32 + Idrss int32 + Isrss int32 + Minflt int32 + Majflt int32 + Nswap int32 + Inblock int32 + Oublock int32 + Msgsnd int32 + Msgrcv int32 + Nsignals int32 + Nvcsw int32 + Nivcsw int32 +} + +type Rlimit struct { + Cur int64 + Max int64 } -// copied from sys/user.h type KinfoProc struct { - KiStructsize int32 - KiLayout int32 - KiArgs int32 - KiPaddr int32 - KiAddr int32 - KiTracep int32 - KiTextvp int32 - KiFd int32 - KiVmspace int32 - KiWchan int32 - KiPid int32 - KiPpid int32 - KiPgid int32 - KiTpgid int32 - KiSid int32 - KiTsid int32 - KiJobc [2]byte - KiSpareShort1 [2]byte - KiTdev int32 - KiSiglist [16]byte - KiSigmask [16]byte - KiSigignore [16]byte - KiSigcatch [16]byte - KiUID int32 - KiRuid int32 - KiSvuid int32 - KiRgid int32 - KiSvgid int32 - KiNgroups [2]byte - KiSpareShort2 [2]byte - KiGroups [64]byte - KiSize int32 - KiRssize int32 - KiSwrss int32 - KiTsize int32 - KiDsize int32 - KiSsize int32 - KiXstat [2]byte - KiAcflag [2]byte - KiPctcpu int32 - KiEstcpu int32 - KiSlptime int32 - KiSwtime int32 - KiCow int32 - KiRuntime int64 - KiStart [8]byte - KiChildtime [8]byte - KiFlag int32 - KiKflag int32 - KiTraceflag int32 - KiStat [1]byte - KiNice [1]byte - KiLock [1]byte - KiRqindex [1]byte - KiOncpu [1]byte - KiLastcpu [1]byte - KiOcomm [17]byte - KiWmesg [9]byte - KiLogin [18]byte - KiLockname [9]byte - KiComm [20]byte - KiEmul [17]byte - KiSparestrings [68]byte - KiSpareints [36]byte - KiCrFlags int32 - KiJid int32 - KiNumthreads int32 - KiTid int32 - KiPri int32 - KiRusage Rusage - KiRusageCh [72]byte - KiPcb int32 - KiKstack int32 - KiUdata int32 - KiTdaddr int32 - KiSpareptrs [24]byte - KiSpareint64s [48]byte - KiSflag int32 - KiTdflags int32 + Structsize int32 + Layout int32 + Args int32 /* pargs */ + Paddr int32 /* proc */ + Addr int32 /* user */ + Tracep int32 /* vnode */ + Textvp int32 /* vnode */ + Fd int32 /* filedesc */ + Vmspace int32 /* vmspace */ + Wchan int32 + Pid int32 + Ppid int32 + Pgid int32 + Tpgid int32 + Sid int32 + Tsid int32 + Jobc int16 + Spare_short1 int16 + Tdev uint32 + Siglist [16]byte /* sigset */ + Sigmask [16]byte /* sigset */ + Sigignore [16]byte /* sigset */ + Sigcatch [16]byte /* sigset */ + Uid uint32 + Ruid uint32 + Svuid uint32 + Rgid uint32 + Svgid uint32 + Ngroups int16 + Spare_short2 int16 + Groups [16]uint32 + Size uint32 + Rssize int32 + Swrss int32 + Tsize int32 + Dsize int32 + Ssize int32 + Xstat uint16 + Acflag uint16 + Pctcpu uint32 + Estcpu uint32 + Slptime uint32 + Swtime uint32 + Cow uint32 + Runtime uint64 + Start Timeval + Childtime Timeval + Flag int32 + Kiflag int32 + Traceflag int32 + Stat int8 + Nice int8 + Lock int8 + Rqindex int8 + Oncpu uint8 + Lastcpu uint8 + Tdname [17]int8 + Wmesg [9]int8 + Login [18]int8 + Lockname [9]int8 + Comm [20]int8 + Emul [17]int8 + Loginclass [18]int8 + Sparestrings [50]int8 + Spareints [7]int32 + Flag2 int32 + Fibnum int32 + Cr_flags uint32 + Jid int32 + Numthreads int32 + Tid int32 + Pri Priority + Rusage Rusage + Rusage_ch Rusage + Pcb int32 /* pcb */ + Kstack int32 + Udata int32 + Tdaddr int32 /* thread */ + Spareptrs [6]int32 + Sparelongs [12]int32 + Sflag int32 + Tdflags int32 +} + +type Priority struct { + Class uint8 + Level uint8 + Native uint8 + User uint8 +} + +type KinfoVmentry struct { + Structsize int32 + Type int32 + Start uint64 + End uint64 + Offset uint64 + Vn_fileid uint64 + Vn_fsid uint32 + Flags int32 + Resident int32 + Private_resident int32 + Protection int32 + Ref_count int32 + Shadow_count int32 + Vn_type int32 + Vn_size uint64 + Vn_rdev uint32 + Vn_mode uint16 + Status uint16 + X_kve_ispare [12]int32 + Path [1024]int8 } diff --git a/vendor/github.com/shirou/gopsutil/process/process_freebsd_amd64.go b/vendor/github.com/shirou/gopsutil/process/process_freebsd_amd64.go index 69a352f34b8..79e2ba8816e 100644 --- a/vendor/github.com/shirou/gopsutil/process/process_freebsd_amd64.go +++ b/vendor/github.com/shirou/gopsutil/process/process_freebsd_amd64.go @@ -1,125 +1,192 @@ -// +build freebsd -// +build amd64 +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + package process -// copied from sys/sysctl.h const ( - CTLKern = 1 // "high kernel": proc, limits - KernProc = 14 // struct: process entries - KernProcPID = 1 // by process id - KernProcProc = 8 // only return procs - KernProcPathname = 12 // path to executable - KernProcArgs = 7 // get/set arguments/proctitle + CTLKern = 1 + KernProc = 14 + KernProcPID = 1 + KernProcProc = 8 + KernProcPathname = 12 + KernProcArgs = 7 +) + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 +) + +const ( + sizeOfKinfoVmentry = 0x488 + sizeOfKinfoProc = 0x440 +) + +const ( + SIDL = 1 + SRUN = 2 + SSLEEP = 3 + SSTOP = 4 + SZOMB = 5 + SWAIT = 6 + SLOCK = 7 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 ) type Timespec struct { - Sec int64 - Nsec int64 + Sec int64 + Nsec int64 } type Timeval struct { - Sec int64 - Usec int64 + Sec int64 + Usec int64 } type Rusage struct { - Utime Timeval - Stime Timeval - Maxrss int64 - Ixrss int64 - Idrss int64 - Isrss int64 - Minflt int64 - Majflt int64 - Nswap int64 - Inblock int64 - Oublock int64 - Msgsnd int64 - Msgrcv int64 - Nsignals int64 - Nvcsw int64 - Nivcsw int64 + Utime Timeval + Stime Timeval + Maxrss int64 + Ixrss int64 + Idrss int64 + Isrss int64 + Minflt int64 + Majflt int64 + Nswap int64 + Inblock int64 + Oublock int64 + Msgsnd int64 + Msgrcv int64 + Nsignals int64 + Nvcsw int64 + Nivcsw int64 +} + +type Rlimit struct { + Cur int64 + Max int64 } -// copied from sys/user.h type KinfoProc struct { - KiStructsize int32 - KiLayout int32 - KiArgs int64 - KiPaddr int64 - KiAddr int64 - KiTracep int64 - KiTextvp int64 - KiFd int64 - KiVmspace int64 - KiWchan int64 - KiPid int32 - KiPpid int32 - KiPgid int32 - KiTpgid int32 - KiSid int32 - KiTsid int32 - KiJobc [2]byte - KiSpareShort1 [2]byte - KiTdev int32 - KiSiglist [16]byte - KiSigmask [16]byte - KiSigignore [16]byte - KiSigcatch [16]byte - KiUID int32 - KiRuid int32 - KiSvuid int32 - KiRgid int32 - KiSvgid int32 - KiNgroups [2]byte - KiSpareShort2 [2]byte - KiGroups [64]byte - KiSize int64 - KiRssize int64 - KiSwrss int64 - KiTsize int64 - KiDsize int64 - KiSsize int64 - KiXstat [2]byte - KiAcflag [2]byte - KiPctcpu int32 - KiEstcpu int32 - KiSlptime int32 - KiSwtime int32 - KiCow int32 - KiRuntime int64 - KiStart [16]byte - KiChildtime [16]byte - KiFlag int64 - KiKflag int64 - KiTraceflag int32 - KiStat [1]byte - KiNice [1]byte - KiLock [1]byte - KiRqindex [1]byte - KiOncpu [1]byte - KiLastcpu [1]byte - KiOcomm [17]byte - KiWmesg [9]byte - KiLogin [18]byte - KiLockname [9]byte - KiComm [20]byte - KiEmul [17]byte - KiSparestrings [68]byte - KiSpareints [36]byte - KiCrFlags int32 - KiJid int32 - KiNumthreads int32 - KiTid int32 - KiPri int32 - KiRusage Rusage - KiRusageCh [144]byte - KiPcb int64 - KiKstack int64 - KiUdata int64 - KiTdaddr int64 - KiSpareptrs [48]byte - KiSpareint64s [96]byte - KiSflag int64 - KiTdflags int64 + Structsize int32 + Layout int32 + Args int64 /* pargs */ + Paddr int64 /* proc */ + Addr int64 /* user */ + Tracep int64 /* vnode */ + Textvp int64 /* vnode */ + Fd int64 /* filedesc */ + Vmspace int64 /* vmspace */ + Wchan int64 + Pid int32 + Ppid int32 + Pgid int32 + Tpgid int32 + Sid int32 + Tsid int32 + Jobc int16 + Spare_short1 int16 + Tdev uint32 + Siglist [16]byte /* sigset */ + Sigmask [16]byte /* sigset */ + Sigignore [16]byte /* sigset */ + Sigcatch [16]byte /* sigset */ + Uid uint32 + Ruid uint32 + Svuid uint32 + Rgid uint32 + Svgid uint32 + Ngroups int16 + Spare_short2 int16 + Groups [16]uint32 + Size uint64 + Rssize int64 + Swrss int64 + Tsize int64 + Dsize int64 + Ssize int64 + Xstat uint16 + Acflag uint16 + Pctcpu uint32 + Estcpu uint32 + Slptime uint32 + Swtime uint32 + Cow uint32 + Runtime uint64 + Start Timeval + Childtime Timeval + Flag int64 + Kiflag int64 + Traceflag int32 + Stat int8 + Nice int8 + Lock int8 + Rqindex int8 + Oncpu uint8 + Lastcpu uint8 + Tdname [17]int8 + Wmesg [9]int8 + Login [18]int8 + Lockname [9]int8 + Comm [20]int8 + Emul [17]int8 + Loginclass [18]int8 + Sparestrings [50]int8 + Spareints [7]int32 + Flag2 int32 + Fibnum int32 + Cr_flags uint32 + Jid int32 + Numthreads int32 + Tid int32 + Pri Priority + Rusage Rusage + Rusage_ch Rusage + Pcb int64 /* pcb */ + Kstack int64 + Udata int64 + Tdaddr int64 /* thread */ + Spareptrs [6]int64 + Sparelongs [12]int64 + Sflag int64 + Tdflags int64 +} + +type Priority struct { + Class uint8 + Level uint8 + Native uint8 + User uint8 +} + +type KinfoVmentry struct { + Structsize int32 + Type int32 + Start uint64 + End uint64 + Offset uint64 + Vn_fileid uint64 + Vn_fsid uint32 + Flags int32 + Resident int32 + Private_resident int32 + Protection int32 + Ref_count int32 + Shadow_count int32 + Vn_type int32 + Vn_size uint64 + Vn_rdev uint32 + Vn_mode uint16 + Status uint16 + X_kve_ispare [12]int32 + Path [1024]int8 } diff --git a/vendor/github.com/shirou/gopsutil/process/process_linux.go b/vendor/github.com/shirou/gopsutil/process/process_linux.go index f54af9f3619..ae300412c24 100644 --- a/vendor/github.com/shirou/gopsutil/process/process_linux.go +++ b/vendor/github.com/shirou/gopsutil/process/process_linux.go @@ -3,7 +3,9 @@ package process import ( + "bytes" "encoding/json" + "errors" "fmt" "io/ioutil" "os" @@ -18,6 +20,8 @@ import ( "github.com/shirou/gopsutil/net" ) +var ErrorNoChildren = errors.New("process does not have children") + const ( PrioProcess = 0 // linux/resource.h ) @@ -43,30 +47,35 @@ type MemoryMapsStat struct { Rss uint64 `json:"rss"` Size uint64 `json:"size"` Pss uint64 `json:"pss"` - SharedClean uint64 `json:"shared_clean"` - SharedDirty uint64 `json:"shared_dirty"` - PrivateClean uint64 `json:"private_clean"` - PrivateDirty uint64 `json:"private_dirty"` + SharedClean uint64 `json:"sharedClean"` + SharedDirty uint64 `json:"sharedDirty"` + PrivateClean uint64 `json:"privateClean"` + PrivateDirty uint64 `json:"privateDirty"` Referenced uint64 `json:"referenced"` Anonymous uint64 `json:"anonymous"` Swap uint64 `json:"swap"` } +// String returns JSON value of the process. func (m MemoryMapsStat) String() string { s, _ := json.Marshal(m) return string(s) } -// Create new Process instance -// This only stores Pid +// NewProcess creates a new Process instance, it only stores the pid and +// checks that the process exists. Other method on Process can be used +// to get more information about the process. An error will be returned +// if the process does not exist. func NewProcess(pid int32) (*Process, error) { p := &Process{ Pid: int32(pid), } - err := p.fillFromStatus() + file, err := os.Open(common.HostProc(strconv.Itoa(int(p.Pid)))) + defer file.Close() return p, err } +// Ppid returns Parent Process ID of the process. func (p *Process) Ppid() (int32, error) { _, ppid, _, _, _, err := p.fillFromStat() if err != nil { @@ -74,15 +83,35 @@ func (p *Process) Ppid() (int32, error) { } return ppid, nil } + +// Name returns name of the process. func (p *Process) Name() (string, error) { + if p.name == "" { + if err := p.fillFromStatus(); err != nil { + return "", err + } + } return p.name, nil } + +// Exe returns executable path of the process. func (p *Process) Exe() (string, error) { return p.fillFromExe() } + +// Cmdline returns the command line arguments of the process as a string with +// each argument separated by 0x20 ascii character. func (p *Process) Cmdline() (string, error) { return p.fillFromCmdline() } + +// CmdlineSlice returns the command line arguments of the process as a slice with each +// element being an argument. +func (p *Process) CmdlineSlice() ([]string, error) { + return p.fillSliceFromCmdline() +} + +// CreateTime returns created time of the process in seconds since the epoch, in UTC. func (p *Process) CreateTime() (int64, error) { _, _, _, createTime, _, err := p.fillFromStat() if err != nil { @@ -91,23 +120,28 @@ func (p *Process) CreateTime() (int64, error) { return createTime, nil } +// Cwd returns current working directory of the process. func (p *Process) Cwd() (string, error) { return p.fillFromCwd() } + +// Parent returns parent Process of the process. func (p *Process) Parent() (*Process, error) { - r, err := callLsof("R", p.Pid) + err := p.fillFromStatus() if err != nil { return nil, err } - if len(r) != 1 { // TODO: pid 1 + if p.parent == 0 { return nil, fmt.Errorf("wrong number of parents") } - v, err := strconv.Atoi(r[0]) - if err != nil { - return nil, err - } - return NewProcess(int32(v)) + return NewProcess(p.parent) } + +// Status returns the process status. +// Return value could be one of these. +// R: Running S: Sleep T: Stop I: Idle +// Z: Zombie W: Wait L: Lock +// The charactor is same within all supported platforms. func (p *Process) Status() (string, error) { err := p.fillFromStatus() if err != nil { @@ -115,6 +149,8 @@ func (p *Process) Status() (string, error) { } return p.status, nil } + +// Uids returns user ids of the process as a slice of the int func (p *Process) Uids() ([]int32, error) { err := p.fillFromStatus() if err != nil { @@ -122,6 +158,8 @@ func (p *Process) Uids() ([]int32, error) { } return p.uids, nil } + +// Gids returns group ids of the process as a slice of the int func (p *Process) Gids() ([]int32, error) { err := p.fillFromStatus() if err != nil { @@ -129,6 +167,8 @@ func (p *Process) Gids() ([]int32, error) { } return p.gids, nil } + +// Terminal returns a terminal which is associated with the process. func (p *Process) Terminal() (string, error) { terminal, _, _, _, _, err := p.fillFromStat() if err != nil { @@ -136,6 +176,9 @@ func (p *Process) Terminal() (string, error) { } return terminal, nil } + +// Nice returns a nice value (priority). +// Notice: gopsutil can not set nice value. func (p *Process) Nice() (int32, error) { _, _, _, _, nice, err := p.fillFromStat() if err != nil { @@ -143,15 +186,23 @@ func (p *Process) Nice() (int32, error) { } return nice, nil } + +// IOnice returns process I/O nice value (priority). func (p *Process) IOnice() (int32, error) { - return 0, common.NotImplementedError + return 0, common.ErrNotImplementedError } + +// Rlimit returns Resource Limits. func (p *Process) Rlimit() ([]RlimitStat, error) { - return nil, common.NotImplementedError + return nil, common.ErrNotImplementedError } + +// IOCounters returns IO Counters. func (p *Process) IOCounters() (*IOCountersStat, error) { return p.fillFromIO() } + +// NumCtxSwitches returns the number of the context switches of the process. func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { err := p.fillFromStatus() if err != nil { @@ -159,10 +210,14 @@ func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { } return p.numCtxSwitches, nil } + +// NumFDs returns the number of File Descriptors used by the process. func (p *Process) NumFDs() (int32, error) { numFds, _, err := p.fillFromfd() return numFds, err } + +// NumThreads returns the number of threads used by the process. func (p *Process) NumThreads() (int32, error) { err := p.fillFromStatus() if err != nil { @@ -170,27 +225,41 @@ func (p *Process) NumThreads() (int32, error) { } return p.numThreads, nil } + +// Threads returns a map of threads +// +// Notice: Not implemented yet. always returns empty map. func (p *Process) Threads() (map[string]string, error) { ret := make(map[string]string, 0) return ret, nil } -func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) { + +// Times returns CPU times of the process. +func (p *Process) Times() (*cpu.TimesStat, error) { _, _, cpuTimes, _, _, err := p.fillFromStat() if err != nil { return nil, err } return cpuTimes, nil } + +// CPUAffinity returns CPU affinity of the process. +// +// Notice: Not implemented yet. func (p *Process) CPUAffinity() ([]int32, error) { - return nil, common.NotImplementedError + return nil, common.ErrNotImplementedError } + +// MemoryInfo returns platform in-dependend memory information, such as RSS, VMS and Swap func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { - _, _, err := p.fillFromStatm() + meminfo, _, err := p.fillFromStatm() if err != nil { return nil, err } - return p.memInfo, nil + return meminfo, nil } + +// MemoryInfoEx returns platform dependend memory information. func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { _, memInfoEx, err := p.fillFromStatm() if err != nil { @@ -198,13 +267,14 @@ func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { } return memInfoEx, nil } -func (p *Process) MemoryPercent() (float32, error) { - return 0, common.NotImplementedError -} +// Children returns a slice of Process of the process. func (p *Process) Children() ([]*Process, error) { pids, err := common.CallPgrep(invoke, p.Pid) if err != nil { + if pids == nil || len(pids) == 0 { + return nil, ErrorNoChildren + } return nil, err } ret := make([]*Process, 0, len(pids)) @@ -218,12 +288,14 @@ func (p *Process) Children() ([]*Process, error) { return ret, nil } +// OpenFiles returns a slice of OpenFilesStat opend by the process. +// OpenFilesStat includes a file path and file descriptor. func (p *Process) OpenFiles() ([]OpenFilesStat, error) { _, ofs, err := p.fillFromfd() if err != nil { return nil, err } - ret := make([]OpenFilesStat, 0, len(ofs)) + ret := make([]OpenFilesStat, len(ofs)) for i, o := range ofs { ret[i] = *o } @@ -231,12 +303,22 @@ func (p *Process) OpenFiles() ([]OpenFilesStat, error) { return ret, nil } -func (p *Process) Connections() ([]net.NetConnectionStat, error) { - return net.NetConnectionsPid("all", p.Pid) +// Connections returns a slice of net.ConnectionStat used by the process. +// This returns all kind of the connection. This measn TCP, UDP or UNIX. +func (p *Process) Connections() ([]net.ConnectionStat, error) { + return net.ConnectionsPid("all", p.Pid) +} + +// NetIOCounters returns NetIOCounters of the process. +func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) { + filename := common.HostProc(strconv.Itoa(int(p.Pid)), "net/dev") + return net.IOCountersByFile(pernic, filename) } +// IsRunning returns whether the process is running or not. +// Not implemented yet. func (p *Process) IsRunning() (bool, error) { - return true, common.NotImplementedError + return true, common.ErrNotImplementedError } // MemoryMaps get memory maps from /proc/(pid)/smaps @@ -333,7 +415,7 @@ func (p *Process) fillFromfd() (int32, []*OpenFilesStat, error) { fnames, err := d.Readdirnames(-1) numFDs := int32(len(fnames)) - openfiles := make([]*OpenFilesStat, numFDs) + var openfiles []*OpenFilesStat for _, fd := range fnames { fpath := filepath.Join(statPath, fd) filepath, err := os.Readlink(fpath) @@ -394,6 +476,28 @@ func (p *Process) fillFromCmdline() (string, error) { return strings.Join(ret, " "), nil } +func (p *Process) fillSliceFromCmdline() ([]string, error) { + pid := p.Pid + cmdPath := common.HostProc(strconv.Itoa(int(pid)), "cmdline") + cmdline, err := ioutil.ReadFile(cmdPath) + if err != nil { + return nil, err + } + if len(cmdline) == 0 { + return nil, nil + } + if cmdline[len(cmdline)-1] == 0 { + cmdline = cmdline[:len(cmdline)-1] + } + parts := bytes.Split(cmdline, []byte{0}) + var strParts []string + for _, p := range parts { + strParts = append(strParts, string(p)) + } + + return strParts, nil +} + // Get IO status from /proc/(pid)/io func (p *Process) fillFromIO() (*IOCountersStat, error) { pid := p.Pid @@ -423,9 +527,9 @@ func (p *Process) fillFromIO() (*IOCountersStat, error) { ret.ReadCount = t case "syscw": ret.WriteCount = t - case "read_bytes": + case "readBytes": ret.ReadBytes = t - case "write_bytes": + case "writeBytes": ret.WriteBytes = t } } @@ -506,10 +610,13 @@ func (p *Process) fillFromStatus() error { case "Name": p.name = strings.Trim(value, " \t") case "State": - // get between "(" and ")" - s := strings.Index(value, "(") + 1 - e := strings.Index(value, ")") - p.status = value[s:e] + p.status = value[0:1] + case "PPid", "Ppid": + pval, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return err + } + p.parent = int32(pval) case "Uid": p.uids = make([]int32, 0, 4) for _, i := range strings.Split(value, "\t") { @@ -573,7 +680,7 @@ func (p *Process) fillFromStatus() error { return nil } -func (p *Process) fillFromStat() (string, int32, *cpu.CPUTimesStat, int64, int32, error) { +func (p *Process) fillFromStat() (string, int32, *cpu.TimesStat, int64, int32, error) { pid := p.Pid statPath := common.HostProc(strconv.Itoa(int(pid)), "stat") contents, err := ioutil.ReadFile(statPath) @@ -611,7 +718,7 @@ func (p *Process) fillFromStat() (string, int32, *cpu.CPUTimesStat, int64, int32 return "", 0, nil, 0, 0, err } - cpuTimes := &cpu.CPUTimesStat{ + cpuTimes := &cpu.TimesStat{ CPU: "cpu", User: float64(utime / ClockTicks), System: float64(stime / ClockTicks), @@ -633,6 +740,7 @@ func (p *Process) fillFromStat() (string, int32, *cpu.CPUTimesStat, int64, int32 return terminal, int32(ppid), cpuTimes, createTime, nice, nil } +// Pids returns a slice of process ID list which are running now. func Pids() ([]int32, error) { var ret []int32 @@ -657,26 +765,3 @@ func Pids() ([]int32, error) { return ret, nil } - -func callLsof(arg string, pid int32) ([]string, error) { - var cmd []string - if pid == 0 { // will get from all processes. - cmd = []string{"-F" + arg} - } else { - cmd = []string{"-a", "-F" + arg, "-p", strconv.Itoa(int(pid))} - } - out, err := invoke.Command("/usr/bin/lsof", cmd...) - if err != nil { - return []string{}, err - } - lines := strings.Split(string(out), "\n") - - var ret []string - for _, l := range lines[1:] { - if strings.HasPrefix(l, arg) { - ret = append(ret, l[1:]) // delete first char - } - } - - return ret, nil -} diff --git a/vendor/github.com/shirou/gopsutil/process/process_posix.go b/vendor/github.com/shirou/gopsutil/process/process_posix.go index 38f06e9e753..8853118ac65 100644 --- a/vendor/github.com/shirou/gopsutil/process/process_posix.go +++ b/vendor/github.com/shirou/gopsutil/process/process_posix.go @@ -51,6 +51,8 @@ func getTerminalMap() (map[uint64]string, error) { return ret, nil } +// SendSignal sends a syscall.Signal to the process. +// Currently, SIGSTOP, SIGCONT, SIGTERM and SIGKILL are supported. func (p *Process) SendSignal(sig syscall.Signal) error { sigAsStr := "INT" switch sig { @@ -64,9 +66,13 @@ func (p *Process) SendSignal(sig syscall.Signal) error { sigAsStr = "KILL" } - cmd := exec.Command("kill", "-s", sigAsStr, strconv.Itoa(int(p.Pid))) + kill, err := exec.LookPath("kill") + if err != nil { + return err + } + cmd := exec.Command(kill, "-s", sigAsStr, strconv.Itoa(int(p.Pid))) cmd.Stderr = os.Stderr - err := cmd.Run() + err = cmd.Run() if err != nil { return err } @@ -74,18 +80,27 @@ func (p *Process) SendSignal(sig syscall.Signal) error { return nil } +// Suspend sends SIGSTOP to the process. func (p *Process) Suspend() error { return p.SendSignal(syscall.SIGSTOP) } + +// Resume sends SIGCONT to the process. func (p *Process) Resume() error { return p.SendSignal(syscall.SIGCONT) } + +// Terminate sends SIGTERM to the process. func (p *Process) Terminate() error { return p.SendSignal(syscall.SIGTERM) } + +// Kill sends SIGKILL to the process. func (p *Process) Kill() error { return p.SendSignal(syscall.SIGKILL) } + +// Username returns a username of the process. func (p *Process) Username() (string, error) { uids, err := p.Uids() if err != nil { diff --git a/vendor/github.com/shirou/gopsutil/process/process_windows.go b/vendor/github.com/shirou/gopsutil/process/process_windows.go index d7a4b1cb6fa..3176cde05a7 100644 --- a/vendor/github.com/shirou/gopsutil/process/process_windows.go +++ b/vendor/github.com/shirou/gopsutil/process/process_windows.go @@ -5,6 +5,7 @@ package process import ( "errors" "fmt" + "strings" "syscall" "time" "unsafe" @@ -12,8 +13,8 @@ import ( "github.com/StackExchange/wmi" "github.com/shirou/w32" - "github.com/shirou/gopsutil/internal/common" cpu "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/internal/common" net "github.com/shirou/gopsutil/net" ) @@ -50,7 +51,7 @@ type Win32_Process struct { CommandLine *string Priority uint32 CreationDate *time.Time - ProcessId uint32 + ProcessID uint32 ThreadCount uint32 /* @@ -70,7 +71,7 @@ type Win32_Process struct { OtherTransferCount uint64 PageFaults uint32 PageFileUsage uint32 - ParentProcessId uint32 + ParentProcessID uint32 PeakPageFileUsage uint32 PeakVirtualSize uint64 PeakWorkingSetSize uint32 @@ -145,6 +146,17 @@ func (p *Process) Cmdline() (string, error) { return *dst[0].CommandLine, nil } +// CmdlineSlice returns the command line arguments of the process as a slice with each +// element being an argument. This merely returns the CommandLine informations passed +// to the process split on the 0x20 ASCII character. +func (p *Process) CmdlineSlice() ([]string, error) { + cmdline, err := p.Cmdline() + if err != nil { + return nil, err + } + return strings.Split(cmdline, " "), nil +} + func (p *Process) CreateTime() (int64, error) { dst, err := GetWin32Proc(p.Pid) if err != nil { @@ -155,28 +167,28 @@ func (p *Process) CreateTime() (int64, error) { } func (p *Process) Cwd() (string, error) { - return "", common.NotImplementedError + return "", common.ErrNotImplementedError } func (p *Process) Parent() (*Process, error) { - return p, common.NotImplementedError + return p, common.ErrNotImplementedError } func (p *Process) Status() (string, error) { - return "", common.NotImplementedError + return "", common.ErrNotImplementedError } func (p *Process) Username() (string, error) { - return "", common.NotImplementedError + return "", common.ErrNotImplementedError } func (p *Process) Uids() ([]int32, error) { var uids []int32 - return uids, common.NotImplementedError + return uids, common.ErrNotImplementedError } func (p *Process) Gids() ([]int32, error) { var gids []int32 - return gids, common.NotImplementedError + return gids, common.ErrNotImplementedError } func (p *Process) Terminal() (string, error) { - return "", common.NotImplementedError + return "", common.ErrNotImplementedError } // Nice returnes priority in Windows @@ -188,21 +200,21 @@ func (p *Process) Nice() (int32, error) { return int32(dst[0].Priority), nil } func (p *Process) IOnice() (int32, error) { - return 0, common.NotImplementedError + return 0, common.ErrNotImplementedError } func (p *Process) Rlimit() ([]RlimitStat, error) { var rlimit []RlimitStat - return rlimit, common.NotImplementedError + return rlimit, common.ErrNotImplementedError } func (p *Process) IOCounters() (*IOCountersStat, error) { - return nil, common.NotImplementedError + return nil, common.ErrNotImplementedError } func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { - return nil, common.NotImplementedError + return nil, common.ErrNotImplementedError } func (p *Process) NumFDs() (int32, error) { - return 0, common.NotImplementedError + return 0, common.ErrNotImplementedError } func (p *Process) NumThreads() (int32, error) { dst, err := GetWin32Proc(p.Pid) @@ -213,43 +225,44 @@ func (p *Process) NumThreads() (int32, error) { } func (p *Process) Threads() (map[string]string, error) { ret := make(map[string]string, 0) - return ret, common.NotImplementedError + return ret, common.ErrNotImplementedError } -func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) { - return nil, common.NotImplementedError +func (p *Process) Times() (*cpu.TimesStat, error) { + return nil, common.ErrNotImplementedError } func (p *Process) CPUAffinity() ([]int32, error) { - return nil, common.NotImplementedError + return nil, common.ErrNotImplementedError } func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { - return nil, common.NotImplementedError + return nil, common.ErrNotImplementedError } func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { - return nil, common.NotImplementedError -} -func (p *Process) MemoryPercent() (float32, error) { - return 0, common.NotImplementedError + return nil, common.ErrNotImplementedError } func (p *Process) Children() ([]*Process, error) { - return nil, common.NotImplementedError + return nil, common.ErrNotImplementedError } func (p *Process) OpenFiles() ([]OpenFilesStat, error) { - return nil, common.NotImplementedError + return nil, common.ErrNotImplementedError +} + +func (p *Process) Connections() ([]net.ConnectionStat, error) { + return nil, common.ErrNotImplementedError } -func (p *Process) Connections() ([]net.NetConnectionStat, error) { - return nil, common.NotImplementedError +func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) { + return nil, common.ErrNotImplementedError } func (p *Process) IsRunning() (bool, error) { - return true, common.NotImplementedError + return true, common.ErrNotImplementedError } func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { - ret := make([]MemoryMapsStat, 0) - return &ret, common.NotImplementedError + var ret []MemoryMapsStat + return &ret, common.ErrNotImplementedError } func NewProcess(pid int32) (*Process, error) { @@ -259,20 +272,20 @@ func NewProcess(pid int32) (*Process, error) { } func (p *Process) SendSignal(sig syscall.Signal) error { - return common.NotImplementedError + return common.ErrNotImplementedError } func (p *Process) Suspend() error { - return common.NotImplementedError + return common.ErrNotImplementedError } func (p *Process) Resume() error { - return common.NotImplementedError + return common.ErrNotImplementedError } func (p *Process) Terminate() error { - return common.NotImplementedError + return common.ErrNotImplementedError } func (p *Process) Kill() error { - return common.NotImplementedError + return common.ErrNotImplementedError } func (p *Process) getFromSnapProcess(pid int32) (int32, int32, string, error) { @@ -315,7 +328,7 @@ func processes() ([]*Process, error) { } results := make([]*Process, 0, len(dst)) for _, proc := range dst { - p, err := NewProcess(int32(proc.ProcessId)) + p, err := NewProcess(int32(proc.ProcessID)) if err != nil { continue } diff --git a/vendor/github.com/shirou/gopsutil/process/types_freebsd.go b/vendor/github.com/shirou/gopsutil/process/types_freebsd.go new file mode 100644 index 00000000000..aa7b3462de6 --- /dev/null +++ b/vendor/github.com/shirou/gopsutil/process/types_freebsd.go @@ -0,0 +1,95 @@ +// +build ignore + +// We still need editing by hands. +// go tool cgo -godefs types_freebsd.go | sed 's/\*int64/int64/' | sed 's/\*byte/int64/' > process_freebsd_amd64.go + +/* +Input to cgo -godefs. +*/ + +// +godefs map struct_pargs int64 /* pargs */ +// +godefs map struct_proc int64 /* proc */ +// +godefs map struct_user int64 /* user */ +// +godefs map struct_vnode int64 /* vnode */ +// +godefs map struct_vnode int64 /* vnode */ +// +godefs map struct_filedesc int64 /* filedesc */ +// +godefs map struct_vmspace int64 /* vmspace */ +// +godefs map struct_pcb int64 /* pcb */ +// +godefs map struct_thread int64 /* thread */ +// +godefs map struct___sigset [16]byte /* sigset */ + +package process + +/* +#include +#include + +enum { + sizeofPtr = sizeof(void*), +}; + + +*/ +import "C" + +// Machine characteristics; for internal use. + +const ( + CTLKern = 1 // "high kernel": proc, limits + KernProc = 14 // struct: process entries + KernProcPID = 1 // by process id + KernProcProc = 8 // only return procs + KernProcPathname = 12 // path to executable + KernProcArgs = 7 // get/set arguments/proctitle +) + +const ( + sizeofPtr = C.sizeofPtr + sizeofShort = C.sizeof_short + sizeofInt = C.sizeof_int + sizeofLong = C.sizeof_long + sizeofLongLong = C.sizeof_longlong +) + +const ( + sizeOfKinfoVmentry = C.sizeof_struct_kinfo_vmentry + sizeOfKinfoProc = C.sizeof_struct_kinfo_proc +) + +// from sys/proc.h +const ( + SIDL = 1 /* Process being created by fork. */ + SRUN = 2 /* Currently runnable. */ + SSLEEP = 3 /* Sleeping on an address. */ + SSTOP = 4 /* Process debugging or suspension. */ + SZOMB = 5 /* Awaiting collection by parent. */ + SWAIT = 6 /* Waiting for interrupt. */ + SLOCK = 7 /* Blocked on a lock. */ +) + +// Basic types + +type ( + _C_short C.short + _C_int C.int + _C_long C.long + _C_long_long C.longlong +) + +// Time + +type Timespec C.struct_timespec + +type Timeval C.struct_timeval + +// Processes + +type Rusage C.struct_rusage + +type Rlimit C.struct_rlimit + +type KinfoProc C.struct_kinfo_proc + +type Priority C.struct_priority + +type KinfoVmentry C.struct_kinfo_vmentry diff --git a/vendor/vendor.json b/vendor/vendor.json index f1e55169a01..32b3328a6ea 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -606,34 +606,46 @@ "revision": "983d3a5fab1bf04d1b412465d2d9f8430e2e917e" }, { + "checksumSHA1": "dtcffK/n1nDKPtvHg2GnU1C194s=", "comment": "1.0.0-230-gf58654f", "path": "github.com/shirou/gopsutil/cpu", - "revision": "f58654fa1c30aab9b8c503ecea4922e80abcd2bf" + "revision": "7b991b8135166c7fc81b65665a2f35c7a1966cfc", + "revisionTime": "2016-05-04T15:08:50Z" }, { + "checksumSHA1": "lpPewrgMOYaTKZ0wRZpmZJxnEBw=", "comment": "1.0.0-230-gf58654f", "path": "github.com/shirou/gopsutil/host", - "revision": "f58654fa1c30aab9b8c503ecea4922e80abcd2bf" + "revision": "7b991b8135166c7fc81b65665a2f35c7a1966cfc", + "revisionTime": "2016-05-04T15:08:50Z" }, { + "checksumSHA1": "xrMuUxCr8UckY5oI2lwCerYwIhk=", "comment": "1.0.0-230-gf58654f", "path": "github.com/shirou/gopsutil/internal/common", - "revision": "f58654fa1c30aab9b8c503ecea4922e80abcd2bf" + "revision": "7b991b8135166c7fc81b65665a2f35c7a1966cfc", + "revisionTime": "2016-05-04T15:08:50Z" }, { + "checksumSHA1": "bHTDi2QAN4SmdZpHOtyQ6fQeCnQ=", "comment": "1.0.0-230-gf58654f", "path": "github.com/shirou/gopsutil/mem", - "revision": "f58654fa1c30aab9b8c503ecea4922e80abcd2bf" + "revision": "7b991b8135166c7fc81b65665a2f35c7a1966cfc", + "revisionTime": "2016-05-04T15:08:50Z" }, { + "checksumSHA1": "LHJDMUXWamoP/yje0R42k96OPG8=", "comment": "1.0.0-230-gf58654f", "path": "github.com/shirou/gopsutil/net", - "revision": "f58654fa1c30aab9b8c503ecea4922e80abcd2bf" + "revision": "7b991b8135166c7fc81b65665a2f35c7a1966cfc", + "revisionTime": "2016-05-04T15:08:50Z" }, { + "checksumSHA1": "o544bXejDPLi6qpjHav8a91W4Ks=", "comment": "1.0.0-230-gf58654f", "path": "github.com/shirou/gopsutil/process", - "revision": "f58654fa1c30aab9b8c503ecea4922e80abcd2bf" + "revision": "7b991b8135166c7fc81b65665a2f35c7a1966cfc", + "revisionTime": "2016-05-04T15:08:50Z" }, { "path": "github.com/shirou/w32", From fe8f640cc8030e9f3e205c8b1c2526584d857e6f Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Mon, 9 May 2016 08:55:19 -0700 Subject: [PATCH 08/69] Collecting host stats --- client/client.go | 74 +++++++++++++++++++++++++++------ client/stats/host.go | 55 ++++++++++++++++++++++++ command/agent/stats_endpoint.go | 14 ++++--- 3 files changed, 125 insertions(+), 18 deletions(-) create mode 100644 client/stats/host.go diff --git a/client/client.go b/client/client.go index 6e60e6bd8cf..bb2a37067a3 100644 --- a/client/client.go +++ b/client/client.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/nomad/client/consul" "github.com/hashicorp/nomad/client/driver" "github.com/hashicorp/nomad/client/fingerprint" + "github.com/hashicorp/nomad/client/stats" "github.com/hashicorp/nomad/nomad" "github.com/hashicorp/nomad/nomad/structs" "github.com/mitchellh/hashstructure" @@ -81,6 +82,11 @@ func DefaultConfig() *config.Config { } } +type ClientStatsReporter interface { + AllocStats() map[string]AllocStatsReporter + HostStats() *stats.HostStats +} + // Client is used to implement the client interaction with Nomad. Clients // are expected to register as a schedulable node to the servers, and to // run allocations as determined by the servers. @@ -116,6 +122,9 @@ type Client struct { consulService *consul.ConsulService + resourceUsage *stats.RingBuff + resourceUsageLock sync.RWMutex + shutdown bool shutdownCh chan struct{} shutdownLock sync.Mutex @@ -126,15 +135,21 @@ func NewClient(cfg *config.Config) (*Client, error) { // Create a logger logger := log.New(cfg.LogOutput, "", log.LstdFlags) + resourceUsage, err := stats.NewRingBuff(60) + if err != nil { + return nil, err + } + // Create the client c := &Client{ - config: cfg, - start: time.Now(), - connPool: nomad.NewPool(cfg.LogOutput, clientRPCCache, clientMaxStreams, nil), - logger: logger, - allocs: make(map[string]*AllocRunner), - allocUpdates: make(chan *structs.Allocation, 64), - shutdownCh: make(chan struct{}), + config: cfg, + start: time.Now(), + connPool: nomad.NewPool(cfg.LogOutput, clientRPCCache, clientMaxStreams, nil), + logger: logger, + resourceUsage: resourceUsage, + allocs: make(map[string]*AllocRunner), + allocUpdates: make(chan *structs.Allocation, 64), + shutdownCh: make(chan struct{}), } // Initialize the client @@ -189,6 +204,9 @@ func NewClient(cfg *config.Config) (*Client, error) { // Start the client! go c.run() + // Start collecting stats + go c.monitorUsage() + // Start the consul sync go c.syncConsul() @@ -394,12 +412,24 @@ func (c *Client) Node() *structs.Node { return c.config.Node } -func (c *Client) AllocStats(alloc string) (AllocStatsReporter, error) { - ar, ok := c.allocs[alloc] - if !ok { - return nil, fmt.Errorf("allocation: %q not running on this client", alloc) +func (c *Client) StatsReporter() ClientStatsReporter { + return c +} + +func (c *Client) AllocStats() map[string]AllocStatsReporter { + res := make(map[string]AllocStatsReporter) + for alloc, ar := range c.allocs { + res[alloc] = ar } - return ar.StatsReporter(), nil + return res +} + +func (c *Client) HostStats() *stats.HostStats { + val := c.resourceUsage.Peek() + if val != nil { + return val.(*stats.HostStats) + } + return nil } // GetAllocFS returns the AllocFS interface for the alloc dir of an allocation @@ -1235,3 +1265,23 @@ func (c *Client) syncConsul() { } } + +func (c *Client) monitorUsage() { + for { + next := time.NewTimer(1 * time.Second) + select { + case <-next.C: + ru, err := stats.CollectHostStats() + if err != nil { + c.logger.Printf("[DEBUG] client: error fetching stats of host: %v", err) + } + c.resourceUsageLock.RLock() + c.resourceUsage.Enqueue(ru) + c.resourceUsageLock.RUnlock() + next.Reset(1 * time.Second) + case <-c.shutdownCh: + next.Stop() + return + } + } +} diff --git a/client/stats/host.go b/client/stats/host.go new file mode 100644 index 00000000000..53c29d32e99 --- /dev/null +++ b/client/stats/host.go @@ -0,0 +1,55 @@ +package stats + +import ( + "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/mem" +) + +type HostStats struct { + Memory *MemoryStats + CPU []*CPUStats +} + +type MemoryStats struct { + Total uint64 + Available uint64 + Used uint64 + Free uint64 +} + +type CPUStats struct { + CPU string + User float64 + System float64 + Idle float64 +} + +func CollectHostStats() (*HostStats, error) { + memStats, err := mem.VirtualMemory() + if err != nil { + return nil, err + } + ms := &MemoryStats{ + Total: memStats.Total, + Available: memStats.Available, + Used: memStats.Used, + Free: memStats.Free, + } + + cpuStats, err := cpu.Times(true) + cs := make([]*CPUStats, len(cpuStats)) + for idx, cpuStat := range cpuStats { + cs[idx] = &CPUStats{ + CPU: cpuStat.CPU, + User: cpuStat.User, + System: cpuStat.System, + Idle: cpuStat.Idle, + } + } + + hs := &HostStats{ + Memory: ms, + CPU: cs, + } + return hs, nil +} diff --git a/command/agent/stats_endpoint.go b/command/agent/stats_endpoint.go index 66c64217fa9..276d4b38b47 100644 --- a/command/agent/stats_endpoint.go +++ b/command/agent/stats_endpoint.go @@ -9,23 +9,25 @@ func (s *HTTPServer) StatsRequest(resp http.ResponseWriter, req *http.Request) ( if s.agent.client == nil { return nil, clientNotRunning } + cStatsReporter := s.agent.client.StatsReporter() var allocID, task string if allocID = req.URL.Query().Get("allocation"); allocID == "" { - return nil, fmt.Errorf("provide a valid alloc id") + return cStatsReporter.HostStats(), nil } - statsReporter, err := s.agent.client.AllocStats(allocID) - if err != nil { - return nil, err + allocStats := cStatsReporter.AllocStats() + arStatsReporter, ok := allocStats[allocID] + if !ok { + return nil, fmt.Errorf("alloc %q is not running on this client", allocID) } if task = req.URL.Query().Get("task"); task != "" { - taskStatsReporter, err := statsReporter.TaskStats(task) + taskStatsReporter, err := arStatsReporter.TaskStats(task) if err != nil { return nil, err } return taskStatsReporter.ResourceUsage(), nil } res := make(map[string]interface{}) - for task, sr := range statsReporter.AllocStats() { + for task, sr := range arStatsReporter.AllocStats() { res[task] = sr.ResourceUsage() } return res, nil From 7d8196d7ea56b2d0f12af06e46886e1b351c4c32 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Mon, 9 May 2016 09:53:00 -0700 Subject: [PATCH 09/69] Added some documentation --- client/alloc_runner.go | 7 +++++++ client/driver/executor/executor_linux.go | 1 + client/stats/host.go | 4 ++++ client/stats/ring.go | 7 +++++++ client/task_runner.go | 6 ++++++ 5 files changed, 25 insertions(+) diff --git a/client/alloc_runner.go b/client/alloc_runner.go index 073d383158b..6dc47f72874 100644 --- a/client/alloc_runner.go +++ b/client/alloc_runner.go @@ -28,6 +28,7 @@ const ( // AllocStateUpdater is used to update the status of an allocation type AllocStateUpdater func(alloc *structs.Allocation) +// AllocStatsReporter exposes stats related APIs of an allocation runner type AllocStatsReporter interface { AllocStats() map[string]TaskStatsReporter TaskStats(task string) (TaskStatsReporter, error) @@ -476,10 +477,14 @@ func (r *AllocRunner) Update(update *structs.Allocation) { } } +// StatsReporter returns an interface to query resource usage statistics of an +// allocation func (r *AllocRunner) StatsReporter() AllocStatsReporter { return r } +// AllocStats returns the stats reporter of all the tasks running in the +// allocation func (r *AllocRunner) AllocStats() map[string]TaskStatsReporter { res := make(map[string]TaskStatsReporter) for task, tr := range r.tasks { @@ -488,6 +493,8 @@ func (r *AllocRunner) AllocStats() map[string]TaskStatsReporter { return res } +// TaskStats returns the stats reporter for a specific task running in the +// allocation func (r *AllocRunner) TaskStats(task string) (TaskStatsReporter, error) { tr, ok := r.tasks[task] if !ok { diff --git a/client/driver/executor/executor_linux.go b/client/driver/executor/executor_linux.go index 2aa92fe3188..a2b8a593a4a 100644 --- a/client/driver/executor/executor_linux.go +++ b/client/driver/executor/executor_linux.go @@ -124,6 +124,7 @@ func (e *UniversalExecutor) configureCgroups(resources *structs.Resources) error return nil } +// Stats reports the resource utilization of the cgroup func (e *UniversalExecutor) Stats() (*cstructs.TaskResourceUsage, error) { manager := getCgroupManager(e.groups, e.cgPaths) stats, err := manager.GetStats() diff --git a/client/stats/host.go b/client/stats/host.go index 53c29d32e99..09a2f2fa70b 100644 --- a/client/stats/host.go +++ b/client/stats/host.go @@ -5,11 +5,13 @@ import ( "github.com/shirou/gopsutil/mem" ) +// HostStats represents resource usage stats of the host running a Nomad client type HostStats struct { Memory *MemoryStats CPU []*CPUStats } +// MemoryStats represnts stats related to virtual memory usage type MemoryStats struct { Total uint64 Available uint64 @@ -17,6 +19,7 @@ type MemoryStats struct { Free uint64 } +// CPUStats represents stats related to cpu usage type CPUStats struct { CPU string User float64 @@ -24,6 +27,7 @@ type CPUStats struct { Idle float64 } +// CollectHostStats collects stats related to resource usage of a host func CollectHostStats() (*HostStats, error) { memStats, err := mem.VirtualMemory() if err != nil { diff --git a/client/stats/ring.go b/client/stats/ring.go index d20dd895cb2..b9dbbf7c7d6 100644 --- a/client/stats/ring.go +++ b/client/stats/ring.go @@ -5,14 +5,17 @@ import ( ) var ( + // The default size of the ring buffer defaultCap = 60 ) +// RingBuff is a data structure which is a circular list based on slices type RingBuff struct { head int buff []interface{} } +// NewRingBuff creates a new ring buffer of the specified size func NewRingBuff(capacity int) (*RingBuff, error) { if capacity < 1 { return nil, fmt.Errorf("can not create a ring buffer with capacity: %v", capacity) @@ -20,6 +23,8 @@ func NewRingBuff(capacity int) (*RingBuff, error) { return &RingBuff{buff: make([]interface{}, capacity), head: -1}, nil } +// Enqueue queues a new value in the ring buffer. This operation would +// over-write an older value if the list has reached it's capacity func (r *RingBuff) Enqueue(value interface{}) { r.head += 1 if r.head == len(r.buff) { @@ -28,10 +33,12 @@ func (r *RingBuff) Enqueue(value interface{}) { r.buff[r.head] = value } +// Peek returns the last value enqueued in the ring buffer func (r *RingBuff) Peek() interface{} { return r.buff[r.head] } +// Values returns all the values in the buffer. func (r *RingBuff) Values() []interface{} { if r.head == len(r.buff)-1 { vals := make([]interface{}, len(r.buff)) diff --git a/client/task_runner.go b/client/task_runner.go index 307b85cd4d8..9574506f027 100644 --- a/client/task_runner.go +++ b/client/task_runner.go @@ -35,6 +35,7 @@ const ( killFailureLimit = 5 ) +// TaskStatsReporter exposes APIs to query resource usage of a Task type TaskStatsReporter interface { ResourceUsage() *cstructs.TaskResourceUsage ResourceUsageTS() []*cstructs.TaskResourceUsage @@ -458,6 +459,7 @@ func (r *TaskRunner) startTask() error { return nil } +// monitorUsage starts collecting resource usage stats of a Task func (r *TaskRunner) monitorUsage() { for { next := time.NewTimer(1 * time.Second) @@ -478,10 +480,12 @@ func (r *TaskRunner) monitorUsage() { } } +// TaskStatsReporter returns the stats reporter of the task func (r *TaskRunner) StatsReporter() TaskStatsReporter { return r } +// ResourceUsage returns the last resource utilization datapoint collected func (r *TaskRunner) ResourceUsage() *cstructs.TaskResourceUsage { r.resourceUsageLock.RLock() defer r.resourceUsageLock.RUnlock() @@ -492,6 +496,8 @@ func (r *TaskRunner) ResourceUsage() *cstructs.TaskResourceUsage { return nil } +// ResourceUsageTS returns the list of all the resource utilization datapoints +// collected func (r *TaskRunner) ResourceUsageTS() []*cstructs.TaskResourceUsage { r.resourceUsageLock.RLock() defer r.resourceUsageLock.RUnlock() From 1f12e90b9bcd759c225ce6a63d6c00ed48d42c0c Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Mon, 9 May 2016 10:18:33 -0700 Subject: [PATCH 10/69] Returning nil if peek is called before any value is enqueued --- client/stats/ring.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/stats/ring.go b/client/stats/ring.go index b9dbbf7c7d6..9ef01748e3c 100644 --- a/client/stats/ring.go +++ b/client/stats/ring.go @@ -35,6 +35,9 @@ func (r *RingBuff) Enqueue(value interface{}) { // Peek returns the last value enqueued in the ring buffer func (r *RingBuff) Peek() interface{} { + if r.head == -1 { + return nil + } return r.buff[r.head] } From 3f0c42cca3f0bce9e33363920c7a7c6d7b0a609f Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Mon, 9 May 2016 12:11:41 -0700 Subject: [PATCH 11/69] Making the conversion to Stats simpler --- client/client.go | 6 ++---- client/task_runner.go | 9 ++++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/client/client.go b/client/client.go index bb2a37067a3..c9a6c70fb44 100644 --- a/client/client.go +++ b/client/client.go @@ -426,10 +426,8 @@ func (c *Client) AllocStats() map[string]AllocStatsReporter { func (c *Client) HostStats() *stats.HostStats { val := c.resourceUsage.Peek() - if val != nil { - return val.(*stats.HostStats) - } - return nil + ru, _ := val.(*stats.HostStats) + return ru } // GetAllocFS returns the AllocFS interface for the alloc dir of an allocation diff --git a/client/task_runner.go b/client/task_runner.go index 9574506f027..1879499156a 100644 --- a/client/task_runner.go +++ b/client/task_runner.go @@ -490,10 +490,8 @@ func (r *TaskRunner) ResourceUsage() *cstructs.TaskResourceUsage { r.resourceUsageLock.RLock() defer r.resourceUsageLock.RUnlock() val := r.resourceUsage.Peek() - if val != nil { - return val.(*cstructs.TaskResourceUsage) - } - return nil + ru, _ := val.(*cstructs.TaskResourceUsage) + return ru } // ResourceUsageTS returns the list of all the resource utilization datapoints @@ -504,7 +502,8 @@ func (r *TaskRunner) ResourceUsageTS() []*cstructs.TaskResourceUsage { values := r.resourceUsage.Values() ts := make([]*cstructs.TaskResourceUsage, len(values)) for index, val := range values { - ts[index] = val.(*cstructs.TaskResourceUsage) + ru, _ := val.(*cstructs.TaskResourceUsage) + ts[index] = ru } return ts } From 72c60d6b995b7cfede0dd34488b7ece2ed2d2025 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Mon, 9 May 2016 12:24:03 -0700 Subject: [PATCH 12/69] Added some docs to resource stats endpoint --- client/client.go | 7 +++++++ command/agent/stats_endpoint.go | 25 +++++++++++++++++-------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/client/client.go b/client/client.go index c9a6c70fb44..2f7e379080a 100644 --- a/client/client.go +++ b/client/client.go @@ -82,6 +82,8 @@ func DefaultConfig() *config.Config { } } +// ClientStatsReporter exposes all the APIs related to resource usage of a Nomad +// Client type ClientStatsReporter interface { AllocStats() map[string]AllocStatsReporter HostStats() *stats.HostStats @@ -412,10 +414,14 @@ func (c *Client) Node() *structs.Node { return c.config.Node } +// StatsReporter exposes the various APIs related resource usage of a Nomad +// client func (c *Client) StatsReporter() ClientStatsReporter { return c } +// AllocStats returns all the stats reporter of the allocations running on a +// Nomad client func (c *Client) AllocStats() map[string]AllocStatsReporter { res := make(map[string]AllocStatsReporter) for alloc, ar := range c.allocs { @@ -424,6 +430,7 @@ func (c *Client) AllocStats() map[string]AllocStatsReporter { return res } +// HostStats returns all the stats related to a Nomad client func (c *Client) HostStats() *stats.HostStats { val := c.resourceUsage.Peek() ru, _ := val.(*stats.HostStats) diff --git a/command/agent/stats_endpoint.go b/command/agent/stats_endpoint.go index 276d4b38b47..35d0d8c658d 100644 --- a/command/agent/stats_endpoint.go +++ b/command/agent/stats_endpoint.go @@ -9,26 +9,35 @@ func (s *HTTPServer) StatsRequest(resp http.ResponseWriter, req *http.Request) ( if s.agent.client == nil { return nil, clientNotRunning } - cStatsReporter := s.agent.client.StatsReporter() + + clientStats := s.agent.client.StatsReporter() + + // Return the host stats if alloc ID is not present var allocID, task string if allocID = req.URL.Query().Get("allocation"); allocID == "" { - return cStatsReporter.HostStats(), nil + return clientStats.HostStats(), nil } - allocStats := cStatsReporter.AllocStats() - arStatsReporter, ok := allocStats[allocID] + + // Check if the allocation is running on the node + allocStats, ok := clientStats.AllocStats()[allocID] if !ok { return nil, fmt.Errorf("alloc %q is not running on this client", allocID) } + + // Return the resource usage of the task if the task name is specified if task = req.URL.Query().Get("task"); task != "" { - taskStatsReporter, err := arStatsReporter.TaskStats(task) + taskStats, err := allocStats.TaskStats(task) if err != nil { return nil, err } - return taskStatsReporter.ResourceUsage(), nil + return taskStats.ResourceUsage(), nil } + + // Return the resource usage of all the tasks in an allocation if task name + // is not specified res := make(map[string]interface{}) - for task, sr := range arStatsReporter.AllocStats() { - res[task] = sr.ResourceUsage() + for task, taskStats := range allocStats.AllocStats() { + res[task] = taskStats.ResourceUsage() } return res, nil } From 0fdff61d2d266fbebf57cf854cf703a523afc7c9 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Wed, 11 May 2016 12:56:47 -0700 Subject: [PATCH 13/69] Implemented stats for raw_exec --- client/driver/executor/executor.go | 105 +++++++++++++++++++++++ client/driver/executor/executor_basic.go | 6 +- client/driver/executor/executor_linux.go | 15 +++- client/driver/structs/structs.go | 5 +- 4 files changed, 126 insertions(+), 5 deletions(-) diff --git a/client/driver/executor/executor.go b/client/driver/executor/executor.go index 8d7304fda3c..da484fbfe07 100644 --- a/client/driver/executor/executor.go +++ b/client/driver/executor/executor.go @@ -15,7 +15,9 @@ import ( "time" "github.com/hashicorp/go-multierror" + "github.com/mitchellh/go-ps" cgroupConfig "github.com/opencontainers/runc/libcontainer/configs" + "github.com/shirou/gopsutil/process" "github.com/hashicorp/nomad/client/allocdir" "github.com/hashicorp/nomad/client/consul" @@ -146,6 +148,7 @@ type UniversalExecutor struct { ctx *ExecutorContext command *ExecCommand + pids []int taskDir string exitState *ProcessState processExited chan interface{} @@ -251,6 +254,7 @@ func (e *UniversalExecutor) LaunchCmd(command *ExecCommand, ctx *ExecutorContext if err := e.cmd.Start(); err != nil { return nil, err } + go e.collectPids() go e.wait() ic := &cstructs.IsolationConfig{Cgroup: e.groups, CgroupPaths: e.cgPaths} return &ProcessState{Pid: e.cmd.Process.Pid, ExitCode: -1, IsolationConfig: ic, Time: time.Now()}, nil @@ -619,3 +623,104 @@ func (e *UniversalExecutor) interpolateServices(task *structs.Task) { service.Tags = e.ctx.TaskEnv.ParseAndReplace(service.Tags) } } + +func (e *UniversalExecutor) collectPids() { + for { + timer := time.NewTimer(5 * time.Second) + select { + case <-timer.C: + pids, err := e.getAllPids() + e.logger.Printf("DIPTANU PIDS %#v", pids) + timer.Reset(5 * time.Second) + if err != nil { + e.logger.Printf("[DEBUG] executor: error collecting pids: %v", err) + } + e.pids = pids + case <-e.processExited: + timer.Stop() + return + } + } +} + +func (e *UniversalExecutor) scanPids() ([]int, error) { + processFamily := make(map[int]struct{}) + processFamily[os.Getpid()] = struct{}{} + pids, err := ps.Processes() + if err != nil { + return nil, err + } + for _, pid := range pids { + _, parentPid := processFamily[pid.Pid()] + _, childPid := processFamily[pid.PPid()] + if parentPid || childPid { + processFamily[pid.Pid()] = struct{}{} + } + } + res := make([]int, 0, len(processFamily)) + for pid := range processFamily { + res = append(res, pid) + } + return res, nil +} + +func (e *UniversalExecutor) resourceUsagePids() (*cstructs.TaskResourceUsage, error) { + resourceUsage := make(map[int]*cstructs.TaskResourceUsage) + ts := time.Now() + for _, pid := range e.pids { + p, err := process.NewProcess(int32(pid)) + if err != nil { + e.logger.Printf("[DEBUG] executor: unable to create new process with pid: %v", pid) + } + memInfo, err := p.MemoryInfo() + if err != nil { + e.logger.Printf("[DEBUG] executor: unable to get memory stats for process: %v", pid) + } + cpuStats, err := p.Times() + if err != nil { + e.logger.Printf("[DEBUG] executor: unable to get cpu stats for process: %v", pid) + } + ms := &cstructs.MemoryStats{ + RSS: memInfo.RSS, + Swap: memInfo.Swap, + } + + percent, _ := p.Percent(0) + cs := &cstructs.CpuUsage{ + SystemMode: cpuStats.System, + UserMode: cpuStats.User, + Percent: percent, + } + resourceUsage[pid] = &cstructs.TaskResourceUsage{MemoryStats: ms, CpuStats: cs, Timestamp: ts} + + } + + var ( + systemModeCPU, userModeCPU, percent float64 + + totalRSS, totalSwap uint64 + ) + + for _, rs := range resourceUsage { + systemModeCPU += rs.CpuStats.SystemMode + userModeCPU += rs.CpuStats.UserMode + percent += rs.CpuStats.Percent + + totalRSS += rs.MemoryStats.RSS + totalSwap += rs.MemoryStats.Swap + } + + totalCPU := &cstructs.CpuUsage{ + SystemMode: systemModeCPU, + UserMode: userModeCPU, + Percent: percent, + } + + totalMemory := &cstructs.MemoryStats{ + RSS: totalRSS, + Swap: totalSwap, + } + + return &cstructs.TaskResourceUsage{MemoryStats: totalMemory, CpuStats: totalCPU}, nil + +} diff --git a/client/driver/executor/executor_basic.go b/client/driver/executor/executor_basic.go index 65558aa9153..cb87f030193 100644 --- a/client/driver/executor/executor_basic.go +++ b/client/driver/executor/executor_basic.go @@ -32,5 +32,9 @@ func (e *UniversalExecutor) configureIsolation() error { } func (e *UniversalExecutor) Stats() (*cstructs.TaskResourceUsage, error) { - return nil, nil + return e.resourceUsagePids() +} + +func (e *UniversalExecutor) getAllPids() ([]int, error) { + return e.scanPids() } diff --git a/client/driver/executor/executor_linux.go b/client/driver/executor/executor_linux.go index a2b8a593a4a..513c6e2eadb 100644 --- a/client/driver/executor/executor_linux.go +++ b/client/driver/executor/executor_linux.go @@ -126,6 +126,9 @@ func (e *UniversalExecutor) configureCgroups(resources *structs.Resources) error // Stats reports the resource utilization of the cgroup func (e *UniversalExecutor) Stats() (*cstructs.TaskResourceUsage, error) { + if !e.command.ResourceLimits { + return e.resourceUsagePids() + } manager := getCgroupManager(e.groups, e.cgPaths) stats, err := manager.GetStats() if err != nil { @@ -154,8 +157,8 @@ func (e *UniversalExecutor) Stats() (*cstructs.TaskResourceUsage, error) { kmTicks := (kernelModeTime * clockTicks) / nanosecondsInSecond cs := &cstructs.CpuUsage{ - SystemMode: kmTicks, - UserMode: umTicks, + SystemMode: float64(kmTicks), + UserMode: float64(umTicks), ThrottledPeriods: stats.CpuStats.ThrottlingData.ThrottledPeriods, ThrottledTime: stats.CpuStats.ThrottlingData.ThrottledTime, } @@ -232,6 +235,14 @@ func (e *UniversalExecutor) removeChrootMounts() error { return e.ctx.AllocDir.UnmountAll() } +func (e *UniversalExecutor) getAllPids() ([]int, error) { + if e.command.ResourceLimits { + manager := getCgroupManager(e.groups, e.cgPaths) + return manager.GetAllPids() + } + return e.scanPids() +} + // destroyCgroup kills all processes in the cgroup and removes the cgroup // configuration from the host. This function is idempotent. func DestroyCgroup(groups *cgroupConfig.Cgroup, cgPaths map[string]string, executorPid int) error { diff --git a/client/driver/structs/structs.go b/client/driver/structs/structs.go index 89e0ed40b05..2550e281d88 100644 --- a/client/driver/structs/structs.go +++ b/client/driver/structs/structs.go @@ -95,10 +95,11 @@ type MemoryStats struct { } type CpuUsage struct { - SystemMode uint64 - UserMode uint64 + SystemMode float64 + UserMode float64 ThrottledPeriods uint64 ThrottledTime uint64 + Percent float64 } type TaskResourceUsage struct { From c85b4de46a58f6cd9adee507c5eb8cbdcccc7060 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Wed, 18 May 2016 07:11:25 +0200 Subject: [PATCH 14/69] Adding a query param to return time series of stats --- client/driver/executor/executor.go | 9 ++++++--- command/agent/stats_endpoint.go | 23 +++++++++++++++++++++-- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/client/driver/executor/executor.go b/client/driver/executor/executor.go index da484fbfe07..d2929a2d415 100644 --- a/client/driver/executor/executor.go +++ b/client/driver/executor/executor.go @@ -27,6 +27,10 @@ import ( "github.com/hashicorp/nomad/nomad/structs" ) +const ( + pidScanInterval = 5 * time.Second +) + // Executor is the interface which allows a driver to launch and supervise // a process type Executor interface { @@ -631,7 +635,7 @@ func (e *UniversalExecutor) collectPids() { case <-timer.C: pids, err := e.getAllPids() e.logger.Printf("DIPTANU PIDS %#v", pids) - timer.Reset(5 * time.Second) + timer.Reset(pidScanInterval) if err != nil { e.logger.Printf("[DEBUG] executor: error collecting pids: %v", err) } @@ -721,6 +725,5 @@ func (e *UniversalExecutor) resourceUsagePids() (*cstructs.TaskResourceUsage, er Swap: totalSwap, } - return &cstructs.TaskResourceUsage{MemoryStats: totalMemory, CpuStats: totalCPU}, nil - + return &cstructs.TaskResourceUsage{MemoryStats: totalMemory, CpuStats: totalCPU, Timestamp: ts}, nil } diff --git a/command/agent/stats_endpoint.go b/command/agent/stats_endpoint.go index 35d0d8c658d..7c1c8e5dc6a 100644 --- a/command/agent/stats_endpoint.go +++ b/command/agent/stats_endpoint.go @@ -3,6 +3,9 @@ package agent import ( "fmt" "net/http" + "strconv" + + "github.com/hashicorp/nomad/client" ) func (s *HTTPServer) StatsRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { @@ -10,6 +13,15 @@ func (s *HTTPServer) StatsRequest(resp http.ResponseWriter, req *http.Request) ( return nil, clientNotRunning } + var tsRequest bool + + // Check if the user has requested time series + if qp := req.URL.Query().Get("ts"); qp != "" { + if tsReq, err := strconv.ParseBool(qp); err == nil { + tsRequest = tsReq + } + } + clientStats := s.agent.client.StatsReporter() // Return the host stats if alloc ID is not present @@ -30,14 +42,21 @@ func (s *HTTPServer) StatsRequest(resp http.ResponseWriter, req *http.Request) ( if err != nil { return nil, err } - return taskStats.ResourceUsage(), nil + return s.getStats(tsRequest, taskStats), nil } // Return the resource usage of all the tasks in an allocation if task name // is not specified res := make(map[string]interface{}) for task, taskStats := range allocStats.AllocStats() { - res[task] = taskStats.ResourceUsage() + res[task] = s.getStats(tsRequest, taskStats) } return res, nil } + +func (s *HTTPServer) getStats(tsRequest bool, taskStats client.TaskStatsReporter) interface{} { + if tsRequest { + return taskStats.ResourceUsageTS() + } + return taskStats.ResourceUsage() +} From 9a851a11ee4a5c599366dd300957161e59f895ba Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Thu, 19 May 2016 07:41:11 -0700 Subject: [PATCH 15/69] Added the PidStats method on the executor --- client/driver/driver.go | 1 + client/driver/executor/executor.go | 82 +++++++++++++++++++----------- client/driver/executor_plugin.go | 14 +++++ 3 files changed, 66 insertions(+), 31 deletions(-) diff --git a/client/driver/driver.go b/client/driver/driver.go index 1040ca094e6..9e8513073fd 100644 --- a/client/driver/driver.go +++ b/client/driver/driver.go @@ -114,6 +114,7 @@ type DriverHandle interface { // Kill is used to stop the task Kill() error + // Stats returns aggregated stats of the driver Stats() (*cstructs.TaskResourceUsage, error) } diff --git a/client/driver/executor/executor.go b/client/driver/executor/executor.go index d2929a2d415..9b315deabe7 100644 --- a/client/driver/executor/executor.go +++ b/client/driver/executor/executor.go @@ -45,6 +45,7 @@ type Executor interface { DeregisterServices() error Version() (*ExecutorVersion, error) Stats() (*cstructs.TaskResourceUsage, error) + PidStats() (map[int]*cstructs.TaskResourceUsage, error) } // ConsulContext holds context to configure the consul client and run checks @@ -436,6 +437,8 @@ func (e *UniversalExecutor) ShutDown() error { return nil } +// SyncServices syncs the services of the task that the executor is running with +// Consul func (e *UniversalExecutor) SyncServices(ctx *ConsulContext) error { e.logger.Printf("[INFO] executor: registering services") e.consulCtx = ctx @@ -457,6 +460,8 @@ func (e *UniversalExecutor) SyncServices(ctx *ConsulContext) error { return err } +// DeregisterServices removes the services of the task that the executor is +// running from Consul func (e *UniversalExecutor) DeregisterServices() error { e.logger.Printf("[INFO] executor: de-registering services and shutting down consul service") if e.consulService != nil { @@ -465,6 +470,41 @@ func (e *UniversalExecutor) DeregisterServices() error { return nil } +// PidStats returns the resource usage stats per pid +func (e *UniversalExecutor) PidStats() (map[int]*cstructs.TaskResourceUsage, error) { + stats := make(map[int]*cstructs.TaskResourceUsage) + ts := time.Now() + for _, pid := range e.pids { + p, err := process.NewProcess(int32(pid)) + if err != nil { + e.logger.Printf("[DEBUG] executor: unable to create new process with pid: %v", pid) + continue + } + memInfo, err := p.MemoryInfo() + if err != nil { + e.logger.Printf("[DEBUG] executor: unable to get memory stats for process: %v", pid) + } + cpuStats, err := p.Times() + if err != nil { + e.logger.Printf("[DEBUG] executor: unable to get cpu stats for process: %v", pid) + } + ms := &cstructs.MemoryStats{ + RSS: memInfo.RSS, + Swap: memInfo.Swap, + } + + percent, _ := p.Percent(0) + cs := &cstructs.CpuUsage{ + SystemMode: cpuStats.System, + UserMode: cpuStats.User, + Percent: percent, + } + stats[pid] = &cstructs.TaskResourceUsage{MemoryStats: ms, CpuStats: cs, Timestamp: ts} + } + + return stats, nil +} + // configureTaskDir sets the task dir in the executor func (e *UniversalExecutor) configureTaskDir() error { taskDir, ok := e.ctx.AllocDir.TaskDirs[e.ctx.Task.Name] @@ -628,9 +668,11 @@ func (e *UniversalExecutor) interpolateServices(task *structs.Task) { } } +// collectPids collects the pids of the child processes that the executor is +// running every 5 seconds func (e *UniversalExecutor) collectPids() { for { - timer := time.NewTimer(5 * time.Second) + timer := time.NewTimer(pidScanInterval) select { case <-timer.C: pids, err := e.getAllPids() @@ -647,6 +689,8 @@ func (e *UniversalExecutor) collectPids() { } } +// scanPids scans all the pids on the machine running the current executor and +// returns the child processes of the executor. func (e *UniversalExecutor) scanPids() ([]int, error) { processFamily := make(map[int]struct{}) processFamily[os.Getpid()] = struct{}{} @@ -668,41 +712,17 @@ func (e *UniversalExecutor) scanPids() ([]int, error) { return res, nil } +// resourceUsagePids aggreagates the resources used by all the pids that are +// spawned by the executor and the user process. func (e *UniversalExecutor) resourceUsagePids() (*cstructs.TaskResourceUsage, error) { - resourceUsage := make(map[int]*cstructs.TaskResourceUsage) ts := time.Now() - for _, pid := range e.pids { - p, err := process.NewProcess(int32(pid)) - if err != nil { - e.logger.Printf("[DEBUG] executor: unable to create new process with pid: %v", pid) - } - memInfo, err := p.MemoryInfo() - if err != nil { - e.logger.Printf("[DEBUG] executor: unable to get memory stats for process: %v", pid) - } - cpuStats, err := p.Times() - if err != nil { - e.logger.Printf("[DEBUG] executor: unable to get cpu stats for process: %v", pid) - } - ms := &cstructs.MemoryStats{ - RSS: memInfo.RSS, - Swap: memInfo.Swap, - } - - percent, _ := p.Percent(0) - cs := &cstructs.CpuUsage{ - SystemMode: cpuStats.System, - UserMode: cpuStats.User, - Percent: percent, - } - resourceUsage[pid] = &cstructs.TaskResourceUsage{MemoryStats: ms, CpuStats: cs, Timestamp: ts} - + resourceUsage, err := e.PidStats() + if err != nil { + return nil, err } - var ( systemModeCPU, userModeCPU, percent float64 - - totalRSS, totalSwap uint64 + totalRSS, totalSwap uint64 ) for _, rs := range resourceUsage { diff --git a/client/driver/executor_plugin.go b/client/driver/executor_plugin.go index a4edf2da70e..7ebb494df49 100644 --- a/client/driver/executor_plugin.go +++ b/client/driver/executor_plugin.go @@ -95,6 +95,12 @@ func (e *ExecutorRPC) Stats() (*cstructs.TaskResourceUsage, error) { return &resourceUsage, err } +func (e *ExecutorRPC) PidStats() (map[int]*cstructs.TaskResourceUsage, error) { + stats := make(map[int]*cstructs.TaskResourceUsage) + err := e.client.Call("Plugin.PidStats", new(interface{}), &stats) + return stats, err +} + type ExecutorRPCServer struct { Impl executor.Executor logger *log.Logger @@ -164,6 +170,14 @@ func (e *ExecutorRPCServer) Stats(args interface{}, resourceUsage *cstructs.Task return err } +func (e *ExecutorRPCServer) PidStats(args interface{}, stats map[int]*cstructs.TaskResourceUsage) error { + ps, err := e.Impl.PidStats() + if ps != nil { + stats = ps + } + return err +} + type ExecutorPlugin struct { logger *log.Logger Impl *ExecutorRPCServer From ea1370d4265736792323721f139cec0cdd901b46 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Thu, 19 May 2016 10:05:40 -0700 Subject: [PATCH 16/69] Fixed implementation of the docker stats --- client/driver/docker.go | 49 ++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index 59ba2da13ae..a91c7ceda16 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -123,6 +123,9 @@ type DockerHandle struct { version string killTimeout time.Duration maxKillTimeout time.Duration + stats chan *docker.Stats + doneMonitoring chan bool + resourceUsage *cstructs.TaskResourceUsage waitCh chan *cstructs.WaitResult doneCh chan struct{} } @@ -768,12 +771,16 @@ func (d *DockerDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle version: d.config.Version, killTimeout: GetKillTimeout(task.KillTimeout, maxKill), maxKillTimeout: maxKill, + stats: make(chan *docker.Stats), + doneMonitoring: make(chan bool), doneCh: make(chan struct{}), waitCh: make(chan *cstructs.WaitResult, 1), } if err := exec.SyncServices(consulContext(d.config, container.ID)); err != nil { d.logger.Printf("[ERR] driver.docker: error registering services with consul for task: %q: %v", task.Name, err) } + go h.monitorUsage() + go h.startMonitoring() go h.run() return h, nil } @@ -841,6 +848,8 @@ func (d *DockerDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, er version: pid.Version, killTimeout: pid.KillTimeout, maxKillTimeout: pid.MaxKillTimeout, + stats: make(chan *docker.Stats), + doneMonitoring: make(chan bool), doneCh: make(chan struct{}), waitCh: make(chan *cstructs.WaitResult, 1), } @@ -909,16 +918,7 @@ func (h *DockerHandle) Kill() error { } func (h *DockerHandle) Stats() (*cstructs.TaskResourceUsage, error) { - stats := make(chan *docker.Stats) - done := make(chan bool) - statsOpts := docker.StatsOptions{ID: h.containerID, Done: done, Stats: stats, Stream: false, Timeout: 2 * time.Second} - if err := h.client.Stats(statsOpts); err != nil { - return nil, err - } - containerStats := <-stats - close(done) - resourceUsage := cstructs.TaskResourceUsage{MemoryStats: &cstructs.MemoryStats{RSS: containerStats.MemoryStats.Stats.Rss}} - return &resourceUsage, nil + return h.resourceUsage, nil } func (h *DockerHandle) run() { @@ -933,6 +933,8 @@ func (h *DockerHandle) run() { } close(h.doneCh) + h.doneMonitoring <- true + close(h.doneMonitoring) h.waitCh <- cstructs.NewWaitResult(exitCode, 0, err) close(h.waitCh) @@ -969,3 +971,30 @@ func (h *DockerHandle) run() { } } } + +func (h *DockerHandle) startMonitoring() { + statsOpts := docker.StatsOptions{ID: h.containerID, Done: h.doneMonitoring, Stats: h.stats, Stream: true} + if err := h.client.Stats(statsOpts); err != nil { + h.logger.Printf("[DEBUG] driver.docker: error collecting stats from container %s: %v", h.containerID, err) + } +} + +func (h *DockerHandle) monitorUsage() { + for { + select { + case s := <-h.stats: + if s != nil { + ms := &cstructs.MemoryStats{ + RSS: s.MemoryStats.Stats.Rss, + Cache: s.MemoryStats.Stats.Cache, + Swap: s.MemoryStats.Stats.Swap, + MaxUsage: s.MemoryStats.MaxUsage, + } + h.resourceUsage = &cstructs.TaskResourceUsage{MemoryStats: ms, Timestamp: s.Read} + } + case <-h.doneMonitoring: + case <-h.doneCh: + return + } + } +} From 30cbfe1f71fcc1d3fb3e696561373c5cf29391c2 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Thu, 19 May 2016 13:32:03 -0700 Subject: [PATCH 17/69] Implemented cpu stats --- client/driver/docker.go | 16 ++++++++- client/driver/executor/executor.go | 11 +++++-- client/driver/executor/executor_linux.go | 4 +++ client/stats/cpu.go | 41 ++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 client/stats/cpu.go diff --git a/client/driver/docker.go b/client/driver/docker.go index a91c7ceda16..22f3042edb0 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -990,7 +990,21 @@ func (h *DockerHandle) monitorUsage() { Swap: s.MemoryStats.Stats.Swap, MaxUsage: s.MemoryStats.MaxUsage, } - h.resourceUsage = &cstructs.TaskResourceUsage{MemoryStats: ms, Timestamp: s.Read} + + cs := &cstructs.CpuUsage{ + SystemMode: float64(s.CPUStats.CPUUsage.UsageInKernelmode), + UserMode: float64(s.CPUStats.CPUUsage.UsageInKernelmode), + ThrottledPeriods: s.CPUStats.ThrottlingData.ThrottledPeriods, + ThrottledTime: s.CPUStats.ThrottlingData.ThrottledTime, + } + // Calculate percentage + cs.Percent = 0.0 + cpuDelta := float64(s.CPUStats.CPUUsage.TotalUsage) - float64(s.PreCPUStats.CPUUsage.TotalUsage) + systemDelta := float64(s.CPUStats.SystemCPUUsage) - float64(s.PreCPUStats.SystemCPUUsage) + if cpuDelta > 0.0 && systemDelta > 0.0 { + cs.Percent = (cpuDelta / systemDelta) * float64(len(s.CPUStats.CPUUsage.PercpuUsage)) * 100.0 + } + h.resourceUsage = &cstructs.TaskResourceUsage{MemoryStats: ms, CpuStats: cs, Timestamp: s.Read} } case <-h.doneMonitoring: case <-h.doneCh: diff --git a/client/driver/executor/executor.go b/client/driver/executor/executor.go index 9b315deabe7..74fdfa4a927 100644 --- a/client/driver/executor/executor.go +++ b/client/driver/executor/executor.go @@ -24,6 +24,7 @@ import ( "github.com/hashicorp/nomad/client/driver/env" "github.com/hashicorp/nomad/client/driver/logging" cstructs "github.com/hashicorp/nomad/client/driver/structs" + "github.com/hashicorp/nomad/client/stats" "github.com/hashicorp/nomad/nomad/structs" ) @@ -171,15 +172,22 @@ type UniversalExecutor struct { consulService *consul.ConsulService consulCtx *ConsulContext + cpuStats *stats.CpuStats logger *log.Logger } // NewExecutor returns an Executor func NewExecutor(logger *log.Logger) Executor { - return &UniversalExecutor{ + exec := &UniversalExecutor{ logger: logger, processExited: make(chan interface{}), } + + if cpuStats, err := stats.NewCpuStats(); err == nil { + exec.cpuStats = cpuStats + } + + return exec } // Version returns the api version of the executor @@ -676,7 +684,6 @@ func (e *UniversalExecutor) collectPids() { select { case <-timer.C: pids, err := e.getAllPids() - e.logger.Printf("DIPTANU PIDS %#v", pids) timer.Reset(pidScanInterval) if err != nil { e.logger.Printf("[DEBUG] executor: error collecting pids: %v", err) diff --git a/client/driver/executor/executor_linux.go b/client/driver/executor/executor_linux.go index 513c6e2eadb..2a77ad90779 100644 --- a/client/driver/executor/executor_linux.go +++ b/client/driver/executor/executor_linux.go @@ -150,6 +150,7 @@ func (e *UniversalExecutor) Stats() (*cstructs.TaskResourceUsage, error) { } // CPU Related Stats + totalProcessCPUUsage := stats.CpuStats.CpuUsage.TotalUsage userModeTime := stats.CpuStats.CpuUsage.UsageInUsermode kernelModeTime := stats.CpuStats.CpuUsage.UsageInKernelmode @@ -162,6 +163,9 @@ func (e *UniversalExecutor) Stats() (*cstructs.TaskResourceUsage, error) { ThrottledPeriods: stats.CpuStats.ThrottlingData.ThrottledPeriods, ThrottledTime: stats.CpuStats.ThrottlingData.ThrottledTime, } + if e.cpuStats != nil { + cs.Percent = e.cpuStats.Percent(totalProcessCPUUsage) + } return &cstructs.TaskResourceUsage{MemoryStats: ms, CpuStats: cs, Timestamp: time.Now()}, nil } diff --git a/client/stats/cpu.go b/client/stats/cpu.go new file mode 100644 index 00000000000..351f81dc5db --- /dev/null +++ b/client/stats/cpu.go @@ -0,0 +1,41 @@ +package stats + +import ( + "github.com/shirou/gopsutil/cpu" +) + +type CpuStats struct { + prevSystemUsage float64 + prevProcessUsage uint64 + + totalCpus int +} + +func NewCpuStats() (*CpuStats, error) { + cpuInfo, err := cpu.Info() + if err != nil { + return nil, err + } + return &CpuStats{totalCpus: len(cpuInfo)}, nil +} + +func (c *CpuStats) Percent(currentProcessUsage uint64) float64 { + percent := 0.0 + + sysCPUStats, err := cpu.Times(false) + if err != nil { + return 0 + } + currentSysUsage := 0.0 + for _, cpuStat := range sysCPUStats { + currentSysUsage += cpuStat.Total() * 1000000000 + } + + delta := float64(currentProcessUsage) - float64(c.prevProcessUsage) + sysDelta := float64(currentSysUsage) - float64(c.prevSystemUsage) + + percent = (delta / sysDelta) * float64(c.totalCpus) * 100.0 + c.prevSystemUsage = currentSysUsage + c.prevProcessUsage = currentProcessUsage + return percent +} From f693545814f547c841ddfacefd6e7787a0ff600e Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Thu, 19 May 2016 14:01:13 -0700 Subject: [PATCH 18/69] Vendoring go-ps --- vendor/github.com/mitchellh/go-ps/LICENSE.md | 21 ++ vendor/github.com/mitchellh/go-ps/README.md | 36 +++ vendor/github.com/mitchellh/go-ps/Vagrantfile | 43 +++ vendor/github.com/mitchellh/go-ps/process.go | 40 +++ .../mitchellh/go-ps/process_darwin.go | 72 +++++ .../mitchellh/go-ps/process_darwin.h | 66 +++++ .../mitchellh/go-ps/process_freebsd.go | 260 ++++++++++++++++++ .../mitchellh/go-ps/process_unix.go | 129 +++++++++ .../mitchellh/go-ps/process_windows.go | 119 ++++++++ vendor/vendor.json | 6 + 10 files changed, 792 insertions(+) create mode 100644 vendor/github.com/mitchellh/go-ps/LICENSE.md create mode 100644 vendor/github.com/mitchellh/go-ps/README.md create mode 100644 vendor/github.com/mitchellh/go-ps/Vagrantfile create mode 100644 vendor/github.com/mitchellh/go-ps/process.go create mode 100644 vendor/github.com/mitchellh/go-ps/process_darwin.go create mode 100644 vendor/github.com/mitchellh/go-ps/process_darwin.h create mode 100644 vendor/github.com/mitchellh/go-ps/process_freebsd.go create mode 100644 vendor/github.com/mitchellh/go-ps/process_unix.go create mode 100644 vendor/github.com/mitchellh/go-ps/process_windows.go diff --git a/vendor/github.com/mitchellh/go-ps/LICENSE.md b/vendor/github.com/mitchellh/go-ps/LICENSE.md new file mode 100644 index 00000000000..22985159044 --- /dev/null +++ b/vendor/github.com/mitchellh/go-ps/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Mitchell Hashimoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/mitchellh/go-ps/README.md b/vendor/github.com/mitchellh/go-ps/README.md new file mode 100644 index 00000000000..11cea98f082 --- /dev/null +++ b/vendor/github.com/mitchellh/go-ps/README.md @@ -0,0 +1,36 @@ +# Process List Library for Go + +go-ps is a library for Go that implements OS-specific APIs to list and +manipulate processes in a platform-safe way. The library can find and +list processes on Linux, Mac OS X, and Windows. + +If you're new to Go, this library has a good amount of advanced Go educational +value as well. It uses some advanced features of Go: build tags, accessing +DLL methods for Windows, cgo for Darwin, etc. + +How it works: + + * **Darwin** uses the `sysctl` syscall to retrieve the process table, via + cgo. + * **Unix** uses the procfs at `/proc` to inspect the process tree. + * **Windows** uses the Windows API, and methods such as + `CreateToolhelp32Snapshot` to get a point-in-time snapshot of + the process table. + +## Installation + +Install using standard `go get`: + +``` +$ go get github.com/mitchellh/go-ps +... +``` + +## TODO + +Want to contribute? Here is a short TODO list of things that aren't +implemented for this library that would be nice: + + * FreeBSD support + * Plan9 support + * Eliminate the need for cgo with Darwin diff --git a/vendor/github.com/mitchellh/go-ps/Vagrantfile b/vendor/github.com/mitchellh/go-ps/Vagrantfile new file mode 100644 index 00000000000..61662ab1e3e --- /dev/null +++ b/vendor/github.com/mitchellh/go-ps/Vagrantfile @@ -0,0 +1,43 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! +VAGRANTFILE_API_VERSION = "2" + +Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| + config.vm.box = "chef/ubuntu-12.04" + + config.vm.provision "shell", inline: $script + + ["vmware_fusion", "vmware_workstation"].each do |p| + config.vm.provider "p" do |v| + v.vmx["memsize"] = "1024" + v.vmx["numvcpus"] = "2" + v.vmx["cpuid.coresPerSocket"] = "1" + end + end +end + +$script = <