Upload
kathleen-welding
View
216
Download
0
Embed Size (px)
Citation preview
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
methodbefore(String s): execution(void go(*)) && args(s){
System.out.println(s);} Is compiled into a method encapsulating the body of the
advicepublic void ajc$before$A$a9();0: getstatic [java/lang/System.out]3: aload_14: 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.StaticPartthisEncolsingJoinPointStaticPart) 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
void go(java/lang/Object):0: aload_1 # copy first argument into1: astore_2 # a temporary frame location2: aload_2 # check whether argument3: instanceof [String] # is actually a String6: ifeq 19 # if not, skip advice9: invokestatic [A.aspectOf] # get aspect12: aload_2 # load argument again13: checkcast [String] # guaranteed to succeed16: invokevirtual [A.ajc$before$A$a3] # run advice19: return
Transformed
before(String s): execution(void go(*)) && args(s)
{System.out.println(s);
}
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); }
}
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) nullgo(Object s) null
before (String s): execution(void go(*)) && args(s) Stringbefore (Object s): execution(void go(*)) && args(s) Stringgo(Object s) String
Bytecode transformation - cont.
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 ALOAD_0 or none Same as this Local vars Entire code segment of methodMethod-call method ALOAD_0 or none From stack From stack Invokeinterface, invokespecial (only for privates),
invokestatic, invokevirtualConstructor-exec constructor ALOAD_0 Same as this Local vars Code segment of <init> after call to superConstructor-call constructor ALOAD_0 or none None From stack Invokespecial (plus some extra pieces)
Field-get Field ALOAD_0 or none From stack none Getfield or getstaticField-set Field ALOAD_0 or none From stack From stack Putfield or putstatic
Advice-execution None ALOAD_0 Same as this Local vars Code segment of corresponding method
Initialization Corresp. ALOAD_0 Same as this Complex Requires inlining of all constructors in a given class constructor into oneStatic-initialization Typename None None None Code segment of <clinit>Pre-initialization Corresp. None None Local vars Code segment of <init> before call to super,
constructor this may require in-lining
Exception-handler Typename ALOAD_0 or none None From stack Start is found from exception handler table. execution of exception (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
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
Transformed
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
0: getstatic [java/lang/System.out]3: ldc [String hello world]5: astore_16: astore_27: invokestatic [A.aspectOf]10: aload_211: invokevirtual [A.ajc$before$A$15a]14: aload_215: aload_116: invokevirtual [java/io/PrintStream.println]19: return
Transformed
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
0: invokestatic [A.ajc$if_0] # dynamic test3: ifeq 126: 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
Transformedbefore(): execution(void main(*))
&& if(Tracing.level == 1) {
System.out.println("got here");
}
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
astore_3 # arg2astore_4 # arg1astore_5 # targetaload_5 # repushaload_4 # repushaload_3 # repush
mungers needtarget, arg1, andarg2
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
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
aload_5 # targetinstanceof [Foo]ifeq end:invokestatic [A.aspectOf][push args to advice]invokevirtual [A.before379]end:
before withresidualtarget(Foo)
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
returngoto
weaving
after returning advice – cont.
then add residual and advice code to end of shadow
returngoto
advice
weaving
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_01: ifne 64: iconst_15: ireturn6: iload_07: iconst_18: isub9: invokestatic
[fact]12: iload_013: imul14: ireturn
static int fact(int);0: iload_01: ifne 84: iconst_15: goto 198: iload_09: iconst_110: isub11: invokestatic [fact]14: iload_015: imul16: goto 1919: dup20: istore_121: invokestatic [A.aspectOf]24: iload_125: invokevirtual
[A.ajc$afterReturning$A$ff]
28: ireturn
after() returning(int i):execution(int fact(int))
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
invokevirtual or
invokestatic
run
Closure837
weaving
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 – cont.
Improvements can be achieved by: Basic engineering work. improving the fastmatch algorithm
to handle more cases support incremental recompilation
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 hand-rolled 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
methodslog.entering(“<ClassName>”,
“<MethodName>”)
benchmarks
Performance of woven code - an unfortunate surprise
Logging enabled
L
benchmarks
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()); }}
Performance of woven code - an unfortunate surprise
Logging disabled 2900% overhead (!)
benchmarks
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()); }}
0
5
10
15
20
25
30
35
no logging hand-coded naïve AspectJ
Lo
gg
ing
Ove
rhea
d
Use Signature.getDeclaringClassName()
Performance of woven code - guarding with isLoggable()
22% over hand-coded
benchmarks
public aspect Trace { private static Logger log = Logger.getLogger(“xalan”); pointcut traced(): execution(* *(..)); before(): traced() { if (!log.isLoggable(Level.FINER)) return; Signature s = thisJoinPointStaticPart.getSignature(); log.entering( s.getDeclaringType().getName(), s.getname()); }}
0
0.2
0.4
0.6
0.8
1
1.2
1.4
no logging hand-coded AspectJloggable
Ove
rhea
d
Performance of woven code - using the if pointcut
8% over hand-coded
benchmarks
public aspect Trace { private static Logger log = Logger.getLogger(“xalan”); pointcut traced(): execution(* *(..)) && if (log.isLoggable(Level.FINER));
before(): traced() { Signature s = thisJoinPointStaticPart.getSignature(); log.entering( s.getDeclaringType().getName(), s.getname()); }}
0
0.2
0.4
0.6
0.8
1
1.2
1.4
no logging hand-coded AspectJloggable
AspectJif(loggable)
Ove
rhea
d
Performance of woven code - using a field
8% under hand-coded cross-cutting
optimization
benchmarks
public aspect Trace { private static Logger log = Logger.getLogger(“xalan”); static boolean enabled; pointcut traced(): execution(* *(..)) && if(enabled) && if(log.isLoggable(Level.FINER)); before(): traced() { Signature s = thisJoinPointStaticPart.getSignature(); log.entering( s.getDeclaringType().getName(), s.getname()); }}
0
0.2
0.4
0.6
0.8
1
1.2
1.4
no logging hand-coded AspectJloggable
AspectJif(loggable)
AspectJif(enabled)
Ove
rhea
d
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