Javassist进行方法插桩
javassist官网 http://jboss-javassist.github.io/javassist/
javassist API网 http://jboss-javassist.github.io/javassist/html/index.html
javassist参考博客 https://www.ibm.com/developerworks/cn/java/j-dyn0916/
Ⅰ插桩
自动用例生成(使用Randoop)
评价(对用例筛选冗余)>功能覆盖、语句覆盖(一般用后者)
>插桩 (插入语句)
用Javassist实现自动插入语句,即插桩
方法层次的插桩:
在方法前插入进入方法的语句,方法后插入退出方法的语句
Ⅱ用Javassist对方法插桩
1‘ 首先在官网下Javassist的jar包,放置在web项目的lib下,在eclipse里右击项目选择build path的最后一个,导入jar包。
2’ 新建一个相对要插桩类a.java的被插桩程序aJssistTiming.java
弄明白这个对方法的插桩,还是得看程序,我对测试第二个博客里的Triangle.java代码进行方法插桩,在方法前后都插入一个输出语句,TriangleJssistTiming.java代码示例如下:
import java.io.IOException;
import javassist.*; public class TriangleJssistTiming {
public static void main(String argv[]){
try {
String classname="Triangle";
//获取class文件
CtClass clas =ClassPool.getDefault().get(classname); if(clas==null){
System.out.println("classname"+clas+" not found.");
}else{
//调用方法
addTiming(clas,"isTriangle");
addTiming(clas,"getType");
addTiming(clas,"diffOfBorders");
addTiming(clas,"getBorders");
clas.writeFile();
}
} catch (CannotCompileException ex) {
ex.printStackTrace();
} catch (NotFoundException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
} } private static void addTiming(CtClass cct,String method) throws NotFoundException, CannotCompileException{
//获取方法信息,如果方法不存在,则抛出异常
CtMethod ctMethod = cct.getDeclaredMethod(method);
//将旧的方法名称进行重新命名
String nname = method + "$impl";
ctMethod.setName(nname);
//方法的副本,采用过滤器的方式
CtMethod newCtMethod = CtNewMethod.copy(ctMethod, method, cct, null); //为该方法添加时间过滤器来计算时间,并判断获取时间的方法是否有返回值
String type = ctMethod.getReturnType().getName();
StringBuffer body = new StringBuffer();
body.append("{\n long start = System.currentTimeMillis();\n"); body.append("System.out.println(\" "+ nname +"_in_11\");");
//返回值类型不同
if(!"void".equals(type)) {
body.append(type + " result = ");
}
//可以通过$$将传递给拦截器的参数,传递给原来的方法
body.append(nname + "($$);\n");
//输出方法运行时间差
// body.append("System.out.println(\"Call to method " + nname + " took \" + \n (System.currentTimeMillis()-start) + " + "\" ms.\");\n");
body.append("System.out.println(\" "+ nname +"_out_11\");");
if(!"void".equals(type)) {
body.append("return result;\n");
} body.append("}");
//替换拦截器方法的主体内容,并将该方法添加到class之中
newCtMethod.setBody(body.toString());
cct.addMethod(newCtMethod); //输出拦截器的代码块
System.out.println("拦截器方法的主体:");
System.out.println(body.toString());
}
}
▲1' body.append()插入字符串,里面若是输出语句需要注意用转义字符 “ “ 改成/“ /"
2’ 构造一个addtiming(0方法,方法里构造拦截器时使用一个 java.lang.StringBuffer
来累积正文文本(这显示了处理 String
的构造的正确方法。这种变化取决于原来的方法是否有返回值。如果它 有返回值,那么构造的代码就将这个值保存在局部变量中,这样在拦截器方法结束时就可以返回它。如果原来的方法类型为 void
,那么就什么也不需要保存,也不用在拦截器方法中返回任何内容。
3‘ body.append(nname + "($$);\n")
中 nname
是原来方法修改后的名字。在调用中使用的 $$
标识符是 Javassist 表示正在构造的方法的一系列参数的方式。通过在对原来方法的调用中使用这个标识符,在调用拦截器方法时提供的参数就可以传递给原来的方法。
3‘ 运行
在eclipse里运行插桩程序和被插桩程序,然后看当地目录下又生成一个Triangle.class文件,大小比原来的大一点。
在cmd代码目录下运行该文件 java Triangle
▲原Triangle里没有main方法,需自己插入main方法并创建一个对象引用第一个方法isTriangle()
比较插桩前后Triangle.class,在当地目录找到Triangle.class(后生成的,不在bin目录里,较大的文件),然后用,导入该文件,可以查看原java代码,会发现多了四个类似的方法:
import java.io.PrintStream; public class Triangle
{
public static void main(String[] args)
{
Triangle tr = new Triangle(2L, 3L, 4L);
tr.isTriangle(tr);
} protected long lborderA = 0L;
protected long lborderB = 0L;
protected long lborderC = 0L; public Triangle(long lborderA, long lborderB, long lborderC)
{
this.lborderA = lborderA; this.lborderB = lborderB; this.lborderC = lborderC;
} public boolean isTriangle$impl(Triangle triangle)
{
boolean isTriangle = false;
if ((triangle.lborderA > 0L) && (triangle.lborderA <= 9223372036854775807L) &&
(triangle.lborderB > 0L) && (triangle.lborderB <= 9223372036854775807L) &&
(triangle.lborderC > 0L) && (triangle.lborderC <= 9223372036854775807L)) {
if ((diffOfBorders(triangle.lborderA, triangle.lborderB) < triangle.lborderC) &&
(diffOfBorders(triangle.lborderB, triangle.lborderC) < triangle.lborderA) &&
(diffOfBorders(triangle.lborderC, triangle.lborderA) < triangle.lborderB)) {
isTriangle = true;
}
}
return isTriangle;
} public String getType$impl(Triangle triangle)
{
String strType = "Illegal";
if (isTriangle(triangle)) {
if ((triangle.lborderA == triangle.lborderB) &&
(triangle.lborderB == triangle.lborderC)) {
strType = "Regular";
} else if ((triangle.lborderA != triangle.lborderB) &&
(triangle.lborderB != triangle.lborderC) &&
(triangle.lborderA != triangle.lborderC)) {
strType = "Scalene";
} else {
strType = "Isosceles";
}
}
return strType;
} public long diffOfBorders$impl(long a, long b)
{
return a > b ? a - b : b - a;
} public long[] getBorders$impl()
{
long[] borders = new long[3];
borders[0] = this.lborderA;
borders[1] = this.lborderB;
borders[2] = this.lborderC;
return borders;
} public boolean isTriangle(Triangle paramTriangle)
{
long l = System.currentTimeMillis();
System.out.println(" isTriangle$impl_in_11");
boolean bool = isTriangle$impl(paramTriangle);
System.out.println(" isTriangle$impl_out_11");
return bool;
} public String getType(Triangle paramTriangle)
{
long l = System.currentTimeMillis();
System.out.println(" getType$impl_in_11");
String str = getType$impl(paramTriangle);
System.out.println(" getType$impl_out_11");
return str;
} public long diffOfBorders(long paramLong1, long paramLong2)
{
long l1 = System.currentTimeMillis();
System.out.println(" diffOfBorders$impl_in_11");
long l2 = diffOfBorders$impl(paramLong1, paramLong2);
System.out.println(" diffOfBorders$impl_out_11");
return l2;
} public long[] getBorders()
{
long l = System.currentTimeMillis();
System.out.println(" getBorders$impl_in_11");
long[] arrayOfLong = getBorders$impl();
System.out.println(" getBorders$impl_out_11");
return arrayOfLong;
}
}
总结:
Javassist 不仅是一个处理字节码的库,而且更因为它的另一项功能使得它成为试验 classworking 的很好的起点。这一项功能就是:可以用 Javassist 改变 Java 类的字节码,而无需真正了解关于字节码或者 Java 虚拟机(Java virtual machine JVM)结构的任何内容。
用Javassist API的方法,对插桩程序用ctclass、classpool、CtMethod等方法进行方法插桩,然后生成第二个插桩程序的class文件,此时run这个class文件就会显示插入的语句要输出的内容。
Javassist进行方法插桩的更多相关文章
- Javsssist用InsertAt()方法对语句插桩
基于上一篇的方法插桩,这一篇则是进一步的对每行的语句进行插桩. 对于存在分支的方法(例如if(){}else{}),对方法插桩的方法是不能够全部涉及到的.所以要对程序的每条语句进行插桩. 插入什么语句 ...
- zorka源码解读之通过beanshell进行插桩的流程
zorka中插桩流程概述 1.在SpyDefinition中配置插桩属性,将SpyDefinition实例提交给插桩引擎.2.SpyDefinition实例中包含了插桩探针probes,probe插入 ...
- 方案设计:基于IDEA插件开发和字节码插桩技术,实现研发交付质量自动分析
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 如何保证代码质量? 业务提需求,产品定方案,研发做实现,测试验流程.四种角色的相互配 ...
- 开发 IDEA Plugin 引入探针,基于字节码插桩获取执行SQL
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 片面了! 一月三舟,托尔斯泰说:"多么伟大的作家,也不过就是在书写自己的片 ...
- 插桩 inline hook 动态二进制插桩的原理和基本实现过程
插桩测试 https://source.android.google.cn/compatibility/tests/development/instrumentation https://zhuanl ...
- Java Instrumentation插桩技术学习
Instrumentation基础 openrasp中用到了Instrumentation技术,它的最大作用,就是类的动态改变和操作. 使用Instrumentation实际上也可以可以开发一个代理来 ...
- 手淘架构组最新实践 | iOS基于静态库插桩的⼆进制重排启动优化 抖音研发实践:基于二进制文件重排的解决方案 APP启动速度提升超15% 编译期插桩
抖音研发实践:基于二进制文件重排的解决方案 APP启动速度提升超15% 原创 Leo 字节跳动技术团队 2019-08-09 https://mp.weixin.qq.com/s/Drmmx5JtjG ...
- APK修改神器:插桩工具 DexInjector
本文介绍了一个针对Dex进行插桩的工具,讲解了一下直接修改Dalvik字节码和Dex文件时遇到的问题和解决方法 作者:字节跳动终端技术-- 李言 背景 线下场景中,我们经常需要在APK中插入一些检测代 ...
- Flymeos插桩适配教程
插桩适配前提,安装Ubuntu或者其他linux系统. 安装JDK7 sudo apt--jdk Ubuntu 16.04与基于它的版本,需要添加源 sudo add-apt-repository p ...
随机推荐
- R与并行计算(转)
文章摘要 本文首先介绍了并行计算的基本概念,然后简要阐述了R和并行计算的关系.之后作者从R用户的使用角度讨论了隐式和显示两种并行计算模式,并给出了相应的案例.隐式并行计算模式不仅提供了简单清晰的使用方 ...
- 《HelloGitHub》第 14 期
公告 欢迎通过在 GitHub 上新建 issues 方式推荐项目,我真心希望读者可以在 HelloGItHub,找到真正的编程乐趣! <HelloGitHub>第 14 期 兴趣是最好的 ...
- javascript获取json对象的key名称的两种方法
javascript获取json对象的key名称的两种方法 数据处理中,你可能接收到一个不确定内容格式的json对象,然后要把key的值提取出来.今天试过两种可以提取json key的方法,均可以正常 ...
- 【JAVAWEB学习笔记】网上商城实战2:异步加载分类、Redis缓存分类和显示商品
网上商城实战2 今日任务 完成分类模块的功能 完成商品模块的功能 1.1 分类模块的功能: 1.1.1 查询分类的功能: 1.1.2 查询分类的代码实现: 1.1.2.1 创建 ...
- WebSocket和kafka实现数据实时推送到前端
一. 需求背景 最近新接触一个需求,需要将kafka中的数据实时推送到前端展示.最开始想到的是前端轮询接口数据,但是无法保证轮询的频率和消费的频率完全一致,或造成数据缺失等问题.最终确定用利 ...
- Android WebView 不支持 H5 input type="file" 解决方法
最近因为赶项目进度,因此将本来要用原生控件实现的界面,自己做了H5并嵌入webview中.发现点击H5中 标签 不能打开android资源管理器. 通过网络搜索发现是因为 android webvie ...
- 基于TF-IDF的新闻标签提取
基于TF-IDF的新闻标签提取 1. 新闻标签 新闻标签是一条新闻的关键字,可以由编辑上传,或者通过机器提取.新闻标签的提取主要用于推荐系统中,所以,提取的准确性影响推荐系统的有效性.同时,对于将标签 ...
- 浅析DES与AES、RSA三种典型加密算法的比较
DES与AES的比较 自DES 算法公诸于世以来,学术界围绕它的安全性等方面进行了研究并展开了激烈的争论.在技术上,对DES的批评主要集中在以下几个方面: 1.作为分组密码,DES 的加密单位仅有64 ...
- 限制容器的 Block IO - 每天5分钟玩转 Docker 容器技术(29)
前面学习了如何限制容器对内存和CPU的使用,本节我们来看 Block IO. Block IO 是另一种可以限制容器使用的资源.Block IO 指的是磁盘的读写,docker 可通过设置权重.限制 ...
- Struts+jdbc+分页 实例
根据项目里分页实例,带有注解. package org.tarena.netctoss.dao.impl; import java.sql.Connection; import java.sql.Pr ...