@@ -104,39 +104,51 @@ func Times(percpu bool) ([]TimesStat, error) {
104104}
105105
106106func TimesWithContext (_ context.Context , percpu bool ) ([]TimesStat , error ) {
107+ // Get the CPU performance counters per processor via Windows API
108+ stats , err := perfInfo ()
109+ if err != nil {
110+ return nil , err
111+ }
112+
107113 if percpu {
108- return perCPUTimes ()
114+ ret := make ([]TimesStat , 0 , len (stats ))
115+ for core , v := range stats {
116+ c := TimesStat {
117+ CPU : fmt .Sprintf ("cpu%d" , core ),
118+ User : float64 (v .UserTime ) / ClocksPerSec ,
119+ System : float64 (v .KernelTime - v .IdleTime ) / ClocksPerSec ,
120+ Idle : float64 (v .IdleTime ) / ClocksPerSec ,
121+ Irq : float64 (v .InterruptTime ) / ClocksPerSec ,
122+ }
123+ ret = append (ret , c )
124+ }
125+ return ret , nil
109126 }
110127
111- var ret []TimesStat
112- var lpIdleTime common.FILETIME
113- var lpKernelTime common.FILETIME
114- var lpUserTime common.FILETIME
115- // GetSystemTimes returns 0 for error, in which case we check err,
116- // see https://pkg.go.dev/golang.org/x/sys/windows#LazyProc.Call
117- r , _ , err := common .ProcGetSystemTimes .Call (
118- uintptr (unsafe .Pointer (& lpIdleTime )),
119- uintptr (unsafe .Pointer (& lpKernelTime )),
120- uintptr (unsafe .Pointer (& lpUserTime )))
121- if r == 0 {
122- return nil , err
128+ // Accumulate the times over all CPUs as GetSystemTimes() will only return
129+ // the counters for the current processor group. This causes issues for
130+ // machines with more than 64 logical processors when the current thread is
131+ // switched to another processor group as then the counters are not
132+ // monotonic anymore.
133+ var total win32_SystemProcessorPerformanceInformation
134+ for _ , v := range stats {
135+ total .IdleTime += v .IdleTime
136+ total .KernelTime += v .KernelTime
137+ total .UserTime += v .UserTime
138+ total .DpcTime += v .DpcTime
139+ total .InterruptTime += v .InterruptTime
140+ total .InterruptCount += v .InterruptCount
123141 }
124142
125- // Do all arithmetic on the integer tick counts and convert to float64
126- // only once, mirroring perCPUTimes. Converting each FILETIME half to
127- // float64 first loses precision on large counters and can make the
128- // returned values non-monotonic. See issue #2110.
129- idle := uint64 (lpIdleTime .DwHighDateTime )<< 32 | uint64 (lpIdleTime .DwLowDateTime )
130- user := uint64 (lpUserTime .DwHighDateTime )<< 32 | uint64 (lpUserTime .DwLowDateTime )
131- kernel := uint64 (lpKernelTime .DwHighDateTime )<< 32 | uint64 (lpKernelTime .DwLowDateTime )
132-
133- ret = append (ret , TimesStat {
134- CPU : "cpu-total" ,
135- Idle : float64 (idle ) / ClocksPerSec ,
136- User : float64 (user ) / ClocksPerSec ,
137- System : float64 (kernel - idle ) / ClocksPerSec , // kernel time includes idle time
138- })
139- return ret , nil
143+ return []TimesStat {
144+ {
145+ CPU : "cpu-total" ,
146+ User : float64 (total .UserTime ) / ClocksPerSec ,
147+ System : float64 (total .KernelTime - total .IdleTime ) / ClocksPerSec ,
148+ Idle : float64 (total .IdleTime ) / ClocksPerSec ,
149+ Irq : float64 (total .InterruptTime ) / ClocksPerSec ,
150+ },
151+ }, nil
140152}
141153
142154func Info () ([]InfoStat , error ) {
@@ -243,26 +255,6 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
243255 return ret , nil
244256}
245257
246- // perCPUTimes returns times stat per cpu, per core and overall for all CPUs
247- func perCPUTimes () ([]TimesStat , error ) {
248- var ret []TimesStat
249- stats , err := perfInfo ()
250- if err != nil {
251- return nil , err
252- }
253- for core , v := range stats {
254- c := TimesStat {
255- CPU : fmt .Sprintf ("cpu%d" , core ),
256- User : float64 (v .UserTime ) / ClocksPerSec ,
257- System : float64 (v .KernelTime - v .IdleTime ) / ClocksPerSec ,
258- Idle : float64 (v .IdleTime ) / ClocksPerSec ,
259- Irq : float64 (v .InterruptTime ) / ClocksPerSec ,
260- }
261- ret = append (ret , c )
262- }
263- return ret , nil
264- }
265-
266258// makes call to Windows API function to retrieve performance information for each core
267259func perfInfo () ([]win32_SystemProcessorPerformanceInformation , error ) {
268260 // On hosts with more than 64 logical CPUs Windows splits CPUs into Processor Groups
0 commit comments