Tracing as a Service

Spencer is an online service that lets you analyse large program traces of programs running on the JVM using custom queries. Queries in spencer are composable: If you have one analysis, you are able to refine it using others.

Queries

Queries in Spencer are expressions that return a set of object IDs. They implement a test: does an object's usage fulfill a certain definition or not?

Spencer distinguishes between primitive queries and composed queries. Primitive queries are the basic building blocks that are implemented "natively" in the backend of the service.

The primitive queries are:

Query Meaning
MutableObj()
All objects that are changed outside their constructor.
ImmutableObj()
All objects that are never changed outside their constructor.
UniqueObj()
All objects that are never aliased.
HeapUniqueObj()
All objects that are never aliased.
TinyObj()
All objects that do not have or do not use reference type fields.
StackBoundObj()
All objects that are never aliased.
AgeOrderedObj()
All objects that are only holding field references to objects created before them.
ReverseAgeOrderedObj()
All objects that are only holding field references to objects created after them.
InstanceOf(java.lang.String)
All objects that are instances of class java.lang.String.
AllocatedAt(String.java:1933)
All objects that were allocated at String.java:1933.
Obj()
All objects that were traced.

Spencer's power lies in the fact that these queries all return the same kind of structure: sets of objects! This restriction makes it possible to compose queries into larger ones, like so:

Query Meaning
And(ImmutableObj() AllocatedAt(String.java:1933))
All objects that are never changed outside their constructor, and were allocated at String.java:1933.
Or(UniqueObj() ImmutableObj())
All objects that are never aliased, or are never changed outside their constructor.
Deeply(Or(UniqueObj() ImmutableObj()))
All objects that are never aliased, or are never changed outside their constructor, and the same is true for all reachable objects.
HeapDeeply(Or(UniqueObj() ImmutableObj()))
All objects that are never aliased, or are never changed outside their constructor, and the same is true for all reachable objects.
ReachableFrom(AllocatedAt(String.java:1933))
All objects that are reachable from objects that were allocated at String.java:1933.
HeapReachableFrom(AllocatedAt(String.java:1933))
All objects that are heap-reachable from objects that were allocated at String.java:1933.
CanReach(AllocatedAt(String.java:1933))
All objects that are able to reach objects that were allocated at String.java:1933.
CanHeapReach(AllocatedAt(String.java:1933))
All objects that are able to heap-reach objects that were allocated at String.java:1933.

Assembling queries from smaller parts is nice — but sometimes, you'll want to compare different queries with each other: how do prevalent are mutability, stationarity, and immutability, compared to each other? Comparing queries with each other can be done by separating several queries with a slash, like so: MutableObj()/StationaryObj()/ImmutableObj().

These combined queries bring up an interactive visualisation, try clicking on the query name labels..