简介这份资源面向在高通8155p平台上进行QNXAndroid9双系统开发的工程师聚焦于开启ASANAddressSanitizer以定位内存越界、泄漏等疑难问题的编译配置方法。包内同时提供Android.mk与Android.bp两套构建脚本示例覆盖libAsanDebug、asan_debugd及其vnd变体、libclang_rt.asan运行时库等模块帮助开发者解决ASAN无法使能、编译报错等常见障碍。资源共18个文件包含4个mk、4个bp、4个cpp、2个so、2个h及2个mk备份文件涵盖构建脚本、源码、头文件与预编译库压缩包约1.21MB结构紧凑便于对照移植。目前已有1995人学习下载适合不熟悉Android.mk/Android.bp写法或反复遭遇编译问题的开发者参考可快速掌握ASAN在车机双系统环境下的集成思路与排错方向。1. 高通8155p上开ASAN为什么你的Android.bp改了没生效在高通8155p这类车机平台上做QNXAndroid9双系统开发最让人头疼的不是写业务代码而是内存问题排查。Native层一个野指针踩了堆进程随机崩溃tombstone里只有几行backtrace符号还经常对不上。这时候ASANAddressSanitizer几乎是唯一能快速定位到“谁在哪儿越界写”的手段。但坑就坑在Android9的构建系统正处在Android.mk向Android.bp迁移的过渡期同一个模块可能两套构建脚本并存你改了Android.bp加-fsanitizeaddress编译过了跑起来却没任何ASAN报错——因为实际生效的是旁边那个Android.mk。这篇笔记就把8155p平台上QNXAndroid9开ASAN的完整排查路径拆开从“改了不生效”这个最反直觉的坑讲起到根因、到可抄的配置、再到怎么快速确认ASAN真的挂上了。适合正在车机项目里被native内存问题折磨的开发者新手能照着命令跑通熟手能看到构建系统的取舍逻辑。2. ASAN开了没反应Android.mk与Android.bp双构建脚本的优先级陷阱2.1 现象编译通过、进程照崩、logcat里没有ASAN输出典型场景是这样的你在某个native模块目录下看到两个文件——Android.mk和Android.bp。你凭直觉在Android.bp的cflags里加了-fsanitizeaddress-fno-omit-frame-pointer然后mm编译push到设备跑起来该崩还是崩logcat里搜AddressSanitizer一个字都没有。更迷惑的是你甚至把Android.mk删了重新编译报错说找不到模块定义——说明构建系统确实在读某个脚本但ASAN就是没生效。这个现象在8155p的Android9 BSP里特别常见因为高通给的vendor部分大量保留Android.mk而AOSP侧新模块用Android.bp两套并存。构建系统Soong Kati对同一个模块名如果两处都定义行为是未定义的——不同版本可能报重复定义也可能静默选一个。你改的那个文件很可能根本不是最终生效的那个。2.2 根因Soong与Kati的模块解析顺序以及LOCAL_CFLAGS被覆盖Android9的构建是Soong读Android.bp和Kati读Android.mk转Ninja混合的。对于同一个模块名如果只有Android.bpSoong生成规则。如果只有Android.mkKati生成规则。如果两者都有Android9早期版本会报duplicate module但部分高通BSP打了补丁后变成“mk优先”或“bp优先”取决于build/soong/里的合并逻辑。更隐蔽的坑是即使你改对了文件LOCAL_CFLAGS可能被后续的include或LOCAL_CFLAGS 覆盖。比如模块自己写了LOCAL_CFLAGS : -O2你前面加的-fsanitizeaddress被:冲掉。或者LOCAL_SANITIZE这个专用变量没设只靠LOCAL_CFLAGS手塞链接阶段缺libclang_rt.asan运行时直接静默失败。还有一个8155p特有的点QNX侧和Android侧共享部分内存区域ASAN默认只插桩Android侧进程。如果你要查的是跨系统共享内存的越界光开Android ASAN不够得确认问题确实发生在Android native heap上。2.3 解决先确认生效脚本再用LOCAL_SANITIZE正确开启第一步不是改代码是确认哪个脚本生效。在项目根目录执行# 在Android源码根目录查模块定义来自哪个文件 # 把 module_name 换成你的模块名 ./build/soong/soong_ui.bash --make-mode --dump-many-modules 2/dev/null | grep -A5 module_name如果这条太重用更直接的办法在模块目录下临时把Android.bp改名重新编译。如果报错说模块未定义说明生效的是Android.mk如果照常编译说明生效的是Android.bp。确认后只改生效的那个文件。Android.mk里正确开ASAN的写法# Android.mk 中开启ASAN LOCAL_PATH : $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE : my_native_module LOCAL_SRC_FILES : main.cpp # 关键用LOCAL_SANITIZE而不是手塞cflags # address 开启ASANundefined可选开UBSan LOCAL_SANITIZE : address # 保留帧指针否则backtrace符号会丢 LOCAL_CFLAGS -fno-omit-frame-pointer -g # 关闭优化避免变量被优化掉导致定位不准 LOCAL_CFLAGS -O0 include $(BUILD_EXECUTABLE)Android.bp里对应写法// Android.bp 中开启ASAN cc_binary { name: my_native_module, srcs: [main.cpp], // sanitize 是bp的专用属性不要手写-fsanitize sanitize: { address: true, // 可选undefined_behavior: true, }, cflags: [ -fno-omit-frame-pointer, -g, -O0, ], }逻辑说明LOCAL_SANITIZE : address和sanitize: { address: true }是构建系统识别的专用开关它会自动帮你链接对应的运行时库libclang_rt.asan并设置正确的编译和链接flag。手写-fsanitizeaddress只加了编译flag链接阶段不会自动带运行时这是“编译过、跑起来没反应”的最常见原因。-fno-omit-frame-pointer保证栈回溯完整-O0避免优化导致行号错乱。参数怎么改如果只想查堆越界address就够如果怀疑有符号整数溢出或未定义行为加undefined_behavior: truebp或LOCAL_SANITIZE : address,undefinedmk。但UBSan在车机这种资源受限环境会拖慢明显建议先只开address。2.4 验证push后怎么确认ASAN真的挂上了编译产物push到设备后别急着跑业务。先确认进程加载了ASAN运行时# 在设备shell里查进程的maps里有没有asan adb shell # 先启动你的进程然后 cat /proc/$(pidof my_native_module)/maps | grep asan如果输出里有libclang_rt.asan相关的so说明ASAN运行时已加载。如果没有回到2.3检查是不是改错了脚本或者链接阶段被strip掉了。另一个快速验证故意在代码里写一个堆越界// 临时测试代码验证ASAN是否工作 int *p new int[4]; p[5] 1; // 越界写ASAN应报heap-buffer-overflow如果logcat里出现AddressSanitizer: heap-buffer-overflow并带完整栈说明整条链路通了。如果没报说明ASAN没生效别继续查业务bug先解决构建问题。3. 编译报错与链接失败ASAN运行时找不到、符号冲突怎么破3.1 现象undefined reference to __asan_init / 编译报duplicate symbol开了ASAN之后编译阶段可能直接挂掉。常见两类报错第一类链接报错undefined reference to __asan_init、undefined reference to __asan_report_load4。这说明编译时加了-fsanitizeaddress但链接时没带上ASAN运行时库。在Android.mk里手写cflags的写法最容易出这个。第二类duplicate symbol或multiple definition通常发生在你的模块和某个预编译库.a或.so都带了ASAN插桩或者预编译库本身没插桩但符号冲突。3.2 根因ASAN需要编译期和链接期同时插桩预编译库是盲区ASAN的工作原理是编译期在每个内存访问前后插入检查代码链接期把__asan_*这些运行时函数链进来。所以只加编译flag不加链接flag →undefined reference。用LOCAL_SANITIZE/sanitize属性 → 构建系统自动处理两边。但如果你依赖了一个预编译的静态库比如高通给的libxxx.a这个库没有插桩它里面的内存访问ASAN管不到。更麻烦的是如果这个预编译库和你的插桩代码共享了一些全局符号可能报重复定义。8155p平台上vendor侧大量so是预编译的这是ASAN排查的天然盲区。你只能查自己编译的模块预编译库内部的越界得靠其他手段比如valgrind或硬件断点。3.3 解决用LOCAL_SANITIZE统一开关预编译库单独处理正确做法是全程用构建系统的sanitize开关不要手写flag。如果必须手写比如某些老模块不认LOCAL_SANITIZE编译和链接都要加# 不推荐的手写方式仅当LOCAL_SANITIZE不生效时用 LOCAL_CFLAGS -fsanitizeaddress -fno-omit-frame-pointer LOCAL_LDFLAGS -fsanitizeaddress但优先用LOCAL_SANITIZE : address对于预编译库导致的符号冲突排查步骤# 查看报冲突的符号来自哪个库 # 在编译log里找到 duplicate symbol 后面的符号名然后 find out/target/product/ -name *.a -o -name *.so | xargs nm -A 2/dev/null | grep 冲突符号名如果确认是预编译库和插桩代码冲突解决办法是把这个预编译库也重新编译如果有源码或者用LOCAL_EXPORT_CFLAGS隔离。实在不行只能把ASAN范围缩小到不依赖该预编译库的模块。3.4 参数说明ASAN_OPTIONS在车机上的常用配置ASAN运行时行为由环境变量ASAN_OPTIONS控制。在8155p上跑默认配置可能因为内存或日志限制表现异常。常用配置# 在启动进程前设置或写进init.rc export ASAN_OPTIONSdetect_leaks0:abort_on_error1:log_path/data/asandetect_leaks0车机进程通常常驻LeakSanitizer在退出时才报常驻进程等不到退出关掉省开销。abort_on_error1发现错误立即abort方便抓tombstone而不是继续跑。log_path/data/asanASAN报告写到文件因为logcat可能被冲掉。注意路径要有写权限。如果不开log_path报告默认走stderr在Android上就是logcat但高频率崩溃时logcat会丢建议写文件。4. 避坑记录8155p上开ASAN最容易翻车的五个点4.1 坑一改了Android.bp生效的却是Android.mk现象加了一堆flag编译通过运行时无ASAN输出。 原因模块同时存在Android.mk和Android.bp构建系统选了另一个。 解决按2.3的方法先确认生效脚本只改生效的那个。最稳妥的做法是临时重命名非生效文件编译一次确认。4.2 坑二LOCAL_CFLAGS被后续赋值覆盖现象明明写了-fsanitizeaddress编译log里却找不到这个flag。 原因模块后面有LOCAL_CFLAGS : ...用了:直接覆盖或者include了其他mk把变量重置。 解决用LOCAL_CFLAGS 追加并且放在所有include之后。更推荐直接用LOCAL_SANITIZE它不受cflags覆盖影响。4.3 坑三ASAN报告被logcat冲掉找不到现场现象进程崩了logcat里翻不到ASAN报告。 原因ASAN默认输出到stderr高频率日志下被冲或者进程abort太快。 解决设ASAN_OPTIONSlog_path/data/asan:abort_on_error1报告落文件。复现后直接adb pull /data/asan.*。4.4 坑四预编译库没插桩越界查不到现象怀疑某个预编译so里有越界但ASAN毫无反应。 原因ASAN只插桩重新编译的代码预编译库是盲区。 解决确认问题模块是否依赖预编译库。如果是尝试找源码重编或用其他工具辅助。别在预编译库上浪费时间调ASAN。4.5 坑五开了ASAN后性能暴跌车机卡到无法操作现象ASAN开启后界面卡顿甚至看门狗超时重启。 原因ASAN插桩带来2-3倍内存和CPU开销车机资源紧张。 解决只在排查特定模块时开ASAN不要全系统开。用LOCAL_SANITIZE只针对目标模块。排查完立即关掉别留在正式版本里。5. 快速定位三分钟判断ASAN有没有真正生效排查这类问题最怕的就是在“ASAN到底开没开”上反复试。我一般用下面这套流程三分钟内能确认状态。第一步确认生效脚本。在模块目录下执行# 列出模块目录下的构建脚本 ls -la Android.mk Android.bp 2/dev/null两个都在就按2.3的方法确认哪个生效。只有一个直接改那个。第二步编译后查编译log里有没有sanitize flag# 在out目录里搜编译命令 grep -r fsanitizeaddress out/target/product/*/obj/ 2/dev/null | head -5有输出说明编译期插桩了。没输出说明flag没进去回到第2章查脚本。第三步设备上确认运行时加载adb shell cat /proc/\$(pidof my_native_module)/maps | grep asan有libclang_rt.asan说明运行时在了。第四步故意触发一次越界看报告# 复现后拉报告 adb pull /data/asan.* ./asan_report grep -A20 ERROR: AddressSanitizer asan_report四步都过ASAN就是真生效了可以开始查真正的内存bug。任何一步卡住先解决那一步别往下走。下面这张表是我自己排查时用的对照贴在工位上省得每次重新想检查项命令/位置通过标准生效脚本重命名法或dump模块确认唯一生效文件编译flaggrep out目录有fsanitizeaddress运行时/proc/pid/maps有libclang_rt.asan报告输出ASAN_OPTIONS log_path文件里有ERROR行从那以后我每次在8155p上开ASAN都强制走一遍这四步确认生效了再查业务代码。血泪经验就是别信“我改了就应该生效”构建系统在双脚本并存时的行为比你想象的玄学得多。希望帮到你。本文还有配套的精品资源点击获取