From 59c33dfe0b82232c72b48d171ebd1541a88fc1bc Mon Sep 17 00:00:00 2001 From: Vishnu Kannan Date: Wed, 4 Jun 2014 06:41:03 +0000 Subject: [PATCH] Add stats for memory allocation failure count and instantaneous cpu usage in the usermode and kernelmode. Docker-DCO-1.1-Signed-off-by: Vishnu Kannan (github: vishh) --- libcontainer/cgroups/fs/cpuacct.go | 44 +++++++++++++++----------- libcontainer/cgroups/fs/memory.go | 5 +++ libcontainer/cgroups/fs/memory_test.go | 4 ++- libcontainer/cgroups/stats.go | 6 ++++ 4 files changed, 39 insertions(+), 20 deletions(-) diff --git a/libcontainer/cgroups/fs/cpuacct.go b/libcontainer/cgroups/fs/cpuacct.go index dc17fdf..5ea01dc 100644 --- a/libcontainer/cgroups/fs/cpuacct.go +++ b/libcontainer/cgroups/fs/cpuacct.go @@ -20,6 +20,8 @@ var ( clockTicks = uint64(system.GetClockTicks()) ) +const nanosecondsInSecond = 1000000000 + type cpuacctGroup struct { } @@ -37,13 +39,13 @@ func (s *cpuacctGroup) Remove(d *data) error { func (s *cpuacctGroup) GetStats(d *data, stats *cgroups.Stats) error { var ( - startCpu, lastCpu, startSystem, lastSystem, startUsage, lastUsage uint64 - percentage uint64 + startCpu, lastCpu, startSystem, lastSystem, startUsage, lastUsage, kernelModeUsage, userModeUsage, percentage uint64 ) path, err := d.path("cpuacct") - if startCpu, err = s.getCpuUsage(d, path); err != nil { + if kernelModeUsage, userModeUsage, err = s.getCpuUsage(d, path); err != nil { return err } + startCpu = kernelModeUsage + userModeUsage if startSystem, err = s.getSystemCpuUsage(d); err != nil { return err } @@ -53,9 +55,10 @@ func (s *cpuacctGroup) GetStats(d *data, stats *cgroups.Stats) error { } // sample for 100ms time.Sleep(100 * time.Millisecond) - if lastCpu, err = s.getCpuUsage(d, path); err != nil { + if kernelModeUsage, userModeUsage, err = s.getCpuUsage(d, path); err != nil { return err } + lastCpu = kernelModeUsage + userModeUsage if lastSystem, err = s.getSystemCpuUsage(d); err != nil { return err } @@ -82,6 +85,8 @@ func (s *cpuacctGroup) GetStats(d *data, stats *cgroups.Stats) error { return err } stats.CpuStats.CpuUsage.PercpuUsage = percpuUsage + stats.CpuStats.CpuUsage.UsageInKernelmode = (kernelModeUsage * nanosecondsInSecond) / clockTicks + stats.CpuStats.CpuUsage.UsageInUsermode = (userModeUsage * nanosecondsInSecond) / clockTicks return nil } @@ -119,24 +124,25 @@ func (s *cpuacctGroup) getSystemCpuUsage(d *data) (uint64, error) { return 0, fmt.Errorf("invalid stat format") } -func (s *cpuacctGroup) getCpuUsage(d *data, path string) (uint64, error) { - cpuTotal := uint64(0) - f, err := os.Open(filepath.Join(path, "cpuacct.stat")) +func (s *cpuacctGroup) getCpuUsage(d *data, path string) (uint64, uint64, error) { + kernelModeUsage := uint64(0) + userModeUsage := uint64(0) + data, err := ioutil.ReadFile(filepath.Join(path, "cpuacct.stat")) if err != nil { - return 0, err + return 0, 0, err + } + fields := strings.Fields(string(data)) + if len(fields) != 4 { + return 0, 0, fmt.Errorf("Failure - %s is expected to have 4 fields", filepath.Join(path, "cpuacct.stat")) + } + if userModeUsage, err = strconv.ParseUint(fields[1], 10, 64); err != nil { + return 0, 0, err + } + if kernelModeUsage, err = strconv.ParseUint(fields[3], 10, 64); 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, err - } - // set the raw data in map - cpuTotal += v - } - return cpuTotal, nil + return kernelModeUsage, userModeUsage, nil } func (s *cpuacctGroup) getPercpuUsage(path string) ([]uint64, error) { diff --git a/libcontainer/cgroups/fs/memory.go b/libcontainer/cgroups/fs/memory.go index f202b16..202ddc8 100644 --- a/libcontainer/cgroups/fs/memory.go +++ b/libcontainer/cgroups/fs/memory.go @@ -84,6 +84,11 @@ func (s *memoryGroup) GetStats(d *data, stats *cgroups.Stats) error { return err } stats.MemoryStats.MaxUsage = value + value, err = getCgroupParamInt(path, "memory.failcnt") + if err != nil { + return err + } + stats.MemoryStats.Failcnt = value return nil } diff --git a/libcontainer/cgroups/fs/memory_test.go b/libcontainer/cgroups/fs/memory_test.go index e7d2018..29aea1f 100644 --- a/libcontainer/cgroups/fs/memory_test.go +++ b/libcontainer/cgroups/fs/memory_test.go @@ -11,6 +11,7 @@ const ( rss 1024` memoryUsageContents = "2048\n" memoryMaxUsageContents = "4096\n" + memoryFailcnt = "100\n" ) func TestMemoryStats(t *testing.T) { @@ -20,6 +21,7 @@ func TestMemoryStats(t *testing.T) { "memory.stat": memoryStatContents, "memory.usage_in_bytes": memoryUsageContents, "memory.max_usage_in_bytes": memoryMaxUsageContents, + "memory.failcnt": memoryFailcnt, }) memory := &memoryGroup{} @@ -27,7 +29,7 @@ func TestMemoryStats(t *testing.T) { if err != nil { t.Fatal(err) } - expectedStats := cgroups.MemoryStats{Usage: 2048, MaxUsage: 4096, Stats: map[string]uint64{"cache": 512, "rss": 1024}} + expectedStats := cgroups.MemoryStats{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Stats: map[string]uint64{"cache": 512, "rss": 1024}} expectMemoryStatEquals(t, expectedStats, actualStats.MemoryStats) } diff --git a/libcontainer/cgroups/stats.go b/libcontainer/cgroups/stats.go index eddf0ee..20a5f00 100644 --- a/libcontainer/cgroups/stats.go +++ b/libcontainer/cgroups/stats.go @@ -15,6 +15,10 @@ type CpuUsage struct { // nanoseconds of cpu time consumed over the last 100 ms. CurrentUsage uint64 `json:"current_usage,omitempty"` PercpuUsage []uint64 `json:"percpu_usage,omitempty"` + // Time spent by tasks of the cgroup in kernel mode. Units: nanoseconds. + UsageInKernelmode uint64 `json:"usage_in_kernelmode"` + // Time spent by tasks of the cgroup in user mode. Units: nanoseconds. + UsageInUsermode uint64 `json:"usage_in_usermode"` } type CpuStats struct { @@ -30,6 +34,8 @@ type MemoryStats struct { // TODO(vishh): Export these as stronger types. // all the stats exported via memory.stat. Stats map[string]uint64 `json:"stats,omitempty"` + // number of times memory usage hits limits. + Failcnt uint64 `json:"failcnt"` } type BlkioStatEntry struct {