Requirement
Track the total memory allocated (malloc) and freed (free). Capture backtraces of all malloc and free calls (for now, logging can be ignored). Current Approach For malloc:
I need every malloc call to complete execution. After completion, I retrieve $rax and use malloc_usable_size($rax) to determine the actual allocated memory. For free:
This is simpler since malloc_usable_size($rdi) gives the size being freed. Manually continuing execution from the GDB prompt works fine. However, my debugging case involves thousands of malloc and free calls, so I want this to run automatically without user intervention.
The Issue
When I add continue inside hookpost-myfinish, I observe skipped malloc breakpoints. As a result, malloc_count, free_count, total_malloced, and total_freed become incorrect. I suspect this happens when consecutive malloc calls occur: finish from malloc returns execution to main(), and another continue causes the next malloc entry breakpoint to be skipped. Reproduction Steps Using gdb_commands.txt → Works correctly (manual continuation required). Using gdb_commands_continue.txt → Causes alternate malloc calls to be skipped, leading to incorrect tracking. This seems to be due to how continue interacts with finish, but I’m unsure of the best way to ensure every malloc and free call is properly accounted for without skipping breakpoints.
Could someone help me understand why continue is skipping alternate malloc calls and suggest a reliable way to handle this?
code.c
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h> // Required for malloc_usable_size
int main() {
printf("Starting memory allocation test...\n");
// Allocate memory blocks of different sizes
void *ptr1 = malloc(32);
printf("ptr1 allocated at address: %p\n", ptr1);
free(ptr1);
printf("ptr1 freed");
void *ptr2 = malloc(64);
printf("ptr2 allocated at address: %p\n", ptr2);
printf("ptr2 allocated at address: %p\n", ptr2);
printf("ptr2 allocated at address: %p\n", ptr2);
printf("ptr2 allocated at address: %p\n", ptr2);
printf("ptr2 allocated at address: %p\n", ptr2);
printf("ptr2 allocated at address: %p\n", ptr2);
printf("ptr2 allocated at address: %p\n", ptr2);
printf("ptr2 allocated at address: %p\n", ptr2);
printf("ptr2 allocated at address: %p\n", ptr2);
printf("ptr2 allocated at address: %p\n", ptr2);
printf("ptr2 allocated at address: %p\n", ptr2);
printf("ptr2 allocated at address: %p\n", ptr2);
void *ptr3 = malloc(128);
printf("ptr3 allocated at address: %p\n", ptr3);
free(ptr2);
free(ptr3);
void *ptr4 = malloc(128);
printf("ptr4 allocated at address: %p\n", ptr4);
void *ptr5 = malloc(128);
printf("ptr5 allocated at address: %p\n", ptr5);
void *ptr6 = malloc(256);
printf("ptr6 allocated at address: %p\n", ptr6);
void *ptr7 = malloc(256);
void *ptr8 = malloc(256);
void *ptr9 = malloc(256);
void *ptr10 = malloc(256);
void *ptr11 = malloc(256);
void *ptr12 = malloc(256);
void *ptr13 = malloc(256);
void *ptr14 = malloc(256);
void *ptr15 = malloc(256);
void *ptr16 = malloc(256);
free(ptr5);
free(ptr4);
free(ptr6);
free(ptr7);
free(ptr8);
free(ptr9);
free(ptr10);
free(ptr11);
free(ptr12);
free(ptr13);
free(ptr14);
free(ptr15);
free(ptr16);
printf("Memory allocation test completed.\n");
return 0;
}
gdb_commands.txt
Manually pressing continue works
set unwindonsignal off
set $mc = 0
set $fc = 0
set $mallocsize = 0
set $in_malloc = 0
set $total_malloced = 0
set $total_freed = 0
b memory_test.c:8
b malloc
b free
disable 2 3
commands 1
silent
enable 2 3
continue
end
commands 2
set $mc = $mc + 1
set $mallocsize = $rdi
printf "Asked to malloc %d bytes\n", $mallocsize
set $in_malloc = 1
myfinish
end
commands 3
set $fc = $fc + 1
if ($rdi)
set $total_freed = $total_freed + (size_t)malloc_usable_size($rdi)
printf "Freed %d bytes \n ", (size_t)malloc_usable_size($rdi)
end
continue
end
define myfinish
finish
end
define hookpost-myfinish
set $total_malloced = $total_malloced + (size_t)malloc_usable_size($rax)
printf "Actually malloced %d bytes and total malloced till now is %d \n", (size_t)malloc_usable_size($rax), $total_malloced
set $in_malloc = 0
end
gdb_commands_continue.txt
malloc calls gets skipped with continue.
set unwindonsignal off
set $mc = 0
set $fc = 0
set $mallocsize = 0
set $in_malloc = 0
set $total_malloced = 0
set $total_freed = 0
b memory_test.c:8
b malloc
b free
disable 2 3
commands 1
silent
enable 2 3
continue
end
commands 2
set $mc = $mc + 1
set $mallocsize = $rdi
printf "Asked to malloc %d bytes\n", $mallocsize
set $in_malloc = 1
myfinish
end
commands 3
set $fc = $fc + 1
if ($rdi)
set $total_freed = $total_freed + (size_t)malloc_usable_size($rdi)
printf "Freed %d bytes \n ", (size_t)malloc_usable_size($rdi)
end
continue
end
define myfinish
finish
end
define hookpost-myfinish
set $total_malloced = $total_malloced + (size_t)malloc_usable_size($rax)
printf "Actually malloced %d bytes and total malloced till now is %d \n", (size_t)malloc_usable_size($rax), $total_malloced
set $in_malloc = 0
continue
end
Would appreciate any insights or alternative approaches!
finishat all? You could just doset $total_malloced = $total_malloced + $mallocsizeinside thecommands 2block.$mallocsize = N(size_t)malloc_usable_size($rax) = N+xtotal_freed = N+xHence if you keep adding the $mallocsize it won't give the actual memory allocated, eventually leading to mismatch with total memory freed .malloc_usable_sizein free, it would be better to keep track of allocations and their size. Your current approach will also give you wrong results for double-free. If you really want to implement your project in gdb, I suggest to look into the python API. This way you can collect your data in maps (dicts).