方法拦截中获取相关数据的实现
如果你看不懂我的上一篇文章,请你退回去一步一步根据例子运行起来,在你运行的过程中你会加深对
程序的理解。
本篇文章主要是利用ASM来在拦截方法时获取方法调用时的相关数据,如参数列表,本地变量列表,方法
调用栈以及操作状态码等重要数据,以及方法执行时间。结合当前系统的内存状态,CPU占用率等系统信息
可以为业务逻辑出现异常时提供最可靠的分析依据。本篇要求对ByteCode有一定了解,即使你因为某些原
因不得不照抄这里的实现,我也希望你能在先使用这些实现范例后再弄懂它。能看懂这些内容并能正确使用
这些实现的,你本来就有能力弄懂它。
一个比较绕人的地方,我们来比较一下用ASM动态生成class和利用代理代理来注入代码的不同:
在利用代理来注入代码时,其实我们是利用反射来invoke一个Method对象。所以要注入的代码直接插入
在invoke的前后:
这样的实现非常好理解,说白了就是如果你要执行方法m,那么请你交给我来帮助你执行,这样我就可以向别
人炫耀一下:我要执行方法m了(before),我已经执行完方法m了(after)。
而在ASM动态生成class时,MyAdviceAdaptor的onMethodEnter和onMethodEixt方法是在包装某个方法
调用的,而不是运行某个方法时调用的。如果我们在onMethodEnter方法中调用
System.out.println("Hello,world");
那么是在visitMethod时调用了和要生成的新方法无关的一个System.out.println("Hello");的调用。
生成后的这个方法没有被注入任何指令,所以在执行的时候不会调用System.out.println("Hello");
正确的做法是在onMethodEnter中调用ASM操作来向要包装的方法插入指令:
这样的才能将System.out.println("Hello,world");插入到新生成的方法的最前端。
比如原来的方法是
那么,利用ASM生成这个方法应该是
而现在因为MyAdviceAdaptor的onMethodEnterr的方法中有插入指令的代码,所以这些代码会在MethoeVisitor
开始原始方法的生成之前最先插入,即生成新方法的指令变成:
这样原来的方法就被重新生成为:
同样,必须在onMethodEixt中使用ASM插入指令的方法才能将代码注入到生成后的方法中。
好了,弄明白上面的理论。我们来实现相关数据的获取。
首先,在一个方法调用时,利用ASM我们能够获取到方法的参数列表和本地变量列表。以及方法最后操作状态码
利用注入在原方法体前后的代码的时间差可以计算出原方法代码的执行时间。
因为onMethodEnter方法中插入的代码会在原始方法的所有操作包括实参传递之前插入,所以获取参数表,
本地变量表,操作状态码都在onMethodEixt方法获取:
获取本地变量列表:
获取方法参数列表:
好了,假如我们现在要传出这样几个信息:
被拦截的方法所在的类,方法名称,本地变量列表(数组),参数列表(数组),操作状态码。那么:
OK,现在栈顶向下的五个操作数分别是args,vars,opcode,mthodName,className,我们当然可以同样用ASM指令
生成代码将栈中的五个操作数传递到指定的目的地,但这样的操作太复杂,我们只要调用一个有五个参数的方法,然后在
这个方法内用普通JAVA代码自由地实现向目的地传送,而不是在被拦截的方法中用ASM代码来实现向目的地传送。
所以我们只要在外部定义好一个方法形如:
就可以将被拦截的方法的实参列表,本地变量列表,操作状态码等信息传递到外部来。
获取调用栈
利用sun.reflect.Reflection.getCallerClass(x);我们只能获取调用者所在的类,如果是同一类中不同方法的
之间的调用测无法明确地查看方法调用链。
使用Throwable的getStackTrace()则可以获取完整的方法调用栈。我们只要构造一个Throwable对象就可以通过
它来获取调用这个方法的所有调用者:
这里仅仅是调用StackTraceElement的toString()方法,你可以根据需要获取它的详细信息。
由于set[0]是t对象所在的方法本身,我们不需要这个调用者信息。所以循环直接从1开始。
如果Test.main()方法中调用了这个方法,那么第一个调用者直接是Test.main();
本来我们需要在onMethodEixt方法中利用ASM指令来让被拦截的方法调用这个方法,并将方法返回值压栈作为一个参数
传给send方法。但是因为被拦截的方法一定会调用send方法,所以我们直接在send中调用getInvokeStack(),那么被拦截的
方法就是第二调用者。所以我们只要将循环从2开始,然后在send方法中调用getInvokeStack();就可以获取到被拦截的方法
的所有调用链。
实际上,我们在OnMethidEnter方法中同样会将className,methodName利用类似的方法传递出来。同时在输入要拦截的方法时还会传入
一个随机串给MyAdviceAdaptor的构造方法,然后在OnMethidEnter和OnMethidEixt方法中同时会调用ASM指令把这个随机串都传给形如
Sender.send()的方法,这样可以对方法进入和退出的send数据进行配对。并可以用两个配对的send的时间差计算方法执行时间而不是把
计算原方法代码执行时间的代码注入到原方法。
其它的细节问题。以后再作交待。(整个测试项目的压缩文档需要联系获取,以前在blog上提供MMS项目的压缩档,有很多兄弟竟然直接拿项目中我的手机号进行开发测试,弄得我收到大量的测试彩信)
分享到:
相关推荐
NULL 博文链接:https://xkorey.iteye.com/blog/1670257
ASM9260T的中文数据手册,使用ASM9260T芯片的首选
在 buf 缓冲区中存放有 50 个字节数据(无符号数),编写程序将这些数据由小到大排序,排序后的数据仍放在该区域中。具体功能如下: (1)原始数据在源程序中由定义给出; (2)在屏幕上先显示排序前的数据(十六进制),...
NULL 博文链接:https://xkorey.iteye.com/blog/1667944
IMU芯片ASM330LHH的数据手册
埋点计时Gradle插件,利用ASM插入字节码,对指定包名内的类或指定注解的方法,打印其方法的耗时时间。
Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至...
15 - ASM之方法Frame - 简书1
Java class 被存储在严格格式定义的 .class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至...
Oracle_GoldenGate ASM抽取方法最佳实践,介绍了Goldengate用四种方法连接Oracle ASM实例
oracle不使用oracleasm的包配置ASM磁盘配置方法
实现基于ASM9,动态生成entity、repository、service、serviceImpl、controller相关.class 可根据库表,一键生成新增、修改删除、查询等接口 实现部分基于mybatis-plus,动态代码生成(相关包,未包含) 数据库文件...
是用于asm训练或搜索图片时的数据,是位图格式
压缩包里有:ASM9128T数据手册精简版 V1.0.3.pdf 、ASM9260T SIP176 EVK Rev.B 和 2014.03.01.pdf ASM9260T数据手册V1.2 20141201.pdf
asm算法的opencv实现,中含有三个工具,分别可以从图片,摄像机中获得人脸识别特征点描述
ASM1166datasheet.pdf
ASM1153 USB转sata芯片datasheet
ASM模拟坏块 ASM里实现修改指定的Block
asm-1.3.3.jar, asm-1.3.4.jar, asm-1.3.5.jar, asm-1.4.1.jar, asm-1.4.2.jar, asm-1.4.3.jar, asm-1.4.jar, asm-1.5.1.jar, asm-1.5.2.jar, asm-1.5.3.jar, asm-2.0.jar, asm-2.1.jar, asm-2.2.1-sources.jar, asm...
赠送jar包:asm-9.1.jar; 赠送原API文档:asm-9.1-javadoc.jar; 赠送源代码:asm-9.1-sources.jar; 赠送Maven依赖信息文件:asm-9.1.pom; 包含翻译后的API文档:asm-9.1-javadoc-API文档-中文(简体)版.zip; ...