Watchdog wraps dynamic memory functions (malloc, calloc, realloc, free) and
tracks all memory activity at runtime:
- Memory Leak Detection
- Buffer Overflows / Out-of-Bounds Access
- Double Free Detection
- Thread Safe
- Verbose Logging with Optional File Output
- Minimal Integration – Just One Header and One C File
YouTube - Stop Memory Leaks In C With Watchdog
#define WATCHDOG_ENABLE
#include "watchdog.h"
#include <stdio.h>
#include <stdlib.h>
int main([[maybe_unused]] int argc, [[maybe_unused]] char **argv) {
bool enable_verbose_log = true;
bool log_to_file = false;
bool enable_color_output = true;
w_init(enable_verbose_log, log_to_file, enable_color_output);
int *buffer = malloc(sizeof *buffer * 1024);
free(buffer);
return EXIT_SUCCESS;
}When I first started learning C and low-level programming, I barely understood pointers, let alone used them with confidence. Coming from a Python background, the whole process felt intimidating. But I was determined.
At the time, I had just finished a Computer Architecture course and was wrapping up an Operating Systems class. I enjoyed these classes far more than anything I had done in Python or web development. Seeing "how the sausage is made" didn't put me off; it made me hungry for more. Computer Architecture introduced me to assembly, but that felt too low-level for my taste. I wanted to build something useful in a reasonable amount of time.
Yes, we have all heard the horror stories about C's "footguns" and lack of safety. Rather than scare me off, they made me curious. C's simplicity and its explicit control over memory made learning it super fun.
For a while, I rode the high of learning a new language. I churned out countless "Hello, World!" programs in my minimal but efficient Neovim setup. I use Neovim by the way. Then I tried writing a real program and I found myself in a pit full of segmentation faults, buffer overflows, invalid memory accesses, leaks, and a blizzard of compiler errors and warnings.
I knew about tools like AddressSanitizer and Valgrind, and I did try them. But as a beginner, I found their output overwhelming. I wanted something cleaner, friendlier, and more approachable. I couldn't find something that of that kind, so I built my own.
These days, I do use Valgrind and AddressSanitizer. But I'm glad I took the time to create Watchdog. The project taught me a lot about C, memory management, and computer architecture.
Include watchdog.h and watchdog.c in your project.
Then #include watchdog.h in a source/header file and pass flag -DWATCHDOG_ENABLE to
the CFLAGS of your build system to enable the debugger or add #define WATCHDOG_ENABLE
to a file.
Verbose logging is on by default, log to file is off by default, and color output is off by default.
static bool verbose_log = true;
static bool log_to_file = false;
static bool color_output = false;To customize the defaults, pass appropriate boolean to w_init.
bool enable_verbose_log = true;
bool log_to_file = false;
bool enable_color_output = true;
w_init(enable_verbose_log, log_to_file, enable_color_output);Enabling log_to_file will direct log output to a log file named watchdog.log.
Color output is turned off if log_to_file is enabled regardless of the
enable_color_output variable value.
If enable_verbose_log is set to false, only errors will be logged.
Code samples are located in a dedicated examples/ folder.
void malloc_example(void) {
size_t count = 5;
int *buffer = malloc(sizeof *buffer * 5);
for (size_t i = 0; i < count; i++) {
buffer[i] = -(i - count) * count;
}
for (size_t i = 0; i < count; i++) {
printf("%d ", buffer[i]);
}
putchar('\n');
free(buffer);
buffer = NULL;
}void realloc_example(void) {
short *buffer = malloc(34222);
buffer = realloc(buffer, 2342);
buffer = realloc(buffer, 2342342);
buffer = realloc(buffer, 2);
buffer = realloc(buffer, 10);
buffer = realloc(buffer, 0);
free(buffer);
buffer = NULL;
}void calloc_example(void) {
// Demonstrates correct usage of calloc
size_t count = 5;
int *buffer = calloc(count, sizeof *buffer);
for (size_t i = 0; i < count; i++) {
printf("%d ", buffer[i]); // should output 0
}
putchar('\n');
free(buffer);
buffer = NULL;
}void free_example(void) {
// Demonstrates correct usage of free
size_t count = 5;
short int *buffer0 = malloc(sizeof *buffer0 * count);
unsigned char *buffer1 = calloc(count, sizeof *buffer1);
long double *buffer2 = realloc(buffer2, sizeof *buffer2 * count);
free(buffer1);
buffer1 = NULL;
free(buffer2);
buffer2 = NULL;
free(buffer0);
buffer1 = NULL;
}void leak_example(void) {
unsigned long long int *buffer = malloc(sizeof *buffer * 20);
// intentionally not calling free
// watchdog will detect this and report it
}void overflow_example(void) {
char *buffer = malloc(sizeof *buffer * 10);
strcpy(buffer, "This will overflow"); // out-of-bounds write
// of buffer overflows will be detected with using canary values
}void double_free_example(void) {
char **buffer = malloc(sizeof *buffer * 20);
free(buffer);
free(buffer); // triggers a double-free error
}void invalid_free_example(void) {
float *buffer;
free(buffer); // will trigger an error since buffer wasn't allocated
}This project is licensed under the MIT License.
No. It's intended for development/debug builds.
No. It only monitors dynamic memory (heap).
POSIX-compliant systems (Linux, macOS). Windows support is limited (for now).








