• Keine Ergebnisse gefunden

Current Implementation of Control Flow Quantifica- Quantifica-tionQuantifica-tion

Chapter 1 Overview

4.4 Control Flow Quantification

4.4.1 Current Implementation of Control Flow Quantifica- Quantifica-tionQuantifica-tion

There are different implementations of cflow in current AO execution envi-ronments, each addressing the following two issues:

1. At constituent join points, actions need to be taken to monitor the state of the control flow such that it becomes possible to determine at dependent shadows whether certain control flows are active.

2. At dependent shadows, the dispatch function depends on whether the control flow referred to by the cflow designator is currently active.

It is usually possible in AOP implementations to access context values of constituent join points, and to use such context information in advice attached to dependent join points. Therefore, in AspectWerkz, abcand ajc a stack is maintained which stores context values from the constituting join points. Control flow checks are implemented by testing whether the stack is

Control Flow Quantification

empty; in this case the control flow is not active. The context values can be accessed at a dependent shadow by reading the value from this stack.

When no access to context values from the constituent join point is re-quired, the abc and ajc compilers utilize a more efficient infrastructure for cflow. Instead of growing and shrinking stacks that represent the control flows, counters are incremented and decremented. Each cflow is assigned a counter and code that updates the counter is inserted at its entries and exits. When a control flow matched by acflow designator is entered, the cor-responding counter is incremented; it is decremented when the control flow is left. This is necessary because a control flow can be entered recursively.

Thus, a control flow matched by acflow designator is activated when a con-stituting join point is entered the first time and it is deactivated when the join point is exited the last time. At dependent shadows, the residue checks whether the counter is greater than zero. If so, the control flow is active and the dynamic property is satisfied.

Counters must exist once per thread for this approach to work; otherwise, different threads entering and leaving the same control flow would corrupt the counter states. To realize thread-locality, the ThreadLocal class from the Java class library is used to manage the counters.

The abc [ACH+05b, ACH+05a, ACH+04] compiler performs intra- and interprocedural static analyses to improve the performance of code using cflow pointcuts [SdM03].

Of the intraprocedural optimizations implemented in abc, only one is of further interest with regard to this section. Others deal with binding context values from constituent join points, which is out of the focus of this prototype. The interesting optimization is the one that ensures that counters are reused throughout a method [ACH+05b]. From the observation that retrieving a counter from thread-local storage can be expensive, the implementors of abc have derived the following optimization. Whenever a control flow counter is required several times in a method the counter is stored in a local variable and has to be retrieved from the thread-local storage only once. For example, at control flow constitution, the counter is updated via the local variable at entering and exiting the control flow; if multiple join point shadows appear in a method and depend on the same cflow dynamic property, or if a dependent shadow is executed in a loop, the counter only has to be retrieved from the local variable. Since local variables are implicitly thread-local, this optimization is obviously correct.

Furthermore, as a result of interprocedural analysis, abc can completely avoid weaving cflow infrastructure at some join point shadows. Due to its cost in terms of time and memory consumption, the interprocedural analysis is only activated at the highest optimization level which can be chosen by

Control Flow Quantification

the user when compiling AspectJ source code. The interprocedural analysis determines three sets of join point shadows for each pointcut expression con-taining a cflow designator. The computed sets are as follows for a pointcut of the form <staticProperties>&&cflow(<pointcut>).

1. The first set contains those shadows with the specified<staticProperties>

thatmay be executed in a control flow constituted by a join point that matches <pointcut>. At the shadows contained in this set, residual advice dispatch must be woven.

2. The second set contains shadows with the specified <staticProperties>

that are guaranteed to occuronly in a control flow constituted by join points matched by <pointcut>. At these shadows, advice invocation can be performed unconditionally.

3. In the third set, join points matching<pointcut>are contained thatmay influence the evaluation of residues at shadows in<staticProperties>. At the shadows of these join points, counter or stack maintenance must be woven.

Interprocedural analysis [ACH+05b] exploits a call graph of the entire application, which is why all classes reachable from the application’s entry points must be known at compile-time. That is, abc performs a whole-program analysis that needs to know all possible entry points and the class files for all classes reachable from there. This puts Java applications under a closed-world assumption, which contradicts Java’s dynamic class loading capabilities. If the virtual machine dynamically loads classes that are not known at compile-time, new execution paths may be possible due to late binding of method calls in Java. If this happens, the interprocedural analysis becomes unsound.

Another implementation approach for cflow is to inspect the call stack at dependent join point shadows. To determine whether the control flow is active, the stack frames are walked down one by one. For each frame, the method is matched against the constituent’s pointcut. If the match succeeds the current join point is executed in the desired control flow.

In Java, the call stack can be accessed by creating an instance ofThrowable, which can be queried for the stack frames via itsgetStackTracemethod. JAsCo [SVJ03], JBoss AOP [JBo], and Prose [PGA01] follow this approach.

The stack inspection approach does not require any infrastructural code at control flow entries and exits. Thus, there is no cost imposed at these points.

Also, no measures have to be taken for ensuring thread locality, because the call stack that a residue accesses isalways the one of the currently executing

Control Flow Quantification

thread. However, the cost imposed by stack walking at dependent shadows is high and not constant; it directly depends on the depth of the call stack.

In cases where the control flow is not active, the entire stack must be walked and inspected.

In the first version of Steamloom1 [Hau06, HMB+05, BHMO04]2, Haupt et al. have implemented the approaches based on counters respectively stack walking at the virtual machine level. Compared to implementations restricted to Java bytecode generation and using the functionality of the standard class library, the VM integration led to an improved performance of updating and querying thread-local values and of accessing the call stack respectively. This implementation has shown that stack walking is still not feasible even with virtual machine support. The counters at virtual machine level outperform the previous implementations in the multi-threaded case. In a single-threaded application it performs slightly worse than abc because neither inter- nor intraprocedural analyses are applied.