Search code examples
javabyte-buddy

Is there a way to get the method signature with Advice API?



I'm currently working on a Java agent whose job is to track (and let's say print) the current location of each thread in the program. As an example, if I have a Java program like so :
public class MyClass {
    public static void a(String s) {
        b(s);
    }

    public static void b(String s) {
        System.out.println(s);
    }

    public static void main(String[] args) {
        a("Hello")
    }
}

I would like my instrumented program to output :

[+] Thread X : Entering MyClass.main([])
[+] Thread X : Entering MyClass.a(java.lang.String Hello)
[+] Thread X : Entering MyClass.b(java.lang.String Hello)
[+] Thread X : Entering java.io.PrintStream.println(java.lang.String Hello)
Hello
[-] Thread X : Leaving java.io.PrintStream.println(java.lang.String Hello)
[-] Thread X : Leaving MyClass.b(java.lang.String Hello)
[-] Thread X : Leaving MyClass.a(java.lang.String Hello)
[-] Thread X : Leaving MyClass.main([])

I cannot modify the original program, that is why I am creating an agent. I think the Advice API is the way to go, as it allows me to append code at the beginning and the end of each and every function with a very low computation overhead, but I'm running into two issues :

  • My main question : Can the Advice API retrieve the signature (Class + method) of the method it has been called on ?
  • The second issue concerns transforming every method of every class. Currently, my plan is to create a dozen of AgentBuilder, each targetting functions with zero, one, two, three, ... arguments. Is there a better way to go ? What about the types of the arguments ?

Currently, I have the following agent :

public static void agentmain(String agentArgs, Instrumentation inst) {
    AgentBuilder mybuilder = new AgentBuilder.Default()
    .disableClassFormatChanges()
    .with(RedefinitionStrategy.RETRANSFORMATION)
    .with(InitializationStrategy.NoOp.INSTANCE)
    .with(TypeStrategy.Default.REDEFINE);
    mybuilder.type(nameMatches(".*"))
    .transform((builder, type, classLoader, module) -> {
        try {
            return builder
            .visit(Advice.to(TraceAdvice.class).on(isMethod()));
        } catch (SecurityException e) {
            e.printStackTrace();
            return null;
        }
    }).installOn(inst);
}

and my TraceAdvice class implements onEnter and onExit respectively with System.out.println("Entering") and System.out.println("Exiting").

Thank you in advance


Solution

  • Have you tried adding parameters like these to your before/after advice methods?

        @Origin Method method,
        @AllArguments(readOnly = false, typing = DYNAMIC) Object[] args
    

    Printing the Method object already gives to the method signature in one easy step. You can also print Object[] args in any way you see fit, e.g. Arrays.toString(args) or even Arrays.deepToString(args).

    I simply copied the snippet from one of my advices. If you do not wish to change any argument values, of course you do not need readOnly = false.