From 7e542d630920c063ee0bdc7a9767327c919bd756 Mon Sep 17 00:00:00 2001 From: Chris Newland Date: Tue, 26 Sep 2017 22:41:46 +0100 Subject: [PATCH] Snapshot button on main UI windows --- build.properties | 2 +- core/pom.xml | 2 +- .../main/resources/examples/LoopUnroll.java | 189 ++++++++++++++++++ pom.xml | 2 +- ui/pom.xml | 2 +- .../ui/compilechain/CompileChainStage.java | 24 ++- .../jitwatch/ui/main/JITWatchUI.java | 5 +- .../jitwatch/ui/sandbox/SandboxStage.java | 19 +- .../jitwatch/ui/triview/TriView.java | 35 ++-- .../jitwatch/util/UserInterfaceUtil.java | 74 ++++++- ui/src/main/resources/images/camera.png | Bin 0 -> 3094 bytes 11 files changed, 322 insertions(+), 32 deletions(-) create mode 100644 core/src/main/resources/examples/LoopUnroll.java create mode 100755 ui/src/main/resources/images/camera.png diff --git a/build.properties b/build.properties index 2d48fba9..2da9394b 100644 --- a/build.properties +++ b/build.properties @@ -1 +1 @@ -version=1.2.1 +version=1.2.2 diff --git a/core/pom.xml b/core/pom.xml index 0715a76a..a696da8d 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ com.chrisnewland jitwatch-parent - 1.2.1 + 1.2.2 jitwatch-core diff --git a/core/src/main/resources/examples/LoopUnroll.java b/core/src/main/resources/examples/LoopUnroll.java new file mode 100644 index 00000000..9aef9926 --- /dev/null +++ b/core/src/main/resources/examples/LoopUnroll.java @@ -0,0 +1,189 @@ + +public class LoopUnroll +{ + private static final int MAX = 1_000_000; + + private long[] data = new long[MAX]; + + public LoopUnroll() + { + createData(); + + long total = 0; + + total += constantStride1Int(); + total += constantStride2Int(); + total += constantStride4Int(); + total += constantStride8Int(); + total += constantStride16Int(); + total += variableStrideInt(1); + total += constantStride1Long(); + total += constantStride1IntWithExit(); + total += constantStride1IntWith2Exits(); + total += constantStride1IntWith4Exits(); + + System.out.println("Total: " + total); + } + + private void createData() + { + java.util.Random random = new java.util.Random(); + + for (int i = 0; i < MAX; i++) + { + data[i] = random.nextLong(); + } + } + + private long constantStride1Int() + { + long sum = 0; + for (int i = 0; i < MAX; i += 1) + { + sum += data[i]; + } + return sum; + } + + private long constantStride2Int() + { + long sum = 0; + for (int i = 0; i < MAX; i += 2) + { + sum += data[i]; + } + return sum; + } + + private long constantStride4Int() + { + long sum = 0; + for (int i = 0; i < MAX; i += 4) + { + sum += data[i]; + } + return sum; + } + + private long constantStride8Int() + { + long sum = 0; + for (int i = 0; i < MAX; i += 8) + { + sum += data[i]; + } + return sum; + } + + private long constantStride16Int() + { + long sum = 0; + for (int i = 0; i < MAX; i += 16) + { + sum += data[i]; + } + return sum; + } + + private long variableStrideInt(int stride) + { + long sum = 0; + for (int i = 0; i < MAX; i += stride) + { + sum += data[i]; + } + return sum; + } + + private long constantStride1Long() + { + long sum = 0; + for (long l = 0; l < MAX; l++) + { + // cast because array index can't be longer than int + sum += data[(int) l]; + } + return sum; + } + + // NICE !!! unrolls with test on each unroll :) + private long constantStride1IntWithExit() + { + long sum = 0; + + for (int i = 0; i < MAX; i += 1) + { + if (data[i] == 0x1234) + { + break; + } + else + { + sum += data[i]; + } + } + + return sum; + } + + // NICE !!! unrolls with test on each unroll :) + private long constantStride1IntWith2Exits() + { + long sum = 0; + + for (int i = 0; i < MAX; i += 1) + { + if (data[i] == 0x1234) + { + break; + } + else if (data[i] == 0x5678) + { + break; + } + else + { + sum += data[i]; + } + } + + return sum; + } + + // NICE !!! unrolls with test on each unroll :) + private long constantStride1IntWith4Exits() + { + long sum = 0; + + for (int i = 0; i < MAX; i += 1) + { + if (data[i] == 0x1234) + { + break; + } + else if (data[i] == 0x5678) + { + break; + } + else if (data[i] == 0x9ABC) + { + break; + } + else if (data[i] == 0xDEF0) + { + break; + } + else + { + sum += data[i]; + } + } + + return sum; + } + + public static void main(String[] args) + { + new LoopUnroll(); + } +} diff --git a/pom.xml b/pom.xml index eb5734c8..e2ef929f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.chrisnewland jitwatch-parent - 1.2.1 + 1.2.2 UTF-8 diff --git a/ui/pom.xml b/ui/pom.xml index e4c12103..7f7e072b 100644 --- a/ui/pom.xml +++ b/ui/pom.xml @@ -4,7 +4,7 @@ com.chrisnewland jitwatch-parent - 1.2.1 + 1.2.2 jitwatch-ui diff --git a/ui/src/main/java/org/adoptopenjdk/jitwatch/ui/compilechain/CompileChainStage.java b/ui/src/main/java/org/adoptopenjdk/jitwatch/ui/compilechain/CompileChainStage.java index 3501e674..e7da7819 100644 --- a/ui/src/main/java/org/adoptopenjdk/jitwatch/ui/compilechain/CompileChainStage.java +++ b/ui/src/main/java/org/adoptopenjdk/jitwatch/ui/compilechain/CompileChainStage.java @@ -23,10 +23,14 @@ import javafx.event.EventHandler; import javafx.scene.Scene; +import javafx.scene.control.Button; import javafx.scene.control.ScrollPane; import javafx.scene.control.Tooltip; import javafx.scene.input.MouseEvent; +import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.shape.Line; @@ -80,12 +84,24 @@ public CompileChainStage(IMemberSelectedListener selectionListener, final IStage scrollPane.setContent(pane); compilationChooser = new CompilationChooser(selectionListener); - + VBox verticalLayout = new VBox(); - - verticalLayout.getChildren().addAll(compilationChooser.getCombo(), scrollPane); - + Scene scene = UserInterfaceUtil.getScene(verticalLayout, JITWatchUI.WINDOW_WIDTH, JITWatchUI.WINDOW_HEIGHT); + + Region spacer = new Region(); + HBox.setHgrow(spacer, Priority.ALWAYS); + + Button buttonSnapShot = UserInterfaceUtil.getSnapshotButton(scene, "CompileChain"); + + HBox hBox = new HBox(); + + hBox.getChildren().add(compilationChooser.getCombo()); + hBox.getChildren().add(spacer); + hBox.getChildren().add(buttonSnapShot); + + verticalLayout.getChildren().addAll(hBox, scrollPane); + RateLimitedResizeListener resizeListener = new RateLimitedResizeListener(this, 200); diff --git a/ui/src/main/java/org/adoptopenjdk/jitwatch/ui/main/JITWatchUI.java b/ui/src/main/java/org/adoptopenjdk/jitwatch/ui/main/JITWatchUI.java index 94c060b6..94bf25c5 100644 --- a/ui/src/main/java/org/adoptopenjdk/jitwatch/ui/main/JITWatchUI.java +++ b/ui/src/main/java/org/adoptopenjdk/jitwatch/ui/main/JITWatchUI.java @@ -832,7 +832,9 @@ public void changed(ObservableValue arg0, Compila lblHeap.setStyle(labelStyle); lblVmVersion.setStyle(labelStyle); - + + Button buttonSnapShot = UserInterfaceUtil.getSnapshotButton(scene, "JITWatch"); + hboxBottom.setPadding(new Insets(4)); hboxBottom.setPrefHeight(statusBarHeight); hboxBottom.setSpacing(4); @@ -844,6 +846,7 @@ public void changed(ObservableValue arg0, Compila hboxBottom.getChildren().add(lblTweakLog); hboxBottom.getChildren().add(springRight); hboxBottom.getChildren().add(lblVmVersion); + hboxBottom.getChildren().add(buttonSnapShot); borderPane.setTop(hboxTop); borderPane.setCenter(spMain); diff --git a/ui/src/main/java/org/adoptopenjdk/jitwatch/ui/sandbox/SandboxStage.java b/ui/src/main/java/org/adoptopenjdk/jitwatch/ui/sandbox/SandboxStage.java index cdee6d45..038f2ade 100644 --- a/ui/src/main/java/org/adoptopenjdk/jitwatch/ui/sandbox/SandboxStage.java +++ b/ui/src/main/java/org/adoptopenjdk/jitwatch/ui/sandbox/SandboxStage.java @@ -65,6 +65,8 @@ import javafx.scene.control.TextArea; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; import javafx.scene.layout.VBox; import javafx.stage.FileChooser; import javafx.stage.Stage; @@ -299,7 +301,11 @@ public void handle(ActionEvent e) showOutput(outputString); } }); + + BorderPane borderPane = new BorderPane(); + Scene scene = UserInterfaceUtil.getScene(borderPane, JITWatchUI.WINDOW_WIDTH, JITWatchUI.WINDOW_HEIGHT); + HBox hBoxTools = new HBox(); hBoxTools.setSpacing(10); @@ -312,7 +318,15 @@ public void handle(ActionEvent e) hBoxTools.getChildren().add(btnResetSandbox); hBoxTools.getChildren().add(comboBoxVMLanguage); hBoxTools.getChildren().add(btnRun); - hBoxTools.getChildren().add(btnOutput); + hBoxTools.getChildren().add(btnOutput); + + Region spacer = new Region(); + HBox.setHgrow(spacer, Priority.ALWAYS); + + Button buttonSnapShot = UserInterfaceUtil.getSnapshotButton(scene, "Sandbox"); + + hBoxTools.getChildren().add(spacer); + hBoxTools.getChildren().add(buttonSnapShot); splitVertical.getItems().add(tabPane); splitVertical.getItems().add(taLog); @@ -323,14 +337,11 @@ public void handle(ActionEvent e) vBoxMain.getChildren().add(hBoxTools); vBoxMain.getChildren().add(splitVertical); - BorderPane borderPane = new BorderPane(); borderPane.setTop(hBoxTools); borderPane.setCenter(splitVertical); initialiseLog(); - Scene scene = UserInterfaceUtil.getScene(borderPane, JITWatchUI.WINDOW_WIDTH, JITWatchUI.WINDOW_HEIGHT); - setScene(scene); setOnCloseRequest(new EventHandler() diff --git a/ui/src/main/java/org/adoptopenjdk/jitwatch/ui/triview/TriView.java b/ui/src/main/java/org/adoptopenjdk/jitwatch/ui/triview/TriView.java index d301e1c9..17d51f5d 100644 --- a/ui/src/main/java/org/adoptopenjdk/jitwatch/ui/triview/TriView.java +++ b/ui/src/main/java/org/adoptopenjdk/jitwatch/ui/triview/TriView.java @@ -109,7 +109,7 @@ public class TriView extends Stage implements ILineListener, ICompilationChangeL private CompilationInfo compilationInfo; - private Label lblMemberInfo; + private Label lblStatusBar; private boolean ignoreComboChanged = false; @@ -122,7 +122,7 @@ public class TriView extends Stage implements ILineListener, ICompilationChangeL private TriViewNavigationStack navigationStack; private IReadOnlyJITDataModel model; - + private boolean selectedProgrammatically = false; public TriView(final JITWatchUI parent, final JITWatchConfig config) @@ -252,7 +252,7 @@ public void changed(ObservableValue ov, IMetaMember oldMe if (!ignoreComboChanged) { if (newMember != null) - { + { if (!selectedProgrammatically) { memberSelectionListener.selectMember(newMember, true, true); @@ -303,12 +303,21 @@ public IMetaMember fromString(String arg0) splitViewer.prefHeightProperty().bind(vBox.heightProperty()); - lblMemberInfo = new Label(); + lblStatusBar = new Label(); + + Button buttonSnapShot = UserInterfaceUtil.getSnapshotButton(scene, "TriView"); + + Region spacerStatus = new Region(); + HBox.setHgrow(spacerStatus, Priority.ALWAYS); + + HBox hBoxStatusBar = new HBox(); + hBoxStatusBar.setSpacing(16.0); + hBoxStatusBar.getChildren().addAll(lblStatusBar, spacerStatus, buttonSnapShot); vBox.getChildren().add(hBoxToolBarClass); vBox.getChildren().add(hBoxToolBarButtons); vBox.getChildren().add(splitViewer); - vBox.getChildren().add(lblMemberInfo); + vBox.getChildren().add(hBoxStatusBar); setScene(scene); @@ -520,7 +529,7 @@ public void setMetaClass(MetaClass metaClass) if (!members.isEmpty()) { IMetaMember firstMember = members.get(0); - + if (!selectedProgrammatically) { memberSelectionListener.selectMember(firstMember, true, true); @@ -553,15 +562,15 @@ public void setMember(IMetaMember member, boolean force, int highlightBCI) } public void setMember(final IMetaMember member, boolean force, final boolean jumpToSource, final int highlightBCI) - { + { selectedProgrammatically = true; if (member == null) { clear(); - + selectedProgrammatically = false; - + return; } @@ -629,10 +638,10 @@ public void setMember(final IMetaMember member, boolean force, final boolean jum applyActionsIfOffsetMismatchDetected(statusBarBuilder); - lblMemberInfo.setText(statusBarBuilder.toString()); + lblStatusBar.setText(statusBarBuilder.toString()); updateBytecodeAndAssembly(jumpToSource, highlightBCI); - + selectedProgrammatically = false; } @@ -730,7 +739,7 @@ private void updateBytecodeAndAssembly(boolean focusSource, int highlightBCI) compilationInfo.setBytecodeSize(Integer.toString(memberBytecode.size())); } - lblMemberInfo.setText(S_EMPTY); + lblStatusBar.setText(S_EMPTY); } } @@ -796,7 +805,7 @@ public void clear() classSearch.clear(); - lblMemberInfo.setText(S_EMPTY); + lblStatusBar.setText(S_EMPTY); updateButtons(); } diff --git a/ui/src/main/java/org/adoptopenjdk/jitwatch/util/UserInterfaceUtil.java b/ui/src/main/java/org/adoptopenjdk/jitwatch/util/UserInterfaceUtil.java index c3a969c8..214da45d 100644 --- a/ui/src/main/java/org/adoptopenjdk/jitwatch/util/UserInterfaceUtil.java +++ b/ui/src/main/java/org/adoptopenjdk/jitwatch/util/UserInterfaceUtil.java @@ -5,22 +5,26 @@ */ package org.adoptopenjdk.jitwatch.util; +import java.io.File; import java.io.InputStream; import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.HashMap; -import java.util.List; -import org.adoptopenjdk.jitwatch.model.IMetaMember; -import org.adoptopenjdk.jitwatch.model.IReadOnlyJITDataModel; import org.adoptopenjdk.jitwatch.model.bytecode.BCAnnotationType; -import org.adoptopenjdk.jitwatch.model.bytecode.ClassBC; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javafx.application.Platform; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; import javafx.scene.Parent; import javafx.scene.Scene; +import javafx.scene.control.Button; import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.image.WritableImage; import javafx.scene.paint.Color; import javafx.scene.text.Font; @@ -31,9 +35,12 @@ public final class UserInterfaceUtil // https://www.iconfinder.com/icons/173960/tick_icon#size=16 public static final Image IMAGE_TICK; + public static final Image IMAGE_CAMERA; + public static final String FONT_MONOSPACE_FAMILY; public static final String FONT_MONOSPACE_SIZE; - public static final boolean ADD_CLOSE_DECORATION; // for fullscreen JavaFX systems + public static final boolean ADD_CLOSE_DECORATION; // for fullscreen JavaFX + // systems private UserInterfaceUtil() { @@ -42,6 +49,7 @@ private UserInterfaceUtil() static { IMAGE_TICK = loadResource("/images/tick.png"); + IMAGE_CAMERA = loadResource("/images/camera.png"); FONT_MONOSPACE_FAMILY = System.getProperty("monospaceFontFamily", Font.font(java.awt.Font.MONOSPACED, 12).getName()); FONT_MONOSPACE_SIZE = System.getProperty("monospaceFontSize", "12"); @@ -68,6 +76,60 @@ private static Image loadResource(String path) return result; } + public static Button getSnapshotButton(final Scene scene, final String filenamePrefix) + { + Button buttonSnapShot = new Button(); + + Image image = UserInterfaceUtil.IMAGE_CAMERA; + + buttonSnapShot.setGraphic(new ImageView(image)); + + buttonSnapShot.setOnAction(new EventHandler() + { + @Override + public void handle(ActionEvent e) + { + takeSnapShot(scene, filenamePrefix); + } + }); + + return buttonSnapShot; + } + + private static void takeSnapShot(Scene scene, String filenamePrefix) + { + WritableImage imageSnap = new WritableImage((int) scene.getWidth(), (int) scene.getHeight()); + + scene.snapshot(imageSnap); + + SimpleDateFormat sfd = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss"); + + try + { + String snapshotFilename = filenamePrefix + "-snapshot-" + sfd.format(new Date()) + ".png"; + + Class classImageIO = Class.forName("javax.imageio.ImageIO"); + + Class classSwingFXUtils = Class.forName("javafx.embed.swing.SwingFXUtils"); + + Method methodWrite = classImageIO.getMethod("write", + new Class[] { java.awt.image.RenderedImage.class, String.class, File.class }); + + Method methodFromFXImage = classSwingFXUtils.getMethod("fromFXImage", + new Class[] { javafx.scene.image.Image.class, java.awt.image.BufferedImage.class }); + + methodWrite.invoke(null, + new Object[] { + methodFromFXImage.invoke(null, new Object[] { imageSnap, null }), + "png", + new File(snapshotFilename) }); + } + catch (Throwable t) + { + logger.error("Could not create snapshot", t); + } + } + public static Scene getScene(Parent rootNode, double width, double height) { Scene scene = new Scene(rootNode, width, height); diff --git a/ui/src/main/resources/images/camera.png b/ui/src/main/resources/images/camera.png new file mode 100755 index 0000000000000000000000000000000000000000..5f818d25b768a079490378412ea15771276a6887 GIT binary patch literal 3094 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 literal 0 HcmV?d00001