java if debug_java debug初探

JPDA、JDI、JDWP傻傻分不清楚

关于Java debug架构,有一堆相关的名词。其中JPDA是整个debug架构的缩写:Java Platform Debugger Architecture,

整个架构可以从 JPDA文档

最开头了解到:

Components Debugger Interfaces

/ |--------------|

/ | VM |

debuggee ----( |--------------|

\ | back-end |

\ |--------------|

/ |

comm channel -( |

\ |

|--------------|

| front-end |

|--------------|

| UI |

|--------------|

从图中可以看出,

JDI: Java Debug Interface,作为整个debug架构的客户端API,封装了一些Java API用于debug。包括连接JVM,断点等等功能。

JDWP:Java Debug Wire Protocol,它实际是一个传输协议,定义了debug客户端和被debug的JVM之间的通信协议。具体协议参见 文档

JVMTI:前文已经介绍过,这是JVM提供的扩展接口。

JDI如何工作

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 referenceTypes = vm.classesByName("TempTest");

for (ReferenceType referenceType : referenceTypes) {

List instances = referenceType.instances(0L);

for (ObjectReference instance : instances) {

System.out.println(instance);

}

}

}

private static void attach() throws IOException, IllegalConnectorArgumentsException {

// 一、取得连接器

VirtualMachineManager vmm = Bootstrap.virtualMachineManager();

List 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 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 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对外提供服务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值