This video introduces Time Travel Debugging as a solution for the challenges of debugging multi-threaded code, particularly race conditions. Using the UDB (Undo Debugger) integrated with the Visual Studio Code IDE, the presenter demonstrates how to debug a crashing multi-threaded C++ program. The core feature highlighted is the ability to reverse execute the code execution and specifically jump backward to the point where a variable was last changed. This process quickly identifies the root cause of a race condition leading to an assertion failure: an unsafe increment of a shared global variable by a thread outside of a mutex lock. The video suggests the solution is to protect the access with a mutex and mentions other features like timelines and thread fuzzing for reproducing concurrency bugs.
Debugging Multi-Threaded Code with Time Travel Debugging (UDB)
0:00 Solving Multi-threading Debugging: The video introduces Time Travel Debugging as a significantly easier way to debug complex multi-threaded code issues compared to traditional debuggers like GDB.
0:18 Demonstration Setup: A small multi-threaded C++ example program that crashes due to an assertion failure is used for the demonstration within the Visual Studio Code IDE using the UDB extension.
0:54 Running and Crashing: The program is run, and it quickly crashes at a specific point, showing several stack frames leading up to the failure.
1:30 Reverse Execution: The key feature is demonstrated: reversing execution using "reverse finish" or "reverse step out" to move backward through the call stack and program state to reach the location of the assertion.
1:44 State Unwinding: The debugger not only unwinds stack frames but also the state of global and state variables during reverse execution.
2:06 Examining Variables: Variables can be inspected by hovering the mouse or adding them to the 'Watch' window to understand their values at different points in the past execution.
2:55 Setting Bookmarks: Bookmarks can be set at specific points in the timeline (like the assertion failure or a state where a value was correct) for easy navigation.
3:09 Finding Last Change: Using "reverse next" or "reverse step over" helps trace backward line by line to find where a variable's value became incorrect.
3:52 Killer Feature: Last Change: The "last change" feature allows the debugger to automatically jump backward in time to the exact point where a specified variable was last modified, dramatically speeding up root cause analysis.
4:04 Identifying Root Cause: Using "last change" reveals that a shared global variable ('g') was incorrectly modified at line 46 by a function (thread_function_two) that was not under a mutex lock.
4:44 Timeline View: The timeline view visualizes the sequence of events, showing where values were read, incorrectly modified, and where the assertion failed.
5:03 Simple Solution: The identified solution is to protect the section of code modifying the shared variable in thread_function_two with a mutex.
5:13 Benefits: Time Travel Debugging eliminates the need for extensive logging or tedious forward-only debugging cycles to find the root cause of concurrency bugs.
5:39 Real-World Application: UDB is capable of debugging large, complex software using terabytes of memory and many threads, running for days.
5:57 Thread Fuzzing: Undo comes with a unique "thread fuzzing" capability to intentionally alter thread scheduling and increase the likelihood of reproducing elusive race conditions.