3

I am trying to use Aspect Oriented Programming to execute a simple Fibonacci function and trace all calls to any method apart from the ones in Java and also display the nesting level of them.

Java Code:

package tracing;

public class Test {

    static int fib(int n) {
        if (n<=1)
            return n;
        else
            return fib(n-1) + fib(n-2);
    }

    static void report(int n,int r) {
        System.out.println("fib("+n+")="+r);
    }

    public static void main(String[] a) {
        report(4,fib(4));
    }
}

AspectJ Code:

package tracing;

public aspect Trace {
    String prefix = "";

    Object around(): call(* *(..)) && !within(java..*) && !within(FigureEditor..*) {
        System.out.println(prefix+thisJoinPoint.getSignature());
        prefix = ">" + prefix;
        Object result = proceed();
        prefix = prefix.substring(1);
        return result;
    }
}

Note: && !within(FigureEditor..*) is used only to avoid the functions in a class of a different package.


Console Output - Errors:

Exception in thread "main" java.lang.StackOverflowError     at
org.aspectj.runtime.internal.AroundClosure.<init>(AroundClosure.java:34)
    at tracing.Trace$AjcClosure1.<init>(Trace.aj:1)     at
tracing.Trace.ajc$around$tracing_Trace$1$ef88057b(Trace.aj:7)   at
tracing.Trace.ajc$around$tracing_Trace$1$ef88057b(Trace.aj:7)   at
tracing.Trace.ajc$around$tracing_Trace$1$ef88057b(Trace.aj:7)   at
tracing.Trace.ajc$around$tracing_Trace$1$ef88057b(Trace.aj:7)   at
tracing.Trace.ajc$around$tracing_Trace$1$ef88057b(Trace.aj:7)   at
tracing.Trace.ajc$around$tracing_Trace$1$ef88057b(Trace.aj:7)

Update: The output I want is similar to below:

void Test.main(String [])
>void Test.report(int, int)<br>
>>int Test.fib(int)<br>
>>>int Test.fib(int)<br>
>>>>int Test.fib(int)<br>
>>>>>int Test.fib(int)<br>
>>>>>int Test.fib(int)<br>
>>>>int Test.fib(int)<br>
>>>int Test.fib(int)<br>
>>>>int Test.fib(int)<br>
>>>>int Test.fib(int)<br>
>> void Test.write(String) fib(4)=3
kriegaex
  • 63,017
  • 15
  • 111
  • 202
Marialena
  • 817
  • 8
  • 31
  • I have fixed your formatting problems. The syntax for code formatting is to indent by 4 spaces, you should also use that for your console logs instead of the quoting syntax ">". – kriegaex Feb 21 '16 at 11:34

2 Answers2

2

I'm not sure if you're using Eclipse or some other IDE to develop your AspectJ code, but if you do, you might notice that there are AspectJ markers shown by the IDE in the method body of your around() advice itself, which means they themselves (the method calls inside your around() advice) get woven by your aspect, as shown in the following image:

AspectJ markers shown in the advice itself

That means, in order to avoid the infinite recursion occuring in your around() advice, where the advice is getting called repeatedly from inside itself, you need to exclude the control flow of the advice from getting advised. You can exclude all code inside the control flow of any advice easily by including the following pointcut expression: !cflow(adviceexecution()) so your aspect code would look like this (reformatted a little bit):

public aspect Trace {
    
    String prefix = "";

    Object around(): call(* *(..)) 
        && !within(java..*) 
        && !within(FigureEditor..*) 
        && !cflow(adviceexecution()) {
        
        System.out.println(prefix+thisJoinPoint.getSignature());
        prefix = ">" + prefix;
        Object result = proceed();
        prefix = prefix.substring(1);

        return result;
    }
}

From the AspectJ documentation on pointcut expressions:

cflow(Pointcut): Picks out each join point in the control flow of any join point P picked out by Pointcut, including P itself.

adviceexecution(): Picks out all advice execution join points.

EDIT: adding Aspect code to reflect the updated question

Your update to your original question clarifies your intention with your aspect. I've updated the aspect code to reflect these changes. The resulting aspect would look like this:

public aspect Trace {
    
    String prefix = "";

    Object around(): call(* *(..)) 
        && !within(java..*)
        && !if(thisJoinPoint.getTarget()!=null && thisJoinPoint.getTarget().getClass().getName().startsWith("java"))
        && !within(FigureEditor..*) 
        && !within(Trace) {
        
        System.out.println(prefix+thisJoinPoint.getSignature());
        prefix = ">" + prefix;
        Object result = proceed();
        prefix = prefix.substring(1);

        return result;
    }
}

I replaced the exclusion of the control flows of all advice execution code with the exclusion of only the Trace aspect, and included a runtime test to exclude all calls to code in the java package.

Community
  • 1
  • 1
Nándor Előd Fekete
  • 6,988
  • 1
  • 22
  • 47
  • I have updated my question to see what is the output I want.... Now the execution works fine with no problems... But the output I get is this: `int tracing.Test.fib(int) void tracing.Test.report(int, int) fib(4)=3` – Marialena Feb 20 '16 at 14:10
  • 1
    I added details to my answer to reflect to the changes in your question. – Nándor Előd Fekete Feb 20 '16 at 14:53
  • @NándorElődFekete: What do you think about my answer? I think it is a lot less complicated than yours and does not involve runtime checks or reflection. – kriegaex Feb 21 '16 at 11:42
  • @kriegaex You're totally right. My solution is suboptimal. Your answer is the perfect one. – Nándor Előd Fekete Feb 21 '16 at 13:50
2

While Nándor's answer is correct, it is also expensive because control flow pointcuts such as cflow() and cflowbelow() are evaluated during runtime. I would suggest you simply use !within(Trace) which can be evaluated during compile time and is faster.

You also want to use a simpler way to exclude calls to Java classes, not involving any reflection: !call(* java..*(..))

package tracing;

public aspect Trace {
    String prefix = "";

    Object around() :
        !within(Trace) &&
        call(* *(..)) &&
        !call(* java..*(..)) &&
        !within(FigureEditor..*)
    {
        System.out.println(prefix + thisJoinPoint.getSignature());
        prefix = ">" + prefix;
        Object result = proceed();
        prefix = prefix.substring(1);
        return result;
    }
}

This yields the desired output:

int tracing.Test.fib(int)
>int tracing.Test.fib(int)
>>int tracing.Test.fib(int)
>>>int tracing.Test.fib(int)
>>>int tracing.Test.fib(int)
>>int tracing.Test.fib(int)
>int tracing.Test.fib(int)
>>int tracing.Test.fib(int)
>>int tracing.Test.fib(int)
void tracing.Test.report(int, int)
fib(4)=3
kriegaex
  • 63,017
  • 15
  • 111
  • 202