Debugging Concord Applications

May 7, 2025 ยท View on GitHub

Introduction

Debugging is an essential skill for C programmers. While compilers catch many errors at compile-time, runtime issues like segmentation faults, memory leaks, and race conditions require specialized tools to diagnose and fix.

This guide covers the two most important debugging tools for Concord development:

  • GDB - For tracking down crashes and runtime errors
  • Valgrind - For detecting memory leaks and other memory-related issues

Prerequisites for Effective Debugging

Before starting to debug, make sure your code is compiled with these flags:

$ CFLAGS="-g -O0" make
  • -g adds debugging symbols that tools need to show meaningful information
  • -O0 disables compiler optimizations that can make debugging more difficult

Using GDB to Debug Crashes

Installation

GDB is available on most platforms:

# Debian/Ubuntu
$ sudo apt install gdb

# Fedora/RHEL/CentOS
$ sudo dnf install gdb

# Arch/Manjaro
$ sudo pacman -S gdb

# macOS
$ brew install gdb

# FreeBSD
$ pkg install gdb

Basic GDB Usage

  1. Start your application under GDB:
$ gdb ./your_bot
  1. Run your application:
(gdb) run
  1. When a crash occurs, get a backtrace:
(gdb) bt

This will show the call stack at the point of the crash, helping you identify where the problem occurred.

Useful GDB Commands

CommandDescription
run or rStart your program
btShow backtrace (call stack)
frame NSwitch to frame N in the backtrace
print VARIABLEDisplay the value of a variable
listShow source code around current line
break FUNCTIONSet breakpoint at function
break FILE:LINESet breakpoint at specific line
continue or cContinue execution after stop
next or nExecute next line (step over function calls)
step or sExecute next line (step into function calls)
quit or qExit GDB

Using Valgrind to Find Memory Issues

Installation

Valgrind is available on most Unix-like platforms:

# Debian/Ubuntu
$ sudo apt install valgrind

# Fedora/RHEL/CentOS
$ sudo dnf install valgrind

# Arch/Manjaro
$ sudo pacman -S valgrind

# macOS
$ brew install valgrind

# FreeBSD
$ pkg install valgrind

Basic Valgrind Usage

Run your application with memory leak checking enabled:

$ valgrind --leak-check=full --show-leak-kinds=all ./your_bot

When you exit your application, Valgrind will report any memory leaks detected.

Understanding Valgrind Output

Valgrind's output includes:

  1. Definitely Lost: Memory that was allocated but never freed
  2. Indirectly Lost: Memory that was allocated but became unreachable when parent pointers were lost
  3. Possibly Lost: Memory that Valgrind cannot determine if it is accessible
  4. Still Reachable: Memory that was still accessible at program exit (not necessarily a problem)

For each leak, Valgrind shows a backtrace indicating where the memory was allocated.

Concord-specific Valgrind Tips

Concord uses many asynchronous operations that can complicate memory management. When using Valgrind with Concord:

  1. Ensure you call discord_shutdown() before exiting, or compile with the SIGINT handler:
$ CFLAGS="-DCCORD_SIGINTCATCH" make
  1. Be aware that some false positives may occur due to libcurl's internal memory management

Advanced Debugging Techniques

Address Sanitizer (ASan)

For faster and more detailed memory error detection than Valgrind, consider using Address Sanitizer:

$ CFLAGS="-g -fsanitize=address -fno-omit-frame-pointer" LDFLAGS="-fsanitize=address" make

Run your program normally, and ASan will report memory errors as they occur.

Thread Sanitizer (TSan)

For detecting race conditions and threading issues:

$ CFLAGS="-g -fsanitize=thread -fno-omit-frame-pointer" LDFLAGS="-fsanitize=thread" make

Core Dumps Analysis

If your program crashes, the system might generate a core dump file. To enable core dumps:

$ ulimit -c unlimited

After a crash, you can analyze the core dump with GDB:

$ gdb ./your_bot core

Discord-Specific Debugging

To enable verbose logging of HTTP and WebSocket communications:

$ CFLAGS="-DCCORD_DEBUG_HTTP -DCCORD_DEBUG_WEBSOCKETS" make

You can also configure logging in your config.json:

{
  "log": {
    "level": "TRACE",
    "trace": "debug.log",
    "http": "http.log",
    "ws": "ws.log"
  }
}

Common Issues and Solutions

SIGSEGV (Segmentation Fault)

Common causes:

  • Dereferencing NULL pointers
  • Accessing freed memory
  • Buffer overflows
  • Stack overflows

Diagnosis: Use GDB to get a backtrace and examine variables at the point of crash.

Memory Leaks

Common causes:

  • Forgetting to free allocated memory
  • Losing the pointer to allocated memory
  • Not calling discord_shutdown()

Diagnosis: Use Valgrind to identify where the memory was allocated.

Deadlocks

Common causes:

  • Circular waits for resources
  • Improper lock ordering

Diagnosis: Use GDB to examine the state of all threads when the program hangs.

Further Reading