簡體   English   中英

如何使用 java 代理和 ASM 監控 object 創建?

[英]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,您還需要檢測newarrayanewarraymultianewarray字節碼。

此外,本機代碼和 JVM 本身可以在不調用構造函數的情況下創建或克隆對象。 這需要使用 JVM TI 單獨處理。

有關更多信息,請查看此問題 »

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM