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

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)

限制:

  • 仅支持 address sanitizer
  • 需要 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: 这可能是由于:

  1. 库冲突 - 确保所有库都用相同的 sanitizer 编译
  2. 不兼容的选项 - 尝试移除其他优化选项
  3. 系统限制 - 检查是否有足够的内存

Q: AddressSanitizer 报告误报?

A: 可能的原因:

  1. 第三方库的问题 - 考虑抑制特定模块的检查
  2. 已知的无害问题 - 使用 __attribute__((no_sanitize)) 禁用特定函数
// 在 C++ 代码中
__attribute__((no_sanitize("address")))
void safe_function() {
    // 这个函数不会被 ASan 检查
}

Q: 性能太慢怎么办?

A:

  1. 只在调试时使用 sanitizer
  2. 发布版本禁用 sanitizer
  3. 使用 -O1 而不是 -O0(仍保持较好的检测能力)

📊 性能影响

配置 速度 内存 适用场景
无 sanitizer, -O2 100% 100% 生产环境
-fsanitize=address, -O0 ~50% ~200% 开发调试
-fsanitize=address, -O1 ~60% ~180% 测试环境
-fsanitize=undefined, -O0 ~80% ~120% 轻量级检查

🎯 最佳实践

  1. 持续集成中启用

    # .github/workflows/test.yml
    - name: Compile with ASan
      run: php bin/compiler.php tests/*.php --sanitize=address
    
    - name: Run tests
      run: ./run-tests.sh
    
  2. 定期扫描

    • 每周运行一次带 sanitizer 的完整测试套件
    • 修复所有报告的问题
  3. 结合其他工具

    • Valgrind(Linux)
    • Dr. Memory(Windows)
    • Static analyzers(静态分析器)
  4. 文档化已知问题

    • 记录无法立即修复的 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 来检测和修复内存错误!