Profiling Support in JastAdd

JastAdd supports profiling with tracing of evaluation events, such as ''an attribute value was cached'', or ''an attribute computation was started''. By tracing these events, the evaluation behavior can be analyzed and improved. To turn on tracing give JastAdd the following flag:

$ ./jastadd2 --tracing ...

With tracing turned on, JastAdd generates a Tracer class collecting events from event-generating code weaved into AST node classes. For each event, the tracer collects information about the context of the event, for example, attribute name, AST node type etc. (see Tracer.TraceObject for details).

Overview of Trace Events

By default, all of the following events are traced (listed in Tracer.Event):

The above event category names (compute,cache,rewrite,circular,copy) can be given to the --tracing flag as a comma separated list value. This will limit tracing to events in those categories. That is, if no value is given to the flag it corresponds to the following:

$ ./jastadd2 --tracing=compute,cache,rewrite,circular,copy ...

Analyzing the Trace Result

During tracing the tracer class collects a list of trace events. After evaluation this list is available for processing, or filtering and printing. The entire trace can be printed for inspection using the Tracer.printTrace(PrintWriter) method. (Beware that a trace list can become quite large even for small examples.)


The tracing result can be analyzed by giving the Tracer.process method a subclass of Tracer.Processor after evaluation has finished. This trace processor will get a call to a process method for each trace object collected by the tracer.

Filtering and Printing

The tracer class provides a couple of sample methods printing reports generated from collected trace events. For instance,

Both these methods use the Tracer.printReport(PrintWriter,Tracer.Filter) method to print a report with a filter they provide. Customized reports on the same format can easily be created by calling this method directly and providing it with a custom trace filter implementing Tracer.Filter.

Customizing the Trace Input Filter

The tracer uses an input filter (Tracer.InputFilter) to decide which events to include in its internal trace object list. This default input filter includes all events. A user wishing to limit the number of trace events that are included in this internal list, or who wants to collect data in a different structure, can register a customized input filter. A custom input filter is registered via the Tracer.registerInputFilter(Trace.InputFilter) method and should typically be registered before attribute evaluation starts.

For example, the following input filter corresponds to the following flag --tracing=compute,copy:

class CustomInputFilter extends Tracer.InputFilter {
    public boolean include(Tracer.TraceObject obj) {
        return obj.event == Tracer.Event.COMPUTE_BEGIN ||
            obj.event == Tracer.Event.COMPUTE_END ||
            obj.event == Tracer.Event.COPY_NODE;

Note that by using an input filter rather than a static flag configuration filtering is done during runtime. That is, all trace events are generated, but only those included by the input filter are stored in the trace object list.

Cache Analysis

The presented profiling approach can be used to analyze and optimize cache usage, as described in:

The approach presented in the above paper is supported by the --cache=analyze flag. That is, JastAdd supports a cache flag which takes a value as follows:

$ ./jastadd2 --cache=[all|none|config|implicit|analyze] ...

The first two flags (all,none) provides a global cache configuration to JastAdd which overrides all local attribute configurations using the lazy keyword. The second two flags (config,implicit) instructs JastAdd to collect a cache configuration from a .config file. A .config file has the following format:

(('cache'|'uncache')' 'NodeType'.'AttributeName'('(ParamType(','ParamType)*)?');')*

With the config value a .config file takes precedence and lazy is ignored. For the implicit keyword a .config file also takes precedence but with the starting point that all attributes are cached.

Finally, the analyze value instructs JastAdd to analyze the cache behavior. This flag corresponds to the following flag configuration:

$ ./jastadd2 --cache=all --tracing=cache ...

Where the tracer class uses a custom input filter (CacheAnalyzer), which JastAdd generates and registers when the analyze option is given. The cache analyzer is available via the tracer method Tracer.cacheAnalyzer().

After attribute evaluation, the cache analyzer class provides cache configurations, via the printAllOneConfig(PrintWriter) and printUsedOneConfig(PrintWriter) methods. These methods use the following three sets to compute a configuration:

The name of each method describes how it uses the above sets. For instance, printAllOneConfig returns a configuration where the One set has been subtracted from the All set. The All-One configuration is more conservative than the Used-One configuration. The latter configuration may use less memory but depends on the attribute coverage of the profiling input. That is, the percentage of all attributes that were used during tracing. The attribute coverage is available via the CacheAnalyzer.getAttributeCoverage() method.

To use the result of the cache analysis, give the cache configuration to JastAdd as a .config file together with the --cache=config flag.