一件程序员必备武器的诞生

作者 | 刘欣

责编 | 伍杏玲

本文经授权转载自码农翻身(ID:coderising)

夜已深,但是Java第一代国王却无心睡眠,帝国刚刚建立,东边的C/C++王国虎视眈眈,随时准备把新生的王国扼杀在摇篮中。

今日GUI大臣上奏,说帝国子民抱怨运行速度慢,这一点Java国王也没有好办法,解释执行嘛,肯定比不上编译好的程序,不过Java国王已经下令去研发HotSpot了,等到儿子即位就会大有改观。

这怎么能行?程序不能调试,相当于瘸了一条腿啊!这将严重影响新生Java帝国的找Bug事业。

调试的基础

第二天早朝,眼圈发黑的国王把JVM大臣怒斥了一顿,勒令他马上把调试这一块给搞好。

JVM大臣非常委屈:“陛下,当初我们在设计Class文件的字节码的时候,就考虑到了调试的需求,Java文件编译成class文件以后,其中有个叫做LineNumberTable的区域,它描述了Java源代码和字节码行号(字节码偏移量)之间的对应关系,有了它,我们才能加断点调试啊!”

他担心国王不明白是怎么回事,现场画了一张图。

国王没有心思去理解那些iload, iadd,istore是什么含义,但是他理解了源代码和字节码之间的对应关系,确实是在LineNumberTable中记录的。

源码的第13行 是int sum = x + y;对应的字节码行号是0 ~ 3。

源码中第14行是 return sum。对应的字节码行号是 4 ~ 5。

国王点头认可,问道:“那是不是可以做一个调试器了?”

JVM大臣:“臣正有此意,臣打算把Java的调试器叫做jdb。”

IO大臣听到JDB立刻跳了起来:“加(J)多(D)宝(B),你怎么不叫王老吉啊!”

JVM大臣蔑视地看了IO大臣一眼:“C王国有个调试器叫gdb, 我把它叫做jdb, Java Debugger, 别想歪了!”

国王说:“你这个JDB是命令行的吧?”

JVM大臣答道:“陛下明鉴, 臣这里只能弄个命令行的调试器,因为帝国的子民用的IDE都不一样,臣也没法给每个IDE都开发一个图形界面的调试器,这不是臣应该干的活。”

国王点头:“寡人理解,你的重点还是要放在HotSpot上,我们已经被C/C++嘲笑很久了,能不能翻身出这口恶气就靠你了。”

GUI大臣说:“陛下圣明,我们应该充分发挥我们Java帝国善于制定规范和协议的特长,搞一套关于调试的规范出来,这样,任何人/任何IDE都可以根据规范来开发一个调试器。”

国王:“爱卿之言甚合我意,GUI大臣,IO大臣,JVM大臣,你们三个通力合作,把这一套规范给制定出来!”

JVM接口

三位大臣不敢怠慢,一退朝就急忙赶到JVM大臣府上讨论这套规范该怎么制定。

JVM大臣率先发言:“诸位,我这里设置一个底线,那就是调试器和被调试的程序不要处于一个JVM中。”

GUI大臣表示不解:“为什么?”

“很简单,如果它们两个在一个JVM中,那被调试程序的独立性就不能保证了,可能会受到调试器的影响。举个极端的例子,调试器占据了很多Heap空间,导致被调试程序OOM了.....”

IO大臣:“那我们可以设计成C/S模式的,让它们之间通过socket通信怎么样?”

“如果这调试器和被调试程序都在一台机器上,用socket多少有点怪,我们也要支持共享内存的方式来通信。”

GUI大臣说:“如此看来, JVM老兄,你得提供接口啊,让调试器可以访问Java程序在运行时的状态,嗯,我觉得至少得有这些功能:

获取一个线程的状态, 挂起一个线程,让线程恢复执行, 设置一个线程,单步执行

获取线程的当前栈帧,调用栈帧,栈帧对应的方法名

获取变量的值, 设置变量的值

设置断点,清除断点

查看类的信息,方法,字段 等等”

JVM大臣撇了一眼GUI大臣,心说这家伙是个内行啊,看来写过不少GUI的调试器,不过他也难不住我,我负责JVM,拿到这些Java程序运行时的信息还不是小菜一碟?

JVM大臣说:“这没问题,我可以把这些接口给细化了,形成规范,然后请一道圣旨,要求各个JVM的提供商都要实现这些接口。”

“不过,” JVM大臣接着说:“为了通用性和性能,我这里只能提供C语言的接口。嗯,这个接口就叫做JVM Tool Interface,简称JVM TI。”

“那怎么通过socket来使用啊?” GUI大臣急了。

IO大臣说:“封装一下嘛,程序员可以写个程序(Agent),充当通信的桥梁 。”

通信

GUI大臣说:“唉,这就麻烦了,我们还得考虑通信的协议问题!”

IO大臣:“那是,刚才你提的那一大堆调试的需求,都需要能通过网络发给JVM才行,不过不用担心,这方面我擅长,让我来制定一个协议,供调试器和JVM 通信 !这个协议的名称就叫 (JDWP)Java Debug Wire Protocol 吧。”

IO大臣看到JVM大臣的JVM TI,心中痒痒,也急不可耐地提出了创造了属于自己的缩写。

创造通信协议的机会可不多,IO大臣浮现出一幅调试器和JVM通信的场景:

双方先来一个“握手”,表明通信要开始了,然后调试器可以发送命令给JVM,JVM处理以后发送响应,还可以主动向调试器推送事件,嗯,这个协议应该是异步的......

调试器

GUI大臣看到这这张图,立刻意识到一个问题:“如果我们把JVM关于调试的能力使用JDWP这个协议的方式暴露出来,那调试器可以使用任意语言来编写啊!”

IO大臣笑道:“是啊,可不仅仅是你老兄的Swing、AWT,别人用C、C++、Python、C#都可以写一个调试器。”

GUI大臣说到:“不不,陛下看到这个设计肯定会发怒的,我们还是提供一个Java版本的接口吧,让这个接口把JDWP还有什么JVM TI都给封装起来,主要供我们的Java IDE来使用,来集成。”

看到JVM大臣提出了JVM TI ,IO大臣提出了JDWP,自己没有,怎么在陛下那里交差?GUI大臣赶紧说:“嗯,我希望这个接口叫做 JDI( Java Debug Interface),怎么样?”

三位大臣相视一笑,心照不宣, 这下平衡了。

早朝

又是早朝, JVM大臣代表三人向国王献上了设计图,着重强调了自己提出的JVM TI是多么精妙,完美,至于JDWP、JDI、JVM大臣语焉不详,一笔带过, 气得IO大臣, GUI大臣吹胡子瞪眼。

国王看着设计图,频频点头:“嗯,层次划分得不错,程序员可以直接使用JVM 提供的接口,也可以用JDWP, 还可以用JDI..... ”

三位大臣甚感佩服,国王就是厉害。

可是国王的脸色很快多云转阴:“只有设计图,代码呢?Talk is cheap , show me the code !”

就在JVM大臣懵逼之时, GUI大臣从怀中掏出一张写满代码的纸,双手呈给了国王,还回过头来对JVM大臣神秘一笑。

国王拿到了代码,只见上面写着:

创建一个断点:

ClassPrepareEventevent= ....略....

ClassType classType = (ClassType)event.referenceType();

// 获取表示第10行的Location对象

Location location = classType.locationsOfLine(10).get();

// 通过Location对象创建一个断点

BreakpointRequest bpReq = vm.eventRequestManager().createBreakpointRequest(location);

bpReq.enable();

在断点处获取变量的值:

// 到达了一个断点

BreakpointEventevent= .....略......

//获取当前的线程

ThreadReference threadReference =event.thread();

//获取当前的栈帧

StackFrame stackFrame = threadReference.frame();

//从栈帧中得到本地变量 i

LocalVariable localVariable = stackFrame.visibleVariableByName("i");

Valuevalue= stackFrame.getValue(localVariable);

inti = ((IntegerValue)value).value();

System.out.println("The local variable "+"i"+" is "+ i);

国王扫了一眼,龙颜大悦,说到:“爱卿多虑了,还给我加了这么多注释,其实不加注释我也看得懂,你展示的就是通过JDI这个接口创建断点,然后在断点处获取变量的值。我知道这代码的背后其实会用JDWP协议向JVM TI发出请求,因为所有的数据都在那里,对不对?”

JVM大臣赶紧说:“陛下圣明,一下子就点透了我们几个小心思。”

“各位爱卿受累了,赏黄马褂,朕打算把你们三个人创建的东西合起来起一个名,叫Java Platform Debugger Architecture,JPDA, 怎么样?”

三人哪敢反对?如小鸡啄米般纷纷点头称颂,从此, JPDA就成为了Java帝国有关调试的标准,各个IDE逐渐都用来起来。

后记:实际上JDK最早只有 JVM DI (Debugger Interface) 和 JVM PI (Profile Interface),后来才出现JVM TI,并不是文章中所说的一步到位。

作者简介:刘欣,前IBM架构师,近20年从业经验,「码农翻身」公众号作者,畅销书《码农翻身》作者,用故事讲解技术是拿手好戏。拨开技术迷雾,轻松理解技术本质,从「码农翻身」开始。

【END】

热 文推 荐

你点的每个“在看”,我都认真当成了喜欢