Path: blob/master/test/hotspot/jtreg/vmTestbase/nsk/share/jpda/DebugeeArgumentHandler.java
41161 views
/*1* Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223package nsk.share.jpda;2425import nsk.share.*;2627import java.io.*;28import java.net.ServerSocket;2930/**31* Parser for JPDA test's launching and connection arguments.32* <p>33* <Code>DebugeeArgumentHandler</code> handles specific JDI/JDWP/JDB tests34* command line arguments related to launching and connection parameters35* for debugee VM in addition to general arguments recognized by36* <code>ArgumentParser</code>.37* <p>38* Following is the list of specific options for39* <code>DebugeeAgrumentHandler</code>:40* <ul>41* <li> <code>-test.host=</code><<i>host</i>> -42* address of a host where test executes43* <li> <code>-debugee.host=</code><<i>host</i>> -44* address of a host where debugee VM executes45* <li> <code>-connector=[attaching|listening]</code> -46* connector type to connect to debugee VM47* <li> <code>-transport=[socket|shmem]</code> -48* transport type to connect to debugee VM49* <li> <code>-transport.port=</code><<i>port</i>> -50* port number for <code>socket</code> transport51* <li> <code>-transport.shname=</code><<i>name</i>> -52* shared memory name for <code>shmem</code> transport53* <li> <code>-transport.address=</code><<i>dynamic</i>> -54* use dynamically allocated unique transport address for JDWP connection55* ignoring settings for <code>-transport.port</code> and <code>-transport.shname</code>56* (this works only with <code>-connector=listening</code> and <code>-transport=socket</code>)57* <li> <code>-debugee.suspend=[yes|no|default]</code> -58* should debugee start in suspend mode or not59* <li> <code>-debugee.launch=[local|remote|manual]</code> -60* launch and bind to debugee VM locally, remotely (via BindSever) or manually61* <li> <code>-debugee.vmhome=</code><<i>path</i>> -62* path to JDK used for launching debugee VM63* <li> <code>-debugee.vmkind=</code><<i>name</i>> -64* name of debugee VM launcher executable65* <li> <code>-debugee.vmkeys=</code><<i>string</i>> -66* additional options for launching debugee VM67* <li> <code>-jvmdi.strict=[yes|no|default]</code> -68* using JVMDI strict mode69* <li> <code>-pipe.port=</code><<i>port</i>> -70* port number for internal IOPipe connection71* <li> <code>-bind.port=</code><<i>port</i>> -72* port number for BindServer connection73* </ul>74* <p>75* See also list of basic options recognized by76* <code>ArgumentParser</code>.77* <p>78* See also comments to <code>ArgumentParser</code> for list of general79* recognized options and how to work with command line arguments and options.80*81* @see ArgumentParser82* @see nsk.share.jdi.ArgumentHandler83* @see nsk.share.jdwp.ArgumentHandler84*/85public class DebugeeArgumentHandler extends ArgumentParser {8687public static final String DEFAULT_PIPE_PORT = "7123";88public static final String DEFAULT_TRANSPORT_PORT = "8123";89public static final String DEFAULT_BIND_PORT = "9123";909192/**93* Keep a copy of raw command-line arguments and parse them;94* but throw an exception on parsing error.95*96* @param args Array of the raw command-line arguments.97*98* @throws BadOption If unknown option or illegal99* option value found100*101* @see #setRawArguments(String[])102*/103public DebugeeArgumentHandler(String args[]) {104super(args);105}106107/**108* Return name of the host where test executes, specified by109* <code>-test.host</code> command line option or110* "<i>localhost</i>" string by default.111*112* @see #setRawArguments(String[])113*/114public String getTestHost() {115return options.getProperty("test.host", "localhost");116}117118/**119* Return name of host where the debugee VM is executed, specified by120* <code>-debugee.host</code> command line option or value of121* getTestHost() by default.122*123* @see #getTestHost()124* @see #setRawArguments(String[])125*/126public String getDebugeeHost() {127return options.getProperty("debugee.host", getTestHost());128}129130private boolean transportPortInited = false;131/**132* Return string representation of port number for socket transport,133* specified by <code>-tranport.port</code> command line option or134* "<code>DEFAULT_TRANSPORT_PORT</code>" string by default.135*136* @see #getTransportPortIfNotDynamic()137* @see #getTransportPortNumber()138* @see #setTransportPortNumber(int)139* @see #setRawArguments(String[])140*/141synchronized public String getTransportPort() {142String port = options.getProperty("transport.port");143if (port == null) {144if (!transportPortInited) {145port = findFreePort();146if (port == null) {147port = DEFAULT_TRANSPORT_PORT;148}149options.setProperty("transport.port", port);150transportPortInited = true;151}152}153return port;154}155156/**157* Return string representation of port number for socket transport,158* specified by <code>-tranport.port</code> command line option or159* "<code>DEFAULT_TRANSPORT_PORT</code>" string by default in case transport address is160* not dynamic.161* Otherwise null is returned.162*163* @see #getTransportPort()164* @see #getTransportPortNumber()165* @see #setTransportPortNumber(int)166* @see #setRawArguments(String[])167*/168public String getTransportPortIfNotDynamic() {169return ( isTransportAddressDynamic() ?170null : getTransportPort() );171}172173/**174* Return string port number for socket transport,175* specified by <code>-debugee.port</code> command line option or176* <code>DEFAULT_TRANSPORT_PORT</code> port number by default.177*178* @see #getTransportPort()179* @see #getTransportPortIfNotDynamic()180* @see #setTransportPortNumber(int)181* @see #setRawArguments(String[])182*/183public int getTransportPortNumber() {184String value = getTransportPort();185try {186return Integer.parseInt(value);187} catch (NumberFormatException e) {188throw new TestBug("Not integer value of \"-transport.port\" argument: " + value);189}190}191192/**193* Add or replace value of option <code>-transport.port</code> in options list194* with the specified port number.195*196* @see #getTransportPortNumber()197* @see #setRawArguments(String[])198*/199public void setTransportPortNumber(int port) {200String value = Integer.toString(port);201setOption("-", "transport.port", value);202}203204/**205* Return shared name for shmem transport, specified by206* <code>-transport.shname</code> command line option, or207* "<i>nskjpdatestchannel</i>" + a process unique string by default.208*209* @see #setTransportSharedName(String)210* @see #setRawArguments(String[])211*/212// Use a unique id for this process by default. This makes sure that213// tests running concurrently do not use the same shared name.214private static String defaultTransportSharedName215= "nskjpdatestchannel" + ProcessHandle.current().pid();216public String getTransportSharedName() {217return options.getProperty("transport.shname", defaultTransportSharedName);218}219220/**221* Add or replace value of option <code>-transport.shname</code> in options list222* with the specified name.223*224* @see #getTransportSharedName()225* @see #setRawArguments(String[])226*/227public void setTransportSharedName(String name) {228setOption("-", "transport.shname", name);229}230231/**232* Return <i>true</i> if <code>-transport.address=dynamic</code> command line option233* is specified.234*235* @see #setRawArguments(String[])236*/237public boolean isTransportAddressDynamic() {238String value = options.getProperty("transport.address", null);239if (value != null && value.equals("dynamic"))240return true;241return false;242}243244/**245* Return suspend mode for launching debugee VM, specified by246* <code>-debugee.suspend</code> command line option, or247* "<i>default</i>" string by default.248*249* @see #isDefaultDebugeeSuspendMode()250* @see #willDebugeeSuspended()251* @see #setRawArguments(String[])252*/253public String getDebugeeSuspendMode() {254return options.getProperty("debugee.suspend", "default");255}256257/**258* Return <i>true</i> if default suspend mode is used259* for launching debugee VM.260*261* @see #getDebugeeSuspendMode()262* @see #willDebugeeSuspended()263*/264public boolean isDefaultDebugeeSuspendMode() {265String mode = getDebugeeSuspendMode();266return mode.equals("default");267}268269/**270* Return <i>true</i> if debugee VM will be suspended after launching,271* either according to specified suspend mode or by default.272*273* @see #getDebugeeSuspendMode()274* @see #isDefaultDebugeeSuspendMode()275*/276public boolean willDebugeeSuspended() {277if (isLaunchedLocally()) {278String mode = getDebugeeSuspendMode();279return mode.equals("no");280}281return true;282}283284private boolean pipePortInited = false;285/**286* Return string representation of the port number for IOPipe connection,287* specified by <code>-pipe.port</code> command line option, or288* "<i>DEFAULT_PIPE_PORT</i>" string by default.289*290* @see #getPipePortNumber()291* @see #setPipePortNumber(int)292* @see #setRawArguments(String[])293*/294synchronized public String getPipePort() {295String port = options.getProperty("pipe.port");296if (port == null) {297if (!pipePortInited) {298port = findFreePort();299if (port == null) {300port = DEFAULT_PIPE_PORT;301}302pipePortInited = true;303options.setProperty("pipe.port", port);304}305}306return port;307}308309/**310* Return port number for IOPipe connection,311* specified by <code>-pipe.port</code> command line option, or312* <i>DEFAULT_PIPE_PORT</i> port number by default.313*314* @see #getPipePort()315* @see #setPipePortNumber(int)316* @see #setRawArguments(String[])317*/318public int getPipePortNumber() {319String value = getPipePort();320try {321return Integer.parseInt(value);322} catch (NumberFormatException e) {323throw new TestBug("Not integer value of \"-pipe.port\" argument: " + value);324}325}326327/**328* Add or replace value of option <code>-pipe.port</code> in options list329* with the specified port number.330*331* @see #getPipePortNumber()332* @see #setRawArguments(String[])333*/334public void setPipePortNumber(int port) {335String value = Integer.toString(port);336setOption("-", "pipe.port", value);337}338339/**340* Return debugee VM launching mode, specified by341* <code>-launch.mode</code> command line option, or342* "<i>local</i>" string by default.343*344* Possible values for this option are:345* <ul>346* <li> "<code>local</code>"347* <li> "<code>remote</code>"348* <li> "<code>manual</code>"349* </ul>350*351* @see #isLaunchedLocally()352* @see #isLaunchedRemotely()353* @see #isLaunchedManually()354* @see #setRawArguments(String[])355*/356public String getLaunchMode() {357return options.getProperty("debugee.launch", "local");358}359360/**361* Return <i>true</i> if debugee should be launched locally.362*363* @see #getLaunchMode()364*/365public boolean isLaunchedLocally() {366return getLaunchMode().equals("local");367}368369/**370* Return <i>true</i> if debugee should be launched remotely via371* BindServer.372*373* @see #getLaunchMode()374*/375public boolean isLaunchedRemotely() {376return getLaunchMode().equals("remote");377}378379/**380* Return <i>true</i> if debugee should be launched manually by user.381*382* @see #getLaunchMode()383*/384public boolean isLaunchedManually() {385return getLaunchMode().equals("manual");386}387388/**389* Return additional options for launching debugee VM, specified by390* <code>-launch.options</code> command line option, or391* empty string by default.392*393* @see #setRawArguments(String[])394*/395public String getLaunchOptions() {396String result = options.getProperty("debugee.vmkeys", "").trim();397if (result.startsWith("\"") && result.endsWith("\"")) {398result = result.substring(1, result.length() - 1);399}400return result;401}402403/**404* Return name of debugee VM launcher executable, specified by405* <code>-launch.vmexec</code> command line option, or406* "<i>java</i>" string by default.407*408* @see #setRawArguments(String[])409*/410public String getLaunchExecName() {411return options.getProperty("debugee.vmkind", "java");412}413414/**415* Return full path to debugee VM launcher executable.416*417* @see #getLaunchExecName()418* @see #getLaunchExecPath(String)419* @see #getDebugeeJavaHome()420*/421public String getLaunchExecPath() {422String java_home = getDebugeeJavaHome();423return getLaunchExecPath(java_home);424}425426/**427* Return full path to VM launcher executable using givet JAVA_HOME path.428*429* @see #getLaunchExecName()430*/431public String getLaunchExecPath(String java_home) {432String filesep = System.getProperty("file.separator");433return java_home + filesep + "bin" + filesep + getLaunchExecName();434}435436/**437* Return full JAVA_HOME path for debugee VM.438*439* @see #getLaunchExecName()440*/441public String getDebugeeJavaHome() {442String java_home = System.getProperty("java.home");443return options.getProperty("debugee.vmhome", java_home);444}445446/**447* Return true if default debuggee VM launcher executable is used.448*449* @see #getLaunchExecName()450*/451public boolean isDefaultLaunchExecName() {452String vmkind = options.getProperty("debugee.vmkind", null);453return (vmkind == null);454}455456/**457* Return true if default JAVA_HOME path for debuggee VM is used.458*459* @see #getDebugeeJavaHome()460*/461public boolean isDefaultDebugeeJavaHome() {462String java_home = options.getProperty("debugee.vmhome", null);463return (java_home == null);464}465466private boolean bindPortInited = false;467/**468* Return string representation of the port number for BindServer connection,469* specified by <code>-bind.port</code> command line option, or470* "<i>DEFAULT_BIND_PORT</i>" string by default.471*472* @see #getBindPortNumber()473* @see #setRawArguments(String[])474*/475public String getBindPort() {476String port = options.getProperty("bind.port");477if (port == null) {478if (!bindPortInited) {479port = findFreePort();480if (port == null) {481port = DEFAULT_BIND_PORT;482}483options.setProperty("bind.port", port);484bindPortInited = true;485}486}487return port;488}489490/**491* Return port number for BindServer connection,492* specified by <code>-bind.port</code> command line option, or493* "<i>DEFAULT_BIND_PORT</i>" port number by default.494*495* @see #getBindPort()496* @see #setRawArguments(String[])497*/498public int getBindPortNumber() {499String value = getBindPort();500try {501return Integer.parseInt(value);502} catch (NumberFormatException e) {503throw new TestBug("Not integer value of \"bind.port\" argument: " + value);504}505}506507/**508* Return JVMDI strict mode for launching debugee VM, specified by.509* <code>-jvmdi.strict</code> command line option, or510* "<i>default</i>" string by default.511*512* Possible values for this option are:513* <ul>514* <li> "<code>yes</code>"515* <li> "<code>no</code>"516* <li> "<code>default</code>"517* </ul>518*519* @see #setRawArguments(String[])520*/521public String getJVMDIStrictMode() {522return options.getProperty("jvmdi.strict", "default");523}524525/**526* Return <i>true</i> if JVMDI strict mode for launching debugeeVM is used^527* either by specifying in command line or by default.528*529* @see #getJVMDIStrictMode()530* @see #isDefaultJVMDIStrictMode()531* @see #setRawArguments(String[])532*/533public boolean isJVMDIStrictMode() {534String mode = getJVMDIStrictMode();535return mode.equals("yes");536}537538/**539* Return <i>true</i> if JVMDI default strict mode for launching debugee VM is used.540*541* @see #getJVMDIStrictMode()542* @see #isJVMDIStrictMode()543* @see #setRawArguments(String[])544*/545public boolean isDefaultJVMDIStrictMode() {546String mode = getJVMDIStrictMode();547return mode.equals("default");548}549550/**551* Return type of JDI connector used for connecting to debugee VM, specified by552* <code>-connector</code> command line option, or553* "<i>listening</i>" string by default.554*555* Possible values for this option are:556* <ul>557* <li> "<code>attaching</code>"558* <li> "<code>listening</code>"559* </ul>560*561* @see #isAttachingConnector()562* @see #isListeningConnector()563* @see #setRawArguments(String[])564*/565public String getConnectorType() {566return options.getProperty("connector", "listening");567}568569/**570* Return <i>true</i> if type of the used JDI connector is <code>attaching</code>.571*572* @see #getConnectorType()573*/574public boolean isAttachingConnector() {575return getConnectorType().equals("attaching");576}577578/**579* Return <i>true</i> if type of the used JDI connector is <code>listening</code>.580*581* @see #getConnectorType()582*/583public boolean isListeningConnector() {584return getConnectorType().equals("listening");585}586587/**588* Return <i>true</i> if connector type is not actually specified.589* In this case getConnectorType() returns some default connector type.590*591* @see #getConnectorType()592*/593public boolean isDefaultConnector() {594return options.getProperty("connector") == null;595}596597/**598* Return type of JDWP transport for connecting to debugee VM, specified by599* <code>-transport</code> command line option, or600* "<i>socket</i>" string by default.601*602* Possible values for this option are:603* <ul>604* <li> "<code>socket</code>"605* <li> "<code>shmem</code>"606* </ul>607*608* @see #getTransportName()609* @see #isSocketTransport()610* @see #isShmemTransport()611* @see #setRawArguments(String[])612*/613public String getTransportType() {614return options.getProperty("transport", "socket");615}616617/**618* Return transport name corresponding to the used JDWP transport type.619*620* @see #getTransportType()621*/622public String getTransportName() {623if (isSocketTransport()) {624return "dt_socket";625} else if (isShmemTransport()) {626return "dt_shmem";627} else {628throw new TestBug("Undefined transport type");629}630}631632/**633* Return <i>true</i> if the used JDWP transport type is <code>socket</code>,634* either by specifying in command line or as a platform default transport.635*636* @see #getTransportType()637*/638public boolean isSocketTransport() {639String transport = getTransportType();640return transport.equals("socket");641}642643/**644* Return <i>true</i> if the used JDWP transport type is <code>shmem</code>,645* either by specifying in command line or as a platform default transport.646*647* @see #getTransportType()648*/649public boolean isShmemTransport() {650String transport = getTransportType();651return transport.equals("shmem");652}653654/**655* Return <i>true</i> if transport type is not actually specified.656* In this case getTransportType() returns some default transport kind.657*658* @see #getTransportType()659*/660public boolean isDefaultTransport() {661return options.getProperty("transport") == null;662}663664/**665* Create <code>Log</code> for debugee application using command line options.666*/667public Log createDebugeeLog() {668return new Log(System.err, this);669};670671/**672* Create IOPipe for debugee application using command line options.673*/674public IOPipe createDebugeeIOPipe() {675return createDebugeeIOPipe(createDebugeeLog());676};677678/**679* Create IOPipe for debugee application using connection680* parameters from the command line and specify Log.681*/682public IOPipe createDebugeeIOPipe(Log log) {683return new IOPipe(this, log);684};685686/**687* Check if an option is aloowed and has proper value.688* This method is invoked by <code>parseArgumentss()</code>689*690* @param option option name691* @param value string representation of value692* (could be an empty string too)693* null if this option has no value694* @return <i>true</i> if option is allowed and has proper value695* <i>false</i> if otion is not admissible696*697* @throws <i>BadOption</i> if option has an illegal value698*699* @see #parseArguments()700*/701protected boolean checkOption(String option, String value) {702703if(option.equals("traceAll"))704return true;705706// option with any string value707if (option.equals("debugee.vmkeys")) {708return true;709}710711// option with any nonempty string value712if (option.equals("test.host")713|| option.equals("debugee.host")714|| option.equals("debugee.vmkind")715|| option.equals("debugee.vmhome")716|| option.equals("transport.shname")) {717if (value.length() <= 0) {718throw new BadOption(option + ": cannot be an empty string");719}720return true;721}722723// option with positive integer port value724if (option.equals("transport.port")725|| option.equals("bind.port")726|| option.equals("pipe.port")) {727try {728int number = Integer.parseInt(value);729if (number < 0) {730throw new BadOption(option + ": must be a positive integer");731}732} catch (NumberFormatException e) {733throw new BadOption(option + ": must be an integer");734}735return true;736}737738// options with enumerated values739740if (option.equals("debugee.suspend")) {741if ((!value.equals("yes"))742&& (!value.equals("no"))743&& (!value.equals("default"))) {744throw new BadOption(option + ": must be one of: "745+ "yes, no, default");746}747return true;748}749750if (option.equals("debugee.launch")) {751if ((!value.equals("local"))752&& (!value.equals("remote"))753&& (!value.equals("manual"))) {754throw new BadOption(option + ": must be one of: "755+ "local, remote, manual " + value);756}757return true;758}759760if (option.equals("jvmdi.strict")) {761if ((!value.equals("yes"))762&& (!value.equals("no"))763&& (!value.equals("default"))) {764throw new BadOption(option + ": must be one of: "765+ "yes, no, default");766}767return true;768}769770if (option.equals("transport")) {771if ((!value.equals("socket"))772&& (!value.equals("shmem"))) {773throw new BadOption(option + ": must be one of: "774+ "socket, shmem");775}776return true;777}778779if (option.equals("connector")) {780if ((!value.equals("attaching"))781&& (!value.equals("listening"))) {782throw new BadOption(option + ": value must be one of: "783+ "attaching, listening");784}785return true;786}787788if (option.equals("transport.address")) {789if (!value.equals("dynamic")) {790throw new BadOption(option + ": must be only: "791+ "dynamic");792}793return true;794}795796return super.checkOption(option, value);797}798799/**800* Check if the values of all options are consistent.801* This method is invoked by <code>parseArguments()</code>802*803* @throws <i>BadOption</i> if options have inconsistent values804*805* @see #parseArguments()806*/807protected void checkOptions() {808super.checkOptions();809}810811private String findFreePort() {812ServerSocket ss = null;813try {814ss = new ServerSocket(0);815return String.valueOf(ss.getLocalPort());816} catch (IOException e) {817return null;818} finally {819try {820ss.close();821} catch (Throwable t) {822// ignore823}824}825}826827} // DebugeeArgumentHandler828829830