!tt (time travel)
The !tt (time travel) debugger extension that allows you to navigate forward and backwards in time.
!tt navigation commands
Use the !tt extension to navigate forwards or backwards in time, by traveling to a given position in the trace.
!tt [position]
Parameters
position
Provide a time position in any of the following formats to travel to that point in time.
If {position} is a decimal number between 0 and 100, it travels to approximately that percent into the trace. For example:
- !tt 0 - Time travel to the beginning of the trace
- !tt 50 - Time travel to halfway through the trace
- !tt 100 - Time travel to the end of the trace
If {position} is a floating point number between 0 and 100, it travels to approximately that percent into the trace. For example:
- !tt 0.00 - Time travel to the beginning of the trace
- !tt 50.1 - Time travel to just over halfway through the trace
- !tt 99.9 - Time travel to almost the end of the trace
If {position} is #:#, where # are a hexadecimal numbers, it travels to that position. If the number after : is omitted, it defaults to zero.
- !tt 1A0: - Time travel to position 1A0:0
- !tt 1A0:0 - Time travel to position 1A0:0
- !tt 1A0:12F - Time travel to position 1A0:12F
Note
Traces use a two-part instruction position that references a specific position reference in the trace, for example 12:0. or 15:7. The two elements are hexadecimal numbers defined as described here.
xx:yy
xx- the first element is the sequencing number, which corresponds to a sequencing event.
yy - the second element is a step count, which corresponds roughly to the instruction count since the sequencing event.
!tt break commands
Break on register
!tt br[-] <register> [<value>]
This command will navigate to the previous/next position where the specified register changed values (or, if <value>
is specified, becomes the specified value) on the current thread.
Examples:
!tt br rbx 0x12345678
– find next position on current thread that sets rbx to 0x12345678.
!tt br- ebx
– find previous position on current thread that set rbx to its current value.
0:000> !tt br- ebx
Setting position: 2C8:0
(3b24.2d98): Break instruction exception - code 80000003 (first/second chance not available)
Time Travel Position: 2C8:0
ntdll!LdrInitializeThunk:
00007ff9`999e3dd0 4053 push rbx
- The register watchpoint command supports all supported Windows architectures, such as x64 and ARM64.
- The register watchpoint is limited to general purpose (and XMM) registers.
- The register watchpoint is implemented with an algorithm that is slow compared to normal trace replay speed. The search can be cancelled using Ctrl+C.
Break on access
!tt ba[-] <rwe> <address> <size> [<address> <size> …]
This command will navigate to the previous/next position where one of the specified memory ranges is accessed in the specified way(s): “R” is for memory read, “W” is for memory write and “E” is for execution. More than one can be specified at same time, i.e. “RW”. This is similar to the ba (Break on Access) debugger command.
Examples:
!tt ba- rw 0x12345678 0x4000
– find previous position that reads from memory range. (0x12345678 – 0x12345678 + 0x4000).
!tt ba e 0x7fffe0001234 0x30000
– find next position that executes from specified range. Pretend the address and range represent the range of ntdll.dll. This command would find the next position where ntdll.dll is entered.
- Addresses / sizes must be supplied as numbers. Symbols are not supported.
Break on module
Use Break on module navigation method move forwards or backwards at the module level.
!tt bm[-] [<module> ...]
This command time travels to the previous/next position that executes one of the specified module(s).
If no module is specified, it will travel to the previous/next execution point outside of currently executing module.
When using this command to move in the trace, searching backwards is considerably slower than searching forwards.
Example use cases
Here are two example use cases.
Use case 1 - Move forward or backwards until you are in a different module on the current thread. In this case, TTD moves from the dwmcore to ucrtbase modules going forwards and then travels backwards to ntdll.
The trace example starts at an exception in the dwmcore module.
Setting position: 1C46AE:0
(a54.c98): Break instruction exception - code 80000003 (first/second chance not available)
Time Travel Position: 1C46AE:0
dwmcore!wil_atomic_uint32_compare_exchange_relaxed+0xc [inlined in dwmcore!wil_details_FeatureReporting_RecordUsageInCache+0x124]:
00007ffa`441e16a0 f0450fb102 lock cmpxchg dword ptr [r10],r8d ds:00007ffa`444b0450=00000003
Execute the !tt bm commmand. As no module is specified, travel to the next execution point outside of currently executing module - ucrtbase.
0:001> !tt bm
Replaying - Currently at 1D1BEE:0 ( 2.95%)
Setting position: 1C46AF:61
(a54.c98): Break instruction exception - code 80000003 (first/second chance not available)
Time Travel Position: 1C46AF:61
ucrtbase!_finite:
00007ffa`4ac3c080 48b9ffffffffffffff7f mov rcx,7FFFFFFFFFFFFFFFh
Single stepping from that location, TTD correctly found the module boundary.
0:001> t-
Time Travel Position: 1C46AF:60
dwmcore!CBaseExpression::IsExpressionValueValid+0x50:
00007ffa`44124b40 48ff15f9db2700 call qword ptr [dwmcore!_imp__finite (00007ffa`443a2740)] ds:00007ffa`443a2740={ucrtbase!_finite (00007ffa`4ac3c080)}
(a54.c98): Break instruction exception - code 80000003 (first/second chance not available)
Time Travel Position: 1C46AE:0
dwmcore!wil_atomic_uint32_compare_exchange_relaxed+0xc [inlined in dwmcore!wil_details_FeatureReporting_RecordUsageInCache+0x124]:
00007ffa`441e16a0 f0450fb102 lock cmpxchg dword ptr [r10],r8d ds:00007ffa`444b0450=00000003
Search backwards for the next module boundary.
0:001> !tt bm-
Setting position: 1C46AD:2B1
(a54.c98): Break instruction exception - code 80000003 (first/second chance not available)
Time Travel Position: 1C46AD:2B1
ntdll!LdrpDispatchUserCallTarget+0x3b:
00007ffa`4d27f24b 48ffe0 jmp rax {dwmcore!CKeyframeAnimation::CalculateValueWorker (00007ffa`441264e0)}
Then travel forward from the module boundary, to the next module.
0:001> t
Time Travel Position: 1C46AD:2B2
dwmcore!CKeyframeAnimation::CalculateValueWorker:
00007ffa`441264e0 48895c2408 mov qword ptr [rsp+8],rbx ss:0000004e`5151f4d0=0000000000000000
Use case 2 - Move forward until a module is entered. In this example, ntdll.
0:001> !tt bm ntdll
Replaying - Currently at 1CBF15:0 ( 1.66%)
Setting position: 1C46B0:97
(a54.c98): Break instruction exception - code 80000003 (first/second chance not available)
Time Travel Position: 1C46B0:97
ntdll!LdrpDispatchUserCallTarget:
00007ffa`4d27f210 4c8b1db1c11000 mov r11,qword ptr [ntdll!LdrSystemDllInitBlock+0xb8 (00007ffa`4d38b3c8)] ds:00007ffa`4d38b3c8=00007df503990000
You can use !tt bm- <module>
to search backwards.
Data Model - NextModuleAccess and PrevModuleAccess
Use the dx (Display Debugger Object Model Expression) command and the NextModuleAccess and PrevModuleAccess data model objects, to access the same functionality, that is available in the !tt command.
Travel forward to the next module access boundary.
0:001> dx @$curprocess.TTD.NextModuleAccess()
Replaying - Currently at 1D1BEE:0 ( 2.95%)
@$curprocess.TTD.NextModuleAccess() : [UTID 3] Execute [1C46AE:0] -> [1C46AF:61] ucrtbase 0x7ffa4ac3c080
Position : 1C46AF:61
OriginalPosition : 1C46AE:0
UniqueThreadId : 0x3
AccessType : Execute
Address : 0x7ffa4ac3c080
ModuleName : ucrtbase
Travel backwards to the next previous access boundary.
0:001> dx @$curprocess.TTD.PrevModuleAccess()
@$curprocess.TTD.PrevModuleAccess() : [UTID 3] Execute [1C46AE:0] -> [1C46AD:2B1] ntdll 0x7ffa4d27f24b
Position : 1C46AD:2B1
OriginalPosition : 1C46AE:0
UniqueThreadId : 0x3
AccessType : Execute
Address : 0x7ffa4d27f24b
ModuleName : ntdll
Travel forward to the next ntdll module access boundary.
0:001> dx @$curprocess.TTD.NextModuleAccess("ntdll")
Replaying - Currently at 1CBF15:0 ( 1.66%)
@$curprocess.TTD.NextModuleAccess("ntdll") : [UTID 3] Execute [1C46AE:0] -> [1C46B0:97] ntdll 0x7ffa4d27f210
Position : 1C46B0:97
OriginalPosition : 1C46AE:0
UniqueThreadId : 0x3
AccessType : Execute
Address : 0x7ffa4d27f210
ModuleName : ntdll
Use debugger model objects for TTD breakpoints
The break on memory access functionality can be accessed through the dx (Display Debugger Object Model Expression) command, the debuggers data model windows, JavaScript and C++. For general information on working with LINQ queries and debugger objects, see Using LINQ With the debugger objects.
TTD.PrevRegisterWrite
dx @$curthread.TTD.PrevRegisterWrite("<reg>" [, <value>])
This method searches for the previous position where the specified register changed values (or, if <value>
is specified, becomes the specified value) on the current thread. It is analogous to the !tt br-
command above, except it does not automatically navigate to the position. Instead, it returns information like the following:
0:000> dx @$curthread.TTD.PrevRegisterWrite("rbx")
@$curthread.TTD.PrevRegisterWrite("rbx") : [UTID 2] rbx [2C8:0] 0x0 -> [2C8:0] 0x0
Register : rbx
Position : 2C8:0 [Time Travel]
Value : 0x0
OriginalPosition : 2C8:0 [Time Travel]
OriginalValue : 0x0
UniqueThreadId : 0x2
Similarly, you can use @$curthread.TTD.NextRegisterWrite(…) to search for the next register change.
0:000> dx @$curthread.TTD.NextRegisterWrite("rbx")
@$curthread.TTD.NextRegisterWrite("rbx") : [UTID 2] rbx [2C8:1] 0x0 -> [2C8:2] 0x963127f5a0
Register : rbx
Position : 2C8:2 [Time Travel]
Value : 0x963127f5a0
OriginalPosition : 2C8:1 [Time Travel]
OriginalValue : 0x0
UniqueThreadId : 0x2
Use the [Time Travel] links in the output, to move to that postion in the trace.
0:000> dx -s @$create("Debugger.Models.TTD.Position", 712, 0).SeekTo()
(3b24.2d98): Break instruction exception - code 80000003 (first/second chance not available)
Time Travel Position: 2C8:0
TTD.PrevMemoryAccess
dx @$curprocess.TTD.PrevMemoryAccess("<rwe>", <address>, <size> [, <address>, <size> …])
This method searches for the previous position where one of the specified memory ranges is accessed in the specified way(s). It is analogous to the !tt ba- command above, except it does not automatically navigate to the position. Instead, it returns information like the following:
@$curprocess.TTD.PrevMemoryAccess("w", 0x01a16a939820, 0x10) : [UTID 3] Write 0x1a16a939828 4 bytes [1C46AE:0] -> [1C4575:0]
Position : 1C4575:0
OriginalPosition : 1C46AE:0
UniqueThreadId : 0x3
Address : 0x1a16a939828
Size : 0x4
AccessType : Write
Similarly, you can use @$curprocess.TTD.NextMemoryAccess(…)
to search for the next memory access.
0:000> dx @$curprocess.TTD.NextMemoryAccess("r", 0x00007ff9`95420000, 0xFF)
@$curprocess.TTD.NextMemoryAccess("r", 0x00007ff9`95420000, 0xFF) : [UTID 2] Read 0x7ff995420000 2 bytes [2C8:0] -> [66C:10A2]
Position : 66C:10A2 [Time Travel]
OriginalPosition : 2C8:0 [Time Travel]
UniqueThreadId : 0x2
Address : 0x7ff995420000
Size : 0x2
AccessType : Read
!tt DLL
ttdext.dll
Additional Information
This extension only works with time travel traces. For more information about time travel, see Time Travel Debugging - Overview.