Enabling Time Travel Debugging (TTD)

TTD (TimeTravel Debugging) makes it possible to travel back in time to previous states of your application when testing by getting a snapshot of when each line is executed. You can step over each line of the code and track the values of the variables at each test run. TTD (TimeTravel Debugging) is disabled by default, but can be enabled programmatically (by annotations) or by configuration (environment variables, etc ...).

As offline debugging requires source code, the thundra-agent-trace-instrument-metadatagen module should be optionally in the dependencies/classpath during compilation. Otherwise, Thundra decompiles the class files on demand to geenerate the source code on the file and as you guess, it may be not be exactly the same as the original source code.

thundra-agent-trace-instrument-metadatagen module includes Thundra Java annotation processor and generates metadata (source code, line mappings, etc ...) about classes to be used for tracing at runtime.

<dependency>
<groupId>io.thundra.agent</groupId>
<artifactId>thundra-agent-trace-instrument-metadatagen</artifactId>
<scope>test</scope>
<version>${thundra.version}</version>
</dependency>

Configuration via Annotations

The @Traced annotation with the traceLineByLine attribute true must be put onto classes/methods to be debugged.

Classes and methods you want to debug can be specified with the @Traced annotation – they must be marked with this annotation. The class level @Traced annotation is mandatory; if not marked with the annotation, classes won’t be checked or debugged. Additionally, methods can be marked with this annotation as well.

To have the @Traced annotation, you need to add the thundra-agent-api-trace module into your dependencies:

Maven
Gradle
Maven
<dependency>
<groupId>io.thundra.agent</groupId>
<artifactId>thundra-agent-api-trace</artifactId>
<version>${thundra.agent.version}</version>
<scope>provided</scope>
</dependency>
Gradle
dependencies {
compileOnly 'io.thundra.agent:thundra-agent-api-trace:${thundraAgentVersion}'
}
  • If the annotation is used on the class, all public methods of the class are debugged by default.

UserService.java
...
import io.thundra.agent.api.trace.annotations.Traced;
...
@Traced(traceLineByLine=true)
public class UserService {
...
public User get(String id) {
...
}
}

In the example above, the public get method is debugged automatically.

  • If the annotation is used on the method, the specified method is also debugged.

UserService.java
...
import io.thundra.agent.api.trace.annotations.Traced;
...
@Traced(traceLineByLine=true)
public class UserService {
...
@Traced(traceLineByLine=true)
private User getFromCache(String id) {
...
}
@Traced(traceLineByLine=true)
private User getFromRepository(String id) {
...
}
...
}

In this example, the getFromCache and getFromRepository methods are debugged in addition to the get method.

If you want to explicitly specify a method to be debug, configure the class level @Traced annotation by setting the justMarker property and annotate the desired method with the method level @Traced annotation.

UserService.java
...
import io.thundra.agent.api.trace.annotations.Traced;
...
@Traced(justMarker=true)
public class UserService {
@Traced(traceLineByLine=true)
public User get(String id) {
...
}
...
}

In this example, only the get method is debugged because the class level @Traced annotation is marked and only the get method is annotated.

Configuration via Environment Variables

To configure TTD (Time Travel Debugging) behavior through environment variables without any code change, the traceLineByLine attribute of the THUNDRA_AGENT_TRACE_INSTRUMENT_TRACEABLECONFIG environment variable must be set to true.

Environment variable names must be in the THUNDRA_AGENT_TRACE_INSTRUMENT_TRACEABLECONFIG... format, meaning a name must start with the THUNDRA_AGENT_TRACE_INSTRUMENT_TRACEABLECONFIG. For example, THUNDRA_AGENT_TRACE_INSTRUMENT_TRACEABLECONFIG, THUNDRA_AGENT_TRACE_INSTRUMENT_TRACEABLECONFIG1, THUNDRA_AGENT_TRACE_INSTRUMENT_TRACEABLECONFIG2, etc ... So multiple definitions can be defined through multiple environment variables starting with THUNDRA_AGENT_TRACE_INSTRUMENT_TRACEABLECONFIG environment variable name.

Environment variable values must be in the <class-def>.<method-def>[...] format, The asterisk character (*) in the <class-def> and <method-def> is supported. For example:

  • get method in the com.mycompany.UserService class: com.mycompany.UserService.get

  • All methods start with validate in the class com.mycompany.UserService: com.mycompany.UserService.validate*

  • All methods (by default only public methods) in the com.mycompany.UserService class: com.mycompany.UserService.*

  • All methods (by default only public methods) in the com.mycompany package: com.mycompany.*.*

For example, if you want to debug all methods (by default only public methods are taken care of), the UserService class over environment variables, the THUNDRA_AGENT_TRACE_INSTRUMENT_TRACEABLECONFIG environment variable can be specified as com.mycompany.UserService.*[traceLineByLine=true].

Specifying non-public methods

As mentioned above, by default only public methods are counted while debugging. So, if you want to configure non-public methods, you need to specify methodModifiers attribute which can be configured as a hexadecimal format definition of modifiers from the java.lang.reflect.Modifier class, as shown below:

  • 0x00000001 means public methods.

  • 0x00000002 means private methods.

  • 0x00000004 means protected methods.

  • 0x00000008 means static methods.

  • 0x00000001 | 0x00000002 means private or public methods. As shown here, multiple modifiers can be combined with the | character.

  • 0x70000000 means any method.

Note that, as shown in the examples above, multiple modifiers can be combined with the | character and this means logical OR.

In our example, if we want to debug all public and private methods in the UserService class over environment variables, the THUNDRA_AGENT_TRACE_INSTRUMENT_TRACEABLECONFIG environment variable can be specified as com.mycompany.UserService.*[traceLineByLine=true,methodModifiers=0x00000001|0x00000002]