From 4a475466f6dfdbe5b7375e9dab7a38b0df266b34 Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Tue, 1 Dec 2015 09:44:13 +0000 Subject: [PATCH] Add host memory and CPU usage metrics --- probe/host/reporter.go | 25 +- probe/host/reporter_test.go | 32 +- probe/host/system_darwin.go | 13 +- probe/host/system_linux.go | 44 ++- probe/host/system_test.go | 3 +- render/detailed_node.go | 15 +- report/metrics.go | 11 + .../c9s/goprocinfo/linux/cpuinfo.go | 127 ++++++ .../c9s/goprocinfo/linux/cpuinfo_test.go | 62 +++ .../github.com/c9s/goprocinfo/linux/disk.go | 26 ++ .../c9s/goprocinfo/linux/disk_test.go | 14 + .../c9s/goprocinfo/linux/diskstat.go | 100 +++++ .../c9s/goprocinfo/linux/diskstat_test.go | 56 +++ .../c9s/goprocinfo/linux/loadavg.go | 67 ++++ .../c9s/goprocinfo/linux/loadavg_test.go | 30 ++ .../c9s/goprocinfo/linux/meminfo.go | 97 +++++ .../c9s/goprocinfo/linux/meminfo_test.go | 61 +++ .../github.com/c9s/goprocinfo/linux/mounts.go | 49 +++ .../c9s/goprocinfo/linux/mounts_test.go | 19 + .../github.com/c9s/goprocinfo/linux/net_ip.go | 173 ++++++++ .../c9s/goprocinfo/linux/net_ip_test.go | 65 +++ .../c9s/goprocinfo/linux/net_tcp.go | 82 ++++ .../c9s/goprocinfo/linux/net_tcp_test.go | 88 +++++ .../c9s/goprocinfo/linux/net_udp.go | 59 +++ .../c9s/goprocinfo/linux/net_udp_test.go | 137 +++++++ .../c9s/goprocinfo/linux/netstat.go | 174 ++++++++ .../c9s/goprocinfo/linux/netstat_test.go | 63 +++ .../c9s/goprocinfo/linux/network_stat.go | 73 ++++ .../c9s/goprocinfo/linux/network_stat_test.go | 41 ++ .../c9s/goprocinfo/linux/process.go | 62 +++ .../c9s/goprocinfo/linux/process_cmdline.go | 39 ++ .../goprocinfo/linux/process_cmdline_test.go | 39 ++ .../c9s/goprocinfo/linux/process_io.go | 71 ++++ .../c9s/goprocinfo/linux/process_io_test.go | 31 ++ .../c9s/goprocinfo/linux/process_pid.go | 54 +++ .../c9s/goprocinfo/linux/process_pid_test.go | 38 ++ .../c9s/goprocinfo/linux/process_stat.go | 303 ++++++++++++++ .../c9s/goprocinfo/linux/process_stat_test.go | 193 +++++++++ .../c9s/goprocinfo/linux/process_statm.go | 61 +++ .../goprocinfo/linux/process_statm_test.go | 31 ++ .../c9s/goprocinfo/linux/process_status.go | 331 ++++++++++++++++ .../goprocinfo/linux/process_status_test.go | 68 ++++ .../c9s/goprocinfo/linux/process_test.go | 134 +++++++ .../c9s/goprocinfo/linux/sockstat.go | 82 ++++ .../c9s/goprocinfo/linux/sockstat_test.go | 19 + .../github.com/c9s/goprocinfo/linux/stat.go | 106 +++++ .../c9s/goprocinfo/linux/stat_test.go | 12 + .../github.com/c9s/goprocinfo/linux/uptime.go | 43 ++ .../c9s/goprocinfo/linux/uptime_test.go | 22 ++ .../github.com/c9s/goprocinfo/linux/vmstat.go | 373 ++++++++++++++++++ .../c9s/goprocinfo/linux/vmstat_test.go | 12 + vendor/manifest | 7 + 52 files changed, 3910 insertions(+), 27 deletions(-) create mode 100644 vendor/github.com/c9s/goprocinfo/linux/cpuinfo.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/cpuinfo_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/disk.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/disk_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/diskstat.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/diskstat_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/loadavg.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/loadavg_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/meminfo.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/meminfo_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/mounts.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/mounts_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/net_ip.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/net_ip_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/net_tcp.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/net_tcp_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/net_udp.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/net_udp_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/netstat.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/netstat_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/network_stat.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/network_stat_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_cmdline.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_cmdline_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_io.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_io_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_pid.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_pid_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_stat.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_stat_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_statm.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_statm_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_status.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_status_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/process_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/sockstat.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/sockstat_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/stat.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/stat_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/uptime.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/uptime_test.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/vmstat.go create mode 100644 vendor/github.com/c9s/goprocinfo/linux/vmstat_test.go diff --git a/probe/host/reporter.go b/probe/host/reporter.go index 1d1425f942..a3d738788d 100644 --- a/probe/host/reporter.go +++ b/probe/host/reporter.go @@ -4,6 +4,7 @@ import ( "runtime" "time" + "github.com/weaveworks/scope/common/mtime" "github.com/weaveworks/scope/report" ) @@ -18,17 +19,16 @@ const ( Load1 = "load1" Load5 = "load5" Load15 = "load15" + CPUUsage = "cpu_usage_percent" + MemUsage = "mem_usage_percent" ) // Exposed for testing. const ( - ProcUptime = "/proc/uptime" - ProcLoad = "/proc/loadavg" -) - -// Exposed for testing. -var ( - Now = func() string { return time.Now().UTC().Format(time.RFC3339Nano) } + ProcUptime = "/proc/uptime" + ProcLoad = "/proc/loadavg" + ProcStat = "/proc/stat" + ProcMemInfo = "/proc/meminfo" ) // Reporter generates Reports containing the host topology. @@ -72,15 +72,22 @@ func (r *Reporter) Report() (report.Report, error) { return rep, err } + now := mtime.Now() + metrics := GetLoad(now) + cpuUsage, max := GetCPUUsagePercent() + metrics[CPUUsage] = report.MakeMetric().Add(now, cpuUsage).WithMax(max) + memUsage, max := GetMemoryUsagePercent() + metrics[MemUsage] = report.MakeMetric().Add(now, memUsage).WithMax(max) + rep.Host.AddNode(report.MakeHostNodeID(r.hostID), report.MakeNodeWith(map[string]string{ - Timestamp: Now(), + Timestamp: mtime.Now().UTC().Format(time.RFC3339Nano), HostName: r.hostName, OS: runtime.GOOS, KernelVersion: kernel, Uptime: uptime.String(), }).WithSets(report.Sets{ LocalNetworks: report.MakeStringSet(localCIDRs...), - }).WithMetrics(GetLoad())) + }).WithMetrics(metrics)) return rep, nil } diff --git a/probe/host/reporter_test.go b/probe/host/reporter_test.go index 49ff3ca9a0..d5b83be9eb 100644 --- a/probe/host/reporter_test.go +++ b/probe/host/reporter_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/weaveworks/scope/common/mtime" "github.com/weaveworks/scope/probe/host" "github.com/weaveworks/scope/report" "github.com/weaveworks/scope/test" @@ -18,13 +19,14 @@ func TestReporter(t *testing.T) { version = "version" network = "192.168.0.0/16" hostID = "hostid" - now = "now" hostname = "hostname" timestamp = time.Now() load = report.Metrics{ - host.Load1: report.MakeMetric().Add(timestamp, 1.0), - host.Load5: report.MakeMetric().Add(timestamp, 5.0), - host.Load15: report.MakeMetric().Add(timestamp, 15.0), + host.Load1: report.MakeMetric().Add(timestamp, 1.0), + host.Load5: report.MakeMetric().Add(timestamp, 5.0), + host.Load15: report.MakeMetric().Add(timestamp, 15.0), + host.CPUUsage: report.MakeMetric().Add(timestamp, 30.0).WithMax(100.0), + host.MemUsage: report.MakeMetric().Add(timestamp, 60.0).WithMax(100.0), } uptime = "278h55m43s" kernel = "release version" @@ -32,26 +34,32 @@ func TestReporter(t *testing.T) { localNets = report.Networks([]*net.IPNet{ipnet}) ) + mtime.NowForce(timestamp) + defer mtime.NowReset() + var ( - oldGetKernelVersion = host.GetKernelVersion - oldGetLoad = host.GetLoad - oldGetUptime = host.GetUptime - oldNow = host.Now + oldGetKernelVersion = host.GetKernelVersion + oldGetLoad = host.GetLoad + oldGetUptime = host.GetUptime + oldGetCPUUsagePercent = host.GetCPUUsagePercent + oldGetMemoryUsagePercent = host.GetMemoryUsagePercent ) defer func() { host.GetKernelVersion = oldGetKernelVersion host.GetLoad = oldGetLoad host.GetUptime = oldGetUptime - host.Now = oldNow + host.GetCPUUsagePercent = oldGetCPUUsagePercent + host.GetMemoryUsagePercent = oldGetMemoryUsagePercent }() host.GetKernelVersion = func() (string, error) { return release + " " + version, nil } - host.GetLoad = func() report.Metrics { return load } + host.GetLoad = func(time.Time) report.Metrics { return load } host.GetUptime = func() (time.Duration, error) { return time.ParseDuration(uptime) } - host.Now = func() string { return now } + host.GetCPUUsagePercent = func() (float64, float64) { return 30.0, 100.0 } + host.GetMemoryUsagePercent = func() (float64, float64) { return 60.0, 100.0 } want := report.MakeReport() want.Host.AddNode(report.MakeHostNodeID(hostID), report.MakeNodeWith(map[string]string{ - host.Timestamp: now, + host.Timestamp: timestamp.UTC().Format(time.RFC3339Nano), host.HostName: hostname, host.OS: runtime.GOOS, host.Uptime: uptime, diff --git a/probe/host/system_darwin.go b/probe/host/system_darwin.go index 453446ab22..e0a8278cbf 100644 --- a/probe/host/system_darwin.go +++ b/probe/host/system_darwin.go @@ -30,12 +30,11 @@ var GetKernelVersion = func() (string, error) { } // GetLoad returns the current load averages as metrics. -var GetLoad = func() report.Metrics { +var GetLoad = func(now time.Time) report.Metrics { out, err := exec.Command("w").CombinedOutput() if err != nil { return nil } - now := time.Now() matches := loadRe.FindAllStringSubmatch(string(out), -1) if matches == nil || len(matches) < 1 || len(matches[0]) < 4 { return nil @@ -84,3 +83,13 @@ var GetUptime = func() (time.Duration, error) { } return (time.Duration(d) * 24 * time.Hour) + (time.Duration(h) * time.Hour) + (time.Duration(m) * time.Minute), nil } + +// GetCPUUsagePercent returns the percent cpu usage and max (ie #cpus * 100) +var GetCPUUsagePercent = func() (float64, float64) { + return 0.0, 0.0 +} + +// GetMemoryUsagePercent returns the percent memory usage and max (ie 100) +var GetMemoryUsagePercent = func() (float64, float64) { + return 0.0, 0.0 +} diff --git a/probe/host/system_linux.go b/probe/host/system_linux.go index d713e3b916..5c1a7846cc 100644 --- a/probe/host/system_linux.go +++ b/probe/host/system_linux.go @@ -8,6 +8,8 @@ import ( "syscall" "time" + linuxproc "github.com/c9s/goprocinfo/linux" + "github.com/weaveworks/scope/report" ) @@ -24,12 +26,11 @@ var GetKernelVersion = func() (string, error) { } // GetLoad returns the current load averages as metrics. -var GetLoad = func() report.Metrics { +var GetLoad = func(now time.Time) report.Metrics { buf, err := ioutil.ReadFile("/proc/loadavg") if err != nil { return nil } - now := time.Now() toks := strings.Fields(string(buf)) if len(toks) < 3 { return nil @@ -72,3 +73,42 @@ var GetUptime = func() (time.Duration, error) { return time.Duration(uptime) * time.Second, nil } + +var previousStat = linuxproc.CPUStat{} + +// GetCPUUsagePercent returns the percent cpu usage and max (ie #cpus * 100) +var GetCPUUsagePercent = func() (float64, float64) { + stat, err := linuxproc.ReadStat(ProcStat) + if err != nil { + return 0.0, 0.0 + } + + // From http://stackoverflow.com/questions/23367857/accurate-calculation-of-cpu-usage-given-in-percentage-in-linux + var ( + currentStat = stat.CPUStatAll + prevIdle = previousStat.Idle + previousStat.IOWait + idle = currentStat.Idle + currentStat.IOWait + prevNonIdle = (previousStat.User + previousStat.Nice + previousStat.System + + previousStat.IRQ + previousStat.SoftIRQ + previousStat.Steal) + nonIdle = (currentStat.User + currentStat.Nice + currentStat.System + + currentStat.IRQ + currentStat.SoftIRQ + currentStat.Steal) + prevTotal = prevIdle + prevNonIdle + total = idle + nonIdle + // differentiate: actual value minus the previous one + totald = total - prevTotal + idled = idle - prevIdle + ) + previousStat = currentStat + return float64(totald-idled) * 100. / float64(totald), float64(len(stat.CPUStats)) * 100. +} + +// GetMemoryUsagePercent returns the percent memory usage and max (ie 100) +var GetMemoryUsagePercent = func() (float64, float64) { + meminfo, err := linuxproc.ReadMemInfo(ProcMemInfo) + if err != nil { + return 0.0, 0.0 + } + + used := meminfo.MemTotal - meminfo.MemFree - meminfo.Buffers - meminfo.Cached + return float64(used) * 100. / float64(meminfo.MemTotal), 100. +} diff --git a/probe/host/system_test.go b/probe/host/system_test.go index c868550e21..ec17e1836b 100644 --- a/probe/host/system_test.go +++ b/probe/host/system_test.go @@ -3,6 +3,7 @@ package host_test import ( "strings" "testing" + "time" "github.com/weaveworks/scope/probe/host" ) @@ -19,7 +20,7 @@ func TestGetKernelVersion(t *testing.T) { } func TestGetLoad(t *testing.T) { - have := host.GetLoad() + have := host.GetLoad(time.Now()) if len(have) != 3 { t.Fatalf("Expected 3 metrics, but got: %v", have) } diff --git a/render/detailed_node.go b/render/detailed_node.go index ffe7ea626b..825b1e0dbf 100644 --- a/render/detailed_node.go +++ b/render/detailed_node.go @@ -346,7 +346,9 @@ func processOriginTable(nmd report.Node, addHostTag bool, addContainerTag bool) }, len(rows) > 0 || commFound || pidFound } -func sparklineRow(human string, metric report.Metric, format func(report.Metric) (report.Metric, string)) Row { +type formatter func(report.Metric) (report.Metric, string) + +func sparklineRow(human string, metric report.Metric, format formatter) Row { if format == nil { format = formatDefault } @@ -520,6 +522,17 @@ func hostOriginTable(nmd report.Node) (Table, bool) { rows = append(rows, sparklineRow(tuple.human, val, nil)) } } + for _, tuple := range []struct { + key, human string + fmt formatter + }{ + {host.CPUUsage, "CPU Usage", formatPercent}, + {host.MemUsage, "Memory Usage", formatPercent}, + } { + if val, ok := nmd.Metrics[tuple.key]; ok { + rows = append(rows, sparklineRow(tuple.human, val, tuple.fmt)) + } + } for _, tuple := range []struct{ key, human string }{ {host.OS, "Operating system"}, {host.KernelVersion, "Kernel version"}, diff --git a/report/metrics.go b/report/metrics.go index 144f0c74f9..a098939250 100644 --- a/report/metrics.go +++ b/report/metrics.go @@ -70,6 +70,17 @@ func (m Metric) WithFirst(t time.Time) Metric { } } +// WithMax returns a fresh copy of m, with Max set to max +func (m Metric) WithMax(max float64) Metric { + return Metric{ + Samples: m.Samples, + Max: max, + Min: m.Min, + First: m.First, + Last: m.Last, + } +} + // Len returns the number of samples in the metric. func (m Metric) Len() int { if m.Samples == nil { diff --git a/vendor/github.com/c9s/goprocinfo/linux/cpuinfo.go b/vendor/github.com/c9s/goprocinfo/linux/cpuinfo.go new file mode 100644 index 0000000000..40947e5839 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/cpuinfo.go @@ -0,0 +1,127 @@ +package linux + +import ( + "io/ioutil" + "regexp" + "strconv" + "strings" +) + +type CPUInfo struct { + Processors []Processor `json:"processors"` +} + +func (self *CPUInfo) NumCPU() int { + return len(self.Processors) +} + +func (self *CPUInfo) NumCore() int { + core := make(map[string]bool) + + for _, p := range self.Processors { + pid := p.PhysicalId + cid := p.CoreId + + if pid == -1 { + return self.NumCPU() + } else { + // to avoid fmt import + key := strconv.FormatInt(int64(pid), 10) + ":" + strconv.FormatInt(int64(cid), 10) + core[key] = true + } + } + + return len(core) +} + +func (self *CPUInfo) NumPhysicalCPU() int { + pcpu := make(map[string]bool) + + for _, p := range self.Processors { + pid := p.PhysicalId + + if pid == -1 { + return self.NumCPU() + } else { + // to avoid fmt import + key := strconv.FormatInt(int64(pid), 10) + pcpu[key] = true + } + } + + return len(pcpu) +} + +type Processor struct { + Id int64 `json:"id"` + VendorId string `json:"vendor_id"` + Model int64 `json:"model"` + ModelName string `json:"model_name"` + Flags []string `json:"flags"` + Cores int64 `json:"cores"` + MHz float64 `json:"mhz"` + PhysicalId int64 `json:"physical_id"` + CoreId int64 `json:"core_id"` +} + +var cpuinfoRegExp = regexp.MustCompile("([^:]*?)\\s*:\\s*(.*)$") + +func ReadCPUInfo(path string) (*CPUInfo, error) { + b, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + content := string(b) + lines := strings.Split(content, "\n") + + var cpuinfo = CPUInfo{} + var processor = &Processor{CoreId: -1, PhysicalId: -1} + + for i, line := range lines { + var key string + var value string + + if len(line) == 0 && i != len(lines)-1 { + // end of processor + cpuinfo.Processors = append(cpuinfo.Processors, *processor) + processor = &Processor{} + continue + } else if i == len(lines)-1 { + continue + } + + submatches := cpuinfoRegExp.FindStringSubmatch(line) + key = submatches[1] + value = submatches[2] + + switch key { + case "processor": + processor.Id, _ = strconv.ParseInt(value, 10, 32) + case "vendor_id": + processor.VendorId = value + case "model": + processor.Model, _ = strconv.ParseInt(value, 10, 32) + case "model name": + processor.ModelName = value + case "flags": + processor.Flags = strings.Fields(value) + case "cpu cores": + processor.Cores, _ = strconv.ParseInt(value, 10, 32) + case "cpu MHz": + processor.MHz, _ = strconv.ParseFloat(value, 64) + case "physical id": + processor.PhysicalId, _ = strconv.ParseInt(value, 10, 32) + case "core id": + processor.CoreId, _ = strconv.ParseInt(value, 10, 32) + } + /* + processor : 0 + vendor_id : GenuineIntel + cpu family : 6 + model : 26 + model name : Intel(R) Xeon(R) CPU L5520 @ 2.27GHz + */ + } + return &cpuinfo, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/cpuinfo_test.go b/vendor/github.com/c9s/goprocinfo/linux/cpuinfo_test.go new file mode 100644 index 0000000000..2970729ca4 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/cpuinfo_test.go @@ -0,0 +1,62 @@ +package linux + +import "testing" + +func TestCPUInfo(t *testing.T) { + + cpuinfo, err := ReadCPUInfo("proc/cpuinfo") + if err != nil { + t.Fatal(err) + } + t.Logf("%+v", cpuinfo) + + if len(cpuinfo.Processors) != 8 { + t.Fatal("wrong processor number : ", len(cpuinfo.Processors)) + } + + if cpuinfo.NumCore() != 8 { + t.Fatal("wrong core number", cpuinfo.NumCore()) + } + + if cpuinfo.NumPhysicalCPU() != 2 { + t.Fatal("wrong physical cpu number", cpuinfo.NumPhysicalCPU()) + } + + cpuinfo, err = ReadCPUInfo("proc/cpuinfo_2") + if err != nil { + t.Fatal(err) + } + t.Logf("%+v", cpuinfo) + + if len(cpuinfo.Processors) != 4 { + t.Fatal("wrong processor number : ", len(cpuinfo.Processors)) + } + + if cpuinfo.NumCore() != 4 { + t.Fatal("wrong core number", cpuinfo.NumCore()) + } + + // not sure at all here + // does not match with https://github.com/randombit/cpuinfo/blob/master/x86/xeon_l5520 + if cpuinfo.NumPhysicalCPU() != 4 { + t.Fatal("wrong physical cpu number", cpuinfo.NumPhysicalCPU()) + } + + cpuinfo, err = ReadCPUInfo("proc/cpuinfo_3") + if err != nil { + t.Fatal(err) + } + t.Logf("%+v", cpuinfo) + + if len(cpuinfo.Processors) != 4 { + t.Fatal("wrong processor number : ", len(cpuinfo.Processors)) + } + + if cpuinfo.NumCore() != 2 { + t.Fatal("wrong core number", cpuinfo.NumCore()) + } + + if cpuinfo.NumPhysicalCPU() != 1 { + t.Fatal("wrong physical cpu number", cpuinfo.NumPhysicalCPU()) + } +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/disk.go b/vendor/github.com/c9s/goprocinfo/linux/disk.go new file mode 100644 index 0000000000..91d7351bfc --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/disk.go @@ -0,0 +1,26 @@ +package linux + +import ( + "syscall" +) + +type Disk struct { + All uint64 `json:"all"` + Used uint64 `json:"used"` + Free uint64 `json:"free"` + FreeInodes uint64 `json:"freeInodes"` +} + +func ReadDisk(path string) (*Disk, error) { + fs := syscall.Statfs_t{} + err := syscall.Statfs(path, &fs) + if err != nil { + return nil, err + } + disk := Disk{} + disk.All = fs.Blocks * uint64(fs.Bsize) + disk.Free = fs.Bfree * uint64(fs.Bsize) + disk.Used = disk.All - disk.Free + disk.FreeInodes = fs.Ffree + return &disk, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/disk_test.go b/vendor/github.com/c9s/goprocinfo/linux/disk_test.go new file mode 100644 index 0000000000..678f042472 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/disk_test.go @@ -0,0 +1,14 @@ +package linux + +import "testing" + +func TestDisk(t *testing.T) { + disk, err := ReadDisk("/") + t.Logf("%+v", disk) + if err != nil { + t.Fatal("disk read fail") + } + if disk.Free <= 0 { + t.Log("no good") + } +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/diskstat.go b/vendor/github.com/c9s/goprocinfo/linux/diskstat.go new file mode 100644 index 0000000000..27352b9409 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/diskstat.go @@ -0,0 +1,100 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" + "time" +) + +// DiskStat is disk statistics to help measure disk activity. +// +// Note: +// * On a very busy or long-lived system values may wrap. +// * No kernel locks are held while modifying these counters. This implies that +// minor inaccuracies may occur. +// +// More more info see: +// https://www.kernel.org/doc/Documentation/iostats.txt and +// https://www.kernel.org/doc/Documentation/block/stat.txt +type DiskStat struct { + Major int `json:"major"` // major device number + Minor int `json:"minor"` // minor device number + Name string `json:"name"` // device name + ReadIOs uint64 `json:"read_ios"` // number of read I/Os processed + ReadMerges uint64 `json:"read_merges"` // number of read I/Os merged with in-queue I/O + ReadSectors uint64 `json:"read_sectors"` // number of 512 byte sectors read + ReadTicks uint64 `json:"read_ticks"` // total wait time for read requests in milliseconds + WriteIOs uint64 `json:"write_ios"` // number of write I/Os processed + WriteMerges uint64 `json:"write_merges"` // number of write I/Os merged with in-queue I/O + WriteSectors uint64 `json:"write_sectors"` // number of 512 byte sectors written + WriteTicks uint64 `json:"write_ticks"` // total wait time for write requests in milliseconds + InFlight uint64 `json:"in_flight"` // number of I/Os currently in flight + IOTicks uint64 `json:"io_ticks"` // total time this block device has been active in milliseconds + TimeInQueue uint64 `json:"time_in_queue"` // total wait time for all requests in milliseconds +} + +// ReadDiskStats reads and parses the file. +// +// Note: +// * Assumes a well formed file and will panic if it isn't. +func ReadDiskStats(path string) ([]DiskStat, error) { + data, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + devices := strings.Split(string(data), "\n") + results := make([]DiskStat, len(devices)-1) + + for i := range results { + fields := strings.Fields(devices[i]) + Major, _ := strconv.ParseInt(fields[0], 10, strconv.IntSize) + results[i].Major = int(Major) + Minor, _ := strconv.ParseInt(fields[1], 10, strconv.IntSize) + results[i].Minor = int(Minor) + results[i].Name = fields[2] + results[i].ReadIOs, _ = strconv.ParseUint(fields[3], 10, 64) + results[i].ReadMerges, _ = strconv.ParseUint(fields[4], 10, 64) + results[i].ReadSectors, _ = strconv.ParseUint(fields[5], 10, 64) + results[i].ReadTicks, _ = strconv.ParseUint(fields[6], 10, 64) + results[i].WriteIOs, _ = strconv.ParseUint(fields[7], 10, 64) + results[i].WriteMerges, _ = strconv.ParseUint(fields[8], 10, 64) + results[i].WriteSectors, _ = strconv.ParseUint(fields[9], 10, 64) + results[i].WriteTicks, _ = strconv.ParseUint(fields[10], 10, 64) + results[i].InFlight, _ = strconv.ParseUint(fields[11], 10, 64) + results[i].IOTicks, _ = strconv.ParseUint(fields[12], 10, 64) + results[i].TimeInQueue, _ = strconv.ParseUint(fields[13], 10, 64) + } + + return results, nil +} + +// GetReadBytes returns the number of bytes read. +func (ds *DiskStat) GetReadBytes() int64 { + return int64(ds.ReadSectors) * 512 +} + +// GetReadTicks returns the duration waited for read requests. +func (ds *DiskStat) GetReadTicks() time.Duration { + return time.Duration(ds.ReadTicks) * time.Millisecond +} + +// GetWriteBytes returns the number of bytes written. +func (ds *DiskStat) GetWriteBytes() int64 { + return int64(ds.WriteSectors) * 512 +} + +// GetReadTicks returns the duration waited for write requests. +func (ds *DiskStat) GetWriteTicks() time.Duration { + return time.Duration(ds.WriteTicks) * time.Millisecond +} + +// GetIOTicks returns the duration the disk has been active. +func (ds *DiskStat) GetIOTicks() time.Duration { + return time.Duration(ds.IOTicks) * time.Millisecond +} + +// GetTimeInQueue returns the duration waited for all requests. +func (ds *DiskStat) GetTimeInQueue() time.Duration { + return time.Duration(ds.TimeInQueue) * time.Millisecond +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/diskstat_test.go b/vendor/github.com/c9s/goprocinfo/linux/diskstat_test.go new file mode 100644 index 0000000000..cee3253daa --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/diskstat_test.go @@ -0,0 +1,56 @@ +package linux + +import ( + "reflect" + "testing" +) + +func TestDiskStats(t *testing.T) { + + var expected = []DiskStat{ + {8, 0, "sda", 408408680, 130632009, 4314826058, 420257268, 1096727, 1393188, 17797821, 100425864, 0, 28419096, 520607448}, + {8, 1, "sda1", 408408656, 130632009, 4314825866, 420257172, 833896, 1393188, 17797821, 97461104, 0, 27780028, 517642756}, + {8, 32, "sdc", 783343, 36781, 110773558, 4017272, 361505, 12583190, 103537696, 36551180, 1, 2894524, 40572548}, + {8, 33, "sdc1", 782412, 36169, 110761218, 4013708, 355897, 12581496, 103518680, 36525596, 1, 2871296, 40543408}, + {8, 34, "sdc2", 2, 0, 4, 516, 0, 0, 0, 0, 0, 516, 516}, + {8, 37, "sdc5", 683, 612, 10360, 2088, 683, 1694, 19016, 1368, 0, 2436, 3452}, + {8, 16, "sdb", 312308426, 182345841, 3959499204, 361778396, 1106171, 1389910, 17845949, 43843528, 0, 29961500, 405464444}, + {8, 17, "sdb1", 312308273, 182345841, 3959497980, 361778280, 843340, 1389910, 17845949, 41908056, 0, 29850872, 403529168}, + {8, 48, "sdd", 417146071, 77508205, 3959480897, 326427168, 1111215, 1414930, 18087405, 81691580, 0, 24858444, 407955392}, + {8, 49, "sdd1", 417145917, 77508205, 3959479665, 326427124, 848384, 1414930, 18087405, 78896812, 0, 24244124, 405160740}, + {8, 96, "sdg", 235527286, 259133373, 3959571171, 622690468, 1103264, 1412477, 18003005, 55607916, 0, 40736480, 678171012}, + {8, 97, "sdg1", 235527254, 259133373, 3959570915, 622690416, 840433, 1412477, 18003005, 53727712, 0, 40290036, 676291336}, + {8, 112, "sdh", 236183930, 258478518, 3959539350, 736661480, 1102696, 1417936, 18042109, 234611108, 3, 168077436, 1235195636}, + {8, 113, "sdh1", 236183899, 258478518, 3959539102, 736661420, 839866, 1417936, 18042109, 231864320, 2, 167583296, 1106678880}, + {8, 128, "sdi", 241879666, 252778748, 3959567060, 565428444, 1077187, 1403162, 17722541, 139558580, 0, 39615292, 704848044}, + {8, 129, "sdi1", 241879648, 252778748, 3959566916, 565428432, 814356, 1403162, 17722541, 136947936, 0, 39169104, 702237520}, + {8, 144, "sdj", 239842786, 254815073, 3959571267, 605240464, 1093790, 1412969, 17933493, 217510180, 0, 40249492, 822590992}, + {8, 145, "sdj1", 239842768, 254815073, 3959571123, 605240444, 830959, 1412969, 17933493, 214796948, 0, 39767160, 819880212}, + {8, 176, "sdl", 108207, 237256, 2854160, 13616744, 68344261, 905844149, 7819441640, 2108756004, 0, 54571620, 2128460032}, + {8, 177, "sdl1", 107707, 237243, 2850056, 13611560, 64425824, 421281850, 3912412464, 135188688, 0, 40488396, 154892008}, + {8, 64, "sde", 244185799, 250469327, 3959527864, 359434792, 1100737, 1407009, 17939589, 49226088, 0, 38876124, 408523288}, + {8, 65, "sde1", 244185781, 250469327, 3959527720, 359434700, 837906, 1407009, 17939589, 47151748, 0, 38603948, 406448252}, + {8, 80, "sdf", 240921831, 253733300, 3959498760, 353025952, 1128792, 1408474, 18175485, 53618980, 0, 38840468, 406489356}, + {8, 81, "sdf1", 240921678, 253733300, 3959497536, 353025840, 865961, 1408474, 18175485, 51534252, 0, 38585732, 404403848}, + {8, 160, "sdk", 236490604, 258168217, 3959527459, 1334815348, 1130766, 1411015, 18211397, 41485768, 0, 47199324, 1376168140}, + {8, 161, "sdk1", 236490587, 258168217, 3959527323, 1334815288, 867935, 1411015, 18211397, 39843692, 0, 46972288, 1374525752}, + {7, 0, "loop0", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {7, 1, "loop1", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {7, 2, "loop2", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {7, 3, "loop3", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {7, 4, "loop4", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {7, 5, "loop5", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {7, 6, "loop6", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {7, 7, "loop7", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {9, 2, "md2", 796135, 0, 31381793, 0, 1575830, 0, 72485781, 0, 0, 0, 0}, + {253, 0, "dm-0", 795799, 0, 31379112, 17971064, 1316610, 0, 72485909, 3892582864, 4, 144707052, 5470248}, + } + + stats, err := ReadDiskStats("proc/diskstats") + if err != nil { + t.Fatal("disk stat read fail") + } + if !reflect.DeepEqual(stats, expected) { + t.Error("not equal to expected") + } +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/loadavg.go b/vendor/github.com/c9s/goprocinfo/linux/loadavg.go new file mode 100644 index 0000000000..ad75fa942c --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/loadavg.go @@ -0,0 +1,67 @@ +package linux + +import ( + "errors" + "io/ioutil" + "strconv" + "strings" +) + +type LoadAvg struct { + Last1Min float64 `json:"last1min"` + Last5Min float64 `json:"last5min"` + Last15Min float64 `json:"last15min"` + ProcessRunning uint64 `json:"process_running"` + ProcessTotal uint64 `json:"process_total"` + LastPID uint64 `json:"last_pid"` +} + +func ReadLoadAvg(path string) (*LoadAvg, error) { + + b, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + content := strings.TrimSpace(string(b)) + fields := strings.Fields(content) + + if len(fields) < 5 { + return nil, errors.New("Cannot parse loadavg: " + content) + } + + process := strings.Split(fields[3], "/") + + if len(process) != 2 { + return nil, errors.New("Cannot parse loadavg: " + content) + } + + loadavg := LoadAvg{} + + if loadavg.Last1Min, err = strconv.ParseFloat(fields[0], 64); err != nil { + return nil, err + } + + if loadavg.Last5Min, err = strconv.ParseFloat(fields[1], 64); err != nil { + return nil, err + } + + if loadavg.Last15Min, err = strconv.ParseFloat(fields[2], 64); err != nil { + return nil, err + } + + if loadavg.ProcessRunning, err = strconv.ParseUint(process[0], 10, 64); err != nil { + return nil, err + } + + if loadavg.ProcessTotal, err = strconv.ParseUint(process[1], 10, 64); err != nil { + return nil, err + } + + if loadavg.LastPID, err = strconv.ParseUint(fields[4], 10, 64); err != nil { + return nil, err + } + + return &loadavg, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/loadavg_test.go b/vendor/github.com/c9s/goprocinfo/linux/loadavg_test.go new file mode 100644 index 0000000000..5d1c001223 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/loadavg_test.go @@ -0,0 +1,30 @@ +package linux + +import ( + "reflect" + "testing" +) + +func TestLoadAvg(t *testing.T) { + + loadavg, err := ReadLoadAvg("proc/loadavg") + + if err != nil { + t.Fatal("read loadavg fail", err) + } + + expected := &LoadAvg{ + Last1Min: 0.01, + Last5Min: 0.02, + Last15Min: 0.05, + ProcessRunning: 1, + ProcessTotal: 135, + LastPID: 11975, + } + + if !reflect.DeepEqual(loadavg, expected) { + t.Errorf("not equal to expected %+v", expected) + } + + t.Logf("%+v", loadavg) +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/meminfo.go b/vendor/github.com/c9s/goprocinfo/linux/meminfo.go new file mode 100644 index 0000000000..09d76281a3 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/meminfo.go @@ -0,0 +1,97 @@ +package linux + +import ( + "io/ioutil" + "reflect" + "strconv" + "strings" +) + +type MemInfo struct { + MemTotal uint64 `json:"mem_total"` + MemFree uint64 `json:"mem_free"` + MemAvailable uint64 `json:"mem_available"` + Buffers uint64 `json:"buffers"` + Cached uint64 `json:"cached"` + SwapCached uint64 `json:"swap_cached"` + Active uint64 `json:"active"` + Inactive uint64 `json:"inactive"` + ActiveAnon uint64 `json:"active_anon" field:"Active(anon)"` + InactiveAnon uint64 `json:"inactive_anon" field:"Inactive(anon)"` + ActiveFile uint64 `json:"active_file" field:"Active(file)"` + InactiveFile uint64 `json:"inactive_file" field:"Inactive(file)"` + Unevictable uint64 `json:"unevictable"` + Mlocked uint64 `json:"mlocked"` + SwapTotal uint64 `json:"swap_total"` + SwapFree uint64 `json:"swap_free"` + Dirty uint64 `json:"dirty"` + Writeback uint64 `json:"write_back"` + AnonPages uint64 `json:"anon_pages"` + Mapped uint64 `json:"mapped"` + Shmem uint64 `json:"shmem"` + Slab uint64 `json:"slab"` + SReclaimable uint64 `json:"s_reclaimable"` + SUnreclaim uint64 `json:"s_unclaim"` + KernelStack uint64 `json:"kernel_stack"` + PageTables uint64 `json:"page_tables"` + NFS_Unstable uint64 `json:"nfs_unstable"` + Bounce uint64 `json:"bounce"` + WritebackTmp uint64 `json:"writeback_tmp"` + CommitLimit uint64 `json:"commit_limit"` + Committed_AS uint64 `json:"committed_as"` + VmallocTotal uint64 `json:"vmalloc_total"` + VmallocUsed uint64 `json:"vmalloc_used"` + VmallocChunk uint64 `json:"vmalloc_chunk"` + HardwareCorrupted uint64 `json:"hardware_corrupted"` + AnonHugePages uint64 `json:"anon_huge_pages"` + HugePages_Total uint64 `json:"huge_pages_total"` + HugePages_Free uint64 `json:"huge_pages_free"` + HugePages_Rsvd uint64 `json:"huge_pages_rsvd"` + HugePages_Surp uint64 `json:"huge_pages_surp"` + Hugepagesize uint64 `json:"hugepagesize"` + DirectMap4k uint64 `json:"direct_map_4k"` + DirectMap2M uint64 `json:"direct_map_2M"` + DirectMap1G uint64 `json:"direct_map_1G"` +} + +func ReadMemInfo(path string) (*MemInfo, error) { + data, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + lines := strings.Split(string(data), "\n") + + // Maps a meminfo metric to its value (i.e. MemTotal --> 100000) + statMap := make(map[string]uint64) + + var info = MemInfo{} + + for _, line := range lines { + fields := strings.SplitN(line, ":", 2) + if len(fields) < 2 { + continue + } + valFields := strings.Fields(fields[1]) + val, _ := strconv.ParseUint(valFields[0], 10, 64) + statMap[fields[0]] = val + } + + elem := reflect.ValueOf(&info).Elem() + typeOfElem := elem.Type() + + for i := 0; i < elem.NumField(); i++ { + val, ok := statMap[typeOfElem.Field(i).Name] + if ok { + elem.Field(i).SetUint(val) + continue + } + val, ok = statMap[typeOfElem.Field(i).Tag.Get("field")] + if ok { + elem.Field(i).SetUint(val) + } + } + + return &info, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/meminfo_test.go b/vendor/github.com/c9s/goprocinfo/linux/meminfo_test.go new file mode 100644 index 0000000000..fe347183b8 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/meminfo_test.go @@ -0,0 +1,61 @@ +package linux + +import ( + "fmt" + "reflect" + "testing" +) + +func TestMemInfo(t *testing.T) { + { + var expected = MemInfo{1011048, 92096, 0, 44304, 681228, 4, 494100, 306804, 71424, 9576, 422676, 297228, 0, 0, 524284, 524280, 28, 0, 75444, 26384, 5624, 60884, 45068, 15816, 1112, 2936, 0, 0, 0, 1029808, 528152, 34359738367, 10504, 34359725792, 0, 0, 0, 0, 0, 0, 2048, 1056768, 0, 0} + + read, err := ReadMemInfo("proc/meminfo_1") + if err != nil { + t.Fatal("meminfo read fail") + } + t.Logf("%+v", read) + + if err := compareExpectedReadFieldsMemInfo(&expected, read); err != nil { + t.Error(err.Error()) + } + + if !reflect.DeepEqual(*read, expected) { + t.Error("not equal to expected") + } + } + { + var expected = MemInfo{132003228, 126199196, 130327756, 819908, 2910788, 0, 3043760, 1027084, 340788, 1056, 2702972, 1026028, 0, 0, 3903484, 3903484, 8, 0, 342276, 72380, 1704, 899472, 737432, 162040, 7328, 7120, 0, 0, 0, 69905096, 1024672, 34359738367, 495100, 34290957508, 0, 172032, 0, 0, 0, 0, 2048, 143652, 14501888, 121634816} + + read, err := ReadMemInfo("proc/meminfo_2") + if err != nil { + t.Fatal("meminfo read fail") + } + t.Logf("%+v", read) + + if err := compareExpectedReadFieldsMemInfo(&expected, read); err != nil { + t.Error(err.Error()) + } + + if !reflect.DeepEqual(*read, expected) { + t.Error("not equal to expected") + } + } +} + +//This is a helper function which makes it easier to track down errors in expected versus read values. +func compareExpectedReadFieldsMemInfo(expected *MemInfo, read *MemInfo) error { + elemExpected := reflect.ValueOf(*expected) + typeOfElemExpected := elemExpected.Type() + elemRead := reflect.ValueOf(*read) + + for i := 0; i < elemExpected.NumField(); i++ { + fieldName := typeOfElemExpected.Field(i).Name + + if elemExpected.Field(i).Uint() != elemRead.Field(i).Uint() { + return fmt.Errorf("Read value not equal to expected value for field %s. Got %d and expected %d.", fieldName, elemRead.Field(i).Uint(), elemExpected.Field(i).Uint()) + } + } + + return nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/mounts.go b/vendor/github.com/c9s/goprocinfo/linux/mounts.go new file mode 100644 index 0000000000..5aa4925fa7 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/mounts.go @@ -0,0 +1,49 @@ +package linux + +import ( + "bufio" + "os" + "strings" +) + +type Mounts struct { + Mounts []Mount `json:"mounts"` +} + +type Mount struct { + Device string `json:"device"` + MountPoint string `json:"mountpoint"` + FSType string `json:"fstype"` + Options string `json:"options"` +} + +const ( + DefaultBufferSize = 1024 +) + +func ReadMounts(path string) (*Mounts, error) { + fin, err := os.Open(path) + if err != nil { + return nil, err + } + defer fin.Close() + + var mounts = Mounts{} + + scanner := bufio.NewScanner(fin) + for scanner.Scan() { + fields := strings.Fields(scanner.Text()) + var mount = &Mount{ + fields[0], + fields[1], + fields[2], + fields[3], + } + mounts.Mounts = append(mounts.Mounts, *mount) + } + + if err := scanner.Err(); err != nil { + return nil, err + } + return &mounts, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/mounts_test.go b/vendor/github.com/c9s/goprocinfo/linux/mounts_test.go new file mode 100644 index 0000000000..272df54c6f --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/mounts_test.go @@ -0,0 +1,19 @@ +package linux + +import ( + "testing" +) + +func TestMounts(t *testing.T) { + mounts, err := ReadMounts("proc/mounts") + if err != nil { + t.Fatal("mounts read fail") + } + t.Logf("%+v", mounts) + if mounts.Mounts[0].Device != "rootfs" { + t.Fatal("unexpected value") + } + if mounts.Mounts[1].FSType != "proc" { + t.Fatal("unexpected value") + } +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/net_ip.go b/vendor/github.com/c9s/goprocinfo/linux/net_ip.go new file mode 100644 index 0000000000..ed02894056 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/net_ip.go @@ -0,0 +1,173 @@ +package linux + +import ( + "errors" + "net" + "regexp" + "strconv" + "strings" +) + +var ( + ipv4RegExp = regexp.MustCompile("^[0-9a-fA-F]{8}:[0-9a-fA-F]{4}$") // Regex for NetIPv4Decoder + ipv6RegExp = regexp.MustCompile("^[0-9a-fA-F]{32}:[0-9a-fA-F]{4}$") // Regex for NetIPv6Decoder +) + +type NetIPDecoder func(string) (string, error) // Either NetIPv4Decoder or NetIPv6Decoder + +type NetSocket struct { + LocalAddress string `json:"local_address"` + RemoteAddress string `json:"remote_address"` + Status uint8 `json:"st"` + TxQueue uint64 `json:"tx_queue"` + RxQueue uint64 `json:"rx_queue"` + Uid uint32 `json:"uid"` + Inode uint64 `json:"inode"` + SocketReferenceCount uint64 `json:"ref"` +} + +func parseNetSocket(f []string, ip NetIPDecoder) (*NetSocket, error) { + + if len(f) < 11 { + return nil, errors.New("Cannot parse net socket line: " + strings.Join(f, " ")) + } + + if strings.Index(f[4], ":") == -1 { + return nil, errors.New("Cannot parse tx/rx queues: " + f[4]) + } + + q := strings.Split(f[4], ":") + + socket := &NetSocket{} + + var s uint64 // socket.Status + var u uint64 // socket.Uid + var err error // parse error + + if socket.LocalAddress, err = ip(f[1]); err != nil { + return nil, err + } + + if socket.RemoteAddress, err = ip(f[2]); err != nil { + return nil, err + } + + if s, err = strconv.ParseUint(f[3], 16, 8); err != nil { + return nil, err + } + + if socket.TxQueue, err = strconv.ParseUint(q[0], 16, 64); err != nil { + return nil, err + } + + if socket.RxQueue, err = strconv.ParseUint(q[1], 16, 64); err != nil { + return nil, err + } + + if u, err = strconv.ParseUint(f[7], 10, 32); err != nil { + return nil, err + } + + if socket.Inode, err = strconv.ParseUint(f[9], 10, 64); err != nil { + return nil, err + } + + if socket.SocketReferenceCount, err = strconv.ParseUint(f[10], 10, 64); err != nil { + return nil, err + } + + socket.Status = uint8(s) + socket.Uid = uint32(u) + + return socket, nil +} + +// Decode an IPv4 address with port from a given hex string +// NOTE: This function match NetIPDecoder type +func NetIPv4Decoder(s string) (string, error) { + + if !ipv4RegExp.MatchString(s) { + return "", errors.New("Cannot decode ipv4 address: " + s) + } + + i := strings.Split(s, ":") + + b := make([]byte, 4) + + for j := 0; j < 4; j++ { + + x := j * 2 + y := x + 2 + z := 3 - j + + // Extract 2 characters from hex string, 4 times. + // + // s: "0100007F" -> [ + // h: "01", h: "00", h: "00", h: "7F", + // ] + h := i[0][x:y] + + // Reverse byte order + n, _ := strconv.ParseUint(h, 16, 8) + b[z] = byte(n) + + } + + h := net.IP(b).String() + n, _ := strconv.ParseUint(i[1], 16, 64) + p := strconv.FormatUint(n, 10) + + // ipv4:port + v := h + ":" + p + + return v, nil +} + +// Decode an IPv6 address with port from a given hex string +// NOTE: This function match NetIPDecoder type +func NetIPv6Decoder(s string) (string, error) { + + if !ipv6RegExp.MatchString(s) { + return "", errors.New("Cannot decode ipv6 address: " + s) + } + + i := strings.Split(s, ":") + + b := make([]byte, 16) + + for j := 0; j < 4; j++ { + + x := j * 8 + y := x + 8 + + // Extract 8 characters from hex string, 4 times. + // + // s: "350E012A900F122E85EDEAADA64DAAD1" -> [ + // h: "350E012A", h: "900F122E", + // h: "85EDEAAD", h: "A64DAAD1", + // ] + h := i[0][x:y] + + for k := 0; k < 4; k++ { + + // Reverse byte order + // "350E012A" -> [ 0x2A, 0x01, 0x0E, 0x35 ] + z := (j * 4) + k + g := 8 - (k * 2) + f := g - 2 + + n, _ := strconv.ParseUint(h[f:g], 16, 8) + b[z] = byte(n) + + } + } + + h := net.IP(b).String() + n, _ := strconv.ParseUint(i[1], 16, 64) + p := strconv.FormatUint(n, 10) + + // ipv6:port + v := h + ":" + p + + return v, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/net_ip_test.go b/vendor/github.com/c9s/goprocinfo/linux/net_ip_test.go new file mode 100644 index 0000000000..69c2be03b3 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/net_ip_test.go @@ -0,0 +1,65 @@ +package linux + +import ( + "testing" +) + +func TestNetIPv4DecoderRemote(t *testing.T) { + + ip, err := NetIPv4Decoder("00000000:0050") + + if err != nil { + t.Fatal("net ipv4 decode fail", err) + } + + if ip != "0.0.0.0:80" { + t.Error("unexpected value") + } + + t.Logf("%+v", ip) +} + +func TestNetIPv4DecoderLocal(t *testing.T) { + + ip, err := NetIPv4Decoder("0100007F:1F90") + + if err != nil { + t.Fatal("net ipv4 decode fail", err) + } + + if ip != "127.0.0.1:8080" { + t.Error("unexpected value") + } + + t.Logf("%+v", ip) +} + +func TestNetIPv6DecoderRemote(t *testing.T) { + + ip, err := NetIPv6Decoder("350E012A900F122E85EDEAADA64DAAD1:0016") + + if err != nil { + t.Fatal("net ipv6 decode fail", err) + } + + if ip != "2a01:e35:2e12:f90:adea:ed85:d1aa:4da6:22" { + t.Error("unexpected value") + } + + t.Logf("%+v", ip) +} + +func TestNetIPv6DecoderLocal(t *testing.T) { + + ip, err := NetIPv6Decoder("00000000000000000000000001000000:2328") + + if err != nil { + t.Fatal("net ipv6 decode fail", err) + } + + if ip != "::1:9000" { + t.Error("unexpected value") + } + + t.Logf("%+v", ip) +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/net_tcp.go b/vendor/github.com/c9s/goprocinfo/linux/net_tcp.go new file mode 100644 index 0000000000..1e720b7261 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/net_tcp.go @@ -0,0 +1,82 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" +) + +type NetTCPSockets struct { + Sockets []NetTCPSocket `json:"sockets"` +} + +type NetTCPSocket struct { + NetSocket + RetransmitTimeout uint64 `json:"retransmit_timeout"` + PredictedTick uint64 `json:"predicted_tick"` + AckQuick uint8 `json:"ack_quick"` + AckPingpong bool `json:"ack_pingpong"` + SendingCongestionWindow uint64 `json:"sending_congestion_window"` + SlowStartSizeThreshold int64 `json:"slow_start_size_threshold"` +} + +func ReadNetTCPSockets(path string, ip NetIPDecoder) (*NetTCPSockets, error) { + + b, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + lines := strings.Split(string(b), "\n") + + tcp := &NetTCPSockets{} + + for i := 1; i < len(lines); i++ { + + line := lines[i] + + f := strings.Fields(line) + + if len(f) < 17 { + continue + } + + s, err := parseNetSocket(f, ip) + + if err != nil { + return nil, err + } + + var n int64 + e := &NetTCPSocket{ + NetSocket: *s, + } + + if e.RetransmitTimeout, err = strconv.ParseUint(f[12], 10, 64); err != nil { + return nil, err + } + + if e.PredictedTick, err = strconv.ParseUint(f[13], 10, 64); err != nil { + return nil, err + } + + if n, err = strconv.ParseInt(f[14], 10, 8); err != nil { + return nil, err + } + e.AckQuick = uint8(n >> 1) + e.AckPingpong = ((n & 1) == 1) + + if e.SendingCongestionWindow, err = strconv.ParseUint(f[15], 10, 64); err != nil { + return nil, err + } + + if e.SlowStartSizeThreshold, err = strconv.ParseInt(f[16], 10, 32); err != nil { + return nil, err + } + + tcp.Sockets = append(tcp.Sockets, *e) + } + + return tcp, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/net_tcp_test.go b/vendor/github.com/c9s/goprocinfo/linux/net_tcp_test.go new file mode 100644 index 0000000000..758ea7e210 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/net_tcp_test.go @@ -0,0 +1,88 @@ +package linux + +import ( + "reflect" + "testing" +) + +func TestReadNetTCP(t *testing.T) { + + tcp, err := ReadNetTCPSockets("proc/net_tcp", NetIPv4Decoder) + + if err != nil { + t.Fatal("net tcp read fail", err) + } + + expected := &NetTCPSockets{ + Sockets: []NetTCPSocket{ + NetTCPSocket{ + NetSocket: NetSocket{ + LocalAddress: "127.0.0.1:8080", RemoteAddress: "0.0.0.0:0", Status: 10, + TxQueue: 0, RxQueue: 0, Uid: 1000, Inode: 569261, SocketReferenceCount: 1, + }, + RetransmitTimeout: 100, PredictedTick: 0, AckQuick: 0, AckPingpong: false, + SendingCongestionWindow: 10, SlowStartSizeThreshold: -1, + }, + NetTCPSocket{ + NetSocket: NetSocket{ + LocalAddress: "0.0.0.0:80", RemoteAddress: "0.0.0.0:0", Status: 10, + TxQueue: 0, RxQueue: 0, Uid: 0, Inode: 4609, SocketReferenceCount: 1, + }, + RetransmitTimeout: 100, PredictedTick: 0, AckQuick: 0, AckPingpong: false, + SendingCongestionWindow: 10, SlowStartSizeThreshold: -1, + }, + NetTCPSocket{ + NetSocket: NetSocket{ + LocalAddress: "0.0.0.0:22", RemoteAddress: "0.0.0.0:0", Status: 10, + TxQueue: 0, RxQueue: 0, Uid: 0, Inode: 420553, SocketReferenceCount: 1, + }, + RetransmitTimeout: 100, PredictedTick: 0, AckQuick: 0, AckPingpong: false, + SendingCongestionWindow: 10, SlowStartSizeThreshold: -1, + }, + NetTCPSocket{ + NetSocket: NetSocket{ + LocalAddress: "10.0.7.21:22", RemoteAddress: "10.0.251.11:53280", Status: 1, + TxQueue: 96, RxQueue: 0, Uid: 0, Inode: 582338, SocketReferenceCount: 4, + }, + RetransmitTimeout: 29, PredictedTick: 4, AckQuick: 13, AckPingpong: true, + SendingCongestionWindow: 10, SlowStartSizeThreshold: -1, + }, + }, + } + + if !reflect.DeepEqual(tcp, expected) { + t.Errorf("not equal to expected %+v", expected) + } + + t.Logf("%+v", tcp) + +} + +func TestReadNetTCP6(t *testing.T) { + + tcp, err := ReadNetTCPSockets("proc/net_tcp6", NetIPv6Decoder) + + if err != nil { + t.Fatal("net tcp read fail", err) + } + + expected := &NetTCPSockets{ + Sockets: []NetTCPSocket{ + NetTCPSocket{ + NetSocket: NetSocket{ + LocalAddress: ":::22", RemoteAddress: ":::0", Status: 10, + TxQueue: 0, RxQueue: 0, Uid: 0, Inode: 420555, SocketReferenceCount: 1, + }, + RetransmitTimeout: 100, PredictedTick: 0, AckQuick: 0, AckPingpong: false, + SendingCongestionWindow: 2, SlowStartSizeThreshold: -1, + }, + }, + } + + if !reflect.DeepEqual(tcp, expected) { + t.Errorf("not equal to expected %+v", expected) + } + + t.Logf("%+v", tcp) + +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/net_udp.go b/vendor/github.com/c9s/goprocinfo/linux/net_udp.go new file mode 100644 index 0000000000..b1b68e3695 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/net_udp.go @@ -0,0 +1,59 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" +) + +type NetUDPSockets struct { + Sockets []NetUDPSocket `json:"sockets"` +} + +type NetUDPSocket struct { + NetSocket + Drops uint64 `json:"drops"` +} + +func ReadNetUDPSockets(path string, ip NetIPDecoder) (*NetUDPSockets, error) { + + b, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + lines := strings.Split(string(b), "\n") + + udp := &NetUDPSockets{} + + for i := 1; i < len(lines); i++ { + + line := lines[i] + + f := strings.Fields(line) + + if len(f) < 13 { + continue + } + + s, err := parseNetSocket(f, ip) + + if err != nil { + return nil, err + } + + e := &NetUDPSocket{ + NetSocket: *s, + Drops: 0, + } + + if e.Drops, err = strconv.ParseUint(f[12], 10, 64); err != nil { + return nil, err + } + + udp.Sockets = append(udp.Sockets, *e) + } + + return udp, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/net_udp_test.go b/vendor/github.com/c9s/goprocinfo/linux/net_udp_test.go new file mode 100644 index 0000000000..6275b4c3a5 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/net_udp_test.go @@ -0,0 +1,137 @@ +package linux + +import ( + "reflect" + "testing" +) + +func TestReadNetUDP(t *testing.T) { + + udp, err := ReadNetUDPSockets("proc/net_udp", NetIPv4Decoder) + + if err != nil { + t.Fatal("net udp read fail", err) + } + + expected := &NetUDPSockets{ + Sockets: []NetUDPSocket{ + NetUDPSocket{ + NetSocket: NetSocket{ + LocalAddress: "127.0.0.1:53", RemoteAddress: "0.0.0.0:0", Status: 7, + TxQueue: 0, RxQueue: 0, Uid: 0, Inode: 11833, SocketReferenceCount: 2, + }, + Drops: 0, + }, + NetUDPSocket{ + NetSocket: NetSocket{ + LocalAddress: "0.0.0.0:68", RemoteAddress: "0.0.0.0:0", Status: 7, + TxQueue: 0, RxQueue: 0, Uid: 0, Inode: 12616, SocketReferenceCount: 2, + }, + Drops: 0, + }, + NetUDPSocket{ + NetSocket: NetSocket{ + LocalAddress: "192.168.1.111:123", RemoteAddress: "0.0.0.0:0", Status: 7, + TxQueue: 0, RxQueue: 0, Uid: 0, Inode: 18789, SocketReferenceCount: 2, + }, + Drops: 0, + }, + NetUDPSocket{ + NetSocket: NetSocket{ + LocalAddress: "127.0.0.1:123", RemoteAddress: "0.0.0.0:0", Status: 7, + TxQueue: 0, RxQueue: 0, Uid: 0, Inode: 18788, SocketReferenceCount: 2, + }, + Drops: 0, + }, + NetUDPSocket{ + NetSocket: NetSocket{ + LocalAddress: "0.0.0.0:123", RemoteAddress: "0.0.0.0:0", Status: 7, + TxQueue: 0, RxQueue: 0, Uid: 0, Inode: 18781, SocketReferenceCount: 2, + }, + Drops: 0, + }, + NetUDPSocket{ + NetSocket: NetSocket{ + LocalAddress: "0.0.0.0:5353", RemoteAddress: "0.0.0.0:0", Status: 7, + TxQueue: 0, RxQueue: 0, Uid: 109, Inode: 9025, SocketReferenceCount: 2, + }, + Drops: 2237, + }, + }, + } + + if !reflect.DeepEqual(udp, expected) { + t.Errorf("not equal to expected %+v", expected) + } + + t.Logf("%+v", udp) +} + +func TestReadNetUDP6(t *testing.T) { + + udp, err := ReadNetUDPSockets("proc/net_udp6", NetIPv6Decoder) + + if err != nil { + t.Fatal("net udp read fail", err) + } + + expected := &NetUDPSockets{ + Sockets: []NetUDPSocket{ + NetUDPSocket{ + NetSocket: NetSocket{ + LocalAddress: "::1:123", RemoteAddress: ":::0", Status: 7, + TxQueue: 0, RxQueue: 0, Uid: 0, Inode: 840244, SocketReferenceCount: 2, + }, + Drops: 0, + }, + NetUDPSocket{ + NetSocket: NetSocket{ + LocalAddress: "fe80::221:6aff:fea0:dd5e:123", RemoteAddress: ":::0", Status: 7, + TxQueue: 0, RxQueue: 0, Uid: 0, Inode: 840243, SocketReferenceCount: 2, + }, + Drops: 0, + }, + NetUDPSocket{ + NetSocket: NetSocket{ + LocalAddress: "fe80::226:b9ff:fe1f:155e:123", RemoteAddress: ":::0", Status: 7, + TxQueue: 0, RxQueue: 0, Uid: 0, Inode: 840242, SocketReferenceCount: 2, + }, + Drops: 0, + }, + NetUDPSocket{ + NetSocket: NetSocket{ + LocalAddress: "2a01:e35:2e12:f90:226:b9ff:fe1f:155e:123", RemoteAddress: ":::0", Status: 7, + TxQueue: 0, RxQueue: 0, Uid: 0, Inode: 840241, SocketReferenceCount: 2, + }, + Drops: 0, + }, + NetUDPSocket{ + NetSocket: NetSocket{ + LocalAddress: "2a01:e35:2e12:f90:adea:ed85:d1aa:4da6:123", RemoteAddress: ":::0", Status: 7, + TxQueue: 0, RxQueue: 0, Uid: 0, Inode: 840240, SocketReferenceCount: 2, + }, + Drops: 0, + }, + NetUDPSocket{ + NetSocket: NetSocket{ + LocalAddress: ":::123", RemoteAddress: ":::0", Status: 7, + TxQueue: 0, RxQueue: 0, Uid: 0, Inode: 840231, SocketReferenceCount: 2, + }, + Drops: 8946, + }, + NetUDPSocket{ + NetSocket: NetSocket{ + LocalAddress: ":::5353", RemoteAddress: ":::0", Status: 7, + TxQueue: 0, RxQueue: 0, Uid: 109, Inode: 8944, SocketReferenceCount: 2, + }, + Drops: 0, + }, + }, + } + + if !reflect.DeepEqual(udp, expected) { + t.Errorf("not equal to expected %+v", expected) + } + + t.Logf("%+v", udp) +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/netstat.go b/vendor/github.com/c9s/goprocinfo/linux/netstat.go new file mode 100644 index 0000000000..a2c913dd28 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/netstat.go @@ -0,0 +1,174 @@ +package linux + +import ( + "io/ioutil" + "reflect" + "strconv" + "strings" +) + +type NetStat struct { + // TcpExt + SyncookiesSent uint64 `json:"syncookie_sent"` + SyncookiesRecv uint64 `json:"syncookies_recv"` + SyncookiesFailed uint64 `json:"syncookies_failed"` + EmbryonicRsts uint64 `json:"embryonic_rsts"` + PruneCalled uint64 `json:"prune_called"` + RcvPruned uint64 `json:"rcv_pruned"` + OfoPruned uint64 `json:"ofo_pruned"` + OutOfWindowIcmps uint64 `json:"out_of_window_icmps"` + LockDroppedIcmps uint64 `json:"lock_dropped_icmps"` + ArpFilter uint64 `json:"arp_filter"` + TW uint64 `json:"tw"` + TWRecycled uint64 `json:"tw_recycled"` + TWKilled uint64 `json:"tw_killed"` + PAWSPassive uint64 `json:"paws_passive"` + PAWSActive uint64 `json:"paws_active"` + PAWSEstab uint64 `json:"paws_estab"` + DelayedACKs uint64 `json:"delayed_acks"` + DelayedACKLocked uint64 `json:"delayed_ack_locked"` + DelayedACKLost uint64 `json:"delayed_ack_lost"` + ListenOverflows uint64 `json:"listen_overflows"` + ListenDrops uint64 `json:"listen_drops"` + TCPPrequeued uint64 `json:"tcp_prequeued"` + TCPDirectCopyFromBacklog uint64 `json:"tcp_direct_copy_from_backlog"` + TCPDirectCopyFromPrequeue uint64 `json:"tcp_direct_copy_from_prequeue"` + TCPPrequeueDropped uint64 `json:"tcp_prequeue_dropped"` + TCPHPHits uint64 `json:"tcp_hp_hits"` + TCPHPHitsToUser uint64 `json:"tcp_hp_hits_to_user"` + TCPPureAcks uint64 `json:"tcp_pure_acks"` + TCPHPAcks uint64 `json:"tcp_hp_acks"` + TCPRenoRecovery uint64 `json:"tcp_reno_recovery"` + TCPSackRecovery uint64 `json:"tcp_sack_recovery"` + TCPSACKReneging uint64 `json:"tcp_sack_reneging"` + TCPFACKReorder uint64 `json:"tcp_fack_reorder"` + TCPSACKReorder uint64 `json:"tcp_sack_reorder"` + TCPRenoReorder uint64 `json:"tcp_reno_reorder"` + TCPTSReorder uint64 `json:"tcp_ts_reorder"` + TCPFullUndo uint64 `json:"tcp_full_undo"` + TCPPartialUndo uint64 `json:"tcp_partial_undo"` + TCPDSACKUndo uint64 `json:"tcp_dsack_undo"` + TCPLossUndo uint64 `json:"tcp_loss_undo"` + TCPLoss uint64 `json:"tcp_loss"` + TCPLostRetransmit uint64 `json:"tcp_lost_retransmit"` + TCPRenoFailures uint64 `json:"tcp_reno_failures"` + TCPSackFailures uint64 `json:"tcp_sack_failures"` + TCPLossFailures uint64 `json:"tcp_loss_failures"` + TCPFastRetrans uint64 `json:"tcp_fast_retrans"` + TCPForwardRetrans uint64 `json:"tcp_forward_retrans"` + TCPSlowStartRetrans uint64 `json:"tcp_slow_start_retrans"` + TCPTimeouts uint64 `json:"tcp_timeouts"` + TCPLossProbes uint64 `json:"tcp_loss_probes"` + TCPLossProbeRecovery uint64 `json:"tcp_loss_probe_recovery"` + TCPRenoRecoveryFail uint64 `json:"tcp_reno_recovery_fail"` + TCPSackRecoveryFail uint64 `json:"tcp_sack_recovery_fail"` + TCPSchedulerFailed uint64 `json:"tcp_scheduler_failed"` + TCPRcvCollapsed uint64 `json:"tcp_rcv_collapsed"` + TCPDSACKOldSent uint64 `json:"tcp_dsack_old_sent"` + TCPDSACKOfoSent uint64 `json:"tcp_dsack_ofo_sent"` + TCPDSACKRecv uint64 `json:"tcp_dsack_recv"` + TCPDSACKOfoRecv uint64 `json:"tcp_dsack_ofo_recv"` + TCPAbortOnSyn uint64 `json:"tcp_abort_on_syn"` + TCPAbortOnData uint64 `json:"tcp_abort_on_data"` + TCPAbortOnClose uint64 `json:"tcp_abort_on_close"` + TCPAbortOnMemory uint64 `json:"tcp_abort_on_memory"` + TCPAbortOnTimeout uint64 `json:"tcp_abort_on_timeout"` + TCPAbortOnLinger uint64 `json:"tcp_abort_on_linger"` + TCPAbortFailed uint64 `json:"tcp_abort_failed"` + TCPMemoryPressures uint64 `json:"tcp_memory_pressures"` + TCPSACKDiscard uint64 `json:"tcp_sack_discard"` + TCPDSACKIgnoredOld uint64 `json:"tcp_dsack_ignored_old"` + TCPDSACKIgnoredNoUndo uint64 `json:"tcp_dsack_ignored_no_undo"` + TCPSpuriousRTOs uint64 `json:"tcp_spurious_rtos"` + TCPMD5NotFound uint64 `json:"tcp_md5_not_found"` + TCPMD5Unexpected uint64 `json:"tcp_md5_unexpected"` + TCPSackShifted uint64 `json:"tcp_sack_shifted"` + TCPSackMerged uint64 `json:"tcp_sack_merged"` + TCPSackShiftFallback uint64 `json:"tcp_sack_shift_fallback"` + TCPBacklogDrop uint64 `json:"tcp_backlog_drop"` + TCPMinTTLDrop uint64 `json:"tcp_min_ttl_drop"` + TCPDeferAcceptDrop uint64 `json:"tcp_defer_accept_drop"` + IPReversePathFilter uint64 `json:"ip_reverse_path_filter"` + TCPTimeWaitOverflow uint64 `json:"tcp_time_wait_overflow"` + TCPReqQFullDoCookies uint64 `json:"tcp_req_q_full_do_cookies"` + TCPReqQFullDrop uint64 `json:"tcp_req_q_full_drop"` + TCPRetransFail uint64 `json:"tcp_retrans_fail"` + TCPRcvCoalesce uint64 `json:"tcp_rcv_coalesce"` + TCPOFOQueue uint64 `json:"tcp_ofo_drop"` + TCPOFODrop uint64 `json:"tcp_ofo_drop"` + TCPOFOMerge uint64 `json:"tcp_ofo_merge"` + TCPChallengeACK uint64 `json:"tcp_challenge_ack"` + TCPSYNChallenge uint64 `json:"tcp_syn_challenge"` + TCPFastOpenActive uint64 `json:"tcp_fast_open_active"` + TCPFastOpenActiveFail uint64 `json:"tcp_fast_open_active_fail"` + TCPFastOpenPassive uint64 `json:"tcp_fast_open_passive"` + TCPFastOpenPassiveFail uint64 `json:"tcp_fast_open_passive_fail"` + TCPFastOpenListenOverflow uint64 `json:"tcp_fast_open_listen_overflow"` + TCPFastOpenCookieReqd uint64 `json:"tcp_fast_open_cookie_reqd"` + TCPSpuriousRtxHostQueues uint64 `json:"tcp_spurious_rtx_host_queues"` + BusyPollRxPackets uint64 `json:"busy_poll_rx_packets"` + TCPAutoCorking uint64 `json:"tcp_auto_corking"` + TCPFromZeroWindowAdv uint64 `json:"tcp_from_zero_window_adv"` + TCPToZeroWindowAdv uint64 `json:"tcp_to_zero_window_adv"` + TCPWantZeroWindowAdv uint64 `json:"tcp_want_zero_window_adv"` + TCPSynRetrans uint64 `json:"tcp_syn_retrans"` + TCPOrigDataSent uint64 `json:"tcp_orig_data_sent"` + // IpExt + InNoRoutes uint64 `json:"in_no_routes"` + InTruncatedPkts uint64 `json:"in_truncated_pkts"` + InMcastPkts uint64 `json:"in_mcast_pkts"` + OutMcastPkts uint64 `json:"out_mcast_pkts"` + InBcastPkts uint64 `json:"in_bcast_pkts"` + OutBcastPkts uint64 `json:"out_bcast_pkts"` + InOctets uint64 `json:"in_octets"` + OutOctets uint64 `json:"out_octets"` + InMcastOctets uint64 `json:"in_mcast_octets"` + OutMcastOctets uint64 `json:"out_mcast_octets"` + InBcastOctets uint64 `json:"in_bcast_octets"` + OutBcastOctets uint64 `json:"out_bcast_octets"` + InCsumErrors uint64 `json:"in_csum_errors"` + InNoECTPkts uint64 `json:"in_no_ect_pkts"` + InECT1Pkts uint64 `json:"in_ect1_pkts"` + InECT0Pkts uint64 `json:"in_ect0_pkts"` + InCEPkts uint64 `json:"in_ce_pkts"` +} + +func ReadNetStat(path string) (*NetStat, error) { + data, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + lines := strings.Split(string(data), "\n") + + // Maps a netstat metric to its value (i.e. SyncookiesSent --> 0) + statMap := make(map[string]string) + + // patterns + // TcpExt: SyncookiesSent SyncookiesRecv SyncookiesFailed... <-- header + // TcpExt: 0 0 1764... <-- values + + for i := 1; i < len(lines); i = i + 2 { + headers := strings.Fields(lines[i-1][strings.Index(lines[i-1], ":")+1:]) + values := strings.Fields(lines[i][strings.Index(lines[i], ":")+1:]) + + for j, header := range headers { + statMap[header] = values[j] + } + } + + var netstat NetStat = NetStat{} + + elem := reflect.ValueOf(&netstat).Elem() + typeOfElem := elem.Type() + + for i := 0; i < elem.NumField(); i++ { + if val, ok := statMap[typeOfElem.Field(i).Name]; ok { + parsedVal, _ := strconv.ParseUint(val, 10, 64) + elem.Field(i).SetUint(parsedVal) + } + } + + return &netstat, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/netstat_test.go b/vendor/github.com/c9s/goprocinfo/linux/netstat_test.go new file mode 100644 index 0000000000..f304cb12ab --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/netstat_test.go @@ -0,0 +1,63 @@ +package linux + +import "testing" +import "reflect" +import "fmt" + +func TestNetStat(t *testing.T) { + { + var expected = NetStat{0, 0, 1764, 180, 0, 0, 0, 0, 0, 0, 28321, 0, 0, 0, 0, 243, 25089, 53, 837, 0, 0, 95994, 623148353, 640988091, 0, 92391, 81263, 594305, 590571, 35, 6501, 81, 113, 213, 1, 223, 318, 1056, 287, 218, 6619, 435, 1, 975, 264, 17298, 871, 5836, 3843, 0, 0, 2, 520, 0, 0, 833, 0, 3235, 44, 0, 571, 163, 0, 138, 0, 0, 0, 19, 1312, 677, 129, 0, 0, 27986, 27713, 40522, 837, 0, 38648, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2772402103, 5189844022, 0, 0, 0, 0, 0, 0, 0, 0, 0} + + read, err := ReadNetStat("proc/net_netstat_1") + if err != nil { + t.Fatal("netstat read fail", err) + } + + t.Logf("%+v", expected) + t.Logf("%+v", read) + + if err := compareExpectedReadFieldsNetStat(&expected, read); err != nil { + t.Error(err.Error()) + } + + if !reflect.DeepEqual(*read, expected) { + t.Error("not equal to expected") + } + } + { + expected := NetStat{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 427717, 0, 0, 0, 0, 370, 3111446, 59, 1825, 0, 0, 170176, 0, 507248, 0, 47385919, 0, 7377770, 29264396, 0, 410, 0, 3, 1, 0, 0, 4, 0, 18, 579, 0, 2, 0, 50, 6, 949, 50, 332, 1005, 5893, 978, 0, 8, 0, 0, 1838, 8, 764, 0, 0, 157868, 3281, 0, 35, 0, 0, 0, 0, 28, 453, 46, 0, 0, 226, 316, 2725, 0, 0, 0, 0, 0, 0, 0, 0, 11292855, 88470, 0, 8, 261, 198, 0, 0, 0, 0, 0, 0, 0, 0, 842446, 118, 118, 11490, 859, 105365136, 0, 0, 0, 0, 249, 0, 205328912480, 353370957921, 0, 0, 92394, 0, 0, 157218430, 0, 0, 0} + + read, err := ReadNetStat("proc/net_netstat_2") + if err != nil { + t.Fatal("netstat read fail", err) + } + + t.Logf("%+v", expected) + t.Logf("%+v", read) + + if err := compareExpectedReadFieldsNetStat(&expected, read); err != nil { + t.Error(err.Error()) + } + + if !reflect.DeepEqual(*read, expected) { + t.Error("not equal to expected") + } + } +} + +// This is a helper function which makes it easier to track down errors in expected versus read values. +func compareExpectedReadFieldsNetStat(expected *NetStat, read *NetStat) error { + elemExpected := reflect.ValueOf(*expected) + typeOfElemExpected := elemExpected.Type() + elemRead := reflect.ValueOf(*read) + + for i := 0; i < elemExpected.NumField(); i++ { + fieldName := typeOfElemExpected.Field(i).Name + + if elemExpected.Field(i).Uint() != elemRead.Field(i).Uint() { + return fmt.Errorf("Read value not equal to expected value for field %s. Got %d and expected %d.", fieldName, elemRead.Field(i).Uint(), elemExpected.Field(i).Uint()) + } + } + + return nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/network_stat.go b/vendor/github.com/c9s/goprocinfo/linux/network_stat.go new file mode 100644 index 0000000000..5909369577 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/network_stat.go @@ -0,0 +1,73 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" +) + +type NetworkStat struct { + Iface string `json:"iface"` + RxBytes uint64 `json:"rxbytes"` + RxPackets uint64 `json:"rxpackets"` + RxErrs uint64 `json:"rxerrs"` + RxDrop uint64 `json:"rxdrop"` + RxFifo uint64 `json:"rxfifo"` + RxFrame uint64 `json:"rxframe"` + RxCompressed uint64 `json:"rxcompressed"` + RxMulticast uint64 `json:"rxmulticast"` + TxBytes uint64 `json:"txbytes"` + TxPackets uint64 `json:"txpackets"` + TxErrs uint64 `json:"txerrs"` + TxDrop uint64 `json:"txdrop"` + TxFifo uint64 `json:"txfifo"` + TxColls uint64 `json:"txcolls"` + TxCarrier uint64 `json:"txcarrier"` + TxCompressed uint64 `json:"txcompressed"` +} + +func ReadNetworkStat(path string) ([]NetworkStat, error) { + data, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + lines := strings.Split(string(data), "\n") + + // lines[2:] remove /proc/net/dev header + results := make([]NetworkStat, len(lines[2:])-1) + + for i, line := range lines[2:] { + // patterns + // : 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // or + // :0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 (without space after colon) + colon := strings.Index(line, ":") + + if colon > 0 { + metrics := line[colon+1:] + fields := strings.Fields(metrics) + + results[i].Iface = strings.Replace(line[0:colon], " ", "", -1) + results[i].RxBytes, _ = strconv.ParseUint(fields[0], 10, 64) + results[i].RxPackets, _ = strconv.ParseUint(fields[1], 10, 64) + results[i].RxErrs, _ = strconv.ParseUint(fields[2], 10, 64) + results[i].RxDrop, _ = strconv.ParseUint(fields[3], 10, 64) + results[i].RxFifo, _ = strconv.ParseUint(fields[4], 10, 64) + results[i].RxFrame, _ = strconv.ParseUint(fields[5], 10, 64) + results[i].RxCompressed, _ = strconv.ParseUint(fields[6], 10, 64) + results[i].RxMulticast, _ = strconv.ParseUint(fields[7], 10, 64) + results[i].TxBytes, _ = strconv.ParseUint(fields[8], 10, 64) + results[i].TxPackets, _ = strconv.ParseUint(fields[9], 10, 64) + results[i].TxErrs, _ = strconv.ParseUint(fields[10], 10, 64) + results[i].TxDrop, _ = strconv.ParseUint(fields[11], 10, 64) + results[i].TxFifo, _ = strconv.ParseUint(fields[12], 10, 64) + results[i].TxColls, _ = strconv.ParseUint(fields[13], 10, 64) + results[i].TxCarrier, _ = strconv.ParseUint(fields[14], 10, 64) + results[i].TxCompressed, _ = strconv.ParseUint(fields[15], 10, 64) + } + } + + return results, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/network_stat_test.go b/vendor/github.com/c9s/goprocinfo/linux/network_stat_test.go new file mode 100644 index 0000000000..7566fcc40f --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/network_stat_test.go @@ -0,0 +1,41 @@ +package linux + +import "testing" +import "reflect" + +func TestNetworkStat(t *testing.T) { + + var expected = []NetworkStat{ + {"eth0", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {"lo", 870813, 8693, 0, 0, 0, 0, 0, 0, 870813, 8693, 0, 0, 0, 0, 0, 0}, + {"virbr0", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {"wlan0", 1163823097, 838432, 0, 0, 0, 0, 0, 0, 73047180, 641124, 0, 0, 0, 0, 0, 0}, + } + + networkStat, err := ReadNetworkStat("proc/net_dev") + if err != nil { + t.Fatal("network stat read fail", err) + } + + t.Logf("%+v", networkStat) + + if !reflect.DeepEqual(networkStat, expected) { + t.Error("not equal to expected") + } + + var squeezeexpected = []NetworkStat{ + {"lo", 480134461, 2323077, 0, 0, 0, 0, 0, 0, 480134461, 2323077, 0, 0, 0, 0, 0, 0}, + {"eth0", 23443246382, 63554887, 0, 0, 0, 0, 0, 0, 10900929232, 27373481, 0, 0, 0, 0, 0, 0}, + } + + networkStat, err = ReadNetworkStat("proc/net_dev_squeeze") + if err != nil { + t.Fatal("network stat read fail", err) + } + + t.Logf("%+v", networkStat) + + if !reflect.DeepEqual(networkStat, squeezeexpected) { + t.Error("not equal to expected") + } +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process.go b/vendor/github.com/c9s/goprocinfo/linux/process.go new file mode 100644 index 0000000000..4fd062b785 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process.go @@ -0,0 +1,62 @@ +package linux + +import ( + "os" + "path/filepath" + "strconv" +) + +type Process struct { + Status ProcessStatus `json:"status"` + Statm ProcessStatm `json:"statm"` + Stat ProcessStat `json:"stat"` + IO ProcessIO `json:"io"` + Cmdline string `json:"cmdline"` +} + +func ReadProcess(pid uint64, path string) (*Process, error) { + + var err error + + p := filepath.Join(path, strconv.FormatUint(pid, 10)) + + if _, err = os.Stat(p); err != nil { + return nil, err + } + + process := Process{} + + var io *ProcessIO + var stat *ProcessStat + var statm *ProcessStatm + var status *ProcessStatus + var cmdline string + + if io, err = ReadProcessIO(filepath.Join(p, "io")); err != nil { + return nil, err + } + + if stat, err = ReadProcessStat(filepath.Join(p, "stat")); err != nil { + return nil, err + } + + if statm, err = ReadProcessStatm(filepath.Join(p, "statm")); err != nil { + return nil, err + } + + if status, err = ReadProcessStatus(filepath.Join(p, "status")); err != nil { + return nil, err + } + + if cmdline, err = ReadProcessCmdline(filepath.Join(p, "cmdline")); err != nil { + return nil, err + } + + process.IO = *io + process.Stat = *stat + process.Statm = *statm + process.Status = *status + process.Cmdline = cmdline + + return &process, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_cmdline.go b/vendor/github.com/c9s/goprocinfo/linux/process_cmdline.go new file mode 100644 index 0000000000..4fc6afe77f --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_cmdline.go @@ -0,0 +1,39 @@ +package linux + +import ( + "io/ioutil" + "strings" +) + +func ReadProcessCmdline(path string) (string, error) { + + b, err := ioutil.ReadFile(path) + + if err != nil { + return "", err + } + + l := len(b) - 1 // Define limit before last byte ('\0') + z := byte(0) // '\0' or null byte + s := byte(0x20) // space byte + c := 0 // cursor of useful bytes + + for i := 0; i < l; i++ { + + // Check if next byte is not a '\0' byte. + if b[i+1] != z { + + // Offset must match a '\0' byte. + c = i + 2 + + // If current byte is '\0', replace it with a space byte. + if b[i] == z { + b[i] = s + } + } + } + + x := strings.TrimSpace(string(b[0:c])) + + return x, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_cmdline_test.go b/vendor/github.com/c9s/goprocinfo/linux/process_cmdline_test.go new file mode 100644 index 0000000000..db620d8399 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_cmdline_test.go @@ -0,0 +1,39 @@ +package linux + +import ( + "testing" +) + +func TestReadProcessCmdlineSimple(t *testing.T) { + + cmdline, err := ReadProcessCmdline("proc/3323/cmdline") + + if err != nil { + t.Fatal("process cmdline read fail", err) + } + + expected := "proftpd: (accepting connections)" + + if cmdline != expected { + t.Error("not equal to expected", expected) + } + + t.Logf("%+v", cmdline) +} + +func TestReadProcessCmdlineComplex(t *testing.T) { + + cmdline, err := ReadProcessCmdline("proc/5811/cmdline") + + if err != nil { + t.Fatal("process cmdline read fail", err) + } + + expected := "/home/c9s/.config/sublime-text-2/Packages/User/GoSublime/linux-x64/bin/gosublime.margo_r14.12.06-1_go1.4.2.exe -oom 1000 -poll 30 -tag r14.12.06-1" + + if cmdline != expected { + t.Error("not equal to expected", expected) + } + + t.Logf("%+v", cmdline) +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_io.go b/vendor/github.com/c9s/goprocinfo/linux/process_io.go new file mode 100644 index 0000000000..8106a1e449 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_io.go @@ -0,0 +1,71 @@ +package linux + +import ( + "io/ioutil" + "reflect" + "strconv" + "strings" +) + +// I/O statistics for the process. +type ProcessIO struct { + RChar uint64 `json:"rchar" field:"rchar"` // chars read + WChar uint64 `json:"wchar" field:"wchar"` // chars written + Syscr uint64 `json:"syscr" field:"syscr"` // read syscalls + Syscw uint64 `json:"syscw" field:"syscw"` // write syscalls + ReadBytes uint64 `json:"read_bytes" field:"read_bytes"` // bytes read + WriteBytes uint64 `json:"write_bytes" field:"write_bytes"` // bytes written + CancelledWriteBytes uint64 `json:"cancelled_write_bytes" field:"cancelled_write_bytes"` // bytes truncated +} + +func ReadProcessIO(path string) (*ProcessIO, error) { + + b, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + // Maps a io metric to its value (i.e. rchar --> 100000) + m := map[string]uint64{} + + io := ProcessIO{} + + lines := strings.Split(string(b), "\n") + + for _, line := range lines { + + if strings.Index(line, ": ") == -1 { + continue + } + + l := strings.Split(line, ": ") + + k := l[0] + v, err := strconv.ParseUint(l[1], 10, 64) + + if err != nil { + return nil, err + } + + m[k] = v + + } + + e := reflect.ValueOf(&io).Elem() + t := e.Type() + + for i := 0; i < e.NumField(); i++ { + + k := t.Field(i).Tag.Get("field") + + v, ok := m[k] + + if ok { + e.Field(i).SetUint(v) + } + + } + + return &io, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_io_test.go b/vendor/github.com/c9s/goprocinfo/linux/process_io_test.go new file mode 100644 index 0000000000..c9b8f07733 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_io_test.go @@ -0,0 +1,31 @@ +package linux + +import ( + "reflect" + "testing" +) + +func TestReadProcessIO(t *testing.T) { + + io, err := ReadProcessIO("proc/3323/io") + + if err != nil { + t.Fatal("process io read fail", err) + } + + expected := &ProcessIO{ + RChar: 3865585, + WChar: 183294, + Syscr: 6697, + Syscw: 997, + ReadBytes: 90112, + WriteBytes: 45056, + CancelledWriteBytes: 0, + } + + if !reflect.DeepEqual(io, expected) { + t.Error("not equal to expected", expected) + } + + t.Logf("%+v", io) +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_pid.go b/vendor/github.com/c9s/goprocinfo/linux/process_pid.go new file mode 100644 index 0000000000..8085d25f7c --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_pid.go @@ -0,0 +1,54 @@ +package linux + +import ( + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" +) + +func ReadMaxPID(path string) (uint64, error) { + + b, err := ioutil.ReadFile(path) + + if err != nil { + return 0, err + } + + s := strings.TrimSpace(string(b)) + + i, err := strconv.ParseUint(s, 10, 64) + + if err != nil { + return 0, err + } + + return i, nil + +} + +func ListPID(path string, max uint64) ([]uint64, error) { + + l := make([]uint64, 0, 5) + + for i := uint64(1); i <= max; i++ { + + p := filepath.Join(path, strconv.FormatUint(i, 10)) + + s, err := os.Stat(p) + + if err != nil && !os.IsNotExist(err) { + return nil, err + } + + if err != nil || !s.IsDir() { + continue + } + + l = append(l, i) + + } + + return l, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_pid_test.go b/vendor/github.com/c9s/goprocinfo/linux/process_pid_test.go new file mode 100644 index 0000000000..3ecdb98649 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_pid_test.go @@ -0,0 +1,38 @@ +package linux + +import ( + "reflect" + "testing" +) + +func TestMaxPID(t *testing.T) { + + max, err := ReadMaxPID("proc/sys_kernel_pid_max") + + if err != nil { + t.Fatal("max pid read fail", err) + } + + if max != 32768 { + t.Error("unexpected value") + } + + t.Logf("%+v", max) +} + +func TestListPID(t *testing.T) { + + list, err := ListPID("proc", 32768) + + if err != nil { + t.Fatal("list pid fail", err) + } + + var expected = []uint64{884, 3323, 4854, 5811} + + if !reflect.DeepEqual(list, expected) { + t.Error("not equal to expected", expected) + } + + t.Logf("%+v", list) +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_stat.go b/vendor/github.com/c9s/goprocinfo/linux/process_stat.go new file mode 100644 index 0000000000..5343cbdec3 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_stat.go @@ -0,0 +1,303 @@ +package linux + +import ( + "io/ioutil" + "regexp" + "strconv" + "strings" +) + +// Status information about the process. +type ProcessStat struct { + Pid uint64 `json:"pid"` + Comm string `json:"comm"` + State string `json:"state"` + Ppid int64 `json:"ppid"` + Pgrp int64 `json:"pgrp"` + Session int64 `json:"session"` + TtyNr int64 `json:"tty_nr"` + Tpgid int64 `json:"tpgid"` + Flags uint64 `json:"flags"` + Minflt uint64 `json:"minflt"` + Cminflt uint64 `json:"cminflt"` + Majflt uint64 `json:"majflt"` + Cmajflt uint64 `json:"cmajflt"` + Utime uint64 `json:"utime"` + Stime uint64 `json:"stime"` + Cutime int64 `json:"cutime"` + Cstime int64 `json:"cstime"` + Priority int64 `json:"priority"` + Nice int64 `json:"nice"` + NumThreads int64 `json:"num_threads"` + Itrealvalue int64 `json:"itrealvalue"` + Starttime uint64 `json:"starttime"` + Vsize uint64 `json:"vsize"` + Rss int64 `json:"rss"` + Rsslim uint64 `json:"rsslim"` + Startcode uint64 `json:"startcode"` + Endcode uint64 `json:"endcode"` + Startstack uint64 `json:"startstack"` + Kstkesp uint64 `json:"kstkesp"` + Kstkeip uint64 `json:"kstkeip"` + Signal uint64 `json:"signal"` + Blocked uint64 `json:"blocked"` + Sigignore uint64 `json:"sigignore"` + Sigcatch uint64 `json:"sigcatch"` + Wchan uint64 `json:"wchan"` + Nswap uint64 `json:"nswap"` + Cnswap uint64 `json:"cnswap"` + ExitSignal int64 `json:"exit_signal"` + Processor int64 `json:"processor"` + RtPriority uint64 `json:"rt_priority"` + Policy uint64 `json:"policy"` + DelayacctBlkioTicks uint64 `json:"delayacct_blkio_ticks"` + GuestTime uint64 `json:"guest_time"` + CguestTime int64 `json:"cguest_time"` + StartData uint64 `json:"start_data"` + EndData uint64 `json:"end_data"` + StartBrk uint64 `json:"start_brk"` + ArgStart uint64 `json:"arg_start"` + ArgEnd uint64 `json:"arg_end"` + EnvStart uint64 `json:"env_start"` + EnvEnd uint64 `json:"env_end"` + ExitCode int64 `json:"exit_code"` +} + +var processStatRegExp = regexp.MustCompile("^(\\d+)( \\(.*?\\) )(.*)$") + +func ReadProcessStat(path string) (*ProcessStat, error) { + + b, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + s := string(b) + + f := make([]string, 0, 32) + + e := processStatRegExp.FindStringSubmatch(strings.TrimSpace(s)) + + // Inject process Pid + f = append(f, e[1]) + + // Inject process Comm + f = append(f, strings.TrimSpace(e[2])) + + // Inject all remaining process info + f = append(f, (strings.Fields(e[3]))...) + + stat := ProcessStat{} + + for i := 0; i < len(f); i++ { + switch i { + case 0: + if stat.Pid, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 1: + stat.Comm = f[i] + case 2: + stat.State = f[i] + case 3: + if stat.Ppid, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 4: + if stat.Pgrp, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 5: + if stat.Session, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 6: + if stat.TtyNr, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 7: + if stat.Tpgid, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 8: + if stat.Flags, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 9: + if stat.Minflt, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 10: + if stat.Cminflt, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 11: + if stat.Majflt, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 12: + if stat.Cmajflt, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 13: + if stat.Utime, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 14: + if stat.Stime, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 15: + if stat.Cutime, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 16: + if stat.Cstime, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 17: + if stat.Priority, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 18: + if stat.Nice, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 19: + if stat.NumThreads, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 20: + if stat.Itrealvalue, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 21: + if stat.Starttime, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 22: + if stat.Vsize, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 23: + if stat.Rss, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 24: + if stat.Rsslim, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 25: + if stat.Startcode, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 26: + if stat.Endcode, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 27: + if stat.Startstack, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 28: + if stat.Kstkesp, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 29: + if stat.Kstkeip, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 30: + if stat.Signal, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 31: + if stat.Blocked, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 32: + if stat.Sigignore, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 33: + if stat.Sigcatch, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 34: + if stat.Wchan, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 35: + if stat.Nswap, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 36: + if stat.Cnswap, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 37: + if stat.ExitSignal, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 38: + if stat.Processor, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 39: + if stat.RtPriority, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 40: + if stat.Policy, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 41: + if stat.DelayacctBlkioTicks, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 42: + if stat.GuestTime, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 43: + if stat.CguestTime, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + case 44: + if stat.StartData, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 45: + if stat.EndData, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 46: + if stat.StartBrk, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 47: + if stat.ArgStart, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 48: + if stat.ArgEnd, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 49: + if stat.EnvStart, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 50: + if stat.EnvEnd, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + case 51: + if stat.ExitCode, err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + } + } + + return &stat, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_stat_test.go b/vendor/github.com/c9s/goprocinfo/linux/process_stat_test.go new file mode 100644 index 0000000000..d654707ec0 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_stat_test.go @@ -0,0 +1,193 @@ +package linux + +import ( + "reflect" + "testing" +) + +func TestReadProcessStat(t *testing.T) { + + stat, err := ReadProcessStat("proc/3323/stat") + + if err != nil { + t.Fatal("process stat read fail", err) + } + + expected := &ProcessStat{ + Pid: 3323, + Comm: "(proftpd)", + State: "S", + Ppid: 1, + Pgrp: 3323, + Session: 3323, + TtyNr: 0, + Tpgid: -1, + Flags: 4202816, + Minflt: 1311, + Cminflt: 57367, + Majflt: 0, + Cmajflt: 1, + Utime: 23, + Stime: 58, + Cutime: 24, + Cstime: 49, + Priority: 20, + Nice: 0, + NumThreads: 1, + Itrealvalue: 0, + Starttime: 2789, + Vsize: 16601088, + Rss: 522, + Rsslim: 4294967295, + Startcode: 134512640, + Endcode: 135222176, + Startstack: 3217552592, + Kstkesp: 3217551836, + Kstkeip: 4118799382, + Signal: 0, + Blocked: 0, + Sigignore: 272633856, + Sigcatch: 8514799, + Wchan: 0, + Nswap: 0, + Cnswap: 0, + ExitSignal: 17, + Processor: 7, + RtPriority: 0, + Policy: 0, + DelayacctBlkioTicks: 1, + GuestTime: 0, + CguestTime: 0, + } + + if !reflect.DeepEqual(stat, expected) { + t.Errorf("not equal to expected %+v", expected) + } + + t.Logf("%+v", stat) +} + +func TestReadProcessStatWithSpace(t *testing.T) { + + stat, err := ReadProcessStat("proc/884/stat") + + if err != nil { + t.Fatal("process stat read fail", err) + } + + expected := &ProcessStat{ + Pid: 884, + Comm: "(rs:main Q:Reg)", + State: "S", + Ppid: 1, + Pgrp: 873, + Session: 873, + TtyNr: 0, + Tpgid: -1, + Flags: 4202816, + Minflt: 561, + Cminflt: 0, + Majflt: 0, + Cmajflt: 0, + Utime: 68, + Stime: 132, + Cutime: 0, + Cstime: 0, + Priority: 20, + Nice: 0, + NumThreads: 4, + Itrealvalue: 0, + Starttime: 2161, + Vsize: 255451136, + Rss: 409, + Rsslim: 18446744073709551615, + Startcode: 1, + Endcode: 1, + Startstack: 0, + Kstkesp: 0, + Kstkeip: 0, + Signal: 0, + Blocked: 2146172671, + Sigignore: 16781830, + Sigcatch: 1133601, + Wchan: 18446744073709551615, + Nswap: 0, + Cnswap: 0, + ExitSignal: -1, + Processor: 1, + RtPriority: 0, + Policy: 0, + DelayacctBlkioTicks: 34, + GuestTime: 0, + CguestTime: 0, + } + + if !reflect.DeepEqual(stat, expected) { + t.Errorf("not equal to expected %+v", expected) + } + + t.Logf("%+v", stat) +} + +func TestReadProcessStatWithDoubleParentheses(t *testing.T) { + + stat, err := ReadProcessStat("proc/4854/stat") + + if err != nil { + t.Fatal("process stat read fail", err) + } + + expected := &ProcessStat{ + Pid: 4854, + Comm: "((sd-pam))", + State: "S", + Ppid: 4853, + Pgrp: 4853, + Session: 4853, + TtyNr: 0, + Tpgid: -1, + Flags: 1077944640, + Minflt: 21, + Cminflt: 0, + Majflt: 0, + Cmajflt: 0, + Utime: 0, + Stime: 0, + Cutime: 0, + Cstime: 0, + Priority: 20, + Nice: 0, + NumThreads: 1, + Itrealvalue: 0, + Starttime: 4912, + Vsize: 83931136, + Rss: 405, + Rsslim: 18446744073709551615, + Startcode: 1, + Endcode: 1, + Startstack: 0, + Kstkesp: 0, + Kstkeip: 0, + Signal: 0, + Blocked: 0, + Sigignore: 4096, + Sigcatch: 0, + Wchan: 18446744073709551615, + Nswap: 0, + Cnswap: 0, + ExitSignal: 17, + Processor: 7, + RtPriority: 0, + Policy: 0, + DelayacctBlkioTicks: 0, + GuestTime: 0, + CguestTime: 0, + } + + if !reflect.DeepEqual(stat, expected) { + t.Errorf("not equal to expected %+v", expected) + } + + t.Logf("%+v", stat) + +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_statm.go b/vendor/github.com/c9s/goprocinfo/linux/process_statm.go new file mode 100644 index 0000000000..8720cdf121 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_statm.go @@ -0,0 +1,61 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" +) + +// Provides information about memory usage, measured in pages. +type ProcessStatm struct { + Size uint64 `json:"size"` // total program size + Resident uint64 `json:"resident"` // resident set size + Share uint64 `json:"share"` // shared pages + Text uint64 `json:"text"` // text (code) + Lib uint64 `json:"lib"` // library (unused in Linux 2.6) + Data uint64 `json:"data"` // data + stack + Dirty uint64 `json:"dirty"` // dirty pages (unused in Linux 2.6) +} + +func ReadProcessStatm(path string) (*ProcessStatm, error) { + + b, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + s := string(b) + f := strings.Fields(s) + + statm := ProcessStatm{} + + var n uint64 + + for i := 0; i < len(f); i++ { + + if n, err = strconv.ParseUint(f[i], 10, 64); err != nil { + return nil, err + } + + switch i { + case 0: + statm.Size = n + case 1: + statm.Resident = n + case 2: + statm.Share = n + case 3: + statm.Text = n + case 4: + statm.Lib = n + case 5: + statm.Data = n + case 6: + statm.Dirty = n + } + + } + + return &statm, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_statm_test.go b/vendor/github.com/c9s/goprocinfo/linux/process_statm_test.go new file mode 100644 index 0000000000..9da2181a6e --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_statm_test.go @@ -0,0 +1,31 @@ +package linux + +import ( + "reflect" + "testing" +) + +func TestReadProcessStatm(t *testing.T) { + + statm, err := ReadProcessStatm("proc/3323/statm") + + if err != nil { + t.Fatal("process statm read fail", err) + } + + expected := &ProcessStatm{ + Size: 4053, + Resident: 522, + Share: 174, + Text: 174, + Lib: 0, + Data: 286, + Dirty: 0, + } + + if !reflect.DeepEqual(statm, expected) { + t.Error("not equal to expected", expected) + } + + t.Logf("%+v", statm) +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_status.go b/vendor/github.com/c9s/goprocinfo/linux/process_status.go new file mode 100644 index 0000000000..8441806bae --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_status.go @@ -0,0 +1,331 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" +) + +// Provides much of the information from ProcessStatm and ProcessStat +type ProcessStatus struct { + Name string + State string + Tgid uint64 + Pid uint64 + PPid int64 + TracerPid uint64 + RealUid uint64 + EffectiveUid uint64 + SavedSetUid uint64 + FilesystemUid uint64 + RealGid uint64 + EffectiveGid uint64 + SavedSetGid uint64 + FilesystemGid uint64 + FDSize uint64 + Groups []int64 + VmPeak uint64 + VmSize uint64 + VmLck uint64 + VmHWM uint64 + VmRSS uint64 + VmData uint64 + VmStk uint64 + VmExe uint64 + VmLib uint64 + VmPTE uint64 + VmSwap uint64 + Threads uint64 + SigQLength uint64 + SigQLimit uint64 + SigPnd uint64 + ShdPnd uint64 + SigBlk uint64 + SigIgn uint64 + SigCgt uint64 + CapInh uint64 + CapPrm uint64 + CapEff uint64 + CapBnd uint64 + Seccomp uint8 + CpusAllowed []uint32 + MemsAllowed []uint32 + VoluntaryCtxtSwitches uint64 + NonvoluntaryCtxtSwitches uint64 +} + +func ReadProcessStatus(path string) (*ProcessStatus, error) { + + b, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + status := ProcessStatus{} + + lines := strings.Split(string(b), "\n") + + for _, line := range lines { + + if strings.Index(line, ":") == -1 { + continue + } + + l := strings.Split(line, ":") + + k := strings.TrimSpace(l[0]) + v := strings.TrimSpace(l[1]) + + switch k { + case "Name": + status.Name = v + case "State": + status.State = v + case "Tgid": + if status.Tgid, err = strconv.ParseUint(v, 10, 64); err != nil { + return nil, err + } + case "Pid": + if status.Pid, err = strconv.ParseUint(v, 10, 64); err != nil { + return nil, err + } + case "PPid": + if status.PPid, err = strconv.ParseInt(v, 10, 64); err != nil { + return nil, err + } + case "TracerPid": + if status.TracerPid, err = strconv.ParseUint(v, 10, 64); err != nil { + return nil, err + } + case "Uid": + if f := strings.Fields(v); len(f) == 4 { + if status.RealUid, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + if status.EffectiveUid, err = strconv.ParseUint(f[1], 10, 64); err != nil { + return nil, err + } + if status.SavedSetUid, err = strconv.ParseUint(f[2], 10, 64); err != nil { + return nil, err + } + if status.FilesystemUid, err = strconv.ParseUint(f[3], 10, 64); err != nil { + return nil, err + } + } + case "Gid": + if f := strings.Fields(v); len(f) == 4 { + if status.RealGid, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + if status.EffectiveGid, err = strconv.ParseUint(f[1], 10, 64); err != nil { + return nil, err + } + if status.SavedSetGid, err = strconv.ParseUint(f[2], 10, 64); err != nil { + return nil, err + } + if status.FilesystemGid, err = strconv.ParseUint(f[3], 10, 64); err != nil { + return nil, err + } + } + case "FDSize": + if status.FDSize, err = strconv.ParseUint(v, 10, 64); err != nil { + return nil, err + } + case "Groups": + { + + f := strings.Fields(v) + status.Groups = make([]int64, len(f)) + + for i := range status.Groups { + if status.Groups[i], err = strconv.ParseInt(f[i], 10, 64); err != nil { + return nil, err + } + } + + } + case "VmPeak": + { + f := strings.Fields(v) + if status.VmPeak, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmSize": + { + f := strings.Fields(v) + if status.VmSize, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmLck": + { + f := strings.Fields(v) + if status.VmLck, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmHWM": + { + f := strings.Fields(v) + if status.VmHWM, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmRSS": + { + f := strings.Fields(v) + if status.VmRSS, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmData": + { + f := strings.Fields(v) + if status.VmData, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmStk": + { + f := strings.Fields(v) + if status.VmStk, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmExe": + { + f := strings.Fields(v) + if status.VmExe, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmLib": + { + f := strings.Fields(v) + if status.VmLib, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmPTE": + { + f := strings.Fields(v) + if status.VmPTE, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "VmSwap": + { + f := strings.Fields(v) + if status.VmSwap, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + } + case "Threads": + if status.Threads, err = strconv.ParseUint(v, 10, 64); err != nil { + return nil, err + } + case "SigQ": + { + if f := strings.Split(v, "/"); len(f) == 2 { + if status.SigQLength, err = strconv.ParseUint(f[0], 10, 64); err != nil { + return nil, err + } + if status.SigQLimit, err = strconv.ParseUint(f[1], 10, 64); err != nil { + return nil, err + } + } + } + case "SigPnd": + if status.SigPnd, err = strconv.ParseUint(v, 16, 64); err != nil { + return nil, err + } + case "ShdPnd": + if status.ShdPnd, err = strconv.ParseUint(v, 16, 64); err != nil { + return nil, err + } + case "SigBlk": + if status.SigBlk, err = strconv.ParseUint(v, 16, 64); err != nil { + return nil, err + } + case "SigIgn": + if status.SigIgn, err = strconv.ParseUint(v, 16, 64); err != nil { + return nil, err + } + case "SigCgt": + if status.SigCgt, err = strconv.ParseUint(v, 16, 64); err != nil { + return nil, err + } + case "CapInh": + if status.CapInh, err = strconv.ParseUint(v, 16, 64); err != nil { + return nil, err + } + case "CapPrm": + if status.CapPrm, err = strconv.ParseUint(v, 16, 64); err != nil { + return nil, err + } + case "CapEff": + if status.CapEff, err = strconv.ParseUint(v, 16, 64); err != nil { + return nil, err + } + case "CapBnd": + if status.CapBnd, err = strconv.ParseUint(v, 16, 64); err != nil { + return nil, err + } + case "Seccomp": + { + + var n uint64 + + if n, err = strconv.ParseUint(v, 10, 8); err != nil { + return nil, err + } + + status.Seccomp = uint8(n) + } + case "Cpus_allowed": + { + + var n uint64 + + f := strings.Split(v, ",") + status.CpusAllowed = make([]uint32, len(f)) + + for i := range status.CpusAllowed { + if n, err = strconv.ParseUint(f[i], 16, 32); err != nil { + return nil, err + } + status.CpusAllowed[i] = uint32(n) + } + + } + case "Mems_allowed": + { + + var n uint64 + + f := strings.Split(v, ",") + status.MemsAllowed = make([]uint32, len(f)) + + for i := range status.MemsAllowed { + if n, err = strconv.ParseUint(f[i], 16, 32); err != nil { + return nil, err + } + status.MemsAllowed[i] = uint32(n) + } + + } + case "voluntary_ctxt_switches": + if status.VoluntaryCtxtSwitches, err = strconv.ParseUint(v, 10, 64); err != nil { + return nil, err + } + case "nonvoluntary_ctxt_switches": + if status.NonvoluntaryCtxtSwitches, err = strconv.ParseUint(v, 10, 64); err != nil { + return nil, err + } + } + } + + return &status, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_status_test.go b/vendor/github.com/c9s/goprocinfo/linux/process_status_test.go new file mode 100644 index 0000000000..9ba6cc3a9f --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_status_test.go @@ -0,0 +1,68 @@ +package linux + +import ( + "reflect" + "testing" +) + +func TestReadProcessStatus(t *testing.T) { + + status, err := ReadProcessStatus("proc/3323/status") + + if err != nil { + t.Fatal("process io read fail", err) + } + + expected := &ProcessStatus{ + Name: "proftpd", + State: "S (sleeping)", + Tgid: 3323, + Pid: 3323, + PPid: 1, + TracerPid: 0, + RealUid: 0, + EffectiveUid: 111, + SavedSetUid: 0, + FilesystemUid: 111, + RealGid: 65534, + EffectiveGid: 65534, + SavedSetGid: 65534, + FilesystemGid: 65534, + FDSize: 32, + Groups: []int64{2001, 65534}, + VmPeak: 16216, + VmSize: 16212, + VmLck: 0, + VmHWM: 2092, + VmRSS: 2088, + VmData: 872, + VmStk: 272, + VmExe: 696, + VmLib: 9416, + VmPTE: 36, + VmSwap: 0, + Threads: 1, + SigQLength: 0, + SigQLimit: 12091, + SigPnd: 0, + ShdPnd: 0, + SigBlk: 0, + SigIgn: 272633856, + SigCgt: 6450965743, + CapInh: 0, + CapPrm: 18446744073709551615, + CapEff: 0, + CapBnd: 18446744073709551615, + Seccomp: 0, + CpusAllowed: []uint32{255}, + VoluntaryCtxtSwitches: 5899, + NonvoluntaryCtxtSwitches: 26, + } + + if !reflect.DeepEqual(status, expected) { + t.Error("not equal to expected", expected) + } + + t.Logf("%+v", status) + +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/process_test.go b/vendor/github.com/c9s/goprocinfo/linux/process_test.go new file mode 100644 index 0000000000..55cfbaa119 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/process_test.go @@ -0,0 +1,134 @@ +package linux + +import ( + "reflect" + "testing" +) + +func TestReadProcess(t *testing.T) { + + p, err := ReadProcess(3323, "proc") + + if err != nil { + t.Fatal("process read fail", err) + } + + expected := &Process{ + Status: ProcessStatus{ + Name: "proftpd", + State: "S (sleeping)", + Tgid: 3323, + Pid: 3323, + PPid: 1, + TracerPid: 0, + RealUid: 0, + EffectiveUid: 111, + SavedSetUid: 0, + FilesystemUid: 111, + RealGid: 65534, + EffectiveGid: 65534, + SavedSetGid: 65534, + FilesystemGid: 65534, + FDSize: 32, + Groups: []int64{2001, 65534}, + VmPeak: 16216, + VmSize: 16212, + VmLck: 0, + VmHWM: 2092, + VmRSS: 2088, + VmData: 872, + VmStk: 272, + VmExe: 696, + VmLib: 9416, + VmPTE: 36, + VmSwap: 0, + Threads: 1, + SigQLength: 0, + SigQLimit: 12091, + SigPnd: 0, + ShdPnd: 0, + SigBlk: 0, + SigIgn: 272633856, + SigCgt: 6450965743, + CapInh: 0, + CapPrm: 18446744073709551615, + CapEff: 0, + CapBnd: 18446744073709551615, + Seccomp: 0, + CpusAllowed: []uint32{255}, + VoluntaryCtxtSwitches: 5899, + NonvoluntaryCtxtSwitches: 26, + }, + Statm: ProcessStatm{ + Size: 4053, + Resident: 522, + Share: 174, + Text: 174, + Lib: 0, + Data: 286, + Dirty: 0, + }, + Stat: ProcessStat{ + Pid: 3323, + Comm: "(proftpd)", + State: "S", + Ppid: 1, + Pgrp: 3323, + Session: 3323, + TtyNr: 0, + Tpgid: -1, + Flags: 4202816, + Minflt: 1311, + Cminflt: 57367, + Majflt: 0, + Cmajflt: 1, + Utime: 23, + Stime: 58, + Cutime: 24, + Cstime: 49, + Priority: 20, + Nice: 0, + NumThreads: 1, + Itrealvalue: 0, + Starttime: 2789, + Vsize: 16601088, + Rss: 522, + Rsslim: 4294967295, + Startcode: 134512640, + Endcode: 135222176, + Startstack: 3217552592, + Kstkesp: 3217551836, + Kstkeip: 4118799382, + Signal: 0, + Blocked: 0, + Sigignore: 272633856, + Sigcatch: 8514799, + Wchan: 0, + Nswap: 0, + Cnswap: 0, + ExitSignal: 17, + Processor: 7, + RtPriority: 0, + Policy: 0, + DelayacctBlkioTicks: 1, + GuestTime: 0, + CguestTime: 0, + }, + IO: ProcessIO{ + RChar: 3865585, + WChar: 183294, + Syscr: 6697, + Syscw: 997, + ReadBytes: 90112, + WriteBytes: 45056, + CancelledWriteBytes: 0, + }, + Cmdline: "proftpd: (accepting connections)", + } + + if !reflect.DeepEqual(p, expected) { + t.Error("not equal to expected", expected) + } + + t.Logf("%+v", p) +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/sockstat.go b/vendor/github.com/c9s/goprocinfo/linux/sockstat.go new file mode 100644 index 0000000000..f8a2c5da0f --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/sockstat.go @@ -0,0 +1,82 @@ +package linux + +import ( + "io/ioutil" + "reflect" + "strconv" + "strings" +) + +type SockStat struct { + // sockets: + SocketsUsed uint64 `json:"sockets_used" field:"sockets.used"` + + // TCP: + TCPInUse uint64 `json:"tcp_in_use" field:"TCP.inuse"` + TCPOrphan uint64 `json:"tcp_orphan" field:"TCP.orphan"` + TCPTimeWait uint64 `json:"tcp_time_wait" field:"TCP.tw"` + TCPAllocated uint64 `json:"tcp_allocated" field:"TCP.alloc"` + TCPMemory uint64 `json:"tcp_memory" field:"TCP.mem"` + + // UDP: + UDPInUse uint64 `json:"udp_in_use" field:"UDP.inuse"` + UDPMemory uint64 `json:"udp_memory" field:"UDP.mem"` + + // UDPLITE: + UDPLITEInUse uint64 `json:"udplite_in_use" field:"UDPLITE.inuse"` + + // RAW: + RAWInUse uint64 `json:"raw_in_use" field:"RAW.inuse"` + + // FRAG: + FRAGInUse uint64 `json:"frag_in_use" field:"FRAG.inuse"` + FRAGMemory uint64 `json:"frag_memory" field:"FRAG.memory"` +} + +func ReadSockStat(path string) (*SockStat, error) { + data, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + lines := strings.Split(string(data), "\n") + + // Maps a meminfo metric to its value (i.e. MemTotal --> 100000) + statMap := map[string]uint64{} + + var sockStat SockStat = SockStat{} + + for _, line := range lines { + if strings.Index(line, ":") == -1 { + continue + } + + statType := line[0:strings.Index(line, ":")] + "." + + // The fields have this pattern: inuse 27 orphan 1 tw 23 alloc 31 mem 3 + // The stats are grouped into pairs and need to be parsed and placed into the stat map. + key := "" + for k, v := range strings.Fields(line[strings.Index(line, ":")+1:]) { + // Every second field is a value. + if (k+1)%2 != 0 { + key = v + continue + } + val, _ := strconv.ParseUint(v, 10, 64) + statMap[statType+key] = val + } + } + + elem := reflect.ValueOf(&sockStat).Elem() + typeOfElem := elem.Type() + + for i := 0; i < elem.NumField(); i++ { + val, ok := statMap[typeOfElem.Field(i).Tag.Get("field")] + if ok { + elem.Field(i).SetUint(val) + } + } + + return &sockStat, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/sockstat_test.go b/vendor/github.com/c9s/goprocinfo/linux/sockstat_test.go new file mode 100644 index 0000000000..663309aae4 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/sockstat_test.go @@ -0,0 +1,19 @@ +package linux + +import "testing" +import "reflect" + +func TestSockStat(t *testing.T) { + var expected = SockStat{231, 27, 1, 23, 31, 3, 19, 17, 0, 0, 0, 0} + + sockStat, err := ReadSockStat("proc/sockstat") + if err != nil { + t.Fatal("sockstat read fail", err) + } + + t.Logf("%+v", sockStat) + + if !reflect.DeepEqual(*sockStat, expected) { + t.Error("not equal to expected") + } +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/stat.go b/vendor/github.com/c9s/goprocinfo/linux/stat.go new file mode 100644 index 0000000000..8c921c38be --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/stat.go @@ -0,0 +1,106 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" + "time" +) + +type Stat struct { + CPUStatAll CPUStat `json:"cpu_all"` + CPUStats []CPUStat `json:"cpus"` + Interrupts uint64 `json:"intr"` + ContextSwitches uint64 `json:"ctxt"` + BootTime time.Time `json:"btime"` + Processes uint64 `json:"processes"` + ProcsRunning uint64 `json:"procs_running"` + ProcsBlocked uint64 `json:"procs_blocked"` +} + +type CPUStat struct { + Id string `json:"id"` + User uint64 `json:"user"` + Nice uint64 `json:"nice"` + System uint64 `json:"system"` + Idle uint64 `json:"idle"` + IOWait uint64 `json:"iowait"` + IRQ uint64 `json:"irq"` + SoftIRQ uint64 `json:"softirq"` + Steal uint64 `json:"steal"` + Guest uint64 `json:"guest"` + GuestNice uint64 `json:"guest_nice"` +} + +func createCPUStat(fields []string) *CPUStat { + s := CPUStat{} + s.Id = fields[0] + + for i := 1; i < len(fields); i++ { + v, _ := strconv.ParseUint(fields[i], 10, 64) + switch i { + case 1: + s.User = v + case 2: + s.Nice = v + case 3: + s.System = v + case 4: + s.Idle = v + case 5: + s.IOWait = v + case 6: + s.IRQ = v + case 7: + s.SoftIRQ = v + case 8: + s.Steal = v + case 9: + s.Guest = v + case 10: + s.GuestNice = v + } + } + return &s +} + +func ReadStat(path string) (*Stat, error) { + b, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + content := string(b) + lines := strings.Split(content, "\n") + + var stat Stat = Stat{} + + for i, line := range lines { + fields := strings.Fields(line) + if len(fields) == 0 { + continue + } + if fields[0][:3] == "cpu" { + if cpuStat := createCPUStat(fields); cpuStat != nil { + if i == 0 { + stat.CPUStatAll = *cpuStat + } else { + stat.CPUStats = append(stat.CPUStats, *cpuStat) + } + } + } else if fields[0] == "intr" { + stat.Interrupts, _ = strconv.ParseUint(fields[1], 10, 64) + } else if fields[0] == "ctxt" { + stat.ContextSwitches, _ = strconv.ParseUint(fields[1], 10, 64) + } else if fields[0] == "btime" { + seconds, _ := strconv.ParseInt(fields[1], 10, 64) + stat.BootTime = time.Unix(seconds, 0) + } else if fields[0] == "processes" { + stat.Processes, _ = strconv.ParseUint(fields[1], 10, 64) + } else if fields[0] == "procs_running" { + stat.ProcsRunning, _ = strconv.ParseUint(fields[1], 10, 64) + } else if fields[0] == "procs_blocked" { + stat.ProcsBlocked, _ = strconv.ParseUint(fields[1], 10, 64) + } + } + return &stat, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/stat_test.go b/vendor/github.com/c9s/goprocinfo/linux/stat_test.go new file mode 100644 index 0000000000..ccd40d9d72 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/stat_test.go @@ -0,0 +1,12 @@ +package linux + +import "testing" + +func TestCPUStat(t *testing.T) { + stat, err := ReadStat("proc/stat") + if err != nil { + t.Fatal("stat read fail") + } + _ = stat + t.Logf("%+v", stat) +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/uptime.go b/vendor/github.com/c9s/goprocinfo/linux/uptime.go new file mode 100644 index 0000000000..393076c41b --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/uptime.go @@ -0,0 +1,43 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" + "time" +) + +type Uptime struct { + Total float64 `json:"total"` + Idle float64 `json:"idle"` +} + +func (self *Uptime) GetTotalDuration() time.Duration { + return time.Duration(self.Total) * time.Second +} + +func (self *Uptime) GetIdleDuration() time.Duration { + return time.Duration(self.Idle) * time.Second +} + +func (self *Uptime) CalculateIdle() float64 { + // XXX + // num2/(num1*N) # N = SMP CPU numbers + return 0 +} + +func ReadUptime(path string) (*Uptime, error) { + b, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + fields := strings.Fields(string(b)) + uptime := Uptime{} + if uptime.Total, err = strconv.ParseFloat(fields[0], 64); err != nil { + return nil, err + } + if uptime.Idle, err = strconv.ParseFloat(fields[1], 64); err != nil { + return nil, err + } + return &uptime, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/uptime_test.go b/vendor/github.com/c9s/goprocinfo/linux/uptime_test.go new file mode 100644 index 0000000000..d123b1488f --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/uptime_test.go @@ -0,0 +1,22 @@ +package linux + +import "testing" + +func TestUptime(t *testing.T) { + + uptime, err := ReadUptime("proc/uptime") + if err != nil { + t.Fatal(err) + } + if uptime.Total == 0 { + t.Fatal("uptime total read fail") + } + if uptime.Idle == 0 { + t.Fatal("uptime idel read fail") + } + + t.Logf("Total: %+v", uptime.GetTotalDuration()) + t.Logf("Idle: %+v", uptime.GetIdleDuration()) + + t.Logf("%+v", uptime) +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/vmstat.go b/vendor/github.com/c9s/goprocinfo/linux/vmstat.go new file mode 100644 index 0000000000..91d5a716a4 --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/vmstat.go @@ -0,0 +1,373 @@ +package linux + +import ( + "io/ioutil" + "strconv" + "strings" +) + +type VMStat struct { + NrFreePages uint64 `json:"nr_free_pages"` + NrAllocBatch uint64 `json:"nr_alloc_batch"` + NrInactiveAnon uint64 `json:"nr_inactive_anon"` + NrActiveAnon uint64 `json:"nr_active_anon"` + NrInactiveFile uint64 `json:"nr_inactive_file"` + NrActiveFile uint64 `json:"nr_active_file"` + NrUnevictable uint64 `json:"nr_unevictable"` + NrMlock uint64 `json:"nr_mlock"` + NrAnonPages uint64 `json:"nr_anon_pages"` + NrMapped uint64 `json:"nr_mapped"` + NrFilePages uint64 `json:"nr_file_pages"` + NrDirty uint64 `json:"nr_dirty"` + NrWriteback uint64 `json:"nr_writeback"` + NrSlabReclaimable uint64 `json:"nr_slab_reclaimable"` + NrSlabUnreclaimable uint64 `json:"nr_slab_unreclaimable"` + NrPageTablePages uint64 `json:"nr_page_table_pages"` + NrKernelStack uint64 `json:"nr_kernel_stack"` + NrUnstable uint64 `json:"nr_unstable"` + NrBounce uint64 `json:"nr_bounce"` + NrVmscanWrite uint64 `json:"nr_vmscan_write"` + NrVmscanImmediateReclaim uint64 `json:"nr_vmscan_immediate_reclaim"` + NrWritebackTemp uint64 `json:"nr_writeback_temp"` + NrIsolatedAnon uint64 `json:"nr_isolated_anon"` + NrIsolatedFile uint64 `json:"nr_isolated_file"` + NrShmem uint64 `json:"nr_shmem"` + NrDirtied uint64 `json:"nr_dirtied"` + NrWritten uint64 `json:"nr_written"` + NumaHit uint64 `json:"numa_hit"` + NumaMiss uint64 `json:"numa_miss"` + NumaForeign uint64 `json:"numa_foreign"` + NumaInterleave uint64 `json:"numa_interleave"` + NumaLocal uint64 `json:"numa_local"` + NumaOther uint64 `json:"numa_other"` + WorkingsetRefault uint64 `json:"workingset_refault"` + WorkingsetActivate uint64 `json:"workingset_activate"` + WorkingsetNodereclaim uint64 `json:"workingset_nodereclaim"` + NrAnonTransparentHugepages uint64 `json:"nr_anon_transparent_hugepages"` + NrFreeCma uint64 `json:"nr_free_cma"` + NrDirtyThreshold uint64 `json:"nr_dirty_threshold"` + NrDirtyBackgroundThreshold uint64 `json:"nr_dirty_background_threshold"` + PagePagein uint64 `json:"pgpgin"` + PagePageout uint64 `json:"pgpgout"` + PageSwapin uint64 `json:"pswpin"` + PageSwapout uint64 `json:"pswpout"` + PageAllocDMA uint64 `json:"pgalloc_dma"` + PageAllocDMA32 uint64 `json:"pgalloc_dma32"` + PageAllocNormal uint64 `json:"pgalloc_normal"` + PageAllocMovable uint64 `json:"pgalloc_movable"` + PageFree uint64 `json:"pgfree"` + PageActivate uint64 `json:"pgactivate"` + PageDeactivate uint64 `json:"pgdeactivate"` + PageFault uint64 `json:"pgfault"` + PageMajorFault uint64 `json:"pgmajfault"` + PageRefillDMA uint64 `json:"pgrefill_dma"` + PageRefillDMA32 uint64 `json:"pgrefill_dma32"` + PageRefillMormal uint64 `json:"pgrefill_normal"` + PageRefillMovable uint64 `json:"pgrefill_movable"` + PageStealKswapdDMA uint64 `json:"pgsteal_kswapd_dma"` + PageStealKswapdDMA32 uint64 `json:"pgsteal_kswapd_dma32"` + PageStealKswapdNormal uint64 `json:"pgsteal_kswapd_normal"` + PageStealKswapdMovable uint64 `json:"pgsteal_kswapd_movable"` + PageStealDirectDMA uint64 `json:"pgsteal_direct_dma"` + PageStealDirectDMA32 uint64 `json:"pgsteal_direct_dma32"` + PageStealDirectNormal uint64 `json:"pgsteal_direct_normal"` + PageStealDirectMovable uint64 `json:"pgsteal_direct_movable"` + PageScanKswapdDMA uint64 `json:"pgscan_kswapd_dma"` + PageScanKswapdDMA32 uint64 `json:"pgscan_kswapd_dma32"` + PageScanKswapdNormal uint64 `json:"pgscan_kswapd_normal"` + PageScanKswapdMovable uint64 `json:"pgscan_kswapd_movable"` + PageScanDirectDMA uint64 `json:"pgscan_direct_dma"` + PageScanDirectDMA32 uint64 `json:"pgscan_direct_dma32"` + PageScanDirectNormal uint64 `json:"pgscan_direct_normal"` + PageScanDirectMovable uint64 `json:"pgscan_direct_movable"` + PageScanDirectThrottle uint64 `json:"pgscan_direct_throttle"` + ZoneReclaimFailed uint64 `json:"zone_reclaim_failed"` + PageInodeSteal uint64 `json:"pginodesteal"` + SlabsScanned uint64 `json:"slabs_scanned"` + KswapdInodesteal uint64 `json:"kswapd_inodesteal"` + KswapdLowWatermarkHitQuickly uint64 `json:"kswapd_low_wmark_hit_quickly"` + KswapdHighWatermarkHitQuickly uint64 `json:"kswapd_high_wmark_hit_quickly"` + PageoutRun uint64 `json:"pageoutrun"` + AllocStall uint64 `json:"allocstall"` + PageRotated uint64 `json:"pgrotated"` + DropPagecache uint64 `json:"drop_pagecache"` + DropSlab uint64 `json:"drop_slab"` + NumaPteUpdates uint64 `json:"numa_pte_updates"` + NumaHugePteUpdates uint64 `json:"numa_huge_pte_updates"` + NumaHintFaults uint64 `json:"numa_hint_faults"` + NumaHintFaults_local uint64 `json:"numa_hint_faults_local"` + NumaPagesMigrated uint64 `json:"numa_pages_migrated"` + PageMigrateSuccess uint64 `json:"pgmigrate_success"` + PageMigrateFail uint64 `json:"pgmigrate_fail"` + CompactMigrateScanned uint64 `json:"compact_migrate_scanned"` + CompactFreeScanned uint64 `json:"compact_free_scanned"` + CompactIsolated uint64 `json:"compact_isolated"` + CompactStall uint64 `json:"compact_stall"` + CompactFail uint64 `json:"compact_fail"` + CompactSuccess uint64 `json:"compact_success"` + HtlbBuddyAllocSuccess uint64 `json:"htlb_buddy_alloc_success"` + HtlbBuddyAllocFail uint64 `json:"htlb_buddy_alloc_fail"` + UnevictablePagesCulled uint64 `json:"unevictable_pgs_culled"` + UnevictablePagesScanned uint64 `json:"unevictable_pgs_scanned"` + UnevictablePagesRescued uint64 `json:"unevictable_pgs_rescued"` + UnevictablePagesMlocked uint64 `json:"unevictable_pgs_mlocked"` + UnevictablePagesMunlocked uint64 `json:"unevictable_pgs_munlocked"` + UnevictablePagesCleared uint64 `json:"unevictable_pgs_cleared"` + UnevictablePagesStranded uint64 `json:"unevictable_pgs_stranded"` + THPFaultAlloc uint64 `json:"thp_fault_alloc"` + THPFaultFallback uint64 `json:"thp_fault_fallback"` + THPCollapseAlloc uint64 `json:"thp_collapse_alloc"` + THPCollapseAllocFailed uint64 `json:"thp_collapse_alloc_failed"` + THPSplit uint64 `json:"thp_split"` + THPZeroPageAlloc uint64 `json:"thp_zero_page_alloc"` + THPZeroPageAllocFailed uint64 `json:"thp_zero_page_alloc_failed"` +} + +func ReadVMStat(path string) (*VMStat, error) { + b, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + content := string(b) + lines := strings.Split(content, "\n") + vmstat := VMStat{} + for _, line := range lines { + fields := strings.Fields(line) + if len(fields) != 2 { + continue + } + name := fields[0] + value, _ := strconv.ParseUint(fields[1], 10, 64) + switch name { + case "nr_free_pages": + vmstat.NrFreePages = value + case "nr_alloc_batch": + vmstat.NrAllocBatch = value + case "nr_inactive_anon": + vmstat.NrInactiveAnon = value + case "nr_active_anon": + vmstat.NrActiveAnon = value + case "nr_inactive_file": + vmstat.NrInactiveFile = value + case "nr_active_file": + vmstat.NrActiveFile = value + case "nr_unevictable": + vmstat.NrUnevictable = value + case "nr_mlock": + vmstat.NrMlock = value + case "nr_anon_pages": + vmstat.NrAnonPages = value + case "nr_mapped": + vmstat.NrMapped = value + case "nr_file_pages": + vmstat.NrFilePages = value + case "nr_dirty": + vmstat.NrDirty = value + case "nr_writeback": + vmstat.NrWriteback = value + case "nr_slab_reclaimable": + vmstat.NrSlabReclaimable = value + case "nr_slab_unreclaimable": + vmstat.NrSlabUnreclaimable = value + case "nr_page_table_pages": + vmstat.NrPageTablePages = value + case "nr_kernel_stack": + vmstat.NrKernelStack = value + case "nr_unstable": + vmstat.NrUnstable = value + case "nr_bounce": + vmstat.NrBounce = value + case "nr_vmscan_write": + vmstat.NrVmscanWrite = value + case "nr_vmscan_immediate_reclaim": + vmstat.NrVmscanImmediateReclaim = value + case "nr_writeback_temp": + vmstat.NrWritebackTemp = value + case "nr_isolated_anon": + vmstat.NrIsolatedAnon = value + case "nr_isolated_file": + vmstat.NrIsolatedFile = value + case "nr_shmem": + vmstat.NrShmem = value + case "nr_dirtied": + vmstat.NrDirtied = value + case "nr_written": + vmstat.NrWritten = value + case "numa_hit": + vmstat.NumaHit = value + case "numa_miss": + vmstat.NumaMiss = value + case "numa_foreign": + vmstat.NumaForeign = value + case "numa_interleave": + vmstat.NumaInterleave = value + case "numa_local": + vmstat.NumaLocal = value + case "numa_other": + vmstat.NumaOther = value + case "workingset_refault": + vmstat.WorkingsetRefault = value + case "workingset_activate": + vmstat.WorkingsetActivate = value + case "workingset_nodereclaim": + vmstat.WorkingsetNodereclaim = value + case "nr_anon_transparent_hugepages": + vmstat.NrAnonTransparentHugepages = value + case "nr_free_cma": + vmstat.NrFreeCma = value + case "nr_dirty_threshold": + vmstat.NrDirtyThreshold = value + case "nr_dirty_background_threshold": + vmstat.NrDirtyBackgroundThreshold = value + case "pgpgin": + vmstat.PagePagein = value + case "pgpgout": + vmstat.PagePageout = value + case "pswpin": + vmstat.PageSwapin = value + case "pswpout": + vmstat.PageSwapout = value + case "pgalloc_dma": + vmstat.PageAllocDMA = value + case "pgalloc_dma32": + vmstat.PageAllocDMA32 = value + case "pgalloc_normal": + vmstat.PageAllocNormal = value + case "pgalloc_movable": + vmstat.PageAllocMovable = value + case "pgfree": + vmstat.PageFree = value + case "pgactivate": + vmstat.PageActivate = value + case "pgdeactivate": + vmstat.PageDeactivate = value + case "pgfault": + vmstat.PageFault = value + case "pgmajfault": + vmstat.PageMajorFault = value + case "pgrefill_dma": + vmstat.PageRefillDMA = value + case "pgrefill_dma32": + vmstat.PageRefillDMA32 = value + case "pgrefill_normal": + vmstat.PageRefillMormal = value + case "pgrefill_movable": + vmstat.PageRefillMovable = value + case "pgsteal_kswapd_dma": + vmstat.PageStealKswapdDMA = value + case "pgsteal_kswapd_dma32": + vmstat.PageStealKswapdDMA32 = value + case "pgsteal_kswapd_normal": + vmstat.PageStealKswapdNormal = value + case "pgsteal_kswapd_movable": + vmstat.PageStealKswapdMovable = value + case "pgsteal_direct_dma": + vmstat.PageStealDirectDMA = value + case "pgsteal_direct_dma32": + vmstat.PageStealDirectDMA32 = value + case "pgsteal_direct_normal": + vmstat.PageStealDirectNormal = value + case "pgsteal_direct_movable": + vmstat.PageStealDirectMovable = value + case "pgscan_kswapd_dma": + vmstat.PageScanKswapdDMA = value + case "pgscan_kswapd_dma32": + vmstat.PageScanKswapdDMA32 = value + case "pgscan_kswapd_normal": + vmstat.PageScanKswapdNormal = value + case "pgscan_kswapd_movable": + vmstat.PageScanKswapdMovable = value + case "pgscan_direct_dma": + vmstat.PageScanDirectDMA = value + case "pgscan_direct_dma32": + vmstat.PageScanDirectDMA32 = value + case "pgscan_direct_normal": + vmstat.PageScanDirectNormal = value + case "pgscan_direct_movable": + vmstat.PageScanDirectMovable = value + case "pgscan_direct_throttle": + vmstat.PageScanDirectThrottle = value + case "zone_reclaim_failed": + vmstat.ZoneReclaimFailed = value + case "pginodesteal": + vmstat.PageInodeSteal = value + case "slabs_scanned": + vmstat.SlabsScanned = value + case "kswapd_inodesteal": + vmstat.KswapdInodesteal = value + case "kswapd_low_wmark_hit_quickly": + vmstat.KswapdLowWatermarkHitQuickly = value + case "kswapd_high_wmark_hit_quickly": + vmstat.KswapdHighWatermarkHitQuickly = value + case "pageoutrun": + vmstat.PageoutRun = value + case "allocstall": + vmstat.AllocStall = value + case "pgrotated": + vmstat.PageRotated = value + case "drop_pagecache": + vmstat.DropPagecache = value + case "drop_slab": + vmstat.DropSlab = value + case "numa_pte_updates": + vmstat.NumaPteUpdates = value + case "numa_huge_pte_updates": + vmstat.NumaHugePteUpdates = value + case "numa_hint_faults": + vmstat.NumaHintFaults = value + case "numa_hint_faults_local": + vmstat.NumaHintFaults_local = value + case "numa_pages_migrated": + vmstat.NumaPagesMigrated = value + case "pgmigrate_success": + vmstat.PageMigrateSuccess = value + case "pgmigrate_fail": + vmstat.PageMigrateFail = value + case "compact_migrate_scanned": + vmstat.CompactMigrateScanned = value + case "compact_free_scanned": + vmstat.CompactFreeScanned = value + case "compact_isolated": + vmstat.CompactIsolated = value + case "compact_stall": + vmstat.CompactStall = value + case "compact_fail": + vmstat.CompactFail = value + case "compact_success": + vmstat.CompactSuccess = value + case "htlb_buddy_alloc_success": + vmstat.HtlbBuddyAllocSuccess = value + case "htlb_buddy_alloc_fail": + vmstat.HtlbBuddyAllocFail = value + case "unevictable_pgs_culled": + vmstat.UnevictablePagesCulled = value + case "unevictable_pgs_scanned": + vmstat.UnevictablePagesScanned = value + case "unevictable_pgs_rescued": + vmstat.UnevictablePagesRescued = value + case "unevictable_pgs_mlocked": + vmstat.UnevictablePagesMlocked = value + case "unevictable_pgs_munlocked": + vmstat.UnevictablePagesMunlocked = value + case "unevictable_pgs_cleared": + vmstat.UnevictablePagesCleared = value + case "unevictable_pgs_stranded": + vmstat.UnevictablePagesStranded = value + case "thp_fault_alloc": + vmstat.THPFaultAlloc = value + case "thp_fault_fallback": + vmstat.THPFaultFallback = value + case "thp_collapse_alloc": + vmstat.THPCollapseAlloc = value + case "thp_collapse_alloc_failed": + vmstat.THPCollapseAllocFailed = value + case "thp_split": + vmstat.THPSplit = value + case "thp_zero_page_alloc": + vmstat.THPZeroPageAlloc = value + case "thp_zero_page_alloc_failed": + vmstat.THPZeroPageAllocFailed = value + } + } + return &vmstat, nil +} diff --git a/vendor/github.com/c9s/goprocinfo/linux/vmstat_test.go b/vendor/github.com/c9s/goprocinfo/linux/vmstat_test.go new file mode 100644 index 0000000000..056c55a2dd --- /dev/null +++ b/vendor/github.com/c9s/goprocinfo/linux/vmstat_test.go @@ -0,0 +1,12 @@ +package linux + +import "testing" + +func TestVMStat(t *testing.T) { + vmstat, err := ReadVMStat("proc/vmstat") + if err != nil { + t.Fatal("vmstat read fail") + } + _ = vmstat + t.Logf("%+v", vmstat) +} diff --git a/vendor/manifest b/vendor/manifest index 229349ce48..4bf28c26a3 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -46,6 +46,13 @@ "revision": "fb6c0b0e1ff03057a054886141927cdce6239dec", "branch": "master" }, + { + "importpath": "github.com/c9s/goprocinfo/linux", + "repository": "https://github.com/c9s/goprocinfo", + "revision": "19cb9f127a9c8d2034cf59ccb683cdb94b9deb6c", + "branch": "master", + "path": "/linux" + }, { "importpath": "github.com/certifi/gocertifi", "repository": "https://github.com/certifi/gocertifi",