TypePHP 编译器
https://swoole.com/aot/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
8.2 KiB
8.2 KiB
AddressSanitizer 使用指南
📋 概述
AddressSanitizer (ASan) 是一个快速的内存错误检测工具,可以检测:
- 堆缓冲区溢出/下溢
- 栈缓冲区溢出/下溢
- 全局缓冲区溢出/下溢
- 释放后使用(Use-after-free)
- 返回后使用(Use-after-return)
- 重复释放(Double-free)
- 内存泄漏
🚀 快速开始
Windows (MSVC)
# 启用 AddressSanitizer
php bin/compiler.php your-app.php --sanitize=address
# 或简写
php bin/compiler.php your-app.php --sanitize=addr
要求:
- Visual Studio 2019 16.9+ 或 Visual Studio 2022
- MSVC 编译器版本 19.29+
Linux/macOS (GCC/Clang)
# 单个 sanitizer
php bin/compiler.php your-app.php --sanitize=address
# 多个 sanitizer(用逗号分隔)
php bin/compiler.php your-app.php --sanitize=address,undefined
# 可用的 sanitizer 类型:
# - address: 地址错误检测
# - undefined: 未定义行为检测
# - thread: 线程竞争检测
# - memory: 未初始化内存读取检测
# - leak: 内存泄漏检测
🔍 示例输出
当检测到内存错误时,AddressSanitizer 会输出详细的错误信息:
=================================================================
==12345==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000010
READ of size 4 at 0x602000000010 thread T0
#0 0x7ff6abc12345 in main D:\workspace\compiler\examples\test.php:10
#1 0x7ff6abc67890 in __scrt_common_main_seh
0x602000000010 is located 0 bytes to the right of 16-byte region [0x602000000000,0x602000000010)
allocated by thread T0 here:
#0 0x7ff6abc98765 in operator new[]
#1 0x7ff6abc12340 in main D:\workspace\compiler\examples\test.php:8
SUMMARY: AddressSanitizer: heap-buffer-overflow
=================================================================
💡 使用建议
1. 开发阶段启用
在开发和测试阶段启用 AddressSanitizer,可以帮助您尽早发现内存错误:
# 编译带 AddressSanitizer 的版本
php bin/compiler.php debug-test.php --sanitize=address --no-console
# 运行测试
.\debug-test.exe
2. 不要与优化同时使用
AddressSanitizer 会降低程序性能(约 2x),建议:
- 开发时:
-O0 --sanitize=address - 发布时:
-O2(不使用 sanitizer)
3. 结合调试信息使用
# 同时启用调试信息和 AddressSanitizer
php bin/compiler.php app.php --debug-info --sanitize=address
这样可以在错误报告中看到源代码行号。
🛠️ 常见用例
检测数组越界
<?php
function test_buffer_overflow()
{
$arr = [1, 2, 3, 4, 5];
// 这会触发 AddressSanitizer 错误
$value = $arr[10]; // 越界访问
return $value;
}
function main()
{
test_buffer_overflow();
}
检测释放后使用
<?php
class TestClass
{
public function __destruct()
{
// 对象被销毁
}
}
function test_use_after_free()
{
$obj = new TestClass();
unset($obj); // 对象被销毁
// 如果继续访问 $obj,可能触发错误
// (PHP 的垃圾回收机制通常会防止这种情况)
}
function main()
{
test_use_after_free();
}
检测内存泄漏
<?php
function test_memory_leak()
{
// 在 C++ 层分配的内存如果没有正确释放
// AddressSanitizer 会检测到
}
function main()
{
test_memory_leak();
}
⚙️ 高级配置
环境变量
AddressSanitizer 支持通过环境变量进行配置:
Windows
# 设置 ASan 选项
$env:ASAN_OPTIONS = "detect_leaks=1:print_stats=1"
# 运行程序
.\your-app.exe
Linux/macOS
# 设置 ASan 选项
export ASAN_OPTIONS="detect_leaks=1:print_stats=1"
# 运行程序
./your-app
常用选项
| 选项 | 说明 | 默认值 |
|---|---|---|
detect_leaks |
检测内存泄漏 | 1 |
print_stats |
打印统计信息 | 0 |
abort_on_error |
错误时中止程序 | 0 |
log_path |
日志文件路径 | stderr |
halt_on_error |
第一个错误后停止 | 1 |
示例:
# Windows
$env:ASAN_OPTIONS = "detect_leaks=1:print_stats=1:log_path=asan.log"
.\your-app.exe
# Linux/macOS
export ASAN_OPTIONS="detect_leaks=1:print_stats=1:log_path=asan.log"
./your-app
🔧 平台特定说明
Windows (MSVC)
限制:
- 仅支持
addresssanitizer - 需要 Visual Studio 2019 16.9+ 或更新版本
- 可能与某些第三方库不兼容
注意事项:
- AddressSanitizer 会增加可执行文件大小
- 运行时性能下降约 2x
- 内存使用增加约 2-3x
Linux (GCC/Clang)
支持的 sanitizer:
address- 地址错误undefined- 未定义行为thread- 线程竞争memory- 未初始化内存leak- 内存泄漏
组合使用:
# AddressSanitizer + UndefinedBehaviorSanitizer
php bin/compiler.php app.php --sanitize=address,undefined
# ThreadSanitizer(不能与其他 sanitizer 同时使用)
php bin/compiler.php app.php --sanitize=thread
macOS (Clang)
与 Linux 类似,但需要注意:
- MemorySanitizer 在 macOS 上可能不可用
- 建议使用 Homebrew 安装最新版本的 Clang
brew install llvm
export CC=/usr/local/opt/llvm/bin/clang
export CXX=/usr/local/opt/llvm/bin/clang++
php bin/compiler.php app.php --sanitize=address
🐛 故障排除
Q: 编译时提示不支持 sanitizer?
A: 检查编译器版本:
# Windows
cl
# Linux
gcc --version
clang --version
确保使用支持 sanitizer 的版本。
Q: 运行时出现 "Sanitizer CHECK failed"?
A: 这可能是由于:
- 库冲突 - 确保所有库都用相同的 sanitizer 编译
- 不兼容的选项 - 尝试移除其他优化选项
- 系统限制 - 检查是否有足够的内存
Q: AddressSanitizer 报告误报?
A: 可能的原因:
- 第三方库的问题 - 考虑抑制特定模块的检查
- 已知的无害问题 - 使用
__attribute__((no_sanitize))禁用特定函数
// 在 C++ 代码中
__attribute__((no_sanitize("address")))
void safe_function() {
// 这个函数不会被 ASan 检查
}
Q: 性能太慢怎么办?
A:
- 只在调试时使用 sanitizer
- 发布版本禁用 sanitizer
- 使用
-O1而不是-O0(仍保持较好的检测能力)
📊 性能影响
| 配置 | 速度 | 内存 | 适用场景 |
|---|---|---|---|
| 无 sanitizer, -O2 | 100% | 100% | 生产环境 |
| -fsanitize=address, -O0 | ~50% | ~200% | 开发调试 |
| -fsanitize=address, -O1 | ~60% | ~180% | 测试环境 |
| -fsanitize=undefined, -O0 | ~80% | ~120% | 轻量级检查 |
🎯 最佳实践
-
持续集成中启用
# .github/workflows/test.yml - name: Compile with ASan run: php bin/compiler.php tests/*.php --sanitize=address - name: Run tests run: ./run-tests.sh -
定期扫描
- 每周运行一次带 sanitizer 的完整测试套件
- 修复所有报告的问题
-
结合其他工具
- Valgrind(Linux)
- Dr. Memory(Windows)
- Static analyzers(静态分析器)
-
文档化已知问题
- 记录无法立即修复的 sanitizer 警告
- 说明为什么这些警告可以忽略
📚 相关资源
🔗 编译器命令参考
# Windows - 基础用法
php bin/compiler.php app.php --sanitize=address
# Windows - 结合其他选项
php bin/compiler.php app.php --sanitize=address --debug-info --no-console
# Linux/macOS - 单个 sanitizer
php bin/compiler.php app.php --sanitize=address
# Linux/macOS - 多个 sanitizer
php bin/compiler.php app.php --sanitize=address,undefined
# Linux/macOS - 内存泄漏检测
php bin/compiler.php app.php --sanitize=leak
希望这个指南能帮助您有效使用 AddressSanitizer 来检测和修复内存错误!