From 478ff3d45e951124ffabde7ddd733a8bd9affd70 Mon Sep 17 00:00:00 2001 From: Nan Deng Date: Wed, 11 Jun 2014 13:22:08 -0700 Subject: [PATCH] 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 }