一、ASan 是什么
ASan 全称是 AddressSanitizer,是编译器提供的内存错误检测工具。它会在程序运行时检查:
- 数组或缓冲区越界;
- 空指针和非法地址访问;
- 释放后继续使用;
- 重复释放内存;
- 堆内存被提前破坏。
普通 Release 版本经常是在内存已经被破坏后才崩溃,调用栈只能看到最后出错的位置。ASan 通常可以更早捕获第一次非法访问,并显示源码文件、行号和调用栈。
ASan 不会改变游戏业务逻辑,只是在程序中加入额外的内存检查代码。
二、为什么不能用于线上
ASan 版本只用于测试环境,不能直接替换线上 Server:
- 内存占用通常增加约 2 倍;
- 执行速度会变慢;
- 会改变线程执行时序;
- 长时间运行可能产生较多诊断日志;
- 不能检测所有类型的问题。
线上仍然使用普通 Release 版本,ASan 只部署到独立测试目录,使用测试数据库和测试角色。
三、编译环境要求
Linux 编译机需要安装:
cmake
ninja
g++
clang
Ubuntu 可以执行:
sudo apt update
sudo apt install cmake ninja-build build-essential clang llvm
确认工具可用:
cmake --version
ninja --version
g++ --version
四、配置 ASan 构建目录
当前工程使用独立构建目录:
/opt/tlbb-build/v0-85-8130/build/game-asan
进入 Linux 编译机后执行:
cmake \
-S /opt/tlbb-build/v0-85-8130/GameCode/Server/Server/GameServer \
-B /opt/tlbb-build/v0-85-8130/build/game-asan \
-G Ninja \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DTLBB_BUILD_GAME_SERVER=ON \
-DTLBB_BUILD_BILLING=OFF \
-DTLBB_BUILD_LOGIN=OFF \
-DTLBB_BUILD_SHARE_MEMORY=OFF \
-DTLBB_BUILD_WORLD=OFF \
-DTLBB_ENABLE_WINDOWS_UPDATE_FILE_CHECK=ON \
-DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer" \
-DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address" \
-DCMAKE_SHARED_LINKER_FLAGS="-fsanitize=address"
参数说明:
-fsanitize=address 启用 ASan 检查
-fno-omit-frame-pointer 保留帧指针,方便生成完整调用栈
-O2 保持接近 Release 的优化级别
-g 保留调试符号
RelWithDebInfo 优化代码,同时保留调试信息
五、编译 Server
cmake --build \
/opt/tlbb-build/v0-85-8130/build/game-asan \
--target Server \
--parallel 8
成功时会看到:
[1353/1353] Linking CXX executable bin/Server
生成文件:
/opt/tlbb-build/v0-85-8130/build/game-asan/bin/Server
六、确认确实启用了 ASan
检查编译参数:
ninja -C /opt/tlbb-build/v0-85-8130/build/game-asan \
-t commands Server | grep -oE \
-- '-fsanitize=address|-fno-omit-frame-pointer|-O2|-g|-DNDEBUG' | sort -u
应看到:
-fsanitize=address
-fno-omit-frame-pointer
-O2
-g
-DNDEBUG
检查程序:
file /opt/tlbb-build/v0-85-8130/build/game-asan/bin/Server
ldd /opt/tlbb-build/v0-85-8130/build/game-asan/bin/Server | grep asan
程序应包含:
with debug_info
not stripped
并能看到 libasan.so。
七、运行诊断版
建议在测试副本目录运行,不要使用线上目录:
cd /opt/tlbb-build/v0-85-8130/build/game-asan/bin
设置 ASan 参数:
export ASAN_OPTIONS="halt_on_error=1:abort_on_error=1:detect_leaks=0:fast_unwind_on_fatal=0:symbolize=1:log_path=${LOG_DIR}/asan"
启动:
./Server -loadscriptonce
其中:
abort_on_error=1 发现错误后立即终止
halt_on_error=1 停止继续执行损坏路径
detect_leaks=0 长时间运行服务端时关闭泄漏统计
fast_unwind_on_fatal=0 使用更完整的调用栈
log_path=... 将诊断结果写入指定文件
如需保留 core 文件,可以先执行:
ulimit -c unlimited
八、如何看 ASan 日志
典型日志如下:
ERROR: AddressSanitizer: heap-buffer-overflow
READ of size 4
#0 0x... in SomeFunction source.cpp:123
#1 0x... in OtherFunction other.cpp:456
重点查看第一个属于 TLBB 源码的调用帧:
#0 ...
不要只看最后的 SIGSEGV 或 abort。最后的崩溃位置可能只是内存破坏被发现的地方,真正的问题通常在更早的 READ 或 WRITE。
如果没有自动显示源码行号,可以使用:
llvm-symbolizer
或者:
addr2line -Cfipe \
/opt/tlbb-build/v0-85-8130/build/game-asan/bin/Server \
0x程序地址
九、推荐的复现流程
- 复制一份测试服配置、脚本和数据库。
- 使用 ASan 版
Server,不要替换线上 Release 文件。
- 记录复现前的角色、场景和操作步骤。
- 重放触发崩溃的封包或正常操作。
- 优先查看 ASan 报出的第一处非法读写。
- 修复源码后重新编译并重复测试。
- 确认正常玩家流程没有异常后,再编译正式 Release。
十、正式版本与 ASan 版本必须分开
正式版本:
/opt/tlbb-build/v0-85-8130/build/game-release/bin/Server
ASan 版本:
/opt/tlbb-build/v0-85-8130/build/game-asan/bin/Server
不要把 ASan 版复制到:
/home/tlbb/Server/Server
ASan 适合定位“哪里第一次写坏了内存”,正式代码修复仍然需要增加服务端边界检查、空指针检查和非法封包拦截。