[英]How to monitor object creation using java agent and ASM?
我想要做的是监视 object 的创建并记录该 object 的唯一 ID。
首先,我尝试监视NEW
指令,但它无法工作并抛出VerifyError: (...) Expecting to find object/array on stack
。 听说NEW
后的object没有初始化所以不能传给其他方法。 所以我放弃了这种方法。
其次,我试图监控<init>
的调用,这个方法初始化了未初始化的 object。 但是我不确定初始化之后,如果初始化的object会被压栈?
在我的方法访问者适配器中:
public void visitMethodInsn(int opc, String owner, String name, String desc, boolean isInterface) {
...
mv.visitMethodInsn(opc, owner, name, desc, isInterface);
if (opc == INVOKESPECIAL && name.equals("<init>")) {
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESTATIC, "org/myekstazi/agent/PurityRecorder", "object_new",
"(Ljava/lang/Object;)V", false);
}
}
在MyRecorder.java
:
public static void object_new(Object ref){
log("object_new !");
log("MyRecorder: " + ref);
log("ref.getClass().getName(): " + ref.getClass().getName());
}
我在演示中尝试过它们,它抛出了VerifyError
:
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.VerifyError: Operand stack underflow
Exception Details:
Location:
AbstractDemo.<init>()V @4: dup
Reason:
Attempt to pop empty stack.
Current Frame:
bci: @4
flags: { }
locals: { 'AbstractDemo' }
stack: { }
Bytecode:
0x0000000: 2ab7 0001 59b8 003b b1
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
at java.lang.Class.privateGetMethodRecursive(Unknown Source)
at java.lang.Class.getMethod0(Unknown Source)
at java.lang.Class.getMethod(Unknown Source)
at sun.launcher.LauncherHelper.validateMainClass(Unknown Source)
at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)
它似乎也不起作用。 是否有任何替代方法可以监视 object 的创建?
消息的一部分
Location:
AbstractDemo.<init>()V @4: dup
提示:您正在检测构造函数。 在构造函数中, invokespecial <init>
也用于委托给另一个构造函数,无论是在同一个 class 中还是在超类中。
调用另一个构造函数的典型顺序是aload_0
(this),push arguments, invokespecial <init>
,所以调用后栈上没有对object的引用。
这是VerifyError
的解码字节码的样子:
0 aload_0
1 invokespecial [1]
4 dup
5 invokestatic [59]
8 return
通常,您不想报告这些委托构造函数调用,因为它们会导致多次报告相同的 object。 但识别它们可能很棘手,因为接收器 class 不是可靠的标准。 例如,以下是有效的 Java 代码:
public class Example {
Example reference;
Example(Example anotherObject) {
reference = anotherObject;
}
Example() {
this(new Example(null));
reference.reference = new Example(this);
}
}
在这里,我们有一个包含三个具有相同目标 class 的invokespecial
指令的构造函数,并且委托构造函数调用既不是第一个也不是最后一个,因此指令本身没有简单检查的属性告诉您这一点。 您必须将提供指令的目标标识为索引为零的aload
,即this
,以了解指令是否正在初始化当前实例,当中间有参数提供指令时,这是不平凡的。
也就是说,即使在构造函数之外,也不能保证新实例化的 object 在堆栈上。 当实例化在随后存储或使用结果的表达式上下文中使用时,通常是这种情况,而不是在语句上下文中。 换句话说,对于像这样的方法
void test() {
new Example();
}
幼稚的编译器实现(如javac
)可能会生成表达式代码的等价物,后跟pop
指令,但在这种情况下,其他实现(如ecj
)可以省略前面的dup
,从而消除对后续pop
的需要,因为没有参考将在invokespecial <init>
指令之后在堆栈上。
一种更安全的方法是搜索以new
开头并导致invokespecial <init>
的指令序列(允许嵌套出现)。 然后,在new
指令之后注入一个dup
,在invokestatic
之后注入一个invokespecial
。
由于 Holger 的回答中描述的原因,在实例化站点检测新对象可能非常棘手。 为了检测 object 分配,代理通常 go 以另一种方式 - 他们修改Object()
构造函数,因为所有普通构造函数最终都会通过super()
构造函数链调用Object()
。
但是,这不会捕获所有 object 分配。 如果您关心 arrays,您还需要检测newarray
、 anewarray
、 multianewarray
字节码。
此外,本机代码和 JVM 本身可以在不调用构造函数的情况下创建或克隆对象。 这需要使用 JVM TI 单独处理。
有关更多信息,请查看此问题 »
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.