Skip to content

ResultsAccumulator

Jordan Samhi edited this page Oct 23, 2026 · 3 revisions

ResultsAccumulator

ResultsAccumulator stores caller-defined integer metrics. Create an independent instance with new ResultsAccumulator(), or use the instance returned by analysis.metrics() in a managed analysis session. Obtaining an accumulator does not calculate metrics automatically.

The deprecated ResultsAccumulator.v() remains a process-wide shared accumulator for compatibility. If using it across APKs, call reset() between them. Independent instances avoid that shared state.

Complete example

MetricsExample.java needs the AndroSpecter dependency but no initialized Soot scene:

import com.jordansamhi.androspecter.ResultsAccumulator;

/**
 * Command-line example of independent metrics, deterministic CSV values, and immutable snapshots.
 * The default constructor collects no metrics; invoke {@link #main(String[])} to run it.
 */
public final class MetricsExample {
    /**
     * Prints sample counts and demonstrates that resetting an accumulator leaves its snapshot unchanged.
     * This example needs no initialized Soot scene and does not open an APK.
     *
     * @param args command-line arguments, which are not used
     */
    public static void main(String[] args) {
        var results = new ResultsAccumulator();
        results.setAppName("example.apk");
        results.setMetric("edges", 0);
        results.setMetric("methods", 0);
        results.incrementMetric("methods");
        results.incrementMetric("edges");
        results.incrementMetric("edges");

        var snapshot = results.snapshot();
        System.out.println(snapshot.metrics()); // {edges=2, methods=1}
        System.out.println(results.getVectorResults()); // example.apk,2,1
        results.reset();
        System.out.println(snapshot.metrics()); // Still {edges=2, methods=1}
    }
}

incrementMetric(String) increments by one; there is no two-argument increment overload. Use setMetric(name, value) to assign a computed count. Missing metric names read as zero. Names and the application label must be non-null; the initial application label is empty.

API and output

Method Behavior
setAppName(name) / getAppName() Set/read the application label
incrementMetric(name) Increment an existing count or create it with value one
setMetric(name, value) Assign an integer count
getMetric(name) Read a count; return zero if absent
reset() Clear metrics and reset the application label to an empty string
snapshot() Copy the label and metrics into an immutable detached Snapshot
getVectorResults() Return the CSV-escaped application label followed by counts in alphabetical metric-name order
printVectorResults() Log that vector at SLF4J INFO
printResults() Log the application label and sorted metrics at SLF4J INFO

A vector has no header. Use the same metric names for every row, initializing absent categories to zero, or generate a schema/header explicitly from your chosen metric set. Alphabetical ordering is deterministic for a fixed set of names, but rows with different metric sets have different columns. Application labels containing commas, quotes, or line breaks are CSV-escaped.

The print methods use the application's SLF4J provider and configuration; they do not guarantee output on System.out. Use the returned string when writing a file yourself. See Cancellation and logging for logging setup.

Isolation and lifetime

Accumulator methods are synchronized, so individual increments and snapshots are safe against concurrent calls. Multiple method calls do not form a transaction: a caller-managed read/modify/write sequence is not atomic. Metrics use Java int values; choose another representation in your own application if counts require a larger range.

Snapshot.appName() and Snapshot.metrics() are detached. The metrics map is an unmodifiable copy sorted by name, so later increments, resets, session closure, or Soot resets cannot change it. A snapshot contains no Soot objects and can be serialized by the application's chosen library. The snapshot does not include an analysis UUID; store analysis.id() beside it when that association is needed.

Source: ResultsAccumulator.java.

Clone this wiki locally