网易首页 > 网易号 > 正文 申请入驻

反向Debug了解一下?揭秘Java DEBUG的基本原理

0
分享至

作者:京东云开发者-京东保险 蒋信

链接:https://my.oschina.net/u/4090830/blog/10388524

Debug 的时候,都遇到过手速太快,直接跳过了自己想调试的方法、代码的时候吧……

一旦跳过,可能就得重新执行一遍,准备数据、重新启动可能几分钟就过去了。


好在 IDE 们都很强大,还给你后悔的机会,可以直接删除某个 Stack Frame,直接返回到之前的状态,确切的说是返回到之前的某个 Stack Frame,从而实现让程序 “逆向运行”。


这个 Reset Frame 的能力,可不只是返回上一步,上 N 步也是可以的;选中你期望的那个帧,直接 Reset Frame/Drop Frame,可以直接回到调用栈上的某个栈帧,时间反转!

可惜这玩意也不是那么万能,毕竟是通过 stack pop 这种操作实现,实际上只是给调用栈栈顶的 N 个 frame pop 出来而已,还谈不上是真正的 “反向 DEBUG”。

相比之下, GDB 的 Reverse Debugging 就比较强大,真正的 “反向” DEBUG,逆向运行,实现回放。

所以吧在运行过程中,已经修改的数据,比如引用传递的方法参数、变量,一旦修改肯定回退不了,不然真的成时光机了。

这些乱七八糟的调试功能,都是基于 Java 内置的 Debug 体系来实现的。

JAVA DEBUG 体系

Java 提供了一个完整的 Debug 体系 JPDA(Java Platform Debugger Architecture),这个 JPDA 架构体系由 3 部分组成:

  1. JVM TI- Java VM Tool Interface

  2. JDWP- Java Debug Wire Protocol

  3. JDI- Java Debug Interface

如果结合 IDE 来看,那么一个完整的 Debug 功能看起来就是这个样子:


解释一下这个体系:

JVM TI 是一个 JVM 提供的一个调试接口,提供了一系列控制 JVM 行为的功能,比如分析、调试、监控、线程分析等等。也就是说,这个接口定义了一系列调试分析功能,而 JVM 实现了这个接口,从而提供调试能力。

不过吧,这个接口毕竟是 C++ 的,调用起来确实不方便,所以 Java 还提供了 JDI 这么个 Java 接口。

JDI 接口使用 JDWP 这个私有的应用层协议,通过 TCP 和目标 VM 的 JVMTI 接口进行交互。

也可以把简单这个 JDWP 协议理解为 JSF/Dubbo 协议;相当于 IDE 里通过 JDI 这个 SDK,使用 JDWP 协议调用远程 JVMTI 的 RPC 接口,来传输调试时的各种断点、查看操作。

可能有人会问,搞什么套壳!要什么 JDWP,我直接 JVMTI 调试不是更香,链路越短性能越高!

当然可以,比如 Arthas 里的部分功能,就直接使用了 JVMTI 接口,要什么 JDI!直接 JVMTI 干就完了。

开个玩笑,Arthas 毕竟不是 Debug 工具,人家根本就不用 JDI 接口。而且 JVMTI 的能力也不只是断点,它的功能非常多:


左边的功能类,提供了各种乱七八糟的功能,比如我们常用的添加一个断点:

jvmtiError
SetBreakpoint(jvmtiEnv* env,
jmethodID method,
jlocation location)

右边的事件类,可以简单的理解为回调;还是拿断点举例,如果我用上面的 SetBreakpoint 添加了一个断点,那么当执行到该位置时,就会触发这个事件:

void JNICALL
Breakpoint(jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread,
jmethodID method,
jlocation location)

JVMTI 的功能非常之多,而 JDI 只是实现了部分 JVMTI 的方法,所以某些专业的 Profiler 工具,可能会直接使用 JVMTI,从而实现更丰富的诊断分析功能。

远程调试与本地调试

不知道大家有没有留意过本地 Debug 启动时的日志:


第一行是隐藏了后半段的启动命令,展开后是这个样子:

/path/to/java -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:53631,suspend=y,server=n -javaagent:/path/to/jetbrains/debugger-agent.jar ...

第二行是一个 Connected 日志,意思是使用 socket 连接到远程 VM 的 53631 端口

上一段说到,IDE 通过 JDI 接口,使用 JDWP 协议和目标 VM 的 JVMTI 交互。这里的 53631 端口,就是目标 JVM 暴露出的 JVM TI 的 server 端口。

而第一行里,IDEA 自动给我们加上了-agentlib:jdwp=transport=dt_socket,address=127.0.0.1:53631这么一段,这个参数的意思就是,让 jvm 以 53631 暴露 jdwp 协议

小知识,这个 agentlib 可不只是为 jvmti 提供的。它还可以让 JVM 加载其他的 native lib 包,直接 “外挂” 到你的 jvm 上,下面是 “外挂” 的参数格式:


所以吧,上面的描述其实不太严谨,更专业的说法是:

让 JVM 加载 JDWP 这个 agent 库,参数为transport=dt_socket,address=127.0.0.1:53631,这个 jdwp agent 库以 53631 端口提供了 jdwp 协议的 server。只不过这个 jdwp 是 jvm 内部的库,不需要额外的 so/dylib/dll 文件。

如有需要,你完全可以弄个 “datupiao” 的 agentlib,“外挂” 到这个 jvm 上,然后在这个 lib 里调用 JVMTI 接口,然后暴露个端口提供服务和远程交互,实现自己的 jdwp!

可能某些老板们注意到了,本地调试还要 127.0.0.1 走 tcp 交互一遍,那远程调试呢?

基于上面的解释,本地调试和远程调试真的没啥区别!或者说,在目前 IDEA/Eclipse 的实现下,不存在本地调试,都是远程!只不过一个是 127.0.0.1,一个是远程的 IP 而已。

在本地调试时,IDEA 会自动给我们的 JVM 增加agent参数,随机指定一个端口,然后通过 JDI 接口连接,代码大概长这样(JDI 的 SDK 在 JDK_HOME/lib/tools.jar ):

Map env = connector.defaultArguments();
env.get("hostname").setValue(hostname);
env.get("port").setValue(port);

VirtualMachine vm = connector.attach(env);

瞅瞅, VirtualMachine 里的就这点方法,能力上比 JVMTI 还是差远了

List classesByName(String className);

List allClasses();

void redefineClasses(Mapbyte[]> classToBytes);

List allThreads();

void suspend();

void resume();

List topLevelThreadGroups();

EventQueue eventQueue();

EventRequestManager eventRequestManager();

VoidValue mirrorOfVoid();

Process process();

再回来看看 IDEA 中独立的远程调试,配置好之后,红框里的信息会提示你 ,远程的 JVM 需增加这一段启动参数,而且支持多个版本 JDK 的格式,CV 大法就能直接用。


-agentlib 和 -javaagent

有些细心的同学可能发现了,IDEA 默认的启动脚本里,同时配置了 -agentlib 和 -javaagent。

-javaagent:/path/to/jetbrains/debugger-agent.jar

这个 debugger-agent 吧,其实也没干啥事,只是对 JDK 内置的一些线程做了些增强,辅助 IDEA 的 debug 功能,支持一些异步的调试。


agentlib、javaagent 这俩兄弟,定位其实很像,都是加载自定义的代码。

不过区别在于,agentlib 是加载 native lib,需要 c/cpp 去写,相当于外挂自己的代码在 jvm 上,可以为所欲为,比如在 agentlib 里调用上面说的 JVMTI 。

而 javaagent 是用 java 写的,可以直接用上层的 Instrumentation API,做一些类的增强转换之类,这也是大多数 APM Agent、Profiler Agent 实现的基本原理。

Arthas 的玩法

Arthas 的核心入口,其实还是 javaagent,支持静态加载和动态加载两种玩法。

静态没啥好说的,启动脚本里增加一个-javaagent:/tmp/test/arthas-agent.jar,然后为所欲为。

动态的叫 attach,使用 Java 提供的VirtualMachine就可以实现运行时添加 -javaagent,效果一样:

VirtualMachine virtualMachine = VirtualMachine.attach(virtualMachineDescriptor);
virtualMachine.loadAgent(agentPath, agentArgs);

这个 Agent 在 JVM 里启动了一个 TCP server,用于收发 Arthas Client 的各种 trace、watch 、Dashboard 等指令,然后通过 Instrumentation 增强 Class 插入代码、或者直接调用某些 Java API,实现各种功能。

注意到了吗?Arthas 可以直接下载一个 jar 包,java -jar 就能连上。

其实吧,它这个直接启动的 jar 包,是一个 boot 包,启动之后把乱七八糟的 jar 都下载下来。接着动态 attach 的方式,连接到本机指定进程号的 JVM,然后再为所欲为。

在 3.5 版本之后,Arthas 还新增了一个 vmtool 命令,这个命令可以直接获取内存中的指定对象实例。

$ vmtool --action getInstances --className java.lang.String --limit 10
@String[][
@String[com/taobao/arthas/core/shell/session/Session],
@String[com.taobao.arthas.core.shell.session.Session],
@String[com/taobao/arthas/core/shell/session/Session],
@String[com/taobao/arthas/core/shell/session/Session],
@String[com/taobao/arthas/core/shell/session/Session.class],
@String[com/taobao/arthas/core/shell/session/Session.class],
@String[com/taobao/arthas/core/shell/session/Session.class],
@String[com/],
@String[java/util/concurrent/ConcurrentHashMap$ValueIterator],
@String[java/util/concurrent/locks/LockSupport],

直接获取内存对象,这玩意只靠 Instrumentation API 可做不到。Arthas 搞了个骚操作,直接 JNI 调用自定义 lib,用过 cpp 直接调用了 JVMTI 的 API,融合了 Instrumentation 和 JVMTI 的能力,这下是真的为所欲为了!

#include
#include
#include
#include
#include "arthas_VmTool.h" // under target/native/javah/

static jvmtiEnv *jvmti;

extern "C"
JNIEXPORT jobjectArray JNICALL
Java_arthas_VmTool_getInstances0(JNIEnv *env, jclass thisClass, jclass klass, jint limit) {
jlong tag = getTag();
limitCounter.init(limit);
jvmtiError error = jvmti->IterateOverInstancesOfClass(klass, JVMTI_HEAP_OBJECT_EITHER,
HeapObjectCallback, &tag);
if (error) {
printf("ERROR: JVMTI IterateOverInstancesOfClass failed!%u\n", error);
return NULL;
}

jint count = 0;
jobject *instances;
error = jvmti->GetObjectsWithTags(1, &tag, &count, &instances, NULL);
if (error) {
printf("ERROR: JVMTI GetObjectsWithTags failed!%u\n", error);
return NULL;
}

jobjectArray array = env->NewObjectArray(count, klass, NULL);
//添加元素到数组
for (int i = 0; i < count; i++) {
env->SetObjectArrayElement(array, i, instances[i]);
}
jvmti->Deallocate(reinterpret_cast(instances));
return array;
}

总结

  1. Debug 基于 JDPA 体系

    1. IDE 直接接入 JDPA 体系中的 JDI 接口完成

    2. JDI 通过 JDWP 协议,调用远程 VM 的 JVMTI 接口

    3. JDWP 是通过 agentlib 加载的,agentlib 算是一个 native 的静态 “外挂” 接口

  2. javaagent 是 JAVA 层面的 “外挂” 接口,用过 Instrumentation API(Java)实现各种功能,主要用于 APM、Profiler 工具

  3. 如果你想,在 javaagent 里调用功能更丰富的 JVMTI 也不是不行。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
某音网红“璇子baby”被认出疑是探花203期中女主?

某音网红“璇子baby”被认出疑是探花203期中女主?

挪威森林
2024-06-16 07:25:21
许家印手里有没有命案?

许家印手里有没有命案?

娱乐帝皇丸
2024-06-17 07:50:21
欧洲杯出场纪录:C罗断层领先,前3全是葡萄牙人,前20无英国人!

欧洲杯出场纪录:C罗断层领先,前3全是葡萄牙人,前20无英国人!

风过乡
2024-06-16 19:56:42
广东省地质局局长黄德发被查

广东省地质局局长黄德发被查

新京报
2024-06-17 08:40:08
美国最新法案,研究切断中国石油进口,众议院下令国防部具体执行

美国最新法案,研究切断中国石油进口,众议院下令国防部具体执行

说天说地说实事
2024-06-15 20:27:33
“我错了,但我不能改”:中国足协,不行咱就查查马宁

“我错了,但我不能改”:中国足协,不行咱就查查马宁

中场阴谋家
2024-06-16 22:52:29
第一次性生活有多痛?进不去怎么办

第一次性生活有多痛?进不去怎么办

喜马拉雅主播暮霭
2024-06-12 09:53:49
欧文:绿军希望自家球员能接受队史的一切 否则就会被淘汰

欧文:绿军希望自家球员能接受队史的一切 否则就会被淘汰

直播吧
2024-06-17 01:44:26
爱神归来!埃里克森心脏骤停正好1100天后,在欧洲杯破门

爱神归来!埃里克森心脏骤停正好1100天后,在欧洲杯破门

直播吧
2024-06-17 00:30:27
女县长被18岁男技师杀死,背后真相龌龊不堪,30名老干部因此落马

女县长被18岁男技师杀死,背后真相龌龊不堪,30名老干部因此落马

摇扇公子聊天下
2024-02-03 14:00:09
姜萍走红后:家乡企业给姜父送钱,细节见格局,姜妈首发声被误解

姜萍走红后:家乡企业给姜父送钱,细节见格局,姜妈首发声被误解

小淇言说
2024-06-16 20:43:44
浙大婉拒后,姜萍深造之路有三条,是清华丘成桐领军人才还是哈佛

浙大婉拒后,姜萍深造之路有三条,是清华丘成桐领军人才还是哈佛

大树成长营
2024-06-16 21:03:06
美国顶级预言家再出手!直言2024美日中命运!这个岛最先出事!

美国顶级预言家再出手!直言2024美日中命运!这个岛最先出事!

飞云如水
2024-06-09 21:53:34
中央督察江西:大量违法违规项目运行 无视国家规定 对河岸保护不力

中央督察江西:大量违法违规项目运行 无视国家规定 对河岸保护不力

小小包工头阿汾
2024-06-17 08:52:35
“姜萍能不能被高校破格录取”?江苏教育主管部门已经给出了答案

“姜萍能不能被高校破格录取”?江苏教育主管部门已经给出了答案

辉哥说动漫
2024-06-17 04:06:44
过“紧日子”的地方政府,已经把手伸进老百姓的口袋里去了

过“紧日子”的地方政府,已经把手伸进老百姓的口袋里去了

浮事记
2024-06-03 11:48:21
粟裕出事后无人敢上门,楚青气不平:华野部下不登门,四野有人来

粟裕出事后无人敢上门,楚青气不平:华野部下不登门,四野有人来

云端书馆
2024-06-17 07:40:57
记者:魔术能够给克莱一份8190万美元的合同,报价比勇士还高

记者:魔术能够给克莱一份8190万美元的合同,报价比勇士还高

懂球帝
2024-06-16 19:10:33
缅北女魔头魏榕怪癖,活剥“肉灵芝”是小事,最恐怖的是“驴耳”

缅北女魔头魏榕怪癖,活剥“肉灵芝”是小事,最恐怖的是“驴耳”

马尔科故事会
2024-06-15 11:10:21
新型卖淫方式,让人预想不到,但却真实存在!

新型卖淫方式,让人预想不到,但却真实存在!

雪影的情感
2023-11-18 11:51:16
2024-06-17 11:04:49
开源中国
开源中国
每天为开发者推送最新技术资讯
6329文章数 34225关注度
往期回顾 全部

科技要闻

OpenAI可能会迎来重大变化

头条要闻

牛弹琴:乌和平峰会结束后 泽连斯基谈及中国改口了

头条要闻

牛弹琴:乌和平峰会结束后 泽连斯基谈及中国改口了

体育要闻

欧洲杯15亿豪阵险翻车:半场梦游 王牌打废

娱乐要闻

上影节红毯:倪妮好松弛,娜扎吸睛

财经要闻

枝江酒业的补税单,张道红的下马威

汽车要闻

传奇新篇章 全新一代大众迈腾来了

态度原创

家居
本地
旅游
房产
游戏

家居要闻

空谷来音 朴素留白的侘寂之美

本地新闻

粽情一夏|海河龙舟赛,竟然成了外国人的大party!

旅游要闻

游客放狗进赛里木湖追天鹅,景区回应!

房产要闻

万华对面!海口今年首宗超百亩宅地,重磅挂出!

《AC:影》弥助头盔收藏品亮相 玩家:他不是武士

无障碍浏览 进入关怀版