From 2f24b5a9dcc9ca5da07563fb0dd381e49ed844cc Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Sun, 20 Apr 2014 23:27:05 -0400 Subject: [PATCH] work on cpu stats Docker-DCO-1.1-Signed-off-by: Evan Hazlett (github: ehazlett) --- pkg/cgroups/fs/cpuacct.go | 114 ++++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 42 deletions(-) diff --git a/pkg/cgroups/fs/cpuacct.go b/pkg/cgroups/fs/cpuacct.go index a2cbb81bc3..aa2cac5d8f 100644 --- a/pkg/cgroups/fs/cpuacct.go +++ b/pkg/cgroups/fs/cpuacct.go @@ -2,11 +2,13 @@ package fs import ( "bufio" - "io/ioutil" + "fmt" "os" "path/filepath" + "runtime" "strconv" "strings" + "time" "github.com/dotcloud/docker/pkg/cgroups" "github.com/dotcloud/docker/pkg/system" @@ -29,58 +31,36 @@ func (s *cpuacctGroup) Remove(d *data) error { func (s *cpuacctGroup) Stats(d *data) (map[string]float64, error) { var ( - uptime, startTime float64 - paramData = make(map[string]float64) - cpuTotal = 0.0 + startCpu, lastCpu, startSystem, lastSystem float64 + paramData = make(map[string]float64) + percentage = 0.0 ) - path, err := d.path("cpuacct") - if err != nil { - return paramData, err - } - f, err := os.Open(filepath.Join(path, "cpuacct.stat")) - if err != nil { + if startCpu, err = s.getCpuUsage(d, path); err != nil { return nil, err } - defer f.Close() - - sc := bufio.NewScanner(f) - for sc.Scan() { - t, v, err := getCgroupParamKeyValue(sc.Text()) - if err != nil { - return paramData, err - } - // set the raw data in map - paramData[t] = v - cpuTotal += v - } - - if uptime, err = s.getUptime(); err != nil { + if startSystem, err = s.getSystemCpuUsage(d); err != nil { return nil, err } - if startTime, err = s.getProcStarttime(d); err != nil { + // sample for 100ms + time.Sleep(100 * time.Millisecond) + if lastCpu, err = s.getCpuUsage(d, path); err != nil { return nil, err } - //paramData["percentage"] = 100.0 * ((cpuTotal/100.0)/uptime - (startTime / 100)) - paramData["percentage"] = cpuTotal / (uptime - (startTime / 100)) - + if lastSystem, err = s.getSystemCpuUsage(d); err != nil { + return nil, err + } + var ( + deltaProc = lastCpu - startCpu + deltaSystem = lastSystem - startSystem + ) + if deltaSystem > 0.0 { + percentage = ((deltaProc / deltaSystem) * 100.0) * float64(runtime.NumCPU()) + } + paramData["percentage"] = percentage return paramData, nil } -func (s *cpuacctGroup) getUptime() (float64, error) { - f, err := os.Open("/proc/uptime") - if err != nil { - return 0, err - } - defer f.Close() - - data, err := ioutil.ReadAll(f) - if err != nil { - return 0, err - } - return strconv.ParseFloat(strings.Fields(string(data))[0], 64) -} - func (s *cpuacctGroup) getProcStarttime(d *data) (float64, error) { rawStart, err := system.GetProcessStartTime(d.pid) if err != nil { @@ -88,3 +68,53 @@ func (s *cpuacctGroup) getProcStarttime(d *data) (float64, error) { } return strconv.ParseFloat(rawStart, 64) } + +func (s *cpuacctGroup) getSystemCpuUsage(d *data) (float64, error) { + total := 0.0 + f, err := os.Open("/proc/stat") + if err != nil { + return 0, err + } + defer f.Close() + + sc := bufio.NewScanner(f) + for sc.Scan() { + txt := sc.Text() + if strings.Index(txt, "cpu") == 0 { + parts := strings.Fields(txt) + partsLength := len(parts) + if partsLength != 11 { + return 0.0, fmt.Errorf("Unable to parse cpu usage: expected 11 fields ; received %d", partsLength) + } + for _, i := range parts[1:10] { + val, err := strconv.ParseFloat(i, 64) + if err != nil { + return 0.0, fmt.Errorf("Unable to convert value to float: %s", err) + } + total += val + } + break + } + } + return total, nil +} + +func (s *cpuacctGroup) getCpuUsage(d *data, path string) (float64, error) { + cpuTotal := 0.0 + f, err := os.Open(filepath.Join(path, "cpuacct.stat")) + if err != nil { + return 0.0, err + } + defer f.Close() + + sc := bufio.NewScanner(f) + for sc.Scan() { + _, v, err := getCgroupParamKeyValue(sc.Text()) + if err != nil { + return 0.0, err + } + // set the raw data in map + cpuTotal += v + } + return cpuTotal, nil +}