diff --git a/calculator/cpu.go b/calculator/cpu.go index d41e807..ccef312 100644 --- a/calculator/cpu.go +++ b/calculator/cpu.go @@ -29,28 +29,33 @@ func (c CPUCalculatorImpl) PerCpuUsage() common.MapStr { if cap(c.New.PerCpuUsage) == cap(c.Old.PerCpuUsage) { output = common.MapStr{} for index := range c.New.PerCpuUsage { - output["cpu"+strconv.Itoa(index)] = c.calculateLoad(c.New.PerCpuUsage[index] - c.Old.PerCpuUsage[index]) + output["cpu"+strconv.Itoa(index)] = c.calculateLoad(c.New.PerCpuUsage[index], c.Old.PerCpuUsage[index]) } } return output } func (c CPUCalculatorImpl) TotalUsage() float64 { - return c.calculateLoad(c.New.TotalUsage - c.Old.TotalUsage) + return c.calculateLoad(c.New.TotalUsage, c.Old.TotalUsage) } func (c CPUCalculatorImpl) UsageInKernelmode() float64 { - return c.calculateLoad(c.New.UsageInKernelmode - c.Old.UsageInKernelmode) + return c.calculateLoad(c.New.UsageInKernelmode, c.Old.UsageInKernelmode) } func (c CPUCalculatorImpl) UsageInUsermode() float64 { - return c.calculateLoad(c.New.UsageInUsermode - c.Old.UsageInUsermode) + return c.calculateLoad(c.New.UsageInUsermode, c.Old.UsageInUsermode) } -func (c CPUCalculatorImpl) calculateLoad(value uint64) float64 { +func (c CPUCalculatorImpl) calculateLoad(oldValue uint64, newValue uint64) float64 { // value is the count of CPU nanosecond in 1sec // TODO save the old stat timestamp and reuse here in case of docker read time changes... // 1s = 1000000000 ns // value / 1000000000 + + value := int64(oldValue - newValue) + if value < 0 { + value = 0 + } return float64(value) / float64(1000000000) } diff --git a/calculator/cpu_test.go b/calculator/cpu_test.go index 5a24f86..3ff1b48 100644 --- a/calculator/cpu_test.go +++ b/calculator/cpu_test.go @@ -25,6 +25,25 @@ func TestCPUperCpuUsage(t *testing.T) { }, value) } +func TestCPUperCpuUsageAvoidMassiveValues(t *testing.T) { + // GIVEN + var oldData = CPUData{[]uint64{1, 2, 3, 4}, 0, 0, 0} + var newData = CPUData{[]uint64{0, 1, 2, 3}, 0, 0, 0} + var calculator = CPUCalculatorImpl{oldData, newData} + + // WHEN + value := calculator.PerCpuUsage() + + // THEN + // value should be 0%, 0%, 0% and 0% + assert.Equal(t, common.MapStr{ + "cpu0": float64(0.0), + "cpu1": float64(0.0), + "cpu2": float64(0.0), + "cpu3": float64(0.0), + }, value) +} + func TestCPUTotalUsage(t *testing.T) { // GIVEN var oldData = CPUData{nil, 50, 0, 0} @@ -39,6 +58,20 @@ func TestCPUTotalUsage(t *testing.T) { assert.Equal(t, 0.50, value) } +func TestCPUTotalUsageAvoidMassiveValues(t *testing.T) { + // GIVEN + var oldData = CPUData{nil, 55, 0, 0} + var newData = CPUData{nil, 5, 0, 0} + var calculator = CPUCalculatorImpl{oldData, newData} + + // WHEN + value := calculator.TotalUsage() + + // THEN + // value should be 0% + assert.Equal(t, 0.0, value) +} + func TestCPUUsageInKernelmode(t *testing.T) { // GIVEN var oldData = CPUData{nil, 0, 0, 0} @@ -53,15 +86,43 @@ func TestCPUUsageInKernelmode(t *testing.T) { assert.Equal(t, float64(0.80), value) } +func TestCPUUsageInKernelmodeAvoidMassiveValues(t *testing.T) { + // GIVEN + var oldData = CPUData{nil, 0, 1, 0} + var newData = CPUData{nil, 0, 0, 0} + var calculator = CPUCalculatorImpl{oldData, newData} + + // WHEN + value := calculator.UsageInKernelmode() + + // THEN + // value should be 0% + assert.Equal(t, float64(0.0), value) +} + func TestCPUUsageInUsermode(t *testing.T) { // GIVEN - var oldData = CPUData{nil, 0, 0, 800000000} + var oldData = CPUData{nil, 0, 0, 0} var newData = CPUData{nil, 0, 0, 800000000} var calculator = CPUCalculatorImpl{oldData, newData} // WHEN value := calculator.UsageInUsermode() + // THEN + // value should be 80% + assert.Equal(t, float64(0.8), value) +} + +func TestCPUUsageInUsermodeAvoidMassiveValues(t *testing.T) { + // GIVEN + var oldData = CPUData{nil, 0, 0, 1} + var newData = CPUData{nil, 0, 0, 0} + var calculator = CPUCalculatorImpl{oldData, newData} + + // WHEN + value := calculator.UsageInUsermode() + // THEN // value should be 0% assert.Equal(t, float64(0), value)