Programs can be analyzed offline by inspecting the source code. Alternatively, programs can be analyzed online by inserting code in the application to perform the analysis while the program is running. The program is said to be instrumented. An example is a classic debugger which inserts special code at breakpoints to pause the program. The goal of this assignment is to explore and implement several instrumentations.
Instrumentation of arbitrary native programs (say written in C or C++) requires a lot of knowledge about a specific platform and instruction set. We abstract from this by using aspect-oriented programming to weave in code, and use Java as it is slightly easier to instrument.
The assignment consist of 8 small exercises. The grading is determined as follows. You earn an 8.0 for a properly documented, properly structured, and reasonably correct submission. You get higher grades by doing more work: more extensive discussions of design choices, more features, doing some of the optional exercises, etc. On the other hand, omitting an exercise leads to a loss of 1.0 point.
Work on this assignment in pairs and work on each individual exercise together.
Several tools are needed in order to do this assignment: a recent Java virtual machine and AspectJ
, the aspect-oriented Java compiler. Furthermore, you'll need to learn the basics of the AspectJ
language and some of the Java reflection API's.
A recent Java virtual machine is already installed on all Windows and Linux student machines. If you do this exercise at home, make sure you use version 1.6 or later (or Java 1.5 in combination with the Eclipse AspectJ
plugin on the lab machines). Familiarize yourself with using Java from the commandline.
For this exercise we use the aspect-oriented programming extensions of AspectJ
and the AspectJ
compiler (ajc) to compile the Java source files. Download AspectJ
Download the appropriate .jar file and run it with java -jar jarfile. A graphical installer will appear. Use the default options, which installs AspectJ
in your home directory (~/aspectj1.5). Add the appropriate paths to your PATH and CLASSPATH environment variables, for example:
You should then be able to run it:
> ajc -version
AspectJ Compiler 1.5.3 built on Wednesday Nov 22, 2006 at 11:18:15 GMT
You can use Eclipse to help you develop AspectJ
programs. If you use Eclipse, be sure to verify that your code also works when using AspectJ
from the commandline.
Make sure you understand the basics of AspectJ
before proceeding with the exercise. Read the Programming Guide
and scan through the Quick Reference
on the documentation page: http://www.eclipse.org/aspectj/docs.php
. There is some additional material available there as well.
Experiment a bit with pointcuts and advice until you think that you have a basic understanding of what these things are and how you can use them.
read this section thoroughly!
We are going to implement several instrumentations. For each instrumentation you'll have to:
- Write some aspects that perform the analysis and produces a report of the analysis when the application closes.
- Write example program(s) to test your aspects.
- Write how to compile the programs and how to run them.
- Appropriate documentation. Discuss design decisions and possible problems that can arise when you use your aspects on real-world programs.
- You are allowed to implement each assignment on its own, disregarding the other assignments. If that requires special build or run instructions, make sure to document it!
- Some of the exercises require a written answer. Give brief but meaningful answers.
- Watch out with multi-threading! Use the appropriate protections. If needed, you may assume that all threads are created as an instance of Runnable.
- The outcome of an instrumentation needs to be some kind of report. Determine for yourself if you want to report to the console (using System.out.println), or to a html/xml-file, or LaTeX-file, or some other reasonable way. If you choose to report to the console, make sure that the output of the instrumentation is not interleaved with the output of the original program.
- Note that the static main-method may return before the last thread has stopped executing. So, printing the instrumentation report as a last statement of the main-method is not a good idea, because it is then possible that the report is printed too early. There are a number of solutions to this problem, for example, using AspectJ to catch the termination of the last thread.
- Take a look at the Java reflection APIs, and information that can be obtained from the ClassLoader.
- Consider searching the internet for example aspects and example introspections of Java code. Do not blindly copy&paste source code though (and at least give a reference to the original source).
- Java classes of interest: Runtime and ClassLoader.
1: Implementation of hashCode() matches equals()
Java programmers often override the equals()
method of Object, but do not provide a proper implementation of hashCode()
(See "Implementing equals"
). Your task is to write an instrumentation of a Java program that generates a report containing the names of the Java classes that do not have a correct equals()/hashCode() combination. See the class documentation for the properties that equals() and hashCode() need to satisfy.
For example, given the class:
public boolean equals(Object o)
public class Main
public static void main(String args) throws Exception
WrongHashcode w = new WrongHashcode();
Object other = new Object();
The output is for example:
> java Main
Invalid implementations of hashCode():
2: All sockets closed
Java is a garbage-collected language. This means that unused resources allocated by a program are eventually released automatically. However, while this works fine for memory, this approach is not as suitable for resources such as sockets, files and database connections, because it can take a while before the garbage collector decides to clean them up, among other reasons. Therefore, programmers explicitly release these kind of resources when they are no longer needed.
The task is to instrument a Java program that uses Sockets, such that those sockets are reported that are opened but not closed. Include some information about the socket (such as to where it is connected to) and the line number and filename where the socket is created.
Consider the following points:
- What to do if the program terminates early (for example due to some unhandled exceptions)
- Can the garbage collector influence your instrumentation?
Answer the following question:
- Can you generalize your approach to files and database connections? You do not have to implement this, but give a sketch and discuss potential caveats.
3: Notify for each wait
Java offers a wait/notify construction for synchronization in a distributed system. Problems arise when there are more waits than notifies. This problem appears to be similar to the previous exercise. Yet, if you try to implement it, you'll notice some problems:
- Describe these problems (i.e. why instrumentation does not help you in order to detect the above situation)
- Describe instrumentations that would help you with debugging when you notice that the waits and notifies are not properly balanced.
- (Optional) discuss how you can use instrumentation to detect situations where there is not enough synchronization (i.e. conflicting field accesses)
4: Direct recursion
Create an instrumentation that lists methods that are directly recursive. A method is said to be directly recursive if the first method call is a call to itself. Can you think of an example where this is hard to analyze statically?
5: Method coverage
Code coverage measurement is important in combination with automated tests. Instead of lines of code we are taking a slightly easier approach and are going to instrument a program such that in the end we know which methods are not executed during the execution of the program. The output could be (for example):
- Use thisJoinPoint to get information about the context of a join point.
- Query the system class loader for class/method information.
- Can you filter out unused methods from system libraries?
- It is not trivial to list all the classes in Java. You have to find (or build) a class/function that does this for you. You can use something from the web, just give a reference.
6: Profiling methods
We are now going to write a simple profiler. The task is to determine for each of your classes how much time and memory the methods take (in total).
- Think about the influence of the garbage collector.
- Use the System and Runtime classes to obtain information about the current time and the memory in use by the process.
Answer the following questions:
- Which concessions did you have to make? How accurate are the numbers produced by your instrumentation?
- (Optional) Determine a call-tree of the program. Give time and memory usage on each of the nodes of the call-tree.
- (Optional) Can you also compute the time spent in a method without considering the time spend in sub methods?
A typical debugger has breakpoints and watches. A breakpoint pauses the program when a certain line of code is executed. A watch pauses the program when a variable is assigned a certain value. Now the task is to create an instrumentation that records the source locations where a numeric variable of your choosing is assigned a value in the range 10..3000. Then, generalize this approach such that the user can configure the object, variable and range.
8: Discuss combining instrumentations
You were allowed to implement each of the instrumentations individually. Now, suppose that you want to use all the instrumentations together. Discuss:
- How this could be done
- How to resolve interference between instrumentations
Keep the text limited to at most one A4 paper (excluding examples).
What to hand in
- Source code (both aspects and sample programs)
- Compilation and running instructions. To simplify matters for us:
You do not need to hand in the compiled class files. However, if you use additional libraries for some reason, make sure to include them or to provide a download link.
- 25 Sep 2007