How to use a Debugger
Last updated
Last updated
For many, a debugger is an essential tool for the process of writing functional programs. While some prefer "manual" debugging, e.g., through adding various log or print statements, a debugger can greatly accelerate the process (if used correctly).
When it comes to Assembly programming, a debugger is almost indispensable. Why is that so? You may suspect that a function is misbehaving because something is wrong with the arguments that are passed. To confirm this suspicion you would want to know what values are present in the registers at a certain moment in time (→ a certain line of your program). However, if you test this by adding a call to printf
this call will change the state of various (caller-saved) registers.
Another common issue is with segmentation faults. When your program crashes due to a segmentation fault you can only estimate the location in the code that caused the segmentation fault by the progress of your program (e.g., through its output). However, depending on the program, this might still be a very wide range of possible locations for the issue. A debugger, on the other hand, is able to determine the exact line that caused the segmentation fault.
The recommended approach is to use the LLDB debugger through the VS Code interface. Thereby, you can graphically follow the progress of your program, examine register states, and set breakpoints - without needing to type commands. The framework already has this setup configured for you. You can find an explanation of how to start the debugger in the README.md
file.
If you are on Windows, make sure that your VS Code is running through WSL, otherwise the debugger will not work. You can validate this by checking whether the bottom left corner of the window shows a small blue box with the "WSL: Ubuntu" text in it:
To open VS Code through WSL, follow the last step of the Windows Setup Guide.
This is merely an introductory guide to using a debugger. It should be sufficient to get you started with debugging your assignments. However, most debuggers offer much more (complex) functionality and it is highly recommended to consider further sources (debugger manuals/online tutorials/...) to extend your debugging toolbox.
You may have noticed that a small red dot appears when you are hovering over the line numbers in your VS Code editor. These red dots indicate the option to set a breakpoint. If you click such a faded red dot, it should become more prominent and remain visible even when you move the mouse somewhere else. You have just set your first breakpoint!
But what even is a breakpoint? A breakpoint is, as the name suggests, a point at which your program execution should stop (→ break) when run through the debugger. You can set as many breakpoints as you want (or at least as you have lines) and you can even set conditional breakpoints (however, that may go beyond the basics of using a debugger - and with that beyond this guide).
Once you have set some breakpoints, you can start the debugger (as described in the README.md
). This will first assemble and link your program and then start the debugger with your program as the input. You will notice that a small bar with controls will have appeared in your VS Code window, looking something like the one shown below:
Let's break down the individual controls (from left to right):
Continue (F5) In this example, the debugger is currently stopped at a breakpoint. The Continue button resumes the program execution (until reaching the next breakpoint)
Step Over (F10)
Instead of resuming the execution until the next breakpoint, instructions can also be executed one by one. The Step Over will execute the next instruction. However, as the name suggests, it will step over any function/subroutine calls. So if the next instruction of the program is a call
instruction, it will continue the execution until the program reaches the next instruction after the call.
Step Into (F11)
Similarly to Step Over, Step Into also executes a single instruction. However, as opposed to the former, it will step into any function/subroutine calls. So if the next instruction of the program is a call
it will stop at the first instruction of the subroutine that is called.
Note: whether this also works for Library functions (like printf
) depends on the system. On some systems, it will simply step over library function calls.
Step Out (⇧F11 | Shift + F11) As you can s_tep into_ a function/subroutine you can also step out. This will continue the program execution until the current function/subroutine returns.
Restart (⇧⌘F5 | Ctrl + Shift + F5) As the name suggests, this will restart the current debugger configuration. So it will rebuild the program and start the debugger again.
Stop (⇧F5 | Shift + F5) Again, as the name suggests, this will stop the debugger.
The debugger not only allows you to take control over the individual execution steps of your program but it also allows you to examine the status of the CPU registers.
To do so, open the "Run and Debug" panel ("View" → "Run" | ⇧⌘D | Ctrl + Shift + D) while the debugger is running. You should see the label "VARIABLES" in this panel. Upon extending this label, you should see various categories, one of which is labeled "Registers". Here, you will find information about all (relevant) CPU registers. Most interesting for you will be the group of "General Purpose Registers".
You can use this view to examine the current register values, but also to change the values to any desired value.
Often you are not only interested in the data stored in your registers, but also in the data stored in memory, such as the stack.
This will open a file with the raw contents of that (and the following) memory addresses. I you installed the recommended extensions of the framework, this page should open in a Hex Viewer.
Note that the memory view does not automatically update when you advance in your program. It will stay as a snapshot from when it was opened.
To examine the memory contents, simply hover over a register that holds the associated address (e.g., rsp
for the stack) and click the small icon on the right.