如何利用 ASan 与 HWAsan 精准定位 Android 端侧推理引擎的内存损坏
在开发基于 ncnn、MNN 或 TFLite 等框架的 Android 端侧推理引擎时,由于涉及大量 C++ 原始指针操作、高性能内存池管理以及复杂的并发调度,开发者常会遇到难以定位的内存损坏问题。本文将介绍如何通过 ASan (AddressSanitizer) 与 HWAsan (Hardware-assisted AddressSanitizer) 快速锁定代码中的内存隐患。
1. 为什么需要这些工具?
传统的调试方式依赖于 Log 打印或 gdb 挂载,但这在多线程、高吞吐的推理场景下效率极低。ASan 是一种基于编译器的内存错误检测工具,它能捕获以下错误:
– 堆/栈缓冲区溢出 (Buffer Overflow)
– 使用已释放内存 (Use-after-free)
– 内存泄漏 (Memory Leak)
HWAsan 则是 ASan 的硬件辅助版,专门针对 ARM64 设计,内存开销仅为 ASan 的一部分,非常适合在 Android 11 及更高版本的真实推理场景中进行压测。
2. 快速开启 ASan 的实操步骤
第一步:修改 CMakeLists.txt
你需要在编译推理库时注入检测代码:
# 在 CMake 脚本中开启 sanitizer 标志
if(ENABLE_ASAN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address")
endif()
第二步:配置 wrap.sh
ASan 需要在应用启动前劫持内存分配。在工程的 src/main/resources/lib/
#!/system/bin/sh
HERE=$(dirname $0)
# 开启日志输出到 logcat 并在检测到错误时崩溃以便收集堆栈
export ASAN_OPTIONS=log_path=/sdcard/asan.log:allow_user_segv_handler=1
LD_PRELOAD=$HERE/libclang_rt.asan-aarch64-android.so "$@"
注意:libclang_rt.asan-aarch64-android.so 需从 NDK 的工具链目录中手动拷贝至对应 lib 目录。
3. HWAsan:Android 11+ 的优选方案
HWAsan 不需要复杂的 wrap.sh。你只需在 build.gradle 的 cppFlags 中添加 -fsanitize=hwaddress,并在 AndroidManifest.xml 的
<application android:allowNativeHeapPointerTagging="false" ...>
4. 实战:定位推理中的缓冲区溢出
假设我们在处理模型输出 Tensor 时,不小心写错了一个索引:
#include <vector>
#include <iostream>
extern "C" JNIEXPORT void JNICALL
Java_com_ai_demo_InferenceEngine_nativeProcess(JNIEnv* env, jobject thiz, jlong ptr) {
std::vector<float> output_tensor(100, 0.0f);
// 故意越界访问第 101 个元素
float bad_val = output_tensor[100];
LOGD("Val: %f", bad_val);
}
在开启 ASan 后,运行该代码 Logcat 会立即输出详细报告:
==1234==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x...
READ of size 4 at 0x... thread T0 (ai.demo.app)
#0 0x... in Java_com_ai_demo_InferenceEngine_nativeProcess jni_bridge.cpp:8
#1 0x... (base.odex+0x...)
报错信息会直接指出越界发生的文件名、行号以及内存块的分配来源,定位速度提升数倍。
5. 小结
在 Android AI 技术栈的开发中,ASan 用于本地单元测试快速排障,HWAsan 用于集成测试长效压测。掌握这两个工具,能大幅提升推理引擎的稳定性和代码质量,避免上线后因内存问题导致的大面积闪退。
汤不热吧