Download Advice Weaving in AspectJ

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
Advice Weaving in AspectJ
Paper by
Erik Hilsdale
Jim Hugunin
Presented by
Shay Cohen
Introduction
Implementation of advice weaving in AspectJ –
ver 1.1
 The Compilation Process
 Join Points Shadows
 Matching
 Weaving
 Compile time performance
 Performance of woven code
The Compilation Process –
Source File Compilation
Each advice declaration is compiled into a standard Java method
before(String s): execution(void go(*)) && args(s)
{
System.out.println(s);
}

Is compiled into a method encapsulating the body of the advice
public void ajc$before$A$a9();
0: getstatic [java/lang/System.out]
3: aload_1
4: invokevirtual [java/io/PrintStream.println]
7: return

Source File Compilation –
cont.
Reflection is made by three special variables added to
the advice declaration.
(…, JoinPoint thisJoinPoint,
JoinPoint.StaticPart thisJoinPointStaticPart,
JoinPoint.StaticPart
thisEncolsingJoinPointStaticPart)
 Optimization - variables which are not referred to
within the body of the advice are removed from the
signature
 Optimization - determine if all uses of thisJoinPoint
can be replaced with thisJoinPointStaticPart.

Source File Compilation –
cont.



Around advice in AspectJ uses the
special form proceed
Generating a method which takes in all
of the original arguments to the around
method plus an additional
AroundClosure object
The body of the proceed method will
call a method on the AroundClosure
Source File Compilation –
cont.
/* *******************************************************************
* Copyright (c) 1999-2001 Xerox Corporation,
*
2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
*
Xerox/PARC
initial implementation
* ******************************************************************/
package org.aspectj.runtime.internal;
public abstract class AroundClosure {
//private Object[] state;
public AroundClosure(/* Object[] state */) {
// this.state = state;
}
protected Object[] state;
protected Object[] preInitializationState;
public AroundClosure(Object[] state) {
this.state = state;
}
public Object[] getPreInitializationState() {
return preInitializationState;
}
}
/**
* This takes in the same arguments as are passed to the proceed
* call in the around advice (with primitives coerced to Object types)
*/
public abstract Object run(Object[] args) throws Throwable;
The Compilation Process –
Bytecode transformation



“static shadow” - certain principled places in
bytecode represent possible join points
The weaver must instrument the bytecode to
insert calls to the an advice whenever a
condition is met.
For each such static shadow, it checks each
piece of advice in the system and determines
if the advice's pointcut could match that static
shadow.
Bytecode transformation cont.
void go(java/lang/String):
0: return
void go(java/lang/String):
0: invokestatic [A.aspectOf]
3: invokevirtual [A.ajc$before$A$a3]
6: return
Transformed
Bytecode transformation cont.
void go(java/lang/Object):
0: return
Transformed
void go(java/lang/Object):
before(String s):
0: aload_1 # copy first argument into
execution(void go(*)) &&
1: astore_2 # a temporary frame location
args(s)
2: aload_2 # check whether argument
{
3: instanceof [String] # is actually a String
System.out.println(s);
6: ifeq 19 # if not, skip advice
}
9: invokestatic [A.aspectOf] # get aspect
12: aload_2 # load argument again
13: checkcast [String] # guaranteed to succeed
16: invokevirtual [A.ajc$before$A$a3] # run advice
19: return
Bytecode transformation cont.

Instanceof – If objectref is not null and is an
instance of the resolved class or array or
implements the resolved interface, the
instanceof instruction pushes an int result of 1 as
an int on the operand stack. Otherwise it
pushes an int result of 0.
The Java Virtual Machine Specification, Second Edition
Tim Lindholm, Franck Yellin
Bytecode transformation cont.
public aspect WeavingAspect {
before (String s): execution(void go(*)) && args(s)
{
System.out.println("before (String s): execution(void go(*)) && args(s) " + s);
}
before (Object s): execution(void go(*)) && args(s)
{
System.out.println("before (Object s): execution(void go(*)) && args(s) " + s);
}
}
Bytecode transformation cont.
public class WeavingObject {
public static void main(String[] args) {
WeavingObject obj = new WeavingObject();
String s = null;
obj.go(s);
System.out.println();
}
}
s = new String("String");
obj.go(s);
public void go(Object s) {
System.out.println("go(Object s) " + s);
}
before (Object s): execution(void go(*)) && args(s) null
go(Object s) null
before (String s): execution(void go(*)) && args(s) String
before (Object s): execution(void go(*)) && args(s) String
go(Object s) String
Join point shadows – What
defines a join point shadow



A join point is a point in the dynamic call
graph of a running program where the
behavior of the program can be modified by
advice.
Every dynamic join point has a corresponding
static shadow in the source code or bytecode
of the program.
a shadow represents a region of bytecode
associated with a join point
What defines a join point
shadow - cont.
kind
signature this
target
args
Bytecode shadow
Method-execution
Method-call
method
method
ALOAD_0 or none
ALOAD_0 or none
Same as this
From stack
Local vars
From stack
Constructor-exec
Constructor-call
constructor ALOAD_0
constructor ALOAD_0 or none
Same as this
None
Local vars
From stack
Entire code segment of method
Invokeinterface, invokespecial (only for privates),
invokestatic, invokevirtual
Code segment of <init> after call to super
Invokespecial (plus some extra pieces)
Field-get
Field-set
Field
Field
ALOAD_0 or none
ALOAD_0 or none
From stack
From stack
none
From stack
Getfield or getstatic
Putfield or putstatic
Advice-execution
None
ALOAD_0
Same as this
Local vars
Code segment of corresponding method
Initialization
Corresp.
ALOAD_0
constructor
Typename None
Corresp.
None
constructor
Same as this
Complex
None
None
None
Local vars
Requires inlining of all constructors in a given class
into one
Code segment of <clinit>
Code segment of <init> before call to super,
this may require in-lining
Typename ALOAD_0 or none
of exception
None
From stack
Static-initialization
Pre-initialization
Exception-handler
execution
Start is found from exception handler table.
(only before advice allowed because
end is poorly defined in bytecode)
The shadows of “hello world”,
Method call
0: getstatic [java/lang/System.out]
3: ldc [String hello world]
5: invokevirtual [java/io/PrintStream.println]
8: return
Transformed
0: getstatic [java/lang/System.out]
3: ldc [String hello world]
5: invokestatic [A.aspectOf]
8: invokevirtual [A.ajc$before$A$15a]
11: invokevirtual [java/io/PrintStream.println]
14: return
The shadows of “hello world”,
Exposing method call - cont.
0: getstatic [java/lang/System.out]
3: ldc [String hello world]
5: invokevirtual [java/io/PrintStream.println]
8: return
Transformed
0: getstatic [java/lang/System.out]
3: ldc [String hello world]
5: astore_1
6: astore_2
7: invokestatic [A.aspectOf]
10: aload_2
11: invokevirtual [A.ajc$before$A$15a]
14: aload_2
15: aload_1
16: invokevirtual [java/io/PrintStream.println]
19: return
Matching – Residues


Advice and other advice-like entities are
represented by shadow munger objects, each
of which contains a pointcut designator (PCD)
When the PCDs depend on the dynamic state
at the join point this mismatch is resolved by
adding a dynamic test that captures the
dynamic part of the matching. This dynamic
test is the residue of the match.
Residues – cont.
0: getstatic [java/lang/System.out]
3: ldc [String hello world]
5: invokevirtual [java/io/PrintStream.println]
8: return
before(): execution(void main(*))
Transformed
&& if(Tracing.level == 1) {
System.out.println("got here");
0: invokestatic [A.ajc$if_0] # dynamic test
3: ifeq 12
}
6: invokestatic [A.aspectOf]
9: invokevirtual [Method A.ajc$before$A$a6]
12: getstatic [java/lang/System.out]
15: ldc ["hello world"]
17: invokevirtual java/io/PrintStream.println]
20: return
Residues, Cflow – cont.


In AspectJ-1.1 this matching is
implemented entirely as a dynamic test
on the join point.
The current implementation of cflow
uses a thread-local stack.
Fastmatch



A “regular” Match is done by if a shadow
munger is defined for each of the static
shadow.
Matching every join point shadow in every
class file can be a time consuming process
In fastmatch, every shadow munger is
matched to the constant pool information in
each class file.
Synthetic methods and
matching



Compilers for the Java language generate
methods and fields that are not in the original
source code
Fortunately, they can be generally recognized
by the SYNTHETIC attribute in the Java
bytecode
There are many additional synthetic methods
added by the AspectJ compiler AJ_SYNTHETIC
Weaving - Context exposure


collect up the desired
state from each
matching munger
modify code so
desired state is in
temporaries
mungers need
target,
arg1, and
arg2
astore_3
astore_4
astore_5
aload_5
aload_4
aload_3
#
#
#
#
#
#
arg2
arg1
target
repush
repush
repush
Shadow Munger
Implementation


Each shadow munger
transforms the
shadow by adding
code inside the
boundary of the
shadow.
apply mungers to
shadow in inverse
precedence order
before
after throwing
before
weaving
before advice

insert code at
beginning of shadow


code pushes aspect,
then arguments to
advice, then invokes
the advice method
guard the inserted
code with residual
test, if necessary
before with
residual
target(Foo)
aload_5 # target
instanceof [Foo]
ifeq end:
invokestatic [A.aspectOf]
[push args to advice]
invokevirtual [A.before379]
end:
weaving
after returning advice

first, process shadow to
ensure exactly one exit
point:


replace all return (or
ireturn, freturn, etc)
bytecodes with goto
shadow’s end
add single return (or
ireturn, freturn, etc) at
end of shadow if there
was one inside shadow
return
goto
weaving
after returning advice – cont.

then add residual
and advice code to
end of shadow
return
goto
advice
Factorial
class Factorial {
static int fact(int n) {
if (n == 0) return 1;
else return n * fact(n-1);
}
public static void main(String[]
args) {
System.out.println(fact(3));
}
}
after returning
advice – cont.
static int fact(int);
0: iload_0
1: ifne 6
4: iconst_1
5: ireturn
6: iload_0
7: iconst_1
8: isub
9: invokestatic [fact]
12: iload_0
after() returning(int i):
13: imul
execution(int fact(int))
14: ireturn
static int fact(int);
0: iload_0
1: ifne 8
4: iconst_1
5: goto 19
8: iload_0
9: iconst_1
10: isub
11: invokestatic [fact]
14: iload_0
15: imul
16: goto 19
19: dup
20: istore_1
21: invokestatic [A.aspectOf]
24: iload_1
25: invokevirtual
[A.ajc$afterReturning$A$ff]
28: ireturn
weaving
around advice


first, extract code
from shadow into its
own method in
containing class
replace shadow
code with call to
new method
invokevirtual
or
invokestatic
weaving
around advice – cont.

create new class,
whose run method
is just a call to
extracted method
Closure837
run
invokevirtual
or
invokestatic
Why we don’t inline advice
code




The primary performance overhead of
AspectJ code is caused by the aspect-instance
lookup and method call
Inlining can be done much more effectively
by a JIT
need to either increase the visibility of the
accessed members, which would let anyone
see them
Inlining is used in default around advice and
privileged aspects
Compile time performance

time required to compile the xalan xslt
processor from apache.org
Compile time performance –
cont.
Improvements can be achieved by:
 Basic engineering work.
 improving the fastmatch algorithm to
handle more cases
 support incremental recompilation
benchmarks
Performance of woven code




we measured the compilation time of a single
large tool, the Xalan XSLT processor from
apache.org (paper talks about compile-time
performance)
for performance evaluation we used the
XSLTMark benchmark
we chose to benchmark logging as it is the
most invasive of the common AspectJ
applications
with logging enabled, both AspectJ and a
hand-rolled logger had a 60,000% overhead

all following measurements are with logging
disabled
benchmarks
Performance of woven code hand-rolled logging

to get a baseline we produced a handrolled implementation of a logging
policy

add a log to each of the 826 classes
static Logger log = Logger.getLogger(“xalan”);

add a call to each of the 7711 methods
log.entering(“<ClassName>”,
“<MethodName>”)
benchmarks
Performance of woven code an unfortunate surprise
public aspect Trace {
private static Logger log = Logger.getLogger(“xalan”);
pointcut traced(): execution(* *(..));
before(): traced() {
Signature s = thisJoinPointStaticPart.getSignature();
log.entering(
s.getDeclaringType().getName(),
s.getname());
}
}
Logging enabled
benchmarks
Performance of woven code an unfortunate surprise
public aspect Trace {
private static Logger log = Logger.getLogger(“xalan”);
pointcut traced(): execution(* *(..));
}


Logging disabled
2900% overhead (!)
Use Signature.getDeclaringClassName()
Logging Overhead
before(): traced() {
Signature s = thisJoinPointStaticPart.getSignature();
log.entering(
35
s.getDeclaringType().getName(),
30
s.getname());
25
}
20
15
10
5
0
no logging
hand-coded
naïve AspectJ
benchmarks
Performance of woven code guarding with isLoggable()
public aspect Trace {
private static Logger log = Logger.getLogger(“xalan”);
pointcut traced(): execution(* *(..));
Overhead
}
before(): traced() {
if (!log.isLoggable(Level.FINER)) return;
Signature s = thisJoinPointStaticPart.getSignature();
log.entering(
s.getDeclaringType().getName(), 1.4
1.2
s.getname());
1
}
0.8

22% over handcoded
0.6
0.4
0.2
0
no logging
hand-coded
AspectJ
loggable
benchmarks
Performance of woven code using the if pointcut
public aspect Trace {
private static Logger log = Logger.getLogger(“xalan”);
pointcut traced(): execution(* *(..))
&& if (log.isLoggable(Level.FINER));
Overhead
}
before(): traced() {
Signature s = thisJoinPointStaticPart.getSignature();
log.entering(
s.getDeclaringType().getName(), 1.4
1.2
s.getname());
1
}
0.8

8% over handcoded
0.6
0.4
0.2
0
no logging hand-coded
AspectJ
loggable
AspectJ
if(loggable)
benchmarks
Performance of woven code using a field
public aspect Trace {
private static Logger log = Logger.getLogger(“xalan”);
static boolean enabled;


8% under hand-coded
cross-cutting
optimization
Overhead
}
pointcut traced(): execution(* *(..)) && if(enabled)
&& if(log.isLoggable(Level.FINER));
before(): traced() {
Signature s = thisJoinPointStaticPart.getSignature();
log.entering(
s.getDeclaringType().getName(), 1.4
1.2
s.getname());
1
}
0.8
0.6
0.4
0.2
0
no logging hand-coded
AspectJ
loggable
AspectJ
AspectJ
if(loggable) if(enabled)
(a late addition)
Conclusions




Complier Implementation is a major
issue in performance
The complier performance has an
impact on aspects users community
Aspects code writing is a major issue in
performance
AspectJ is getting faster by the minute
The end