Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
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