changeset 1421:2e4ec571fd74

7901965: Add JMH and JDK version and effective JVM path to the JSON output Contributed-by: Jens Wilke <jw_list@headissue.com>
author shade
date Tue, 02 May 2017 13:38:06 +0200
parents 3674a478c090
children daa249176624
files jmh-core/src/main/java/org/openjdk/jmh/infra/BenchmarkParams.java jmh-core/src/main/java/org/openjdk/jmh/infra/Blackhole.java jmh-core/src/main/java/org/openjdk/jmh/results/Result.java jmh-core/src/main/java/org/openjdk/jmh/results/format/JSONResultFormat.java jmh-core/src/main/java/org/openjdk/jmh/runner/BaseRunner.java jmh-core/src/main/java/org/openjdk/jmh/runner/PrintPropertiesMain.java jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java jmh-core/src/main/java/org/openjdk/jmh/runner/format/TextReportFormat.java jmh-core/src/main/java/org/openjdk/jmh/util/FileUtils.java jmh-core/src/main/java/org/openjdk/jmh/util/Utils.java jmh-core/src/main/java/org/openjdk/jmh/util/Version.java jmh-core/src/test/java/org/openjdk/jmh/results/TestAggregateResult.java jmh-core/src/test/java/org/openjdk/jmh/results/format/JSONResultFormatTest.java jmh-core/src/test/java/org/openjdk/jmh/results/format/ResultFormatTest.java jmh-core/src/test/java/org/openjdk/jmh/runner/RunnerTest.java jmh-core/src/test/resources/org/openjdk/jmh/results/format/output-golden.json
diffstat 16 files changed, 403 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/jmh-core/src/main/java/org/openjdk/jmh/infra/BenchmarkParams.java	Sun Apr 23 22:19:07 2017 +0200
+++ b/jmh-core/src/main/java/org/openjdk/jmh/infra/BenchmarkParams.java	Tue May 02 13:38:06 2017 +0200
@@ -28,6 +28,7 @@
 import org.openjdk.jmh.runner.WorkloadParams;
 import org.openjdk.jmh.runner.options.TimeValue;
 import org.openjdk.jmh.util.Utils;
+import org.openjdk.jmh.util.Version;
 
 import java.io.Serializable;
 import java.util.Arrays;
@@ -64,20 +65,23 @@
     }
 
     public BenchmarkParams(String benchmark, String generatedTarget, boolean synchIterations,
-                             int threads, int[] threadGroups, Collection<String> threadGroupLabels,
-                             int forks, int warmupForks,
-                             IterationParams warmup, IterationParams measurement,
-                             Mode mode, WorkloadParams params,
-                             TimeUnit timeUnit, int opsPerInvocation,
-                             String jvm, Collection<String> jvmArgs,
-                             TimeValue timeout) {
+                           int threads, int[] threadGroups, Collection<String> threadGroupLabels,
+                           int forks, int warmupForks,
+                           IterationParams warmup, IterationParams measurement,
+                           Mode mode, WorkloadParams params,
+                           TimeUnit timeUnit, int opsPerInvocation,
+                           String jvm, Collection<String> jvmArgs,
+                           String jdkVersion, String vmVersion, String jmhVersion,
+                           TimeValue timeout) {
         super(benchmark, generatedTarget, synchIterations,
                 threads, threadGroups, threadGroupLabels,
                 forks, warmupForks,
                 warmup, measurement,
                 mode, params,
                 timeUnit, opsPerInvocation,
-                jvm, jvmArgs, timeout);
+                jvm, jvmArgs,
+                jdkVersion, vmVersion, jmhVersion,
+                timeout);
     }
 }
 
@@ -92,6 +96,7 @@
                              Mode mode, WorkloadParams params,
                              TimeUnit timeUnit, int opsPerInvocation,
                              String jvm, Collection<String> jvmArgs,
+                             String jdkVersion, String vmVersion, String jmhVersion,
                              TimeValue timeout) {
         super(benchmark, generatedTarget, synchIterations,
                 threads, threadGroups, threadGroupLabels,
@@ -100,6 +105,7 @@
                 mode, params,
                 timeUnit, opsPerInvocation,
                 jvm, jvmArgs,
+                jdkVersion, vmVersion, jmhVersion,
                 timeout);
     }
 }
@@ -131,6 +137,7 @@
                              Mode mode, WorkloadParams params,
                              TimeUnit timeUnit, int opsPerInvocation,
                              String jvm, Collection<String> jvmArgs,
+                             String jdkVersion, String vmVersion, String jmhVersion,
                              TimeValue timeout) {
         super(benchmark, generatedTarget, synchIterations,
                 threads, threadGroups, threadGroupLabels,
@@ -139,6 +146,7 @@
                 mode, params,
                 timeUnit, opsPerInvocation,
                 jvm, jvmArgs,
+                jdkVersion, vmVersion, jmhVersion,
                 timeout);
     }
 }
@@ -185,6 +193,9 @@
     protected final int opsPerInvocation;
     protected final String jvm;
     protected final Collection<String> jvmArgs;
+    protected final String jdkVersion;
+    protected final String jmhVersion;
+    protected final String vmVersion;
     protected final TimeValue timeout;
 
     public BenchmarkParamsL2(String benchmark, String generatedTarget, boolean synchIterations,
@@ -194,6 +205,7 @@
                              Mode mode, WorkloadParams params,
                              TimeUnit timeUnit, int opsPerInvocation,
                              String jvm, Collection<String> jvmArgs,
+                             String jdkVersion, String vmVersion, String jmhVersion,
                              TimeValue timeout) {
         this.benchmark = benchmark;
         this.generatedTarget = generatedTarget;
@@ -211,6 +223,9 @@
         this.opsPerInvocation = opsPerInvocation;
         this.jvm = jvm;
         this.jvmArgs = jvmArgs;
+        this.jdkVersion = jdkVersion;
+        this.vmVersion = vmVersion;
+        this.jmhVersion = jmhVersion;
         this.timeout = timeout;
     }
 
@@ -333,17 +348,44 @@
         return generatedTarget;
     }
 
+    /**
+     * @return JVM executable path
+     */
     public String getJvm() {
         return jvm;
     }
 
     /**
+     * @return JMH version identical to {@link Version#getPlainVersion()}, but output format should
+     *          get there input via bean for testing purposes.
+     */
+    public String getJmhVersion() {
+        return jmhVersion;
+    }
+
+    /**
      * @return JVM options
      */
     public Collection<String> getJvmArgs() {
         return Collections.unmodifiableCollection(jvmArgs);
     }
 
+    /**
+     * @return version information as returned by the effective target JVM,
+     *         via system property {@code java.version} and {@code java.vm.version}
+     */
+    public String getJdkVersion() {
+        return jdkVersion;
+    }
+
+    /**
+     * @return version information as returned by the effective target JVM,
+     *         via system property {@code java.vm.version}
+     */
+    public String getVmVersion() {
+        return vmVersion;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
--- a/jmh-core/src/main/java/org/openjdk/jmh/infra/Blackhole.java	Sun Apr 23 22:19:07 2017 +0200
+++ b/jmh-core/src/main/java/org/openjdk/jmh/infra/Blackhole.java	Tue May 02 13:38:06 2017 +0200
@@ -288,6 +288,8 @@
      *
      * WARNING: This method should only be called by the infrastructure code, in clearly understood cases.
      * Even though it is public, it is not supposed to be called by users.
+     *
+     * @param challengeResponse arbitrary string
      */
     public void evaporate(String challengeResponse) {
         if (!challengeResponse.equals("Yes, I am Stephen Hawking, and know a thing or two about black holes.")) {
--- a/jmh-core/src/main/java/org/openjdk/jmh/results/Result.java	Sun Apr 23 22:19:07 2017 +0200
+++ b/jmh-core/src/main/java/org/openjdk/jmh/results/Result.java	Tue May 02 13:38:06 2017 +0200
@@ -186,9 +186,8 @@
     }
 
     /**
-     * Get derivative results for this result. These do not participate in aggregation,
-     * and computed on the spot from the aggregated result.
-     * @return
+     * @return derivative results for this result. These do not participate in aggregation,
+     *         and computed on the spot from the aggregated result.
      */
     protected Collection<? extends Result> getDerivativeResults() {
         return Collections.emptyList();
--- a/jmh-core/src/main/java/org/openjdk/jmh/results/format/JSONResultFormat.java	Sun Apr 23 22:19:07 2017 +0200
+++ b/jmh-core/src/main/java/org/openjdk/jmh/results/format/JSONResultFormat.java	Tue May 02 13:38:06 2017 +0200
@@ -69,10 +69,18 @@
             }
 
             pw.println("{");
+            pw.println("\"jmhVersion\" : \"" + params.getJmhVersion() + "\",");
             pw.println("\"benchmark\" : \"" + params.getBenchmark() + "\",");
             pw.println("\"mode\" : \"" + params.getMode().shortLabel() + "\",");
             pw.println("\"threads\" : " + params.getThreads() + ",");
             pw.println("\"forks\" : " + params.getForks() + ",");
+            pw.println("\"jvm\" : " + toJsonString(params.getJvm()) + ",");
+            // if empty, write an empty array.
+            pw.println("\"jvmArgs\" : [");
+            printStringArray(pw, params.getJvmArgs());
+            pw.println("],");
+            pw.println("\"jdkVersion\" : " + toJsonString(params.getJdkVersion()) + ",");
+            pw.println("\"vmVersion\" : " + toJsonString(params.getVmVersion()) + ",");
             pw.println("\"warmupIterations\" : " + params.getWarmup().getCount() + ",");
             pw.println("\"warmupTime\" : \"" + params.getWarmup().getTime() + "\",");
             pw.println("\"warmupBatchSize\" : " + params.getWarmup().getBatchSize() + ",");
@@ -234,7 +242,41 @@
         return String.valueOf(d);
     }
 
-    private String tidy(String s) {
+    /**
+     * Escaping for a JSON string. Does the typical escaping of double quotes and backslash.
+     * Also escapes characters that are handled by the tidying process, so that every ASCII
+     * character makes it correctly into the JSON output. Control characters are filtered.
+     */
+    static String toJsonString(String s) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("\"");
+        for (char c : s.toCharArray()) {
+            if (Character.isISOControl(c)) {
+                continue;
+            }
+            switch (c) {
+                // use & as escape character to escape the tidying
+                case '&': sb.append("&&"); break;
+                // we cannot escape to \\\\ since this would create sequences interpreted by the tidying
+                case '\\': sb.append("&/"); break;
+                case '"': sb.append("&'"); break;
+                // escape spacial chars for the tidying formatting below that might appear in a string
+                case ',': sb.append(";"); break;
+                case '[': sb.append("<"); break;
+                case ']': sb.append(">"); break;
+                case '<': sb.append("&-"); break;
+                case '>': sb.append("&="); break;
+                case ';': sb.append("&:"); break;
+                case '{': sb.append("&("); break;
+                case '}': sb.append("&)"); break;
+                default: sb.append(c);
+            }
+        }
+        sb.append("\"");
+        return sb.toString();
+    }
+
+    static String tidy(String s) {
         s = s.replaceAll("\r", "");
         s = s.replaceAll("\n", " ");
         s = s.replaceAll(",", ",\n");
@@ -250,6 +292,15 @@
         s = s.replaceAll(";", ",");
         s = s.replaceAll("\\<", "[");
         s = s.replaceAll("\\>", "]");
+        // translate back from string escaping to keep all string characters intact
+        s = s.replaceAll("&:", ";");
+        s = s.replaceAll("&'", "\\\\\"");
+        s = s.replaceAll("&\\(", "{");
+        s = s.replaceAll("&\\)", "}");
+        s = s.replaceAll("&-", "<");
+        s = s.replaceAll("&=", ">");
+        s = s.replaceAll("&/", "\\\\\\\\");
+        s = s.replaceAll("&&", "&");
 
         String[] lines = s.split("\n");
 
@@ -292,4 +343,16 @@
         return sb.toString();
     }
 
+    private static void printStringArray(PrintWriter pw, Collection<String> col) {
+        boolean isFirst = true;
+        for (String e : col) {
+            if (isFirst) {
+                isFirst = false;
+            } else {
+                pw.print(',');
+            }
+            pw.print(toJsonString(e));
+        }
+    }
+
 }
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/BaseRunner.java	Sun Apr 23 22:19:07 2017 +0200
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/BaseRunner.java	Tue May 02 13:38:06 2017 +0200
@@ -84,16 +84,6 @@
             BenchmarkParams params = action.getParams();
             ActionMode mode = action.getMode();
 
-            String realOpts = Utils.join(ManagementFactory.getRuntimeMXBean().getInputArguments(), " ").trim();
-            if (realOpts.isEmpty()) {
-                realOpts = "<none>";
-            }
-
-            out.println("# " + Version.getVersion());
-            out.println("# VM version: " + Utils.getCurrentJvmVersion());
-            out.println("# VM invoker: " + Utils.getCurrentJvm());
-            out.println("# VM options: " + realOpts);
-
             out.startBenchmark(params);
             out.println("");
             etaBeforeBenchmark();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/PrintPropertiesMain.java	Tue May 02 13:38:06 2017 +0200
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.runner;
+
+import org.openjdk.jmh.util.Utils;
+
+/**
+ * Main program entry point for exporting the system properties, used for detecting the VM version.
+ */
+class PrintPropertiesMain {
+
+    /**
+     * @param argv Command line arguments
+     */
+    public static void main(String[] argv) throws Exception {
+        Utils.getRecordedSystemProperties().storeToXML(
+                System.out,
+                "JMH properties export for target JVM",
+                "UTF-8");
+    }
+
+}
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java	Sun Apr 23 22:19:07 2017 +0200
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java	Tue May 02 13:38:06 2017 +0200
@@ -465,6 +465,13 @@
         String jvm = options.getJvm().orElse(
                 benchmark.getJvm().orElse(Utils.getCurrentJvm()));
 
+        Properties targetProperties;
+        if (jvm.equals(Utils.getCurrentJvm())) {
+            targetProperties = Utils.getRecordedSystemProperties();
+        } else {
+            targetProperties = Utils.readPropertiesFromCommand(getPrintPropertiesCommand(jvm));
+        }
+
         Collection<String> jvmArgs = new ArrayList<>();
 
         jvmArgs.addAll(options.getJvmArgsPrepend().orElse(
@@ -479,11 +486,15 @@
         TimeValue timeout = options.getTimeout().orElse(
                 benchmark.getTimeout().orElse(Defaults.TIMEOUT));
 
+        String jdkVersion = targetProperties.getProperty("java.version");
+        String vmVersion = targetProperties.getProperty("java.vm.version");
         return new BenchmarkParams(benchmark.getUsername(), benchmark.generatedTarget(), synchIterations,
                 threads, threadGroups, benchmark.getThreadGroupLabels().orElse(Collections.<String>emptyList()),
                 forks, warmupForks,
                 warmup, measurement, benchmark.getMode(), benchmark.getWorkloadParams(), timeUnit, opsPerInvocation,
-                jvm, jvmArgs, timeout);
+                jvm, jvmArgs,
+                jdkVersion, vmVersion, Version.getPlainVersion(),
+                timeout);
     }
 
     private List<WorkloadParams> explodeAllParams(BenchmarkListEntry br) throws RunnerException {
@@ -599,17 +610,6 @@
             printOut = forcePrint || printOut;
             printErr = forcePrint || printErr;
 
-            List<String> versionString = getVersionMainCommand(params);
-
-            String opts = Utils.join(params.getJvmArgs(), " ");
-            if (opts.trim().isEmpty()) {
-                opts = "<none>";
-            }
-
-            out.println("# " + Version.getVersion());
-            out.print("# VM version: " + Utils.join(Utils.runWith(versionString), "\n"));
-            out.println("# VM invoker: " + params.getJvm());
-            out.println("# VM options: " + opts);
             out.startBenchmark(params);
             out.println("");
 
@@ -832,14 +832,11 @@
         return command;
     }
 
-    /**
-     * @return
-     */
-    List<String> getVersionMainCommand(BenchmarkParams benchmark) {
+    private List<String> getPrintPropertiesCommand(String jvm) {
         List<String> command = new ArrayList<>();
 
         // use supplied jvm, if given
-        command.add(benchmark.getJvm());
+        command.add(jvm);
 
         // assemble final process command
         command.add("-cp");
@@ -849,7 +846,7 @@
             command.add(System.getProperty("java.class.path"));
         }
 
-        command.add(VersionMain.class.getName());
+        command.add(PrintPropertiesMain.class.getName());
 
         return command;
     }
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/format/TextReportFormat.java	Sun Apr 23 22:19:07 2017 +0200
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/format/TextReportFormat.java	Tue May 02 13:38:06 2017 +0200
@@ -56,6 +56,16 @@
 
     @Override
     public void startBenchmark(BenchmarkParams params) {
+        String opts = Utils.join(params.getJvmArgs(), " ");
+        if (opts.trim().isEmpty()) {
+            opts = "<none>";
+        }
+
+        println("# JMH version: " + params.getJmhVersion());
+        println("# VM version:  JDK " + params.getJdkVersion() + ", VM " + params.getVmVersion());
+        println("# VM invoker:  " + params.getJvm());
+        println("# VM options:  " + opts);
+
         IterationParams warmup = params.getWarmup();
         if (warmup.getCount() > 0) {
             out.println("# Warmup: " + warmup.getCount() + " iterations, " +
--- a/jmh-core/src/main/java/org/openjdk/jmh/util/FileUtils.java	Sun Apr 23 22:19:07 2017 +0200
+++ b/jmh-core/src/main/java/org/openjdk/jmh/util/FileUtils.java	Tue May 02 13:38:06 2017 +0200
@@ -47,7 +47,7 @@
      *
      * @param suffix suffix
      * @return temp file
-     * @throws IOException
+     * @throws IOException if things go crazy
      */
     public static TempFile weakTempFile(String suffix) throws IOException {
         return TEMP_FILE_MANAGER.create(suffix);
@@ -63,7 +63,7 @@
      *
      * @param suffix suffix
      * @return temporary file
-     * @throws IOException
+     * @throws IOException if things go crazy
      */
     public static File tempFile(String suffix) throws IOException {
         File file = File.createTempFile("jmh", suffix);
--- a/jmh-core/src/main/java/org/openjdk/jmh/util/Utils.java	Sun Apr 23 22:19:07 2017 +0200
+++ b/jmh-core/src/main/java/org/openjdk/jmh/util/Utils.java	Tue May 02 13:38:06 2017 +0200
@@ -475,6 +475,49 @@
     }
 
     /**
+     * We don't access the complete system properties via {@link System#getProperties()} because
+     * this would require read/write permissions to the properties. Just copy the properties we
+     * want to record in the result.
+     *
+     * @return Copy of system properties we want to record in the results.
+     */
+    public static Properties getRecordedSystemProperties() {
+        String[] names = new String[]{"java.version", "java.vm.version"};
+        Properties p = new Properties();
+        for (String i : names) {
+            p.setProperty(i, System.getProperty(i));
+        }
+        return p;
+    }
+
+    public static Properties readPropertiesFromCommand(List<String> cmd) {
+        Properties out = new Properties();
+        try {
+            Process p = new ProcessBuilder(cmd).start();
+
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+            // drain streams, else we might lock up
+            InputStreamDrainer errDrainer = new InputStreamDrainer(p.getErrorStream(), System.err);
+            InputStreamDrainer outDrainer = new InputStreamDrainer(p.getInputStream(), baos);
+
+            errDrainer.start();
+            outDrainer.start();
+
+            int err = p.waitFor();
+
+            errDrainer.join();
+            outDrainer.join();
+            out.loadFromXML(new ByteArrayInputStream(baos.toByteArray()));
+        } catch (IOException ex) {
+            throw new RuntimeException(ex);
+        } catch (InterruptedException ex) {
+            throw new IllegalStateException(ex);
+        }
+        return out;
+    }
+
+    /**
      * Adapts Iterator for Iterable.
      * Can be iterated only once!
      *
--- a/jmh-core/src/main/java/org/openjdk/jmh/util/Version.java	Sun Apr 23 22:19:07 2017 +0200
+++ b/jmh-core/src/main/java/org/openjdk/jmh/util/Version.java	Tue May 02 13:38:06 2017 +0200
@@ -39,6 +39,9 @@
 
     private static final int UPDATE_INTERVAL = 180;
 
+    /**
+     * @return the version, build date and update hint.
+     */
     public static String getVersion() {
         StringWriter sw = new StringWriter();
         PrintWriter pw = new PrintWriter(sw);
@@ -48,6 +51,29 @@
         return sw.toString();
     }
 
+    /**
+     * @return only version, e.g. "1.19", or "-" if the version cannot be determined.
+     */
+    public static String getPlainVersion() {
+        Properties p = new Properties();
+        InputStream s = Version.class.getResourceAsStream("/jmh.properties");
+        if (s == null) {
+            return "-";
+        }
+        try {
+            p.load(s);
+        } catch (IOException e) {
+            return "-";
+        } finally {
+            FileUtils.safelyClose(s);
+        }
+        String version = (String) p.get("jmh.version");
+        if (version == null) {
+            return "-";
+        }
+        return version;
+    }
+
     private static void printVersion(PrintWriter pw) {
         Properties p = new Properties();
         InputStream s = Version.class.getResourceAsStream("/jmh.properties");
--- a/jmh-core/src/test/java/org/openjdk/jmh/results/TestAggregateResult.java	Sun Apr 23 22:19:07 2017 +0200
+++ b/jmh-core/src/test/java/org/openjdk/jmh/results/TestAggregateResult.java	Tue May 02 13:38:06 2017 +0200
@@ -32,6 +32,7 @@
 import org.openjdk.jmh.runner.IterationType;
 import org.openjdk.jmh.runner.options.TimeValue;
 import org.openjdk.jmh.util.Utils;
+import org.openjdk.jmh.util.Version;
 
 import java.util.Collections;
 import java.util.concurrent.TimeUnit;
@@ -56,6 +57,7 @@
                         new IterationParams(IterationType.MEASUREMENT, 1, TimeValue.seconds(1), 1),
                         Mode.Throughput, null, TimeUnit.SECONDS, 1,
                         Utils.getCurrentJvm(), Collections.<String>emptyList(),
+                        System.getProperty("java.version"), System.getProperty("java.vm.version"), Version.getPlainVersion(),
                         TimeValue.days(1)),
                 new IterationParams(IterationType.MEASUREMENT, 1, TimeValue.days(1), 1),
                 null
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/test/java/org/openjdk/jmh/results/format/JSONResultFormatTest.java	Tue May 02 13:38:06 2017 +0200
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.results.format;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Extra tests for special cases of the JSON formatter.
+ *
+ * @author Jens Wilke
+ * @see JSONResultFormat
+ */
+public class JSONResultFormatTest {
+
+    @Test
+    public void toJsonString_tidy() {
+        String s = JSONResultFormat.toJsonString("abc,\"{}()\\(\\)[]{}");
+        s = JSONResultFormat.tidy(s);
+        assertEquals("\"abc,\\\"{}()\\\\(\\\\)[]{}\"\n", s);
+    }
+
+    @Test
+    public void toJsonString_tidy_curly() {
+        String s = JSONResultFormat.toJsonString("{}");
+        s = JSONResultFormat.tidy(s);
+        assertEquals("\"{}\"\n", s);
+    }
+
+    @Test
+    public void toJsonString_tidy_curved() {
+        String s = JSONResultFormat.toJsonString("()");
+        s = JSONResultFormat.tidy(s);
+        assertEquals("\"()\"\n", s);
+    }
+
+    @Test
+    public void toJsonString_tidy_escapedDoubleQuote() {
+        String s = JSONResultFormat.toJsonString("\"");
+        s = JSONResultFormat.tidy(s);
+        assertEquals("\"\\\"\"\n", s);
+    }
+
+    @Test
+    public void toJsonString_tidy_escapedEscape() {
+        String s = JSONResultFormat.toJsonString("\\");
+        s = JSONResultFormat.tidy(s);
+        assertEquals("\"\\\\\"\n", s);
+    }
+
+    /**
+     * Check that every ASCII character in a string makes it transparently through
+     * the JSON tidying and formatting process.
+     */
+    @Test
+    public void toJsonString_tidy_asciiTransparent () {
+        for (char i = 32; i < 127; i++) {
+            if (i == '"') {
+                continue;
+            }
+            if (i == '\\') {
+                continue;
+            }
+            String s = JSONResultFormat.toJsonString(Character.toString(i));
+            s = JSONResultFormat.tidy(s);
+            assertEquals("\"" + i + "\"\n", s);
+        }
+    }
+
+}
\ No newline at end of file
--- a/jmh-core/src/test/java/org/openjdk/jmh/results/format/ResultFormatTest.java	Sun Apr 23 22:19:07 2017 +0200
+++ b/jmh-core/src/test/java/org/openjdk/jmh/results/format/ResultFormatTest.java	Tue May 02 13:38:06 2017 +0200
@@ -46,6 +46,20 @@
  */
 public class ResultFormatTest {
 
+    /**
+     * Use constant dummy for JVM instead of current JVM to compare with golden file.
+     */
+    private static final String JVM_DUMMY = "javadummy";
+
+    /**
+     * Use constant dummy for JVM instead of current JVM to compare with golden file.
+     */
+    private static final String JDK_VERSION_DUMMY = "1.8-dummy";
+
+    private static final String VM_VERSION_DUMMY = "4711";
+
+    private static final String JMH_VERSION_DUMMY = "1.18";
+
     private Collection<RunResult> getStub() {
         Collection<RunResult> results = new TreeSet<>(RunResult.DEFAULT_SORT_COMPARATOR);
 
@@ -70,8 +84,9 @@
                     Mode.Throughput,
                     ps,
                     TimeUnit.SECONDS, 1,
-                    Utils.getCurrentJvm(),
+                    JVM_DUMMY,
                     Collections.<String>emptyList(),
+                    JDK_VERSION_DUMMY, VM_VERSION_DUMMY, JMH_VERSION_DUMMY,
                     TimeValue.days(1));
 
             Collection<BenchmarkResult> benchmarkResults = new ArrayList<>();
--- a/jmh-core/src/test/java/org/openjdk/jmh/runner/RunnerTest.java	Sun Apr 23 22:19:07 2017 +0200
+++ b/jmh-core/src/test/java/org/openjdk/jmh/runner/RunnerTest.java	Tue May 02 13:38:06 2017 +0200
@@ -33,6 +33,7 @@
 import org.openjdk.jmh.runner.options.TimeValue;
 import org.openjdk.jmh.util.FileUtils;
 import org.openjdk.jmh.util.Utils;
+import org.openjdk.jmh.util.Version;
 
 import java.io.File;
 import java.io.IOException;
@@ -65,6 +66,7 @@
                 new IterationParams(IterationType.MEASUREMENT, 1, TimeValue.seconds(1), 1),
                 Mode.Throughput, null, TimeUnit.SECONDS, 1,
                 Utils.getCurrentJvm(), Collections.<String>emptyList(),
+                System.getProperty("java.version"), System.getProperty("java.vm.version"), Version.getPlainVersion(),
                 TimeValue.days(1));
         List<String> command = blade.getForkedMainCommand(bp, Collections.<ExternalProfiler>emptyList(), DUMMY_HOST, DUMMY_PORT);
 
@@ -96,6 +98,7 @@
                 new IterationParams(IterationType.MEASUREMENT, 1, TimeValue.seconds(1), 1),
                 Mode.Throughput, null, TimeUnit.SECONDS, 1,
                 Utils.getCurrentJvm(), Collections.singletonList(CompilerHints.XX_COMPILE_COMMAND_FILE + tempHints),
+                System.getProperty("java.version"), System.getProperty("java.vm.version"), Version.getPlainVersion(),
                 TimeValue.days(1));
         List<String> command = blade.getForkedMainCommand(bp, Collections.<ExternalProfiler>emptyList(), DUMMY_HOST, DUMMY_PORT);
 
@@ -132,6 +135,7 @@
                 Mode.Throughput, null, TimeUnit.SECONDS, 1,
                 Utils.getCurrentJvm(),
                 Arrays.asList(CompilerHints.XX_COMPILE_COMMAND_FILE + tempHints1, CompilerHints.XX_COMPILE_COMMAND_FILE + tempHints2),
+                System.getProperty("java.version"), System.getProperty("java.vm.version"), Version.getPlainVersion(),
                 TimeValue.days(1));
         List<String> command = blade.getForkedMainCommand(bp, Collections.<ExternalProfiler>emptyList(), DUMMY_HOST, DUMMY_PORT);
 
--- a/jmh-core/src/test/resources/org/openjdk/jmh/results/format/output-golden.json	Sun Apr 23 22:19:07 2017 +0200
+++ b/jmh-core/src/test/resources/org/openjdk/jmh/results/format/output-golden.json	Tue May 02 13:38:06 2017 +0200
@@ -1,9 +1,15 @@
 [
     {
+        "jmhVersion" : "1.18",
         "benchmark" : "benchmark_0",
         "mode" : "thrpt",
         "threads" : 80,
         "forks" : 828,
+        "jvm" : "javadummy",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8-dummy",
+        "vmVersion" : "4711",
         "warmupIterations" : 84,
         "warmupTime" : "375 s",
         "warmupBatchSize" : 1,
@@ -216,10 +222,16 @@
         }
     },
     {
+        "jmhVersion" : "1.18",
         "benchmark" : "benchmark_1",
         "mode" : "thrpt",
         "threads" : 900,
         "forks" : 364,
+        "jvm" : "javadummy",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8-dummy",
+        "vmVersion" : "4711",
         "warmupIterations" : 544,
         "warmupTime" : "409 s",
         "warmupBatchSize" : 1,
@@ -315,10 +327,16 @@
         }
     },
     {
+        "jmhVersion" : "1.18",
         "benchmark" : "benchmark_2",
         "mode" : "thrpt",
         "threads" : 466,
         "forks" : 677,
+        "jvm" : "javadummy",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8-dummy",
+        "vmVersion" : "4711",
         "warmupIterations" : 384,
         "warmupTime" : "105 s",
         "warmupBatchSize" : 1,
@@ -476,10 +494,16 @@
         }
     },
     {
+        "jmhVersion" : "1.18",
         "benchmark" : "benchmark_3",
         "mode" : "thrpt",
         "threads" : 968,
         "forks" : 581,
+        "jvm" : "javadummy",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8-dummy",
+        "vmVersion" : "4711",
         "warmupIterations" : 628,
         "warmupTime" : "207 s",
         "warmupBatchSize" : 1,
@@ -669,10 +693,16 @@
         }
     },
     {
+        "jmhVersion" : "1.18",
         "benchmark" : "benchmark_4",
         "mode" : "thrpt",
         "threads" : 739,
         "forks" : 670,
+        "jvm" : "javadummy",
+        "jvmArgs" : [
+        ],
+        "jdkVersion" : "1.8-dummy",
+        "vmVersion" : "4711",
         "warmupIterations" : 997,
         "warmupTime" : "651 s",
         "warmupBatchSize" : 1,