From 2aeeb72fa5c4f90d0ab072a361a678c3cdee8e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Sat, 27 Jun 2015 18:12:58 +0300 Subject: [PATCH] cmd/geth, metrics: separate process metric collection, add disk --- cmd/geth/main.go | 24 ++----------------- metrics/disk.go | 9 +++++++ metrics/disk_linux.go | 55 +++++++++++++++++++++++++++++++++++++++++++ metrics/disk_nop.go | 10 ++++++++ metrics/metrics.go | 54 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 130 insertions(+), 22 deletions(-) create mode 100644 metrics/disk.go create mode 100644 metrics/disk_linux.go create mode 100644 metrics/disk_nop.go create mode 100644 metrics/metrics.go diff --git a/cmd/geth/main.go b/cmd/geth/main.go index fcf7f27f02..6a52159eac 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -39,11 +39,11 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/comms" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" - "github.com/rcrowley/go-metrics" ) const ( @@ -288,27 +288,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso return nil } // Start system runtime metrics collection - go func() { - allocs := metrics.GetOrRegisterMeter("system/memory/allocs", metrics.DefaultRegistry) - frees := metrics.GetOrRegisterMeter("system/memory/frees", metrics.DefaultRegistry) - inuse := metrics.GetOrRegisterMeter("system/memory/inuse", metrics.DefaultRegistry) - pauses := metrics.GetOrRegisterMeter("system/memory/pauses", metrics.DefaultRegistry) - - stats := make([]*runtime.MemStats, 2) - for i := 0; i < len(stats); i++ { - stats[i] = new(runtime.MemStats) - } - for i := 1; ; i++ { - runtime.ReadMemStats(stats[i%2]) - - allocs.Mark(int64(stats[i%2].Mallocs - stats[(i-1)%2].Mallocs)) - frees.Mark(int64(stats[i%2].Frees - stats[(i-1)%2].Frees)) - inuse.Mark(int64(stats[i%2].Alloc - stats[(i-1)%2].Alloc)) - pauses.Mark(int64(stats[i%2].PauseTotalNs - stats[(i-1)%2].PauseTotalNs)) - - time.Sleep(3 * time.Second) - } - }() + go metrics.CollectProcessMetrics(3 * time.Second) } func main() { diff --git a/metrics/disk.go b/metrics/disk.go new file mode 100644 index 0000000000..1b6c56773f --- /dev/null +++ b/metrics/disk.go @@ -0,0 +1,9 @@ +package metrics + +// DiskStats is the per process disk io stats. +type DiskStats struct { + ReadCount int64 // Number of read operations executed + ReadBytes int64 // Total number of bytes read + WriteCount int64 // Number of write operations executed + WriteBytes int64 // Total number of byte written +} diff --git a/metrics/disk_linux.go b/metrics/disk_linux.go new file mode 100644 index 0000000000..82b2045344 --- /dev/null +++ b/metrics/disk_linux.go @@ -0,0 +1,55 @@ +// Contains the Linux implementation of process disk IO counter retrieval. + +package metrics + +import ( + "bufio" + "fmt" + "io" + "os" + "strconv" + "strings" +) + +// ReadDiskStats retrieves the disk IO stats belonging to the current process. +func ReadDiskStats(stats *DiskStats) error { + // Open the process disk IO counter file + inf, err := os.Open(fmt.Sprintf("/proc/%d/io", os.Getpid())) + if err != nil { + return err + } + in := bufio.NewReader(inf) + + // Iterate over the IO counter, and extract what we need + for { + // Read the next line and split to key and value + line, err := in.ReadString('\n') + if err != nil { + if err == io.EOF { + return nil + } + return err + } + key, value := "", int64(0) + if parts := strings.Split(line, ":"); len(parts) != 2 { + continue + } else { + key = strings.TrimSpace(parts[0]) + if value, err = strconv.ParseInt(strings.TrimSpace(parts[1]), 10, 64); err != nil { + return err + } + } + // Update the counter based on the key + switch key { + case "syscr": + stats.ReadCount = value + case "syscw": + stats.WriteCount = value + case "rchar": + stats.ReadBytes = value + case "wchar": + stats.WriteBytes = value + } + } + return nil +} diff --git a/metrics/disk_nop.go b/metrics/disk_nop.go new file mode 100644 index 0000000000..539ab8d1a0 --- /dev/null +++ b/metrics/disk_nop.go @@ -0,0 +1,10 @@ +// +build !linux + +package metrics + +import "errors" + +// ReadDiskStats retrieves the disk IO stats belonging to the current process. +func ReadDiskStats(stats *DiskStats) error { + return errors.New("Not implemented") +} diff --git a/metrics/metrics.go b/metrics/metrics.go new file mode 100644 index 0000000000..cb0caaae4e --- /dev/null +++ b/metrics/metrics.go @@ -0,0 +1,54 @@ +// Package metrics provides general system and process level metrics collection. +package metrics + +import ( + "runtime" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/rcrowley/go-metrics" +) + +// CollectProcessMetrics periodically collects various metrics about the running +// process. +func CollectProcessMetrics(refresh time.Duration) { + // Create the various data collectors + memstats := make([]*runtime.MemStats, 2) + diskstats := make([]*DiskStats, 2) + for i := 0; i < len(memstats); i++ { + memstats[i] = new(runtime.MemStats) + diskstats[i] = new(DiskStats) + } + // Define the various metrics to collect + memAllocs := metrics.GetOrRegisterMeter("system/memory/allocs", metrics.DefaultRegistry) + memFrees := metrics.GetOrRegisterMeter("system/memory/frees", metrics.DefaultRegistry) + memInuse := metrics.GetOrRegisterMeter("system/memory/inuse", metrics.DefaultRegistry) + memPauses := metrics.GetOrRegisterMeter("system/memory/pauses", metrics.DefaultRegistry) + + var diskReads, diskReadBytes, diskWrites, diskWriteBytes metrics.Meter + if err := ReadDiskStats(diskstats[0]); err == nil { + diskReads = metrics.GetOrRegisterMeter("system/disk/readcount", metrics.DefaultRegistry) + diskReadBytes = metrics.GetOrRegisterMeter("system/disk/readdata", metrics.DefaultRegistry) + diskWrites = metrics.GetOrRegisterMeter("system/disk/writecount", metrics.DefaultRegistry) + diskWriteBytes = metrics.GetOrRegisterMeter("system/disk/writedata", metrics.DefaultRegistry) + } else { + glog.V(logger.Debug).Infof("failed to read disk metrics: %v", err) + } + // Iterate loading the different stats and updating the meters + for i := 1; ; i++ { + runtime.ReadMemStats(memstats[i%2]) + memAllocs.Mark(int64(memstats[i%2].Mallocs - memstats[(i-1)%2].Mallocs)) + memFrees.Mark(int64(memstats[i%2].Frees - memstats[(i-1)%2].Frees)) + memInuse.Mark(int64(memstats[i%2].Alloc - memstats[(i-1)%2].Alloc)) + memPauses.Mark(int64(memstats[i%2].PauseTotalNs - memstats[(i-1)%2].PauseTotalNs)) + + if ReadDiskStats(diskstats[i%2]) == nil { + diskReads.Mark(int64(diskstats[i%2].ReadCount - diskstats[(i-1)%2].ReadCount)) + diskReadBytes.Mark(int64(diskstats[i%2].ReadBytes - diskstats[(i-1)%2].ReadBytes)) + diskWrites.Mark(int64(diskstats[i%2].WriteCount - diskstats[(i-1)%2].WriteCount)) + diskWriteBytes.Mark(int64(diskstats[i%2].WriteBytes - diskstats[(i-1)%2].WriteBytes)) + } + time.Sleep(refresh) + } +}