# HG changeset patch # User Andrew Azores # Date 1485810419 18000 # Node ID 15a168c5a68d6a2af0cf72528ba9af44f3f0c1e4 # Parent 902e2e96f4e8bb0c12e730a00e7c6dd15fb2bd7a Remove vm-numa dependency on external 'numastats' process Reviewed-by: stooke Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-December/021854.html diff -r 902e2e96f4e8 -r 15a168c5a68d common/portability/src/main/java/com/redhat/thermostat/common/portability/SysConf.java --- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/SysConf.java Mon Jan 30 13:06:07 2017 -0500 +++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/SysConf.java Mon Jan 30 16:06:59 2017 -0500 @@ -70,6 +70,14 @@ } } + public static long getPageSize() { + try { + return Long.valueOf(sysConf("PAGESIZE")); + } catch (NumberFormatException nfe) { + return 0; + } + } + private static String sysConf(String arg) { try { Process process = Runtime.getRuntime().exec(new String[] { "getconf", arg }); diff -r 902e2e96f4e8 -r 15a168c5a68d common/portability/src/main/java/com/redhat/thermostat/common/portability/linux/ProcDataSource.java --- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/linux/ProcDataSource.java Mon Jan 30 13:06:07 2017 -0500 +++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/linux/ProcDataSource.java Mon Jan 30 16:06:59 2017 -0500 @@ -60,6 +60,7 @@ private static final String PID_IO_FILE = "/proc/${pid}/io"; private static final String PID_STAT_FILE = "/proc/${pid}/stat"; private static final String PID_STATUS_FILE = "/proc/${pid}/status"; + private static final String PID_NUMA_MAPS_FILE = "/proc/${pid}/numa_maps"; /** * Returns a reader for /proc/cpuinfo @@ -109,7 +110,7 @@ public Reader getStatReader(int pid) throws IOException { return new FileReader(getPidFile(PID_STAT_FILE, pid)); } - + /** * Returns a reader for /proc/$PID/status */ @@ -117,6 +118,13 @@ return new FileReader(getPidFile(PID_STATUS_FILE, pid)); } + /** + * Returns a reader for /proc/$PID/numa_maps + */ + public Reader getNumaMapsReader(int pid) throws IOException { + return new FileReader(getPidFile(PID_NUMA_MAPS_FILE, pid)); + } + private String getPidFile(String fileName, int pid) { return fileName.replace("${pid}", Integer.toString(pid)); } diff -r 902e2e96f4e8 -r 15a168c5a68d common/portability/src/test/java/com/redhat/thermostat/common/portability/internal/SysConfTest.java --- a/common/portability/src/test/java/com/redhat/thermostat/common/portability/internal/SysConfTest.java Mon Jan 30 13:06:07 2017 -0500 +++ b/common/portability/src/test/java/com/redhat/thermostat/common/portability/internal/SysConfTest.java Mon Jan 30 16:06:59 2017 -0500 @@ -44,9 +44,15 @@ public class SysConfTest { @Test - public void test() { + public void testTicksPerSecond() { long ticksPerSecond = SysConf.getClockTicksPerSecond(); assertTrue(ticksPerSecond >= 1); } + + @Test + public void testPageSize() { + long pagesize = SysConf.getPageSize(); + assertTrue(pagesize >= 1); + } } diff -r 902e2e96f4e8 -r 15a168c5a68d common/portability/src/test/java/com/redhat/thermostat/common/portability/linux/ProcDataSourceTest.java --- a/common/portability/src/test/java/com/redhat/thermostat/common/portability/linux/ProcDataSourceTest.java Mon Jan 30 13:06:07 2017 -0500 +++ b/common/portability/src/test/java/com/redhat/thermostat/common/portability/linux/ProcDataSourceTest.java Mon Jan 30 16:06:59 2017 -0500 @@ -109,5 +109,13 @@ Reader r = new ProcDataSource().getStatusReader(pid); assertNotNull(r); } + + @Test + public void testNumaMapsReader() throws Exception { + Assume.assumeTrue(OS.IS_LINUX); + int pid = TestUtils.getProcessId(); + Reader r = new ProcDataSource().getNumaMapsReader(pid); + assertNotNull(r); + } } diff -r 902e2e96f4e8 -r 15a168c5a68d vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/Activator.java --- a/vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/Activator.java Mon Jan 30 13:06:07 2017 -0500 +++ b/vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/Activator.java Mon Jan 30 16:06:59 2017 -0500 @@ -36,9 +36,15 @@ package com.redhat.thermostat.vm.numa.agent.internal; +import java.io.IOException; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.logging.Level; +import java.util.logging.Logger; +import com.redhat.thermostat.common.Clock; +import com.redhat.thermostat.common.SystemClock; +import com.redhat.thermostat.common.utils.LoggingUtils; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; @@ -54,6 +60,8 @@ public class Activator implements BundleActivator { + private static final Logger logger = LoggingUtils.getLogger(Activator.class); + private ScheduledExecutorService executor; private MultipleServiceTracker tracker; private VmNumaBackend backend; @@ -75,15 +83,20 @@ VmNumaDAO vmNumaDAO = services.get(VmNumaDAO.class); Version version = new Version(context.getBundle()); WriterID writerID = services.get(WriterID.class); - backend = constructBackend(executor, vmNumaDAO, version, registrar, writerID); - if (backend.canRegister()) { + Clock clock = new SystemClock(); + try { + PageSizeProvider pageSizeProvider = new PageSizeProviderImpl(); + NumaMapsReaderProvider readerProvider = new NumaMapsReaderProviderImpl(); + backend = constructBackend(executor, clock, readerProvider, pageSizeProvider, vmNumaDAO, version, registrar, writerID); reg = context.registerService(Backend.class, backend, null); + } catch (IOException e) { + logger.log(Level.WARNING, "Unexpected exception retrieving Linux page sizes. NUMA counts will be disabled", e); } } @Override public void dependenciesUnavailable() { - if (backend.isActive()) { + if (backend != null && backend.isActive()) { backend.deactivate(); } if (reg != null) { @@ -93,7 +106,6 @@ }); tracker.open(); - } @Override @@ -107,7 +119,8 @@ } //Package private for testing - VmNumaBackend constructBackend(ScheduledExecutorService executor, VmNumaDAO vmNumaDAO, Version version, VmStatusListenerRegistrar registrar, WriterID writerID) { - return new VmNumaBackend(executor, vmNumaDAO, version, registrar, writerID); + VmNumaBackend constructBackend(ScheduledExecutorService executor, Clock clock, NumaMapsReaderProvider readerProvider, PageSizeProvider pageSizeProvider, + VmNumaDAO vmNumaDAO, Version version, VmStatusListenerRegistrar registrar, WriterID writerID) { + return new VmNumaBackend(executor, clock, readerProvider, pageSizeProvider, vmNumaDAO, version, registrar, writerID); } } diff -r 902e2e96f4e8 -r 15a168c5a68d vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/NumaMapsReaderProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/NumaMapsReaderProvider.java Mon Jan 30 16:06:59 2017 -0500 @@ -0,0 +1,45 @@ +/* + * 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.vm.numa.agent.internal; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; + +interface NumaMapsReaderProvider { + BufferedReader createReader(int forPid) throws IOException; +} diff -r 902e2e96f4e8 -r 15a168c5a68d vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/NumaMapsReaderProviderImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/NumaMapsReaderProviderImpl.java Mon Jan 30 16:06:59 2017 -0500 @@ -0,0 +1,61 @@ +/* + * 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.vm.numa.agent.internal; + +import com.redhat.thermostat.common.portability.linux.ProcDataSource; + +import java.io.BufferedReader; +import java.io.IOException; + +class NumaMapsReaderProviderImpl implements NumaMapsReaderProvider { + + private final ProcDataSource procDataSource; + + NumaMapsReaderProviderImpl() { + this(new ProcDataSource()); + } + + // testing hook only + NumaMapsReaderProviderImpl(ProcDataSource procDataSource) { + this.procDataSource = procDataSource; + } + + @Override + public BufferedReader createReader(int forPid) throws IOException { + return new BufferedReader(procDataSource.getNumaMapsReader(forPid)); + } +} diff -r 902e2e96f4e8 -r 15a168c5a68d vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/PageSizeProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/PageSizeProvider.java Mon Jan 30 16:06:59 2017 -0500 @@ -0,0 +1,42 @@ +/* + * 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.vm.numa.agent.internal; + +interface PageSizeProvider { + long getPageSize(); + long getHugePageSize(); +} diff -r 902e2e96f4e8 -r 15a168c5a68d vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/PageSizeProviderImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/PageSizeProviderImpl.java Mon Jan 30 16:06:59 2017 -0500 @@ -0,0 +1,76 @@ +/* + * 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.vm.numa.agent.internal; + +import com.redhat.thermostat.common.portability.SysConf; +import com.redhat.thermostat.common.portability.linux.ProcDataSource; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +class PageSizeProviderImpl implements PageSizeProvider { + + private static final Pattern HUGE_PAGESIZE_PATTERN = Pattern.compile("Hugepagesize:[\\s]+([\\d]+) kB"); + private long pagesize = 4L * 1024L; + private long hugePagesize = 2048L * 1024L; + + PageSizeProviderImpl() throws IOException { + pagesize = SysConf.getPageSize(); + + try (BufferedReader br = new BufferedReader(new ProcDataSource().getMemInfoReader())) { + for (String line = br.readLine(); line != null; line = br.readLine()) { + Matcher matcher = HUGE_PAGESIZE_PATTERN.matcher(line); + if (matcher.matches()) { + hugePagesize = Long.parseLong(matcher.group(1)) * 1024; + break; + } + } + } + } + + @Override + public long getPageSize() { + return pagesize; + } + + @Override + public long getHugePageSize() { + return hugePagesize; + } +} diff -r 902e2e96f4e8 -r 15a168c5a68d vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaBackend.java --- a/vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaBackend.java Mon Jan 30 13:06:07 2017 -0500 +++ b/vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaBackend.java Mon Jan 30 16:06:59 2017 -0500 @@ -37,7 +37,6 @@ package com.redhat.thermostat.vm.numa.agent.internal; import java.io.IOException; -import java.text.ParseException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; @@ -47,6 +46,7 @@ import com.redhat.thermostat.agent.VmStatusListenerRegistrar; import com.redhat.thermostat.backend.VmPollingAction; import com.redhat.thermostat.backend.VmPollingBackend; +import com.redhat.thermostat.common.Clock; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.storage.core.WriterID; @@ -55,16 +55,16 @@ public class VmNumaBackend extends VmPollingBackend { + private static final Logger logger = LoggingUtils.getLogger(VmNumaBackend.class); private final VmNumaBackendAction action; - private static final Logger logger = LoggingUtils.getLogger(VmNumaBackend.class); - public VmNumaBackend(ScheduledExecutorService executor, VmNumaDAO vmNumaDAO, Version version, - VmStatusListenerRegistrar registrar, WriterID id) { + public VmNumaBackend(ScheduledExecutorService executor, Clock clock, NumaMapsReaderProvider readerProvider, PageSizeProvider pageSizeProvider, + VmNumaDAO vmNumaDAO, Version version, VmStatusListenerRegistrar registrar, WriterID id) { super("VM NUMA Backend", "Gathers NUMA statistics about a vm", "Red Hat, Inc.", version, executor, registrar); - this.action = new VmNumaBackendAction(id, vmNumaDAO); + this.action = new VmNumaBackendAction(id, clock, readerProvider, pageSizeProvider, vmNumaDAO); registerAction(action); } @@ -74,12 +74,19 @@ } private static class VmNumaBackendAction implements VmPollingAction { + private WriterID writerID; + private Clock clock; + private NumaMapsReaderProvider readerProvider; + private PageSizeProvider pageSizeProvider; private VmNumaDAO dao; - private WriterID writerID; private Map collectors; - private VmNumaBackendAction(final WriterID writerID, VmNumaDAO dao) { + private VmNumaBackendAction(final WriterID writerID, Clock clock, NumaMapsReaderProvider readerProvider, + PageSizeProvider pageSizeProvider, VmNumaDAO dao) { this.writerID = writerID; + this.clock = clock; + this.readerProvider = readerProvider; + this.pageSizeProvider = pageSizeProvider; this.dao = dao; this.collectors = new HashMap<>(); } @@ -87,7 +94,8 @@ @Override public void run(String vmId, int pid) { if (!collectors.containsKey(pid)) { - collectors.put(pid, new VmNumaCollector(pid)); + VmNumaCollector collector = new VmNumaCollector(pid, clock, readerProvider, pageSizeProvider); + collectors.put(pid, collector); } try { @@ -95,26 +103,12 @@ data.setAgentId(writerID.getWriterID()); data.setVmId(vmId); dao.putVmNumaStat(data); - } catch (ParseException e) { - logger.log(Level.FINE, "Unable to read numa info for: " + pid); + } catch (IOException e) { + logger.log(Level.FINE, "Unable to read numa info for: " + pid, e); } } } - /** - * VmNumaBackend requires numastat process to function - * @return true if numastat process exists, false otherwise - */ - public boolean canRegister() { - try { - Runtime.getRuntime().exec("numastat"); - return true; - } catch (IOException e) { - //numastat does not exist, do nothing - } - return false; - } - // For testing purposes only void setVmNumaBackendCollector(int pid, VmNumaCollector collector) { action.collectors.put(pid, collector); diff -r 902e2e96f4e8 -r 15a168c5a68d vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaCollector.java --- a/vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaCollector.java Mon Jan 30 13:06:07 2017 -0500 +++ b/vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaCollector.java Mon Jan 30 16:06:59 2017 -0500 @@ -36,50 +36,177 @@ package com.redhat.thermostat.vm.numa.agent.internal; -import java.io.IOException; -import java.io.InputStream; -import java.text.ParseException; -import java.util.logging.Level; -import java.util.logging.Logger; - +import com.redhat.thermostat.common.Clock; +import com.redhat.thermostat.common.SystemClock; +import com.redhat.thermostat.common.cli.BorderedTableRenderer; import com.redhat.thermostat.common.utils.LoggingUtils; -import com.redhat.thermostat.common.utils.StreamUtils; +import com.redhat.thermostat.shared.config.OS; +import com.redhat.thermostat.vm.numa.common.VmNumaNodeStat; import com.redhat.thermostat.vm.numa.common.VmNumaStat; +import java.io.BufferedReader; +import java.io.IOException; +import java.util.Map; +import java.util.TreeMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +// the "parsing" performed by this class is very rudimentary, but follows the same simple checks performed by the +// "numastat" command public class VmNumaCollector { - private static final String PROCESS = "numastat"; private static final Logger logger = LoggingUtils.getLogger(VmNumaCollector.class); - private final ProcessBuilder processBuilder; - private final VmNumaStatParser parser; + private static final int KILOBYTE = 1024; + private static final int MEGABYTE = 1024 * KILOBYTE; + + private static final Pattern NODE_PATTERN = Pattern.compile("N([0-9]+)=([0-9]+)"); + private static final Pattern WHITESPACE_PATTERN = Pattern.compile("[\\s]+"); + + private final int pid; + private final Clock clock; + private final NumaMapsReaderProvider readerProvider; + private final PageSizeProvider pageSizeProvider; + + public VmNumaCollector(int pid, Clock clock, NumaMapsReaderProvider readerProvider, PageSizeProvider pageSizeProvider) { + this.pid = pid; + this.clock = clock; + this.readerProvider = readerProvider; + this.pageSizeProvider = pageSizeProvider; + } + + public VmNumaStat collect() throws IOException { + Map statsMap = new TreeMap<>(); // need sorted keys for converting values to array in order later + try (BufferedReader br = readerProvider.createReader(pid)) { + for (String line = br.readLine(); line != null; line = br.readLine()) { + processLine(statsMap, WHITESPACE_PATTERN.split(line)); + } + } + return createVmNumaStat(statsMap); + } - public VmNumaCollector(int pid) { - this.processBuilder = new ProcessBuilder(PROCESS, String.valueOf(pid)); - this.parser = new VmNumaStatParser(); + private void processLine(Map map, String[] tokens) { + for (String tok : tokens) { + Matcher matcher = NODE_PATTERN.matcher(tok); + if (matcher.matches()) { + int nodeNumber = Integer.parseInt(matcher.group(1)); + if (!map.containsKey(nodeNumber)) { + VmNumaNodeStat stat = new VmNumaNodeStat(); + stat.setNode(nodeNumber); + map.put(nodeNumber, stat); + } + + Category category = selectCategory(tokens); + double value = Double.parseDouble(matcher.group(2)); + value *= getMultiplier(category); + value /= (double) MEGABYTE; + + VmNumaNodeStat stat = map.get(nodeNumber); + updateStat(stat, category, value); + } + } + } + + private Category selectCategory(String[] tokens) { + for (String tok : tokens) { + for (Category c : Category.values()) { + if (tok.startsWith(c.getToken())) { + return c; + } + } + } + return Category.PRIVATE; } - public VmNumaStat collect() throws ParseException { - String output = getOutput(); - return parser.parse(output); + private long getMultiplier(Category category) { + switch (category) { + case HUGE: + return pageSizeProvider.getHugePageSize(); + default: + return pageSizeProvider.getPageSize(); + } + } + + private void updateStat(VmNumaNodeStat stat, Category category, double value) { + switch (category) { + case PRIVATE: + stat.setPrivateMemory(stat.getPrivateMemory() + value); + break; + case HEAP: + stat.setHeapMemory(stat.getHeapMemory() + value); + break; + case STACK: + stat.setStackMemory(stat.getStackMemory() + value); + break; + case HUGE: + stat.setHugeMemory(stat.getHugeMemory() + value); + break; + default: + throw new IllegalStateException("Invalid category: " + category); + } + } + + private VmNumaStat createVmNumaStat(Map map) { + VmNumaStat numaStat = new VmNumaStat(); + numaStat.setTimeStamp(clock.getRealTimeMillis()); + VmNumaNodeStat[] vmNodeStats = map.values().toArray(new VmNumaNodeStat[map.size()]); + numaStat.setVmNodeStats(vmNodeStats); + verifyVmNodeStatsArray(vmNodeStats); + return numaStat; } - private String getOutput() { - try { - Process p = startProcess(); - - InputStream is = p.getInputStream(); - return new String(StreamUtils.readAll(is)); - } catch (InterruptedException | IOException e) { - logger.log(Level.WARNING, "Unable to run process numastat"); + private void verifyVmNodeStatsArray(VmNumaNodeStat[] vmNumaNodeStats) { + boolean valid = true; + for (int i = 0; i < vmNumaNodeStats.length; i++) { + valid = valid && (vmNumaNodeStats[i].getNode() == i); } - return ""; + if (!valid) { + logger.log(Level.WARNING, "VmNumaNodeStat array contained invalid array indices"); + } } - //For testing - Process startProcess() throws InterruptedException, IOException { - Process p = processBuilder.start(); - p.waitFor(); - return p; + public static void main(String[] args) throws IOException { + if (args.length != 1) { + throw new RuntimeException("PID argument required"); + } + if (!OS.IS_LINUX) { + throw new RuntimeException("Only Linux supported in this manual test case"); + } + int pid = Integer.parseInt(args[0]); + Clock clock = new SystemClock(); + NumaMapsReaderProvider readerProvider = new NumaMapsReaderProviderImpl(); + PageSizeProvider pageSizeProvider = new PageSizeProviderImpl(); + VmNumaCollector collector = new VmNumaCollector(pid, clock, readerProvider, pageSizeProvider); + VmNumaStat stat = collector.collect(); + + for (VmNumaNodeStat nodeStat : stat.getVmNodeStats()) { + BorderedTableRenderer tableRenderer = new BorderedTableRenderer(2); + tableRenderer.printHeader("Node", String.valueOf(nodeStat.getNode())); + tableRenderer.printLine("Huge", String.valueOf(nodeStat.getHugeMemory())); + tableRenderer.printLine("Heap", String.valueOf(nodeStat.getHeapMemory())); + tableRenderer.printLine("Stack", String.valueOf(nodeStat.getStackMemory())); + tableRenderer.printLine("Private", String.valueOf(nodeStat.getPrivateMemory())); + tableRenderer.render(System.out); + } } + + enum Category { + HUGE("huge"), + HEAP("heap"), + STACK("stack"), + PRIVATE("N"); + + private final String token; + + Category(String token) { + this.token = token; + } + + public String getToken() { + return token; + } + } + } diff -r 902e2e96f4e8 -r 15a168c5a68d vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaStatParser.java --- a/vm-numa/agent/src/main/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaStatParser.java Mon Jan 30 13:06:07 2017 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,86 +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.vm.numa.agent.internal; - -import java.text.ParseException; - -import com.redhat.thermostat.vm.numa.common.VmNumaNodeStat; -import com.redhat.thermostat.vm.numa.common.VmNumaStat; - -public class VmNumaStatParser { - public VmNumaStat parse(String input) throws ParseException { - VmNumaStat stat = new VmNumaStat(); - stat.setTimeStamp(System.currentTimeMillis()); - String[] lines = input.split(System.lineSeparator()); - try { - VmNumaNodeStat[] stats = parseStats(lines[4], lines[5], lines[6], lines[7]); - stat.setVmNodeStats(stats); - return stat; - } catch (NumberFormatException | ArrayIndexOutOfBoundsException | NegativeArraySizeException e) { - throw new ParseException("Unexpected input to VmNumaStatParser", 0); - } - } - - private VmNumaNodeStat[] parseStats(String HUGE, String HEAP, String STACK, String PRIVATE) { - VmNumaNodeStat[] stats; - - String[] hugeStats = splitWhitespaces(HUGE); - String[] heapStats = splitWhitespaces(HEAP); - String[] stackStats = splitWhitespaces(STACK); - String[] privateStats = splitWhitespaces(PRIVATE); - - //Take the maximum in-case of erroneous input - int numberOfNodes = -2 + Math.max(hugeStats.length, - Math.max(heapStats.length, - Math.max(stackStats.length, privateStats.length))); - - stats = new VmNumaNodeStat[numberOfNodes]; - for (int i = 0; i < numberOfNodes; i++) { - stats[i] = new VmNumaNodeStat(); - stats[i].setNode(i); - stats[i].setHugeMemory(Double.parseDouble(hugeStats[i + 1])); - stats[i].setHeapMemory(Double.parseDouble(heapStats[i + 1])); - stats[i].setStackMemory(Double.parseDouble(stackStats[i + 1])); - stats[i].setPrivateMemory(Double.parseDouble(privateStats[i + 1])); - } - return stats; - } - - private String[] splitWhitespaces(String s) { - return s.split("\\s+"); - } -} diff -r 902e2e96f4e8 -r 15a168c5a68d vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/ActivatorTest.java --- a/vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/ActivatorTest.java Mon Jan 30 13:06:07 2017 -0500 +++ b/vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/ActivatorTest.java Mon Jan 30 16:06:59 2017 -0500 @@ -45,7 +45,7 @@ import java.util.concurrent.ScheduledExecutorService; -import org.junit.Ignore; +import com.redhat.thermostat.common.Clock; import org.junit.Test; import org.osgi.framework.Bundle; import org.osgi.framework.Version; @@ -94,14 +94,9 @@ Activator activator = new Activator() { @Override - VmNumaBackend constructBackend(ScheduledExecutorService executor, VmNumaDAO vmNumaDAO, com.redhat.thermostat.common.Version version, VmStatusListenerRegistrar registrar, WriterID writerID) { - mock[0] = new VmNumaBackend(executor, vmNumaDAO, version, registrar, writerID) - { - @Override - public boolean canRegister() { - return true; - } - }; + VmNumaBackend constructBackend(ScheduledExecutorService executor, Clock clock, NumaMapsReaderProvider readerProvider, PageSizeProvider pageSizeProvider, + VmNumaDAO vmNumaDAO, com.redhat.thermostat.common.Version version, VmStatusListenerRegistrar registrar, WriterID writerID) { + mock[0] = new VmNumaBackend(executor, clock, readerProvider, pageSizeProvider, vmNumaDAO, version, registrar, writerID); return mock[0]; } }; diff -r 902e2e96f4e8 -r 15a168c5a68d vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/NumaMapsReaderProviderImplTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/NumaMapsReaderProviderImplTest.java Mon Jan 30 16:06:59 2017 -0500 @@ -0,0 +1,77 @@ +/* + * 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.vm.numa.agent.internal; + +import com.redhat.thermostat.common.portability.linux.ProcDataSource; +import com.redhat.thermostat.common.utils.StringUtils; +import org.junit.Before; +import org.junit.Test; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class NumaMapsReaderProviderImplTest { + + private ProcDataSource procDataSource; + private NumaMapsReaderProviderImpl readerProvider; + + @Before + public void setup() throws IOException { + procDataSource = mock(ProcDataSource.class); + when(procDataSource.getNumaMapsReader(anyInt())).thenReturn(new InputStreamReader(StringUtils.toInputStream(""))); + + readerProvider = new NumaMapsReaderProviderImpl(procDataSource); + } + + @Test + public void testProvidesReader() throws IOException { + BufferedReader reader = readerProvider.createReader(100); + verify(procDataSource).getNumaMapsReader(100); + assertThat(reader, is(not(equalTo(null)))); + } + +} diff -r 902e2e96f4e8 -r 15a168c5a68d vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaBackendTest.java --- a/vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaBackendTest.java Mon Jan 30 13:06:07 2017 -0500 +++ b/vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaBackendTest.java Mon Jan 30 16:06:59 2017 -0500 @@ -39,6 +39,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.mock; @@ -47,10 +48,12 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import java.text.ParseException; +import java.io.BufferedReader; +import java.io.IOException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import com.redhat.thermostat.common.Clock; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -67,12 +70,24 @@ private VmNumaBackend backend; private VmNumaDAO vmNumaDAO; private ScheduledExecutorService executor; + private Clock clock; + private NumaMapsReaderProvider readerProvider; + private PageSizeProvider pageSizeProvider; private VmStatusListenerRegistrar registrar; @Before - public void setup() { + public void setup() throws IOException { executor = mock(ScheduledExecutorService.class); vmNumaDAO = mock(VmNumaDAO.class); + clock = mock(Clock.class); + when(clock.getRealTimeMillis()).thenReturn(100L); + + readerProvider = mock(NumaMapsReaderProvider.class); + when(readerProvider.createReader(anyInt())).thenReturn(mock(BufferedReader.class)); + + pageSizeProvider = mock(PageSizeProvider.class); + when(pageSizeProvider.getPageSize()).thenReturn(4L * 1024L); + when(pageSizeProvider.getHugePageSize()).thenReturn(2048L * 1024L); Version version = mock(Version.class); when(version.getVersionNumber()).thenReturn("0.0.0"); @@ -81,7 +96,7 @@ WriterID id = mock(WriterID.class); when(id.getWriterID()).thenReturn("id"); - backend = new VmNumaBackend(executor, vmNumaDAO, version, registrar, id); + backend = new VmNumaBackend(executor, clock, readerProvider, pageSizeProvider, vmNumaDAO, version, registrar, id); } @Test @@ -126,7 +141,7 @@ } @Test - public void testStart() throws ParseException { + public void testStart() throws IOException { mockCollector(backend, 0); mockCollector(backend, 1); @@ -152,7 +167,7 @@ verifyNoMoreInteractions(vmNumaDAO); } - private void mockCollector(VmNumaBackend backend, int pid) throws ParseException { + private void mockCollector(VmNumaBackend backend, int pid) throws IOException { VmNumaCollector collector = mock(VmNumaCollector.class); when(collector.collect()).thenReturn(mock(VmNumaStat.class)); backend.setVmNumaBackendCollector(pid, collector); diff -r 902e2e96f4e8 -r 15a168c5a68d vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaCollectorTest.java --- a/vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaCollectorTest.java Mon Jan 30 13:06:07 2017 -0500 +++ b/vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaCollectorTest.java Mon Jan 30 16:06:59 2017 -0500 @@ -36,15 +36,18 @@ package com.redhat.thermostat.vm.numa.agent.internal; -import static org.junit.Assert.assertTrue; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; -import java.io.ByteArrayInputStream; +import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.text.ParseException; +import java.io.StringReader; -import org.junit.Ignore; +import com.redhat.thermostat.common.Clock; +import org.junit.Before; import org.junit.Test; import com.redhat.thermostat.vm.numa.common.VmNumaNodeStat; @@ -52,105 +55,73 @@ public class VmNumaCollectorTest { + private static final int PID = 100; + private VmNumaCollector collector; + private Clock clock; + private NumaMapsReaderProvider readerProvider; + private PageSizeProvider pageSizeProvider; + + @Before + public void setup() { + clock = mock(Clock.class); + when(clock.getRealTimeMillis()).thenReturn(100L); + + pageSizeProvider = mock(PageSizeProvider.class); + when(pageSizeProvider.getHugePageSize()).thenReturn(2048L * 1024L); + when(pageSizeProvider.getPageSize()).thenReturn(4L * 1024L); + } + @Test - public void testCollectSingleNodeStat() throws ParseException { - final String input = "\n" + - "Per-node process memory usage (in MBs) for PID 16816 (java)\n" + - " Node 0 Total\n" + - " --------------- ---------------\n" + - "Huge 0.00 0.00\n" + - "Heap 0.05 0.05\n" + - "Stack 6.27 6.27\n" + - "Private 385.07 385.07\n" + - "---------------- --------------- ---------------\n" + - "Total 391.39 391.39"; - setupCollector(input); + public void testCollectSingleNodeStat() throws IOException { + readerProvider = mock(NumaMapsReaderProvider.class); + when(readerProvider.createReader(anyInt())).thenReturn(new BufferedReader(new StringReader( + "017ec000 default heap anon=1861 dirty=1796 swapcache=65 active=1667 N0=1861 kernelpagesize_kB=4\n" + + "e09ec000 default stack anon=1776 dirty=1776 swapcache=65 active=1667 N0=1776 kernelpagesize_kB=4\n" + + "d1200000 default anon=45680 dirty=45680 active=43669 N0=45680 kernelpagesize_kB=4\n" + + "d0800000 default huge anon=456 dirty=456 active=43669 N0=456 kernelpagesize_kB=4\n" + ))); + collector = new VmNumaCollector(PID, clock, readerProvider, pageSizeProvider); VmNumaStat stat = collector.collect(); VmNumaNodeStat[] stats = stat.getVmNodeStats(); - assertTrue(stats.length == 1); + assertThat(stats.length, is(1)); VmNumaNodeStat nodeStat = stats[0]; - assertTrue(nodeStat.getNode() == 0); - assertTrue(nodeStat.getHugeMemory() == 0); - assertTrue(nodeStat.getHeapMemory() == 0.05d); - assertTrue(nodeStat.getStackMemory() == 6.27d); - assertTrue(nodeStat.getPrivateMemory() == 385.07d); + assertThat(nodeStat.getNode(), is(0)); + assertThat(nodeStat.getHugeMemory(), is(912.0d)); + assertThat(nodeStat.getHeapMemory(), is(7.26953125d)); + assertThat(nodeStat.getStackMemory(), is(6.9375d)); + assertThat(nodeStat.getPrivateMemory(), is(178.4375d)); } @Test - public void testCollectMultipleNodeStat() throws ParseException { - final String input = "\n" + - "Per-node process memory usage (in MBs) for PID 3 (ksoftirqd/0)\n" + - " Node 0 Node 1 Total\n" + - " --------------- --------------- ---------------\n" + - "Huge 0.00 0.00 0.00\n" + - "Heap 0.00 0.00 0.00\n" + - "Stack 4.00 1.00 5.00\n" + - "Private 5.00 2.00 7.00\n" + - "---------------- --------------- --------------- ---------------\n" + - "Total 9.00 3.00 12.00"; - - setupCollector(input); + public void testCollectMultipleNodeStat() throws IOException { + readerProvider = mock(NumaMapsReaderProvider.class); + when(readerProvider.createReader(anyInt())).thenReturn(new BufferedReader(new StringReader( + "017ec000 default heap anon=1861 dirty=1796 swapcache=65 active=1667 N0=1861 kernelpagesize_kB=4\n" + + "d1200000 default anon=45680 dirty=45680 active=43669 N1=45680 kernelpagesize_kB=4\n" + ))); + collector = new VmNumaCollector(PID, clock, readerProvider, pageSizeProvider); VmNumaStat stat = collector.collect(); VmNumaNodeStat[] stats = stat.getVmNodeStats(); - assertTrue(stats.length == 2); + assertThat(stats.length, is(2)); VmNumaNodeStat nodeStat1 = stats[0]; - assertTrue(nodeStat1.getNode() == 0); - assertTrue(nodeStat1.getHugeMemory() == 0d); - assertTrue(nodeStat1.getHeapMemory() == 0d); - assertTrue(nodeStat1.getStackMemory() == 4d); - assertTrue(nodeStat1.getPrivateMemory() == 5d); + assertThat(nodeStat1.getNode(), is(0)); + assertThat(nodeStat1.getHugeMemory(), is (0.0d)); + assertThat(nodeStat1.getHeapMemory(), is(7.26953125d)); + assertThat(nodeStat1.getStackMemory(), is(0.0d)); + assertThat(nodeStat1.getPrivateMemory(), is(0.0d)); VmNumaNodeStat nodeStat2 = stats[1]; - assertTrue(nodeStat2.getNode() == 1); - assertTrue(nodeStat2.getHugeMemory() == 0d); - assertTrue(nodeStat2.getHeapMemory() == 0d); - assertTrue(nodeStat2.getStackMemory() == 1d); - assertTrue(nodeStat2.getPrivateMemory() == 2d); - } - - private void setupCollector(final String input) { - collector = new VmNumaCollector(0) { - @Override - protected Process startProcess() { - return new Process() { - @Override - public OutputStream getOutputStream() { - return null; - } - - @Override - public InputStream getInputStream() { - return new ByteArrayInputStream(input.getBytes()); - } - - @Override - public InputStream getErrorStream() { - return null; - } - - @Override - public int waitFor() throws InterruptedException { - return 0; - } - - @Override - public int exitValue() { - return 0; - } - - @Override - public void destroy() { - //Do nothing - } - }; - } - }; + assertThat(nodeStat2.getNode(), is(1)); + assertThat(nodeStat2.getHugeMemory(), is(0.0d)); + assertThat(nodeStat2.getHeapMemory(), is(0.0d)); + assertThat(nodeStat2.getStackMemory(), is(0.0d)); + assertThat(nodeStat2.getPrivateMemory(), is(178.4375d)); } } diff -r 902e2e96f4e8 -r 15a168c5a68d vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaStatParserTest.java --- a/vm-numa/agent/src/test/java/com/redhat/thermostat/vm/numa/agent/internal/VmNumaStatParserTest.java Mon Jan 30 13:06:07 2017 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,147 +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.vm.numa.agent.internal; - -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.text.ParseException; - -import org.junit.Test; - -import com.redhat.thermostat.vm.numa.common.VmNumaNodeStat; -import com.redhat.thermostat.vm.numa.common.VmNumaStat; - -public class VmNumaStatParserTest { - - private final VmNumaStatParser parser = new VmNumaStatParser(); - - @Test - public void testParseSingleNodeStat() throws ParseException { - final String input = "\n" + - "Per-node process memory usage (in MBs) for PID 16816 (java)\n" + - " Node 0 Total\n" + - " --------------- ---------------\n" + - "Huge 0.00 0.00\n" + - "Heap 0.05 0.05\n" + - "Stack 6.27 6.27\n" + - "Private 385.07 385.07\n" + - "---------------- --------------- ---------------\n" + - "Total 391.39 391.39"; - - VmNumaStat stat = parser.parse(input); - VmNumaNodeStat[] stats = stat.getVmNodeStats(); - assertTrue(stats.length == 1); - VmNumaNodeStat nodeStat = stats[0]; - assertTrue(nodeStat.getNode() == 0); - assertTrue(nodeStat.getHugeMemory() == 0); - assertTrue(nodeStat.getHeapMemory() == 0.05d); - assertTrue(nodeStat.getStackMemory() == 6.27d); - assertTrue(nodeStat.getPrivateMemory() == 385.07d); - } - - @Test - public void testParseMultipleNodeStat() throws ParseException { - final String input = "\n" + - "Per-node process memory usage (in MBs) for PID 3 (ksoftirqd/0)\n" + - " Node 0 Node 1 Total\n" + - " --------------- --------------- ---------------\n" + - "Huge 0.00 0.00 0.00\n" + - "Heap 0.00 0.00 0.00\n" + - "Stack 4.00 1.00 5.00\n" + - "Private 5.00 2.00 7.00\n" + - "---------------- --------------- --------------- ---------------\n" + - "Total 9.00 3.00 12.00"; - - VmNumaStat stat = parser.parse(input); - VmNumaNodeStat[] stats = stat.getVmNodeStats(); - - assertTrue(stats.length == 2); - - VmNumaNodeStat nodeStat1 = stats[0]; - assertTrue(nodeStat1.getNode() == 0); - assertTrue(nodeStat1.getHugeMemory() == 0d); - assertTrue(nodeStat1.getHeapMemory() == 0d); - assertTrue(nodeStat1.getStackMemory() == 4d); - assertTrue(nodeStat1.getPrivateMemory() == 5d); - - VmNumaNodeStat nodeStat2 = stats[1]; - assertTrue(nodeStat2.getNode() == 1); - assertTrue(nodeStat2.getHugeMemory() == 0d); - assertTrue(nodeStat2.getHeapMemory() == 0d); - assertTrue(nodeStat2.getStackMemory() == 1d); - assertTrue(nodeStat2.getPrivateMemory() == 2d); - } - - @Test (expected = ParseException.class) - public void testParseEmptyString() throws ParseException { - String input = ""; - VmNumaStat stat = parser.parse(input); - } - - @Test (expected = ParseException.class) - public void testParseIncorrectMemoryData() throws ParseException { - final String input = "\n" + - "Per-node process memory usage (in MBs) for PID 3 (ksoftirqd/0)\n" + - " Node 0 Node 1 Total\n" + - " --------------- --------------- ---------------\n" + - "Huge ABCD 0.00 0.00\n" + - "Heap 0.00 0.00 0.00\n" + - "Stack 4.00 1.00 5.00\n" + - "Private 5.00 2.00 7.00\n" + - "---------------- --------------- --------------- ---------------\n" + - "Total 9.00 3.00 12.00"; - - parser.parse(input); - } - - @Test (expected = ParseException.class) - public void testParseIncorrectMemory() throws ParseException { - final String input = "\n" + - "Per-node process memory usage (in MBs) for PID 3 (ksoftirqd/0)\n" + - " Node 0 Node 1 Total\n" + - " --------------- --------------- ---------------\n" + - "Huge\n" + - "Heap 0.00 0.00 0.00\n" + - "Stack 4.00 1.00 5.00\n" + - "Private 5.00 2.00 7.00\n" + - "---------------- --------------- --------------- ---------------\n" + - "Total 9.00 3.00 12.00"; - - parser.parse(input); - } -}