Search code examples
javaintellij-ideaintellij-pluginjvmtijdi

How to hide variables from Java's JDI?


I am instrumenting some classes and introducing some new local variables. Now, when the user places a breakpoint in the code, and execution is stopped, the newly introduced local variables can be seen inside Intellij IDEA's debugger window. How can I hide them?

UPDATE: I will have to somehow remove debug info from the instrumented code, but not sure how to do it.

UPDATE 2: I am using the ASM library for instrumentation.

 public void visitCode() {
       this.mv.visitLdcInsn(stringToPass);
       this.mv.visitMethodInsn(Opcodes.INVOKESTATIC, "MyAgentClass", "loadData", "(Ljava/lang/String;)LDataClass;", false);
       this.mv.visitVarInsn(Opcodes.ASTORE, this.getDataIndex());
}


public void visitMaxs(int maxStack, int maxLocals) {
     if (this.myStartLabel != null && this.myEndLabel != null) {
        this.mv.visitLocalVariable("__my__data__", "Ljava/lang/Object;", (String) null, this.myStartLabel, this.myEndLabel, this.getDataIndex());
     }

       super.visitMaxs(maxStack, maxLocals);
 }

__my__data__ is shown inside Intellij IDEA.


Solution

  • https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.13

    Based on the JVM specs, you can remove your local variables from LocalVariableTable. Javassist does that automatically in generated code and I cannot see the variable secretCode during the run:

    enter image description here

    Decompiling the result class can show that there are no LocalVariableTable entries for it:

    
      public void run();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=3, args_size=1
             0: ldc           #39                 // int -889275714
             2: istore_1
             3: iload_1
             4: invokestatic  #43                 // Method org/example/App.test:(I)Ljava/lang/Integer;
             7: astore_2
             8: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
            11: aload_2
            12: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
            15: new           #2                  // class SecreFoo
            18: dup
            19: invokespecial #3                  // Method "<init>":()V
            22: astore_1
            23: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
            26: aload_1
            27: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
            30: return
          LineNumberTable:
            line 14: 15
            line 15: 23
            line 16: 30
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      31     0  this   LSecreFoo;
               23       8     1 fCopy   LSecreFoo;
    
    

    So when you are instrumenting your class, drop all your local variables from the table (or do not add them).