Tutorial: Getting Started with UDB in VS Code

Tutorial: Getting Started with UDB in VS Code

Overview

This quick start tutorial will walk you through how to use the Time Travel Debug extension in VS Code to identify the root cause of a small bug.

You will:

  • set up and run UDB (time travel debugger)
  • install the Time Travel Debug VS Code extension
  • locate and use a provided example program
  • learn the basics of time travelling backward and forward through code in order to inspect and easily understand program state

The principles learned by following this guide can be used on on complex codebases with millions of lines of code.

Install Visual Studio Code

If you have already installed Visual Studio Code, skip this step.

Otherwise, install it now by following Microsoft’s setup instructions.

Unpack the Tar file

If you haven’t downloaded a Tar file containing an evaluation copy of UDB, request a free trial.

If you have already unpacked the Tar file, skip this step.

Otherwise, you will need to unpack it to get going.

It’s easy to install UDB. Here’s what you need to do:

  1. If you’re debugging remotely via the Remote – ContainersRemote – SSH or Remote – WSL Visual Studio Code extensions, copy the .tar.tgz file that you just downloaded onto the remote system.
  2. Open the Terminal app.
  3. Unpack the Tar f file that you just downloaded with the following command:
    tar -xf UDB-Individual-Evaluation-<version>.tar
  4. This will create a folder named UDB-Individual-Evaluation-<version>

Install the VS Code extension

  1. Run Visual Studio Code
  2. If you’re debugging remotely via the Remote – ContainersRemote – SSH or Remote – WSL extensions, connect to the remote system.
  3. Bring up the Extensions view by clicking on the Extensions icon in the Activity Bar on the left hand side of the window, type undo.udb into the search box and press Install.

Open the examples

We will use the sample program cache-cpp.cpp (cache calculate) that can be found in the examples directory of the UDB-Individual-Evaluation-<version> folder you just created.

This sample program maintains a square root cache data structure in memory and validates it through repeatedly looking up values, caching new values on a cache miss.

  1. Choose File → Open Folder …
  2. Navigate to the examples folder inside the UDB-Individual-Evaluation-<version> folder that you created in the last step and press Open.
  3. You will be building and running the example programs in this folder, so press “Yes, I trust the authors” to allow debugging
  4. Click on cache-cpp.cpp in the sidebar to open the cache calculate sample program

Build & run the example program

To build and run the examples you’ll need a C++ compiler and the make program.

On Debian/Ubuntu you can install these with sudo apt install -y build-essential.

  1. Bring up the Run view by clicking on the Run icon in the Activity Bar on the left hand side of the window
  2. Press the the green Run button to build and debug
  3. View the UDB license and press “Accept the license”
  4. UDB stops the program at main(). Let’s run it to the end. Press the Continue button to do this

Run UDB & diagnose the problem

  1. We can see from the red popup that the program has aborted due to an exception:
  2. Let’s analyze the program’s execution and diagnose the reason for the failure.
    By selecting the cppdbg:cache-cpp button (highlighted below) we can see the output of the program – the number that the program is pulling out of the cache is not the expected number.
  3. Because UDB is a time travel debugger we can run the program in reverse.
    Set a breakpoint on the throw CacheFailure(...) statement by clicking in the margin to the left of the line number, where the yellow pointer is. You’ll see a red circle appear to show that the line has a breakpoint set. Then press the Reverse Continue button to run backwards until the breakpoint is hit.
  4. With a time travel debugger you can go back to any line of code that executed and see the complete program state.
    So we are currently looking at the state of the variables at the point that the exception was thrown.
    Looking in the Variables window we can see that the code is querying the cache for the square root of the number 255.
    The integer square root of the number 255 is 15 (in sqroot_correct). But sqroot_cache is 0; which is the wrong value.
  5. This is the point where the defect manifests, but it’s not the root cause of the defect. We need to find the point where the cache is populated with the incorrect value. Line 123 is where the sqroot_correct variable is set.
    Use the Reverse Step Over button 2 times to step back in time to line 123.
  6. The previous line is where the sqroot_cache variable is set to its incorrect value. Use the Reverse Step Into button to step back into the function which returns this value.
  7. Press Reverse Step Over to step a little further back in time to where this function returns the incorrect value
  8. In the Variables window, hover your cursor over the variable f to see its full value. This confirms that f->second (the number being retrieved from the cache) is 0, which is incorrect.
  9. Now we need to find out where this cache entry was populated with the incorrect value.
    We can do this by running backwards to the last time when the entry in the cache was updated.
    Press the Last Changed button, type f->second, press Enter, and select Last Change from the popup menu.
  10. The program runs backwards to the last time this cache entry was updated.
  11. We’re in the depths of the C++ STL. Press Reverse Step Out four times until we’re back in our code, just before the incorrect value is placed in the cache.
  12. Looking at number_adj in the Variables window we can see that the code is trying to calculate the square root of -1.
    And looking at sqroot_adj we can see that it’s storing the square root of -1 as 0.
    So there are two problems – why does the code think the square root of -1 is 0, and why is it trying to calculate the square root of -1 in the first place?
  13. sqroot_adj is calculated on the previous line; line 68. Because UDB is a time travel debugger we can call arbitrary functions at any time in the program’s execution history.
    Go to the Terminal window by selecting “UDB: cache-cpp” at bottom right. In the Terminal, type print sqrt(-1) and press Enter to see what the function returns.

    The sqrt function returns a special value that indicates “not a number”, which on line 68 is statically cast to an int to fit into the data container which is expecting integers.
    This explains why the code is storing 0 in the cache; there’s no check that the number being stored is representable as an int.
  14. You’ve discovered that the root cause of this program failure happens as a result of attempting to put the square root of -1 into the cache, which was not intended.
    But why is it trying to calculate the square root of -1 in the first place?

    That happens because the for loop in line 66 loops from number-1 to number+1, but there is no protection anywhere to deal with the special case that we just hit where the number is zero.
    A simple if (number_adj < 0) continue; at the start of the for loop would have avoided this error.

Tutorial Complete

Congratulations! you have successfully used time travel debugging in VS Code to diagnose the root cause of an error!

You’re now a time travelling Bug Hunter!

Next steps

Do more with time travel debugging in VS Code.

  1. VS Code docs
  2. Get started with your own project
  3. Operation
  4. Launch configurations
  5. Limitations

Help and Support

If you get stuck, help is always at hand.

UDB Documentation
Community – ask a question

Stay informed. Get the latest in your inbox.