博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Javsssist用InsertAt()方法对语句插桩
阅读量:4576 次
发布时间:2019-06-08

本文共 2389 字,大约阅读时间需要 7 分钟。

基于上一篇的方法插桩,这一篇则是进一步的对每行的语句进行插桩。

对于存在分支的方法(例如if(){}else{}),对方法插桩的方法是不能够全部涉及到的。所以要对程序的每条语句进行插桩。

插入什么语句呢?可以插入包括以下的内容:

1‘  classname

2’  linenumber

此时需要用到javassist  api里的类CtMethod的方法insertAt(),对程序的每条语句进行插桩。

 

逻辑思想就是:读取class文件,对类的方法进行获取,读取每个类的行号范围,在每个行号前用insertAt()方法插入语句,输出行号和类名。

 

示例代码如下:

import java.io.IOException;import javassist.*;public class TriangleInsertAt {	public static void main(String args[]) throws NotFoundException, CannotCompileException, IOException{		//获取class文件		CtClass clas=ClassPool.getDefault().get("Triangle");				if(clas==null){               //方法未找到			System.out.println("classname "+clas+" not found");		}else{			//isTriangle方法的行号范围                        //此处对Triangle四个方法分别进行语句插桩			insertToLine(clas,"isTriangle",31,48);			insertToLine(clas,"getType",55,77);			insertToLine(clas,"diffOfBorders",83,85);			insertToLine(clas,"getBorders",90,96);			clas.writeFile();		}		}	public static void insertToLine (CtClass ccl,String method,int i,int j) throws NotFoundException, CannotCompileException{		//获取方法信息,如果方法不存在,则抛出异常 		CtMethod ctMethod = ccl.getDeclaredMethod(method);  		 //将旧的方法名称进行重新命名        String nname = method + "$impl";          ctMethod.setName(nname);          //方法的副本        CtMethod newCtMethod = CtNewMethod.copy(ctMethod, method, ccl, null);        //用for循环语句插桩      for(;i

▲ 代码中插入输出语句的那行 

newCtMethod.insertAt(i,true,"System.out.println(\" classname "+ccl.getName()+" linenumber  \"+" + i +");");   这里需要注意引号的不同意义,\"   \"是双引号的转义字符,而加号也是有此处字符串相加的,也有System.out.println()里的输出之间的加号。  

这只是一个基于方法插桩的语句插桩,逻辑缜密上并不完善,待要解决的存有:

1‘  获取行号,需要借助于文件复制,在每循环一次readLine方法,就对linenumber执行+1,在此需要做出判断,该行是否为方法体,构造函数,main方法,注释,空行(此处的空行为代码里的空行,并不意味着null而是换行的“\n”),创建一个数组,对特殊行号进行标记,我目前的思想是构造二维数组,记录行号和该行的属性。(此处需用到IO流的文件读取和写入的方法,在判断时会用到contains()和startwith()方法等;

2’  根据上述的代码生成一个新的class文件,借助工具查看插桩后的java代码,会发现输出的行号并不是按顺序,而且有的行没有,有的行有两次输出。此为编译后生成的class文件的显示。

对此,需要进行缜密的插入判断,借助于第一条的建议进行判断性的插入。

3‘  整个程序中需要语句插桩的有main方法,构造函数,方法,此代码对前两者并没有插入,问题出在还借用了方法插桩的方法,对方法进行重命名,插入输出语句,然后将新的方法也写入到源文件中。而语句插桩并不需要重写方法,而是直接经过判断将输出语句写入到每行符合条件的代码前。输出的结果应该并没有整个程序的行数那么多,而且行数需递增型输出。

总结:

java基础重中之重,仅是一个简单的语句插桩就涉及到了文件复制,读取判断,提取字符串,迭代,Javassist API和JDK API都需要运用到很多方法。

将语句插桩进一步完善的思路:首先文件复制时,创建数组记录行数和出现方法和构造函数的特殊行数;其次读取class文件时,用到CtMethod类的insertAt方法进行选择性的插桩,注释,构造方法的那一行,以及类似“\n”," }","{"之类的字符串都要进行排除;之后运行需要进一步完善不足之处。

一个功能只能经过不断地完善才能尽量的减少bug和提升性能,没有十全十美之时。

 

转载于:https://www.cnblogs.com/1996swg/p/7173093.html

你可能感兴趣的文章
机器学习和深度学习综述
查看>>
使用Python和Numpy构建神经网络模型
查看>>
使用Matplotlib简单作图案例
查看>>
Linux下Redis部署
查看>>
tomcat8域名非法解析解决方法
查看>>
Oracle数据库clob字段insert报错
查看>>
监控Linux系统信息【Grafana+Prometheus+node_exporter】
查看>>
使用docker安装Jenkins
查看>>
NextCloud搭建私有云盘【可多设备同步】
查看>>
windowns下安装虚拟化环境 virtualenv
查看>>
Django路由分发【>=2.2.X】
查看>>
seafile安装文档【linux】
查看>>
Eclipse下spingClound和Docker的使用【windowns系统】
查看>>
python3基础系列之六【python推导式】
查看>>
YAML格式介绍
查看>>
JAVA常用工具【一】
查看>>
JAVA快速开发项目汇总
查看>>
Gitblit服务器搭建【基于windown系统】
查看>>
jq 移动端网页分享功能_jquery代码实现多选、不同分享功能
查看>>
python登录面向对象_python基础 面向对象一
查看>>