1. 为什么需要Core Dump和GDB调试当程序在Linux环境下崩溃时最令人头疼的莫过于那些偶现的崩溃问题——它们可能一周出现一次也可能在客户现场才会复现。这时候Core Dump就像程序的黑匣子完整记录了崩溃瞬间的内存状态、寄存器值和调用栈信息。而GDB则是解读这个黑匣子的解码器。我曾在阿里处理过一个线上服务崩溃问题每天凌晨3点左右某个Java服务总会神秘消失。通过分析Core文件最终定位到是JNI调用的一个C库存在内存泄漏。这种问题如果仅靠日志排查可能需要数周时间而有了Core Dump我们只用了2小时就找到了根因。2. 配置系统生成Core Dump文件2.1 基础配置方法要让系统在程序崩溃时生成Core Dump首先需要解除系统限制ulimit -c unlimited # 设置core文件大小为无限制但这个设置只在当前终端会话有效。要使配置永久生效需要修改/etc/security/limits.conf文件* soft core unlimited * hard core unlimited2.2 高级配置技巧在实际生产环境中我们还需要考虑以下问题Core文件命名与存储默认情况下Core文件会生成在当前工作目录命名为core。我们可以通过修改/proc/sys/kernel/core_pattern来定制echo /var/core/core-%e-%p-%t /proc/sys/kernel/core_pattern这里的格式说明符%e可执行文件名%p进程ID%t崩溃时间戳SUID程序的特殊处理对于设置了SUID权限的程序如ping、sudo等出于安全考虑默认不会生成Core文件。需要额外配置echo 1 /proc/sys/fs/suid_dumpable容器环境适配在Docker容器中需要确保容器有写入Core目录的权限容器启动时设置了--ulimit core-1主机和容器共享Core文件存储路径3. GDB分析Core文件实战3.1 基础分析命令拿到Core文件后使用GDB加载分析gdb 可执行文件 core文件关键调试命令btbacktrace查看崩溃时的调用栈frame 编号切换到特定栈帧info locals查看当前栈帧的局部变量print 变量打印变量值x/格式 地址检查内存内容3.2 内存问题诊断案例堆溢出分析(gdb) bt #0 0x00007ffff7a9a860 in __GI_raise (sigsigentry6) at ../sysdeps/unix/sysv/linux/raise.c:50 #1 0x00007ffff7a9c448 in __GI_abort () at abort.c:79 #2 0x00007ffff7ad9cd1 in __libc_message (actionactionentrydo_abort, fmtfmtentry0x7ffff7bfdef8 *** %s ***: %s terminated\n) at ../sysdeps/posix/libc_fatal.c:181 #3 0x00007ffff7b7e537 in malloc_printerr (strstrentry0x7ffff7bfe0d8 corrupted size vs. prev_size) at malloc.c:5342 #4 0x00007ffff7b7f6d6 in _int_free (av0x7ffff7e2cb80 main_arena, p0x602000, have_lock0) at malloc.c:4312看到corrupted size vs. prev_size错误说明堆内存被破坏。这时可以检查崩溃线程的栈帧局部变量使用x/32a 0x602000查看被破坏的内存块内容查找附近的内存写操作3.3 多线程问题诊断对于多线程程序需要检查所有线程的状态(gdb) thread apply all bt # 打印所有线程的调用栈常见多线程问题模式死锁两个线程互相持有对方需要的锁使用info threads查看线程状态使用info mutex检查锁的持有情况竞态条件线程执行顺序导致的不同结果检查共享变量的访问使用watch命令设置数据断点4. 高级调试技巧4.1 条件断点对于偶现问题可以设置条件断点break file.c:123 if count 100 # 当count100时触发断点4.2 反向调试GDB 7.0支持反向调试可以倒带程序执行record full # 开始记录执行轨迹 reverse-step # 反向单步执行4.3 Python脚本扩展GDB支持Python扩展可以编写自动化分析脚本class MyBreakpoint(gdb.Breakpoint): def stop(self): val gdb.parse_and_eval(some_var) if int(val) 100: print(Value exceeded threshold!) return True return False5. 内存问题专题5.1 内存泄漏检测使用mtrace工具#include mcheck.h int main() { mtrace(); // 开始跟踪内存分配 // ...程序代码... muntrace(); // 结束跟踪 }运行程序前设置环境变量export MALLOC_TRACEmemory.log5.2 AddressSanitizer编译时添加检测选项gcc -fsanitizeaddress -fno-omit-frame-pointer -g program.c -o programAddressSanitizer可以检测堆栈缓冲区溢出使用释放后的内存内存泄漏重复释放6. 生产环境调试策略6.1 最小化影响在生产环境调试时需要注意使用gdb -p pid附加到进程而不是直接重启设置handle SIGPIPE nostop忽略非关键信号调试完成后及时detach避免影响服务6.2 核心转储优化对于内存大的服务可以使用压缩core文件echo |/usr/bin/gzip -c /var/core/core.%e.%p.gz /proc/sys/kernel/core_pattern只保存必要的内存页echo 0x37 /proc/pid/coredump_filter7. 典型崩溃场景分析7.1 SIGSEGV常见原因空指针解引用Program received signal SIGSEGV, Segmentation fault. 0x0000000000400526 in foo () at test.c:5 5 return *ptr; (gdb) print ptr $1 (int *) 0x0栈溢出(gdb) bt #0 0x0000000000400541 in recursive (n1048576) at stack.c:6 #1 0x0000000000400551 in recursive (n1048575) at stack.c:7 ... # 非常深的递归调用栈7.2 死锁诊断示例(gdb) thread apply all bt Thread 2 (Thread 0x7ffff6e2b700 (LWP 12345)): #0 __lll_lock_wait () at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135 #1 0x00007ffff7bc7e25 in pthread_mutex_lock (mutex0x601080) at pthread_mutex_lock.c:61 #2 0x0000000000400768 in thread_func (arg0x0) at deadlock.c:15 Thread 1 (Thread 0x7ffff7fe1740 (LWP 12344)): #0 __lll_lock_wait () at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135 #1 0x00007ffff7bc7e25 in pthread_mutex_lock (mutex0x6010c0) at pthread_mutex_lock.c:61 #2 0x00000000004007a2 in main () at deadlock.c:30可以看到两个线程各自持有一个锁同时等待对方持有的锁形成了典型的死锁。8. 调试工具链扩展除了GDB完整的调试工具链还包括Valgrind内存错误检测valgrind --leak-checkfull ./programstrace系统调用跟踪strace -f -o trace.log ./programltrace库函数调用跟踪ltrace -f -o libtrace.log ./programperf性能分析perf record -g ./program perf report在实际工作中我通常会先用strace/ltrace快速定位问题范围再用GDB深入分析核心问题。对于内存问题Valgrind和AddressSanitizer是必不可少的工具。