Generating TuningFork Traces from Java Applications

The TuningFork Java Trace Generation Library supports generating TuningFork traces from a Java program using any Java 5.0 JVM. The library only uses standard Java 5.0 APIs and does not require special JVM support or native libraries.

An example of an instrumented Java program is available for reference; you can cut and paste snippets from there to instrument your program. The example uses multiple threads producing different trace events.

Here we'll explain the basic principles behind Java-level generation of TuningFork traces and how to use the individual APIs.

Creating a Logger

In order to generate traces, you need to create a logger. A logger can either generate a file or output to a socket. To create a file logger, use
    ILogger logger = LoggerFactory.makeFileLogger(new File(filename));
where filename is the String with the filename. The generated trace can then be viewed in TuningFork using the "Open Trace" operation from the File menu.

To create a socket logger, use

    ILogger logger = LoggerFactory.makeServerLogger(portNumber);
where portNumber is the integer port number to use for the socket. The generated trace can then be viewed in TuningFork as the instrumented program is running by connecting to the trace socket with "Connect to a Trace" from the File menu.

There is also a logger that simply ignores all logging requests. This is useful so that you don't have to sprinkle if (LOGGING) conditionals throughout your code. You can simply decide at the beginning of execution whether you actually want to generate a trace. If you don't, create a null logger:

    ILogger logger = LoggerFactory.makeNullLogger();

Creating and Binding Feedlets

A feedlet is a stream of data within the trace. Feedlets are unsynchronized in order to keep tracing overhead as low as possible. Typically, you create one feedlet per thread of interest, although you can also share a feedlet across threads if you synchronize access to it. However, be aware that this synchronization can itself perturb the execution order of your program.

The TuningFork tool will take care of re-assembling the events in the feedlets in order, so you can use the feedlets without worrying about time synchronization between them. To create a feedlet, use

    IFeedlet myFeedlet = logger.makeFeedlet("My Feedlet", "Primary feedlet for my program");
where the first string parameter is the short name for the feedlet and the second one is a longer description. These names are purely for your convenience, so you can use anything you like.

You can add events to feedlets explicitly, but it's more convenient to associate a feedlet with a thread, which you can do with

    myFeedlet.bindToCurrentThread();
After that, if you log a trace event on that thread without specifying a feedlet, it will be placed in the feedlet you bound to the thread.

Creating Event Types

The trace library supports the generation of two different kinds of events: timers and values. Timers are used to mark the beginning and end of operations, for instance a phase of a program or the entry and exit of a method. Values record a single quantity at a point in time, for instance the size of a data structure or the current value of a control port. Events must be registered once and may then be used repeatedly throughout the execution of the program. Events are not specific to a feedlet, and can be used in any feedlet in the trace.

To create a timer event, use

   ITimerEvent phaseTimer = logger.makeTimerEvent("Phase of My Program");
The timer event can now be used to add events to the log. Similarly, to create a value event, use
   IValueEvent sizeEvent = logger.makeValueEvent("Size of My Primary Data Structure");

Value events record a single double value along with their timestamp.

Adding Events to the Log

Once you've created a logger, created one or more feedlets, and created some events, you're ready to add events to the trace. To add a value event to the log, you could use
    sizeEvent.addValue(myDataStructure.size());
where myDataStructure is some object whose size you want to monitor.

To add a timer event marking the beginning and end of some operation, you could use

    phaseTimer.start();
    myApplicationWork();
    phaseTimer.stop();
where myApplicationWork() is a method in your program whose timing you want to measure.

Properties

It is often useful to add information about the environment in which a trace was collected to the trace itself -- that way you don't have to try to remember that information or put it in a separate file. That's where properties come in. A trace property is simply a pair of strings, the property and its value. The data and time at which a trace is created is always added to the properties when a logger is created.

Properties should only be added once; they are not events. If you have some repeating occurrence that you want to log, use a value event and not a property.

For example, you could add information about the way the program was run:

    logger.addProperty("Command", args[0] + " " + args[1]);
where args is the String array passed to your main() function.