...
Table of Contents | ||
---|---|---|
|
Prerequisites
- Module 1: Setup
Debugging
More important than knowing how to make something work is knowing what to do when it doesn't. Debugging is probably one of the most important skills you can learn, since things rarely work perfectly the first time.
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
#include <stdint.h>
#include "soft_timer.h"
#include "gpio.h"
#include "interrupt.h"
#include "log.h"
#include "wait.h"
#define DEBUG_TEST_TIMEOUT_MS 50
typedef struct DebugTestStorage {
GpioAddress *leds;
size_t num_leds;
size_t current_led;
} DebugTestStorage;
static DebugTestStorage s_storage;
static void prv_init_leds(DebugTestStorage *storage) {
GpioAddress leds[] = {
{ .port = GPIO_PORT_C, .pin = 8 }, //
{ .port = GPIO_PORT_C, .pin = 9 }, //
{ .port = GPIO_PORT_C, .pin = 6 }, //
{ .port = GPIO_PORT_C, .pin = 7 }, //
};
GpioSettings led_settings = {
.direction = GPIO_DIR_OUT,
.state = GPIO_STATE_HIGH
};
storage->leds = leds;
storage->num_leds = SIZEOF_ARRAY(leds);
storage->current_led = 0;
for (size_t i = 0; i < SIZEOF_ARRAY(leds); i++) {
gpio_init_pin(&leds[i], &led_settings);
}
}
static void prv_timeout_cb(SoftTimerId timer_id, void *context) {
DebugTestStorage *storage = context;
// Toggle the current LED
GpioAddress *led = &storage->leds[storage->current_led];
LOG_DEBUG("Toggling LED P%d.%x\n", led->port, led->pin);
gpio_toggle_state(led);
// Next LED
storage->current_led = (storage->current_led + 1) % storage->num_leds;
soft_timer_start_millis(DEBUG_TEST_TIMEOUT_MS, prv_timeout_cb, &storage, NULL);
}
int main(void) {
interrupt_init();
soft_timer_init();
gpio_init();
prv_init_leds(&s_storage);
uint32_t buffer[20];
LOG_DEBUG("%ld LEDs set up\n", s_storage.num_leds);
soft_timer_start_millis(DEBUG_TEST_TIMEOUT_MS, prv_timeout_cb, &s_storage, NULL);
size_t i = 0;
while (true) {
wait();
buffer[i % SIZEOF_ARRAY(buffer)] += i;
i++;
}
return 0;
}
|
...
Code Block | ||||
---|---|---|---|---|
| ||||
(gdb) r Starting program: /home/vagrant/shared/firmware/build/bin/x86/debug_test [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". [0] projects/debug_test/src/main.c:58: 4 LEDs set up [0] projects/debug_test/src/main.c:43: Toggling LED P2.8 Program received signal SIGSEGV, Segmentation fault. 0x0000000000400c01 in printf (__fmt=0x401623 "[%u] %s:%u: Toggling LED P%d.%x\n") at /usr/include/x86_64-linux-gnu/bits/stdio2.h:104 104 return __printf_chk (__USE_FORTIFY_LEVEL - 1, __fmt, __va_arg_pack ()); (gdb) |
Woah, what happened? It seems like we hit a segfault, but GDB was able to catch it. Let's take a closer look at what caused that.
...
Info |
---|
If instead you get an error like Program received signal SIG37, Real-time event 37. Try exiting gdb and running the following command, then trying again (see https://stackoverflow.com/a/16742034): echo "set auto-load safe-path /" >> ~/.gdbinit |
Woah, what happened? It seems like we hit a segfault, but GDB was able to catch it. Let's take a closer look at what caused that.
Info |
---|
It's also possible that you won't get a segfault, but it should produce the wrong output! |
First, we use backtrace to print the state of the stack. We can see that the segfault is occuring in prv_timeout_cb
, which is the timer callback we registered. We can use up
and down
to move between function scopes in the stack. In our case, we move up to prv_timeout_cb
. Since it's a segfault, we're probably attempting to access invalid memory. Looking at led
and storage
, we see that they're completely invalid. Since led
is derived from storage
, we should look at where storage
is defined.
...
Hopefully, you were able to see how GDB allows you to observe the state and data of your program without recompiling or relying on debug output. If you are more of a visual learner, CS246 has an amazing GDB tutorial video on their Youtube page that goes over these commands and some other helpful ones. Although they use C++, many of the same principles still apply to C.
Info |
---|
There's actually another issue in this code. The expected behavior is for all LEDs to toggle in sequence, but the values are all wrong. Can you figure out why? Hint: Be careful with addresses to stack-allocated variables! |
...