Java Platform Debugger Architecture Service Provider Interfaces

Java 平台调试器架构服务提供者接口

Java调试接口(JDI)中的服务提供者接口使得可以开发和部署ConnectorTransportService实现。 TransportService类代表了Connector使用的基础传输服务,并用于在调试器和目标VM之间建立连接并传输Java调试线协议(JDWP)数据包。

除了JDI中的服务提供者接口外,JDK还包括一个名为Java调试线协议传输接口(jdwpTransport)的传输库接口。这是一个本地(C/C++)接口,允许开发和部署传输库。传输库由调试对象端的JDWP代理加载,并用于在调试器和VM之间建立连接并传输JDWP数据包。

本页面描述了新接口可能被使用的一些场景。它还提供了开发和部署新Connector和传输实现所涉及的任务概述。


示例场景

服务提供者接口和本地传输接口预计将被以下类别的用户使用:

鉴于上述用户类别,以下描述了可能使用新接口的一些场景。


开发Connector

开发Connector涉及创建LaunchingConnectorAttachingConnectorListeningConnector的具体实现。

每个Connector实现必须有一个公共无参构造函数,除了实现所有Connector方法。构造函数将在初始化期间由VirtualMachineManager调用。构造函数可以是空操作,也可以执行初始化任务,如加载传输服务。构造函数不会抛出任何已检查异常,因此在初始化期间出现的任何问题应该作为Error或其他未检查异常抛出。

Connectors不需要使用TransportService。一些Connectors可能使用除传输之外的机制连接到目标VM(在“示例场景”部分中列出了连接到崩溃转储或挂起进程的AttachingConnectors的示例)。对于使用TransportService实现的ConnectorsConnector可以直接引用TransportService实现,也可以在运行时加载实现。希望使用Oracle提供的传输的Connectors应该使用以下代码加载传输服务:

try {
    Class<?> c = Class.forName("com.sun.tools.jdi.SocketTransportService");
    ts = (TransportService)c.newInstance();
} catch (Exception x) {
    throw new Error(x);
}

Java SE实现不需要包含Oracle的套接字或共享内存传输服务实现,因此在上面的示例中,如果传输服务不存在,将抛出一个Error

假设已知Connector的类型,则在开发实现时应注意以下事项:

    VirtualMachine vm = Bootstrap.virtualMachineManager().createVirtualMachine(conn);
\
\
`VirtualMachineManager`还涉及另一种形式的`createVirtualMachine`方法,供`LaunchingConnector`实例使用。
另一种形式允许`LaunchingConnector`指定代表调试对象的`java.lang.Process`。有关更多详细信息,请参阅`com.sun.jdi.VirtualMachineManager`的规范。

部署Connector

部署Connector需要将Connector实现的类打包到一个jar文件中,同时包含一个服务配置文件,列出Connector的完全限定类名。然后将jar文件部署到系统类加载器可见的位置。

必须包含在jar文件中的服务配置文件名为META-INF/services/com.sun.jdi.connect.Connector。该文件简单地列出了jar文件中包含的Connector的完全限定类名。可以在同一个jar文件中包含多个Connector实现,在这种情况下,每个Connector的类名将在单独的行上列出。

假设您希望部署一个名为SimpleLaunchingConnector的启动连接器。为了部署这个连接器,您创建一个类似以下内容的文件META-INF/services/com.sun.jdi.connect.Connector

# 一个非常简单的启动连接器
SimpleLaunchingConnector

然后将这个服务配置文件打包到一个jar文件中,同时包含组成Connector实现的类:

jar cf SimpleLaunchingConnector.jar \
    META-INF/services/com.sun.jdi.connect.Connector \
    SimpleLaunchingConnector.class

然后将jar文件复制到系统类加载器可见的位置。

部署后,当调试器重新启动时,调试器将找到Connector。也就是说,SimpleLaunchingConnector将包含在VirtualMachineManagerallConnectors()方法返回的Connectors列表中。此外,作为一个启动连接器,它还将出现在launchingConnectors()方法返回的启动连接器列表中。


开发TransportService

开发传输服务需要开发两个组件:

开发传输服务需要对传输和底层通信协议有很高的熟悉度。传输服务基本上将JDWP绑定到底层通信协议上。它提供的服务是可靠的,调试器和被调试程序之间的JDWP数据包在不重复或丢失数据的情况下进行交换。由于数据包必须以可靠的方式进行交换,这可能意味着传输服务中需要包含超出底层通信协议提供的附加协议支持。例如,如果需要通过原始且不可靠的串行连接进行调试,则传输服务实现者可能需要在实现中构建错误检测和恢复,以确保JDWP数据包可以在调试器和被调试程序之间可靠传输。

假设了解了传输和底层通信协议的细节,接下来要考虑以下事项:

    /dev/ttya;9600,1

一旦上述问题得到解决,创建TransportService涉及扩展com.sun.jdi.connect.spi.TransportService并提供一个实现。附加和接受方法返回一个com.sun.jdi.connect.spi.Connection的实例,因此需要一个Connection的实现,以便调试器可以与被调试程序交换JDWP数据包。

开发本机传输库需要实现jdwpTransport规范中列出的每个函数。函数原型和定义在${java_home}/include/jdwpTransport.h中定义。

传输库实现者将函数实现编译并链接到动态库(或等效物)中。该库导出一个jdwpTransport_OnLoad函数,JDWP代理在加载传输库时将调用该函数。一些嵌入式环境不支持动态链接,在这种环境中,传输库可能需要静态链接。在这种情况下,没有任何库加载,但jdwpTransport_OnLoad函数仍将被调用以初始化传输库并获取环境指针。


部署传输服务

TransportService的部署方式类似于Connector。要部署TransportService,需要将TransportService实现的类打包到一个jar文件中,并附带一个服务配置文件,列出TransportService的完全限定类名。然后将jar文件部署在系统类加载器可见的位置。

必须包含在jar文件中的服务配置文件的名称为META-INF/services/com.sun.jdi.connect.spi.TransportService。与Connector部署一样,配置文件可能会列出多个传输服务实现的类名,以防jar文件包含多个实现。

对于传输服务com.sun.SerialTransportService,服务配置文件将类似于以下内容:

# 串行线传输
com.foo.SerialTransportService

然后将此服务配置文件打包到一个jar文件中,同时包含组成实现的类。对于此示例,我们将假设实现涉及多个类:

jar cf SerialTransportService.jar \
    META-INF/services/com.sun.jdi.connect.spi.TransportService \
    com/foo/SerialTransportService.class \
    com/foo/SerialConnection.class \
    com/foo/SerialCapabilities.clas \
    com/foo/SerialIO.class \
    com/foo/SerialProtocol.class

与部署Connectors一样,然后将jar文件复制到系统类加载器可见的位置。

TransportService可能具有本机方法或依赖于其他需要本机库的API。在这种情况下,本机库必须位于允许使用System.loadLibrary加载的位置。

本机传输库由JDWP代理加载。它必须位于操作系统的正常运行时库搜索路径上。例如,在Linux系统上,它必须位于由LD_LIBRARY_PATH环境变量指定的搜索路径上。