changeset 1063:9453f3e72b6e

7901217: \plusminus sign is garbled in JMH output on Windows Summary: Guess the console encoding and use it.
author shade
date Thu, 18 Dec 2014 21:35:14 +0300
parents cfbc4996b875
children 9686a533ffe6
files jmh-core/src/main/java/org/openjdk/jmh/results/format/JSONResultFormat.java jmh-core/src/main/java/org/openjdk/jmh/results/format/LaTeXResultFormat.java jmh-core/src/main/java/org/openjdk/jmh/results/format/ResultFormatFactory.java jmh-core/src/main/java/org/openjdk/jmh/results/format/TextResultFormat.java jmh-core/src/main/java/org/openjdk/jmh/results/format/XSVResultFormat.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/UnCloseablePrintStream.java jmh-core/src/main/java/org/openjdk/jmh/util/Utils.java jmh-core/src/test/java/org/openjdk/jmh/results/format/ResultFormatTest.java
diffstat 10 files changed, 148 insertions(+), 95 deletions(-) [+]
line wrap: on
line diff
--- a/jmh-core/src/main/java/org/openjdk/jmh/results/format/JSONResultFormat.java	Thu Dec 18 17:31:28 2014 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/results/format/JSONResultFormat.java	Thu Dec 18 21:35:14 2014 +0300
@@ -31,6 +31,7 @@
 import org.openjdk.jmh.results.RunResult;
 import org.openjdk.jmh.util.Statistics;
 
+import java.io.PrintStream;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.ArrayList;
@@ -39,9 +40,9 @@
 
 class JSONResultFormat implements ResultFormat {
 
-    private final PrintWriter out;
+    private final PrintStream out;
 
-    public JSONResultFormat(PrintWriter out) {
+    public JSONResultFormat(PrintStream out) {
         this.out = out;
     }
 
--- a/jmh-core/src/main/java/org/openjdk/jmh/results/format/LaTeXResultFormat.java	Thu Dec 18 17:31:28 2014 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/results/format/LaTeXResultFormat.java	Thu Dec 18 21:35:14 2014 +0300
@@ -29,7 +29,7 @@
 import org.openjdk.jmh.results.RunResult;
 import org.openjdk.jmh.util.ClassUtils;
 
-import java.io.PrintWriter;
+import java.io.PrintStream;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.Map;
@@ -39,10 +39,10 @@
 
 class LaTeXResultFormat implements ResultFormat {
 
-    private final PrintWriter pw;
+    private final PrintStream out;
 
-    public LaTeXResultFormat(PrintWriter pw) {
-        this.pw = pw;
+    public LaTeXResultFormat(PrintStream out) {
+        this.out = out;
     }
 
     @Override
@@ -83,37 +83,37 @@
     }
 
     private void printHeader(SortedSet<String> params, boolean singleUnit, String unit) {
-        pw.write("\\begin{tabular}{r|");
+        out.print("\\begin{tabular}{r|");
         for (String p : params) {
-            pw.write("l|");
+            out.print("l|");
         }
-        pw.write("rl" + (singleUnit ? "" : "l") + "}\n");
-        pw.write(" \\multicolumn{1}{c|}{\\texttt{Benchmark}} & ");
+        out.print("rl" + (singleUnit ? "" : "l") + "}\n");
+        out.print(" \\multicolumn{1}{c|}{\\texttt{Benchmark}} & ");
         for (String p : params) {
-            pw.printf("\\texttt{%s}", p);
-            pw.write(" & ");
+            out.printf("\\texttt{%s}", p);
+            out.print(" & ");
         }
-        pw.write(" \\multicolumn{" + (singleUnit ? 2 : 3) + "}{c}{\\texttt{Score" + (singleUnit ? ", " + unit : "") + "}} \\\\\n");
-        pw.write("\\hline\n");
+        out.print(" \\multicolumn{" + (singleUnit ? 2 : 3) + "}{c}{\\texttt{Score" + (singleUnit ? ", " + unit : "") + "}} \\\\\n");
+        out.print("\\hline\n");
     }
 
-    private void printFooter() {pw.write("\\end{tabular}");}
+    private void printFooter() {out.print("\\end{tabular}");}
 
     private void printLine(String label, BenchmarkParams benchParams, SortedSet<String> params,
                            Map<String, String> prefixes, boolean singleUnit, Result res) {
-        pw.printf("\\texttt{%s} & ", escape(prefixes.get(label)));
+        out.printf("\\texttt{%s} & ", escape(prefixes.get(label)));
         for (String p : params) {
-            pw.printf("\\texttt{%s}", escape(benchParams.getParam(p)));
-            pw.write(" & ");
+            out.printf("\\texttt{%s}", escape(benchParams.getParam(p)));
+            out.print(" & ");
         }
-        pw.printf("\\texttt{%5.3f} & ", res.getScore());
-        pw.printf("\\scriptsize $\\pm$ \\texttt{%5.3f}", res.getScoreError());
+        out.printf("\\texttt{%5.3f} & ", res.getScore());
+        out.printf("\\scriptsize $\\pm$ \\texttt{%5.3f}", res.getScoreError());
         if (!singleUnit) {
-            pw.printf(" & \\texttt{%s}", res.getScoreUnit());
+            out.printf(" & \\texttt{%s}", res.getScoreUnit());
         }
-        pw.write(" \\\\");
+        out.print(" \\\\");
 
-        pw.write("\n");
+        out.print("\n");
     }
 
     private static String escape(String s) {
--- a/jmh-core/src/main/java/org/openjdk/jmh/results/format/ResultFormatFactory.java	Thu Dec 18 17:31:28 2014 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/results/format/ResultFormatFactory.java	Thu Dec 18 21:35:14 2014 +0300
@@ -27,7 +27,7 @@
 import org.openjdk.jmh.results.RunResult;
 
 import java.io.IOException;
-import java.io.PrintWriter;
+import java.io.PrintStream;
 import java.util.Collection;
 
 public class ResultFormatFactory {
@@ -45,7 +45,7 @@
             @Override
             public void writeOut(Collection<RunResult> results) {
                 try {
-                    PrintWriter pw = new PrintWriter(file);
+                    PrintStream pw = new PrintStream(file);
                     ResultFormat rf = getInstance(type, pw);
                     rf.writeOut(results);
                     pw.flush();
@@ -58,22 +58,22 @@
     }
 
     /**
-     * Get the instance of ResultFormat of given type which write the result to writer.
-     * It is a user responsibility to initialize and finish the writer as appropriate.
+     * Get the instance of ResultFormat of given type which write the result to out.
+     * It is a user responsibility to initialize and finish the out as appropriate.
      *
      * @param type result format type
-     * @param writer target writer
+     * @param out target out
      * @return result format.
      */
-    public static ResultFormat getInstance(ResultFormatType type, PrintWriter writer) {
+    public static ResultFormat getInstance(ResultFormatType type, PrintStream out) {
         switch (type) {
             case TEXT:
-                return new TextResultFormat(writer);
+                return new TextResultFormat(out);
             case CSV:
                 /*
                  * CSV formatter follows the provisions of http://tools.ietf.org/html/rfc4180
                  */
-                return new XSVResultFormat(writer, ",");
+                return new XSVResultFormat(out, ",");
             case SCSV:
                 /*
                  *    Since some implementations, notably Excel, think it is a good
@@ -81,11 +81,11 @@
                  *    comma in some locales, this is the specialised
                  *     Semi-Colon Separated Values formatter.
                  */
-                return new XSVResultFormat(writer, ";");
+                return new XSVResultFormat(out, ";");
             case JSON:
-                return new JSONResultFormat(writer);
+                return new JSONResultFormat(out);
             case LATEX:
-                return new LaTeXResultFormat(writer);
+                return new LaTeXResultFormat(out);
             default:
                 throw new IllegalStateException("Unsupported result format: " + type);
         }
--- a/jmh-core/src/main/java/org/openjdk/jmh/results/format/TextResultFormat.java	Thu Dec 18 17:31:28 2014 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/results/format/TextResultFormat.java	Thu Dec 18 21:35:14 2014 +0300
@@ -29,7 +29,7 @@
 import org.openjdk.jmh.results.RunResult;
 import org.openjdk.jmh.util.ClassUtils;
 
-import java.io.PrintWriter;
+import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -38,10 +38,10 @@
 import java.util.TreeSet;
 
 class TextResultFormat implements ResultFormat {
-    private final PrintWriter out;
+    private final PrintStream out;
 
-    public TextResultFormat(PrintWriter writer) {
-        this.out = writer;
+    public TextResultFormat(PrintStream out) {
+        this.out = out;
     }
 
     @Override
--- a/jmh-core/src/main/java/org/openjdk/jmh/results/format/XSVResultFormat.java	Thu Dec 18 17:31:28 2014 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/results/format/XSVResultFormat.java	Thu Dec 18 21:35:14 2014 +0300
@@ -28,18 +28,18 @@
 import org.openjdk.jmh.results.Result;
 import org.openjdk.jmh.results.RunResult;
 
-import java.io.PrintWriter;
+import java.io.PrintStream;
 import java.util.Collection;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
 class XSVResultFormat implements ResultFormat {
 
-    private final PrintWriter pw;
+    private final PrintStream out;
     private final String delimiter;
 
-    public XSVResultFormat(PrintWriter pw, String delimiter) {
-        this.pw = pw;
+    public XSVResultFormat(PrintStream out, String delimiter) {
+        this.out = out;
         this.delimiter = delimiter;
     }
 
@@ -66,58 +66,58 @@
     }
 
     private void printHeader(SortedSet<String> params) {
-        pw.write("\"Benchmark\"");
-        pw.write(delimiter);
-        pw.write("\"Mode\"");
-        pw.write(delimiter);
-        pw.write("\"Threads\"");
-        pw.write(delimiter);
-        pw.write("\"Samples\"");
-        pw.write(delimiter);
-        pw.write("\"Score\"");
-        pw.write(delimiter);
-        pw.write("\"Score Error (99.9%)\"");
-        pw.write(delimiter);
-        pw.write("\"Unit\"");
+        out.print("\"Benchmark\"");
+        out.print(delimiter);
+        out.print("\"Mode\"");
+        out.print(delimiter);
+        out.print("\"Threads\"");
+        out.print(delimiter);
+        out.print("\"Samples\"");
+        out.print(delimiter);
+        out.print("\"Score\"");
+        out.print(delimiter);
+        out.print("\"Score Error (99.9%)\"");
+        out.print(delimiter);
+        out.print("\"Unit\"");
         for (String k : params) {
-            pw.write(delimiter);
-            pw.write("\"Param: " + k + "\"");
+            out.print(delimiter);
+            out.print("\"Param: " + k + "\"");
         }
-        pw.write("\r\n");
+        out.print("\r\n");
     }
 
     private void printLine(String label, BenchmarkParams benchmarkParams, SortedSet<String> params, Result result) {
-        pw.write("\"");
-        pw.write(label);
-        pw.write("\"");
-        pw.write(delimiter);
-        pw.write("\"");
-        pw.write(benchmarkParams.getMode().shortLabel());
-        pw.write("\"");
-        pw.write(delimiter);
-        pw.write(String.valueOf(benchmarkParams.getThreads()));
-        pw.write(delimiter);
-        pw.write(String.valueOf(result.getSampleCount()));
-        pw.write(delimiter);
-        pw.write(String.valueOf(result.getScore()));
-        pw.write(delimiter);
-        pw.write(String.valueOf(result.getScoreError()));
-        pw.write(delimiter);
-        pw.write("\"");
-        pw.write(result.getScoreUnit());
-        pw.write("\"");
+        out.print("\"");
+        out.print(label);
+        out.print("\"");
+        out.print(delimiter);
+        out.print("\"");
+        out.print(benchmarkParams.getMode().shortLabel());
+        out.print("\"");
+        out.print(delimiter);
+        out.print(String.valueOf(benchmarkParams.getThreads()));
+        out.print(delimiter);
+        out.print(String.valueOf(result.getSampleCount()));
+        out.print(delimiter);
+        out.print(String.valueOf(result.getScore()));
+        out.print(delimiter);
+        out.print(String.valueOf(result.getScoreError()));
+        out.print(delimiter);
+        out.print("\"");
+        out.print(result.getScoreUnit());
+        out.print("\"");
 
         for (String p : params) {
-            pw.write(delimiter);
-            pw.write("\"");
+            out.print(delimiter);
+            out.print("\"");
             String v = benchmarkParams.getParam(p);
             if (v != null) {
-                pw.write(v);
+                out.print(v);
             }
-            pw.write("\"");
+            out.print("\"");
         }
 
-        pw.write("\r\n");
+        out.print("\r\n");
     }
 
 }
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java	Thu Dec 18 17:31:28 2014 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java	Thu Dec 18 21:35:14 2014 +0300
@@ -56,6 +56,7 @@
 import java.io.IOException;
 import java.io.PrintStream;
 import java.io.RandomAccessFile;
+import java.io.UnsupportedEncodingException;
 import java.lang.management.ManagementFactory;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
@@ -126,7 +127,11 @@
             }
         } else {
             // Protect the System.out from accidental closing
-            out = new UnCloseablePrintStream(System.out);
+            try {
+                out = new UnCloseablePrintStream(System.out, Utils.guessConsoleEncoding());
+            } catch (UnsupportedEncodingException ex) {
+                throw new IllegalStateException(ex);
+            }
         }
 
         return OutputFormatFactory.createFormatInstance(out, options.verbosity().orElse(Defaults.VERBOSITY));
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/format/TextReportFormat.java	Thu Dec 18 17:31:28 2014 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/format/TextReportFormat.java	Thu Dec 18 21:35:14 2014 +0300
@@ -38,7 +38,6 @@
 import org.openjdk.jmh.runner.options.VerboseMode;
 
 import java.io.PrintStream;
-import java.io.PrintWriter;
 import java.util.Collection;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
@@ -170,9 +169,7 @@
 
     @Override
     public void endRun(Collection<RunResult> runResults) {
-        PrintWriter pw = new PrintWriter(out);
-        ResultFormatFactory.getInstance(ResultFormatType.TEXT, pw).writeOut(runResults);
-        pw.flush();
+        ResultFormatFactory.getInstance(ResultFormatType.TEXT, out).writeOut(runResults);
     }
 
 }
--- a/jmh-core/src/main/java/org/openjdk/jmh/util/UnCloseablePrintStream.java	Thu Dec 18 17:31:28 2014 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/util/UnCloseablePrintStream.java	Thu Dec 18 21:35:14 2014 +0300
@@ -26,10 +26,12 @@
 
 import java.io.OutputStream;
 import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
 
 public class UnCloseablePrintStream extends PrintStream {
-    public UnCloseablePrintStream(OutputStream out) {
-        super(out);
+
+    public UnCloseablePrintStream(OutputStream out, String charset) throws UnsupportedEncodingException {
+        super(out, false, charset);
     }
 
     @Override
--- a/jmh-core/src/main/java/org/openjdk/jmh/util/Utils.java	Thu Dec 18 17:31:28 2014 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/util/Utils.java	Thu Dec 18 21:35:14 2014 +0300
@@ -26,10 +26,14 @@
 
 import sun.misc.Unsafe;
 
+import java.io.Console;
 import java.io.File;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -164,6 +168,49 @@
         return max;
     }
 
+    public static String guessConsoleEncoding() {
+        // We cannot use Console class directly, because we also need the access to the raw byte stream,
+        // e.g. for pushing in a raw output from a forked VM invocation. Therefore, we are left with
+        // reflectively poking out the Charset from Console, and use it for our own private output streams.
+
+        try {
+            Field f = Console.class.getDeclaredField("cs");
+            if (f != null) {
+                f.setAccessible(true);
+                Console console = System.console();
+                if (console != null) {
+                    Object res = f.get(console);
+                    if (res instanceof Charset) {
+                        return ((Charset) res).name();
+                    }
+                }
+            }
+        } catch (NoSuchFieldException e) {
+            // fall-through
+        } catch (IllegalAccessException e) {
+            // fall-through
+        }
+
+        try {
+            Method m = Console.class.getDeclaredMethod("encoding");
+            if (m != null) {
+                m.setAccessible(true);
+                Object res = m.invoke(null);
+                if (res instanceof String) {
+                    return (String) res;
+                }
+            }
+        } catch (NoSuchMethodException e) {
+            // fall-through
+        } catch (InvocationTargetException e) {
+            // fall-through
+        } catch (IllegalAccessException e) {
+            // fall-through
+        }
+
+        return Charset.defaultCharset().name();
+    }
+
     static class BurningTask implements Runnable {
         @Override
         public void run() {
--- a/jmh-core/src/test/java/org/openjdk/jmh/results/format/ResultFormatTest.java	Thu Dec 18 17:31:28 2014 +0300
+++ b/jmh-core/src/test/java/org/openjdk/jmh/results/format/ResultFormatTest.java	Thu Dec 18 21:35:14 2014 +0300
@@ -44,6 +44,7 @@
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.io.PrintStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -128,12 +129,12 @@
     public void jsonTest_Stream() throws IOException {
         String actualFile = FileUtils.tempFile("test").getAbsolutePath();
 
-        PrintWriter pw = new PrintWriter(actualFile);
+        PrintStream ps = new PrintStream(actualFile);
         ResultFormatFactory.getInstance(
                     ResultFormatType.JSON,
-                    pw)
+                    ps)
                 .writeOut(getStub());
-        pw.close();
+        ps.close();
 
         compare(actualFile, "output-golden.json");
     }
@@ -154,12 +155,12 @@
     public void csvTest_Stream() throws IOException {
         String actualFile = FileUtils.tempFile("test").getAbsolutePath();
 
-        PrintWriter pw = new PrintWriter(actualFile);
+        PrintStream ps = new PrintStream(actualFile);
         ResultFormatFactory.getInstance(
                     ResultFormatType.CSV,
-                    pw)
+                    ps)
                 .writeOut(getStub());
-        pw.close();
+        ps.close();
 
         compare(actualFile, "output-golden.csv");
     }