# HG changeset patch # User rschmelter # Date 1563542519 -7200 # Fri Jul 19 15:21:59 2019 +0200 # Node ID 6ea46db1543657e0f5f7a26ff12deeff1559f64e # Parent c6923eaecd7bc31fdc26a94a5310243d2fbf7f42 8227868: jinfo and jstack can fail converting UTF8 output to strings Reviewed-by: diff --git a/src/jdk.jcmd/share/classes/sun/tools/common/PrintStreamPrinter.java b/src/jdk.jcmd/share/classes/sun/tools/common/PrintStreamPrinter.java new file mode 100644 --- /dev/null +++ b/src/jdk.jcmd/share/classes/sun/tools/common/PrintStreamPrinter.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019, 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 sun.tools.common; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.util.Arrays; + +/** + * A helper class which prints the content of input streams to print streams. + */ +public class PrintStreamPrinter { + + /** + * Reads characters in UTF-8 format from the input stream and prints them + * with the given print stream. Closes the input stream before it returns. + * + * @return The number of printed characters. + */ + public static long drainUTF8(InputStream is, PrintStream ps) throws IOException { + long result = 0; + + try (BufferedInputStream bis = new BufferedInputStream(is); + InputStreamReader isr = new InputStreamReader(bis, "UTF-8")) { + char c[] = new char[256]; + int n; + + do { + n = isr.read(c); + + if (n > 0) { + result += n; + ps.print(n == c.length ? c : Arrays.copyOf(c, n)); + } + } while (n > 0); + } + + return result; + } +} diff --git a/src/jdk.jcmd/share/classes/sun/tools/jcmd/JCmd.java b/src/jdk.jcmd/share/classes/sun/tools/jcmd/JCmd.java --- a/src/jdk.jcmd/share/classes/sun/tools/jcmd/JCmd.java +++ b/src/jdk.jcmd/share/classes/sun/tools/jcmd/JCmd.java @@ -25,13 +25,10 @@ package sun.tools.jcmd; +import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.IOException; import java.io.UnsupportedEncodingException; -import java.util.List; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.net.URISyntaxException; @@ -42,6 +39,7 @@ import sun.tools.attach.HotSpotVirtualMachine; import sun.tools.common.ProcessArgumentMatcher; +import sun.tools.common.PrintStreamPrinter; import sun.tools.jstat.JStatLogger; import sun.jvmstat.monitor.Monitor; import sun.jvmstat.monitor.MonitoredHost; @@ -122,23 +120,11 @@ if (line.trim().equals("stop")) { break; } - try (InputStream in = hvm.executeJCmd(line); - InputStreamReader isr = new InputStreamReader(in, "UTF-8")) { - // read to EOF and just print output - char c[] = new char[256]; - int n; - boolean messagePrinted = false; - do { - n = isr.read(c); - if (n > 0) { - String s = new String(c, 0, n); - System.out.print(s); - messagePrinted = true; - } - } while (n > 0); - if (!messagePrinted) { - System.out.println("Command executed successfully"); - } + + InputStream is = hvm.executeJCmd(line); + + if (PrintStreamPrinter.drainUTF8(is, System.out) == 0) { + System.out.println("Command executed successfully"); } } vm.detach(); diff --git a/src/jdk.jcmd/share/classes/sun/tools/jinfo/JInfo.java b/src/jdk.jcmd/share/classes/sun/tools/jinfo/JInfo.java --- a/src/jdk.jcmd/share/classes/sun/tools/jinfo/JInfo.java +++ b/src/jdk.jcmd/share/classes/sun/tools/jinfo/JInfo.java @@ -30,10 +30,10 @@ import java.util.Collection; import com.sun.tools.attach.VirtualMachine; -import com.sun.tools.attach.VirtualMachineDescriptor; import sun.tools.attach.HotSpotVirtualMachine; import sun.tools.common.ProcessArgumentMatcher; +import sun.tools.common.PrintStreamPrinter; /* * This class is the main class for the JInfo utility. It parses its arguments @@ -203,17 +203,7 @@ // Read the stream from the target VM until EOF, then detach private static void drain(VirtualMachine vm, InputStream in) throws IOException { - // read to EOF and just print output - byte b[] = new byte[256]; - int n; - do { - n = in.read(b); - if (n > 0) { - String s = new String(b, 0, n, "UTF-8"); - System.out.print(s); - } - } while (n > 0); - in.close(); + PrintStreamPrinter.drainUTF8(in, System.out); vm.detach(); } diff --git a/src/jdk.jcmd/share/classes/sun/tools/jstack/JStack.java b/src/jdk.jcmd/share/classes/sun/tools/jstack/JStack.java --- a/src/jdk.jcmd/share/classes/sun/tools/jstack/JStack.java +++ b/src/jdk.jcmd/share/classes/sun/tools/jstack/JStack.java @@ -29,9 +29,9 @@ import java.util.Collection; import com.sun.tools.attach.VirtualMachine; -import com.sun.tools.attach.VirtualMachineDescriptor; import sun.tools.attach.HotSpotVirtualMachine; import sun.tools.common.ProcessArgumentMatcher; +import sun.tools.common.PrintStreamPrinter; /* * This class is the main class for the JStack utility. It parses its arguments @@ -128,18 +128,8 @@ // Cast to HotSpotVirtualMachine as this is implementation specific // method. InputStream in = ((HotSpotVirtualMachine)vm).remoteDataDump((Object[])args); - // read to EOF and just print output - byte b[] = new byte[256]; - int n; - do { - n = in.read(b); - if (n > 0) { - String s = new String(b, 0, n, "UTF-8"); - System.out.print(s); - } - } while (n > 0); - in.close(); + PrintStreamPrinter.drainUTF8(in, System.out); vm.detach(); } diff --git a/test/jdk/sun/tools/jcmd/JcmdOutputEncodingTest.java b/test/jdk/sun/tools/jcmd/JcmdOutputEncodingTest.java new file mode 100644 --- /dev/null +++ b/test/jdk/sun/tools/jcmd/JcmdOutputEncodingTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019, 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. + * + * 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. + */ + +import java.util.Arrays; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.JDKToolLauncher; + +/* + * @test + * @bug 8222491 + * @summary Tests if we handle the encoding of jcmd output correctly. + * @library /test/lib + * @run main JcmdOutputEncodingTest + */ +public class JcmdOutputEncodingTest { + + public static void main(String[] args) throws Exception { + testThreadDump(); + } + + private static void testThreadDump() throws Exception { + String markerName = "markerName" + "\u00e4\u0bb5".repeat(10_000); + Thread.currentThread().setName(markerName); + + JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jcmd"); + launcher.addToolArg(Long.toString(ProcessTools.getProcessId())); + launcher.addToolArg("Thread.print"); + + ProcessBuilder processBuilder = new ProcessBuilder(); + processBuilder.command(launcher.getCommand()); + OutputAnalyzer output = ProcessTools.executeProcess(processBuilder); + output.shouldHaveExitValue(0); + output.shouldContain(markerName); + } +} diff --git a/test/jdk/sun/tools/jstack/BasicJStackTest.java b/test/jdk/sun/tools/jstack/BasicJStackTest.java --- a/test/jdk/sun/tools/jstack/BasicJStackTest.java +++ b/test/jdk/sun/tools/jstack/BasicJStackTest.java @@ -36,6 +36,7 @@ public class BasicJStackTest { private static ProcessBuilder processBuilder = new ProcessBuilder(); + private static String markerName = "markerName" + "\u00e4\u0bb5".repeat(10_000); public static void main(String[] args) throws Exception { testJstackNoArgs(); @@ -45,14 +46,17 @@ private static void testJstackNoArgs() throws Exception { OutputAnalyzer output = jstack(); output.shouldHaveExitValue(0); + output.shouldContain(markerName); } private static void testJstack_l() throws Exception { OutputAnalyzer output = jstack("-l"); output.shouldHaveExitValue(0); + output.shouldContain(markerName); } private static OutputAnalyzer jstack(String... toolArgs) throws Exception { + Thread.currentThread().setName(markerName); JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jstack"); launcher.addVMArg("-XX:+UsePerfData"); if (toolArgs != null) {