diff --git a/cpu/cpu_test.go b/cpu/cpu_test.go index 6082ffc..a756404 100644 --- a/cpu/cpu_test.go +++ b/cpu/cpu_test.go @@ -22,6 +22,44 @@ func TestCpu_times(t *testing.T) { t.Errorf("could not get CPU User: %v", vv) } } + + // test sum of per cpu stats is within margin of error for cpu total stats + cpuTotal, err := Times(false) + if err != nil { + t.Errorf("error %v", err) + } + if len(cpuTotal) == 0 { + t.Error("could not get CPUs ", err) + } + perCPU, err := Times(true) + if err != nil { + t.Errorf("error %v", err) + } + if len(perCPU) == 0 { + t.Error("could not get CPUs ", err) + } + var perCPUUserTimeSum float64 + var perCPUSystemTimeSum float64 + var perCPUIdleTimeSum float64 + for _, pc := range perCPU { + perCPUUserTimeSum += pc.User + perCPUSystemTimeSum += pc.System + perCPUIdleTimeSum += pc.Idle + } + margin := 2.0 + if !isWithinMargin(perCPUUserTimeSum, cpuTotal[0].User, margin) { + t.Errorf("perCPUUserTimeSum (%f) not within margin (%f) of cpuTotal (%f)", perCPUUserTimeSum, margin, cpuTotal[0].User) + } + if !isWithinMargin(perCPUSystemTimeSum, cpuTotal[0].System, margin) { + t.Errorf("perCPUSystemTimeSum (%f) not within margin (%f) of cpuTotal (%f)", perCPUSystemTimeSum, margin, cpuTotal[0].System) + } + if !isWithinMargin(perCPUIdleTimeSum, cpuTotal[0].Idle, margin) { + t.Errorf("perCPUIdleTimeSum (%f) not within margin (%f) of cpuTotal (%f)", perCPUIdleTimeSum, margin, cpuTotal[0].Idle) + } +} + +func isWithinMargin(n, source, margin float64) bool { + return n >= source-margin && n <= source+margin } func TestCpu_counts(t *testing.T) { diff --git a/cpu/cpu_windows.go b/cpu/cpu_windows.go index ad31bd1..9ea3ac0 100644 --- a/cpu/cpu_windows.go +++ b/cpu/cpu_windows.go @@ -23,21 +23,6 @@ type Win32_Processor struct { MaxClockSpeed uint32 } -// win32_PerfFormattedData_Counters_ProcessorInformation stores instance value of the perf counters -type win32_PerfFormattedData_Counters_ProcessorInformation struct { - Name string - PercentDPCTime uint64 - PercentIdleTime uint64 - PercentUserTime uint64 - PercentProcessorTime uint64 - PercentInterruptTime uint64 - PercentPriorityTime uint64 - PercentPrivilegedTime uint64 - InterruptsPerSec uint32 - ProcessorFrequency uint32 - DPCRate uint32 -} - type win32_PerfRawData_Counters_ProcessorInformation struct { Name string PercentDPCTime uint64 @@ -58,6 +43,10 @@ type Win32_PerfFormattedData_PerfOS_System struct { ProcessorQueueLength uint32 } +const ( + win32_TicksPerSecond = 10000000.0 +) + // Times returns times stat per cpu and combined for all CPUs func Times(percpu bool) ([]TimesStat, error) { return TimesWithContext(context.Background(), percpu) @@ -135,7 +124,6 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) { // Name property is the key by which overall, per cpu and per core metric is known. func perfInfoWithContext(ctx context.Context) ([]win32_PerfRawData_Counters_ProcessorInformation, error) { var ret []win32_PerfRawData_Counters_ProcessorInformation - // Win32_PerfRawData_Counters_ProcessorInformation q := wmi.CreateQuery(&ret, "WHERE NOT Name LIKE '%_Total'") err := common.WMIQueryWithContext(ctx, q, &ret) @@ -172,10 +160,10 @@ func perCPUTimesWithContext(ctx context.Context) ([]TimesStat, error) { for _, v := range stats { c := TimesStat{ CPU: v.Name, - User: float64(v.PercentUserTime), - System: float64(v.PercentPrivilegedTime), - Idle: float64(v.PercentIdleTime), - Irq: float64(v.PercentInterruptTime), + User: float64(v.PercentUserTime) / win32_TicksPerSecond, + System: float64(v.PercentPrivilegedTime) / win32_TicksPerSecond, + Idle: float64(v.PercentIdleTime) / win32_TicksPerSecond, + Irq: float64(v.PercentInterruptTime) / win32_TicksPerSecond, } ret = append(ret, c) }