# HG changeset patch # User Simon Tooke # Date 1492140341 14400 # Node ID 65fcc9af086ce8be7872a43f16576e696f90b338 # Parent e4f6006877d14f580453811edd3b1b36b6481486 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 diff -r e4f6006877d1 -r 65fcc9af086c common/portability/Makefile --- 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) diff -r e4f6006877d1 -r 65fcc9af086c common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableHost.java --- 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(); + } diff -r e4f6006877d1 -r 65fcc9af086c common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxPortableHostImpl.java --- 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); diff -r e4f6006877d1 -r 65fcc9af086c common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSHostImpl.java --- 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()"); + } } diff -r e4f6006877d1 -r 65fcc9af086c common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsHelperImpl.java --- 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 #include #include - -#if !defined(_WIN32) -# include -#else // windows -# include -# include -# include -# include -# include -# include -#endif +#include +#include +#include +#include +#include +#include +#include // 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 */ diff -r e4f6006877d1 -r 65fcc9af086c host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/CpuStatBuilder.java --- 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(); } - diff -r e4f6006877d1 -r 65fcc9af086c host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/CpuStatBuilderFactory.java --- /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 + * . + * + * 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); + } +} diff -r e4f6006877d1 -r 65fcc9af086c host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/HostCpuBackend.java --- 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 diff -r e4f6006877d1 -r 65fcc9af086c host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/LinuxCpuStatBuilder.java --- /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 + * . + * + * 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; + } + +} + diff -r e4f6006877d1 -r 65fcc9af086c host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/WindowsCpuStatBuilder.java --- /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 + * . + * + * 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. + * + * 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); + } + } +} diff -r e4f6006877d1 -r 65fcc9af086c host-cpu/agent/src/test/java/com/redhat/thermostat/host/cpu/agent/internal/CpuStatBuilderTest.java --- 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 - * . - * - * 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); - } - -} - diff -r e4f6006877d1 -r 65fcc9af086c host-cpu/agent/src/test/java/com/redhat/thermostat/host/cpu/agent/internal/LinuxCpuStatBuilderTest.java --- /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 + * . + * + * 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); + } + +} + diff -r e4f6006877d1 -r 65fcc9af086c host-cpu/agent/src/test/java/com/redhat/thermostat/host/cpu/agent/internal/WindowsCpuStatBuilderTest.java --- /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 + * . + * + * 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(); + } +} +