/* * Copyright (c) 2001, 2018, 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. */ package nsk.share.jpda; import nsk.share.*; import java.io.*; import java.net.ServerSocket; /** * Parser for JPDA test's launching and connection arguments. *
* DebugeeArgumentHandler
handles specific JDI/JDWP/JDB tests
* command line arguments related to launching and connection parameters
* for debugee VM in addition to general arguments recognized by
* ArgumentParser
.
*
* Following is the list of specific options for
* DebugeeAgrumentHandler
:
*
-test.host=
<host> -
* address of a host where test executes
* -debugee.host=
<host> -
* address of a host where debugee VM executes
* -connector=[attaching|listening]
-
* connector type to connect to debugee VM
* -transport=[socket|shmem]
-
* transport type to connect to debugee VM
* -transport.port=
<port> -
* port number for socket
transport
* -transport.shname=
<name> -
* shared memory name for shmem
transport
* -transport.address=
<dynamic> -
* use dynamically allocated unique transport address for JDWP connection
* ignoring settings for -transport.port
and -transport.shname
* (this works only with -connector=listening
and -transport=socket
)
* -debugee.suspend=[yes|no|default]
-
* should debugee start in suspend mode or not
* -debugee.launch=[local|remote|manual]
-
* launch and bind to debugee VM locally, remotely (via BindSever) or manually
* -debugee.vmhome=
<path> -
* path to JDK used for launching debugee VM
* -debugee.vmkind=
<name> -
* name of debugee VM launcher executable
* -debugee.vmkeys=
<string> -
* additional options for launching debugee VM
* -jvmdi.strict=[yes|no|default]
-
* using JVMDI strict mode
* -pipe.port=
<port> -
* port number for internal IOPipe connection
* -bind.port=
<port> -
* port number for BindServer connection
*
* See also list of basic options recognized by
* ArgumentParser
.
*
* See also comments to ArgumentParser
for list of general
* recognized options and how to work with command line arguments and options.
*
* @see ArgumentParser
* @see nsk.share.jdi.ArgumentHandler
* @see nsk.share.jdwp.ArgumentHandler
*/
public class DebugeeArgumentHandler extends ArgumentParser {
public static final String DEFAULT_PIPE_PORT = "7123";
public static final String DEFAULT_TRANSPORT_PORT = "8123";
public static final String DEFAULT_BIND_PORT = "9123";
/**
* Keep a copy of raw command-line arguments and parse them;
* but throw an exception on parsing error.
*
* @param args Array of the raw command-line arguments.
*
* @throws BadOption If unknown option or illegal
* option value found
*
* @see #setRawArguments(String[])
*/
public DebugeeArgumentHandler(String args[]) {
super(args);
}
/**
* Return name of the host where test executes, specified by
* -test.host
command line option or
* "localhost" string by default.
*
* @see #setRawArguments(String[])
*/
public String getTestHost() {
return options.getProperty("test.host", "localhost");
}
/**
* Return name of host where the debugee VM is executed, specified by
* -debugee.host
command line option or value of
* getTestHost() by default.
*
* @see #getTestHost()
* @see #setRawArguments(String[])
*/
public String getDebugeeHost() {
return options.getProperty("debugee.host", getTestHost());
}
private boolean transportPortInited = false;
/**
* Return string representation of port number for socket transport,
* specified by -tranport.port
command line option or
* "DEFAULT_TRANSPORT_PORT
" string by default.
*
* @see #getTransportPortIfNotDynamic()
* @see #getTransportPortNumber()
* @see #setTransportPortNumber(int)
* @see #setRawArguments(String[])
*/
synchronized public String getTransportPort() {
String port = options.getProperty("transport.port");
if (port == null) {
if (!transportPortInited) {
port = findFreePort();
if (port == null) {
port = DEFAULT_TRANSPORT_PORT;
}
options.setProperty("transport.port", port);
transportPortInited = true;
}
}
return port;
}
/**
* Return string representation of port number for socket transport,
* specified by -tranport.port
command line option or
* "DEFAULT_TRANSPORT_PORT
" string by default in case transport address is
* not dynamic.
* Otherwise null is returned.
*
* @see #getTransportPort()
* @see #getTransportPortNumber()
* @see #setTransportPortNumber(int)
* @see #setRawArguments(String[])
*/
public String getTransportPortIfNotDynamic() {
return ( isTransportAddressDynamic() ?
null : getTransportPort() );
}
/**
* Return string port number for socket transport,
* specified by -debugee.port
command line option or
* DEFAULT_TRANSPORT_PORT
port number by default.
*
* @see #getTransportPort()
* @see #getTransportPortIfNotDynamic()
* @see #setTransportPortNumber(int)
* @see #setRawArguments(String[])
*/
public int getTransportPortNumber() {
String value = getTransportPort();
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new TestBug("Not integer value of \"-transport.port\" argument: " + value);
}
}
/**
* Add or replace value of option -transport.port
in options list
* with the specified port number.
*
* @see #getTransportPortNumber()
* @see #setRawArguments(String[])
*/
public void setTransportPortNumber(int port) {
String value = Integer.toString(port);
setOption("-", "transport.port", value);
}
/**
* Return shared name for shmem transport, specified by
* -transport.shname
command line option, or
* "nskjpdatestchannel" + a process unique string by default.
*
* @see #setTransportSharedName(String)
* @see #setRawArguments(String[])
*/
// Use a unique id for this process by default. This makes sure that
// tests running concurrently do not use the same shared name.
private static String defaultTransportSharedName
= "nskjpdatestchannel" + ProcessHandle.current().pid();
public String getTransportSharedName() {
return options.getProperty("transport.shname", defaultTransportSharedName);
}
/**
* Add or replace value of option -transport.shname
in options list
* with the specified name.
*
* @see #getTransportSharedName()
* @see #setRawArguments(String[])
*/
public void setTransportSharedName(String name) {
setOption("-", "transport.shname", name);
}
/**
* Return true if -transport.address=dynamic
command line option
* is specified.
*
* @see #setRawArguments(String[])
*/
public boolean isTransportAddressDynamic() {
String value = options.getProperty("transport.address", null);
if (value != null && value.equals("dynamic"))
return true;
return false;
}
/**
* Return suspend mode for launching debugee VM, specified by
* -debugee.suspend
command line option, or
* "default" string by default.
*
* @see #isDefaultDebugeeSuspendMode()
* @see #willDebugeeSuspended()
* @see #setRawArguments(String[])
*/
public String getDebugeeSuspendMode() {
return options.getProperty("debugee.suspend", "default");
}
/**
* Return true if default suspend mode is used
* for launching debugee VM.
*
* @see #getDebugeeSuspendMode()
* @see #willDebugeeSuspended()
*/
public boolean isDefaultDebugeeSuspendMode() {
String mode = getDebugeeSuspendMode();
return mode.equals("default");
}
/**
* Return true if debugee VM will be suspended after launching,
* either according to specified suspend mode or by default.
*
* @see #getDebugeeSuspendMode()
* @see #isDefaultDebugeeSuspendMode()
*/
public boolean willDebugeeSuspended() {
if (isLaunchedLocally()) {
String mode = getDebugeeSuspendMode();
return mode.equals("no");
}
return true;
}
private boolean pipePortInited = false;
/**
* Return string representation of the port number for IOPipe connection,
* specified by -pipe.port
command line option, or
* "DEFAULT_PIPE_PORT" string by default.
*
* @see #getPipePortNumber()
* @see #setPipePortNumber(int)
* @see #setRawArguments(String[])
*/
synchronized public String getPipePort() {
String port = options.getProperty("pipe.port");
if (port == null) {
if (!pipePortInited) {
port = findFreePort();
if (port == null) {
port = DEFAULT_PIPE_PORT;
}
pipePortInited = true;
options.setProperty("pipe.port", port);
}
}
return port;
}
/**
* Return port number for IOPipe connection,
* specified by -pipe.port
command line option, or
* DEFAULT_PIPE_PORT port number by default.
*
* @see #getPipePort()
* @see #setPipePortNumber(int)
* @see #setRawArguments(String[])
*/
public int getPipePortNumber() {
String value = getPipePort();
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new TestBug("Not integer value of \"-pipe.port\" argument: " + value);
}
}
/**
* Add or replace value of option -pipe.port
in options list
* with the specified port number.
*
* @see #getPipePortNumber()
* @see #setRawArguments(String[])
*/
public void setPipePortNumber(int port) {
String value = Integer.toString(port);
setOption("-", "pipe.port", value);
}
/**
* Return debugee VM launching mode, specified by
* -launch.mode
command line option, or
* "local" string by default.
*
* Possible values for this option are:
*
local
"
* remote
"
* manual
"
* -launch.options
command line option, or
* empty string by default.
*
* @see #setRawArguments(String[])
*/
public String getLaunchOptions() {
String result = options.getProperty("debugee.vmkeys", "").trim();
if (result.startsWith("\"") && result.endsWith("\"")) {
result = result.substring(1, result.length() - 1);
}
return result;
}
/**
* Return name of debugee VM launcher executable, specified by
* -launch.vmexec
command line option, or
* "java" string by default.
*
* @see #setRawArguments(String[])
*/
public String getLaunchExecName() {
return options.getProperty("debugee.vmkind", "java");
}
/**
* Return full path to debugee VM launcher executable.
*
* @see #getLaunchExecName()
* @see #getLaunchExecPath(String)
* @see #getDebugeeJavaHome()
*/
public String getLaunchExecPath() {
String java_home = getDebugeeJavaHome();
return getLaunchExecPath(java_home);
}
/**
* Return full path to VM launcher executable using givet JAVA_HOME path.
*
* @see #getLaunchExecName()
*/
public String getLaunchExecPath(String java_home) {
String filesep = System.getProperty("file.separator");
return java_home + filesep + "bin" + filesep + getLaunchExecName();
}
/**
* Return full JAVA_HOME path for debugee VM.
*
* @see #getLaunchExecName()
*/
public String getDebugeeJavaHome() {
String java_home = System.getProperty("java.home");
return options.getProperty("debugee.vmhome", java_home);
}
/**
* Return true if default debuggee VM launcher executable is used.
*
* @see #getLaunchExecName()
*/
public boolean isDefaultLaunchExecName() {
String vmkind = options.getProperty("debugee.vmkind", null);
return (vmkind == null);
}
/**
* Return true if default JAVA_HOME path for debuggee VM is used.
*
* @see #getDebugeeJavaHome()
*/
public boolean isDefaultDebugeeJavaHome() {
String java_home = options.getProperty("debugee.vmhome", null);
return (java_home == null);
}
private boolean bindPortInited = false;
/**
* Return string representation of the port number for BindServer connection,
* specified by -bind.port
command line option, or
* "DEFAULT_BIND_PORT" string by default.
*
* @see #getBindPortNumber()
* @see #setRawArguments(String[])
*/
public String getBindPort() {
String port = options.getProperty("bind.port");
if (port == null) {
if (!bindPortInited) {
port = findFreePort();
if (port == null) {
port = DEFAULT_BIND_PORT;
}
options.setProperty("bind.port", port);
bindPortInited = true;
}
}
return port;
}
/**
* Return port number for BindServer connection,
* specified by -bind.port
command line option, or
* "DEFAULT_BIND_PORT" port number by default.
*
* @see #getBindPort()
* @see #setRawArguments(String[])
*/
public int getBindPortNumber() {
String value = getBindPort();
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new TestBug("Not integer value of \"bind.port\" argument: " + value);
}
}
/**
* Return JVMDI strict mode for launching debugee VM, specified by.
* -jvmdi.strict
command line option, or
* "default" string by default.
*
* Possible values for this option are:
* yes
"
* no
"
* default
"
* -connector
command line option, or
* "listening" string by default.
*
* Possible values for this option are:
* attaching
"
* listening
"
* attaching
.
*
* @see #getConnectorType()
*/
public boolean isAttachingConnector() {
return getConnectorType().equals("attaching");
}
/**
* Return true if type of the used JDI connector is listening
.
*
* @see #getConnectorType()
*/
public boolean isListeningConnector() {
return getConnectorType().equals("listening");
}
/**
* Return true if connector type is not actually specified.
* In this case getConnectorType() returns some default connector type.
*
* @see #getConnectorType()
*/
public boolean isDefaultConnector() {
return options.getProperty("connector") == null;
}
/**
* Return type of JDWP transport for connecting to debugee VM, specified by
* -transport
command line option, or
* "socket" string by default.
*
* Possible values for this option are:
* socket
"
* shmem
"
* socket
,
* either by specifying in command line or as a platform default transport.
*
* @see #getTransportType()
*/
public boolean isSocketTransport() {
String transport = getTransportType();
return transport.equals("socket");
}
/**
* Return true if the used JDWP transport type is shmem
,
* either by specifying in command line or as a platform default transport.
*
* @see #getTransportType()
*/
public boolean isShmemTransport() {
String transport = getTransportType();
return transport.equals("shmem");
}
/**
* Return true if transport type is not actually specified.
* In this case getTransportType() returns some default transport kind.
*
* @see #getTransportType()
*/
public boolean isDefaultTransport() {
return options.getProperty("transport") == null;
}
/**
* Create Log
for debugee application using command line options.
*/
public Log createDebugeeLog() {
return new Log(System.err, this);
};
/**
* Create IOPipe for debugee application using command line options.
*/
public IOPipe createDebugeeIOPipe() {
return createDebugeeIOPipe(createDebugeeLog());
};
/**
* Create IOPipe for debugee application using connection
* parameters from the command line and specify Log.
*/
public IOPipe createDebugeeIOPipe(Log log) {
return new IOPipe(this, log);
};
/**
* Check if an option is aloowed and has proper value.
* This method is invoked by parseArgumentss()
*
* @param option option name
* @param value string representation of value
* (could be an empty string too)
* null if this option has no value
* @return true if option is allowed and has proper value
* false if otion is not admissible
*
* @throws BadOption if option has an illegal value
*
* @see #parseArguments()
*/
protected boolean checkOption(String option, String value) {
if(option.equals("traceAll"))
return true;
// option with any string value
if (option.equals("debugee.vmkeys")) {
return true;
}
// option with any nonempty string value
if (option.equals("test.host")
|| option.equals("debugee.host")
|| option.equals("debugee.vmkind")
|| option.equals("debugee.vmhome")
|| option.equals("transport.shname")) {
if (value.length() <= 0) {
throw new BadOption(option + ": cannot be an empty string");
}
return true;
}
// option with positive integer port value
if (option.equals("transport.port")
|| option.equals("bind.port")
|| option.equals("pipe.port")) {
try {
int number = Integer.parseInt(value);
if (number < 0) {
throw new BadOption(option + ": must be a positive integer");
}
} catch (NumberFormatException e) {
throw new BadOption(option + ": must be an integer");
}
return true;
}
// options with enumerated values
if (option.equals("debugee.suspend")) {
if ((!value.equals("yes"))
&& (!value.equals("no"))
&& (!value.equals("default"))) {
throw new BadOption(option + ": must be one of: "
+ "yes, no, default");
}
return true;
}
if (option.equals("debugee.launch")) {
if ((!value.equals("local"))
&& (!value.equals("remote"))
&& (!value.equals("manual"))) {
throw new BadOption(option + ": must be one of: "
+ "local, remote, manual " + value);
}
return true;
}
if (option.equals("jvmdi.strict")) {
if ((!value.equals("yes"))
&& (!value.equals("no"))
&& (!value.equals("default"))) {
throw new BadOption(option + ": must be one of: "
+ "yes, no, default");
}
return true;
}
if (option.equals("transport")) {
if ((!value.equals("socket"))
&& (!value.equals("shmem"))) {
throw new BadOption(option + ": must be one of: "
+ "socket, shmem");
}
return true;
}
if (option.equals("connector")) {
if ((!value.equals("attaching"))
&& (!value.equals("listening"))) {
throw new BadOption(option + ": value must be one of: "
+ "attaching, listening");
}
return true;
}
if (option.equals("transport.address")) {
if (!value.equals("dynamic")) {
throw new BadOption(option + ": must be only: "
+ "dynamic");
}
return true;
}
return super.checkOption(option, value);
}
/**
* Check if the values of all options are consistent.
* This method is invoked by parseArguments()
*
* @throws BadOption if options have inconsistent values
*
* @see #parseArguments()
*/
protected void checkOptions() {
super.checkOptions();
}
private String findFreePort() {
ServerSocket ss = null;
try {
ss = new ServerSocket(0);
return String.valueOf(ss.getLocalPort());
} catch (IOException e) {
return null;
} finally {
try {
ss.close();
} catch (Throwable t) {
// ignore
}
}
}
} // DebugeeArgumentHandler