asm中导出方法本地变量

在方法出现异常时需要把传入参数值和本地变量值异导出来供分析。之前因为测试的对象都是普通的值,写的loadLocalVarArray能正常工作。

这几天要在方法进入时inject一个long startTime供在方法退出时计算方法方法执行时间,发现如果本地变量是long和double本地变量栈中实际

占用两个位置,而nextLocal和firstLocal是本地变量栈的地址位置,根本无法知道实际的本地变量个数和具体的索引。

当然用for(int i=0;i< nextLocal-firstLocal;i++){

try{ loadLocal(i+firstLocal) }catch(Exception ex){........}

}利用异常来判断是否有某个localIndex是可以牧举出来的,但是这样太猥琐了。

查看GeneratorAdapter发现有一个私有yojgList<Type> localTypes,在visit的时候,本地变量的type会被放在这个数组中。但是存放的方式几乎和栈中差不多。假如有三个double的亦是,栈中是:

firstLocal + 0

firstLocal + 2

firstLocal + 4

这样nextLocal-firstLocal是6,但是loadLocal(6)肯定出异常。而这个list也是把一个type set到对应的位置。这样它就需要把前面没有的地方填null.设置后是这样的:

0 J

1 null

2 J

3 null

4 J

这个list的长度是5,可以正确地 loadLocal(index).但是它是一个私有变量,我只好给GeneratorAdapter增加一个

public int getLocalLength(){
return this.localTypes.size();
}

说明:因为instrumention功能是独立天任何app的,它引用的库在premain中调用,那么如果应用中使用了同样的库而版本不同,则无法再载

入相同的类,因为premain在main之前已经载入了,为了防止版本冲突,我们是把其它 app常用库重新package进来的,这个工作是非常必要的,所以修改源码仍然可以接受。

有了这个getLocalLength()就好办了:

void loadVarArray() {

Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
int varCnt = getLocalLength();
if (varCnt == 0) { //返回一个空数组
push(0);
newArray(OBJECT_TYPE);
return;
}

/*
* 先检查栈的空位(long和double占两个位,其一个标记为null),如果直接用栈长度指定数组
* 则占位的位置为null,返回出去程序不知道是占位的null还是一个localVar的值为null
*/
int arrLen = 0;
for (int i = 0; i < varCnt; i++) {
Type t = getLocalType(super.firstLocal + i);
if (t != null)
arrLen++;
}
push(arrLen); //实际需要的数组长度
newArray(OBJECT_TYPE);
int arrIndex = 0;
for (int i = 0; i < varCnt; i++) {
int index = super.firstLocal + i;
Type t = getLocalType(index);
if (t == null)
continue;

dup();
push(arrIndex++);
loadLocal(index, t);
box(t);
arrayStore(OBJECT_TYPE);
}
}

这样正确地把所有本地变量加载到数组中(数组现在在操作栈的栈顶,你可以访问它了)。

完整的实现:

package com.alibaba.instrumention.classop; import com.alibaba.citrus.asm.MethodVisitor; import com.alibaba.citrus.asm.Type; import com.alibaba.citrus.asm.commons.AdviceAdapter; import com.alibaba.instrumention.Util; /** * Comment of InstrumtionAdviceAdapter * * @author axman.wang */ public class InstrumentionAdviceAdapter extends AdviceAdapter { private String methodName; private String desc; private String className; private int startLocal; protected InstrumentionAdviceAdapter(MethodVisitor mv, int access, String methodName, String desc, String signature, String[] exceptions, String className) { super(mv, access, methodName, desc); this.methodName = methodName; this.desc = desc; this.className = className; } protected void onMethodEnter() { /* invoke the statbaen */ this.mv.visitTypeInsn(NEW, Util.METHODINFO_CLASSNAME); this.mv.visitInsn(DUP); this.mv.visitLdcInsn(this.methodName); this.mv.visitLdcInsn(this.desc); this.mv.visitLdcInsn(this.className); this.mv.visitMethodInsn(INVOKESPECIAL, Util.METHODINFO_CLASSNAME, Util.CSTOR_NAME, Util.METHODINFO_CSTOR_DESC); this.mv.visitMethodInsn(INVOKESTATIC, Util.STAT_CLASSNAME, Util.STAT_BEFORE_NAME, Util.STAT_BEFORE_DESC); /* make a timer start variable */ this.startLocal = this.newLocal(Type.LONG_TYPE); this.mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J"); this.mv.visitVarInsn(LSTORE, startLocal); } protected void onMethodExit(int opcode) { if (opcode >= IRETURN && opcode <= RETURN) { this.mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J"); this.mv.visitVarInsn(LLOAD, startLocal); this.mv.visitInsn(LSUB); this.mv.visitTypeInsn(NEW, Util.METHODINFO_CLASSNAME); this.mv.visitInsn(DUP); this.mv.visitLdcInsn(this.methodName); this.mv.visitLdcInsn(this.desc); this.mv.visitLdcInsn(this.className); this.mv.visitMethodInsn(INVOKESPECIAL, Util.METHODINFO_CLASSNAME, Util.CSTOR_NAME, Util.METHODINFO_CSTOR_DESC); this.mv.visitMethodInsn(INVOKESTATIC, Util.STAT_CLASSNAME, Util.STAT_AFTER_NAME, Util.STAT_AFTER_DESC); } else if (opcode == ATHROW) { this.mv.visitInsn(DUP); //copy Exception instance of stack top this.mv.visitTypeInsn(NEW, Util.METHODINFO_CLASSNAME); this.mv.visitInsn(DUP); this.mv.visitLdcInsn(this.methodName); this.mv.visitLdcInsn(this.desc); this.mv.visitLdcInsn(this.className); this.mv.visitMethodInsn(INVOKESPECIAL, Util.METHODINFO_CLASSNAME, Util.CSTOR_NAME, Util.METHODINFO_CSTOR_DESC); loadVarArray(true); //加载本地变量数组 loadArgArray(); //加载参数数组 this.mv.visitMethodInsn(INVOKESTATIC, Util.STAT_CLASSNAME, Util.STAT_ERROR_NAME, Util.STAT_ERROR_DESC); } } private void loadVarArray(boolean removeInjection) { Type OBJECT_TYPE = Type.getObjectType("java/lang/Object"); int varCnt = getLocalLength(); if (varCnt == 0) { //返回一个空数组 push(0); newArray(OBJECT_TYPE); return; } /* * 先检查栈的空位(long和double占两个位,其一个标记为null),如果直接用栈长度指定数组 * 则占位的位置为null,返回出去程序不知道是占位的null还是一个localVar的值为null */ int arrLen = 0; for (int i = 0; i < varCnt; i++) { Type t = getLocalType(super.firstLocal + i); if (t != null) arrLen++; } if (removeInjection) { arrLen--; //除去注入的startLocal } push(arrLen); //实际需要的数组长度 newArray(OBJECT_TYPE); int arrIndex = 0; boolean isfirst = true; for (int i = 0; i < varCnt; i++) { int index = super.firstLocal + i; Type t = getLocalType(index); if (t == null) continue; if (removeInjection && isfirst){ isfirst = false; continue; } dup(); push(arrIndex++); loadLocal(index, t); box(t); arrayStore(OBJECT_TYPE); } } }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值