views:

130

answers:

3

For debugging purposes, I need to follow the execution of some piece of code, within a class. I would like to generate a log for all method calls, in XML, like :

<call class='pack.age.MyClass' method='myMethod1'>
    <param name='param1'>param1.toString() value</param>
    ...
    <call>Call to other method within myMethod1; you get the idea</call>
</call>

Because the class is long and has lots of methods, I wondered if there is a way to access the parameters generically, maybe using reflection. I am inside of the method, and I want to loop on the parameters given to this call of the method. It would ease my pain and allow me to make a regexp to add the log lines. Is it possible ?

Any other neat way to do it appreciated (but AOP alas not really an option).

+2  A: 

Add a call to log() at the beginning of every method. Within log() either throw an Exception (for Java 1.4) or use Thread.currentThread().getStackTrace() to get a stack trace.

This will give you an array of StackTraceElement. The element you're looking for is at index 1 (this is where log() was called). Use getMethodName() to get the name of the method. After that, you can use reflection to examine the method.

Unfortunately, this doesn't give you access to the real Java stack so you can't print the values of the parameters. One solution would be to simply copy them into the log call and use reflection on the method to figure out what they are. With Java 5, variable argument lists (log(Object...args)) is your friend.

The other option is using the Java Debugger API and write a small debugger. In this case, RemoteStackFrame will contain the parameters and values.

If that's too much work, just use ten lines of AOP and be done with it.

Aaron Digulla
+1  A: 

weird suggestion:

  1. Open eclipse
  2. create a subclass of your class with the first class in the constructor
  3. modifiy the config of eclipse : in the template where eclipse creates a delegate method, add a beginMyMethod(); and a endMyMethod(); surrounding the delegate.method();
  4. ask eclipse to implements all the delegate methods. This solution won't work with the private and the static members.

something like:

 class A
   {
   public void sayHello()
       {
       System.err.println("OK");
       }
   }

 class B
   extends A
   {
   private A delegate;
   public B(A delegate) { this.delegate=delegate);}
   @Override
   public void sayHello()
       {
       beginMyMethod();
       delegate.sayHello();
       endMyMethod();
       }
   }
Pierre
+1  A: 

Another way is using a dynamic tracer like BTrace (similar to AOP, but outside of your code). Look at http://kenai.com/projects/btrace/pages/UserGuide for examples. There are even VisualVM Plugins to attach to running process making thigs easier. With the following BTrace code you get the method calls and its parameters (taken from the examples).

import com.sun.btrace.AnyType;
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;


@BTrace public class YourCalls {
    @OnMethod(clazz="pack.age.MyClass", method="myMethod1",
              location=@Location(value=Kind.CALL, clazz="/.*/", method="/.*/"))
    public static void o(AnyType[] args) { // all calls to methods
        printArray(args);
    }
}

Attaching BTrace to your running program is easy. First get the PID of your process using jps, after this call:

btrace PID YourCalls.java
dz