Hi, today I would like to share a bit one solution that makes me happy on macOS and C programming. Tracking allocated memory to look for memory leaks. It is very careful and quite detailed finds missing memory. It also can be very ellegant disabled by commented out definition of enable it. Maybe not like Valgrind on Linux but for me on macOS is super cool. Enjoy!
// to enable keep it, to disable make next line commented #define MEM_LEAKS_DETECTION // to usage use CALLOC and FREE defined macros // undefine above if you done with tracking #ifndef MEM_TRACK #define MEM_TRACK typedef struct allocation_node { void *pointer; size_t size; const char *file; int line; time_t timestamp; struct allocation_node *next; } allocation_node; static allocation_node *allocation_head = NULL; void add_allocation(void *ptr, size_t size, const char *file, int line) { auto *node = (allocation_node *)malloc(sizeof(allocation_node)); if (!node) { fprintf(stderr, "failed to track allocation at %s:%d\n", file, line); exit(EXIT_FAILURE); } if (0) { allocation_node **current = &allocation_head; while (*current) { if (strcmp((*current)->file, file) == 0 && (*current)->line == line) { allocation_node *to_free = *current; *current = (*current)->next; free(to_free); break; } current = &(*current)->next; } } node->pointer = ptr; node->size = size; node->file = file; node->line = line; node->timestamp = time(0); node->next = allocation_head; allocation_head = node; } void remove_allocation(void *ptr, const char *file, int line) { allocation_node **current = &allocation_head; while (*current) { if ((void*)(*current)->pointer == (void*)ptr) { allocation_node *to_free = *current; *current = (*current)->next; free(to_free); return; } current = &(*current)->next; } fprintf(stderr, "warning: attempt to free untracked or already freed pointer %p at %s:%d\n", ptr, file, line); exit(EXIT_FAILURE); } void check_for_leaks() { allocation_node *current = allocation_head; time_t now = time(0); int detected = 0; while (current) { double seconds_elapsed = difftime(now, current->timestamp); if (seconds_elapsed >= 10) { detected = 1; fprintf(stderr, " leak detected! pointer: %p, Size: %zu, allocated at %s:%d, time elapsed: %.0f seconds\n", current->pointer, current->size, current->file, current->line, seconds_elapsed); } current = current->next; } if (detected) exit(EXIT_FAILURE); } void print_allocations() { allocation_node *current = allocation_head; fprintf(stderr, "active allocations:\n"); while (current) { fprintf(stderr, " pointer: %p, size: %zu, allocated at %s:%d, timestamp: %ld\n", current->pointer, current->size, current->file, current->line, current->timestamp); current = current->next; } } void* tracked_calloc(size_t count, size_t size, const char *file, int line) { void *ptr = calloc(count, size); if (!ptr) { fprintf(stderr, "memory allocation failed at %s:%d (count: %zu, size: %zu)\n", file, line, count, size); exit(EXIT_FAILURE); } add_allocation(ptr, count * size, file, line); //fprintf(stdut, "allocated memory at %p in %s:%d (count: %zu, size: %zu)\n", &ptr, file, line, count, size); return ptr; } void* tracked_track(void *ptr, const char *file, int line) { if (!ptr) { fprintf(stderr, "memory allocation failed at %s:%d (size: %zu)\n", file, line, sizeof(ptr)); exit(EXIT_FAILURE); } add_allocation(ptr, sizeof(ptr), file, line); //fprintf(stdout, "allocated memory at %p in %s:%d (size: %zu)\n", ptr, file, line, sizeof(ptr)); return ptr; } void tracked_free(void *ptr, const char *file, int line) { if (ptr) { remove_allocation(ptr, file, line); free(ptr); //fprintf(stderr, "freed memory at %p in %s:%d\n", ptr, file, line); } else { fprintf(stderr, "attempt to free NULL pointer in %s:%d\n", file, line); } check_for_leaks(); } #ifdef MEM_LEAKS_DETECTION #define CALLOC(count, size) tracked_calloc((count), (size), __FILE__, __LINE__) #define FREE(ptr) tracked_free(ptr, __FILE__, __LINE__) #else #define CALLOC(count, size) calloc(count, size) #define FREE(ptr) free(ptr) #endif #endif
Thanks!