`

Byteman 3.0.5 发布,Java 字节码注入工具

 
阅读更多
Byteman 3.0.5 发布,Java 字节码注入工具 https://m.oschina.net/blog/375413
场景描述

生产服务器上出现了一个 bug,通过现象无法定位这个 bug 产生的原因,这个时候要求修复这个 bug,你的一般做法是?

使用 println 打印的方法(或者记录日志)记录详细内容?
使用远程 debug?
使用字节码操作工具 btrace?
很多时候在 online 的应用出现问题时,我们需要知道更多的程序的运行细节,但又不可能在开发的时候就把程序中所有的运行细节都打印到日志上, 通常这个时候能采取的就是修改代码,重新部署,然后再观察,但这种方法对于 online 应用来说不是很好,另外一方面如果碰到不好改的代码,例如引用的其他的外部的包什么的,就很麻烦了。使用远程 debug 会使得线程挂起,对于 online 环境是不可接受的。虽然使用 btrace 字节码工具可以避免上面两种方式的缺点,但是 btrace 的限制太多了,例如

不能创建对象
不能抛出或者捕获异常
不能用synchronized关键字
不能对目标程序中的instace或者static变量
不能调用目标程序的instance或者static方法
脚本的field、method都必须是static的
脚本不能包括outer,inner,nested Class
脚本中不能有循环,不能继承任何类,任何接口与assert语句
我使用了一段时间 btrace,一直没有找到得到局部变量的方法(若是有网友知道,可以教我一下),这是我最不能接受的,因为这个场景使用很普遍。若是你也有这个需求和困扰,那么 byteman 是你理想的选择。

Byteman 简介

byteman是jboss下的一个项目,是一个非常方便的java分析工具,能够拦截字节码执行,执行代码和修改变量,是一个诊断问题的利器。在linux下使用起来非常方便,不用对目标应用做任何修改,可以动态打开目标应用的监听端口,当然仅限于openjdk,hotspot 和 jrockit,ibm jdk 不支持。
详细看原文




使用 Byteman 对运行中的系统进行定位 http://wangzaixiang.blogspot.com/2014/05/byteman.html
如果一个生产环境的Java应用,出现了问题,我们可以采用哪些工具,来协助我们进行定位呢?
jstack 可以帮助我们了解这个进程的线程状态,一些诸如死锁的问题,可以通过查看线程状态获得反馈。jmap 可以帮助我们进行定位。(很容易出现的一种情况是类的静态初始化导致的死锁,在这里我碰到过一个很奇特的案列,正常的情况下没有发生死锁,但进行了Emma处理后,却产生了死锁的现象,以后有计划的话,可以重现一下这个现象,并作一个深入的分析)
jmap/mat 可以帮助我们抓取内存,并且进行复盘,这个对于内存泄漏的定位来说,是一个非常有效的工具。需要注意的是,MAT会消耗大量的内存,因此,需要合理的配置jvm的堆设置,以及准备好运行MAT的服务器环境:64位是必须的,有足够大的内存也是必须的。
jcmd 可以帮助我们了解 Java进程的启动标志、系统属性等诸多信息。
不过,如果需要对这个进程进行一定的代码级调试工作,上述工具就力不能及了。而打开一个远程调试工具,连接到这个进程,除非在启动时设置了相关的选项,否则,也是无能为力的。 Linux 的 strace 可以对一个正在运行的进程进行System Call的跟踪操作(这个在系统级定位时也是一个利刃),Java则可以选择 btrace 来完成类似的工作。不过,这里,我会推荐一个更强大的工具: Byteman(http://byteman.jboss.org/)。

我个人对btrace的实战经验并不多,总感觉使用起来有一些不顺手的地方,不过,接触到 byteman 后,却有一种亲切感,当然,这可能跟我对 Java bytecode 较为熟悉有关,所以Byteman的各种切入点对我来说,是一个比较自然的技术。ByteMan 有自己的描述代码切入的规则语言(这一点与drools类似,最早的drools是没有自己的语法的,使用起来反而复杂了),因此,通过一定的学习后,ByteMan的规则描述会更加简单、可读。

ByteMan 的 代码插入能力相比btrace而言更强,似乎你可以在代码中任意的位置插入我们的跟踪代码(当然,你可能需要一定的对Java代码生成、字节码有一定的了解),以及访问当前方法中变量的能力(包括方法参数、局部变量、甚至于调用其它函数的参数值、返回值等),而btrace在这方面的能力要弱很多。如果你熟悉了Java的字节码知识的的话,理解起Byteman的这一套切入点是比较简单的,甚至相比起btrace来说,要更简单更直接得多。

不过,建议如果希望能够让ByteMan对我们的生产故障定位能力提供帮助的话,我们需要提前对Byteman尽可能的熟悉,熟读其文档是必须的,也应该先在开发环境多做一些测试工作,建立不错的熟练度。如果等到真实的生产问题需要定位,再来边学边用的话,估计就是来不及的了。(Byteman本身的专业性知识,以及生产环境的紧迫性要求,会让你手忙脚乱的)。

我将我个人学习整理的ByteMan的一些知识,构成一个简单的CheetSheet,一来作为个人学习的一个提炼,二来也可以作为后续的一个快速参考。
RULE <rule-name>
CLASS <class-name> | INTERFACE <class-name-pattern>
METHOD <method-name>
HELPER <qualified-class-name>
<localtions>
BIND <bindings>
IF <condition>
DO <actions>
ENDRULE

<class-name-pattern>: HelloWorld | com.foo.HelloWorld | ^java.lang.Object
<locations>
AT ENTRY
AT EXIT
AT LINE number
AT READ [ type . ] field [ count | ALL ]
AT READ $var-or-idx [count | ALL ]
AFTER READ [ type .] field [count | ALL ]
AFTER READ $ var-or-idx [count | ALL ]
AT WRITE [ type .] field [count | ALL ]
AT WRITE $ var-or-idx [count | ALL ]
AFTER WRITE [ type .] field [count | ALL ]
AFTER WRITE $ var-or-idx [count | ALL ]
AT INVOKE [ type .] method [ ( argtypes ) ] [count | ALL ]
AFTER INVOKE [ type .] method [ ( argtypes ) ][count | ALL ]
AT SYNCHRONIZE [count | ALL ]
AFTER SYNCHRONIZE [count | ALL ]
AT THROW [count | ALL ]

BIND engine:CoordinatorEngine = $0;
     recovered:boolean = engine.isRecovered();
     identifier:String = engine.getId()

BIND Expression: $! (return value), $^(exception), $#(argc), $*(args for this method), $@(args for AT INVOKE), $CLASS(class name) and $METHOD(method signature)
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics