From eaa166e8c34a922840b9b237e51e1850252510eb Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Wed, 11 Jun 2014 10:37:05 -0700 Subject: [PATCH 01/27] Use ContainerStatsSample to store samples because CPU usages are cumilative --- info/container.go | 80 +++++++++++++++++++++++++++++++++--------- info/container_test.go | 12 +++---- 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/info/container.go b/info/container.go index 3f4f8e72..6dfba908 100644 --- a/info/container.go +++ b/info/container.go @@ -101,10 +101,22 @@ func (self *ContainerInfo) StatsEndTime() time.Time { type CpuStats struct { Usage struct { - Total uint64 `json:"total"` + // Number of nanoseconds of CPU time used by the container + // since the beginning. This is a ccumulative + // value, not an instantaneous value. + Total uint64 `json:"total"` + + // Per CPU/core usage of the container. + // Unit: nanoseconds. PerCpu []uint64 `json:"per_cpu,omitempty"` - User uint64 `json:"user"` - System uint64 `json:"system"` + + // How much time was spent in user space since beginning. + // Unit: nanoseconds + User uint64 `json:"user"` + + // How much time was spent in kernel space since beginning. + // Unit: nanoseconds + System uint64 `json:"system"` } `json:"usage"` Load int32 `json:"load"` } @@ -142,6 +154,18 @@ type ContainerStats struct { Memory *MemoryStats `json:"memory,omitempty"` } +type ContainerStatsSample struct { + Cpu struct { + // number of nanoseconds of CPU time used by the container + // within one second. + Usage uint64 `json:"usage"` + } `json:"cpu"` + Memory struct { + // Units: Bytes. + Usage uint64 `json:"usage"` + } `json:"memory"` +} + // This is not exported. // Use FillPercentile to calculate percentiles type percentile struct { @@ -151,11 +175,37 @@ type percentile struct { type ContainerStatsSummary struct { // TODO(dengnan): More things? - MaxMemoryUsage uint64 `json:"max_memory_usage,omitempty"` - AvgMemoryUsage uint64 `json:"avg_memory_usage,omitempty"` - Samples []*ContainerStats `json:"samples,omitempty"` - MemoryUsagePercentiles []percentile `json:"memory_usage_percentiles,omitempty"` - CpuUsagePercentiles []percentile `json:"cpu_usage_percentiles,omitempty"` + MaxMemoryUsage uint64 `json:"max_memory_usage,omitempty"` + AvgMemoryUsage uint64 `json:"avg_memory_usage,omitempty"` + Samples []*ContainerStatsSample `json:"samples,omitempty"` + MemoryUsagePercentiles []percentile `json:"memory_usage_percentiles,omitempty"` + CpuUsagePercentiles []percentile `json:"cpu_usage_percentiles,omitempty"` +} + +// Each sample needs two stats because the cpu usage in ContainerStats is +// cumulative. +// prev should be an earlier observation than current. +// This method is not thread/goroutine safe. +func (self *ContainerStatsSummary) AddSample(prev, current *ContainerStats) { + if prev == nil || current == nil { + return + } + // Ignore this sample if it is incomplete + if prev.Cpu == nil || prev.Memory == nil || current.Cpu == nil || current.Memory == nil { + return + } + // prev must be an early observation + if !current.Timestamp.After(prev.Timestamp) { + return + } + sample := new(ContainerStatsSample) + // Caculate the diff to get the CPU usage within the time interval. + sample.Cpu.Usage = current.Cpu.Usage.Total - prev.Cpu.Usage.Total + // Memory usage is current memory usage + sample.Memory.Usage = current.Memory.Usage + + self.Samples = append(self.Samples, sample) + return } type uint64Slice []uint64 @@ -190,7 +240,7 @@ func (self uint64Slice) Percentiles(ps ...int) []uint64 { } // len(bs) <= len(as) -func float64Zipuint64(as []int, bs []uint64) []percentile { +func intZipuint64(as []int, bs []uint64) []percentile { if len(bs) == 0 { return nil } @@ -216,16 +266,12 @@ func (self *ContainerStatsSummary) FillPercentiles(cpuPercentages, memoryPercent if sample == nil { continue } - if sample.Cpu != nil { - cpuUsages = append(cpuUsages, sample.Cpu.Usage.Total) - } - if sample.Memory != nil { - memUsages = append(memUsages, sample.Memory.Usage) - } + cpuUsages = append(cpuUsages, sample.Cpu.Usage) + memUsages = append(memUsages, sample.Memory.Usage) } cpuPercentiles := uint64Slice(cpuUsages).Percentiles(cpuPercentages...) memPercentiles := uint64Slice(memUsages).Percentiles(memoryPercentages...) - self.CpuUsagePercentiles = float64Zipuint64(cpuPercentages, cpuPercentiles) - self.MemoryUsagePercentiles = float64Zipuint64(memoryPercentages, memPercentiles) + self.CpuUsagePercentiles = intZipuint64(cpuPercentages, cpuPercentiles) + self.MemoryUsagePercentiles = intZipuint64(memoryPercentages, memPercentiles) } diff --git a/info/container_test.go b/info/container_test.go index 3c0d7342..3eff2658 100644 --- a/info/container_test.go +++ b/info/container_test.go @@ -70,17 +70,17 @@ func TestPercentiles(t *testing.T) { for i := 0; i < N; i++ { data[i] = uint64(i) } - ps := []float64{ - 0.8, - 0.9, - 0.5, + ps := []int{ + 80, + 90, + 50, } ss := uint64Slice(data).Percentiles(ps...) for i, s := range ss { p := ps[i] - d := uint64(float64(N) * p) + d := uint64(float64(N) * (float64(p) / 100.0)) if d != s { - t.Errorf("%v \\%tile data should be %v, but got %v", p*float64(100), d, s) + t.Errorf("%v \\%tile data should be %v, but got %v", float64(p)/100.0, d, s) } } } From f1fa77adc3c24e2c2d1eacb1a8e2c8b999c64faf Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Wed, 11 Jun 2014 11:12:09 -0700 Subject: [PATCH 02/27] unit tests for stats sample --- info/container_test.go | 164 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) diff --git a/info/container_test.go b/info/container_test.go index 3eff2658..c1d1029c 100644 --- a/info/container_test.go +++ b/info/container_test.go @@ -84,3 +84,167 @@ func TestPercentiles(t *testing.T) { } } } + +func TestAddSampleNilStats(t *testing.T) { + s := &ContainerStatsSummary{} + + stats := &ContainerStats{ + Cpu: &CpuStats{}, + Memory: &MemoryStats{}, + } + stats.Cpu.Usage.PerCpu = []uint64{uint64(10)} + stats.Cpu.Usage.Total = uint64(10) + stats.Cpu.Usage.System = uint64(2) + stats.Cpu.Usage.User = uint64(8) + stats.Memory.Usage = uint64(200) + s.AddSample(nil, stats) + + if len(s.Samples) != 0 { + t.Errorf("added an unexpected sample: %+v", s.Samples) + } + + s.Samples = nil + s.AddSample(stats, nil) + + if len(s.Samples) != 0 { + t.Errorf("added an unexpected sample: %+v", s.Samples) + } +} + +func TestAddSample(t *testing.T) { + s := &ContainerStatsSummary{} + + cpuPrevUsage := uint64(10) + cpuCurrentUsage := uint64(15) + memCurrentUsage := uint64(200) + + prev := &ContainerStats{ + Cpu: &CpuStats{}, + Memory: &MemoryStats{}, + } + prev.Cpu.Usage.PerCpu = []uint64{cpuPrevUsage} + prev.Cpu.Usage.Total = cpuPrevUsage + prev.Cpu.Usage.System = 0 + prev.Cpu.Usage.User = cpuPrevUsage + prev.Timestamp = time.Now() + + current := &ContainerStats{ + Cpu: &CpuStats{}, + Memory: &MemoryStats{}, + } + current.Cpu.Usage.PerCpu = []uint64{cpuCurrentUsage} + current.Cpu.Usage.Total = cpuCurrentUsage + current.Cpu.Usage.System = 0 + current.Cpu.Usage.User = cpuCurrentUsage + current.Memory.Usage = memCurrentUsage + current.Timestamp = prev.Timestamp.Add(1 * time.Second) + + s.AddSample(prev, current) + + if len(s.Samples) != 1 { + t.Fatalf("unexpected samples: %+v", s.Samples) + } + + if s.Samples[0].Memory.Usage != memCurrentUsage { + t.Errorf("wrong memory usage: %v. should be %v", s.Samples[0].Memory.Usage, memCurrentUsage) + } + + if s.Samples[0].Cpu.Usage != cpuCurrentUsage-cpuPrevUsage { + t.Errorf("wrong CPU usage: %v. should be %v", s.Samples[0].Cpu.Usage, cpuCurrentUsage-cpuPrevUsage) + } +} + +func TestAddSampleIncompleteStats(t *testing.T) { + s := &ContainerStatsSummary{} + + cpuPrevUsage := uint64(10) + cpuCurrentUsage := uint64(15) + memCurrentUsage := uint64(200) + + prev := &ContainerStats{ + Cpu: &CpuStats{}, + Memory: &MemoryStats{}, + } + prev.Cpu.Usage.PerCpu = []uint64{cpuPrevUsage} + prev.Cpu.Usage.Total = cpuPrevUsage + prev.Cpu.Usage.System = 0 + prev.Cpu.Usage.User = cpuPrevUsage + prev.Timestamp = time.Now() + + current := &ContainerStats{ + Cpu: &CpuStats{}, + Memory: &MemoryStats{}, + } + current.Cpu.Usage.PerCpu = []uint64{cpuCurrentUsage} + current.Cpu.Usage.Total = cpuCurrentUsage + current.Cpu.Usage.System = 0 + current.Cpu.Usage.User = cpuCurrentUsage + current.Memory.Usage = memCurrentUsage + current.Timestamp = prev.Timestamp.Add(1 * time.Second) + + stats := &ContainerStats{ + Cpu: prev.Cpu, + Memory: nil, + } + s.AddSample(stats, current) + if len(s.Samples) != 0 { + t.Errorf("added an unexpected sample: %+v", s.Samples) + } + s.Samples = nil + + s.AddSample(prev, stats) + if len(s.Samples) != 0 { + t.Errorf("added an unexpected sample: %+v", s.Samples) + } + s.Samples = nil + + stats = &ContainerStats{ + Cpu: nil, + Memory: prev.Memory, + } + s.AddSample(stats, current) + if len(s.Samples) != 0 { + t.Errorf("added an unexpected sample: %+v", s.Samples) + } + s.Samples = nil + + s.AddSample(prev, stats) + if len(s.Samples) != 0 { + t.Errorf("added an unexpected sample: %+v", s.Samples) + } + s.Samples = nil +} + +func TestAddSampleWrongOrder(t *testing.T) { + s := &ContainerStatsSummary{} + + cpuPrevUsage := uint64(10) + cpuCurrentUsage := uint64(15) + memCurrentUsage := uint64(200) + + prev := &ContainerStats{ + Cpu: &CpuStats{}, + Memory: &MemoryStats{}, + } + prev.Cpu.Usage.PerCpu = []uint64{cpuPrevUsage} + prev.Cpu.Usage.Total = cpuPrevUsage + prev.Cpu.Usage.System = 0 + prev.Cpu.Usage.User = cpuPrevUsage + prev.Timestamp = time.Now() + + current := &ContainerStats{ + Cpu: &CpuStats{}, + Memory: &MemoryStats{}, + } + current.Cpu.Usage.PerCpu = []uint64{cpuCurrentUsage} + current.Cpu.Usage.Total = cpuCurrentUsage + current.Cpu.Usage.System = 0 + current.Cpu.Usage.User = cpuCurrentUsage + current.Memory.Usage = memCurrentUsage + current.Timestamp = prev.Timestamp.Add(1 * time.Second) + + s.AddSample(current, prev) + if len(s.Samples) != 0 { + t.Errorf("added an unexpected sample: %+v", s.Samples) + } +} From 0625caffa3251c6614377d4007823aa46938a6f2 Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Wed, 11 Jun 2014 11:26:55 -0700 Subject: [PATCH 03/27] create sample from a constructor --- container/statssum.go | 1 + info/container.go | 12 +++---- info/container_test.go | 78 +++++++++++++++++------------------------- 3 files changed, 39 insertions(+), 52 deletions(-) diff --git a/container/statssum.go b/container/statssum.go index 6126b877..cda8054b 100644 --- a/container/statssum.go +++ b/container/statssum.go @@ -26,6 +26,7 @@ import ( type statsSummaryContainerHandlerWrapper struct { handler ContainerHandler currentSummary *info.ContainerStatsSummary + prevStats *info.ContainerStats totalMemoryUsage *big.Int numStats uint64 sampler sampling.Sampler diff --git a/info/container.go b/info/container.go index 6dfba908..489a3506 100644 --- a/info/container.go +++ b/info/container.go @@ -15,6 +15,7 @@ package info import ( + "fmt" "sort" "time" ) @@ -186,17 +187,17 @@ type ContainerStatsSummary struct { // cumulative. // prev should be an earlier observation than current. // This method is not thread/goroutine safe. -func (self *ContainerStatsSummary) AddSample(prev, current *ContainerStats) { +func NewSample(prev, current *ContainerStats) (*ContainerStatsSample, error) { if prev == nil || current == nil { - return + return nil, fmt.Errorf("empty stats") } // Ignore this sample if it is incomplete if prev.Cpu == nil || prev.Memory == nil || current.Cpu == nil || current.Memory == nil { - return + return nil, fmt.Errorf("incomplete stats") } // prev must be an early observation if !current.Timestamp.After(prev.Timestamp) { - return + return nil, fmt.Errorf("wrong stats order") } sample := new(ContainerStatsSample) // Caculate the diff to get the CPU usage within the time interval. @@ -204,8 +205,7 @@ func (self *ContainerStatsSummary) AddSample(prev, current *ContainerStats) { // Memory usage is current memory usage sample.Memory.Usage = current.Memory.Usage - self.Samples = append(self.Samples, sample) - return + return sample, nil } type uint64Slice []uint64 diff --git a/info/container_test.go b/info/container_test.go index c1d1029c..50223403 100644 --- a/info/container_test.go +++ b/info/container_test.go @@ -85,9 +85,7 @@ func TestPercentiles(t *testing.T) { } } -func TestAddSampleNilStats(t *testing.T) { - s := &ContainerStatsSummary{} - +func TestNewSampleNilStats(t *testing.T) { stats := &ContainerStats{ Cpu: &CpuStats{}, Memory: &MemoryStats{}, @@ -97,23 +95,19 @@ func TestAddSampleNilStats(t *testing.T) { stats.Cpu.Usage.System = uint64(2) stats.Cpu.Usage.User = uint64(8) stats.Memory.Usage = uint64(200) - s.AddSample(nil, stats) - if len(s.Samples) != 0 { - t.Errorf("added an unexpected sample: %+v", s.Samples) + sample, err := NewSample(nil, stats) + if err == nil { + t.Errorf("generated an unexpected sample: %+v", sample) } - s.Samples = nil - s.AddSample(stats, nil) - - if len(s.Samples) != 0 { - t.Errorf("added an unexpected sample: %+v", s.Samples) + sample, err = NewSample(nil, stats) + if err == nil { + t.Errorf("generated an unexpected sample: %+v", sample) } } func TestAddSample(t *testing.T) { - s := &ContainerStatsSummary{} - cpuPrevUsage := uint64(10) cpuCurrentUsage := uint64(15) memCurrentUsage := uint64(200) @@ -139,24 +133,24 @@ func TestAddSample(t *testing.T) { current.Memory.Usage = memCurrentUsage current.Timestamp = prev.Timestamp.Add(1 * time.Second) - s.AddSample(prev, current) - - if len(s.Samples) != 1 { - t.Fatalf("unexpected samples: %+v", s.Samples) + sample, err := NewSample(prev, current) + if err != nil { + t.Errorf("should be able to generate a sample. but received error: %v", err) + } + if sample == nil { + t.Fatalf("nil sample and nil error. unexpected result!") } - if s.Samples[0].Memory.Usage != memCurrentUsage { - t.Errorf("wrong memory usage: %v. should be %v", s.Samples[0].Memory.Usage, memCurrentUsage) + if sample.Memory.Usage != memCurrentUsage { + t.Errorf("wrong memory usage: %v. should be %v", sample.Memory.Usage, memCurrentUsage) } - if s.Samples[0].Cpu.Usage != cpuCurrentUsage-cpuPrevUsage { - t.Errorf("wrong CPU usage: %v. should be %v", s.Samples[0].Cpu.Usage, cpuCurrentUsage-cpuPrevUsage) + if sample.Cpu.Usage != cpuCurrentUsage-cpuPrevUsage { + t.Errorf("wrong CPU usage: %v. should be %v", sample.Cpu.Usage, cpuCurrentUsage-cpuPrevUsage) } } func TestAddSampleIncompleteStats(t *testing.T) { - s := &ContainerStatsSummary{} - cpuPrevUsage := uint64(10) cpuCurrentUsage := uint64(15) memCurrentUsage := uint64(200) @@ -186,38 +180,30 @@ func TestAddSampleIncompleteStats(t *testing.T) { Cpu: prev.Cpu, Memory: nil, } - s.AddSample(stats, current) - if len(s.Samples) != 0 { - t.Errorf("added an unexpected sample: %+v", s.Samples) + sample, err := NewSample(stats, current) + if err == nil { + t.Errorf("generated an unexpected sample: %+v", sample) } - s.Samples = nil - - s.AddSample(prev, stats) - if len(s.Samples) != 0 { - t.Errorf("added an unexpected sample: %+v", s.Samples) + sample, err = NewSample(prev, stats) + if err == nil { + t.Errorf("generated an unexpected sample: %+v", sample) } - s.Samples = nil stats = &ContainerStats{ Cpu: nil, Memory: prev.Memory, } - s.AddSample(stats, current) - if len(s.Samples) != 0 { - t.Errorf("added an unexpected sample: %+v", s.Samples) + sample, err = NewSample(stats, current) + if err == nil { + t.Errorf("generated an unexpected sample: %+v", sample) } - s.Samples = nil - - s.AddSample(prev, stats) - if len(s.Samples) != 0 { - t.Errorf("added an unexpected sample: %+v", s.Samples) + sample, err = NewSample(prev, stats) + if err == nil { + t.Errorf("generated an unexpected sample: %+v", sample) } - s.Samples = nil } func TestAddSampleWrongOrder(t *testing.T) { - s := &ContainerStatsSummary{} - cpuPrevUsage := uint64(10) cpuCurrentUsage := uint64(15) memCurrentUsage := uint64(200) @@ -243,8 +229,8 @@ func TestAddSampleWrongOrder(t *testing.T) { current.Memory.Usage = memCurrentUsage current.Timestamp = prev.Timestamp.Add(1 * time.Second) - s.AddSample(current, prev) - if len(s.Samples) != 0 { - t.Errorf("added an unexpected sample: %+v", s.Samples) + sample, err := NewSample(current, prev) + if err == nil { + t.Errorf("generated an unexpected sample: %+v", sample) } } From 205a3798875d48b382663ba9a553d73b9675caf9 Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Wed, 11 Jun 2014 11:42:44 -0700 Subject: [PATCH 04/27] usge container stats sample --- container/statssum.go | 32 +++++++++++++++++++++++++++++--- container/statssum_test.go | 14 +++++++------- info/container.go | 4 +++- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/container/statssum.go b/container/statssum.go index cda8054b..08fdf005 100644 --- a/container/statssum.go +++ b/container/statssum.go @@ -37,6 +37,28 @@ func (self *statsSummaryContainerHandlerWrapper) GetSpec() (*info.ContainerSpec, return self.handler.GetSpec() } +func (self *statsSummaryContainerHandlerWrapper) updatePrevStats(stats *info.ContainerStats) { + if stats == nil || stats.Cpu == nil || stats.Memory == nil { + // discard incomplete stats + self.prevStats = nil + return + } + if self.prevStats == nil { + self.prevStats = &info.ContainerStats{ + Cpu: &info.CpuStats{}, + Memory: &info.MemoryStats{}, + } + } + // make a deep copy. + self.prevStats.Timestamp = stats.Timestamp + *self.prevStats.Cpu = *stats.Cpu + self.prevStats.Cpu.Usage.PerCpu = make([]uint64, len(stats.Cpu.Usage.PerCpu)) + for i, perCpu := range stats.Cpu.Usage.PerCpu { + self.prevStats.Cpu.Usage.PerCpu[i] = perCpu + } + *self.prevStats.Memory = *stats.Memory +} + func (self *statsSummaryContainerHandlerWrapper) GetStats() (*info.ContainerStats, error) { stats, err := self.handler.GetStats() if err != nil { @@ -49,7 +71,11 @@ func (self *statsSummaryContainerHandlerWrapper) GetStats() (*info.ContainerStat self.lock.Lock() defer self.lock.Unlock() - self.sampler.Update(stats) + sample, err := info.NewSample(self.prevStats, stats) + if err == nil && sample != nil { + self.sampler.Update(sample) + } + self.updatePrevStats(stats) if self.currentSummary == nil { self.currentSummary = new(info.ContainerStatsSummary) } @@ -87,9 +113,9 @@ func (self *statsSummaryContainerHandlerWrapper) ListProcesses(listType ListType func (self *statsSummaryContainerHandlerWrapper) StatsSummary() (*info.ContainerStatsSummary, error) { self.lock.Lock() defer self.lock.Unlock() - samples := make([]*info.ContainerStats, 0, self.sampler.Len()) + samples := make([]*info.ContainerStatsSample, 0, self.sampler.Len()) self.sampler.Map(func(d interface{}) { - stats := d.(*info.ContainerStats) + stats := d.(*info.ContainerStatsSample) samples = append(samples, stats) }) self.currentSummary.Samples = samples diff --git a/container/statssum_test.go b/container/statssum_test.go index 07adcac6..16280e51 100644 --- a/container/statssum_test.go +++ b/container/statssum_test.go @@ -31,15 +31,15 @@ func init() { rand.Seed(seed) } -type randomStatsContainer struct { +type randomMemoryUsageContainer struct { NoStatsSummary } -func (self *randomStatsContainer) GetSpec() (*info.ContainerSpec, error) { +func (self *randomMemoryUsageContainer) GetSpec() (*info.ContainerSpec, error) { return nil, nil } -func (self *randomStatsContainer) GetStats() (*info.ContainerStats, error) { +func (self *randomMemoryUsageContainer) GetStats() (*info.ContainerStats, error) { stats := new(info.ContainerStats) stats.Cpu = new(info.CpuStats) stats.Memory = new(info.MemoryStats) @@ -47,21 +47,21 @@ func (self *randomStatsContainer) GetStats() (*info.ContainerStats, error) { return stats, nil } -func (self *randomStatsContainer) ListContainers(listType ListType) ([]string, error) { +func (self *randomMemoryUsageContainer) ListContainers(listType ListType) ([]string, error) { return nil, nil } -func (self *randomStatsContainer) ListThreads(listType ListType) ([]int, error) { +func (self *randomMemoryUsageContainer) ListThreads(listType ListType) ([]int, error) { return nil, nil } -func (self *randomStatsContainer) ListProcesses(listType ListType) ([]int, error) { +func (self *randomMemoryUsageContainer) ListProcesses(listType ListType) ([]int, error) { return nil, nil } func TestAvgMaxMemoryUsage(t *testing.T) { handler, err := AddStatsSummary( - &randomStatsContainer{}, + &randomMemoryUsageContainer{}, &StatsParameter{ Sampler: "uniform", NumSamples: 10, diff --git a/info/container.go b/info/container.go index 489a3506..54be4714 100644 --- a/info/container.go +++ b/info/container.go @@ -156,7 +156,8 @@ type ContainerStats struct { } type ContainerStatsSample struct { - Cpu struct { + Timestamp time.Time `json:"timestamp"` + Cpu struct { // number of nanoseconds of CPU time used by the container // within one second. Usage uint64 `json:"usage"` @@ -204,6 +205,7 @@ func NewSample(prev, current *ContainerStats) (*ContainerStatsSample, error) { sample.Cpu.Usage = current.Cpu.Usage.Total - prev.Cpu.Usage.Total // Memory usage is current memory usage sample.Memory.Usage = current.Memory.Usage + sample.Timestamp = current.Timestamp return sample, nil } From 478ff3d45e951124ffabde7ddd733a8bd9affd70 Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Wed, 11 Jun 2014 13:22:08 -0700 Subject: [PATCH 05/27] unit test for sampling --- container/statssum.go | 5 +- container/statssum_test.go | 132 ++++++++++++++++++++++++++++++++----- info/container.go | 6 +- 3 files changed, 126 insertions(+), 17 deletions(-) diff --git a/container/statssum.go b/container/statssum.go index 08fdf005..86725399 100644 --- a/container/statssum.go +++ b/container/statssum.go @@ -67,7 +67,10 @@ func (self *statsSummaryContainerHandlerWrapper) GetStats() (*info.ContainerStat if stats == nil { return nil, nil } - stats.Timestamp = time.Now() + // Only update timestamp if it is zero. + if stats.Timestamp.IsZero() { + stats.Timestamp = time.Now() + } self.lock.Lock() defer self.lock.Unlock() diff --git a/container/statssum_test.go b/container/statssum_test.go index 16280e51..4a1b5b3d 100644 --- a/container/statssum_test.go +++ b/container/statssum_test.go @@ -18,7 +18,9 @@ import ( crand "crypto/rand" "encoding/binary" "math/rand" + "sync" "testing" + "time" "github.com/google/cadvisor/info" ) @@ -31,13 +33,28 @@ func init() { rand.Seed(seed) } -type randomMemoryUsageContainer struct { - NoStatsSummary +type notARealContainer struct { } -func (self *randomMemoryUsageContainer) GetSpec() (*info.ContainerSpec, error) { +func (self *notARealContainer) GetSpec() (*info.ContainerSpec, error) { return nil, nil } +func (self *notARealContainer) ListContainers(listType ListType) ([]string, error) { + return nil, nil +} + +func (self *notARealContainer) ListThreads(listType ListType) ([]int, error) { + return nil, nil +} + +func (self *notARealContainer) ListProcesses(listType ListType) ([]int, error) { + return nil, nil +} + +type randomMemoryUsageContainer struct { + NoStatsSummary + notARealContainer +} func (self *randomMemoryUsageContainer) GetStats() (*info.ContainerStats, error) { stats := new(info.ContainerStats) @@ -47,18 +64,6 @@ func (self *randomMemoryUsageContainer) GetStats() (*info.ContainerStats, error) return stats, nil } -func (self *randomMemoryUsageContainer) ListContainers(listType ListType) ([]string, error) { - return nil, nil -} - -func (self *randomMemoryUsageContainer) ListThreads(listType ListType) ([]int, error) { - return nil, nil -} - -func (self *randomMemoryUsageContainer) ListProcesses(listType ListType) ([]int, error) { - return nil, nil -} - func TestAvgMaxMemoryUsage(t *testing.T) { handler, err := AddStatsSummary( &randomMemoryUsageContainer{}, @@ -96,3 +101,100 @@ func TestAvgMaxMemoryUsage(t *testing.T) { t.Fatalf("Avg memory usage should be %v; received %v", avg, summary.AvgMemoryUsage) } } + +type replayCpuTrace struct { + NoStatsSummary + notARealContainer + cpuTrace []uint64 + totalUsage uint64 + currenttime time.Time + duration time.Duration + lock sync.Mutex +} + +func containerWithCpuTrace(duration time.Duration, cpuUsages ...uint64) ContainerHandler { + return &replayCpuTrace{ + duration: duration, + cpuTrace: cpuUsages, + currenttime: time.Now(), + } +} + +func (self *replayCpuTrace) GetStats() (*info.ContainerStats, error) { + stats := new(info.ContainerStats) + stats.Cpu = new(info.CpuStats) + stats.Memory = new(info.MemoryStats) + stats.Memory.Usage = uint64(rand.Intn(2048)) + + self.lock.Lock() + defer self.lock.Unlock() + + cpuTrace := self.totalUsage + if len(self.cpuTrace) > 0 { + cpuTrace += self.cpuTrace[0] + self.cpuTrace = self.cpuTrace[1:] + } + self.totalUsage = cpuTrace + stats.Timestamp = self.currenttime + self.currenttime = self.currenttime.Add(self.duration) + stats.Cpu.Usage.Total = cpuTrace + stats.Cpu.Usage.PerCpu = []uint64{cpuTrace} + stats.Cpu.Usage.User = cpuTrace + stats.Cpu.Usage.System = 0 + return stats, nil +} + +func TestSampleCpuUsage(t *testing.T) { + // Number of samples + N := 10 + cpuTrace := make([]uint64, 0, N) + + // We need N+1 observations to get N samples + for i := 0; i < N+1; i++ { + usage := uint64(rand.Intn(1000)) + cpuTrace = append(cpuTrace, usage) + } + + samplePeriod := 1 * time.Second + + handler, err := AddStatsSummary( + containerWithCpuTrace(samplePeriod, cpuTrace...), + &StatsParameter{ + // Use uniform sampler with sample size of N, so that + // we will be guaranteed to store the first N samples. + Sampler: "uniform", + NumSamples: N, + }, + ) + if err != nil { + t.Error(err) + } + + // request stats/obervation N+1 times, so that there will be N samples + for i := 0; i < N+1; i++ { + _, err = handler.GetStats() + if err != nil { + t.Fatal(err) + } + } + + s, err := handler.StatsSummary() + if err != nil { + t.Fatal(err) + } + for _, sample := range s.Samples { + if sample.Duration != samplePeriod { + t.Errorf("sample duration is %v, not %v", sample.Duration, samplePeriod) + } + cpuUsage := sample.Cpu.Usage + found := false + for _, u := range cpuTrace { + if u == cpuUsage { + found = true + } + } + if !found { + t.Errorf("unable to find cpu usage %v", cpuUsage) + } + } +} diff --git a/info/container.go b/info/container.go index 54be4714..a1452cd9 100644 --- a/info/container.go +++ b/info/container.go @@ -156,8 +156,11 @@ type ContainerStats struct { } type ContainerStatsSample struct { + // Timetamp of the end of the sample period Timestamp time.Time `json:"timestamp"` - Cpu struct { + // Duration of the sample period + Duration time.Duration `json:"duration"` + Cpu struct { // number of nanoseconds of CPU time used by the container // within one second. Usage uint64 `json:"usage"` @@ -206,6 +209,7 @@ func NewSample(prev, current *ContainerStats) (*ContainerStatsSample, error) { // Memory usage is current memory usage sample.Memory.Usage = current.Memory.Usage sample.Timestamp = current.Timestamp + sample.Duration = current.Timestamp.Sub(prev.Timestamp) return sample, nil } From 0d85d23a8945d0793b685e34f0ff0bec003f48c9 Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 10:31:43 -0700 Subject: [PATCH 06/27] ignore sample if it leads cpu utilization < 0 --- info/container.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/info/container.go b/info/container.go index a1452cd9..c1037c3f 100644 --- a/info/container.go +++ b/info/container.go @@ -203,6 +203,10 @@ func NewSample(prev, current *ContainerStats) (*ContainerStatsSample, error) { if !current.Timestamp.After(prev.Timestamp) { return nil, fmt.Errorf("wrong stats order") } + // This data is invalid. + if current.Cpu.Usage.Total < prev.Cpu.Usage.Total { + return nil, fmt.Errorf("current CPU usage is less than prev CPU usage (cumulative).") + } sample := new(ContainerStatsSample) // Caculate the diff to get the CPU usage within the time interval. sample.Cpu.Usage = current.Cpu.Usage.Total - prev.Cpu.Usage.Total From cc8a8bc61584d2fd0bcb016226835a7299ff35b4 Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 10:36:02 -0700 Subject: [PATCH 07/27] unit test for NewSample() --- info/container_test.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/info/container_test.go b/info/container_test.go index 50223403..88d182d0 100644 --- a/info/container_test.go +++ b/info/container_test.go @@ -234,3 +234,35 @@ func TestAddSampleWrongOrder(t *testing.T) { t.Errorf("generated an unexpected sample: %+v", sample) } } + +func TestAddSampleWrongCpuUsage(t *testing.T) { + cpuPrevUsage := uint64(15) + cpuCurrentUsage := uint64(10) + memCurrentUsage := uint64(200) + + prev := &ContainerStats{ + Cpu: &CpuStats{}, + Memory: &MemoryStats{}, + } + prev.Cpu.Usage.PerCpu = []uint64{cpuPrevUsage} + prev.Cpu.Usage.Total = cpuPrevUsage + prev.Cpu.Usage.System = 0 + prev.Cpu.Usage.User = cpuPrevUsage + prev.Timestamp = time.Now() + + current := &ContainerStats{ + Cpu: &CpuStats{}, + Memory: &MemoryStats{}, + } + current.Cpu.Usage.PerCpu = []uint64{cpuCurrentUsage} + current.Cpu.Usage.Total = cpuCurrentUsage + current.Cpu.Usage.System = 0 + current.Cpu.Usage.User = cpuCurrentUsage + current.Memory.Usage = memCurrentUsage + current.Timestamp = prev.Timestamp.Add(1 * time.Second) + + sample, err := NewSample(current, prev) + if err == nil { + t.Errorf("generated an unexpected sample: %+v", sample) + } +} From 5373161db3e406335a3b41ff516fb252cf1eeeae Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 11:25:38 -0700 Subject: [PATCH 08/27] change comments --- info/container.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/info/container.go b/info/container.go index c1037c3f..6c3380fd 100644 --- a/info/container.go +++ b/info/container.go @@ -100,22 +100,22 @@ func (self *ContainerInfo) StatsEndTime() time.Time { return ret } +// All CPU usage metrics are cumulative from the creation of the container type CpuStats struct { Usage struct { - // Number of nanoseconds of CPU time used by the container - // since the beginning. This is a ccumulative - // value, not an instantaneous value. + // Total CPU usage. + // Units: nanoseconds Total uint64 `json:"total"` // Per CPU/core usage of the container. // Unit: nanoseconds. PerCpu []uint64 `json:"per_cpu,omitempty"` - // How much time was spent in user space since beginning. + // Time spent in user space. // Unit: nanoseconds User uint64 `json:"user"` - // How much time was spent in kernel space since beginning. + // Time spent in kernel space. // Unit: nanoseconds System uint64 `json:"system"` } `json:"usage"` @@ -162,7 +162,6 @@ type ContainerStatsSample struct { Duration time.Duration `json:"duration"` Cpu struct { // number of nanoseconds of CPU time used by the container - // within one second. Usage uint64 `json:"usage"` } `json:"cpu"` Memory struct { From f07a91c0ccb54d2dcd6ff2c004cdc7ae37073e32 Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 11:36:17 -0700 Subject: [PATCH 09/27] remove avg memory usage --- container/statssum.go | 24 ++++++------------------ container/statssum_test.go | 8 +------- info/container.go | 1 - 3 files changed, 7 insertions(+), 26 deletions(-) diff --git a/container/statssum.go b/container/statssum.go index 86725399..2e5224fd 100644 --- a/container/statssum.go +++ b/container/statssum.go @@ -15,7 +15,6 @@ package container import ( - "math/big" "sync" "time" @@ -24,13 +23,12 @@ import ( ) type statsSummaryContainerHandlerWrapper struct { - handler ContainerHandler - currentSummary *info.ContainerStatsSummary - prevStats *info.ContainerStats - totalMemoryUsage *big.Int - numStats uint64 - sampler sampling.Sampler - lock sync.Mutex + handler ContainerHandler + currentSummary *info.ContainerStatsSummary + prevStats *info.ContainerStats + numStats uint64 + sampler sampling.Sampler + lock sync.Mutex } func (self *statsSummaryContainerHandlerWrapper) GetSpec() (*info.ContainerSpec, error) { @@ -87,16 +85,6 @@ func (self *statsSummaryContainerHandlerWrapper) GetStats() (*info.ContainerStat if stats.Memory.Usage > self.currentSummary.MaxMemoryUsage { self.currentSummary.MaxMemoryUsage = stats.Memory.Usage } - - // XXX(dengnan): Very inefficient! - if self.totalMemoryUsage == nil { - self.totalMemoryUsage = new(big.Int) - } - usage := (&big.Int{}).SetUint64(stats.Memory.Usage) - self.totalMemoryUsage = self.totalMemoryUsage.Add(self.totalMemoryUsage, usage) - n := (&big.Int{}).SetUint64(self.numStats) - avg := (&big.Int{}).Div(self.totalMemoryUsage, n) - self.currentSummary.AvgMemoryUsage = avg.Uint64() } return stats, nil } diff --git a/container/statssum_test.go b/container/statssum_test.go index 4a1b5b3d..9a91e21e 100644 --- a/container/statssum_test.go +++ b/container/statssum_test.go @@ -64,7 +64,7 @@ func (self *randomMemoryUsageContainer) GetStats() (*info.ContainerStats, error) return stats, nil } -func TestAvgMaxMemoryUsage(t *testing.T) { +func TestMaxMemoryUsage(t *testing.T) { handler, err := AddStatsSummary( &randomMemoryUsageContainer{}, &StatsParameter{ @@ -76,7 +76,6 @@ func TestAvgMaxMemoryUsage(t *testing.T) { t.Error(err) } var maxUsage uint64 - var totalUsage uint64 N := 100 for i := 0; i < N; i++ { stats, err := handler.GetStats() @@ -87,7 +86,6 @@ func TestAvgMaxMemoryUsage(t *testing.T) { if stats.Memory.Usage > maxUsage { maxUsage = stats.Memory.Usage } - totalUsage += stats.Memory.Usage } summary, err := handler.StatsSummary() if err != nil { @@ -96,10 +94,6 @@ func TestAvgMaxMemoryUsage(t *testing.T) { if summary.MaxMemoryUsage != maxUsage { t.Fatalf("Max memory usage should be %v; received %v", maxUsage, summary.MaxMemoryUsage) } - avg := totalUsage / uint64(N) - if summary.AvgMemoryUsage != avg { - t.Fatalf("Avg memory usage should be %v; received %v", avg, summary.AvgMemoryUsage) - } } type replayCpuTrace struct { diff --git a/info/container.go b/info/container.go index 6c3380fd..aed0c6c3 100644 --- a/info/container.go +++ b/info/container.go @@ -180,7 +180,6 @@ type percentile struct { type ContainerStatsSummary struct { // TODO(dengnan): More things? MaxMemoryUsage uint64 `json:"max_memory_usage,omitempty"` - AvgMemoryUsage uint64 `json:"avg_memory_usage,omitempty"` Samples []*ContainerStatsSample `json:"samples,omitempty"` MemoryUsagePercentiles []percentile `json:"memory_usage_percentiles,omitempty"` CpuUsagePercentiles []percentile `json:"cpu_usage_percentiles,omitempty"` From 71835f9bc41999ce7eb22888b64991d965361454 Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 11:38:37 -0700 Subject: [PATCH 10/27] gofmt -r "ContainerStatsSummary->ContainerStatsPercentiles" --- container/container.go | 4 ++-- container/statssum.go | 16 ++++++++-------- info/container.go | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/container/container.go b/container/container.go index b757ce86..7d21d1d1 100644 --- a/container/container.go +++ b/container/container.go @@ -35,12 +35,12 @@ type ContainerHandler interface { ListContainers(listType ListType) ([]string, error) ListThreads(listType ListType) ([]int, error) ListProcesses(listType ListType) ([]int, error) - StatsSummary() (*info.ContainerStatsSummary, error) + StatsSummary() (*info.ContainerStatsPercentiles, error) } type NoStatsSummary struct { } -func (self *NoStatsSummary) StatsSummary() (*info.ContainerStatsSummary, error) { +func (self *NoStatsSummary) StatsSummary() (*info.ContainerStatsPercentiles, error) { return nil, fmt.Errorf("This method (StatsSummary) should never be called") } diff --git a/container/statssum.go b/container/statssum.go index 2e5224fd..2565fef4 100644 --- a/container/statssum.go +++ b/container/statssum.go @@ -24,11 +24,11 @@ import ( type statsSummaryContainerHandlerWrapper struct { handler ContainerHandler - currentSummary *info.ContainerStatsSummary - prevStats *info.ContainerStats - numStats uint64 - sampler sampling.Sampler - lock sync.Mutex + currentSummary *info.ContainerStatsPercentiles + prevStats *info.ContainerStats + numStats uint64 + sampler sampling.Sampler + lock sync.Mutex } func (self *statsSummaryContainerHandlerWrapper) GetSpec() (*info.ContainerSpec, error) { @@ -78,7 +78,7 @@ func (self *statsSummaryContainerHandlerWrapper) GetStats() (*info.ContainerStat } self.updatePrevStats(stats) if self.currentSummary == nil { - self.currentSummary = new(info.ContainerStatsSummary) + self.currentSummary = new(info.ContainerStatsPercentiles) } self.numStats++ if stats.Memory != nil { @@ -101,7 +101,7 @@ func (self *statsSummaryContainerHandlerWrapper) ListProcesses(listType ListType return self.handler.ListProcesses(listType) } -func (self *statsSummaryContainerHandlerWrapper) StatsSummary() (*info.ContainerStatsSummary, error) { +func (self *statsSummaryContainerHandlerWrapper) StatsSummary() (*info.ContainerStatsPercentiles, error) { self.lock.Lock() defer self.lock.Unlock() samples := make([]*info.ContainerStatsSample, 0, self.sampler.Len()) @@ -132,7 +132,7 @@ func AddStatsSummary(handler ContainerHandler, parameter *StatsParameter) (Conta } return &statsSummaryContainerHandlerWrapper{ handler: handler, - currentSummary: &info.ContainerStatsSummary{}, + currentSummary: &info.ContainerStatsPercentiles{}, sampler: sampler, }, nil } diff --git a/info/container.go b/info/container.go index aed0c6c3..c8b2ebcf 100644 --- a/info/container.go +++ b/info/container.go @@ -62,7 +62,7 @@ type ContainerInfo struct { // Historical statistics gathered from the container. Stats []*ContainerStats `json:"stats,omitempty"` - StatsSummary *ContainerStatsSummary `json:"stats_summary,omitempty"` + StatsSummary *ContainerStatsPercentiles `json:"stats_summary,omitempty"` } func (self *ContainerInfo) StatsAfter(ref time.Time) []*ContainerStats { @@ -177,7 +177,7 @@ type percentile struct { Value uint64 `json:"value"` } -type ContainerStatsSummary struct { +type ContainerStatsPercentiles struct { // TODO(dengnan): More things? MaxMemoryUsage uint64 `json:"max_memory_usage,omitempty"` Samples []*ContainerStatsSample `json:"samples,omitempty"` @@ -263,7 +263,7 @@ func intZipuint64(as []int, bs []uint64) []percentile { return ret } -func (self *ContainerStatsSummary) FillPercentiles(cpuPercentages, memoryPercentages []int) { +func (self *ContainerStatsPercentiles) FillPercentiles(cpuPercentages, memoryPercentages []int) { if len(self.Samples) == 0 { return } From 3440b100a1cc7d97775be08317002aaedf5fcc29 Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 11:45:38 -0700 Subject: [PATCH 11/27] remove float opts. --- info/container_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/info/container_test.go b/info/container_test.go index 88d182d0..b2bbb524 100644 --- a/info/container_test.go +++ b/info/container_test.go @@ -78,9 +78,9 @@ func TestPercentiles(t *testing.T) { ss := uint64Slice(data).Percentiles(ps...) for i, s := range ss { p := ps[i] - d := uint64(float64(N) * (float64(p) / 100.0)) + d := uint64(N * p / 100) if d != s { - t.Errorf("%v \\%tile data should be %v, but got %v", float64(p)/100.0, d, s) + t.Errorf("%v percentile data should be %v, but got %v", p, d, s) } } } From c9c8e0fb4e4bf3ccbecec097a9ad54548fa92c34 Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 11:52:26 -0700 Subject: [PATCH 12/27] Ignore the error because its' same as nil sample --- container/statssum.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/container/statssum.go b/container/statssum.go index 2565fef4..f4cf227e 100644 --- a/container/statssum.go +++ b/container/statssum.go @@ -25,10 +25,10 @@ import ( type statsSummaryContainerHandlerWrapper struct { handler ContainerHandler currentSummary *info.ContainerStatsPercentiles - prevStats *info.ContainerStats - numStats uint64 - sampler sampling.Sampler - lock sync.Mutex + prevStats *info.ContainerStats + numStats uint64 + sampler sampling.Sampler + lock sync.Mutex } func (self *statsSummaryContainerHandlerWrapper) GetSpec() (*info.ContainerSpec, error) { @@ -72,8 +72,8 @@ func (self *statsSummaryContainerHandlerWrapper) GetStats() (*info.ContainerStat self.lock.Lock() defer self.lock.Unlock() - sample, err := info.NewSample(self.prevStats, stats) - if err == nil && sample != nil { + sample, _ := info.NewSample(self.prevStats, stats) + if sample != nil { self.sampler.Update(sample) } self.updatePrevStats(stats) From d669bb65322d423af80ae7ae8574d111c2a82e05 Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 12:40:13 -0700 Subject: [PATCH 13/27] correct a unit test --- info/container_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info/container_test.go b/info/container_test.go index b2bbb524..2ba9e9cd 100644 --- a/info/container_test.go +++ b/info/container_test.go @@ -261,7 +261,7 @@ func TestAddSampleWrongCpuUsage(t *testing.T) { current.Memory.Usage = memCurrentUsage current.Timestamp = prev.Timestamp.Add(1 * time.Second) - sample, err := NewSample(current, prev) + sample, err := NewSample(prev, current) if err == nil { t.Errorf("generated an unexpected sample: %+v", sample) } From b3ec141f90fe97ad8d8d30da36cebb0c880c0ead Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 13:20:13 -0700 Subject: [PATCH 14/27] comment for intZipuint64 --- info/container.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/info/container.go b/info/container.go index c8b2ebcf..7cff0e32 100644 --- a/info/container.go +++ b/info/container.go @@ -247,6 +247,8 @@ func (self uint64Slice) Percentiles(ps ...int) []uint64 { return ret } +// as contains percentages and bs containes the corresponding values. +// This function will pair values in as and bs to construct a list of percentile. // len(bs) <= len(as) func intZipuint64(as []int, bs []uint64) []percentile { if len(bs) == 0 { From f9b016dbe7a8b11d31ad941ba148545803bfde1c Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 13:22:15 -0700 Subject: [PATCH 15/27] change var names --- info/container_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/info/container_test.go b/info/container_test.go index 2ba9e9cd..79da78c8 100644 --- a/info/container_test.go +++ b/info/container_test.go @@ -70,14 +70,14 @@ func TestPercentiles(t *testing.T) { for i := 0; i < N; i++ { data[i] = uint64(i) } - ps := []int{ + percentages := []int{ 80, 90, 50, } - ss := uint64Slice(data).Percentiles(ps...) - for i, s := range ss { - p := ps[i] + percentiles := uint64Slice(data).Percentiles(percentages...) + for i, s := range percentiles { + p := percentages[i] d := uint64(N * p / 100) if d != s { t.Errorf("%v percentile data should be %v, but got %v", p, d, s) From c037cc70ca3d7fea7f0571f081949eda91f9a7f3 Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 13:33:23 -0700 Subject: [PATCH 16/27] set timestamp by default. This behavior cannot be changed by users --- container/statssum.go | 18 ++++++++++-------- container/statssum_test.go | 7 +++++++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/container/statssum.go b/container/statssum.go index f4cf227e..e8e6b3a8 100644 --- a/container/statssum.go +++ b/container/statssum.go @@ -23,12 +23,13 @@ import ( ) type statsSummaryContainerHandlerWrapper struct { - handler ContainerHandler - currentSummary *info.ContainerStatsPercentiles - prevStats *info.ContainerStats - numStats uint64 - sampler sampling.Sampler - lock sync.Mutex + handler ContainerHandler + currentSummary *info.ContainerStatsPercentiles + prevStats *info.ContainerStats + numStats uint64 + sampler sampling.Sampler + dontSetTimestamp bool + lock sync.Mutex } func (self *statsSummaryContainerHandlerWrapper) GetSpec() (*info.ContainerSpec, error) { @@ -65,8 +66,9 @@ func (self *statsSummaryContainerHandlerWrapper) GetStats() (*info.ContainerStat if stats == nil { return nil, nil } - // Only update timestamp if it is zero. - if stats.Timestamp.IsZero() { + // update timestamp if it is required. This feature is for testibility. + // In some test, we want the underlying handler to set timestamp. + if !self.dontSetTimestamp { stats.Timestamp = time.Now() } self.lock.Lock() diff --git a/container/statssum_test.go b/container/statssum_test.go index 9a91e21e..325cad11 100644 --- a/container/statssum_test.go +++ b/container/statssum_test.go @@ -164,6 +164,13 @@ func TestSampleCpuUsage(t *testing.T) { t.Error(err) } + // we want to set our own time. + if w, ok := handler.(*statsSummaryContainerHandlerWrapper); ok { + w.dontSetTimestamp = true + } else { + t.Fatal("handler is not an instance of statsSummaryContainerHandlerWrapper") + } + // request stats/obervation N+1 times, so that there will be N samples for i := 0; i < N+1; i++ { _, err = handler.GetStats() From e91283c262535e816c9eef6dbaae2813de9d626e Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 13:43:31 -0700 Subject: [PATCH 17/27] use memory/cpu trace instead of random numbers --- container/statssum_test.go | 46 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/container/statssum_test.go b/container/statssum_test.go index 325cad11..73169cf4 100644 --- a/container/statssum_test.go +++ b/container/statssum_test.go @@ -51,22 +51,15 @@ func (self *notARealContainer) ListProcesses(listType ListType) ([]int, error) { return nil, nil } -type randomMemoryUsageContainer struct { - NoStatsSummary - notARealContainer -} - -func (self *randomMemoryUsageContainer) GetStats() (*info.ContainerStats, error) { - stats := new(info.ContainerStats) - stats.Cpu = new(info.CpuStats) - stats.Memory = new(info.MemoryStats) - stats.Memory.Usage = uint64(rand.Intn(2048)) - return stats, nil -} - func TestMaxMemoryUsage(t *testing.T) { + N := 100 + memTrace := make([]uint64, N) + for i := 0; i < N; i++ { + memTrace[i] = uint64(i + 1) + } handler, err := AddStatsSummary( - &randomMemoryUsageContainer{}, + // &randomMemoryUsageContainer{}, + containerWithTrace(1*time.Second, nil, memTrace), &StatsParameter{ Sampler: "uniform", NumSamples: 10, @@ -76,7 +69,6 @@ func TestMaxMemoryUsage(t *testing.T) { t.Error(err) } var maxUsage uint64 - N := 100 for i := 0; i < N; i++ { stats, err := handler.GetStats() if err != nil { @@ -96,29 +88,34 @@ func TestMaxMemoryUsage(t *testing.T) { } } -type replayCpuTrace struct { +type replayTrace struct { NoStatsSummary notARealContainer cpuTrace []uint64 + memTrace []uint64 totalUsage uint64 currenttime time.Time duration time.Duration lock sync.Mutex } -func containerWithCpuTrace(duration time.Duration, cpuUsages ...uint64) ContainerHandler { - return &replayCpuTrace{ +func containerWithTrace(duration time.Duration, cpuUsages []uint64, memUsages []uint64) ContainerHandler { + return &replayTrace{ duration: duration, cpuTrace: cpuUsages, + memTrace: memUsages, currenttime: time.Now(), } } -func (self *replayCpuTrace) GetStats() (*info.ContainerStats, error) { +func (self *replayTrace) GetStats() (*info.ContainerStats, error) { stats := new(info.ContainerStats) stats.Cpu = new(info.CpuStats) stats.Memory = new(info.MemoryStats) - stats.Memory.Usage = uint64(rand.Intn(2048)) + if len(self.memTrace) > 0 { + stats.Memory.Usage = self.memTrace[0] + self.memTrace = self.memTrace[1:] + } self.lock.Lock() defer self.lock.Unlock() @@ -142,17 +139,20 @@ func TestSampleCpuUsage(t *testing.T) { // Number of samples N := 10 cpuTrace := make([]uint64, 0, N) + memTrace := make([]uint64, 0, N) // We need N+1 observations to get N samples for i := 0; i < N+1; i++ { - usage := uint64(rand.Intn(1000)) - cpuTrace = append(cpuTrace, usage) + cpuusage := uint64(rand.Intn(1000)) + memusage := uint64(rand.Intn(1000)) + cpuTrace = append(cpuTrace, cpuusage) + memTrace = append(memTrace, memusage) } samplePeriod := 1 * time.Second handler, err := AddStatsSummary( - containerWithCpuTrace(samplePeriod, cpuTrace...), + containerWithTrace(samplePeriod, cpuTrace, memTrace), &StatsParameter{ // Use uniform sampler with sample size of N, so that // we will be guaranteed to store the first N samples. From 4b872741b739fb552e84b41999c406109a27f9e9 Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 13:45:11 -0700 Subject: [PATCH 18/27] nil in a wrong place --- info/container_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info/container_test.go b/info/container_test.go index 79da78c8..be36e69f 100644 --- a/info/container_test.go +++ b/info/container_test.go @@ -101,7 +101,7 @@ func TestNewSampleNilStats(t *testing.T) { t.Errorf("generated an unexpected sample: %+v", sample) } - sample, err = NewSample(nil, stats) + sample, err = NewSample(stats, nil) if err == nil { t.Errorf("generated an unexpected sample: %+v", sample) } From d8901800b87a9efb0c9535fb3af0b2ad96658d91 Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 14:07:12 -0700 Subject: [PATCH 19/27] percentile --- info/container.go | 44 +++++++++++++----------------------------- info/container_test.go | 19 +++++++++++------- 2 files changed, 25 insertions(+), 38 deletions(-) diff --git a/info/container.go b/info/container.go index 7cff0e32..16e47442 100644 --- a/info/container.go +++ b/info/container.go @@ -230,37 +230,21 @@ func (self uint64Slice) Swap(i, j int) { self[i], self[j] = self[j], self[i] } -func (self uint64Slice) Percentiles(ps ...int) []uint64 { +func (self uint64Slice) Percentiles(requestedPercentiles ...int) []percentile { if len(self) == 0 { return nil } - ret := make([]uint64, 0, len(ps)) + ret := make([]percentile, 0, len(requestedPercentiles)) sort.Sort(self) - for _, p := range ps { - idx := (float64(p) / 100.0) * float64(len(self)+1) - if idx > float64(len(self)-1) { - ret = append(ret, self[len(self)-1]) - } else { - ret = append(ret, self[int(idx)]) - } - } - return ret -} - -// as contains percentages and bs containes the corresponding values. -// This function will pair values in as and bs to construct a list of percentile. -// len(bs) <= len(as) -func intZipuint64(as []int, bs []uint64) []percentile { - if len(bs) == 0 { - return nil - } - ret := make([]percentile, len(bs)) - for i, b := range bs { - a := as[i] - ret[i] = percentile{ - Percentage: a, - Value: b, - } + for _, p := range requestedPercentiles { + idx := (len(self) * p / 100) - 1 + ret = append( + ret, + percentile{ + Percentage: p, + Value: self[idx], + }, + ) } return ret } @@ -280,8 +264,6 @@ func (self *ContainerStatsPercentiles) FillPercentiles(cpuPercentages, memoryPer memUsages = append(memUsages, sample.Memory.Usage) } - cpuPercentiles := uint64Slice(cpuUsages).Percentiles(cpuPercentages...) - memPercentiles := uint64Slice(memUsages).Percentiles(memoryPercentages...) - self.CpuUsagePercentiles = intZipuint64(cpuPercentages, cpuPercentiles) - self.MemoryUsagePercentiles = intZipuint64(memoryPercentages, memPercentiles) + self.CpuUsagePercentiles = uint64Slice(cpuUsages).Percentiles(cpuPercentages...) + self.MemoryUsagePercentiles = uint64Slice(memUsages).Percentiles(memoryPercentages...) } diff --git a/info/container_test.go b/info/container_test.go index be36e69f..8735ef24 100644 --- a/info/container_test.go +++ b/info/container_test.go @@ -67,8 +67,8 @@ func TestPercentiles(t *testing.T) { N := 100 data := make([]uint64, N) - for i := 0; i < N; i++ { - data[i] = uint64(i) + for i := 1; i < N+1; i++ { + data[i-1] = uint64(i) } percentages := []int{ 80, @@ -76,12 +76,17 @@ func TestPercentiles(t *testing.T) { 50, } percentiles := uint64Slice(data).Percentiles(percentages...) - for i, s := range percentiles { - p := percentages[i] - d := uint64(N * p / 100) - if d != s { - t.Errorf("%v percentile data should be %v, but got %v", p, d, s) + for _, s := range percentiles { + if s.Value != uint64(s.Percentage) { + t.Errorf("%v percentile data should be %v, but got %v", s.Percentage, s.Percentage, s.Value) } + /* + p := percentages[i] + d := uint64(N * p / 100) + if d != s { + t.Errorf("%v percentile data should be %v, but got %v", p, d, s) + } + */ } } From b3a6ec2c8244d70fd06a31741486d214d6c0c2ed Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 14:13:02 -0700 Subject: [PATCH 20/27] put boilerplate into a function --- info/container_test.go | 107 ++++++++++------------------------------- 1 file changed, 26 insertions(+), 81 deletions(-) diff --git a/info/container_test.go b/info/container_test.go index 8735ef24..fde9ab41 100644 --- a/info/container_test.go +++ b/info/container_test.go @@ -112,31 +112,28 @@ func TestNewSampleNilStats(t *testing.T) { } } +func createStats(cpuUsage, memUsage uint64, timestamp time.Time) *ContainerStats { + stats := &ContainerStats{ + Cpu: &CpuStats{}, + Memory: &MemoryStats{}, + } + stats.Cpu.Usage.PerCpu = []uint64{cpuUsage} + stats.Cpu.Usage.Total = cpuUsage + stats.Cpu.Usage.System = 0 + stats.Cpu.Usage.User = cpuUsage + stats.Memory.Usage = memUsage + stats.Timestamp = timestamp + return stats +} + func TestAddSample(t *testing.T) { cpuPrevUsage := uint64(10) cpuCurrentUsage := uint64(15) memCurrentUsage := uint64(200) + prevTime := time.Now() - prev := &ContainerStats{ - Cpu: &CpuStats{}, - Memory: &MemoryStats{}, - } - prev.Cpu.Usage.PerCpu = []uint64{cpuPrevUsage} - prev.Cpu.Usage.Total = cpuPrevUsage - prev.Cpu.Usage.System = 0 - prev.Cpu.Usage.User = cpuPrevUsage - prev.Timestamp = time.Now() - - current := &ContainerStats{ - Cpu: &CpuStats{}, - Memory: &MemoryStats{}, - } - current.Cpu.Usage.PerCpu = []uint64{cpuCurrentUsage} - current.Cpu.Usage.Total = cpuCurrentUsage - current.Cpu.Usage.System = 0 - current.Cpu.Usage.User = cpuCurrentUsage - current.Memory.Usage = memCurrentUsage - current.Timestamp = prev.Timestamp.Add(1 * time.Second) + prev := createStats(cpuPrevUsage, memCurrentUsage, prevTime) + current := createStats(cpuCurrentUsage, memCurrentUsage, prevTime.Add(1*time.Second)) sample, err := NewSample(prev, current) if err != nil { @@ -159,28 +156,10 @@ func TestAddSampleIncompleteStats(t *testing.T) { cpuPrevUsage := uint64(10) cpuCurrentUsage := uint64(15) memCurrentUsage := uint64(200) + prevTime := time.Now() - prev := &ContainerStats{ - Cpu: &CpuStats{}, - Memory: &MemoryStats{}, - } - prev.Cpu.Usage.PerCpu = []uint64{cpuPrevUsage} - prev.Cpu.Usage.Total = cpuPrevUsage - prev.Cpu.Usage.System = 0 - prev.Cpu.Usage.User = cpuPrevUsage - prev.Timestamp = time.Now() - - current := &ContainerStats{ - Cpu: &CpuStats{}, - Memory: &MemoryStats{}, - } - current.Cpu.Usage.PerCpu = []uint64{cpuCurrentUsage} - current.Cpu.Usage.Total = cpuCurrentUsage - current.Cpu.Usage.System = 0 - current.Cpu.Usage.User = cpuCurrentUsage - current.Memory.Usage = memCurrentUsage - current.Timestamp = prev.Timestamp.Add(1 * time.Second) - + prev := createStats(cpuPrevUsage, memCurrentUsage, prevTime) + current := createStats(cpuCurrentUsage, memCurrentUsage, prevTime.Add(1*time.Second)) stats := &ContainerStats{ Cpu: prev.Cpu, Memory: nil, @@ -212,27 +191,10 @@ func TestAddSampleWrongOrder(t *testing.T) { cpuPrevUsage := uint64(10) cpuCurrentUsage := uint64(15) memCurrentUsage := uint64(200) + prevTime := time.Now() - prev := &ContainerStats{ - Cpu: &CpuStats{}, - Memory: &MemoryStats{}, - } - prev.Cpu.Usage.PerCpu = []uint64{cpuPrevUsage} - prev.Cpu.Usage.Total = cpuPrevUsage - prev.Cpu.Usage.System = 0 - prev.Cpu.Usage.User = cpuPrevUsage - prev.Timestamp = time.Now() - - current := &ContainerStats{ - Cpu: &CpuStats{}, - Memory: &MemoryStats{}, - } - current.Cpu.Usage.PerCpu = []uint64{cpuCurrentUsage} - current.Cpu.Usage.Total = cpuCurrentUsage - current.Cpu.Usage.System = 0 - current.Cpu.Usage.User = cpuCurrentUsage - current.Memory.Usage = memCurrentUsage - current.Timestamp = prev.Timestamp.Add(1 * time.Second) + prev := createStats(cpuPrevUsage, memCurrentUsage, prevTime) + current := createStats(cpuCurrentUsage, memCurrentUsage, prevTime.Add(1*time.Second)) sample, err := NewSample(current, prev) if err == nil { @@ -244,27 +206,10 @@ func TestAddSampleWrongCpuUsage(t *testing.T) { cpuPrevUsage := uint64(15) cpuCurrentUsage := uint64(10) memCurrentUsage := uint64(200) + prevTime := time.Now() - prev := &ContainerStats{ - Cpu: &CpuStats{}, - Memory: &MemoryStats{}, - } - prev.Cpu.Usage.PerCpu = []uint64{cpuPrevUsage} - prev.Cpu.Usage.Total = cpuPrevUsage - prev.Cpu.Usage.System = 0 - prev.Cpu.Usage.User = cpuPrevUsage - prev.Timestamp = time.Now() - - current := &ContainerStats{ - Cpu: &CpuStats{}, - Memory: &MemoryStats{}, - } - current.Cpu.Usage.PerCpu = []uint64{cpuCurrentUsage} - current.Cpu.Usage.Total = cpuCurrentUsage - current.Cpu.Usage.System = 0 - current.Cpu.Usage.User = cpuCurrentUsage - current.Memory.Usage = memCurrentUsage - current.Timestamp = prev.Timestamp.Add(1 * time.Second) + prev := createStats(cpuPrevUsage, memCurrentUsage, prevTime) + current := createStats(cpuCurrentUsage, memCurrentUsage, prevTime.Add(1*time.Second)) sample, err := NewSample(prev, current) if err == nil { From 694f538c7b377dd177bc2e80869c7b1e540b6638 Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 14:21:04 -0700 Subject: [PATCH 21/27] zero timestamp is an error --- container/statssum.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/container/statssum.go b/container/statssum.go index e8e6b3a8..9bad1438 100644 --- a/container/statssum.go +++ b/container/statssum.go @@ -15,6 +15,7 @@ package container import ( + "fmt" "sync" "time" @@ -64,12 +65,10 @@ func (self *statsSummaryContainerHandlerWrapper) GetStats() (*info.ContainerStat return nil, err } if stats == nil { - return nil, nil + return nil, fmt.Errorf("container handler returns a nil error and a nil stats") } - // update timestamp if it is required. This feature is for testibility. - // In some test, we want the underlying handler to set timestamp. - if !self.dontSetTimestamp { - stats.Timestamp = time.Now() + if stats.Timestamp.IsZero() { + return nil, fmt.Errorf("container handler did not set timestamp") } self.lock.Lock() defer self.lock.Unlock() From 4728ee80fa0ddaaaa58deba3d37228662fd0b2f0 Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 14:24:04 -0700 Subject: [PATCH 22/27] incomplete stats is an error --- container/statssum.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/container/statssum.go b/container/statssum.go index 9bad1438..8254b968 100644 --- a/container/statssum.go +++ b/container/statssum.go @@ -73,9 +73,14 @@ func (self *statsSummaryContainerHandlerWrapper) GetStats() (*info.ContainerStat self.lock.Lock() defer self.lock.Unlock() - sample, _ := info.NewSample(self.prevStats, stats) - if sample != nil { - self.sampler.Update(sample) + if self.prevStats != nil { + sample, err := info.NewSample(self.prevStats, stats) + if err != nil { + return nil, fmt.Errorf("wrong stats: %v", err) + } + if sample != nil { + self.sampler.Update(sample) + } } self.updatePrevStats(stats) if self.currentSummary == nil { From caafb0d7e3fea4fb3727e2dab76ced0cd66dc3de Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 14:30:44 -0700 Subject: [PATCH 23/27] rename --- container/statssum.go | 52 +++++++++++++++++++------------------- container/statssum_test.go | 14 +++++----- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/container/statssum.go b/container/statssum.go index 8254b968..f98439bd 100644 --- a/container/statssum.go +++ b/container/statssum.go @@ -23,21 +23,21 @@ import ( "github.com/google/cadvisor/sampling" ) -type statsSummaryContainerHandlerWrapper struct { - handler ContainerHandler - currentSummary *info.ContainerStatsPercentiles - prevStats *info.ContainerStats - numStats uint64 - sampler sampling.Sampler - dontSetTimestamp bool - lock sync.Mutex +type percentilesContainerHandlerWrapper struct { + handler ContainerHandler + containerPercentiles *info.ContainerStatsPercentiles + prevStats *info.ContainerStats + numStats uint64 + sampler sampling.Sampler + dontSetTimestamp bool + lock sync.Mutex } -func (self *statsSummaryContainerHandlerWrapper) GetSpec() (*info.ContainerSpec, error) { +func (self *percentilesContainerHandlerWrapper) GetSpec() (*info.ContainerSpec, error) { return self.handler.GetSpec() } -func (self *statsSummaryContainerHandlerWrapper) updatePrevStats(stats *info.ContainerStats) { +func (self *percentilesContainerHandlerWrapper) updatePrevStats(stats *info.ContainerStats) { if stats == nil || stats.Cpu == nil || stats.Memory == nil { // discard incomplete stats self.prevStats = nil @@ -59,7 +59,7 @@ func (self *statsSummaryContainerHandlerWrapper) updatePrevStats(stats *info.Con *self.prevStats.Memory = *stats.Memory } -func (self *statsSummaryContainerHandlerWrapper) GetStats() (*info.ContainerStats, error) { +func (self *percentilesContainerHandlerWrapper) GetStats() (*info.ContainerStats, error) { stats, err := self.handler.GetStats() if err != nil { return nil, err @@ -83,31 +83,31 @@ func (self *statsSummaryContainerHandlerWrapper) GetStats() (*info.ContainerStat } } self.updatePrevStats(stats) - if self.currentSummary == nil { - self.currentSummary = new(info.ContainerStatsPercentiles) + if self.containerPercentiles == nil { + self.containerPercentiles = new(info.ContainerStatsPercentiles) } self.numStats++ if stats.Memory != nil { - if stats.Memory.Usage > self.currentSummary.MaxMemoryUsage { - self.currentSummary.MaxMemoryUsage = stats.Memory.Usage + if stats.Memory.Usage > self.containerPercentiles.MaxMemoryUsage { + self.containerPercentiles.MaxMemoryUsage = stats.Memory.Usage } } return stats, nil } -func (self *statsSummaryContainerHandlerWrapper) ListContainers(listType ListType) ([]string, error) { +func (self *percentilesContainerHandlerWrapper) ListContainers(listType ListType) ([]string, error) { return self.handler.ListContainers(listType) } -func (self *statsSummaryContainerHandlerWrapper) ListThreads(listType ListType) ([]int, error) { +func (self *percentilesContainerHandlerWrapper) ListThreads(listType ListType) ([]int, error) { return self.handler.ListThreads(listType) } -func (self *statsSummaryContainerHandlerWrapper) ListProcesses(listType ListType) ([]int, error) { +func (self *percentilesContainerHandlerWrapper) ListProcesses(listType ListType) ([]int, error) { return self.handler.ListProcesses(listType) } -func (self *statsSummaryContainerHandlerWrapper) StatsSummary() (*info.ContainerStatsPercentiles, error) { +func (self *percentilesContainerHandlerWrapper) StatsSummary() (*info.ContainerStatsPercentiles, error) { self.lock.Lock() defer self.lock.Unlock() samples := make([]*info.ContainerStatsSample, 0, self.sampler.Len()) @@ -115,13 +115,13 @@ func (self *statsSummaryContainerHandlerWrapper) StatsSummary() (*info.Container stats := d.(*info.ContainerStatsSample) samples = append(samples, stats) }) - self.currentSummary.Samples = samples + self.containerPercentiles.Samples = samples // XXX(dengnan): propabily add to StatsParameter? - self.currentSummary.FillPercentiles( + self.containerPercentiles.FillPercentiles( []int{50, 80, 90, 95, 99}, []int{50, 80, 90, 95, 99}, ) - return self.currentSummary, nil + return self.containerPercentiles, nil } type StatsParameter struct { @@ -136,9 +136,9 @@ func AddStatsSummary(handler ContainerHandler, parameter *StatsParameter) (Conta if err != nil { return nil, err } - return &statsSummaryContainerHandlerWrapper{ - handler: handler, - currentSummary: &info.ContainerStatsPercentiles{}, - sampler: sampler, + return &percentilesContainerHandlerWrapper{ + handler: handler, + containerPercentiles: &info.ContainerStatsPercentiles{}, + sampler: sampler, }, nil } diff --git a/container/statssum_test.go b/container/statssum_test.go index 73169cf4..7c07294d 100644 --- a/container/statssum_test.go +++ b/container/statssum_test.go @@ -33,21 +33,21 @@ func init() { rand.Seed(seed) } -type notARealContainer struct { +type mockContainer struct { } -func (self *notARealContainer) GetSpec() (*info.ContainerSpec, error) { +func (self *mockContainer) GetSpec() (*info.ContainerSpec, error) { return nil, nil } -func (self *notARealContainer) ListContainers(listType ListType) ([]string, error) { +func (self *mockContainer) ListContainers(listType ListType) ([]string, error) { return nil, nil } -func (self *notARealContainer) ListThreads(listType ListType) ([]int, error) { +func (self *mockContainer) ListThreads(listType ListType) ([]int, error) { return nil, nil } -func (self *notARealContainer) ListProcesses(listType ListType) ([]int, error) { +func (self *mockContainer) ListProcesses(listType ListType) ([]int, error) { return nil, nil } @@ -90,7 +90,7 @@ func TestMaxMemoryUsage(t *testing.T) { type replayTrace struct { NoStatsSummary - notARealContainer + mockContainer cpuTrace []uint64 memTrace []uint64 totalUsage uint64 @@ -165,7 +165,7 @@ func TestSampleCpuUsage(t *testing.T) { } // we want to set our own time. - if w, ok := handler.(*statsSummaryContainerHandlerWrapper); ok { + if w, ok := handler.(*percentilesContainerHandlerWrapper); ok { w.dontSetTimestamp = true } else { t.Fatal("handler is not an instance of statsSummaryContainerHandlerWrapper") From 33b2167434064ed8abdbf46bf2bc77f6351db3a1 Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 14:33:10 -0700 Subject: [PATCH 24/27] remove random --- container/statssum_test.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/container/statssum_test.go b/container/statssum_test.go index 7c07294d..657f584c 100644 --- a/container/statssum_test.go +++ b/container/statssum_test.go @@ -15,8 +15,6 @@ package container import ( - crand "crypto/rand" - "encoding/binary" "math/rand" "sync" "testing" @@ -25,14 +23,6 @@ import ( "github.com/google/cadvisor/info" ) -func init() { - // NOTE(dengnan): Even if we picked a good random seed, - // the random number from math/rand is still not cryptographically secure! - var seed int64 - binary.Read(crand.Reader, binary.LittleEndian, &seed) - rand.Seed(seed) -} - type mockContainer struct { } @@ -58,7 +48,6 @@ func TestMaxMemoryUsage(t *testing.T) { memTrace[i] = uint64(i + 1) } handler, err := AddStatsSummary( - // &randomMemoryUsageContainer{}, containerWithTrace(1*time.Second, nil, memTrace), &StatsParameter{ Sampler: "uniform", From 9eeb1c9fc28ea6a77c447241a51734da3a9510f8 Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 14:34:36 -0700 Subject: [PATCH 25/27] static values in unit test --- container/statssum_test.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/container/statssum_test.go b/container/statssum_test.go index 657f584c..8d5bd7e8 100644 --- a/container/statssum_test.go +++ b/container/statssum_test.go @@ -57,16 +57,13 @@ func TestMaxMemoryUsage(t *testing.T) { if err != nil { t.Error(err) } - var maxUsage uint64 + maxUsage := uint64(N) for i := 0; i < N; i++ { - stats, err := handler.GetStats() + _, err := handler.GetStats() if err != nil { t.Errorf("Error when get stats: %v", err) continue } - if stats.Memory.Usage > maxUsage { - maxUsage = stats.Memory.Usage - } } summary, err := handler.StatsSummary() if err != nil { From 39f20bc6a24f7985f06ed1a40f2c276d2a59263a Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 14:37:21 -0700 Subject: [PATCH 26/27] remove dontSetTimestamp --- container/statssum.go | 1 - container/statssum_test.go | 7 ------- 2 files changed, 8 deletions(-) diff --git a/container/statssum.go b/container/statssum.go index f98439bd..faa3c67d 100644 --- a/container/statssum.go +++ b/container/statssum.go @@ -29,7 +29,6 @@ type percentilesContainerHandlerWrapper struct { prevStats *info.ContainerStats numStats uint64 sampler sampling.Sampler - dontSetTimestamp bool lock sync.Mutex } diff --git a/container/statssum_test.go b/container/statssum_test.go index 8d5bd7e8..7385bd2b 100644 --- a/container/statssum_test.go +++ b/container/statssum_test.go @@ -150,13 +150,6 @@ func TestSampleCpuUsage(t *testing.T) { t.Error(err) } - // we want to set our own time. - if w, ok := handler.(*percentilesContainerHandlerWrapper); ok { - w.dontSetTimestamp = true - } else { - t.Fatal("handler is not an instance of statsSummaryContainerHandlerWrapper") - } - // request stats/obervation N+1 times, so that there will be N samples for i := 0; i < N+1; i++ { _, err = handler.GetStats() From 2f4e76b64819ca5888b9a8fc162b73e0b4edae4f Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Thu, 12 Jun 2014 14:39:33 -0700 Subject: [PATCH 27/27] rename --- manager/container.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manager/container.go b/manager/container.go index b167b049..be7eea1e 100644 --- a/manager/container.go +++ b/manager/container.go @@ -39,7 +39,7 @@ type containerInfo struct { Subcontainers []string Spec *info.ContainerSpec Stats *list.List - StatsSummary *info.ContainerStatsSummary + StatsSummary *info.ContainerStatsPercentiles } type containerData struct {