内容简介:关于Java debug架构,有一堆相关的名词。其中JPDA是整个debug架构的缩写:Java Platform Debugger Architecture,整个架构可以从从图中可以看出,
JPDA、JDI、JDWP傻傻分不清楚
关于Java debug架构,有一堆相关的名词。其中JPDA是整个debug架构的缩写:Java Platform Debugger Architecture,
整个架构可以从 JPDA文档 最开头了解到:
Components Debugger Interfaces
/ |--------------|
/ | VM |
debuggee ----( |--------------| <------- JVMTI - Java VM Tool Interface
\ | back-end |
\ |--------------|
/ |
comm channel -( | <--------------- JDWP - Java Debug Wire Protocol
\ |
|--------------|
| front-end |
|--------------| <------- JDI - Java Debug Interface
| UI |
|--------------|
从图中可以看出,
- JDI: Java Debug Interface,作为整个debug架构的客户端API,封装了一些Java API用于debug。包括连接JVM,断点等等功能。
- JDWP:Java Debug Wire Protocol,它实际是一个传输协议,定义了debug客户端和被debug的JVM之间的通信协议。具体协议参见 文档 。
- JVMTI:前文已经介绍过,这是JVM提供的扩展接口。
JDI如何工作
JDI定义了一堆接口,用于实现整个Debug功能。JDI接口文档在: https://docs.oracle.com/javase/8/docs/jdk/api/jpda/jdi/index.html 。
JDI中最核心的的类是 VirtualMachine
,提供连接目标机器debug端口,以及一堆支持的指令。注意tools.jar中包含2个同名对象,JDI相关的在jdi包中。
JDI使用
public class TempAgent {
static VirtualMachine vm;
public static void main(String[] args) throws IOException, IllegalConnectorArgumentsException {
attach();
//获取对象
obtainTempTestObj();
}
private static void obtainTempTestObj() {
List<ReferenceType> referenceTypes = vm.classesByName("TempTest");
for (ReferenceType referenceType : referenceTypes) {
List<ObjectReference> instances = referenceType.instances(0L);
for (ObjectReference instance : instances) {
System.out.println(instance);
}
}
}
private static void attach() throws IOException, IllegalConnectorArgumentsException {
// 一、取得连接器
VirtualMachineManager vmm = Bootstrap.virtualMachineManager();
List<AttachingConnector> connectors = vmm.attachingConnectors();
SocketAttachingConnector sac = null;
for (AttachingConnector ac : connectors) {
if (ac instanceof SocketAttachingConnector) {
sac = (SocketAttachingConnector) ac;
break;
}
}
if (sac == null) {
System.out.println("JDI error");
return;
}
// 二、连接到远程虚拟器
Map<String, Argument> arguments = sac.defaultArguments();
Connector.Argument hostArg = (Connector.Argument) arguments.get("hostname");
Connector.Argument portArg = (Connector.Argument) arguments.get("port");
// hostArg.setValue("127.0.0.1");
portArg.setValue(String.valueOf(8800));
vm = sac.attach(arguments);
vm.process();
}
}
这里通过JDI接口,实现了通过debug端口连接到目标JVM,并获取一个类的对象引用。这里我们来看下实际获取对象引用的方式。
JDI实现
JDI实现代码在 jdk/src/share/classes/com/sun/tools/jdi/VirtualMachineImpl.java
类中,
对于本文关注功能(获得对象引用),代码在 jdk/src/share/classes/com/sun/tools/jdi/ReferenceTypeImpl.java
。
其中获取引用的代:
public List<ObjectReference> instances(long maxInstances) {
if (!vm.canGetInstanceInfo()) {
throw new UnsupportedOperationException(
"target does not support getting instances");
}
if (maxInstances < 0) {
throw new IllegalArgumentException("maxInstances is less than zero: "
+ maxInstances);
}
int intMax = (maxInstances > Integer.MAX_VALUE)?
Integer.MAX_VALUE: (int)maxInstances;
// JDWP can't currently handle more than this (in mustang)
try {
return Arrays.asList(
(ObjectReference[])JDWP.ReferenceType.Instances.
process(vm, this, intMax).instances);
} catch (JDWPException exc) {
throw exc.toJDIException();
}
}
这里可以看见,所有实际操作,都在JDWP这个类中。但是如果直接搜索这个类名,可以发现JDK源码中不包含这个类的源码。
实际这个类是构建时生成的,生成规则在 jdk/make/gensrc/GensrcJDWP.gmk
中:
$(JDK_OUTPUTDIR)/gensrc/com/sun/tools/jdi/JDWP.java: $(JDWP_SPEC_FILE)
$(MKDIR) -p $(@D)
$(MKDIR) -p $(JDK_OUTPUTDIR)/gensrc_jdwp_headers
$(RM) $@ $(JDK_OUTPUTDIR)/gensrc_jdwp_headers/JDWPCommands.h
$(ECHO) $(LOG_INFO) Creating JDWP.java and JDWPCommands.h from jdwp.spec
$(TOOL_JDWPGEN) $< -jdi $@ -include $(JDK_OUTPUTDIR)/gensrc_jdwp_headers/JDWPCommands.h
其中 TOOL_JDWPGEN
变量在 Tools.gmk
文件中定义:
TOOL_JDWPGEN = $(JAVA) -cp $(JDK_OUTPUTDIR)/btclasses build.tools.jdwpgen.Main
实际会通过JDWP协议和目标JVM交互。
目标JVM实现
每个JDI接口,基本上都有一个对应的实现。所有JDWP后端实现都在 jdk/src/share/back
目录中。
对于获取引用的功能,实现在 ReferenceTypeImpl.c
文件中:
static jboolean
instances(PacketInputStream *in, PacketOutputStream *out)
{
jint maxInstances;
jclass clazz;
JNIEnv *env;
if (gdata->vmDead) {
outStream_setError(out, JDWP_ERROR(VM_DEAD));
return JNI_TRUE;
}
env = getEnv();
clazz = inStream_readClassRef(env, in);
maxInstances = inStream_readInt(in);
if (inStream_error(in)) {
return JNI_TRUE;
}
WITH_LOCAL_REFS(env, 1) {
jvmtiError error;
ObjectBatch batch;
error = classInstances(clazz, &batch, maxInstances);
if (error != JVMTI_ERROR_NONE) {
outStream_setError(out, map2jdwpError(error));
} else {
int kk;
jbyte typeKey;
(void)outStream_writeInt(out, batch.count);
if (batch.count > 0) {
/*
* They are all instances of this class and will all have
* the same typeKey, so just compute it once.
*/
typeKey = specificTypeKey(env, batch.objects[0]);
for (kk = 0; kk < batch.count; kk++) {
jobject inst;
inst = batch.objects[kk];
(void)outStream_writeByte(out, typeKey);
(void)outStream_writeObjectRef(env, out, inst);
}
}
jvmtiDeallocate(batch.objects);
}
} END_WITH_LOCAL_REFS(env);
return JNI_TRUE;
}
这里最核心的函数调用是 classInstances
,这个函数在 util.c
文件中。
/* Get instances for one class */
jvmtiError
classInstances(jclass klass, ObjectBatch *instances, int maxInstances)
{
ClassInstancesData data;
jvmtiHeapCallbacks heap_callbacks;
jvmtiError error;
jvmtiEnv *jvmti;
/* Check interface assumptions */
if (klass == NULL) {
return AGENT_ERROR_INVALID_OBJECT;
}
if ( maxInstances < 0 || instances == NULL) {
return AGENT_ERROR_ILLEGAL_ARGUMENT;
}
/* Initialize return information */
instances->count = 0;
instances->objects = NULL;
/* Get jvmti environment to use */
jvmti = getSpecialJvmti();
if ( jvmti == NULL ) {
return AGENT_ERROR_INTERNAL;
}
/* Setup data to passed around the callbacks */
data.instCount = 0;
data.maxInstances = maxInstances;
data.objTag = (jlong)1;
data.error = JVMTI_ERROR_NONE;
/* Clear out callbacks structure */
(void)memset(&heap_callbacks,0,sizeof(heap_callbacks));
/* Set the callbacks we want */
heap_callbacks.heap_reference_callback = &cbObjectTagInstance;
/* Follow references, no initiating object, just this class, all objects */
error = JVMTI_FUNC_PTR(jvmti,FollowReferences)
(jvmti, 0, klass, NULL, &heap_callbacks, &data);
if ( error == JVMTI_ERROR_NONE ) {
error = data.error;
}
/* Get all the instances now that they are tagged */
if ( error == JVMTI_ERROR_NONE ) {
error = JVMTI_FUNC_PTR(jvmti,GetObjectsWithTags)
(jvmti, 1, &(data.objTag), &(instances->count),
&(instances->objects), NULL);
/* Verify we got the count we expected */
if ( data.instCount != instances->count ) {
error = AGENT_ERROR_INTERNAL;
}
}
/* Dispose of any special jvmti environment */
(void)JVMTI_FUNC_PTR(jvmti,DisposeEnvironment)(jvmti);
return error;
}
除了开头一些准备工作,这里实际的调用使用了jvmti的 FollowReferences
和 GetObjectsWithTags
,
两个函数。第一个函数用于在堆中标记期望的对象,第二个函数从堆中将所有做了标记的对象取出来。
因此,实际上JDWP后端,最终通过jvmti实现了一个agent,通过jvmti的API对外提供服务。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Writing Windows VxDs and Device Drivers, Second Edition
Karen Hazzah / CMP / 1996-01-12 / USD 54.95
Software developer and author Karen Hazzah expands her original treatise on device drivers in the second edition of "Writing Windows VxDs and Device Drivers." The book and companion disk include the a......一起来看看 《Writing Windows VxDs and Device Drivers, Second Edition》 这本书的介绍吧!