调试指南
April 30, 2026 · View on GitHub
本文档介绍如何在 PHPX 项目中启用和使用调试功能来排查问题。
目录
启用 Debug 模式
方法 1: CMake 命令行参数
# 清理之前的构建
cmake --build . --target clean
# 配置 Debug 模式
cmake -DCMAKE_BUILD_TYPE=Debug .
# 编译
make -j 4
方法 2: 修改 CMakeLists.txt(不推荐)
在 CMakeLists.txt 开头添加:
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type" FORCE)
验证 Debug 模式已启用
编译时会看到以下输出:
-- Building in Debug mode with debug symbols
-- Debug Mode: Enabled with debug symbols and runtime checks
Debug 模式特性
通用特性(所有平台)
-
完整的调试符号
- 生成
.pdb(Windows) 或 DWARF (Linux/macOS) 调试信息 - 支持断点调试和堆栈跟踪
- 生成
-
禁用优化
-O0(Linux/macOS) 或/Od(Windows)- 变量值在调试器中可见
- 代码执行顺序与源代码一致
-
详细的编译输出
- 显示包含的头文件路径
- 更容易发现头文件依赖问题
Windows 平台特有
-
运行时错误检查 (/RTC1)
- 检测未初始化的变量
- 检测栈帧损坏
- 检测整数溢出
-
显示头文件包含 (/showIncludes)
注意: 包含文件: D:\workspace\phpx\include\phpx.h 注意: 包含文件: D:\workspace\phpx\include\phpx_types.h ... -
生成 PDB 文件
phpx.pdb- 用于 Visual Studio 调试phpx-tests.pdb- 测试程序调试符号
Linux/macOS 平台特有
-
GCC/GDB 调试支持
- 完整的 DWARF 调试信息
- 支持 valgrind 内存检查
-
定义 DEBUG 宏
#ifdef DEBUG // 调试专用代码 #endif
Windows 平台调试
使用 Visual Studio 调试
-
打开项目
cmake -G "Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=Debug . -
加载 PDB 文件
- Visual Studio 会自动加载同目录下的
.pdb文件 - 确保
.dll和.pdb在同一目录
- Visual Studio 会自动加载同目录下的
-
设置断点
- 在源代码中点击行号左侧设置断点
- 按 F5 开始调试
-
查看调用堆栈
- 崩溃时自动显示调用堆栈
- 可以查看每个栈帧的局部变量
使用 GDB (MinGW)
# 启动 GDB
gdb bin/phpx-tests.exe
# 运行测试
(gdb) run --gtest_filter="base.error"
# 如果崩溃,查看堆栈
(gdb) bt
# 查看变量
(gdb) print variable_name
# 单步执行
(gdb) next
(gdb) step
运行时错误检查示例
当启用 /RTC1 时,以下错误会被捕获:
// 错误:未初始化的变量
int x;
if (x > 0) { // RTC1 会检测到 x 未初始化
// ...
}
// 错误:栈缓冲区溢出
char buffer[10];
strcpy(buffer, "this string is too long"); // RTC1 会检测到溢出
Linux/macOS 平台调试
使用 GDB
# 启动 GDB
gdb ./bin/phpx-tests
# 运行
(gdb) run
# 设置断点
(gdb) break main.cpp:42
# 查看变量
(gdb) print my_variable
# 单步执行
(gdb) next # 跳过函数调用
(gdb) step # 进入函数调用
# 查看调用堆栈
(gdb) bt
# 继续执行
(gdb) continue
使用 Valgrind 检查内存问题
# 安装 Valgrind
sudo apt-get install valgrind # Ubuntu/Debian
brew install valgrind # macOS
# 检查内存泄漏
valgrind --leak-check=full ./bin/phpx-tests
# 详细输出
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./bin/phpx-tests
# 生成报告
valgrind --leak-check=full --log-file=valgrind.log ./bin/phpx-tests
使用 AddressSanitizer (ASAN)
# 配置时启用 ASAN
cmake -Denable_asan=ON .
# 编译
make -j 4
# 运行(ASAN 会自动检测内存错误)
./bin/phpx-tests
ASAN 可以检测:
- 堆缓冲区溢出
- 栈缓冲区溢出
- 全局缓冲区溢出
- 释放后使用 (use-after-free)
- 返回后使用 (use-after-return)
常见问题排查
问题 1: 段错误 (Segmentation Fault)
症状:
Segmentation fault (core dumped)
排查步骤:
-
使用 GDB 定位崩溃位置
gdb ./bin/phpx-tests (gdb) run # 崩溃后 (gdb) bt -
查看崩溃时的变量值
(gdb) frame 0 # 选择最内层栈帧 (gdb) info locals # 查看局部变量 (gdb) print ptr # 查看指针值 -
启用 ASAN 重新编译
cmake -Denable_asan=ON . make clean && make ./bin/phpx-tests
问题 2: 内存泄漏
症状:
- 程序运行时间越长,内存占用越高
- Valgrind 报告内存泄漏
排查步骤:
-
使用 Valgrind 检查
valgrind --leak-check=full --show-leak-kinds=all ./bin/phpx-tests -
查看泄漏报告
==12345== 100 bytes in 1 blocks are definitely lost ==12345== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==12345== by 0x401234: my_function (main.cpp:42) -
修复泄漏
- 确保每个
malloc/new都有对应的free/delete - 使用智能指针管理资源
- 检查循环中的内存分配
- 确保每个
问题 3: 未定义行为
症状:
- 程序行为不一致
- 不同编译器结果不同
- Release 模式正常,Debug 模式异常
排查步骤:
-
启用 UndefinedBehaviorSanitizer (UBSAN)
cmake -DCMAKE_CXX_FLAGS="-fsanitize=undefined" . make clean && make ./bin/phpx-tests -
常见未定义行为
- 有符号整数溢出
- 空指针解引用
- 数组越界访问
- 类型双关 (type punning)
问题 4: 编译警告
症状:
- 大量编译警告
- 某些警告可能隐藏真正的问题
解决方法:
-
查看所有警告
# Debug 模式下会显示更多警告 cmake -DCMAKE_BUILD_TYPE=Debug . make VERBOSE=1 -
修复常见警告
- 未使用的变量:删除或标记为
[[maybe_unused]] - 类型转换警告:使用显式类型转换
- 未初始化变量:始终初始化变量
- 未使用的变量:删除或标记为
问题 5: PHP 扩展加载失败
症状:
PHP Warning: PHP Startup: Unable to load dynamic library 'phpx.so'
排查步骤:
-
检查依赖库
# Linux ldd phpx.so # macOS otool -L phpx.so # Windows dumpbin /dependents phpx.dll -
检查符号导出
# Linux nm -D phpx.so | grep "T " # Windows dumpbin /exports phpx.dll -
查看详细错误信息
php -d display_startup_errors=1 -d display_errors=1 -m | grep phpx
调试技巧
1. 添加日志输出
#include <iostream>
void my_function() {
#ifdef DEBUG
std::cerr << "[DEBUG] Entering my_function" << std::endl;
#endif
// 函数逻辑
#ifdef DEBUG
std::cerr << "[DEBUG] Exiting my_function" << std::endl;
#endif
}
2. 使用断言
#include <cassert>
void process_data(int* data, int size) {
assert(data != nullptr); // 检查指针有效性
assert(size > 0); // 检查参数范围
// 处理数据
}
3. 条件编译调试代码
#ifdef DEBUG
#define DEBUG_LOG(msg) std::cerr << "[DEBUG] " << msg << std::endl
#else
#define DEBUG_LOG(msg)
#endif
void my_function() {
DEBUG_LOG("Function called");
// ...
}
4. 性能分析
# Linux: 使用 perf
perf record ./bin/phpx-tests
perf report
# macOS: 使用 Instruments
instruments -t "Time Profiler" ./bin/phpx-tests
最佳实践
-
开发时使用 Debug 模式
- 更容易发现问题
- 更好的调试体验
-
发布前切换到 Release 模式
cmake -DCMAKE_BUILD_TYPE=Release . make clean && make -
定期运行内存检查
valgrind --leak-check=full ./bin/phpx-tests -
保持调试符号
- 不要剥离 Release 版本的符号(除非需要减小文件大小)
- 保留 PDB/DWARF 文件用于事后调试
-
使用版本控制
- 提交能复现问题的最小代码
- 记录调试过程和解决方案
相关文档
本文档最后更新:2026-04-30