JPDA 架构研究19 - JDI的连接模块
引入: 上文提到了JDI的Mirror机制,把整个目标虚拟机上的所有数据、类型、域、方法、事件、状态和资源,以及调试器发向目标虚拟机的事件请求等都映射成Mirror 对象。这里进一步讨论JDI的链接模块。 分析: 连接模块其主要目的是提供调试器(Debugger)到目标虚拟机(Target VM)之间的交互通道。 从连接的发起方来看:连接的发起方可以是调试器,也可以是目标虚拟机。 从连接的数量来看,一个调试器可以连接多个目标VM, 但是一个目标VM只可以连接一个调试器。 我们从调试器(Debugger)的角度,可以把连接分为主动连接和被动连接。 分类1:主动连接 (它表示调试器主动去连接Target VM) 又分两种情况: a. 当Target VM还没启动时,则使用LaunchingConnector这种形式的连接器,它会启动目标VM并连接。 Step 1: 调试器调用 VirtualMachineManager 的 launchingConnectors()方法获取所有的LaunchingConnector的实例。 1 2 3 4 5 6 7 public List<LaunchingConnector>launchingConnectors() { ArrayListlist= new ArrayList( 2 ); list.add( new SocketLaunchingConnectorImpl( this )); list.add( new SocketRawLaunchingConnectorImpl( this )); return list; } Step 2:根据传输方式或其他特征选择一个LaunchingConnector,调用其 launch() 方法启动并且连接目标虚拟机 。启动后,返回目标虚拟机的实例。 比如说,如果选用SocketLaunchingConnectorImpl,则它的launch()方法如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public VirtualMachinelaunch(Map<String,? extends Connector.Argument>connectionArgs) throws IOException,IllegalConnectorArgumentsException,VMStartException { getConnectionArguments(connectionArgs); SocketListeningConnectorImpllistenConnector= new SocketListeningConnectorImpl( virtualMachineManager()); Mapargs=listenConnector.defaultArguments(); ((Connector.IntegerArgument)args.get( "timeout" )).setValue( 10000 ); Stringaddress=listenConnector.startListening(args); Stringslash=System.getProperty( "file.separator" ); StringexecString= this .fHome+slash+ "bin" +slash+ this .fLauncher; execString=execString+ "-Xdebug-Xnoagent-Djava.compiler=NONE" ; execString=execString+ "-Xrunjdwp:transport=dt_socket,address=" +address+ ",server=n,suspend=" +( this .fSuspend? "y" : "n" ); if ( this .fOptions!= null ){ execString=execString+ "" + this .fOptions; } execString=execString+ "" + this .fMain; String[]cmdLine=DebugPlugin.parseArguments(execString); Processproc=Runtime.getRuntime().exec(cmdLine); try { virtualMachine=(VirtualMachineImpl)listenConnector.accept(args); } catch (InterruptedIOExceptionlocalInterruptedIOException) { VirtualMachineImplvirtualMachine; proc.destroy(); Stringmessage=NLS.bind(ConnectMessages.SocketLaunchingConnectorImpl_VM_did_not_connect_within_given_time___0__ms_1, new String[]{ ((Connector.IntegerArgument)args .get( "timeout" )).value()}); throw new VMStartException(message,proc); } VirtualMachineImplvirtualMachine; virtualMachine.setLaunchedProcess(proc); return virtualMachine; } b.当Target VM已经启动时,则使用AttachingConnector这种形式的连接器,它会挂接到目标虚拟机上。 前提是,Target VM必须以以下方式启动 -agentlib:jdwp=transport=xxx,server=y 参数启动,并根据传输方式生成监听地址。 Step1:调试器启动,调用 VirtualMachineManager 的 attachingConnectors() 方法获取所有的AttachingConnector的实例。 1 2 3 4 5 public List<AttachingConnector>attachingConnectors() { ArrayListlist= new ArrayList( 1 ); list.add( new SocketAttachingConnectorImpl( this )); return list; Step 2: 根据目标虚拟机采用的传输方式选择一个AttachingConnector,调用其 attach() 方法挂接到目标虚拟机上。完成连接后,返回目标虚拟机的实例。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public VirtualMachineattach(Map<String,? extends Connector.Argument>connectionArgs) throws IOException,IllegalConnectorArgumentsException { getConnectionArguments(connectionArgs); Connectionconnection= null ; try { connection=((SocketTransportImpl) this .fTransport).attach( this .fHostname, this .fPort, this .fTimeout,0L); } catch (IllegalArgumentExceptione){ Listargs= new ArrayList(); args.add( "hostname" ); args.add( "port" ); throw new IllegalConnectorArgumentsException(e.getMessage(),args); } return establishedConnection(connection); } 分类2:被动连接(它表示Debugger被动地等待或者监听由Target VM发起的连接) 前提是,Target VM必须以以下方式启动 -agentlib:jdwp=transport=xxx,address=yyy 参数启动,并根据传输方式生成监听地址。 Step 1:调试器通过 VirtualMachineManager 的 listeningConnectors() 方法获取所有的ListeningConnector实例。 1 2 3 4 5 6 public List<ListeningConnector>listeningConnectors() { ArrayListlist= new ArrayList( 1 ); list.add( new SocketListeningConnectorImpl( this )); return list; } Step 2:调用ListeningConnector的 startListening() 方法让连接器进入监听状态。通过 accept() 方法通知连接器开始等待正确的入站链接,该方法将返回调试器正在监听的地址描述符;目标虚拟机会自动地 attach 到调试器上建立连接,然后返回目标虚拟机的实例。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public StringstartListening(Map<String,? extends Connector.Argument>connectionArgs) throws IOException,IllegalConnectorArgumentsException { getConnectionArguments(connectionArgs); Stringresult= null ; try { result=((SocketTransportImpl) this .fTransport).startListening( this .fPort); } catch (IllegalArgumentExceptionlocalIllegalArgumentException){ throw new IllegalConnectorArgumentsException( ConnectMessages.SocketListeningConnectorImpl_ListeningConnector_Socket_Port, "port" ); } return result; } 本文转自 charles_wang888 51CTO博客,原文链接:http://blog.51cto.com/supercharles888/1588011,如需转载请自行联系原作者