From cb5eb6cd3170851a414047a44d28e1c79aa06870 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Thu, 17 Apr 2014 18:20:31 +0200 Subject: [PATCH] 8039080: "jinfo server_id@host" fails with "Invalid process identifier" Reviewed-by: sla, sjiang, dsamersoff --- .../share/classes/sun/tools/jinfo/JInfo.java | 147 +++++--- .../sun/tools/jinfo/JInfoLauncherTest.java | 342 ++++++++++++++++++ 2 files changed, 438 insertions(+), 51 deletions(-) create mode 100644 jdk/test/sun/tools/jinfo/JInfoLauncherTest.java diff --git a/jdk/src/share/classes/sun/tools/jinfo/JInfo.java b/jdk/src/share/classes/sun/tools/jinfo/JInfo.java index f02eb27d2e2..4911ef713d9 100644 --- a/jdk/src/share/classes/sun/tools/jinfo/JInfo.java +++ b/jdk/src/share/classes/sun/tools/jinfo/JInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2014, 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 @@ -39,42 +39,73 @@ import sun.tools.attach.HotSpotVirtualMachine; * and decides if the command should be satisfied using the VM attach mechanism * or an SA tool. */ -public class JInfo { +final public class JInfo { + private boolean useSA = false; + private String[] args = null; - @SuppressWarnings("fallthrough") - public static void main(String[] args) throws Exception { + private JInfo(String[] args) throws IllegalArgumentException { if (args.length == 0) { - usage(1); // no arguments + throw new IllegalArgumentException(); } + int argCopyIndex = 0; // First determine if we should launch SA or not - boolean useSA = false; if (args[0].equals("-F")) { // delete the -F - args = Arrays.copyOfRange(args, 1, args.length); + argCopyIndex = 1; useSA = true; } else if (args[0].equals("-flags") - || args[0].equals("-sysprops")) + || args[0].equals("-sysprops")) { if (args.length == 2) { - if (!args[1].matches("[0-9]+")) { + if (!isPid(args[1])) { // If args[1] doesn't parse to a number then // it must be the SA debug server // (otherwise it is the pid) useSA = true; } - } - if (args.length == 3) { + } else if (args.length == 3) { // arguments include an executable and a core file useSA = true; + } else { + throw new IllegalArgumentException(); } } else if (!args[0].startsWith("-")) { if (args.length == 2) { // the only arguments are an executable and a core file useSA = true; + } else if (args.length == 1) { + if (!isPid(args[0])) { + // The only argument is not a PID; it must be SA debug + // server + useSA = true; + } + } else { + throw new IllegalArgumentException(); } - } else if (args[0].equals("-h") - || args[0].equals("-help")) { + } else if (args[0].equals("-h") || args[0].equals("-help")) { + if (args.length > 1) { + throw new IllegalArgumentException(); + } + } else if (args[0].equals("-flag")) { + if (args.length == 3) { + if (!isPid(args[2])) { + throw new IllegalArgumentException(); + } + } else { + throw new IllegalArgumentException(); + } + } else { + throw new IllegalArgumentException(); + } + + this.args = Arrays.copyOfRange(args, argCopyIndex, args.length); + } + + @SuppressWarnings("fallthrough") + private void execute() throws Exception { + if (args[0].equals("-h") + || args[0].equals("-help")) { usage(0); } @@ -87,55 +118,69 @@ public class JInfo { } // invoke SA which does it's own argument parsing - runTool(args); + runTool(); } else { // Now we can parse arguments for the non-SA case String pid = null; switch(args[0]) { - case "-flag": - if (args.length != 3) { - usage(1); - } - String option = args[1]; - pid = args[2]; - flag(pid, option); - break; - case "-flags": - if (args.length != 2) { - usage(1); - } - pid = args[1]; - flags(pid); - break; - case "-sysprops": - if (args.length != 2) { - usage(1); - } - pid = args[1]; - sysprops(pid); - break; - case "-help": - case "-h": - usage(0); - // Fall through - default: - if (args.length == 1) { - // no flags specified, we do -sysprops and -flags - pid = args[0]; - sysprops(pid); - System.out.println(); - flags(pid); - } else { - usage(1); - } + case "-flag": + if (args.length != 3) { + usage(1); + } + String option = args[1]; + pid = args[2]; + flag(pid, option); + break; + case "-flags": + if (args.length != 2) { + usage(1); + } + pid = args[1]; + flags(pid); + break; + case "-sysprops": + if (args.length != 2) { + usage(1); + } + pid = args[1]; + sysprops(pid); + break; + case "-help": + case "-h": + usage(0); + // Fall through + default: + if (args.length == 1) { + // no flags specified, we do -sysprops and -flags + pid = args[0]; + sysprops(pid); + System.out.println(); + flags(pid); + } else { + usage(1); + } } } } + public static void main(String[] args) throws Exception { + JInfo jinfo = null; + try { + jinfo = new JInfo(args); + jinfo.execute(); + } catch (IllegalArgumentException e) { + usage(1); + } + } + + private static boolean isPid(String arg) { + return arg.matches("[0-9]+"); + } + // Invoke SA tool with the given arguments - private static void runTool(String args[]) throws Exception { + private void runTool() throws Exception { String tool = "sun.jvm.hotspot.tools.JInfo"; // Tool not available on this platform. Class c = loadClass(tool); diff --git a/jdk/test/sun/tools/jinfo/JInfoLauncherTest.java b/jdk/test/sun/tools/jinfo/JInfoLauncherTest.java new file mode 100644 index 00000000000..ecd88831770 --- /dev/null +++ b/jdk/test/sun/tools/jinfo/JInfoLauncherTest.java @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2014, 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. + */ + +import org.testng.annotations.Test; +import org.testng.annotations.BeforeClass; +import sun.tools.jinfo.JInfo; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.util.Arrays; + +import static org.testng.Assert.*; + +/** + * @test + * @bug 8039080 + * @run testng JInfoLauncherTest + * @summary Test JInfo launcher argument parsing + */ +@Test +public class JInfoLauncherTest { + public static final String VALIDATION_EXCEPTION_CLSNAME = + IllegalArgumentException.class.getName(); + + private Constructor jInfoConstructor; + private Field fldUseSA; + + @BeforeClass + public void setup() throws Exception { + jInfoConstructor = JInfo.class.getDeclaredConstructor(String[].class); + jInfoConstructor.setAccessible(true); + fldUseSA = JInfo.class.getDeclaredField("useSA"); + fldUseSA.setAccessible(true); + } + + private JInfo newJInfo(String[] args) throws Exception { + try { + return jInfoConstructor.newInstance((Object) args); + } catch (Exception e) { + if (isValidationException(e.getCause())) { + throw (Exception)e.getCause(); + } + throw e; + } + } + + private boolean getUseSA(JInfo jinfo) throws Exception { + return fldUseSA.getBoolean(jinfo); + } + + private void cmdPID(String cmd, String ... params) throws Exception { + int offset = (cmd != null ? 1 : 0); + String[] args = new String[offset + params.length]; + args[0] = cmd; + System.arraycopy(params, 0, args, offset, params.length); + JInfo j = newJInfo(args); + assertFalse(getUseSA(j), "Local jinfo must not forward to SA"); + } + + private void cmdCore(String cmd, String ... params) throws Exception { + int offset = (cmd != null ? 1 : 0); + String[] args = new String[offset + params.length]; + args[0] = cmd; + System.arraycopy(params, 0, args, offset, params.length); + JInfo j = newJInfo(args); + assertTrue(getUseSA(j), "Core jinfo must forward to SA"); + } + + private void cmdRemote(String cmd, String ... params) throws Exception { + int offset = (cmd != null ? 1 : 0); + String[] args = new String[offset + params.length]; + args[0] = cmd; + System.arraycopy(params, 0, args, offset, params.length); + JInfo j = newJInfo(args); + assertTrue(getUseSA(j), "Remote jinfo must forward to SA"); + } + + private void cmdExtraArgs(String cmd, int argsLen) throws Exception { + String[] args = new String[argsLen + 1 + (cmd != null ? 1 : 0)]; + Arrays.fill(args, "a"); + if (cmd != null) { + args[0] = cmd; + } else { + cmd = "default"; + } + try { + JInfo j = newJInfo(args); + fail("\"" + cmd + "\" does not support more than " + argsLen + + " arguments"); + } catch (Exception e) { + if (!isValidationException(e)) { + throw e; + } + // ignore + } + } + + private void cmdMissingArgs(String cmd, int reqArgs) throws Exception { + String[] args = new String[reqArgs - 1 + (cmd != null ? 1 : 0)]; + Arrays.fill(args, "a"); + if (cmd != null) { + args[0] = cmd; + } else { + cmd = "default"; + } + try { + JInfo j = newJInfo(args); + fail("\"" + cmd + "\" requires at least " + reqArgs + " argument"); + } catch (Exception e) { + if (!isValidationException(e)) { + throw e; + } + // ignore + } + } + + public void testDefaultPID() throws Exception { + cmdPID(null, "1234"); + } + + public void testFlagsPID() throws Exception { + cmdPID("-flags", "1234"); + } + + public void testSyspropsPID() throws Exception { + cmdPID("-sysprops", "1234"); + } + + public void testReadFlagPID() throws Exception { + cmdPID("-flag", "SomeManagementFlag", "1234"); + } + + public void testSetFlag1PID() throws Exception { + cmdPID("-flag", "+SomeManagementFlag", "1234"); + } + + public void testSetFlag2PID() throws Exception { + cmdPID("-flag", "-SomeManagementFlag", "1234"); + } + + public void testSetFlag3PID() throws Exception { + cmdPID("-flag", "SomeManagementFlag=314", "1234"); + } + + public void testDefaultCore() throws Exception { + cmdCore(null, "myapp.exe", "my.core"); + } + + public void testFlagsCore() throws Exception { + cmdCore("-flags", "myapp.exe", "my.core"); + } + + public void testSyspropsCore() throws Exception { + cmdCore("-sysprops", "myapp.exe", "my.core"); + } + + public void testReadFlagCore() throws Exception { + try { + cmdCore("-flag", "SomeManagementFlag", "myapp.exe", "my.core"); + fail("Flags can not be read from core files"); + } catch (Exception e) { + if (!isValidationException(e)) { + throw e; + } + // ignore + } + } + + public void testSetFlag1Core() throws Exception { + try { + cmdCore("-flag", "+SomeManagementFlag", "myapp.exe", "my.core"); + fail("Flags can not be set in core files"); + } catch (Exception e) { + if (!isValidationException(e)) { + throw e; + } + // ignore + } + } + + public void testSetFlag2Core() throws Exception { + try { + cmdCore("-flag", "-SomeManagementFlag", "myapp.exe", "my.core"); + fail("Flags can not be set in core files"); + } catch (Exception e) { + if (!isValidationException(e)) { + throw e; + } + // ignore + } + } + + public void testSetFlag3Core() throws Exception { + try { + cmdCore("-flag", "SomeManagementFlag=314", "myapp.exe", "my.core"); + fail("Flags can not be set in core files"); + } catch (Exception e) { + if (!isValidationException(e)) { + throw e; + } + // ignore + } + } + + public void testDefaultRemote() throws Exception { + cmdRemote(null, "serverid@host"); + } + + public void testFlagsRemote() throws Exception { + cmdRemote("-flags", "serverid@host"); + } + + public void testSyspropsRemote() throws Exception { + cmdRemote("-sysprops", "serverid@host"); + } + + public void testReadFlagRemote() throws Exception { + try { + cmdCore("-flag", "SomeManagementFlag", "serverid@host"); + fail("Flags can not be read from SA server"); + } catch (Exception e) { + if (!isValidationException(e)) { + throw e; + } + // ignore + } + } + + public void testSetFlag1Remote() throws Exception { + try { + cmdCore("-flag", "+SomeManagementFlag","serverid@host"); + fail("Flags can not be set on SA server"); + } catch (Exception e) { + if (!isValidationException(e)) { + throw e; + } + // ignore + } + } + + public void testSetFlag2Remote() throws Exception { + try { + cmdCore("-flag", "-SomeManagementFlag", "serverid@host"); + fail("Flags can not be read set on SA server"); + } catch (Exception e) { + if (!isValidationException(e)) { + throw e; + } + // ignore + } + } + + public void testSetFlag3Remote() throws Exception { + try { + cmdCore("-flag", "SomeManagementFlag=314", "serverid@host"); + fail("Flags can not be read set on SA server"); + } catch (Exception e) { + if (!isValidationException(e)) { + throw e; + } + // ignore + } + } + + public void testDefaultExtraArgs() throws Exception { + cmdExtraArgs(null, 2); + } + + public void testFlagsExtraArgs() throws Exception { + cmdExtraArgs("-flags", 2); + } + + public void testSyspropsExtraArgs() throws Exception { + cmdExtraArgs("-sysprops", 2); + } + + public void testFlagExtraArgs() throws Exception { + cmdExtraArgs("-flag", 2); + } + + public void testHelp1ExtraArgs() throws Exception { + cmdExtraArgs("-h", 0); + } + + public void testHelp2ExtraArgs() throws Exception { + cmdExtraArgs("-help", 0); + } + + public void testDefaultMissingArgs() throws Exception { + cmdMissingArgs(null, 1); + } + + public void testFlagsMissingArgs() throws Exception { + cmdMissingArgs("-flags", 1); + } + + public void testSyspropsMissingArgs() throws Exception { + cmdMissingArgs("-sysprops", 1); + } + + public void testFlagMissingArgs() throws Exception { + cmdMissingArgs("-flag", 2); + } + + public void testUnknownCommand() throws Exception { + try { + JInfo j = newJInfo(new String[]{"-unknown_command"}); + fail("JInfo accepts unknown commands"); + } catch (Exception e) { + if (!isValidationException(e)) { + throw e; + } + // ignore + } + } + + private static boolean isValidationException(Throwable e) { + return e.getClass().getName().equals(VALIDATION_EXCEPTION_CLSNAME); + } +}