changeset 9853:8371276d52c0

8039080: "jinfo server_id@host" fails with "Invalid process identifier" Reviewed-by: sla, sjiang, dsamersoff
author jbachorik
date Thu, 17 Apr 2014 18:20:31 +0200
parents ca512a1ade9a
children a311c31fc622 ab06ba289431
files src/share/classes/sun/tools/jinfo/JInfo.java test/sun/tools/jinfo/JInfoLauncherTest.java
diffstat 2 files changed, 438 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/sun/tools/jinfo/JInfo.java	Thu Apr 17 15:44:32 2014 +0100
+++ b/src/share/classes/sun/tools/jinfo/JInfo.java	Thu Apr 17 18:20:31 2014 +0200
@@ -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 @@
  * 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 @@
             }
 
             // 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);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/tools/jinfo/JInfoLauncherTest.java	Thu Apr 17 18:20:31 2014 +0200
@@ -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<JInfo> 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);
+    }
+}