byteman

ECA

一个byteman脚本包含了一系列的ECA(Event Condition Actions Rules),用来精确的指定应用程序运行时的行为。规则分为三个部分,事件(Event),条件(Condition),动作(Action),分别用来定义:

  • where 应用执行过程中在哪儿产生副作用
  • wheter 是否应该产生副作用
  • what 副作用是什么

   

以下面脚本为例:

RULE set up buffer countDown

CLASS org.my.BoundedBuffer

METHOD <init>(int)

AT EXIT

BIND buffer = $0;

size = $1

IF $1 < 100

DO createCountDown(buffer, size - 1)

ENDRULE

   

Event包含CLASS,METHOD,AT EXIT,BIND等表达式,Condition为IF表达式,Action为DO表达式。

   

METHOD可以是实例方法,静态方法和构造方法,如果没有类似AT等表达式,Event指代整个目标方法。

   

RULE表达式后面的名称至少需要一个非空格字符,名称不要求唯一,但显然,唯一、清晰的名称利于调试。当规则在类型检查,解析,编译和执行过程中有任何错误,都会打印出规则名称。

   

CLASS和METHOD后面的名称必须和表达式在同一行,CLASS的名称可以是简单类名或者包名+简单类名,方法名称可以只写名称,也可以带上参数和返回类型。构造方法的名称统一为<init>。如果有同名类,建议带上包名,如果有同名方法,建议带上参数和返回值。内部类可以使用$符号标识,例如Map$Entry$Wrapper。

   

指定一个接口可以使用INTERFACE表达式,例子如下:

# interface rule example

RULE commit with no arguments on any engine

INTERFACE com.arjuna.wst11.messaging.engines.Engine

METHOD commit()

. . .

ENDRULE

所有继承接口并实现commit方法的实现类,都会匹配上面的规则。

   

Event指定了一个抽象方法或者不被调用的空方法是没有意义的,比如下面的例子:

RULE trace Object.finalize

CLASS java.lang.Object

METHOD finalize

IF TRUE

DO System.out.println("Finalizing " + $0)

ENDRULE

只有子类重写finalize方法,JVM才会调用此方法。

   

当然可以使用^符号表示规则匹配所有重写此方法的子类,比如下面的例子:

RULE trace Object.finalize

CLASS ^java.lang.Object

METHOD finalize

IF TRUE

DO System.out.println("Finalizing " + $0)

ENDRULE

   

但是如果子类方法又调用了父类方法,规则会被多次触发。在构造器中这种情况很常见。可以使用callerEquals来判断,比如下面的例子:

RULE trace Object.finalize at initial call

CLASS ^java.lang.Object

METHOD finalize

IF NOT callerEquals("finalize")

DO System.out.println("Finalizing " + $0)

ENDRULE

callerEquals是内建的方法,可以将参数值和被调用的方法名进行比较。上面的表达式中,finalizer线程的调用runFinalizer()为TRUE,而子类的调用super.finalize()的调用为FALSE。

   

诸如AT等表达式能够指定脚本触发的位置,所有的指令如下:

AT ENTRY //目标方法的第一个指令之前

AT EXIT //目标方法的所有正常返回点

AT LINE number //目标方法行号

AT READ [type .] field [count | ALL ] //读取field之前

AT READ $var-or-idx [count | ALL ] //读取local variable、parameter variable之前

AFTER READ [ type .] field [count | ALL ] //读取field之后

AFTER READ $var-or-idx [count | ALL ] //读取local variable、parameter variable之后

AT WRITE [ type .] field [count | ALL ] //写入field之前

AT WRITE $var-or-idx [count | ALL ] //写入local variable、parameter variable之前

AFTER WRITE [ type .] field [count | ALL ]//写入field之后

AFTER WRITE $var-or-idx [count | ALL ] //写入local variable、parameter variable之后

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 ] //抛出异常之前

   

   

   

规则绑定及参数化

RULE set up buffer countDown

CLASS org.my.BoundedBuffer

METHOD <init>(int)

AT EXIT

BIND buffer = $0;

size = $1

IF $1 < 100

DO createCountDown(buffer, size - 1)

ENDRULE

   

上面的例子中,BIND表达式定义了变量bufer,并将$0赋值给buffer,这里的$0指的是当前对象。而下一行$1指的是当前方法的第一个参数,如果有多个参数,数字依次增长。

   

   

内建的条件和动作

byteman提供了一系列内建的条件和动作来协调各个独立的线程的活动。例如:delays,waits,signals,countdowns,flag operations等等。

   

Tracing操作可以用来观察一个测试用例成功或失败,Trace output可以用来调试规则执行情况,或结合BIND来观察本地变量和参数的状态。

   

少数特殊的内建动作可以用来改变程序的执行路径,变更程序行为。可以在测试程序中强制方法生成虚拟的结果,或者模拟错误。似乎Btrace无法做到这一点,这是byteman更为强大的地方。

   

RETURN动作可以及早返回,THROW动作可以抛出异常。规则支持运行时异常,非运行时异常必须是当前方法声明能够抛出的异常。KILLJVM动作可以模拟崩溃并且立即退出JVM。

   

byteman支持修改静态变量和调用静态方法,需要在类加载器中触发规则。byteman具有特殊的访问权限,能够访问和调用protected、private变量和方法。

   

   

代理变换和重变换

byteman修改字节码的功能基于Java代理程序。JVM类加载器提供了一个代理的机会,在字节码加载后,编译前对字节码进行修改(具体的实现在java.lang.Instrumentation)。

   

byteman支持程序运行时上传规则脚本。如果有同名的规则脚本,则旧版本被替换,旧版本相关规则的触发调用也会被删除。因为目标Class可能已经被加载,所以注册新规则后,需要重加载、重变换目标Class。注意此时新的规则不会影响到旧Class产生的实例。

   

   

ECA规则引擎

byteman规则执行引擎包含一个规则解析器(parser),类型检查器(type checker)和解释/编译器(interpreter/compiler)。规则解析器在代理启动的时候被调用,他为代理提供足够的信息来标识潜在的触发点。

   

注册触发点的时候,规则并没有做类型检查和编译,这一步被推迟到触发类被加载的时候。类型检查需要查看触发类的字段类型,字段的可访问性以及方法签名等等。规则第一次被触发的时候,触发类及其依赖的类被加载,规则才开始进行类型检查和编译。这样保证能够访问到触发类,并且避免在触发类没有被调用的时候进行规则检查、编译造成无谓的开销。

   

单个规则可能关联到多个触发点,这个依赖于规则定义的事件的精确性。甚至一个规则会关联到不同类加载器加载的同一个类文件。所以规则必须在每一个合适的触发点进行类型检查和编译(规则会多次进行类型检查和编译)。

   

如果类型检查和编译失败,规则引擎会禁用相关的触发点。一个规则可能在一个触发点上检查成功,而在另一个触发点上检查失败。规则引擎只会禁用检查失败的触发点。

   

在基本的模式下,执行规则的触发点调用是通过解释器解释规则解析树。也可能配置规则引擎,将规则绑定,条件和动作的字节码传递给JIT编译器。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值