/* * RHQ Management Platform * Copyright (C) 2005-2008 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * This program 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 and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.core.system; import java.io.File; import java.net.InetAddress; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Vector; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.hyperic.sigar.Mem; import org.hyperic.sigar.NetConnection; import org.hyperic.sigar.ProcMem; import org.hyperic.sigar.Sigar; import org.hyperic.sigar.Swap; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests the Native System stuff. * * @author John Mazzitelli */ @Test(groups = "native.system") public class NativeSystemInfoTest { private static final boolean ENABLED = true; @AfterMethod @BeforeMethod public void terminateNativeLibrary() { try { SystemInfoFactory.shutdown(); } catch (Throwable ignore) { } } /** * Tests getting network connection information. */ @Test(enabled = ENABLED) public void testGetNetworkConnections() { SystemInfo sysinfo = SystemInfoFactory.createSystemInfo(); if (!sysinfo.isNative()) { System.out.println("~~~ Native library is not available - skipping testGetNetworkConnections"); return; } List<NetConnection> allConnections = sysinfo.getNetworkConnections(null, 0); assert allConnections != null; if (allConnections.size() > 0) { String localAddress = allConnections.get(0).getLocalAddress(); int localPort = (int) allConnections.get(0).getLocalPort(); List<NetConnection> filtered = sysinfo.getNetworkConnections(localAddress, localPort); assert filtered != null; assert filtered.size() > 0 : "connection should not be missing - did it close that fast since we last seen it?"; assert localAddress.equals(filtered.get(0).getLocalAddress()); assert localPort == (int) filtered.get(0).getLocalPort(); } else { System.out.println("testGetNetworkConnections: no active connections - will not perform filtering tests"); } } /** * Tests getting network adapter information. */ @Test(enabled = ENABLED) public void testGetNetworkAdapterInfo() { SystemInfo sysinfo = SystemInfoFactory.createSystemInfo(); if (!sysinfo.isNative()) { System.out.println("~~~ Native library is not available - skipping testGetNetworkAdapterInfo"); return; } List<NetworkAdapterInfo> adapters = sysinfo.getAllNetworkAdapters(); assert adapters != null; assert adapters.size() > 0 : "You can only run this on a machine with at least one detectable network interface"; for (NetworkAdapterInfo adapter : adapters) { assert adapter.getName() != null : adapter; assert adapter.getDisplayName() != null : adapter; assert adapter.getDescription() != null : adapter; //assert adapter.getMacAddressString() != null : adapter; // looks like loopbacks don't have mac addresses, who else doesn't? assert adapter.getType() != null : adapter; assert adapter.getOperationalStatus() != null : adapter; //assert adapter.isDhcpEnabled() != null : adapter; // SIGAR doesn't support this yet //assert adapter.getDnsServers() != null : adapter; // SIGAR doesn't support this yet assert adapter.getUnicastAddresses() != null : adapter; assert adapter.getMulticastAddresses() != null : adapter; // SIGAR doesn't support this yet /* * for ( InetAddress addr : adapter.getDnsServers() ) { assert addr.getAddress() != null; assert * addr.getHostAddress() != null; } */ for (InetAddress addr : adapter.getUnicastAddresses()) { assert addr.getAddress() != null; assert addr.getHostAddress() != null; } for (InetAddress addr : adapter.getMulticastAddresses()) { assert addr.getAddress() != null; assert addr.getHostAddress() != null; } assert sysinfo.getNetworkAdapterStats(adapter.getName()) != null; System.out.println("Network adapter found: " + adapter); } } /** * Tests getting service info. * * @throws Exception */ @Test(enabled = ENABLED) public void testGetAllServices() throws Exception { final SystemInfo sysinfo = SystemInfoFactory.createSystemInfo(); if (!sysinfo.isNative()) { System.out.println("~~~ Native library is not available - skipping testGetAllServices"); return; } if (sysinfo.getOperatingSystemType() != OperatingSystemType.WINDOWS) { System.out.println("~~~ Native library is not windows - skipping testGetAllServices"); return; } List<ServiceInfo> allServices = sysinfo.getAllServices(); //System.out.println( "All installed services: " + allServices ); assert allServices != null; assert allServices.size() > 0; } @Test(enabled = ENABLED) public void testNativeMemory() throws Exception { Sigar sigar = new Sigar(); try { Mem allMemory = sigar.getMem(); Swap allSwap = sigar.getSwap(); NumberFormat nf = NumberFormat.getNumberInstance(); System.out.println("All Memory:" + "\nActualFreeMem=" + nf.format(allMemory.getActualFree()) + "\nActualUsedMem=" + nf.format(allMemory.getActualUsed()) + "\n RamMem=" + nf.format(allMemory.getRam()) + "\n FreeMem=" + nf.format(allMemory.getFree()) + "\n UsedMem=" + nf.format(allMemory.getUsed()) + "\n TotalMem=" + nf.format(allMemory.getTotal()) + "\n FreeSwap=" + nf.format(allSwap.getFree()) + "\n UsedSwap=" + nf.format(allSwap.getUsed()) + "\n TotalSwap=" + nf.format(allSwap.getTotal())); assert allMemory.getActualFree() > 0 : allMemory.getActualFree(); assert allMemory.getFree() > 0 : allMemory.getFree(); assert allMemory.getTotal() > 1000000 : allMemory.getTotal(); if (allSwap.getTotal()>0) { assert allSwap.getTotal() > 1000000 : allSwap.getTotal(); assert allSwap.getFree() > 0 : allSwap.getFree(); } ProcMem processMemory = sigar.getProcMem(sigar.getPid()); System.out.println("Process Memory:" + "\n MinorFaults=" + nf.format(processMemory.getMinorFaults()) + "\n MajorFaults=" + nf.format(processMemory.getMajorFaults()) + "\n PageFaults=" + nf.format(processMemory.getPageFaults()) + "\n Resident=" + nf.format(processMemory.getResident()) + "\n Share=" + nf.format(processMemory.getShare()) + "\n Size=" + nf.format(processMemory.getSize())); assert processMemory.getResident() > 0 : processMemory.getResident(); assert processMemory.getSize() > 0 : processMemory.getSize(); } finally { sigar.close(); } } /** * Tests getting process memory usage information. */ @Test(enabled = ENABLED) public void testProcessMemory() { final SystemInfo sysinfo = SystemInfoFactory.createSystemInfo(); if (!sysinfo.isNative()) { System.out.println("~~~ Native library is not available - skipping testProcessMemory"); return; } // just a side test - need to make sure this works assert SystemInfoFactory.getNativeSystemInfoVersion() != null; Mem allMemory = sysinfo.getMemoryInfo(); ProcMem procMemory = sysinfo.getThisProcess().getMemory(); assert allMemory != null; assert procMemory != null; assert allMemory.getUsed() > procMemory.getResident() : allMemory.getUsed() + "->" + procMemory.getSize(); } /** * Try to create alot of native objects to see if we hit memory leaks. * * @throws Exception */ @Test(enabled = ENABLED) public void testCreateAlotOfNativeObjects() throws Exception { System.out.println("Creating alot of native Who objects"); for (int i = 0; i < 1000; i++) { Sigar sigar = new Sigar(); sigar.getWhoList(); sigar.close(); } System.out.println("Creating alot of native Cpu objects"); for (int i = 0; i < 1000; i++) { Sigar sigar = new Sigar(); sigar.getCpuList(); sigar.close(); } System.out.println("Creating alot of native FileInfo objects"); File tmpFile = File.createTempFile("native", "test"); String tmp = tmpFile.getAbsolutePath(); try { for (int i = 0; i < 500; i++) { Sigar sigar = new Sigar(); sigar.getFileInfo(tmp); sigar.close(); } } finally { tmpFile.delete(); } } /** * Make sure we don't crash the VM when we shutdown then reinitialize the native libraries. * * @throws Exception */ @Test(enabled = ENABLED) public void testShutdownInitialize() throws Exception { Sigar sigar = null; try { sigar = new Sigar(); sigar.getCpu(); } catch (Throwable t) { System.out.println("~~~ Native library is not available - cannot test the shutdown/init cycle"); return; } finally { if (sigar != null) { sigar.close(); } } } /** * Test the ability to disable the native library and then reenable it. */ @Test(enabled = ENABLED) public void testDisable() { SystemInfoFactory.disableNativeSystemInfo(); assert SystemInfoFactory.isNativeSystemInfoDisabled() : "Should have been disabled"; assert !SystemInfoFactory.isNativeSystemInfoInitialized() : "Should never have been initialized"; // we disabled the native stuff processJavaSystemInfo(SystemInfoFactory.createSystemInfo()); assert !SystemInfoFactory.isNativeSystemInfoInitialized() : "Should never have been initialized"; SystemInfoFactory.enableNativeSystemInfo(); assert !SystemInfoFactory.isNativeSystemInfoDisabled() : "Should have been re-enabled"; } /** * Tests the "non-native" Java system info. */ @Test(enabled = ENABLED) public void testJava() { processJavaSystemInfo(SystemInfoFactory.createJavaSystemInfo()); } /** * Just a test to run concurrent requests through the native layer. * * @throws Exception */ @Test(enabled = ENABLED) public void testNativeConcurrency() throws Exception { final SystemInfo sysinfo = SystemInfoFactory.createSystemInfo(); if (!sysinfo.isNative()) { System.out.println("~~~ Native library is not available - skipping testNativeConcurrency"); return; } final int hostname = sysinfo.getHostname().hashCode(); final int osname = sysinfo.getOperatingSystemName().hashCode(); final int osver = sysinfo.getOperatingSystemVersion().hashCode(); final List<Throwable> errors = new Vector<Throwable>(); final int[] count = new int[] { 0 }; class TestConcurrencyTask implements Callable<Object> { public Object call() { try { synchronized (count) { count[0]++; } assert sysinfo.getHostname().hashCode() == hostname; assert sysinfo.getOperatingSystemName().hashCode() == osname; assert sysinfo.getOperatingSystemVersion().hashCode() == osver; } catch (Throwable t) { errors.add(t); } return null; } } // run numTasks total tasks, with numConcurrency being the number we run concurrently at any one time int numTasks = 2000; int numConcurrency = 500; Collection<Callable<Object>> tasks = new ArrayList<Callable<Object>>(numTasks); for (int i = 0; i < numTasks; i++) { tasks.add(new TestConcurrencyTask()); } ExecutorService pool = Executors.newFixedThreadPool(numConcurrency); System.gc(); long beforeFree = Runtime.getRuntime().freeMemory(); long beforeTotal = Runtime.getRuntime().totalMemory(); System.gc(); pool.invokeAll(tasks, 30L, TimeUnit.SECONDS); System.gc(); long afterFree = Runtime.getRuntime().freeMemory(); long afterTotal = Runtime.getRuntime().totalMemory(); pool.shutdown(); assert pool.awaitTermination(120, TimeUnit.SECONDS) : "Wow - needed to wait longer than 120 seconds to finish"; assert count[0] == numTasks : "For some reason, we didn't run [" + numTasks + "] tasks: " + count[0]; assert errors.size() == 0 : "Got some errors: " + errors; long beforeUsed = beforeTotal - beforeFree; long afterUsed = afterTotal - afterFree; System.out.println("1 native concurrency mem usage: before(free/total/used):after(free/total/used)\n" + beforeFree + '/' + beforeTotal + '/' + beforeUsed + ':' + afterFree + '/' + afterTotal + '/' + afterUsed); if (!((afterUsed - beforeUsed) < 500000)) { System.out.println("Hmm.. why did the concurrency test leave 500KB memory still used?"); } long firstTestUsed = afterUsed; // run it a second time count[0] = 0; pool = Executors.newFixedThreadPool(numConcurrency); System.gc(); beforeFree = Runtime.getRuntime().freeMemory(); beforeTotal = Runtime.getRuntime().totalMemory(); System.gc(); pool.invokeAll(tasks, 30L, TimeUnit.SECONDS); System.gc(); afterFree = Runtime.getRuntime().freeMemory(); afterTotal = Runtime.getRuntime().totalMemory(); pool.shutdown(); assert pool.awaitTermination(120, TimeUnit.SECONDS) : "Wow - needed to wait longer than 120 seconds to finish"; assert count[0] == numTasks : "For some reason, we didn't run [" + numTasks + "] tasks: " + count[0]; assert errors.size() == 0 : "Got some errors: " + errors; beforeUsed = beforeTotal - beforeFree; afterUsed = afterTotal - afterFree; System.out.println("2 native concurrency mem usage: before(free/total/used):after(free/total/used)\n" + beforeFree + '/' + beforeTotal + '/' + beforeUsed + ':' + afterFree + '/' + afterTotal + '/' + afterUsed); if (!((afterUsed - beforeUsed) < 2500000)) { System.out.println("Hmm.. why did the concurrency test leave 250KB memory still used?"); } long secondTestUsed = afterUsed; if (!(firstTestUsed >= (secondTestUsed - 50000))) { System.out.println("Hmm.. why did the second concurrency test leak more than 50K mem?"); } } /** * Tests getting stuff if there is no native library available (only can test if this is running on a platform that * doesn't have the native libraries in java.library.path) */ @Test(enabled = ENABLED) public void testNonNative() { if (SystemInfoFactory.isNativeSystemInfoAvailable()) { System.out.println("~~~ Native library is available - cannot test the fallback-to-Java feature"); return; } // since there is no native libraries available, this should fallback to the Java implementation processJavaSystemInfo(SystemInfoFactory.createSystemInfo()); } /** * Test for memory leaks in the native layer. */ @Test(enabled = ENABLED) public void testMemoryLeak() { final SystemInfo sysinfo = SystemInfoFactory.createSystemInfo(); if (!sysinfo.isNative()) { System.out.println("~~~ Native library is not available - skipping testMemoryLeak"); return; } // get memory MemoryLeakChecker.checkForMemoryLeak(new Runnable() { public void run() { sysinfo.getMemoryInfo(); } }, "native-getMemory", 10, 2500, MemoryLeakChecker.JAVA_AND_NATIVE); // get operating system name/version MemoryLeakChecker.checkForMemoryLeak(new Runnable() { public void run() { sysinfo.getOperatingSystemName(); sysinfo.getOperatingSystemVersion(); } }, "native-getOSName/Version", 10, 2500, MemoryLeakChecker.JAVA_AND_NATIVE); // get hostname MemoryLeakChecker.checkForMemoryLeak(new Runnable() { public void run() { sysinfo.getHostname(); } }, "native-getHostname", 5, 250, MemoryLeakChecker.JAVA_AND_NATIVE); // execute processes MemoryLeakChecker.checkForMemoryLeak(new Runnable() { public void run() { ProcessExecution start = new ProcessExecution(""); setupProgram(start); sysinfo.executeProcess(start); } }, "native-executeProcess", 4, 50, MemoryLeakChecker.JAVA_AND_NATIVE); // get process table information MemoryLeakChecker.checkForMemoryLeak(new Runnable() { public void run() { sysinfo.getAllProcesses(); } }, "native-getAllProcesses", 4, 50, MemoryLeakChecker.JAVA_AND_NATIVE); } /** * Just a test to run concurrent requests through the java layer that executes processes. * * @throws Exception */ @Test(enabled = ENABLED) public void testNativeConcurrencyExec() throws Exception { final SystemInfo sysinfo = SystemInfoFactory.createSystemInfo(); if (!sysinfo.isNative()) { System.out.println("~~~ Native library is not available - skipping testNativeConcurrencyExec"); return; } final List<Throwable> errors = new Vector<Throwable>(); final int[] count = new int[] { 0 }; final ProcessExecution start = new ProcessExecution("dummy"); setupProgram(start); class TestExecConcurrencyTask implements Callable<Object> { public Object call() { try { synchronized (count) { count[0]++; } ProcessExecutionResults results = sysinfo.executeProcess(start); if (results.getError() != null) { throw results.getError(); } if (start.isCaptureOutput()) { assert results.getCapturedOutput() != null : "no captured output"; for (int i = 0; i < 10; i++) { if (results.getCapturedOutput().length() > 0) { break; } Thread.sleep(500L); // give it a chance to slurp the output } assert results.getCapturedOutput().length() > 0 : "captured output is empty"; } } catch (Throwable t) { errors.add(t); } return null; } } // run numTasks total tasks, with numConcurrency being the number we run concurrently at any one time int numTasks = 50; int numConcurrency = 30; Collection<Callable<Object>> tasks = new ArrayList<Callable<Object>>(numTasks); for (int i = 0; i < numTasks; i++) { tasks.add(new TestExecConcurrencyTask()); } ExecutorService pool = Executors.newFixedThreadPool(numConcurrency); System.gc(); long beforeFree = Runtime.getRuntime().freeMemory(); long beforeTotal = Runtime.getRuntime().totalMemory(); System.gc(); pool.invokeAll(tasks, 30L, TimeUnit.SECONDS); System.gc(); long afterFree = Runtime.getRuntime().freeMemory(); long afterTotal = Runtime.getRuntime().totalMemory(); pool.shutdown(); assert pool.awaitTermination(120, TimeUnit.SECONDS) : "Wow - needed to wait longer than 120 seconds to finish"; assert count[0] == numTasks : "For some reason, we didn't run [" + numTasks + "] tasks: " + count[0]; assert errors.size() == 0 : "Got some errors: " + errors; long beforeUsed = beforeTotal - beforeFree; long afterUsed = afterTotal - afterFree; System.out.println("1 native concurrency mem usage: before(free/total/used):after(free/total/used)\n" + beforeFree + '/' + beforeTotal + '/' + beforeUsed + ':' + afterFree + '/' + afterTotal + '/' + afterUsed); if (!((afterUsed - beforeUsed) < 500000)) { System.out.println("Hmm.. why did the concurrency test leave 500KB memory still used?"); } long firstTestUsed = afterUsed; // run it a second time start.setCaptureOutput(true); // see if this causes any problems start.setWaitForCompletion(5000L); count[0] = 0; pool = Executors.newFixedThreadPool(numConcurrency); System.gc(); beforeFree = Runtime.getRuntime().freeMemory(); beforeTotal = Runtime.getRuntime().totalMemory(); System.gc(); pool.invokeAll(tasks, 30L, TimeUnit.SECONDS); System.gc(); afterFree = Runtime.getRuntime().freeMemory(); afterTotal = Runtime.getRuntime().totalMemory(); pool.shutdown(); assert pool.awaitTermination(120, TimeUnit.SECONDS) : "Wow - needed to wait longer than 120 seconds to finish"; assert count[0] == numTasks : "For some reason, we didn't run [" + numTasks + "] tasks: " + count[0]; assert errors.size() == 0 : "Got some errors: " + errors; beforeUsed = beforeTotal - beforeFree; afterUsed = afterTotal - afterFree; System.out.println("2 native concurrency mem usage: before(free/total/used):after(free/total/used)\n" + beforeFree + '/' + beforeTotal + '/' + beforeUsed + ':' + afterFree + '/' + afterTotal + '/' + afterUsed); if (!((afterUsed - beforeUsed) < 2500000)) { System.out.println("Hmm.. why did the concurrency test leave 250KB memory still used?"); } long secondTestUsed = afterUsed; if (!(firstTestUsed >= (secondTestUsed - 50000))) { System.out.println("Hmm.. why did the second concurrency test leak more than 50K mem?"); } } /** * Picks a program that can be executed on the test platform. This is not fool proof, might need to tweek this in * case, for example, some platforms do not have "/bin/ls". * * @param start */ private void setupProgram(ProcessExecution start) { // just pick some short-lived executable if (File.separatorChar == '\\') { start.setExecutable("C:\\WINDOWS\\system32\\find.exe"); start.setArguments(new String[] { "/C", "\"yaddayadda\"", "C:\\WINDOWS\\WIN.INI" }); } else { start.setExecutable("/bin/ls"); start.setArguments(new String[] { "/bin" }); } start.setCaptureOutput(false); start.setWaitForCompletion(-1); return; } /** * Performs some tests on the given system info, where the sysinfo must be of type {@link JavaSystemInfo}, otherwise * the test will fail. * * @param sysinfo */ private void processJavaSystemInfo(SystemInfo sysinfo) { boolean isNative = sysinfo.isNative(); String hostname = sysinfo.getHostname(); String operatingSystemName = sysinfo.getOperatingSystemName(); assert sysinfo instanceof JavaSystemInfo : "The SystemInfo is not the Java fallback implementation: " + sysinfo.getClass(); try { sysinfo.getAllProcesses(); assert false : "Java sysinfo should not have been able to give us process table information"; } catch (UnsupportedOperationException ok) { } assert !isNative : "Should not have been native"; assert !(sysinfo instanceof NativeSystemInfo) : "Should not have been in the NativeSystemInfo hierarchy"; assert hostname != null; assert operatingSystemName != null; System.out.println("JAVA: is-native=" + isNative); System.out.println("JAVA: hostname=" + hostname); System.out.println("JAVA: os name=" + operatingSystemName); } }