With the help of two students, we developed COJAC (short for "Checking Overflows in JAva Code"), a freely available tool (http://home.hefr.ch/bapst/cojac) that instruments any existing Java bytecode for overflow detection. It takes as input any *.class file, and outputs an instrumented version that will detect overflows and signal them at runtime. Here are some features of COJAC:
- COJAC is a portable Java program with a command-line interface.
- Instrumentation is applied on individual classes or entire *.jar archives.
- Any type of arithmetic integer overflow can be processed.
- COJAC can also check when narrowing type casting (int2short, for example) leads to a data loss (such as when the original value is out of target type bound).
- The options offer a fine control over the particular bytecode instructions to instrument (six operations on ints, five on longs, four kinds of typecasting).
- COJAC lets you choose between four reporting policies: printing a message on the console (stderr), showing the location in the source code (with or without a detailed stacktrace); writing a similar message to a log file; throwing an ArithmeticException; and applying a user-defined reaction (the user indicates the name of an existing static method that will be used as an error reporting function (Example 4).
- It is possible to instrument only a single selected method, instead of the entire class.
<b> (a)</b> import java.util.HashSet; package logging; ... } } <b> (b)</b> prompt> java -jar cojac.jar -call logging/BetterLog/log Java2Demo.jar
The instrumentation of course introduces overhead, both in speed and space (bytecode size). Consider first how many bytes are added to the bytecode file. Internally, we do not add any new classes, but in any instrumented class we do add:
- Instructions in existing methods (a method call instead of an iadd).
- Some items in the constant pool.
- New methods, which are used for overflow reporting (unless a callback is given) and for overflow detection (unless the user chooses to inline the tests, which makes the code faster and often bigger).
For each instrumented class, the corresponding memory footprint of these additions typically amounts to a constant of less than 5KB, plus a couple of bytes for each arithmetic operation. To give an idea, when activating every possible check, the Java2Demo.jar program grows from 400KB to 625KB.
It is a bit harder to predict the slowdown incurred by COJAC, because it depends on factors like the hardware and OS, the JVM version and parameters (with/without JIT), or the proportion of integer arithmetic operations in the instrumented code. We measured the slowdown on a reasonable configuration for the four operators on ints. The overhead per arithmetic operation ranges from 20 percent (for idiv), to 950 percent (for imul). When inlining the detection method, this overhead is reduced and reaches between 10 percent (for idiv) and 580 percent (for iadd). As a typical Java program does other things than integer operations, we can expect that the runtime will roughly grow by a factor of two (as we observed with sorting algorithms). This is an important overhead, but similar to other means used for discovering bugs (think about using memory checkers like Valgrind in C/C++).