GDB中使用finish和continue时malloc断点被跳过的问题排查
GDB自动continue导致malloc断点被跳过,内存统计数据错误
需求
跟踪已分配(malloc)和释放(free)的总内存,捕获所有malloc与free调用的回溯(暂不考虑日志)。
当前实现
- malloc处理:等待每个malloc调用执行完成,通过
$rax寄存器结合malloc_usable_size($rax)获取实际分配内存大小。 - free处理:通过
malloc_usable_size($rdi)直接获取释放的内存大小。
手动在GDB提示符输入continue可正常统计,但调试场景包含数千次malloc/free调用,需实现无人干预的自动执行。
问题现象
在hookpost-myfinish中添加continue后,出现malloc断点被跳过的情况,导致malloc_count、free_count、total_malloced、total_freed统计数据错误。推测该问题发生在连续malloc调用场景:从malloc执行finish返回至main()后,continue操作跳过了下一个malloc入口断点。
复现步骤
- 使用
gdb_commands.txt:手动输入continue可正常统计。 - 使用
gdb_commands_continue.txt:交替的malloc调用被跳过,统计结果错误。
相关代码
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(手动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 end
gdb_commands_continue.txt(自动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
原因分析
问题出在finish命令与hookpost-myfinish中continue的交互逻辑:
finish命令会让程序运行到malloc函数返回后暂停,此时GDB处于断点暂停状态。- 在
hookpost-myfinish中执行continue,GDB会直接从当前暂停点继续运行程序。但对于连续的malloc调用,GDB的断点触发机制可能因为刚完成finish的上下文处理,没有及时重新监控后续的malloc入口断点,导致跳过下一次malloc的断点触发。 - 手动输入
continue时,GDB处于正常的用户交互暂停状态,所有断点监控逻辑已经正确初始化,因此能正常捕获所有malloc断点。
解决方案
放弃依赖finish和hookpost-myfinish的组合,改为在malloc入口断点处手动设置临时返回地址断点,确保每次malloc返回时都能触发统计逻辑,且自动continue不会跳过后续断点。修改后的GDB命令脚本如下:
修正后的gdb_commands_auto.txt
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 # 获取malloc的返回地址(x86_64架构下,返回地址存在栈顶) set $ret_addr = *(void**)$rsp # 设置临时断点在返回地址,触发后自动删除 break *$ret_addr commands silent # 统计实际分配内存 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 # 删除当前临时断点($bpnum是刚创建的断点编号) delete $bpnum # 继续执行 continue end # 从malloc入口继续执行到返回地址断点 continue 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
方案说明
- 每次触发malloc入口断点时,获取栈顶的返回地址(x86_64架构下,函数调用后返回地址会被压入栈顶)。
- 在返回地址处设置临时断点,并绑定统计逻辑:触发时计算实际分配内存、更新统计值,然后删除临时断点并continue。
- 这种方式避免了
finish命令的上下文问题,确保每个malloc的返回和后续的malloc调用都能被正确捕获,实现完全自动的内存统计。
内容的提问来源于stack exchange,提问作者Puspaul Halder
相关产品推荐
相关产品推荐

