十分钟学习一个日常定位bug技巧之Arthas工具

你独处的能力,决定了你的人生高度
你定位BUG的能力,决定了你的薪资高度
-收录于《胡说八道全集》

平常编码工作中,我们会常遇到如下场景:本地运行没问题,但测试环境就是运行异常,棘手的是,看日志一切都是正常的。

遇到这种情况,常见的做法是到测试环境把class文件拉回本地反编译,比对测试环境代码是否部署有问题。

如果代码没问题,则添加打印参数的日志,再重新部署,再复现。这种方法可以解决问题,但一系列的操作,还是太费时间。

这里介绍一个工具Arthas,它可以直接在运行中的服务把JVM中的class字节码反编译,修改反编译后的代码,再直接重新发布到JVM中,整个过程,不用重启服务。

下面介绍整个过程,目录如下:

  1. 运行Arthas
  2. 反编译class字节码
  3. 修改代码
  4. 发布到JVM

以Arthas官网的Demo为例:

下载Arthas:https://github.com/alibaba/arthas/releases

运行Demo程序,java -jar math-game.jar。这是一个每隔一秒通过Random获取随机数,然后做质因数分解,并打印出分解结果的小程序

http://blog-image-creasylai.oss-cn-shenzhen.aliyuncs.com/blog.images/img/20210906/article1/01.png

运行Arthas,java -jar arthas-boot.jar

http://blog-image-creasylai.oss-cn-shenzhen.aliyuncs.com/blog.images/img/20210906/article1/02.png

运行后,arthas会打印出当前所有的Java进程,根据中括号中的ID选择要处理的进程。如,输入1,然后按ENTER

http://blog-image-creasylai.oss-cn-shenzhen.aliyuncs.com/blog.images/img/20210906/article1/03.png

这时候,arthas已经attach上前面运行的math-game程序。main_class也显示了该程序的主类

我们以这个demo.MathGame为例,反编译它jad demo.MathGame

http://blog-image-creasylai.oss-cn-shenzhen.aliyuncs.com/blog.images/img/20210906/article1/04.png

如上图,可见反编译后的main函数为一个死循环,每隔一秒运行一次run函数。

我们把反编译后的代码存到本地jad demo.MathGame --source-only --lineNumber false > MathGame.java,这样,在运行arthas的目录下就生成了MathGame.java文件。

http://blog-image-creasylai.oss-cn-shenzhen.aliyuncs.com/blog.images/img/20210906/article1/05.png

我们编辑它,在它run函数中加一个打印语句System.out.println("动态加的代码,打印number值为:" + number)

http://blog-image-creasylai.oss-cn-shenzhen.aliyuncs.com/blog.images/img/20210906/article1/06.png

加了代码后,我们可以自己用javac工具编译,也可以用arthas提供mc(Memory Compiler/内存编译器)工具编译

http://blog-image-creasylai.oss-cn-shenzhen.aliyuncs.com/blog.images/img/20210906/article1/07.png

如上,编译得到MathGame.class字节码文件,下面则是通过arthas的redefine命令发布到JVM中

发布前,我们再次确认,当前MathGame运行的结果是

http://blog-image-creasylai.oss-cn-shenzhen.aliyuncs.com/blog.images/img/20210906/article1/08.png

没有执行我们加入的代码System.out.println("动态加的代码,打印number值为:" + number)

执行redefine /Users/creasylai/program/arthas-bin/demo/MathGame.class

http://blog-image-creasylai.oss-cn-shenzhen.aliyuncs.com/blog.images/img/20210906/article1/09.png

再观察MathGame运行的结果

http://blog-image-creasylai.oss-cn-shenzhen.aliyuncs.com/blog.images/img/20210906/article1/10.png

这时候,我们加入的代码就已经执行了。


其他:

  1. 其实arthas提供了watch命令,可以直接观察方法执行的入参、返回值等,在上方的场景,我们没必要加打印语句了

    执行命令watch demo.MathGame primeFactors "{params,returnObj}" -x 2"

http://blog-image-creasylai.oss-cn-shenzhen.aliyuncs.com/blog.images/img/20210906/article1/11.png

如上图,入参和返回值都打印出来了(返回值为null是因为p rimeFactors方法抛出异常了)

  1. arthas还有很多有用的命令,如trace可以追踪每个函数的执行耗时,dashboard可以查看程序的运行状态等

下方链接为arthas在线教程

基础教程【十分钟】https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=arthas-basics

进阶教程【十分钟】https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=arthas-advanced

参考文档:https://arthas.aliyun.com/zh-cn/