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..91343f72e1 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,11 @@ var GetUptime = func() (time.Duration, error) {
 	}
 	return (time.Duration(d) * 24 * time.Hour) + (time.Duration(h) * time.Hour) + (time.Duration(m) * time.Minute), nil
 }
+
+var GetCPUUsagePercent = func() (float64, float64) {
+	return 0.0, 0.0
+}
+
+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..2afe1dc501 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,40 @@ var GetUptime = func() (time.Duration, error) {
 
 	return time.Duration(uptime) * time.Second, nil
 }
+
+var previousStat = linuxproc.CPUStat{}
+
+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.
+}
+
+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..6283849e40 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
 	}
@@ -510,14 +512,19 @@ func hostOriginTable(nmd report.Node) (Table, bool) {
 	}
 
 	rows := []Row{}
-	for _, tuple := range []struct{ key, human string }{
-		{host.Load1, "Load (1m)"},
-		{host.Load5, "Load (5m)"},
-		{host.Load15, "Load (15m)"},
+	for _, tuple := range []struct {
+		key, human string
+		fmt        formatter
+	}{
+		{host.Load1, "Load (1m)", nil},
+		{host.Load5, "Load (5m)", nil},
+		{host.Load15, "Load (15m)", nil},
+		{host.CPUUsage, "CPU Usage", formatPercent},
+		{host.MemUsage, "Memory Usage", formatPercent},
 	} {
 		if val, ok := nmd.Metrics[tuple.key]; ok {
 			val.Max = maxLoad
-			rows = append(rows, sparklineRow(tuple.human, val, nil))
+			rows = append(rows, sparklineRow(tuple.human, val, tuple.fmt))
 		}
 	}
 	for _, tuple := range []struct{ key, human string }{
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
+		// <iface>: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+		// or
+		// <iface>: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",