JPDA 架構研究19 - JDI的鏈接模塊

引入:
java

上文提到了JDI的Mirror機制,把整個目標虛擬機上的全部數據、類型、域、方法、事件、狀態和資源,以及調試器發向目標虛擬機的事件請求等都映射成Mirror 對象。這裏進一步討論JDI的連接模塊。socket


分析:ide

鏈接模塊其主要目的是提供調試器(Debugger)到目標虛擬機(Target VM)之間的交互通道。this

從鏈接的發起方來看:鏈接的發起方能夠是調試器,也能夠是目標虛擬機。spa

從鏈接的數量來看,一個調試器能夠鏈接多個目標VM, 可是一個目標VM只能夠鏈接一個調試器。debug


咱們從調試器(Debugger)的角度,能夠把鏈接分爲主動鏈接和被動鏈接。調試


分類1:主動鏈接 (它表示調試器主動去鏈接Target VM)server

又分兩種狀況:對象

a. 當Target VM還沒啓動時,則使用LaunchingConnector這種形式的鏈接器,它會啓動目標VM並鏈接。
事件

Step 1: 調試器調用 VirtualMachineManager 的 launchingConnectors()方法獲取全部的LaunchingConnector的實例。

public List<LaunchingConnector> launchingConnectors()
  {
    ArrayList list = new ArrayList(2);
    list.add(new SocketLaunchingConnectorImpl(this));
    list.add(new SocketRawLaunchingConnectorImpl(this));
    return list;
  }


Step 2:根據傳輸方式或其餘特徵選擇一個LaunchingConnector,調用其 launch() 方法啓動而且鏈接目標虛擬機 。啓動後,返回目標虛擬機的實例。

好比說,若是選用SocketLaunchingConnectorImpl,則它的launch()方法以下:

public VirtualMachine launch(Map<String, ? extends Connector.Argument> connectionArgs)
    throws IOException, IllegalConnectorArgumentsException, VMStartException
  {
    getConnectionArguments(connectionArgs);

    SocketListeningConnectorImpl listenConnector = new SocketListeningConnectorImpl(
      virtualMachineManager());
    Map args = listenConnector.defaultArguments();
    ((Connector.IntegerArgument)args.get("timeout")).setValue(10000);
    String address = listenConnector.startListening(args);

    String slash = System.getProperty("file.separator");
    String execString = 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);
    Process proc = Runtime.getRuntime().exec(cmdLine);
    try
    {
      virtualMachine = (VirtualMachineImpl)listenConnector.accept(args);
    }
    catch (InterruptedIOException localInterruptedIOException)
    {
      VirtualMachineImpl virtualMachine;
      proc.destroy();
      String message = 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);
    }
    VirtualMachineImpl virtualMachine;
    virtualMachine.setLaunchedProcess(proc);
    return virtualMachine;
  }


b.當Target VM已經啓動時,則使用AttachingConnector這種形式的鏈接器,它會掛接到目標虛擬機上。

前提是,Target VM必須以如下方式啓動  -agentlib:jdwp=transport=xxx,server=y 參數啓動,並根據傳輸方式生成監聽地址。

Step1:調試器啓動,調用 VirtualMachineManager 的 attachingConnectors()  方法獲取全部的AttachingConnector的實例。

 public List<AttachingConnector> attachingConnectors()
  {
    ArrayList list = new ArrayList(1);
    list.add(new SocketAttachingConnectorImpl(this));
    return list;


Step 2: 根據目標虛擬機採用的傳輸方式選擇一個AttachingConnector,調用其 attach() 方法掛接到目標虛擬機上。完成鏈接後,返回目標虛擬機的實例。

 public VirtualMachine attach(Map<String, ? extends Connector.Argument> connectionArgs)
    throws IOException, IllegalConnectorArgumentsException
  {
    getConnectionArguments(connectionArgs);
    Connection connection = null;
    try {
      connection = ((SocketTransportImpl)this.fTransport).attach(this.fHostname, 
        this.fPort, this.fTimeout, 0L);
    } catch (IllegalArgumentException e) {
      List args = 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實例。

 public List<ListeningConnector> listeningConnectors()
  {
    ArrayList list = new ArrayList(1);
    list.add(new SocketListeningConnectorImpl(this));
    return list;
  }


Step 2:調用ListeningConnector的 startListening() 方法讓鏈接器進入監聽狀態。經過 accept() 方法通知鏈接器開始等待正確的入站連接,該方法將返回調試器正在監聽的地址描述符;目標虛擬機會自動地 attach 到調試器上創建鏈接,而後返回目標虛擬機的實例。

 public String startListening(Map<String, ? extends Connector.Argument> connectionArgs)
    throws IOException, IllegalConnectorArgumentsException
  {
    getConnectionArguments(connectionArgs);
    String result = null;
    try {
      result = ((SocketTransportImpl)this.fTransport).startListening(this.fPort);
    } catch (IllegalArgumentException localIllegalArgumentException) {
      throw new IllegalConnectorArgumentsException(
        ConnectMessages.SocketListeningConnectorImpl_ListeningConnector_Socket_Port, 
        "port");
    }
    return result;
  }
相關文章
相關標籤/搜索