Skip to content
Eric Bodden edited this page Mar 27, 2015 · 3 revisions

ICSE 2011 Presentation

Download out ICSE presentation here as Keynote or PDF file. You may use it under the Creative Commons “Attribution-ShareAlike” license.

http://i.creativecommons.org/l/by-sa/3.0/88x31.png

Overview

The following figure gives an overview of the architecture of TamiFlex, our tool suite for taming reflection. On the top left, we show a program that uses (potentially custom) class loaders to load classes from arbitrary locations (the cloud), or even to generate classes on the fly. The program may further call methods such as Class.forName(), Constructor.newInstance() or Method.invoke() to construct objects or invoke methods through reflection.

http://wiki.tamiflex.googlecode.com/hg/images/overview.png

Playing out: logging reflective calls and dumping class files

Let us now assume that the program executes with our first instrumentation agent installed: the Play-out Agent, which the figure shows below the program. In this agent, the Tracer, a class-file transformer, instruments the classes Class, Method and Constructor so that calls to methods such as Class.forName() generate entries in a log file (shown on the bottom left). The agent further comprises a Dumper component, which writes all classes loaded by the program, including classes that the program's class loaders may have generated on the fly, to a local repository. Certain class loaders assign randomized names to such classes. To be able to re-identify such classes across multiple runs, the Dumper renames these classes using a hash code over the contents of the class. The Dumper communicates with a Hasher component to obtain these hash codes.

Executing a program with the Play-out Agent enabled will result in an repository containing a reflection log file and all classes that the program loaded during the observed run. To obtain a reasonably complete log file and set of classes, users can run the program multiple times. The agent will then automatically update the log, appending information about reflective calls that were not previously observed, and dump additional classes that had not been loaded on previous runs. Users can repeat this process until reaching a fixed point.

Static analysis and offline class transformation

The user has now two options to further proceed with the classes gathered by the Play-Out Agent:

  1. use a specialized, TamiFlex-aware static analysis (shown in gray), or
  2. use the Booster in combination with virtually any static-analysis tool for Java bytecode.

Option 1: Specialized static analysis

Users can feed the log file and the dumped classes into into some TamiFlex-aware static-analysis tool to conduct static analyses, and to transform, e.g., optimize or instrument, the code. We use Soot with Spark for this purpose. Running Soot results in a set of transformed class files that we show on the bottom right of the figure.

Option 2: Booster in combination with any static analysis

Alternatively, users can use the Booster to "enrich" the program. Boosting will prepare the program for static analysis by "materializing" reflective method calls into normal Java method calls in the program's bytecode. (More information here.) This will allow virtually any static analysis tool to treat all recorded method calls just as normal method calls, hence improving the soundness of those analyses.

The Booster is part of TamiFlex since version 1.1.

Playing in transformed classes

The right-hand side of the figure shows what happens when the user runs the program with the second agent, the Play-in Agent, enabled. Whenever the original program is about to load a class c, a Replacer within the agent tries to retrieve the offline-transformed version of c from the local repository. For classes that bear a randomly generated class name, the agent asks the Hasher component compute the hash code for the class. The Replacer then looks for the offline-transformed class under the same normalized, i.e., hash-code based, name that the Dumper would have used to store the class. If the Replacer finds a class in the repository, replaces the originally loaded (or generated) class with this loaded class on the fly. Otherwise, i.e., if the Replacer cannot find an appropriate class file, for instance because no such class was loaded on previous runs, the Replacer executes no replacement. This means that in this case the program will instantiate the class that the class loader originally loaded from ``the cloud''. Through a command-line option to the Play-in Agent, users can opt to have a warning message issued when such a situation occurs.

Note the flexibility of this design; TamiFlex works with any Java 6-compatible virtual machine that supports re-transforming classes through the java.lang.instrument application programming interface. Through this interface, our agents are able to write out and replace classes that the program generates at runtime. With the aid of our Hasher component, this even works in cases where the program generates classes with randomized names. Further, we pose no special restrictions on the static-analysis component (here Soot/Spark), except that the analysis must be able to load class files from disk, to write class files to disk and to correctly interpret the reflection log file that TamiFlex generates.