This work was done at the Chair of Computer Science 2, at RWTH Aachen University.
June 5th, 2006
March 3rd, 2005
November 5th, 2005
November 2nd, 2005
October 28th, 2005
This syntax is meant not for annotation-based deployment but rather for using J-LO functionality from within AspectJ. Therefore it is similar to the one of tracematches. Formulae can then be written in the following form:
tracecheck(Singleton s1, Singleton s2) {Free variables s1, s2 are declared in the tracecheck header. Within the tracecheck decleration then a nonempty list of symbol declaration follows, each symbol holding a pointcut declaration and an entry/exit kind before,after... The formula can then reference those declared symbols. The tracecheck body is executed once for all instances which violate the formula.
sym create(Singleton s) after returning (s):
call(static Singleton Singleton+.inst());
sym createAnother(Singleton s, t) after returning (s):
call(static Singleton Singleton+.inst()) && if(s!=t);
G(create(s1) -> XG !createAnother(s2,s1) ) {
throw new SpecViolationException
("two singletons detected:"+s1+","+s2);
}
}
Since Eric Bodden is joining the abc group in January, this AspectJ language extension is likely to be merged with the main abc distribution within the next year.
J-LO, the Java Logical Observer, is a tool for runtime-checking temporal assertions in Java 5 applications. Temporal properties (see below) can be specified using a linear time logic (LTL) over AspectJ pointcuts. Specification takes place right in the source code in the form of Java 5 annotations. After compiling this source code with a Java 5 compliant compiler, Java extracts those annotations from the bytecode and, at the same time, instruments the application with the appropriate runtime checks.
The work is based on a Haskell-prototype developed at our department.
J-Lo is being delevoped by Eric Bodden and Volker Stolz.
* J-LO comes with a small runtime library that contains classes necessary for the runtime verification process. This class library is compiled to Java 5 bytecode at the current time. Thus you will need a Java 5 environment to run your instrumented application in.
If you require a Java 1.4 compatible version, please let us know as we can provide one based on the retroweaver and backport 175 (thanks to Toby Reyelts, Jonas Boner and Alexandre Vasseur for providing those).
At design time...
... software architects usually define temporal interdependencies (TID) between events in the lifecycle of an application. Such TIDs may also be a result of the way certain objects have to interact, a result of the contract, classes adhere to by implementing a certain interface for instance. One such requirement could for instance be that each object of a type T should have its tearDown method called before it is garbage collected and no method is called afterwards on this object. The software architect should formulate those requirements in a formal way, best in LTL.
At implementation time...
... the programmers annotate their source code with LTL formulae stating the requirements as defined in the above process. They use J-LO as an additional tool in their build chain to instument the application under test.
At testing time...
... the instrumented application will emit an error message whenever one of the temporal assertions is falsified or verified. Formulae that could not be falsified or verified by the time the virtual machine shuts down will be reported for manual inspection.
At deployment time...
... J-LO is simply removed from the build chain. This means that no instrumentation will take place. The original bytecode is not touched, No performance overhead is generated.
In the following we require that the reader is familiar with the terminology of pointcuts and join points. A definition can be found in the AOSD Wiki. Also we require basic knowledge of LTL. You might find the Specification Patterns project helpful.
Our formalism consists of two key components:
We provide the following LTL operators:
Syntax | Arity | Semantics |
X | 1 | LTL operator Next * |
F | 1 | LTL operator Finally |
G | 1 | LTL operator Globally |
U | 2 | LTL operator Until |
R | 2 | LTL operator Release |
! | 1 | logical not |
&& | 2 | logical and |
|| | 2 | logical or |
-> | 2 | logical implies: f1 -> f2 = !f1 || f2 |
<-> | 2 | logical if and only if: f1 <-> f2 = (f1 -> f2) && (f2 -> f1) |
We allow X only to occur in front of another temporal operator. This is a deliberate restriction because we believe that users should not be able to reason about the next join point in a system because those semantics can easily break for a number of reasons.
At the moment, also we require full bracketing for all operators. This is going to change in future revisions.
The entry and exit of any valid AspectJ 1.2 pointcut is a valid propositions for our logic, so for example:
entry( call( public static void *.main(String[]) ) )
This captures the event of entering the call join point for an arbitrary main method.
Exits of join point can also expose the returned object or thrown exception (if any). Thus the complete syntax for propositions is:
Proposition -> EntryProposition | ExitProposition EntryProposition -> entry( AspectJPointcut ) ExitProposition -> exit( AspectJPointcut ) [ ReturningDeclaration | ThrowingDeclaration ] ReturningDeclaration -> returning Identifier ThrowingDeclaration -> throwing Identifier |
The identifier can either be the (fully qualified) name of a type or the name of a free variable declared in the formula.
Formulae can contain free variables in the same way as AspectJ pointcuts do. Unlike AspectJ though, variables in if-pointcuts can refer to variables bound earlier in the execution history. Those variables have to be declared at the beginning of each formula for type safety.
As an example we want to use the statement from above:
Each object of a type T should have its tearDown method called before it is garbage collected and no method is called afterwards on this object.
Here we somehow have to quantify over all objects of type T. Thinking in temporal terms, all objects of this type have certainly in common that they have been created some time. Thus we reformulate:
For each object of a type T, after a constructor has been called, its tearDown method is called before it is garbage collected and no method is called afterwards on this object.
This leads us to the following formula:
T t1, T t2:
G(
(
exit( call( T.new(..) ) ) returning t1
) -> (
F(
(
entry( call( * T.tearDown() ) && target(t2) && if( t1==t2 ) )
) && (
G(
!(
entry( call( * T.*() ) && target(t2) && if( t1==t2 ) )
)
)
)
)
)
)
Here, parts of the LTL syntax are colored in blue, parts of AspectJ in purple and black.
Important are the free variables t1 and t2. The former is bound by the first join point in the formula that matches (here naturally any join point picked up by the pointcut call( T.new(..) )). For subsequent join points, this t1 is bound, which means that it can be reffered to, as it is done in the two if pointcuts.
Such if pointcuts can refer to bound variables as well as static members of any class.
Annotating the Java code is the easiest part: Just paste the formula in String form in an appropriate annotation.
If the annotation is put at a reasonable position, two special keywords thisType and thisMember may be used. The former is substituted by the name of the surrounding type/class, the latter is substituted by the complete signature for the member the annotation is put on.
Hence, the above example could look as follows:
import rwth.i2.ltlrv.LTL;
public class T {
@LTL("
T t1, T t2:
G(
(
exit( call( thisType.new(..) ) ) returning t1
) -> (
F(
(
entry( call( thisMember ) && target(t2) && if( t1==t2 ) )
) && (
G(
!(
entry( call( * thisType.*() ) && target(t2) && if( t1==t2 ) )
)
)
)
)
)
)
")
public void tearDown() {
//code for tearDown
}
//code for other members
}
Temporal assertions are a simple String using the following annotation type:
@Retention(CLASS)
@Target({CONSTRUCTOR, FIELD, METHOD, TYPE})
public @interface LTL {
String value();
}
The annotation type itself is annotated with tags stating that this annotation is to be retained in the bytecode, so that J-Lo can process it, (however not at runtime of the application) and that the annotation can be used at constructor, field, method, and type declarations.
J-LO is a commandline based tool, an extension of the popular abc compiler for AspectJ.
You can run J-LO directly from the JAR file:
java -jar jlo.jar -help
This will give you a verbose list of all available options, some of them specific to the underlying abc. All options necessary to operate J-LO are isted here:
Input (see also here) | |
-sourceroots <path> | Use .java files in dirs in <path> as source. Those files will be instrumented by J-LO, however no formulae will be read from those files. |
-injars <jar list> | Use class files from the jars in <jar list> as source. Those files will be instrumented by J-LO, however no formulae will be read from those files. |
-inpath <dir list> | Use class files found in the directories in <dir list> as source. Those files will be instrumented by J-LO, however no formulae will be read from those files. |
-ltlpath <dir list> | Use .class files in dirs in <ltl path> to extract ltl annotations. Annotations in those classes will be processed. Those classes however will not necessarily be instrumented. If you wish to do so, put them on the inpath as well. |
-cp <classpath> |
Specify the class path to be used when searching for libraries. Those files will neither be instrumented nor annotations read from. |
Output | |
-dava | Decompile instead of outputting classes. |
-outjar <jar> | Write output class files into a jar file. |
-d <path> | Write output class files into a directory. |
So a typical usage could look as the following:
java -jar jlo.jar -cp
mylib.jar;myotherlib.jar -ltlpath spec -inpath bin -outjar
instrumented.jar
This would instrument all classes in "bin",
extracting formulae from class files in "spec", loading libraries from
"mylib.jar" and "myotherlib.jar" and producing the output in
"instrumented.jar".
This is a collection of examples for download that we have used in practice together with a short program demonstrating the behaviour. All those examples are contained in the thesis examples packages (see below) along with build scripts to process them.
For each Iterator i obtained from a Collection c, there must never be an invocation of i.next() after the collection has been modified.[Java API docs]
For each HashSet s that contains a Collection c, there must be no invocation of s.contains(c) if the collection has been modified, unless the collection has been removed from the set in between.This example also demonstrates that aliasing is no problem for the analysis.
Variant of the Safe Iterator-pattern causing a fault in JHotDraw. Since instrumentation is a bit more involved here, we provide an already instrumented JAR-file. Start it with:
java -cp ../jlo-rt.jar:jhotdraw-debug.jar CH.ifa.draw.samples.javadraw.JavaDrawApp.
Create a new figure, draw two figures, group them and then start the animation. While the application is running, select the grouped objects and ungroup them. On the console, you should see a message from J-LO stating the violation of the formula. (The unmodified JAR is also required since it contains some images that somehow don't survive the weaving process.)
Those examples were published with the diploma thesis. They are a variant of the Iterator and HashSet case (see above) and also contain a formula for detecting lock order reversals. Instructions for how to compile and run the examples as well as a full J-LO distribution are included.
Please do not hesitate to contact us if the instructions need to be more detailed!
J-LO comes with a default console-listener, which reports
verification
results to the console. It is possible to plug in custom listeners by
setting the LTLRV_LISTENERS environment variable to a
semicolon-separated list classes implementing the interface rwth.i2.ltlrv.management.VerificationRuntime.Listener.
Example: java -DLTLRV_LISTENERS=listeners.L1;listeners.L2
-jar jlo.jar ...
Here are some frequently asked questions:
Tracechecks: Defining semantinc interfaces with temporal logic, E.Bodden, V.Stolz. Fifth Intl. Symp. on Software Composition (SC 2006), to appear in LNCS.
This jar file contains the J-LO runtime library. This code has to
be linked with your instrumented application (included in the
CLASSPATH) in order to get the verification working.
Download here J-LO runtime library, version 0.9.2.
You can find the log of previous changes here.
The following API-Docs are available:
Acknowledgements go to a lot of people including the whole abc development team, especially Oege de Moor who helped quite a lot with debugging our implementation. Also we are grateful to all the people who attended the "perobjects BOF" at the AOSD conference, which ended in a very fruitful discussion about the implicit instantiation and bookkeeping of aspects. We wish to thank the many reviewers involved with the above papers as well as all the people who commented on J-LO during the various presentations. Much gratitude goes to Adrian Colyer for picking up the idea of aspects as automaton. We thank the team of the TU Darmstadt for providing BAT2XML, which we use for annotation extraction.