The following is an introduction to some of the debugging techniques that you can use if you are stuck on a tricky bug. This includes debugging both tests and demos, as well as constructing your own files to debug.
Debugging Files - Using the VSCode Debugger
First, navigate to the test file you wish to debug, and do the following:
(1) Set breakpoints on the lines/functions you want to investigate
(2) Now, navigate to the “Run and Debug” menu by clicking the bug-and-play button on the left
(3) In the dropdown menu, select “Debug active file”
(4) Click the green play button
Make sure you have the file open that you wish to debug
You should see the debugger terminal start up. After a moment, the debugger should jump to the line where you’ve set your breakpoint, as shown below.
You will see a menu of buttons at the top.
(1) jumps to the next breakpoint/continues execution if no future breakpoint is set.
(2) is akin to the ‘Step over’ button in Intellij, and steps to the next line of code in the function.
(3) is akin to the ‘Step into’ button in Intellij, and steps to the next line in the execution, which may be in a different function.
(4) “steps out” of the function, to the line where it was called, essentially allowing the function to finish executing by itself.
(5) restarts the debugging process.
(6) stops the debugging process.
As with Intellij, you are free to set more breakpoints as you debug. As you step through your code, (1) you will see variable values to the left that you can monitor through your execution. (2) You can also watch certain expressions as you step through your code. (3) Finally, the call-stack is also helpful - it is to the bottom left.
Advanced - Creating Your Own Files to Debug
You may find yourself in a situation where you want to debug files that aren’t just the tests. Maybe you have a problematic portion of code in your demo, or want to further test a function. To do this, you can make a new file to debug as follows.
First, make the file - you can put it in the tests folder. In this example, it is named “student_test.c”. Put any code or functions you want to test into this file. You should not make any sdl calls in this file, as otherwise it will not compile. Make sure it has a main that calls whatever functions you want to test.
The last thing we have to do is update the Makefile such that we can run the debugger. This involves making a rule to make your c file. If your file was called ‘student_test.c’, you could add this rule to the Makefile, to compile the executable student_test for the debugger:
bin/student_test: out/student_test.o $(STUDENT_OBJS) $(STAFF_OBJS)
$(CC) $(CFLAGS) $(LIB_MATH) $^ -o $@
Now, after running
make bin/student_test, navigate to the debugger, open your file, set your breakpoints, and debug away!
A Note on Finding Memory Leaks
If you run
./bin/student_test, if your file leaks any memory, asan will helpfully output where it is (this will be covered in more detail in a future course lab). This can be helpful if you suspect your demo is leaking memory but aren’t sure where. Interpreting these kinds of messages will be covered in more detail in a future lab in the course.
Emscripten Demos - Interpreting Console Errors
Your demos are compiled and run with emscripten, which must be debugged with web assembly tools in Chrome. Suppose I try to run a demo, and I see the following message telling me an exception has been thrown:
Or else I’m greeted with a black screen with no exception:
In both these cases, an exception has been thrown, but how do I see where it is? Click on the three dots in the top right corner, then ‘More tools’, then ‘Developer tools’ to open the console, as shown below.
You will see the traceback to the error thrown in this console. In this case, it is telling us we’re accessing restricted memory in
vec_list_size, which is called from
polygon_translate, which is called from
emscripten_main. This gives us a lead of where our bug could be.
A Note on Printing
Print debugging is definitely a viable option when running your demos. Any prints will be outputted in the black box below where your demo runs, as shown in the screenshot below. Be sure to end your prints with a newline character, ‘\n’, otherwise they will not display.
In the below example, a print was inserted at the end of emscripten_init and emscripten_main. Since the bug occurred in emscripten_main, the second print was not displayed.
Advanced - Debugging with Emscripten
Beyond just allowing us to view the stack trace, the ‘Developer tools’ window can also let us debug our demo in real time. First, if you have a demo you want to debug, we would recommend switching the optimization flag from -O2 to -O0 in the EMCC_FLAGS section of the makefile. This means the compiler will compile your code without optimizations, which means it will run in the same order it was written.
Now, open the console as before, and navigate to ‘Sources’. You should be able to view any of your files. To set a breakpoint, click on the line you wish to set a breakpoint on. You may have to refresh the page to rerun your code.
Similarly to the VSCode debugger, you can step through your code with the labeled buttons.
(1) Step to the next breakpoint/resume execution
(2) Step over (continue evaluating in the current function)
(3) Step into the next function call
(4) Step out of the current function (by finishing its execution)
(5) Step to the next line
(6) Remove all breakpoints
(7) Stop when exception is thrown
You can also view (1) the call stack in the bottom right, and (2) a list of variables. Notably, the variables do not have names. For particularly gnarly bugs, you might want to convert your demo into a file without sdl and debug it in VSCode as discussed in the ‘Creating your own files to debug’ section.
We hope this guide has been instructive, please don’t hesitate to make a ticket or schedule OH with your TAs if you run into difficulty. Good luck and happy debugging!