2016-09-14 18 views
0

假設我想在調用某個感興趣的方法之前調用(日誌記錄)方法之前。這意味着在偵聽visitMethodInsn時,堆棧已經填充了感興趣方法的參數。ASM ByteCode:在另一個調用中預先訪問visitMethodInsn

是否有可能將當前堆棧存儲在某處,調用日誌並重新填充堆棧?我是否缺少任何明顯的堆棧變異操作符?還是我真的需要:

  • 存儲在變量堆棧暫時
  • 緩衝區中的所有堆棧突變呼叫,直到一個方法調用到我的(可能不適用)記錄調用之後重播緩衝區?

:給予原代碼

public static void main() 
{ 
    doSomethingUnrelated(); 

    methodOfInterest(); // line 5 for example 

    doSomethingUnrelated(); 
} 

生成的代碼應該是這樣的:

public static void main() 
{ 
    doSomethingUnrelated(); 

    Logger.log("methodOfInterest", "main", 5); 
    methodOfInterest(); 

    doSomethingUnrelated(); 
} 

public Logger 
{ 
    public static void log(String method, String callee, int line) 
    { ... } 
} 

語境:我的實際ASM MethodVisitor中看起來是這樣的:

class UsageClassMethodVisitor extends MethodVisitor implements Opcodes 
{ 
    private final String fileName; 
    private final String visitedClass; 
    private final String visitedMethod; 
    private int lineNumber; 

    UsageClassMethodVisitor(MethodVisitor mv, String fileName, String visitedClass, String visitedMethod, boolean isStatic) 
    { 
     super(Opcodes.ASM5, mv); 
     this.fileName = fileName; 
     this.visitedClass = visitedClass; 
     this.visitedMethod = visitedMethod; 
    } 

    @Override 
    public void visitLineNumber(int i, Label label) { 
     lineNumber = i; 
     super.visitLineNumber(i, label); 
    } 

    @Override 
    public void visitMethodInsn(int access, String ownerClass, String method, String signature, boolean isInterface) { 
     if(ownerClass.contains("org/example/package/")) { 
      System.out.printf("prepending to visitMethodInsn(%s, %s, %s, %b) @ %s.%s:%d\n", 
        ownerClass, method, signature, isInterface, 
        visitedClass, visitedMethod, lineNumber); 
      super.visitLdcInsn(fileName); 
      super.visitLdcInsn(visitedClass); 
      super.visitLdcInsn(visitedMethod); 
      super.visitLdcInsn(lineNumber); 
      super.visitMethodInsn(Opcodes.INVOKESTATIC, 
        Hook.ACCESS_OWNER_NAME, 
        Hook.ACCESS_METHOD_NAME, 
        Hook.ACCESS_METHOD_DESC, false); 
     } 
     super.visitMethodInsn(access, ownerClass, method, signature, isInterface); 
    } 
} 

,但顯然這是拋出一個錯誤堆棧8長,而不是4在調用時間:

Exception in thread "main" java.lang.VerifyError: Bad type on operand stack 
Exception Details: 
    Location: 
    org.using.package.Main.main([Ljava/lang/String;)V @13: invokestatic 
    Reason: 
    Type integer (current frame, stack[8]) is not assignable to 'java/lang/String' 
    Current Frame: 
bci: @13 
flags: { } 
locals: { '[Ljava/lang/String;' } 
stack: { long, long_2nd, long, long_2nd, 'java/util/concurrent/TimeUnit', 'java/lang/String', 'java/lang/String', 'java/lang/String', integer } 
+0

不知道你爲什麼推送文件名。注意:'long'需要2個插槽,所以你有7個值,最後一個位置是8號插槽(這是從0開始的第9個插槽) –

+0

什麼是ACCESS_METHOD_NAME/DESC? –

+0

這些文件位於暴露鉤子的文件中。參見http://www.slideshare.net/Takipi/advanced-production-debugging#33來自Takipi的例子。 – Herman

回答

2

沒有理由來存儲堆棧的任何地方,因爲,堆棧很好,一個堆棧。您可以只推送日誌參數,調用日誌記錄功能,彈出結果,並且原始堆棧仍然存在。

+0

那麼你如何解釋錯誤? – Herman

+0

沒關係,問題與調用錯誤的方法有關。對於聲明的方法0和1,我使用了Hook.class.getDeclaredMethods()[0] .getName(),但是它們的順序在編譯過程中發生了變化。 – Herman