天天看点

JavaAgent学习笔记什么是JavaAgent?如何实现简单的JavaAgent

       粗略查看了它的手册,发现其使用了JavaAgent的技术。那么,什么是JavaAgent呢?

可以在加载class文件之前做拦截把字节码做修改

可以在运行期将已经加载的类的字节码做变更,但是这种情况下会有很多的限制,后面会详细说

还有其他的一些小众的功能

获取所有已经被加载过的类

获取所有已经被初始化过了的类(执行过了clinit方法,是上面的一个子集)

获取某个对象的大小

将某个jar加入到bootstrapclasspath里作为高优先级被bootstrapClassloader加载

将某个jar加入到classpath里供AppClassloard去加载

设置某些native方法的前缀,主要在查找native方法的时候做规则匹配

所以 ,初步认为是故障演练平台使用了mkagent,对hsf接口进行了mock,让其模拟异常。实现接口调用的失败。

     现阶段实现agent至少有二种方式,

通过加载使用C编译的动态库的方式实现。在linux与mac下动态库是“libname.so”,windows下动态库是“libname.dll”。一般会实现以下三个方法。

<code>Agent_OnLoad</code>函数,如果agent是在启动的时候加载的,也就是在vm参数里通过-agentlib来指定,那在启动过程中就会去执行这个agent里的<code>Agent_OnLoad</code>函数。

<code>Agent_OnAttach</code>函数,如果agent不是在启动的时候加载的,是我们先attach到目标进程上,然后给对应的目标进程发送load命令来加载agent,在加载过程中就会调用<code>Agent_OnAttach</code>函数。

<code>Agent_OnUnload</code>函数,在agent做卸载的时候调用,不过貌似基本上很少实现它。

首先是一段C++的动态库代码,注意头文件jvmti.h与jni_md.h

以上代码主要实现了2个方法,Agent_OnAttach是在程序在运行时加载,并打印所有的加载类。Agent_OnLoad是在程序启动时加载,打印一个语句。

然后对这个CPP进行编译。生成一个.so的动态库文件。

然后是被agent的目标的类,并且对其进行编译

然后开始尝试程序启动阶段的agent,使用以下指令进行运行

 可以看到日志文件的输出。

这是程序在启动的时候进行agent了,jvm执行了动态库中的Agent_OnLoad代码。

另外就是程序运行中的agent了。现在需要以下代码。

 当运行TestMain的main函数的时候,ps aux | grep java 查看TestMain的PID,然后修改以上代码中的pid参数,让Agent加载到指定的java进程中。然后立马运行TestAgent,就会看到以下日志。

计数器打印到一半时,执行了动态库中的Agent_OnAttach方法。

以上是使用JVMTI实现的agent。但是故障演练平台应该不是用这种方法。在改平台的手册上看到了这句话:

先看以下代码

除了该段代码,还要在该类包接口下创建META-INF文件夹,并建立MANIFEST.MF文件,以下为文件内容:

指定agent.MyAgent为PremainClass。同时将MyAgent与MF文件打包,我使用的是eclipse导出。然后使用以下指令(可以参考故障演练平台的配置)

 最后,生成了一个test.class。对其反编译

打开该文件,就是利用asm字节码操作生成的一个接口

推测,可能故障演练平台上就是使用的就是javaagent+asm技术,在应用启动或运行时,通过修改类的字节码,模拟系统的各种故障。(以上都是瞎猜,猜错了我不负责,演练平台的同学别找我^o^)

PS:文章中的代码大多来自于网络,但本人亲测有效。