c++ 调试(gdb & cppcheck & addr2line)

gdb调试基础

1. 介绍

linux下程序调试工具,可以基于命令行断点调试程序。

2. 入门文档

gdb tutorial
GDB调试指南
linux常用命令–开发调试篇

3. 用例

step1: 使用debug标志编译链接程序:

注意添加 -g

g++ -g hello.cpp -o hello

step2: 使用gdb运行程序

gdb hello

step3: 使用gdb命令调试程序

# 从头运行程序直到遇到断点或程序末尾
r
# 在'fun'函数开始设置断点
b fun
# 将断点设置在第N行
b N
b file.cpp:N
# 删除断点
d N
# 查看断点
info break
# 继续运行直到下一个断点
c
# 运行直到当前函数结束
f
# 运行下一行
s
# 运行接下来的N行
s N
# 类似于s,但据说不会step into (实验的时候进去了...)
n
# 打印变量的当前值
p var
# 设置变量值
set variable var=value
# 打印调用栈
bt
# 退出
q

注意:
你可能会使用set a=1 来设置变量值,但是参考这儿,由于gdb具有其他以a开头的子命令,set 会先匹配这些子命令,导致gdb不知道该选择哪个命令,最后发生错误:

(gdb) set a=1
Ambiguous set command "a=1": .

因此设置变量最好的方式是使用variable

set variable a=1
# 或者
set $a=1

step4: 退出gdb

quit

4. 使用core dump调试程序

如果程序发生崩溃,例如发生段错误,为了查找崩溃原因,可以用gdb调试core文件。
参考linux下core dump【总结】

  • 判断core dump功能是否打开
ulimit -c
# 若输出0表示默认关闭
# 打开core dump 程序
ulimit -c unlimited 
  • 执行程序,生成core文件(打开core dump后,当程序发生崩溃时,会自动生成core文件)
./hello
  • gdb调试core 文件,查看程序崩溃位置
gdb ./hello core

注意:
如果是在windows下使用docker,如果在windows和docker的共享目录下运行程序,会生成不可用的core文件(0字节)。将可执行文件移动到docker目录下运行,就可以正常生成core文件了。

cppcheck

1. 介绍

c/c++代码缺陷静态检查工具。可以检查内存泄漏,变量未初始化等问题。
可检查的问题列表参考:Checks。或者使用代码

cppcheck --doc
cppcheck --errorlist

在这里插入图片描述

2. 使用文档

参考:cppcheck官网

# linux 安装
sudo apt-get install cppcheck

3. 用例

# 检查源文件
cppcheck test.cpp
# 默认只显示错误信息,可以通过--enable显示其他信息
cppcheck --enable=warning,performance test.cpp
cppcheck --enable=unusedFunction test.cpp
cppcheck --enable=all

内存泄漏检测:Valgrind / jemalloc

  • 另一个适用于linux内存问题查找工具Valgrind。参考这儿
  • jemalloc:这个运行速度快
    • jemalloc最早用于FreeBSD(unix变体),简单的说,jemalloc分配+回收性能比较好,而且可以降低内存分配中的碎片,也可以根据自己的需要配置jemalloc内存分配相关参数(tuning,一般情况下不需要调整)
    • jemalloc的Github
    • 编译jemalloc动态库:./configure --enable-prof --disable-static && make -j18(加上enable-prof使得jemalloc能够定位heap),同时在bin目录下也会生成jeprof用于统计profile的结果。
    • Jemalloc的wiki:

内存越界检测:AddressSanitizer

addr2line

  • addr2line 可以根据程序崩溃地址定位代码行
<think>好的,我现在需要回答用户关于如何在C++中进行堆栈监控的问题。用户已经提供了一些相关的引用资料,我需要结合这些资料来构建一个结构清晰、符合要求的回答。 首先,我得理解用户的问题。他们想知道在C++中监控堆栈的方法和实现。根据提供的引用,特别是引用3,里面提到了几种方法,比如利用编译器生成静态调用图、使用.debug_frame的DWARF信息、编译指令列出堆栈使用情况、设置观察点等。我需要将这些方法整理出来,并可能补充一些其他常见的方法。 接下来,我需要确保回答符合系统级指令:使用中文,正确使用LaTeX格式,生成相关问题,引用正确。同时,用户希望回答结构清晰,逐步解决问题,保证真实可靠。 步骤可能包括: 1. 介绍堆栈监控的重要性,比如预防溢出,优化内存使用。 2. 详细列举不同的监控方法,每个方法的具体实现步骤,可能结合代码示例。 3. 引用提供的资料中的建议,比如引用3中的7个方法,可能需要分点说明。 4. 补充一些其他方法,如使用特定工具或库,或者操作系统相关的函数。 5. 最后生成相关问题,如如何检测溢出、优化堆栈使用、工具比较等。 需要注意LaTeX的正确使用,比如行内公式用$...$,独立公式用$$...$$,但根据问题内容,可能数学公式较少,但如果有变量或术语可能需要用。例如,提到堆栈大小计算时,可以用$stack\_size = ...$这样的形式。 另外,代码部分需要用三个反引号包裹,并注明语言。例如,在讲解编译器指令时,可以展示如何用GCC的编译选项。 还要检查是否在引用的地方正确添加了标识符,比如引用3中的建议需要标注[^3]。 现在开始组织内容: 开头段落:说明堆栈监控的重要性,引出方法。 方法部分: 1. 编译器静态分析:使用编译选项如-fstack-usage,生成堆栈使用报告。 2. 调试信息分析:利用DWARF的.debug_frame信息,通过工具如addr2line或objdump解析。 3. 运行时监控:设置观察点,使用调试器(如GDB)监控堆栈指针。 4. 代码插桩:手动插入检查点,记录函数进入和退出时的堆栈地址。 5. 操作系统工具:如ulimit设置堆栈大小限制,或通过信号处理捕获溢出异常。 6. 第三方工具:如Valgrind的Stack usage检查,或者专用分析工具。 每个方法需要详细说明实现步骤,可能给出代码示例或命令示例,比如GCC编译命令,GDB命令,或者信号处理的代码片段。 最后,生成相关问题,比如如何检测堆栈溢出、不同工具的优缺点比较、如何优化堆栈使用等。 确保每个引用出现的地方正确标注,例如在提到编译器选项时引用,在讨论堆栈溢出捕获时引用。 检查是否有遗漏的方法,比如引用3中的异常捕获设计,或者内存检查方法。 现在,将这些思路整合成一个连贯的回答,结构清晰,分点说明,并加入代码块和引用标识。</think>在C++中实现堆栈监控是预防溢出和优化内存使用的关键。以下是具体方法与实现步骤: ### 一、编译器静态分析 1. **编译选项生成堆栈报告** 使用GCC的`-fstack-usage`编译选项,生成每个函数的堆栈使用量报告。例如: ```bash g++ -fstack-usage -c example.cpp -o example.o ``` 编译后生成的`.su`文件会记录函数名和堆栈消耗,如: ``` example.cpp:5:10:func_name 32 static ``` 此方法适用于静态分析,但无法捕获动态分配。 2. **静态调用图分析** 通过`-fdump-ipa-all`生成调用关系图,结合`-Wstack-usage=<size>`设置警告阈值,超过时触发编译警告[^3]。 ### 二、调试信息追踪 1. **DWARF信息解析** 使用`objdump`或`addr2line`解析ELF文件中的`.debug_frame`段,提取堆栈帧信息: ```bash objdump --dwarf=info example.o | grep "CFA" ``` 可定位函数调用的堆栈偏移量。 2. **运行时堆栈地址监控** 在GDB中通过`watch`命令设置观察点,监控堆栈指针`$rsp`的变化: ```gdb gdb ./a.out (gdb) watch $rsp (gdb) run ``` 当堆栈指针越界时触发中断。 ### 三、运行时代码插桩 1. **手动插入检查点** 在函数入口/出口记录当前堆栈地址,计算使用量: ```cpp #include <stdio.h> extern void* __libc_stack_end; // 声明全局堆栈结束地址 void check_stack_usage() { void* current_stack; size_t used = (uintptr_t)__libc_stack_end - (uintptr_t)&current_stack; printf("Stack used: %zu bytes\n", used); } void recursive_func() { check_stack_usage(); recursive_func(); // 递归调用测试溢出 } ``` 需注意`__libc_stack_end`为Glibc特有符号[^2]。 2. **信号处理捕获溢出** 注册信号处理器捕获`SIGSEGV`: ```cpp #include <signal.h> #include <execinfo.h> void handler(int sig) { void* array[10]; size_t size = backtrace(array, 10); fprintf(stderr, "Stack overflow! Backtrace:\n"); backtrace_symbols_fd(array, size, STDERR_FILENO); exit(1); } int main() { signal(SIGSEGV, handler); // 业务代码 } ``` 此方法可捕获非法内存访问,但无法预防溢出[^3]。 ### 四、操作系统级监控 1. **设置堆栈大小限制** 通过`ulimit`命令限制进程堆栈: ```bash ulimit -s 2048 # 设置堆栈上限为2MB ``` 或编程时使用`setrlimit`: ```cpp #include <sys/resource.h> struct rlimit lim = {2048 * 1024, 2048 * 1024}; setrlimit(RLIMIT_STACK, &lim); ``` 2. **内存布局分析** 在Linux中通过`/proc/<pid>/maps`查看堆栈区域: ```bash cat /proc/self/maps | grep stack ``` 输出示例: ``` 7ffd4578e000-7ffd457af000 rw-p 00000000 00:00 0 [stack] ``` ### 五、第三方工具 1. **Valgrind Massif** 使用Massif工具分析堆栈增长: ```bash valgrind --tool=massif --stacks=yes ./a.out ``` 生成报告显示峰值堆栈使用量。 2. **专用分析工具** - **StackTrack**:实时监控多线程堆栈 - **Gperftools**:Google性能工具集中的堆栈分析模块 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值