changeset 2623:65fcc9af086c

Windows CPU Usage fix this patch adds support for Windows to use WMI to read CPU usage %. Reviewed-by: sgehwolf Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-April/022678.html
author Simon Tooke <stooke@redhat.com>
date Thu, 13 Apr 2017 23:25:41 -0400
parents e4f6006877d1
children 92612114d1ce
files common/portability/Makefile common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableHost.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxPortableHostImpl.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSHostImpl.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsHelperImpl.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsPortableHostImpl.java common/portability/src/main/native/WindowsHelperImpl.c host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/CpuStatBuilder.java host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/CpuStatBuilderFactory.java host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/HostCpuBackend.java host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/LinuxCpuStatBuilder.java host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/WindowsCpuStatBuilder.java host-cpu/agent/src/test/java/com/redhat/thermostat/host/cpu/agent/internal/CpuStatBuilderFactoryTest.java host-cpu/agent/src/test/java/com/redhat/thermostat/host/cpu/agent/internal/CpuStatBuilderTest.java host-cpu/agent/src/test/java/com/redhat/thermostat/host/cpu/agent/internal/LinuxCpuStatBuilderTest.java host-cpu/agent/src/test/java/com/redhat/thermostat/host/cpu/agent/internal/WindowsCpuStatBuilderTest.java
diffstat 16 files changed, 777 insertions(+), 248 deletions(-) [+]
line wrap: on
line diff
--- a/common/portability/Makefile	Mon Mar 20 14:09:36 2017 -0400
+++ b/common/portability/Makefile	Thu Apr 13 23:25:41 2017 -0400
@@ -57,7 +57,7 @@
 
 ifeq ($(JNI_PLATFORM),win32)
     JNI_LIST     +=  com.redhat.thermostat.common.portability.internal.windows.WindowsHelperImpl
-    HELPER_LIBS  += -l psapi -l Netapi32
+    HELPER_LIBS  += -l psapi -l Netapi32 -l ole32 -l oleaut32 -l wbemuuid -o wmi
 endif
 
 ifeq ($(JNI_PLATFORM),darwin)
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableHost.java	Mon Mar 20 14:09:36 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableHost.java	Thu Apr 13 23:25:41 2017 -0400
@@ -53,4 +53,14 @@
     long getClockTicksPerSecond();
 
     PortableMemoryStat getMemoryStat();
+
+    static final int CPU_TIMES_SIZE = 3;
+
+    // returns an array (one row per CPU) of an array of ints (idle, system and user ticks)
+    long[][] getCPUUsageTicks();
+
+    // returns an array (one row per logical CPU) of an array of ints (idle, system and user percent)
+    // the order of the CPUs is (from outer counter to inner) group, package, chip, core, hyperthread
+    int[][] getCPUUsagePercent();
+
 }
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxPortableHostImpl.java	Mon Mar 20 14:09:36 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxPortableHostImpl.java	Thu Apr 13 23:25:41 2017 -0400
@@ -207,6 +207,17 @@
         throw new UnimplementedError("getMemoryStat()");
     }
 
+    @Override
+    public long[][] getCPUUsageTicks() {
+        // see LinuxCpuStatBuilder class in the host-cpu plugin
+        throw new UnimplementedError("getCPUUsageTicks()");
+    }
+
+    @Override
+    public int[][] getCPUUsagePercent() {
+        throw new UnimplementedError("getCPUUsagePercent()");
+    }
+
     String getHostName(InetAddress localAddress) {
         String hostname = localAddress.getCanonicalHostName();
         logger.log(Level.FINEST, "hostname: " + hostname);
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSHostImpl.java	Mon Mar 20 14:09:36 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSHostImpl.java	Thu Apr 13 23:25:41 2017 -0400
@@ -38,6 +38,7 @@
 
 import com.redhat.thermostat.common.portability.PortableHost;
 import com.redhat.thermostat.common.portability.PortableMemoryStat;
+import com.redhat.thermostat.common.portability.internal.UnimplementedError;
 
 public class MacOSHostImpl implements PortableHost {
 
@@ -86,4 +87,14 @@
     public PortableMemoryStat getMemoryStat() {
         return new MacOSMemoryStat();
     }
+
+    @Override
+    public long[][] getCPUUsageTicks() {
+        throw new UnimplementedError("getCPUUsageTicks()");
+    }
+
+    @Override
+    public int[][] getCPUUsagePercent() {
+        throw new UnimplementedError("getCPUUsagePercent()");
+    }
 }
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsHelperImpl.java	Mon Mar 20 14:09:36 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsHelperImpl.java	Thu Apr 13 23:25:41 2017 -0400
@@ -44,6 +44,8 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import static com.redhat.thermostat.common.portability.PortableHost.CPU_TIMES_SIZE;
+
 /**
  * Utility class to access Windows native code
  */
@@ -259,6 +261,32 @@
         return info;
     }
 
+    /**
+     * getSystemTimes()
+     * This returns consolidated ticks over all CPUs
+     * @return long[3] array of idle, system and user times (in ticks)
+     */
+    long[] getSystemTimes() {
+        final long times[] = new long[CPU_TIMES_SIZE];
+        getSystemTimes0(times);
+        return times;
+    }
+
+    /**
+     * getCPUUsagePercent()
+     * @return array (each row is a logical processor) of array (idle/system/uyser percents)
+     */
+    int[][] getCPUUsagePercent() {
+        final int numProcessors = getCPUCount();
+        final int[][] procs = new int[numProcessors][];
+        for (int i=0; i<numProcessors; i++) {
+            final int times[] = new int[CPU_TIMES_SIZE];
+            procs[i] = times;
+        }
+        getCPUUsagePercent0(procs);
+        return procs;
+    }
+
     public int getLastError() {
         return getLastError0();
     }
@@ -288,7 +316,10 @@
     private static native boolean getGlobalMemoryStatus0(long[] info);
     private static native String getCPUString0();
     private static native int getCPUCount0();
+    private static native int getCPUUsagePercent0(int[][] percents);
+
     private static native long queryPerformanceFrequency0();
+    private static native void getSystemTimes0(long[] times); // array is idle, kernel, user times
 
     private static native int getLastError0();
 
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsPortableHostImpl.java	Mon Mar 20 14:09:36 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsPortableHostImpl.java	Thu Apr 13 23:25:41 2017 -0400
@@ -88,4 +88,19 @@
     public PortableMemoryStat getMemoryStat() {
         return new WindowsMemoryStat();
     }
+
+    @Override
+    public long[][] getCPUUsageTicks() {
+        // ideally, we want to return an array (one per logical CPU) of (an array of idle/system/user ticks)
+        // unfortunateltly, Windows GetSystemTimes() returns the totals over all CPUs
+        // we return an array length 1 to represent this, instead of one element per CPU
+        final long[][] procs = new long[1][];
+        procs[0] = helper.getSystemTimes();
+        return procs;
+    }
+
+    @Override
+    public int[][] getCPUUsagePercent() {
+        return helper.getCPUUsagePercent();
+    }
 }
--- a/common/portability/src/main/native/WindowsHelperImpl.c	Mon Mar 20 14:09:36 2017 -0400
+++ b/common/portability/src/main/native/WindowsHelperImpl.c	Thu Apr 13 23:25:41 2017 -0400
@@ -38,17 +38,13 @@
 #include <jni.h>
 #include <unistd.h>
 #include <string.h>
-
-#if !defined(_WIN32)
-# include <netdb.h>
-#else // windows
-# include <winsock2.h>
-# include <psapi.h>
-# include <intrin.h>
-# include <Sddl.h>
-# include <Lm.h>
-# include <Winternl.h>
-#endif
+#include <winsock2.h>
+#include <psapi.h>
+#include <intrin.h>
+#include <Sddl.h>
+#include <Lm.h>
+#include <Winternl.h>
+#include <wbemidl.h>  // for WMI functions
 
 #if !defined(STATUS_NOT_IMPLEMENTED)
 # define STATUS_NOT_IMPLEMENTED 0xC0000002;
@@ -678,6 +674,128 @@
 
 /*
  * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
+ * Method:    getSystemTimes0
+ * array is idle, kernel, user times
+ * Signature: ([J)V
+ */
+JNIEXPORT void JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getSystemTimes0
+  (JNIEnv *env, jclass winHelperClass, jlongArray times) {
+
+    testLength(env, times, 3);
+
+    FILETIME idleTime;
+    FILETIME kernelTime;
+    FILETIME userTime;
+
+    GetSystemTimes(&idleTime, &kernelTime, &userTime);
+
+    // Get the element pointer
+    jlong* data = (*env)->GetLongArrayElements(env, times, 0);
+
+    data[0] = convertFileTimeToInt64(&idleTime);
+    data[1] = convertFileTimeToInt64(&kernelTime);
+    data[2] = convertFileTimeToInt64(&userTime);
+
+    (*env)->ReleaseLongArrayElements(env, times, data, 0);
+ }
+
+ /*
+  * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
+  * Method:    getCPUUsagePercent0
+  * Signature: ([[I)I
+  */
+ JNIEXPORT jint JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getCPUUsagePercent0
+   (JNIEnv *env, jclass winHelperClass, jobjectArray arrayOfArrayOfInts)
+ {
+    int cpuIdx = 0;
+    const jsize arraylen = (*env)->GetArrayLength(env, arrayOfArrayOfInts);
+
+     // result code from COM calls
+     HRESULT hr = 0;
+
+     // COM interface pointers
+     IWbemLocator         *locator  = NULL;
+     IWbemServices        *services = NULL;
+     IEnumWbemClassObject *results  = NULL;
+
+     // BSTR strings we'll use (http://msdn.microsoft.com/en-us/library/ms221069.aspx)
+     BSTR resource = SysAllocString(L"ROOT\\CIMV2");
+     BSTR language = SysAllocString(L"WQL");
+     BSTR query    = SysAllocString(L"SELECT Name, PercentIdleTime, PercentPrivilegedTime, PercentUserTime FROM Win32_PerfFormattedData_Counters_ProcessorInformation");
+
+     // initialize COM
+     hr = CoInitializeEx(0, COINIT_MULTITHREADED);
+     hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
+
+     // connect to WMI
+     hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, &IID_IWbemLocator, (LPVOID *) &locator);
+     hr = locator->lpVtbl->ConnectServer(locator, resource, NULL, NULL, NULL, 0, NULL, NULL, &services);
+
+     // issue a WMI query
+     hr = services->lpVtbl->ExecQuery(services, language, query, WBEM_FLAG_BIDIRECTIONAL, NULL, &results);
+
+     // list the query results
+     if (results != NULL) {
+         IWbemClassObject *result = NULL;
+         ULONG returnedCount = 0;
+
+         // enumerate the retrieved objects
+         while((hr = results->lpVtbl->Next(results, WBEM_INFINITE, 1, &result, &returnedCount)) == S_OK) {
+
+            if (cpuIdx >= arraylen) {
+                // we've overflowed - only return what the array can hold
+                break;
+            }
+
+             VARIANT name;
+             VARIANT idlePercent;
+ 			 VARIANT systemPercent;
+ 			 VARIANT userPercent;
+
+             // obtain the desired properties of the next result and print them out
+             hr = result->lpVtbl->Get(result, L"Name", 0, &name, 0, 0);
+
+ 			// ignore _Total rows
+ 			if (wcsstr(name.bstrVal, L"_Total") == NULL) {
+ 				hr = result->lpVtbl->Get(result, L"PercentIdleTime", 0, &idlePercent, 0, 0);
+ 				hr = result->lpVtbl->Get(result, L"PercentPrivilegedTime", 0, &systemPercent, 0, 0);
+ 				hr = result->lpVtbl->Get(result, L"PercentUserTime", 0, &userPercent, 0, 0);
+ 				const int idlePct = _wtoi(idlePercent.bstrVal);
+ 				const int sysPct = _wtoi(systemPercent.bstrVal);
+ 				const int userPct = _wtoi(userPercent.bstrVal);
+
+ 				jintArray arrayObj = (jintArray)(*env)->GetObjectArrayElement(env, arrayOfArrayOfInts, cpuIdx);
+ 				testLength(env, arrayObj, 3);
+ 				jint* array = (*env)->GetIntArrayElements(env, arrayObj, NULL);
+ 				array[0] = idlePct;
+ 				array[1] = sysPct;
+ 				array[2] = userPct;
+ 				(*env)->ReleaseIntArrayElements(env, arrayObj, array, 0);
+
+ 				cpuIdx++; // only count cpus, not totals or subtotals
+ 			}
+             // release the current result object
+             result->lpVtbl->Release(result);
+
+         }
+     }
+
+     // release WMI COM interfaces
+     results->lpVtbl->Release(results);
+     services->lpVtbl->Release(services);
+     locator->lpVtbl->Release(locator);
+
+     // unwind everything else we've allocated
+     CoUninitialize();
+
+     SysFreeString(query);
+     SysFreeString(language);
+     SysFreeString(resource);
+     return (jint)(cpuIdx);
+ }
+
+/*
+ * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
  * Method:    getProcessMemoryInfo0
  * Signature: (I[J)V
  */
--- a/host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/CpuStatBuilder.java	Mon Mar 20 14:09:36 2017 -0400
+++ b/host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/CpuStatBuilder.java	Thu Apr 13 23:25:41 2017 -0400
@@ -36,112 +36,10 @@
 
 package com.redhat.thermostat.host.cpu.agent.internal;
 
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.portability.linux.ProcDataSource;
-import com.redhat.thermostat.common.Clock;
-import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.host.cpu.common.model.CpuStat;
-import com.redhat.thermostat.shared.config.OS;
-import com.redhat.thermostat.storage.core.WriterID;
-
-public class CpuStatBuilder {
-
-    private static final Logger logger = LoggingUtils.getLogger(CpuStatBuilder.class);
-
-    private final ProcDataSource dataSource;
-    private final Clock clock;
-    private final long ticksPerSecond;
-    private final WriterID writerId;
-
-    private boolean initialized = false;
-
-    private long[] previousCpuTicks;
-    private long previousTime;
-
-    public CpuStatBuilder(Clock clock, ProcDataSource dataSource, long ticksPerSecond, WriterID writerId) {
-        this.writerId = writerId;
-        this.dataSource = dataSource;
-        this.clock = clock;
-        this.ticksPerSecond = ticksPerSecond;
-    }
-
-    public void initialize() {
-        if (initialized) {
-            throw new IllegalStateException("already initialized");
-        }
-
-        previousTime = clock.getMonotonicTimeNanos();
-        previousCpuTicks = OS.IS_LINUX ? getCurrentCpuTicksLinux() : getCurrentCpuTicks();
-        initialized = true;
-    }
-
-    public CpuStat build() {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized yet");
-        }
-
-        long currentRealTime = clock.getRealTimeMillis();
-        long currentTime = clock.getMonotonicTimeNanos();
-        long[] currentValues = OS.IS_LINUX ? getCurrentCpuTicksLinux() : getCurrentCpuTicks();
-
-        double[] cpuUsage = new double[currentValues.length];
 
-        double timeDelta = (currentTime - previousTime) * 1E-9;
-        for (int i = 0; i < currentValues.length; i++) {
-            long cpuTicksDelta = currentValues[i] - previousCpuTicks[i];
-            // 100 as in 100 percent.
-            cpuUsage[i] = cpuTicksDelta * (100.0 / timeDelta / ticksPerSecond);
-        }
-        previousTime = currentTime;
-        previousCpuTicks = currentValues;
-        String wId = writerId.getWriterID();
-
-        return new CpuStat(wId, currentRealTime, cpuUsage);
-    }
-
-    private long[] getCurrentCpuTicksLinux() {
-        int maxIndex = 0;
-        long[] values = new long[1];
-        try (BufferedReader reader = new BufferedReader(dataSource.getStatReader())) {
-            String line;
-            while ((line = reader.readLine()) != null) {
-                if (!line.startsWith("cpu")) {
-                    continue;
-                }
-                String[] parts = line.split("\\s");
-                if (!parts[0].matches("cpu\\d+")) {
-                    continue;
-                }
-
-                int cpuIndex = Integer.valueOf(parts[0].substring("cpu".length()));
-                if (cpuIndex > maxIndex) {
-                    long[] newValues = new long[cpuIndex+1];
-                    System.arraycopy(values, 0, newValues, 0, cpuIndex);
-                    values = newValues;
-                    maxIndex = cpuIndex;
-                }
-                values[cpuIndex] = Long.valueOf(parts[1]) + Long.valueOf(parts[2]) + Long.valueOf(parts[3]);
-            }
-        } catch (IOException e) {
-            logger.log(Level.WARNING, "error reading stat file", e);
-        }
-
-        return values;
-    }
-
-    private long[] getCurrentCpuTicks() {
-        long[] values = new long[1];
-        values[0] = clock.getMonotonicTimeNanos();
-        return values;
-    }
-
-    public boolean isInitialized() {
-        return initialized;
-    }
-
+public interface CpuStatBuilder {
+    CpuStat build();
+    void initialize();
+    boolean isInitialized();
 }
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/CpuStatBuilderFactory.java	Thu Apr 13 23:25:41 2017 -0400
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.host.cpu.agent.internal;
+
+import com.redhat.thermostat.common.Clock;
+import com.redhat.thermostat.common.SystemClock;
+import com.redhat.thermostat.common.portability.SysConf;
+import com.redhat.thermostat.common.portability.linux.ProcDataSource;
+import com.redhat.thermostat.shared.config.OS;
+import com.redhat.thermostat.storage.core.WriterID;
+
+public class CpuStatBuilderFactory {
+
+    CpuStatBuilderFactory() {
+    }
+
+    public CpuStatBuilder build(final WriterID id) {
+        return OS.IS_LINUX ? buildForLinux(id) : buildForWindows(id);
+    }
+
+    private CpuStatBuilder buildForLinux(final WriterID id) {
+        final Clock clock = new SystemClock();
+        final long ticksPerSecond = SysConf.getClockTicksPerSecond();
+        final ProcDataSource source = new ProcDataSource();
+        return new LinuxCpuStatBuilder(clock, source, ticksPerSecond, id);
+    }
+
+    private CpuStatBuilder buildForWindows(final WriterID id) {
+        final Clock clock = new SystemClock();
+        return new WindowsCpuStatBuilder(clock, id);
+    }
+}
--- a/host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/HostCpuBackend.java	Mon Mar 20 14:09:36 2017 -0400
+++ b/host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/HostCpuBackend.java	Thu Apr 13 23:25:41 2017 -0400
@@ -38,12 +38,8 @@
 
 import java.util.concurrent.ScheduledExecutorService;
 
-import com.redhat.thermostat.common.portability.linux.ProcDataSource;
-import com.redhat.thermostat.common.portability.SysConf;
 import com.redhat.thermostat.backend.HostPollingAction;
 import com.redhat.thermostat.backend.HostPollingBackend;
-import com.redhat.thermostat.common.Clock;
-import com.redhat.thermostat.common.SystemClock;
 import com.redhat.thermostat.common.Version;
 import com.redhat.thermostat.host.cpu.common.CpuStatDAO;
 import com.redhat.thermostat.storage.core.WriterID;
@@ -61,14 +57,11 @@
 
     private static class CpuProcBackendAction implements HostPollingAction {
 
-        private CpuStatBuilder builder;
-        private CpuStatDAO dao;
+        private final CpuStatBuilder builder;
+        private final CpuStatDAO dao;
 
-        CpuProcBackendAction(final WriterID id, CpuStatDAO dao) {
-            Clock clock = new SystemClock();
-            long ticksPerSecond = SysConf.getClockTicksPerSecond();
-            ProcDataSource source = new ProcDataSource();
-            builder = new CpuStatBuilder(clock, source, ticksPerSecond, id);
+        CpuProcBackendAction(final WriterID id, final CpuStatDAO dao) {
+            this.builder = new CpuStatBuilderFactory().build(id);
             this.dao = dao;
         }
 
@@ -80,7 +73,6 @@
                 dao.putCpuStat(builder.build());
             }
         }
-        
     }
 
     @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/LinuxCpuStatBuilder.java	Thu Apr 13 23:25:41 2017 -0400
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.host.cpu.agent.internal;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.portability.linux.ProcDataSource;
+import com.redhat.thermostat.common.Clock;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.host.cpu.common.model.CpuStat;
+import com.redhat.thermostat.storage.core.WriterID;
+
+public class LinuxCpuStatBuilder implements CpuStatBuilder {
+
+    private static final Logger logger = LoggingUtils.getLogger(LinuxCpuStatBuilder.class);
+
+    private final ProcDataSource dataSource;
+    private final Clock clock;
+    private final long ticksPerSecond;
+    private final WriterID writerId;
+
+    private boolean initialized = false;
+
+    private long[] previousCpuTicks;
+    private long previousTime;
+
+    LinuxCpuStatBuilder(Clock clock, ProcDataSource dataSource, long ticksPerSecond, WriterID writerId) {
+        this.writerId = writerId;
+        this.dataSource = dataSource;
+        this.clock = clock;
+        this.ticksPerSecond = ticksPerSecond;
+    }
+
+    @Override
+    public void initialize() {
+        if (initialized) {
+            throw new IllegalStateException("already initialized");
+        }
+
+        previousTime = clock.getMonotonicTimeNanos();
+        previousCpuTicks = getCurrentCpuTicks();
+        initialized = true;
+    }
+
+    @Override
+    public CpuStat build() {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized yet");
+        }
+
+        long currentRealTime = clock.getRealTimeMillis();
+        long currentTime = clock.getMonotonicTimeNanos();
+        long[] currentValues = getCurrentCpuTicks();
+
+        double[] cpuUsage = new double[currentValues.length];
+
+        double timeDelta = (currentTime - previousTime) * 1E-9;
+        for (int i = 0; i < currentValues.length; i++) {
+            long cpuTicksDelta = currentValues[i] - previousCpuTicks[i];
+            // 100 as in 100 percent.
+            cpuUsage[i] = cpuTicksDelta * (100.0 / timeDelta / ticksPerSecond);
+        }
+        previousTime = currentTime;
+        previousCpuTicks = currentValues;
+        String wId = writerId.getWriterID();
+        return new CpuStat(wId, currentRealTime, cpuUsage);
+    }
+
+    private long[] getCurrentCpuTicks() {
+        int maxIndex = 0;
+        long[] values = new long[1];
+        try (BufferedReader reader = new BufferedReader(dataSource.getStatReader())) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                if (!line.startsWith("cpu")) {
+                    continue;
+                }
+                String[] parts = line.split("\\s");
+                if (!parts[0].matches("cpu\\d+")) {
+                    continue;
+                }
+
+                int cpuIndex = Integer.valueOf(parts[0].substring("cpu".length()));
+                if (cpuIndex > maxIndex) {
+                    long[] newValues = new long[cpuIndex+1];
+                    System.arraycopy(values, 0, newValues, 0, cpuIndex);
+                    values = newValues;
+                    maxIndex = cpuIndex;
+                }
+                // add the user, user-nice and system times to get the CPU busy time
+                values[cpuIndex] = Long.valueOf(parts[1]) + Long.valueOf(parts[2]) + Long.valueOf(parts[3]);
+            }
+        } catch (IOException e) {
+            logger.log(Level.WARNING, "error reading stat file", e);
+        }
+
+        return values;
+    }
+
+    @Override
+    public boolean isInitialized() {
+        return initialized;
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/WindowsCpuStatBuilder.java	Thu Apr 13 23:25:41 2017 -0400
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.host.cpu.agent.internal;
+
+import com.redhat.thermostat.common.Clock;
+import com.redhat.thermostat.common.portability.PortableHost;
+import com.redhat.thermostat.common.portability.PortableHostImpl;
+import com.redhat.thermostat.host.cpu.common.model.CpuStat;
+import com.redhat.thermostat.storage.core.WriterID;
+
+public class WindowsCpuStatBuilder implements CpuStatBuilder {
+
+    private final Clock clock;
+    private final WriterID writerId;
+    private final PortableHost portableHost;
+
+    private boolean initialized = false;
+
+    WindowsCpuStatBuilder(Clock clock, WriterID writerId) {
+        this.writerId = writerId;
+        this.clock = clock;
+        this.portableHost = PortableHostImpl.getInstance();
+    }
+
+    @Override
+    public void initialize() {
+        if (initialized) {
+            throw new IllegalStateException("already initialized");
+        }
+        initialized = true;
+    }
+
+    @Override
+    public  CpuStat build() {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized yet");
+        }
+        /*
+         * On Windows, we only have the aggegate over all CPUs.
+         * But, we also get the idle ticks back.  So we don't need to use the delta time.
+         * The calculation here is ((delta busy time) / (delta busy time + delta free time)) * 100%
+         */
+        final long currentRealTime = clock.getRealTimeMillis();
+        int[][] currentValues = portableHost.getCPUUsagePercent();
+        final int numCPUs = currentValues.length;
+        final double[] cpuUsagePercent = new double[numCPUs];
+        for (int i=0; i<numCPUs; i++) {
+            // each cpu row returned from getCPUUsagePercent() has idle/system/user percents
+            // calculate the usage as the non-idle time
+            cpuUsagePercent[i] = (double)(100-currentValues[i][0]);
+        }
+        String wId = writerId.getWriterID();
+        return new CpuStat(wId, currentRealTime, cpuUsagePercent);
+    }
+
+    @Override
+    public boolean isInitialized() {
+        return initialized;
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host-cpu/agent/src/test/java/com/redhat/thermostat/host/cpu/agent/internal/CpuStatBuilderFactoryTest.java	Thu Apr 13 23:25:41 2017 -0400
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.host.cpu.agent.internal;
+
+import com.redhat.thermostat.shared.config.OS;
+import com.redhat.thermostat.storage.core.WriterID;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+
+public class CpuStatBuilderFactoryTest {
+
+    @Test
+    public void testSimpleBuild() {
+        CpuStatBuilderFactory factory = new CpuStatBuilderFactory();
+        WriterID id = mock(WriterID.class);
+        CpuStatBuilder builder = factory.build(id);
+        Assert.assertTrue(builder != null);
+    }
+
+    @Test
+    public void testCorrectBuild() {
+        WriterID id = mock(WriterID.class);
+        CpuStatBuilderFactory factory = new CpuStatBuilderFactory();
+        CpuStatBuilder builder = factory.build(id);
+        Assert.assertTrue(builder != null);
+        if (OS.IS_LINUX) {
+            Assert.assertTrue(builder instanceof LinuxCpuStatBuilder);
+        } else if (OS.IS_WINDOWS) {
+            Assert.assertTrue(builder instanceof WindowsCpuStatBuilder);
+        }
+    }
+}
--- a/host-cpu/agent/src/test/java/com/redhat/thermostat/host/cpu/agent/internal/CpuStatBuilderTest.java	Mon Mar 20 14:09:36 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,118 +0,0 @@
-/*
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.host.cpu.agent.internal;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.StringReader;
-
-import com.redhat.thermostat.shared.config.OS;
-import org.junit.Assume;
-import org.junit.Test;
-
-import com.redhat.thermostat.common.portability.linux.ProcDataSource;
-import com.redhat.thermostat.common.Clock;
-import com.redhat.thermostat.common.SystemClock;
-import com.redhat.thermostat.host.cpu.common.model.CpuStat;
-import com.redhat.thermostat.storage.core.WriterID;
-
-public class CpuStatBuilderTest {
-
-    @Test
-    public void testSimpleBuild() {
-        ProcDataSource dataSource = new ProcDataSource();
-        WriterID writerId = mock(WriterID.class);
-        CpuStatBuilder builder= new CpuStatBuilder(new SystemClock(), dataSource, 100l, writerId);
-        builder.initialize();
-        CpuStat stat = builder.build();
-        assertNotNull(stat);
-    }
-
-    @Test (expected=IllegalStateException.class)
-    public void buildWithoutInitializeThrowsException() {
-        Clock clock = mock(Clock.class);
-        ProcDataSource dataSource = mock(ProcDataSource.class);
-        long ticksPerSecond = 1;
-        CpuStatBuilder builder = new CpuStatBuilder(clock, dataSource, ticksPerSecond, null);
-        builder.build();
-    }
-
-    @Test
-    public void testBuildCpuStatFromFile() throws IOException {
-        Assume.assumeTrue(OS.IS_LINUX); // only Linux builds from /proc
-        long CLOCK1 = 1000;
-        long CLOCK2 = 2000;
-
-        String firstReadContents =
-            "cpu 100 0 0 1000 1000\n" +
-            "cpu0 100 0 0 1000 1000\n" +
-            "cpu1 10 80 10 1000 1000\n";
-        BufferedReader reader1 = new BufferedReader(new StringReader(firstReadContents));
-
-        String secondReadContents =
-            "cpu 400 0 0 1000 1000\n" +
-            "cpu0 200 0 0 1000 1000\n" +
-            "cpu1 30 50 120 1000 1000\n";
-        BufferedReader reader2 = new BufferedReader(new StringReader(secondReadContents));
-
-        long ticksPerSecond = 100;
-        Clock clock = mock(Clock.class);
-        when(clock.getRealTimeMillis()).thenReturn(CLOCK2);
-        when(clock.getMonotonicTimeNanos()).thenReturn((long)(CLOCK1 * 1E6)).thenReturn((long)(CLOCK2 * 1E6));
-
-        ProcDataSource dataSource = mock(ProcDataSource.class);
-        when(dataSource.getStatReader()).thenReturn(reader1).thenReturn(reader2);
-        WriterID writerId = mock(WriterID.class);
-        CpuStatBuilder builder = new CpuStatBuilder(clock, dataSource, ticksPerSecond, writerId);
-
-        builder.initialize();
-
-        CpuStat stat = builder.build();
-
-        verify(dataSource, times(2)).getStatReader();
-        assertArrayEquals(new double[] {100, 100}, stat.getPerProcessorUsage(), 0.01);
-    }
-
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host-cpu/agent/src/test/java/com/redhat/thermostat/host/cpu/agent/internal/LinuxCpuStatBuilderTest.java	Thu Apr 13 23:25:41 2017 -0400
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.host.cpu.agent.internal;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+
+import com.redhat.thermostat.shared.config.OS;
+import org.junit.Assume;
+import org.junit.Test;
+
+import com.redhat.thermostat.common.portability.linux.ProcDataSource;
+import com.redhat.thermostat.common.Clock;
+import com.redhat.thermostat.common.SystemClock;
+import com.redhat.thermostat.host.cpu.common.model.CpuStat;
+import com.redhat.thermostat.storage.core.WriterID;
+
+public class LinuxCpuStatBuilderTest {
+
+    @Test
+    public void testSimpleBuild() {
+        ProcDataSource dataSource = new ProcDataSource();
+        WriterID writerId = mock(WriterID.class);
+        LinuxCpuStatBuilder builder = new LinuxCpuStatBuilder(new SystemClock(), dataSource, 100l, writerId);
+        builder.initialize();
+        CpuStat stat = builder.build();
+        assertNotNull(stat);
+    }
+
+    @Test (expected=IllegalStateException.class)
+    public void buildWithoutInitializeThrowsException() {
+        Clock clock = mock(Clock.class);
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        long ticksPerSecond = 1;
+        LinuxCpuStatBuilder builder = new LinuxCpuStatBuilder(clock, dataSource, ticksPerSecond, null);
+        builder.build();
+    }
+
+    @Test
+    public void testBuildCpuStatFromFile() throws IOException {
+        Assume.assumeTrue(OS.IS_LINUX); // only Linux builds from /proc
+        long CLOCK1 = 1000;
+        long CLOCK2 = 2000;
+
+        String firstReadContents =
+            "cpu 100 0 0 1000 1000\n" +
+            "cpu0 100 0 0 1000 1000\n" +
+            "cpu1 10 80 10 1000 1000\n";
+        BufferedReader reader1 = new BufferedReader(new StringReader(firstReadContents));
+
+        String secondReadContents =
+            "cpu 400 0 0 1000 1000\n" +
+            "cpu0 200 0 0 1000 1000\n" +
+            "cpu1 30 50 120 1000 1000\n";
+        BufferedReader reader2 = new BufferedReader(new StringReader(secondReadContents));
+
+        long ticksPerSecond = 100;
+        Clock clock = mock(Clock.class);
+        when(clock.getRealTimeMillis()).thenReturn(CLOCK2);
+        when(clock.getMonotonicTimeNanos()).thenReturn((long)(CLOCK1 * 1E6)).thenReturn((long)(CLOCK2 * 1E6));
+
+        ProcDataSource dataSource = mock(ProcDataSource.class);
+        when(dataSource.getStatReader()).thenReturn(reader1).thenReturn(reader2);
+        WriterID writerId = mock(WriterID.class);
+        LinuxCpuStatBuilder builder = new LinuxCpuStatBuilder(clock, dataSource, ticksPerSecond, writerId);
+
+        builder.initialize();
+
+        CpuStat stat = builder.build();
+
+        verify(dataSource, times(2)).getStatReader();
+        assertArrayEquals(new double[] {100, 100}, stat.getPerProcessorUsage(), 0.01);
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host-cpu/agent/src/test/java/com/redhat/thermostat/host/cpu/agent/internal/WindowsCpuStatBuilderTest.java	Thu Apr 13 23:25:41 2017 -0400
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.host.cpu.agent.internal;
+
+import com.redhat.thermostat.common.Clock;
+import com.redhat.thermostat.common.SystemClock;
+import com.redhat.thermostat.host.cpu.common.model.CpuStat;
+import com.redhat.thermostat.shared.config.OS;
+import com.redhat.thermostat.storage.core.WriterID;
+import org.junit.Assume;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+
+public class WindowsCpuStatBuilderTest {
+
+    @Test
+    public void testSimpleBuild() {
+        Assume.assumeTrue(OS.IS_WINDOWS); // uses a method that throws UnimplementedException on Linux amd Macos
+        WriterID writerId = mock(WriterID.class);
+        CpuStatBuilder builder= new WindowsCpuStatBuilder(new SystemClock(), writerId);
+        builder.initialize();
+        CpuStat stat = builder.build();
+        assertNotNull(stat);
+    }
+
+    @Test (expected=IllegalStateException.class)
+    public void buildWithoutInitializeThrowsException() {
+        Assume.assumeTrue(OS.IS_WINDOWS); // uses a method that throws UnimplementedException on Linux amd Macos
+        Clock clock = mock(Clock.class);
+        CpuStatBuilder builder = new WindowsCpuStatBuilder(clock, null);
+        builder.build();
+    }
+}
+