/* * Copyright (C) 2014 Mark Clarke * * This program 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 3 of the License, or * (at your option) any later version. * * 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 for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package za.co.jumpingbean.utils; import java.io.File; import java.io.IOException; import java.net.ServerSocket; import java.net.URL; import java.net.URLClassLoader; import java.sql.Timestamp; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.collections.ObservableList; import javafx.scene.chart.XYChart.Series; import javax.inject.Inject; import za.co.jumpingbean.gc.service.GCExplorerServiceException; import za.co.jumpingbean.gc.service.GeneratorService; import za.co.jumpingbean.gc.service.constants.DESC; import za.co.jumpingbean.gcexplorer.model.MemoryPool; import za.co.jumpingbean.gcexplorer.model.ProcessFactory; import za.co.jumpingbean.gcexplorer.model.UUIDProcess; import za.co.jumpingbean.gcexplorer.ui.GCExplorer; import za.co.jumpingbean.gcexplorer.ui.ProcessViewForm; /** * * @author mark */ public class RunningProcessUpdater implements Runnable { private final Map<UUID, UUIDProcess> liveProcesses = new HashMap<>(); private int millisecs = 1000; private final GCExplorer main; private boolean isRunning = true; @Inject private final GeneratorService gen = new GeneratorService(); ExecutorService executorService = Executors.newFixedThreadPool(2); public RunningProcessUpdater(GCExplorer main) { this.main = main; } public UUIDProcess getUUIDProcess(UUID id) { synchronized (liveProcesses) { return liveProcesses.get(id); } } public void stopAllProcesses() { isRunning = false; synchronized (liveProcesses) { gen.stopAllTestApps(); this.liveProcesses.clear(); } executorService.shutdownNow(); } public void stopProcess(UUID procId) { synchronized (liveProcesses) { gen.stopTestApp(procId); this.liveProcesses.remove(procId); } } @Override public void run() { while (isRunning) { synchronized (liveProcesses) { liveProcesses.keySet().stream().forEach((procObj) -> { updateDataItems(procObj); }); try { liveProcesses.wait(millisecs); } catch (InterruptedException ex) { Logger.getLogger(RunningProcessUpdater.class.getName()).log(Level.SEVERE, null, ex); } } } } private void updateDataItems(UUID procId) { synchronized (liveProcesses) { try { UUIDProcess proc = this.liveProcesses.get(procId); int convert = main.getUnits().getConversionUnits(); final Timestamp ts = new Timestamp(System.currentTimeMillis()); double result = gen.queryJMXForValue(procId, DESC.EDENSPACEUSED).doubleValue() / convert; proc.getEdenPool().setUsedValue(result); //runLater(proc.getEdenPool(), 0, ts, result); double result2 = gen.queryJMXForValue(procId, DESC.EDENSPACECOMMITTED).doubleValue() / convert; proc.getEdenPool().setCommittedValue(result2); double result3 = gen.queryJMXForValue(procId, DESC.EDENSPACEFREE).doubleValue() / convert; proc.getEdenPool().setFreeValue(result3); runLater(proc.getEdenPool(), ts, result, result2, result3); result = gen.queryJMXForValue(procId, DESC.EDENSPACEMAX).doubleValue() / convert; proc.getEdenPool().setMaxValue(result); result = gen.queryJMXForValue(procId, DESC.SURVIVORSPACEUSED).doubleValue() / convert; proc.getSurvivorPool().setUsedValue(result); //runLater(proc.getSurvivorPool(), 0, ts, result); result2 = gen.queryJMXForValue(procId, DESC.SURVIVORSPACECOMMITTED).doubleValue() / convert; proc.getSurvivorPool().setCommittedValue(result2); result3 = gen.queryJMXForValue(procId, DESC.SURVIVORSPACEFREE).doubleValue() / convert; proc.getSurvivorPool().setFreeValue(result3); runLater(proc.getSurvivorPool(), ts, result, result2, result3); proc.getSurvivorPool().setMaxValue(gen.queryJMXForValue(procId, DESC.SURVIVORSPACEMAX).doubleValue() / convert); //Set free survivor space stats proc.getEmptySurvivorPool().setUsedValue(0d); proc.getEmptySurvivorPool().setCommittedValue(result2); proc.getEmptySurvivorPool().setFreeValue(result2); runLater(proc.getEmptySurvivorPool(), ts, 0, result2, result2); proc.getEmptySurvivorPool().setMaxValue(gen.queryJMXForValue(procId, DESC.SURVIVORSPACEMAX).doubleValue() / convert); result = gen.queryJMXForValue(procId, DESC.OLDGENSPACEUSED).doubleValue() / convert; proc.getOldGenPool().setUsedValue(result); //runLater(proc.getOldGenPool(), 0, ts, result); result2 = gen.queryJMXForValue(procId, DESC.OLDGENSPACECOMMITTED).doubleValue() / convert; proc.getOldGenPool().setCommittedValue(result2); result3 = gen.queryJMXForValue(procId, DESC.OLDGENSPACEFREE).doubleValue() / convert; proc.getOldGenPool().setFreeValue(result3); runLater(proc.getOldGenPool(), ts, result, result2, result3); proc.getOldGenPool().setMaxValue(gen.queryJMXForValue(procId, DESC.OLDGENSPACEMAX).doubleValue() / convert); result = gen.queryJMXForValue(procId, DESC.PERMGENSPACEUSED).doubleValue() / convert; proc.getPermGenPool().setUsedValue(result); //runLater(proc.getPermGenPool(), 0, ts, result); result2 = gen.queryJMXForValue(procId, DESC.PERMGENSPACECOMMITTED).doubleValue() / convert; proc.getPermGenPool().setCommittedValue(result2); result3 = gen.queryJMXForValue(procId, DESC.PERMGENSPACEFREE).doubleValue() / convert; proc.getPermGenPool().setFreeValue(result3); runLater(proc.getPermGenPool(), ts, result, result2, result3); proc.getPermGenPool().setMaxValue(gen.queryJMXForValue(procId, DESC.PERMGENSPACEMAX).doubleValue() / convert); String gcInfo = gen.getGCInfo(procId); proc.setGCInfo(gcInfo); String log = gen.getGCLogEntries(procId); if (!log.isEmpty()) { proc.updateConsoleLog(log); } //force a wait to update sys info block. Sometimes it doesn't //update display. if (proc.getDataItems().get(0).getFreeCommittedSeries().get(0).getData().size() < 6) { proc.setSysInfo(true); } else { proc.setSysInfo(false); } } catch (GCExplorerServiceException ex) { //proce removed during read. } } } public int getMillisecs() { return millisecs; } public void setMillisecs(int millisecs) { this.millisecs = millisecs; } public boolean isRunning() { return isRunning; } public UUID connectToProcess(int pid, String cmdLine) throws IOException { UUID id = gen.connectToJavaProcess(pid); String javaVersion = gen.getJavaVersion(id); synchronized (liveProcesses) { this.liveProcesses.put(id, ProcessFactory.newProcess(id, cmdLine, javaVersion)); } return id; } public UUID connectToProcess(String url, String username, String password) throws IOException { UUID id = gen.connectToJavaProcess(url, username, password); String javaVersion = gen.getJavaVersion(id); synchronized (liveProcesses) { this.liveProcesses.put(id, ProcessFactory.newProcess(id, "", javaVersion)); } return id; } public UUID launchProcess(String string, List<String> gcOptionsExtra) throws IllegalStateException, IOException { Integer port = null; //find an open port for (int i = 8181; i < 65535; i++) { try { try (ServerSocket soc = new ServerSocket(i)) { port = i; } break; } catch (IOException ex) { //socket not available continue search for new socket } } //check if we exhausted port scan if (port == null) { throw new IOException("No open ports available"); } List<String> gcOptions = new LinkedList<>(); gcOptions.add(string); gcOptions.addAll(gcOptionsExtra); UUID procId; String tmpPort = port.toString(); port++; //Get current classpath StringBuilder buffer = new StringBuilder(); for (URL url : ((URLClassLoader) (Thread.currentThread() .getContextClassLoader())).getURLs()) { buffer.append(new File(url.getPath())); buffer.append(System.getProperty("path.separator")); } String classpath = buffer.toString(); int toIndex = classpath .lastIndexOf(System.getProperty("path.separator")); classpath = classpath.substring(0, toIndex); procId = gen.startTestApp(tmpPort, classpath, "za.co.jumpingbean.gc.testApp.GarbageGeneratorApp", gcOptions); String javaVersion = gen.getJavaVersion(procId); synchronized (liveProcesses) { this.liveProcesses.put(procId, ProcessFactory.newProcess(procId, gcOptions.toString(), javaVersion)); } return procId; } public ObservableList<Series<Number, Number>> getEdenSeries(UUID procId) { synchronized (liveProcesses) { return this.liveProcesses.get(procId).getEdenPool().getFreeCommittedSeries(); } } public ObservableList<Series<Number, Number>> getSurvivorSeries(UUID procId) { synchronized (liveProcesses) { return this.liveProcesses.get(procId).getSurvivorPool().getFreeCommittedSeries(); } } public ObservableList<Series<Number, Number>> getOldGenSeries(UUID procId) { synchronized (liveProcesses) { return this.liveProcesses.get(procId).getOldGenPool().getFreeCommittedSeries(); } } public ObservableList<Series<Number, Number>> getPermGenSeries(UUID procId) { synchronized (liveProcesses) { return this.liveProcesses.get(procId).getPermGenPool().getFreeCommittedSeries(); } } private void runLater(MemoryPool pool, Timestamp ts, final double used, double committed, double free) { Platform.runLater(new Runnable() { @Override public void run() { pool.addDataPoint(ts, used, committed, free); } }); } public ObservableList<Series<String, Number>> getStackedBarChartSeries(UUID id) { UUIDProcess process; synchronized (liveProcesses) { process = liveProcesses.get(id); } return process.getStackedBarChartSeries(); } public ObservableList<Series<Number, Number>> getStackedAreaChartSeries(UUID id) { UUIDProcess process; synchronized (liveProcesses) { process = liveProcesses.get(id); } return process.getStackedAreaChartDataItems(); } public Future genLocalInstances(UUID id, int numInstances, int instanceSize, int creationPauseTime, ProcessViewForm form) { return executorService.submit(new Callable<String>() { @Override public String call() throws Exception { String result = gen.genLocalInstances(id, numInstances, instanceSize, creationPauseTime); if (result.equalsIgnoreCase("Out of memory!")) { form.setGenStatus("Process terminated - out of memory"); stopProcess(id); } else { StringBuilder str = new StringBuilder("Obj creation started with:"); str.append("Objects:\t").append(numInstances).append("\n\r"); str.append("Size(MB):\t").append(instanceSize).append("\n\r"); str.append("Creation Pause (ms):\t").append(creationPauseTime).append("\n\r"); form.setGenStatus(str.toString()); } return result; } }); } public Future genLongLivedInstances(UUID id, int numInstances, int instanceSize, int creationPauseTime, ProcessViewForm form) { return executorService.submit(new Callable<String>() { @Override public String call() throws Exception { String result = gen.genLongLivedInstances(id, numInstances, instanceSize, creationPauseTime); if (result.equalsIgnoreCase("Out of memory!")) { form.setGenStatus("Process terminated - out of memory"); stopProcess(id); } else { StringBuilder str = new StringBuilder("Obj creation started with:"); str.append("Objects:\t").append(numInstances).append("\n\r"); str.append("Size(MB):\t").append(instanceSize).append("\n\r"); str.append("Creation Pause (ms):\t").append(creationPauseTime).append("\n\r"); form.setGenStatus(str.toString()); } return result; } }); } String getParameters(UUID procId) { return this.gen.getProcessParams(procId).getStartupParameters(); } // public double getMaxHeap(UUID uuid){ // return this.liveProcesses.get(uuid).getMaxHeap(); // } public void addSystemInfoEventListener(UUID procId, ChangeListener changeListener) { synchronized (liveProcesses) { if (this.liveProcesses.get(procId) != null) { this.liveProcesses.get(procId).addSystemInfoEventListener(changeListener); } } } public void addGCInfoEventListener(UUID procId, ChangeListener changeListener) { synchronized (liveProcesses) { if (this.liveProcesses.get(procId) != null) { this.liveProcesses.get(procId).addGCInfoEventListener(changeListener); } } } public void updateNumberOfDataPoints(int numDataPoints) { synchronized (liveProcesses) { for (UUIDProcess proc : liveProcesses.values()) { proc.updateNumDataPoints(numDataPoints); } } } public int getNumber(UUID procId) { synchronized (liveProcesses) { if (liveProcesses.get(procId) != null) { return liveProcesses.get(procId).getNumber(); } else { return 0; } } } public void releaseLongLivedInstances(UUID id, int numInstances, boolean reverse) { Thread thread = new Thread(new Runnable() { @Override public void run() { gen.releaseLongLivedInstances(id, numInstances, reverse); } }); thread.setName("GC JMX Query Thread"); thread.setDaemon(true); thread.start(); } public List<String> getLocalProcessesList() { return gen.getLocalProcessesList(); } public String getProcName(UUID procId) { return "Proc " + this.getNumber(procId); } }