ReCrash: Making software failures reproducible by preserving object states
It is very hard to fix a software failure without being able to reproduce it. However, reproducing a failure is often difficult and time-consuming. This paper proposes a novel technique, ReCrash, that generates multiple unit tests that reproduce a given program failure. During every execution of the target program, ReCrash stores partial copies of method arguments in memory. If the program fails (e.g., crashes), ReCrash uses the saved information to create unit tests reproducing the failure.
We present ReCrashJ, an implementation of ReCrash for Java. ReCrashJ reproduced real crashes from Javac, SVNKit, Eclipsec, and BST. ReCrashJ is efficient, incurring 13%--64% performance overhead. If this overhead is unacceptable, then ReCrashJ has another mode that has negligible overhead until a crash occurs and 0%--1.7% overhead until the crash occurs for a second time, at which point the test cases are generated.
Contents:
Download
Publication
-
"ReCrash: Making software failures reproducible by preserving object states"
by Shay Artzi, Sunghun Kim, and Michael D. Ernst.
In ECOOP 2008, Object-Oriented Programming, 22nd European Conference, (Paphos, Cyprus), July 9-11, 2008.
[PDF]
ReCrash HOWTO
This section shows how to use ReCrash.
- Sample Java Program
Suppose we have an example Java program, crash/CrashExample.java:
package crash;
import java.util.Random;
public class CrashExample {
public static int abs(Integer i) {
Integer ret = null;
if (i < 0) {
ret = -i;
} else if (i > 0) {
ret = i;
}
return ret.intValue();
}
public static void main(String args[]) {
try {
CrashExample crash = new CrashExample();
abs(rand());
System.out.println("No crash this time");
} catch(Exception e) {
e.printStackTrace();
}
}
/** Returns a random int between -2 and 2, inclusive. */
public static int rand() {
return new Random().nextInt(5)-2;
}
} |
-
Compile the program.
javac crash/CrashExample.java
|
then run it:
You may need to run it many times, because it only crashes some of the time.
- Optional: Annotate Exception Handler: "reCrash with".equals(e);
ReCrash automatically captures any uncaught exceptions in the main method,
so typically you do not have to change your source code in any way.
However, if you have your own exception handler (in any method) that
prevents the main method from throwing an error, you need to annotate
which exceptions ReCrash should try to reproduce.
You do so by adding the statement
"reCrash with".equals(e); (where e is the
exception to reproduce.
This does not have any side-effect on your program and does not require
any additional jar files to compile or run.
For example, because the CrashExample program above never throws an exception, you would change it as follows:
...
} catch(Exception e) {
"reCrash with".equals(e);
e.printStack();
}
...
|
Now, compile CrashExample.java as usual.
javac crash/CrashExample.java
|
You can also run it to confirm that the program behaves just as before.
- Using ReCrash in the Development Mode
If you are a developer and want to use ReCrash in your development process, ReCrash provides the javaagent mode. Run your application as usual and add only the -javaagent option with reCrash.jar
. For example, to run CrashExample:
java -javaagent:reCrash.jar crash.CrashExample
|
- Using ReCrash in the Deploying Mode
If you want to distribute ReCrash enabled (instrumented) class file(s),
ReCrash can automatically generate instrumented class file(s) for you.
To instrument CrashExample.class:
java -jar reCrash.jar crash/CrashExample.class trans/crash/CrashExample.class
|
To instrument an entire jar file (yourProgram.jar):
java -jar reCrash.jar yourProgram.jar transformedProgram.jar
|
Then users download and run the instrumented CrashExample.class instead of the original one.
To run the instrumented class file(s), you need reCrash.jar
in your class path.
When you distribute the instrumented class files, you need to include reCrash.jar
with the instrumented class file(s).
For example, you could run
java -jar reCrash.jar crash/CrashExample.class trans/crash/CrashExample.class
java -cp trans:reCrash.jar crash.CrashExample
|
(repeating the last command until it produces test cases).
- Reproducing crashes by running generated Test Cases
If the subject program crashes (any uncaught exception happens or the
program reaches the annotated location, "reCrash with".equals(e)) in the
ReCrash development or deploying mode, ReCrash will generate test cases.
The test cases are printed to standard output, but they are also saved in
directory /tmp/generated_tests/, and it is easier for you to run
them from there.
An example generated test case is:
public class Recrash_reCrash37419_trace_gz extends TestCase {
public void setUp() throws Exception {
TraceReader.readTrace("/tmp/reCrash37419.trace.gz");
}
public void test_crash_CrashExample_abs_1() throws Throwable {
TraceReader.setMethodTraceItem(1);
crash.CrashExample thisObject = (crash.CrashExample) TraceReader.readObject(0);
// load arguments
Integer arg_1 = 0;
// Method invocation
thisObject.abs(arg_1);
}
} |
To compile and run the generated test cases, you need
reCrash.jar
and your subject program in your class path, as
shown here (you will need to change the number "37419"):
javac -cp $CLASSPATH:reCrash.jar:. /tmp/generated_tests/Recrash_reCrash37419_trace_gz.java
java -cp $CLASSPATH:reCrash.jar:.:/tmp junit.textui.TestRunner generated_tests.Recrash_reCrash37419_trace_gz
|
Limitations
ReCrash cannot reproduce errors in the default package (sometimes called the
top-level package). This is due to a change in Java 1.4 that makes it
impossible to access the default package from any non-default package.
Good Java style discourages the use of the default package.
Contributions
You can contribute to ReCrash by sending bug reports, code patches, and suggestions.
Use the public mailing list for ReCrash bug report or suggestions:
recrash@googlegroups.com.
You can subscribe and view the mail archive at
http://groups.google.com/group/recrash.
People
Last updated: April 13, 2009