/* * 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.gcexplorer.ui; import java.io.IOException; import java.net.URL; import java.text.SimpleDateFormat; import java.time.temporal.TemporalUnit; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.ResourceBundle; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import javafx.event.ActionEvent; import javafx.event.Event; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Button; import javafx.scene.control.RadioButton; import javafx.scene.control.TextArea; import javafx.scene.control.TextField; import javafx.scene.control.ToggleGroup; import javafx.scene.input.InputEvent; import javafx.scene.input.KeyEvent; import javafx.stage.Stage; /** * * @author mark */ public class LaunchProcessDialog implements Initializable { @FXML private Button btnLaunch; @FXML private TextArea txtStatus; @FXML private ToggleGroup garbageCollectorGroup; @FXML private RadioButton rdbSerial; @FXML private RadioButton rdbG1; @FXML private RadioButton rdbConcMarkSweep; @FXML private RadioButton rdbParallel; @FXML private RadioButton rdbParallelOld; @FXML private RadioButton rdbUseAdaptiveSizePolicy; @FXML private RadioButton rdbUseCMSInitiatingOccupancyOnly; @FXML private RadioButton rdbUseCompressedOops; @FXML private TextField txtXms; @FXML private TextField txtXmx; @FXML private TextField txtNewSize; @FXML private TextField txtMaxNewSize; @FXML private TextField txtNewRatio; @FXML private TextField txtSurvivorRatio; @FXML private TextField txtPermSize; @FXML private TextField txtMaxPermSize; @FXML private TextField txtInitialTenuringThreshold; @FXML private TextField txtMaxTenuringThreshold; @FXML private TextField txtCMSInitiatingOccupancyFraction; @FXML private TextField txtConcGCThreads; @FXML private TextField txtParallelGCThreads; @FXML private TextField txtCMSTriggerRatio; @FXML private TextField txtCMSTriggerPermRatio; @FXML private TextField txtG1ReservePercent; @FXML private TextField txtG1HeapRegionSize; @FXML private TextField txtInitiatingHeapOccupancyPercent; @FXML private TextField txtParallelCMSThreads; @FXML private TextField txtMaxGCPauseMillis; @FXML private RadioButton rdbUseParNewGC; @FXML private RadioButton rdbVerboseGC; @FXML private RadioButton rdbPrintGCDetails; @FXML private RadioButton rdbPrintTenuringDistribution; @FXML private RadioButton rdbPrintTimeStamp; @FXML private RadioButton rdbPrintDateStamp; @FXML private RadioButton rdbLoggc; private final GCExplorer app; private final MainForm parent; public LaunchProcessDialog(GCExplorer app, MainForm parent) { this.app = app; this.parent = parent; } protected void launchProcess(ActionEvent e) { //garbageCollectorGroup.getSelectedToggle(); UUID procId; String selectedGC = (String) garbageCollectorGroup.getSelectedToggle().getUserData(); List<String> gcOptions=new LinkedList<String>(); if(!validateAndRetrieveOptions(gcOptions)){ return; } try { procId = app.getProcessController().launchProcess(selectedGC, gcOptions); } catch (IOException | IllegalStateException | NullPointerException ex) { Logger.getLogger(GCExplorer.class.getName()).log(Level.SEVERE, null, ex); StringBuilder str = new StringBuilder("Error launching process: "); str.append(ex.getMessage()); txtStatus.setText(str.toString()); return; } StringBuilder strBuilder = new StringBuilder(txtStatus.getText()); strBuilder.append("\n\r"); strBuilder.append("Process id:"); strBuilder.append(procId.toString()); strBuilder.append(" Started with GC:"); strBuilder.append(selectedGC); txtStatus.setText(strBuilder.toString()); try { parent.addPane(procId,false); ((Stage)this.btnLaunch.getScene().getWindow()).close(); } catch (IOException ex) { String txt = txtStatus.getText(); txtStatus.setText(txt + "\n\rThere was an error adding tab to display"); } e.consume(); } @Override public void initialize(URL location, ResourceBundle resources) { this.rdbConcMarkSweep.setUserData("-XX:+UseConcMarkSweepGC"); this.rdbSerial.setUserData("-XX:+UseSerialGC"); this.rdbG1.setUserData("-XX:+UseG1GC"); this.rdbParallel.setUserData("-XX:+UseParallelGC"); this.rdbParallelOld.setUserData("-XX:+UseParallelOldGC"); this.rdbUseAdaptiveSizePolicy.setUserData("-XX:+UseAdaptiveSizePolicy"); this.rdbUseCMSInitiatingOccupancyOnly.setUserData("-XX:+UseCMSInitiatingOccupancyOnly"); this.rdbSerial.setOnAction(new EventHandler() { @Override public void handle(Event event) { boolean disable = false; if (rdbSerial.isSelected()) { disable = true; } setCMSOptionsState(disable); setCommonCMSG1State(disable); setG1OptionsState(disable); setParallelGCOptionsState(disable); setCommonParallelG1State(disable); } }); this.rdbConcMarkSweep.setOnAction(new EventHandler() { @Override public void handle(Event event) { boolean disable = false; if (rdbConcMarkSweep.isSelected()) { disable = true; } setCMSOptionsState(!disable); setCommonCMSG1State(!disable); setG1OptionsState(disable); setParallelGCOptionsState(disable); setCommonParallelG1State(disable); } }); this.rdbG1.setOnAction(new EventHandler() { @Override public void handle(Event event) { boolean disable = false; if (rdbG1.isSelected()) { disable = true; } setCMSOptionsState(disable); setCommonCMSG1State(!disable); setG1OptionsState(!disable); setParallelGCOptionsState(disable); setCommonParallelG1State(!disable); } }); this.rdbParallel.setOnAction(new EventHandler() { @Override public void handle(Event event) { boolean disable = false; if (rdbParallel.isSelected()) { disable = true; } setCMSOptionsState(disable); setCommonCMSG1State(disable); setG1OptionsState(disable); setParallelGCOptionsState(!disable); setCommonParallelG1State(!disable); } }); this.rdbParallelOld.setOnAction(new EventHandler() { @Override public void handle(Event event) { boolean disable = false; if (rdbParallelOld.isSelected()) { disable = true; } setCMSOptionsState(disable); setCommonCMSG1State(disable); setG1OptionsState(disable); setParallelGCOptionsState(!disable); } }); rdbUseAdaptiveSizePolicy.setOnAction(new EventHandler() { @Override public void handle(Event event) { if (rdbUseAdaptiveSizePolicy.isSelected() && !rdbUseAdaptiveSizePolicy.isDisabled()) { txtNewRatio.disableProperty().set(true); txtMaxNewSize.disableProperty().set(true); txtNewSize.disableProperty().set(true); txtSurvivorRatio.disableProperty().set(true); } else { txtNewRatio.disableProperty().set(false); txtMaxNewSize.disableProperty().set(false); txtNewSize.disableProperty().set(false); txtSurvivorRatio.disableProperty().set(false); } } }); this.btnLaunch.setOnAction(this::launchProcess); this.rdbSerial.fireEvent(new ActionEvent()); } private void setCMSOptionsState(boolean state) { rdbUseCMSInitiatingOccupancyOnly.disableProperty().set(state); txtCMSInitiatingOccupancyFraction.disableProperty().set(state); txtCMSTriggerPermRatio.disableProperty().set(state); txtCMSTriggerRatio.disableProperty().set(state); rdbUseParNewGC.disableProperty().set(state); txtParallelCMSThreads.disableProperty().set(state); } private void setParallelGCOptionsState(boolean state) { this.rdbUseAdaptiveSizePolicy.disableProperty().set(state); this.rdbUseAdaptiveSizePolicy.fireEvent(new ActionEvent()); this.txtMaxGCPauseMillis.disableProperty().set(state); txtParallelGCThreads.disableProperty().set(state); } private void setCommonCMSG1State(boolean state) { txtConcGCThreads.disableProperty().set(state); } private void setCommonParallelG1State(boolean state) { txtParallelGCThreads.disableProperty().set(state); } private void setG1OptionsState(boolean state) { txtG1HeapRegionSize.disableProperty().set(state); txtG1ReservePercent.disableProperty().set(state); txtInitiatingHeapOccupancyPercent.disableProperty().set(state); } public void validateAndRetrieveOptions(ActionEvent event){ validateAndRetrieveOptions(new LinkedList<String>()); } public void validateAndRetrieveOptions(KeyEvent event){ validateAndRetrieveOptions(new LinkedList<String>()); } private boolean validateAndRetrieveOptions(List<String> list) { StringBuilder errorMsg = new StringBuilder(); this.addTextFieldOptionBetween(txtCMSInitiatingOccupancyFraction, "-XX:CMSInitiatingOccupancyFraction=" + txtCMSInitiatingOccupancyFraction.getText(), "CMSInitiatingOccupancyFraction", 0, 100, list, errorMsg); this.addTextFieldOption(txtCMSTriggerPermRatio, "-XX:CMSTriggerPermRatio=" + txtCMSTriggerPermRatio.getText(), "CMSTriggerPermRatio", list, errorMsg); this.addTextFieldOption(txtCMSTriggerRatio, "-XX:CMSTriggerRatio=" + txtCMSTriggerPermRatio.getText(), "CMSTriggerRatio", list, errorMsg); this.addTextFieldOption(txtConcGCThreads, "-XX:ConcGCThreads=" + txtConcGCThreads.getText(), "ConcGCThreads", list, errorMsg); this.addTextFieldOption(txtG1HeapRegionSize, "-XX:G1HeapRegionSize=" + txtG1HeapRegionSize.getText(), "G1HeapRegionSize", list, errorMsg); this.addTextFieldOptionBetween(txtG1ReservePercent, "-XX:G1ReservePercent=" + txtG1ReservePercent.getText(), "G1ReservePercent=", 0, 100, list, errorMsg); this.addTextFieldOption(txtInitiatingHeapOccupancyPercent, "-XX:InitiatingHeapOccupancyPercent=" + txtInitiatingHeapOccupancyPercent.getText(), "InitiatingHeapOccupancyPercent", list, errorMsg); this.addTextFieldOption(txtMaxGCPauseMillis, "-XX:MaxGCPauseMillis=" + txtMaxGCPauseMillis.getText(), "MaxGCPauseMillis", list, errorMsg); this.addTextFieldOption(txtMaxNewSize, "-XX:MaxNewSize=" + txtMaxNewSize.getText() + "m", "MaxNewSize", list, errorMsg); this.addTextFieldOption(txtMaxPermSize, "-XX:MaxPermSize=" + txtMaxPermSize.getText() + "m", "MaxPermSize", list, errorMsg); this.addTextFieldOptionBetween(txtMaxTenuringThreshold, "-XX:MaxTenuringThreshold=" + txtMaxTenuringThreshold.getText(), "MaxTenuringThreshold", 0, 15, list, errorMsg); // int tmpMax = 0; // if (!txtMaxTenuringThreshold.getText().isEmpty()) { // try { // String text = txtMaxTenuringThreshold.getText(); // tmpMax = Integer.parseInt(text); // tmpMax = tmpMax > 15 ? 15 : tmpMax; // } catch (NumberFormatException ex) { // tmpMax = 0; // } // } // this.addTextFieldOptionBetween(txtInitialTenuringThreshold, "-XX:InitialTenuringThreshold=" // + txtInitialTenuringThreshold.getText(), "InitialTenuringThreshold", 0, tmpMax, list, errorMsg); this.addTextFieldOption(txtInitialTenuringThreshold, "-XX:InitialTenuringThreshold=" + txtInitialTenuringThreshold.getText(),"-XX:InitialTenuringThreshold=", list, errorMsg); this.addTextFieldOption(txtNewRatio, "-XX:NewRatio=" + txtNewRatio.getText(), "-XX:NewRatio=", list, errorMsg); this.addTextFieldOption(txtNewSize, "-XX:NewSize=" + txtNewSize.getText() + "m", "-XX:NewSize=", list, errorMsg); this.addTextFieldOption(txtParallelCMSThreads, "-XX:ParallelCMSThreads=" + txtParallelCMSThreads.getText(), "-XX:ParallelCMSThreads=", list, errorMsg); this.addTextFieldOption(txtParallelGCThreads, "-XX:ParallelGCThreads=" + txtParallelGCThreads.getText(), "-XX:ParallelGCThreads=", list, errorMsg); this.addTextFieldOption(txtPermSize, "-XX:PermSize=" + txtPermSize.getText() + "m", "-XX:PermSize==", list, errorMsg); this.addTextFieldOption(txtSurvivorRatio, "-XX:SurvivorRatio=" + txtSurvivorRatio.getText(), "-XX:SurvivorRatio=", list, errorMsg); int xms = -1; if (!txtXms.isDisabled() && !txtXms.getText().isEmpty()) { if (isNumber(txtXms.getText())) { xms = getNumber(txtXms.getText()); list.add("-Xms" + txtXms.getText() + "m"); } else { errorMsg.append("-Xms must be a number\n\r"); } } int xmx = -1; if (!txtXmx.isDisabled() && !txtXmx.getText().isEmpty()) { if (isNumber(txtXmx.getText())) { xmx = Integer.parseInt(txtXmx.getText()); list.add("-Xmx" + txtXmx.getText() + "m"); } else { errorMsg.append("-Xmx must be a number\n\r"); } } if (xms != -1 && xmx != -1 && xms > xmx) { errorMsg.append("-Xms cannot be greater than -Xmx"); } if (!rdbUseAdaptiveSizePolicy.isDisabled() && rdbUseAdaptiveSizePolicy.isSelected()) { list.add("-XX:+UseAdaptiveSizePolicy"); } if (!rdbUseCMSInitiatingOccupancyOnly.isDisabled() && rdbUseCMSInitiatingOccupancyOnly.isSelected()) { list.add("-XX:+UseCMSInitiatingOccupancyOnly"); } if (!rdbUseParNewGC.isDisabled() && rdbUseParNewGC.isSelected()) { list.add("-XX:-UseParNewGC"); } if (!rdbUseCompressedOops.isDisabled() && rdbUseCompressedOops.isSelected()) { list.add("-XX:+UseCompressedOops"); } if (rdbVerboseGC.isSelected()){ list.add("-verbose:gc"); } if (rdbLoggc.isSelected()){ StringBuilder str = new StringBuilder("-Xloggc:gc-"); str.append(new SimpleDateFormat("yyyyMMddhhmm").format(new Date())); list.add(str.append(".log").toString()); } if (rdbPrintDateStamp.isSelected()){ list.add("-XX:+PrintGCDateStamps"); } if (rdbPrintTimeStamp.isSelected()){ list.add("-XX:+PrintGCTimeStamps"); } if (rdbPrintGCDetails.isSelected()){ list.add("-XX:+PrintGCDetails"); } if (rdbPrintTenuringDistribution.isSelected()){ list.add("-XX:+PrintTenuringDistribution"); } if (errorMsg.toString().isEmpty()) { StringBuilder options = new StringBuilder(); for (String str : list){ options.append(str).append(" "); } txtStatus.setText(options.toString()); return true; } else { txtStatus.setText(errorMsg.toString()); return false; //throw new IllegalStateException("Invalid parameter inputs"); } } private void addTextFieldOption(TextField field, String param, String fieldLabel, List<String> list, StringBuilder errorMsg) { if (!field.isDisabled() && !field.getText().isEmpty()) { if (isNumber(field.getText())) { list.add(param); } else { errorMsg.append(fieldLabel).append(" must be a number\n\r"); } } } // private void addTextFieldOptionGreaterThan(TextField field, String param, String fieldLabel, int maxVal, // List<String> list, StringBuilder errorMsg) { // if (!field.isDisabled() // && !field.getText().isEmpty()) { // if (isNumber(field.getText())) { // int num = getNumber(field.getText()); // if (num < maxVal) { // list.add(param); // }else{ // errorMsg.append(fieldLabel).append(" must be less than ").append(maxVal).append("\n\r"); // } // } else { // errorMsg.append(fieldLabel).append(" must be a number\n\r"); // } // } // } private void addTextFieldOptionBetween(TextField field, String param, String fieldLabel, int minVal, int maxVal, List<String> list, StringBuilder errorMsg) { if (!field.isDisabled() && !field.getText().isEmpty()) { if (isNumber(field.getText())) { int num = getNumber(field.getText()); if (num <= maxVal && num >= minVal) { list.add(param); } else { errorMsg.append(fieldLabel).append(" must be greater than equal to "). append(minVal).append(" less than equal to ").append(maxVal).append("\n\r"); } } else { errorMsg.append(fieldLabel).append(" must be a number\n\r"); } } } private boolean isNumber(String text) { try { Integer.parseInt(text); return true; } catch (NumberFormatException ex) { return false; } } private int getNumber(String text) { int number = Integer.parseInt(text); return number; } }