Mercurial > hg > release > thermostat-0.11
changeset 156:ddb36e8b9995
Tests for VmCpuStatBuilder
Reviewed-by: rkennke, vanaltj
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-March/000488.html
author | Omair Majid <omajid@redhat.com> |
---|---|
date | Thu, 29 Mar 2012 11:31:43 -0400 |
parents | 08829d9943de |
children | a9f2b8a4dbbb |
files | agent/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java agent/src/main/java/com/redhat/thermostat/backend/system/VmCpuStatBuilder.java agent/src/test/java/com/redhat/thermostat/backend/system/VmCpuStatBuilderTest.java common/src/main/java/com/redhat/thermostat/common/Clock.java common/src/main/java/com/redhat/thermostat/common/SystemClock.java common/src/main/java/com/redhat/thermostat/common/model/VmCpuStat.java |
diffstat | 6 files changed, 279 insertions(+), 16 deletions(-) [+] |
line wrap: on
line diff
--- a/agent/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java Thu Mar 29 13:15:28 2012 +0200 +++ b/agent/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java Thu Mar 29 11:31:43 2012 -0400 @@ -55,6 +55,8 @@ import com.redhat.thermostat.agent.JvmStatusListener; import com.redhat.thermostat.agent.JvmStatusNotifier; import com.redhat.thermostat.backend.Backend; +import com.redhat.thermostat.common.Clock; +import com.redhat.thermostat.common.SystemClock; import com.redhat.thermostat.common.dao.CpuStatConverter; import com.redhat.thermostat.common.dao.CpuStatDAO; import com.redhat.thermostat.common.dao.HostInfoConverter; @@ -92,8 +94,16 @@ private Set<Integer> pidsToMonitor = new CopyOnWriteArraySet<Integer>(); + private final VmCpuStatBuilder vmCpuBuilder; + private static List<Category> categories = new ArrayList<Category>(); + public SystemBackend() { + Clock clock = new SystemClock(); + ProcessStatusInfoBuilder builder = new ProcessStatusInfoBuilder(new ProcDataSource()); + long ticksPerSecond = SysConf.getClockTicksPerSecond(); + vmCpuBuilder = new VmCpuStatBuilder(clock, ticksPerSecond, builder); + } static { // Set up categories that will later be registered. @@ -153,8 +163,11 @@ store(new MemoryStatConverter().memoryStatToChunk(new MemoryStatBuilder(dataSource).build())); for (Integer pid : pidsToMonitor) { - new VmCpuStatBuilder(); - store(new VmCpuStatConverter().vmCpuStatToChunk(VmCpuStatBuilder.build(pid))); + if (vmCpuBuilder.knowsAbout(pid)) { + store(new VmCpuStatConverter().vmCpuStatToChunk(vmCpuBuilder.build(pid))); + } else { + vmCpuBuilder.learnAbout(pid); + } } } }, 0, procCheckInterval); @@ -234,5 +247,6 @@ @Override public void jvmStopped(int vmId) { pidsToMonitor.remove(vmId); + vmCpuBuilder.forgetAbout(vmId); } }
--- a/agent/src/main/java/com/redhat/thermostat/backend/system/VmCpuStatBuilder.java Thu Mar 29 13:15:28 2012 +0200 +++ b/agent/src/main/java/com/redhat/thermostat/backend/system/VmCpuStatBuilder.java Thu Mar 29 11:31:43 2012 -0400 @@ -39,16 +39,25 @@ import java.util.HashMap; import java.util.Map; +import com.redhat.thermostat.common.Clock; import com.redhat.thermostat.common.model.VmCpuStat; public class VmCpuStatBuilder { // pid -> ticks - private static Map<Integer, Long> lastProcessTicks = new HashMap<Integer, Long>(); + private final Map<Integer, Long> lastProcessTicks = new HashMap<Integer, Long>(); // pid -> last time the ticks were updated - private static Map<Integer, Long> lastProcessTickTime = new HashMap<Integer, Long>(); + private final Map<Integer, Long> lastProcessTickTime = new HashMap<Integer, Long>(); + + private final Clock clock; + private final long ticksPerSecond; + private final ProcessStatusInfoBuilder statusBuilder; - private static long clockTicksPerSecond = SysConf.getClockTicksPerSecond(); + public VmCpuStatBuilder(Clock clock, long ticksPerSecond, ProcessStatusInfoBuilder statusBuilder) { + this.clock = clock; + this.ticksPerSecond = ticksPerSecond; + this.statusBuilder = statusBuilder; + } /** * To build the stat, this method needs to be called repeatedly. The first @@ -58,26 +67,43 @@ * @param pid * @return */ - public static synchronized VmCpuStat build(Integer pid) { + public synchronized VmCpuStat build(Integer pid) { + if (!lastProcessTicks.containsKey(pid) || !lastProcessTickTime.containsKey(pid)) { + throw new IllegalArgumentException("unknown pid"); + } - ProcDataSource dataSource = new ProcDataSource(); - ProcessStatusInfo info = new ProcessStatusInfoBuilder(dataSource).build(pid); - long miliTime = System.currentTimeMillis(); - long time = System.nanoTime(); + ProcessStatusInfo info = statusBuilder.build(pid); + long miliTime = clock.getRealTimeMillis(); + long time = clock.getMonotonicTimeNanos(); long programTicks = (info.getKernelTime() + info.getUserTime()); double cpuLoad = 0.0; - if (lastProcessTicks.containsKey(pid)) { - double timeDelta = (time - lastProcessTickTime.get(pid)) * 1E-9; - long programTicksDelta = programTicks - lastProcessTicks.get(pid); - cpuLoad = programTicksDelta * (100.0 / timeDelta / clockTicksPerSecond); - } + double timeDelta = (time - lastProcessTickTime.get(pid)) * 1E-9; + long programTicksDelta = programTicks - lastProcessTicks.get(pid); + // 100 as in 100 percent. + cpuLoad = programTicksDelta * (100.0 / timeDelta / ticksPerSecond); lastProcessTicks.put(pid, programTicks); lastProcessTickTime.put(pid, time); - return new VmCpuStat(miliTime, pid, cpuLoad); } + public synchronized boolean knowsAbout(int pid) { + return (lastProcessTickTime.containsKey(pid) && lastProcessTicks.containsKey(pid)); + } + + public synchronized void learnAbout(int pid) { + long time = clock.getMonotonicTimeNanos(); + ProcessStatusInfo info = statusBuilder.build(pid); + + lastProcessTickTime.put(pid, time); + lastProcessTicks.put(pid, info.getUserTime()+ info.getKernelTime()); + } + + public synchronized void forgetAbout(int pid) { + lastProcessTicks.remove(pid); + lastProcessTickTime.remove(pid); + } + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/src/test/java/com/redhat/thermostat/backend/system/VmCpuStatBuilderTest.java Thu Mar 29 11:31:43 2012 -0400 @@ -0,0 +1,117 @@ +/* + * Copyright 2012 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.backend.system; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.Test; + +import com.redhat.thermostat.common.Clock; +import com.redhat.thermostat.common.model.VmCpuStat; + +public class VmCpuStatBuilderTest { + + @Test + public void testBuilderKnowsNothing() { + Clock clock = mock(Clock.class); + ProcessStatusInfoBuilder statusBuilder = mock(ProcessStatusInfoBuilder.class); + long ticksPerSecond = 0; + VmCpuStatBuilder builder = new VmCpuStatBuilder(clock, ticksPerSecond, statusBuilder); + + assertFalse(builder.knowsAbout(0)); + assertFalse(builder.knowsAbout(1)); + assertFalse(builder.knowsAbout(Integer.MIN_VALUE)); + assertFalse(builder.knowsAbout(Integer.MAX_VALUE)); + + } + + @Test(expected = IllegalArgumentException.class) + public void testBuilderThrowsOnBuildOfUnknownPid() { + Clock clock = mock(Clock.class); + long ticksPerSecond = 0; + ProcessStatusInfoBuilder statusBuilder = mock(ProcessStatusInfoBuilder.class); + VmCpuStatBuilder builder = new VmCpuStatBuilder(clock, ticksPerSecond, statusBuilder); + builder.build(0); + } + + @Test + public void testSaneBuild() { + final int PID = 0; + final long USER_INITIAL_TICKS = 1; + final long KERNEL_INITIAL_TICKS = 1; + + final long USER_LATER_TICKS = 10; + final long KERNEL_LATER_TICKS = 10; + + final long CLOCK1 = 10000; + final long CLOCK2 = 20000; + + final long TICKS_PER_SECOND = 100; + + final double CPU_LOAD_PERCENT = + 100.0 + * ((USER_LATER_TICKS + KERNEL_LATER_TICKS) - (USER_INITIAL_TICKS + KERNEL_INITIAL_TICKS)) + / TICKS_PER_SECOND + / ((CLOCK2 - CLOCK1) * 1E-3 /* millis to seconds */); + + final ProcessStatusInfo initialInfo = new ProcessStatusInfo(PID, USER_INITIAL_TICKS, KERNEL_INITIAL_TICKS); + final ProcessStatusInfo laterInfo = new ProcessStatusInfo(PID, USER_LATER_TICKS, KERNEL_LATER_TICKS); + + Clock clock = mock(Clock.class); + when(clock.getRealTimeMillis()).thenReturn(CLOCK2); + when(clock.getMonotonicTimeNanos()).thenReturn((long) (CLOCK1 * 1E6)).thenReturn((long) (CLOCK2 * 1E6)); + + ProcessStatusInfoBuilder statusBuilder = mock(ProcessStatusInfoBuilder.class); + when(statusBuilder.build(any(Integer.class))).thenReturn(initialInfo).thenReturn(laterInfo).thenReturn(null); + + VmCpuStatBuilder builder = new VmCpuStatBuilder(clock, TICKS_PER_SECOND, statusBuilder); + + builder.learnAbout(PID); + VmCpuStat stat = builder.build(PID); + + assertNotNull(stat); + assertEquals(PID, stat.getVmId()); + assertEquals(CLOCK2, stat.getTimeStamp()); + assertEquals(CPU_LOAD_PERCENT, stat.getCpuLoad(), 0.0001); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/main/java/com/redhat/thermostat/common/Clock.java Thu Mar 29 11:31:43 2012 -0400 @@ -0,0 +1,52 @@ +/* + * Copyright 2012 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.common; + +public interface Clock { + + /** + * Returns the current real time in milliseconds (measured since the UNIX epoch). + */ + long getRealTimeMillis(); + + /** + * Returns a time value corresponding to a monotonic clock measuring time + * in nanoseconds since some unspecified epoch. + */ + long getMonotonicTimeNanos(); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/src/main/java/com/redhat/thermostat/common/SystemClock.java Thu Mar 29 11:31:43 2012 -0400 @@ -0,0 +1,51 @@ +/* + * Copyright 2012 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.common; + +public class SystemClock implements Clock { + + @Override + public long getRealTimeMillis() { + return System.currentTimeMillis(); + } + + @Override + public long getMonotonicTimeNanos() { + return System.nanoTime(); + } + +}
--- a/common/src/main/java/com/redhat/thermostat/common/model/VmCpuStat.java Thu Mar 29 13:15:28 2012 +0200 +++ b/common/src/main/java/com/redhat/thermostat/common/model/VmCpuStat.java Thu Mar 29 11:31:43 2012 -0400 @@ -56,6 +56,9 @@ return vmId; } + /** + * The cpu load in percent (as in 100.0 for 100%) + */ public double getCpuLoad() { return cpuLoad; }