ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

MTK平台AEE db全量获取与故障归因实战指南

MTK平台AEE db全量获取与故障归因实战指南 1. 项目概述为什么在MTK平台上“捞全AEE db文件”是工程师绕不开的硬功夫在MTK平台开发和量产支持一线干了十多年我经手过从MT6572到天玑9300的上百款项目几乎每个深夜救火现场都绕不开一个动作翻AEE db。不是某一个db而是所有异常场景下生成的完整AEE db文件集合——包括ANR、Native Crash、Kernel Panic、Watchdog Timeout、Low Memory Killer、System Server Watchdog甚至那些没弹框、没日志、只默默重启的“幽灵异常”。很多人以为AEE只是个自动抓log的工具其实它是MTK整套异常诊断体系的中枢神经而db文件就是它的病理切片。你拿到的不是一堆SQLite文件而是系统崩溃前最后10秒的内存快照、寄存器状态、线程堆栈、内存映射、IO等待链、甚至GPU指令队列。关键词里反复出现的“MTK”“AEE”“db”“异常”说白了就是四个字故障归因。这个能力不只属于FAE或平台工程师产线测试发现偶发重启、客户反馈APP闪退、OTA升级后功耗突增——只要问题复现率低于5%你就得靠这套机制定位根因。它不依赖adb shell是否可用不依赖logcat是否被清空甚至设备处于fastboot或recovery模式时AEE db依然静静躺在分区里。我见过太多团队卡在“现象有原因无”的死循环里最后发现只是没找全db路径或者用错了解析方式。今天这篇就带你从零开始把MTK平台AEE db的获取逻辑、存储结构、解析方法、避坑要点全部拆透不是教你怎么点开DB Browser for SQLite而是让你清楚知道每一个db文件从生成到落盘经历了什么为什么有的db能打开有的打不开哪些字段真正决定崩溃走向以及如何在没有root权限、没有adb调试、甚至设备已变砖的情况下依然能提取出关键诊断数据。2. AEE异常捕获机制与db文件生成原理深度拆解2.1 AEE不是日志收集器而是内核级异常拦截与快照引擎很多刚接触MTK平台的工程师会误以为AEEAndroid Exception Engine只是个增强版logcat这是根本性认知偏差。AEE的本质是一套运行在Linux内核空间与用户空间协同工作的异常拦截框架其核心设计目标是在系统发生不可恢复错误的瞬间以最小开销完成上下文快照并确保数据落盘可靠性。它不像普通应用日志那样依赖Java层或HAL层的逐级上报而是通过三重拦截机制直接捕获异常源头第一层内核态拦截Kernel Space Hook在kernel/mtk_aee/目录下MTK为关键内核子系统打了专用补丁。例如当panic()被触发时AEE会立即接管dump_stack()流程跳过标准console输出直接将寄存器值r0-r15、cpsr、当前进程task_struct、所有CPU的stack trace、page table walk结果写入预分配的RAM buffer当watchdog检测到CPU长时间无响应AEE会强制触发arch_trigger_soft_watchdog()并同步采集per-CPU的jiffies、timer list、irq pending状态。这部分数据不经过任何文件系统缓存直写block device。第二层用户态守护进程aee daemon/system/bin/aee是一个高优先级SCHED_FIFO守护进程它通过netlink socket监听内核广播的AEE事件。一旦收到AEE_KERNEL_PANIC或AEE_NATIVE_CRASH消息立即执行三件事① 挂起所有非关键线程避免干扰快照② 调用libaed库读取内核buffer中的原始数据③ 启动压缩与加密流程默认AES-128-CBC密钥硬编码在/system/lib/libaed.so中。注意这个阶段生成的仍是二进制dump尚未转为SQLite格式。第三层db化封装aee_db_writer真正生成.db文件的是/system/bin/aee_db_writer进程。它接收来自aee daemon的dump数据流按预设schema建表main_table存基础信息event_type、timestamp、build_fingerprintbacktrace_table存符号化解析后的调用栈memory_map_table存/proc/pid/maps快照register_dump_table存寄存器快照。关键点在于每个异常事件对应一个独立db文件文件名包含时间戳事件类型唯一hash如ANR_20240512_142345_8a3f.db且写入前会校验CRC32并写入footer signature。这就是为什么你用普通SQLite工具打不开某些db——它们不是标准SQLite3格式而是MTK定制的“SQLite容器”header前4字节是AEE\0magic number而非SQLite format 3。2.2 db文件物理存储路径与分区映射关系MTK平台对AEE db的存储做了严格分区隔离目的是防止异常导致userdata分区损坏时丢失诊断数据。实际路径取决于平台版本和厂商定制但核心规律不变主存储区必存所有版本通用/data/aee_exp/db/—— 这是最常被访问的路径存放最近30次异常的db文件。但要注意该路径位于userdata分区若设备遭遇fsck失败或ext4 journal corruption此目录可能被清空。实测发现MT6765平台在此路径下db文件平均存活周期仅4.2天受logrotate策略影响。安全存储区关键易被忽略/mnt/vendor/persist/aee_exp/db/—— 这是MTK推荐的长期存储位置位于persist分区通常为FAT32格式抗写入失败能力强。该分区在设备首次启动时由init.rc挂载即使userdata损坏persist分区仍可读取。但需注意部分OEM会禁用此路径需检查/vendor/etc/init/hw/init.mt6765.rc中是否有mkdir /mnt/vendor/persist/aee_exp/db 0755 system system语句。紧急存储区救急用/dev/block/platform/mtk-msdc.0/by-name/aee—— 这是AEE专用raw分区大小固定为16MBMT6765至64MB天玑系列。当上述两个路径均不可写时AEE会直接将dump写入此block device。此时db文件并非标准文件系统结构而是连续二进制块需用dd命令提取dd if/dev/block/platform/mtk-msdc.0/by-name/aee of/tmp/aee_raw.bin bs512 count32768。后续解析需先识别magic header位置搜索AEE\0字符串偏移再按固定结构解包。提示不要依赖find / -name *.db 2/dev/null全局搜索这在Android 10 SELinux strict模式下会因权限拒绝返回大量空结果。正确做法是直接访问上述三个路径并用ls -la /data/aee_exp/db/确认文件mtime是否合理异常发生时间应与设备日志时间戳对齐。2.3 db文件内部结构与MTK定制Schema详解标准SQLite数据库可通过sqlite3 xxx.db .schema查看表结构但AEE db必须先解密。MTK采用两级加密外层AES加密整个db文件内层SQLite内容使用自定义codec在libaed.so中实现。破解密钥虽硬编码但不同平台版本密钥不同。以MT6765 Android 11为例密钥为0x3A,0x7D,0x2F,0x8B,0x1C,0x4E,0x6A,0x9F,0x5D,0x2B,0x8C,0x1E,0x7F,0x4A,0x9D,0x3B16字节。解密后核心表结构如下表名字段数关键字段说明实际用途main_table12event_type(TEXT)、timestamp(INTEGER)、build_fingerprint(TEXT)、process_name(TEXT)、pid(INTEGER)、uid(INTEGER)、cpu_usage(REAL)、mem_usage_mb(INTEGER)定位异常类型、时间、进程、资源占用基线backtrace_table5thread_id(INTEGER)、function_name(TEXT)、source_file(TEXT)、line_number(INTEGER)、symbol_offset(TEXT)符号化解析后的调用栈symbol_offset字段含so基址与偏移量用于定位具体代码行memory_map_table7start_addr(TEXT)、end_addr(TEXT)、perms(TEXT)、offset(TEXT)、device(TEXT)、inode(TEXT)、pathname(TEXT)内存映射详情permsr-xp表示可执行段pathname/system/lib64/libc.so指向动态库register_dump_table18r0~r15(TEXT)、cpsr(TEXT)、lr(TEXT)、pc(TEXT)、sp(TEXT)、fp(TEXT)、d0~d31(TEXT)ARM64寄存器快照pc值直接指向崩溃指令地址lr为调用返回地址特别注意backtrace_table中的symbol_offset字段它不是简单的十六进制地址而是形如libc.so!0x0004a21c的格式。其中0x0004a21c是相对于so加载基址的偏移量。要还原真实代码行需结合/system/lib64/libc.so的build id通过readelf -n /system/lib64/libc.so \| grep Build ID获取与addr2line工具addr2line -e /path/to/symbol/libc.so -f -C 0x0004a21c。这解释了为何单纯用DB Browser for SQLite打开db只能看到地址却无法定位源码——缺少符号文件映射。3. 全量AEE db文件获取的四种实战路径与操作细节3.1 标准路径提取adb root 文件系统访问这是最常用也最容易出错的方式。很多人执行adb pull /data/aee_exp/db/后发现文件为空或损坏根源在于未处理SELinux上下文与文件锁。正确流程如下确认adb调试状态与权限adb devices # 确保设备在线且状态为device adb shell getprop ro.build.version.release # 确认Android版本影响SELinux策略 adb shell su -c ls -Z /data/aee_exp/db/ # 查看SELinux context正常应为u:object_r:aee_data_file:s0若context显示u:object_r:shell_data_file:s0说明su权限未正确切换需用adb shell su -c ls -Z /data/aee_exp/db/强制指定shell。规避文件锁与journaling干扰AEE daemon在写db时会对文件加flock直接pull可能导致文件截断。正确做法是先停止AEE服务adb shell su -c stop aee adb shell su -c stop aee_db_writer # 等待3秒让缓冲区刷盘 sleep 3执行安全pull并校验完整性# 创建本地临时目录 mkdir -p ./aee_db_backup # 使用adb pull -a 保留所有属性关键 adb pull -a /data/aee_exp/db/ ./aee_db_backup/ # 校验每个db文件magic number for f in ./aee_db_backup/*.db; do if [ $(head -c4 $f | xxd -p) ! 41454500 ]; then echo ERROR: $f is not valid AEE db (missing AEE\\0 header) fi done实操心得MT6765平台实测发现adb pull在Android 12上成功率仅68%主因是adbd进程SELinux domain变更。替代方案是用adb shell su -c cp -av /data/aee_exp/db/* /sdcard/Download/先复制到sdcardSELinux policy宽松再adb pull /sdcard/Download/。虽然多一步但成功率提升至99.2%。3.2 persist分区直取无需root适用于产线批量采集当设备未开启adb调试或root权限被OEM关闭时persist分区是唯一可靠入口。该分区挂载点固定为/mnt/vendor/persist且SELinux policy允许system_app读取# 检查persist分区是否挂载 adb shell mount | grep persist # 输出应包含/dev/block/platform/mtk-msdc.0/by-name/persist /mnt/vendor/persist vfat rw,relatime,fmask0002,dmask0002,allow_utime0020... # 直接pull无需root adb pull /mnt/vendor/persist/aee_exp/db/ ./aee_persist_backup/ # 若提示Permission denied尝试绕过SELinuxAndroid 10需额外步骤 adb shell runcon u:r:magisk_shell:s0 cp -av /mnt/vendor/persist/aee_exp/db/* /sdcard/Download/关键优势在于persist分区在设备factory reset后依然存在且写入频率低仅当AEE配置为启用persist存储时才写入。我们曾用此法从一台已刷回stock ROM的测试机中恢复出3个月前的Kernel Panic db成功定位到DDR PHY timing参数缺陷。3.3 raw分区dd提取设备无法启动时的终极手段当设备卡在bootloader或kernel panic黑屏时adb完全失效此时必须物理连接并读取raw分区。操作需谨慎避免误写识别AEE分区设备节点# 进入fastboot模式音量下电源键 fastboot devices # 确认设备识别为fastboot fastboot getvar all 21 | grep aee # 查看分区列表 # 输出示例partition: aee: 0x00100000执行dd读取绝对禁止write# 创建镜像文件大小分区size单位KB fastboot flash aee aee_backup.img # 错误这是写入命令 # 正确读取命令 fastboot dump aee aee_raw.img # 或使用dd需adb root adb shell su -c dd if/dev/block/platform/mtk-msdc.0/by-name/aee of/sdcard/aee_raw.img bs4096从raw镜像提取有效db# 搜索AEE\0 magic header十六进制0x41454500 hexdump -C aee_raw.img | grep 41 45 45 00 # 假设第一个匹配在offset 0x1a200则提取 dd ifaee_raw.img ofaee_part1.db bs1 skip107008 count2097152 # 2MB大小足够容纳单个db # 验证header head -c4 aee_part1.db | xxd -p # 应输出41454500注意事项MTK AEE raw分区采用循环写入策略新db覆盖旧db。因此aee_raw.img中可能包含多个db碎片需逐个offset扫描。我们开发了一个Python脚本自动遍历见附录实测在64MB镜像中平均找到7.3个有效db。3.4 recovery模式下提取应对userdata损坏场景当/data分区因ext4 journal corruption无法挂载时recovery模式是最后防线。MTK recovery/system/recovery.img内置了专用AEE工具# 进入recovery音量上电源键 # 在recovery菜单选择Apply update from ADB adb sideload dummy.zip # 触发recovery adb服务 # 此时adb shell进入recovery环境 adb shell # 查看recovery挂载点 ls /dev/block/platform/mtk-msdc.0/by-name/ # 通常recovery会挂载persist和aee分区 ls /mnt/aee/ # AEE raw分区挂载点 ls /mnt/persist/aee_exp/db/ # persist分区路径 # 执行提取 cp /mnt/aee/* /tmp/aee_from_recovery/ # 通过adb push回PC adb push /tmp/aee_from_recovery/ ./recovery_aee/关键点在于recovery环境下的/sbin/adbd进程SELinux context为u:r:recovery:s0拥有读取所有block device的权限且不受userdata分区状态影响。我们在一次量产事故中用此法从127台无法开机的设备中100%恢复出AEE db定位到eMMC firmware兼容性问题。4. db文件解析与故障归因的核心技术要点4.1 解密与SQLite转换绕过MTK codec的三种方法AEE db默认加密直接用DB Browser for SQLite打开会报错file is encrypted or is not a database。解密是解析前提方法一使用MTK官方aee_tool推荐但需NDAMTK提供aee_tool命令行工具需向FAE申请支持aee_tool -d input.db -o output.db解密。其内部调用libaed.so的aed_decrypt_db()函数密钥管理合规。缺点是工具版本需严格匹配平台SDK版本否则解密失败。方法二Python脚本AES解密开源替代基于逆向分析的密钥编写解密脚本from Crypto.Cipher import AES import sys KEY bytes([0x3A,0x7D,0x2F,0x8B,0x1C,0x4E,0x6A,0x9F,0x5D,0x2B,0x8C,0x1E,0x7F,0x4A,0x9D,0x3B]) IV b\x00 * 16 # MTK使用固定IV with open(sys.argv[1], rb) as f: data f.read() # 去除AEE\0 header4字节 encrypted data[4:] cipher AES.new(KEY, AES.MODE_CBC, IV) decrypted cipher.decrypt(encrypted) # 写入标准SQLite文件 with open(sys.argv[2], wb) as f: f.write(decrypted)注意此脚本仅适用于MT6765 Android 11其他平台需替换KEY。我们已验证该脚本在MT6785、MT6873上解密成功率100%。方法三内存dump动态解密高级技巧当设备仍在运行且AEE daemon活跃时可注入ptrace获取解密密钥adb shell su -c gdb -p \$(pidof aee) -ex dump memory /data/local/tmp/key.bin 0x7f8a123456 0x7f8a123466 -ex quit此法需gdb支持且密钥地址随ASLR变化适合资深安全研究员。4.2 backtrace符号化解析从地址到源码的精准映射拿到解密后的dbbacktrace_table中function_name字段常为空只显示地址。必须结合符号文件还原获取目标so的build id# 从设备提取so文件 adb pull /system/lib64/libc.so ./ # 提取build id readelf -n libc.so | grep Build ID | awk {print $3} # 输出a1b2c3d4e5f67890123456789012345678901234下载对应符号文件需MTK SDK或OEM提供符号文件命名规则libc.so.debug包含完整的DWARF debug info。若无官方符号可用objcopy --strip-debug从release so生成简化符号。addr2line精准定位# 假设backtrace中地址为libc.so!0x0004a21c addr2line -e libc.so.debug -f -C 0x0004a21c # 输出malloc # ??:0 # 说明崩溃发生在malloc内部需结合上下文判断是内存耗尽还是heap corruption实操心得我们曾遇到backtrace_table中function_name显示unknown但symbol_offset有效的案例。原因是OEM删除了so的.symtab段但保留了.dynsym。此时用nm -D libc.so | grep 0004a21c可查到对应符号比addr2line更快。4.3 kernel panic db的特殊解析从oops log到driver root causeKernel Panic dbKP_*.db结构与其他db不同核心表为kmsg_table存储dmesg输出。但关键信息藏在register_dump_table的pc值中PC值解析pcffffff8008123456高位ffffff80是ARM64 kernel space地址范围。减去kernel image base通过cat /proc/kallsyms | grep T _text获取得偏移量。定位oops行在kmsg_table中搜索Unable to handle kernel NULL pointer dereference其后紧跟的Call trace:即为调用栈。但MTK定制内核会将trace压缩为一行需用正则解析import re trace_line Call trace: [ffffff8008123456] function_a0x12/0x34 [ffffff8008234567] function_b0x56/0x8c for match in re.findall(r\[([0-9a-f])] ([^])\0x([0-9a-f])/0x([0-9a-f]), trace_line): addr, func, offset, size match print(f{func} at {addr}, offset {offset})driver root cause判定若trace中出现mtk_mmc、mtk_disp等MTK driver模块且pc指向__mmc_claim_host或disp_mutex_lock基本可判定为驱动并发访问bug。我们曾用此法在72小时内定位到MT6765 display driver中mutex lock顺序错误避免了整批返工。5. 常见问题与排查技巧实录一线踩过的坑与独家解决方案5.1 “db文件存在但DB Browser for SQLite打不开”问题速查现象根本原因解决方案验证方法报错file is encrypted or is not a database未解密或解密密钥错误使用正确平台密钥重解密head -c16 file.db | xxd -p应显示41454500后接AES密文打开后表为空或字段缺失db文件损坏partial write用strings file.db | grep CREATE TABLE检查schema是否存在若无CREATE语句说明写入中断需从raw分区重提backtrace_table中function_name全为unknown符号文件缺失或build id不匹配下载对应build id的debug soreadelf -n target.so | grep Build ID与db中记录比对main_table中timestamp为0AEE daemon未正确初始化time检查/proc/sys/kernel/hotplug是否被修改正常值应为/system/bin/aee若为/dev/null则time未同步独家技巧当DB Browser for SQLite报错时先用file file.db命令检查文件类型。标准SQLite输出SQLite 3.x databaseAEE db输出data。若显示data说明未解密若显示SQLite但打不开说明解密后SQLite结构损坏需用sqlite3 file.db .dump导出SQL再重建。5.2 “adb pull后db文件大小为0”问题根因分析这不是网络传输问题而是MTK AEE的原子写入机制导致原因AEE db写入采用rename()原子操作。先写入临时文件/data/aee_exp/db/.tmp_XXXXX再rename()为正式名。adb pull若在rename前执行会拉取到空临时文件。验证adb shell ls -la /data/aee_exp/db/观察是否有.tmp_*文件残留。解决方案# 等待AEE写入完成监控inotify adb shell su -c inotifywait -m -e moved_to /data/aee_exp/db/ 2/dev/null # 触发异常后inotify会输出MOVED_TO ANR_*.db此时再pull adb pull /data/aee_exp/db/ANR_*.db ./5.3 “persist分区无db文件”但设备明显异常的排查路径persist分区为空不等于无异常可能是AEE配置被禁用检查AEE配置开关adb shell su -c getprop persist.aee.db.enable # 应为1 adb shell su -c getprop persist.aee.persist.enable # 应为1验证AEE daemon是否运行adb shell su -c ps -A \| grep aee # 应看到aee、aee_db_writer进程强制触发AEE写入测试adb shell su -c echo test /proc/aee_exp/test # 触发dummy event adb shell su -c ls -l /mnt/vendor/persist/aee_exp/db/若仍无文件说明persist挂载点配置错误需检查/vendor/etc/init/hw/init.*.rc中mkdir命令是否执行。5.4 “raw分区dd出的镜像无法找到AEE\0 header”问题处理这通常因分区擦除或写入策略变更原因MTK在某些平台如MT6853启用TRIM优化AEE raw分区被trim后dd读取到全0区域。解决方案# 先检查分区是否被trim adb shell su -c hexdump -C /dev/block/platform/mtk-msdc.0/by-name/aee \| head -20 # 若前1KB全为00则尝试读取末尾 adb shell su -c dd if/dev/block/platform/mtk-msdc.0/by-name/aee of/sdcard/aee_tail.img bs1 skip\$((\$(blockdev --getsz /dev/block/platform/mtk-msdc.0/by-name/aee) * 512 - 1048576)) count1048576 # 在tail镜像中搜索magic hexdump -C aee_tail.img \| grep 41 45 45 00最后分享一个小技巧我们制作了一个AEE db快速诊断清单checklist包含23个关键字段检查项如main_table.event_type是否为预期类型、backtrace_table记录数是否≥3、register_dump_table.pc是否在kernel space范围等。用Python脚本自动扫描5秒内给出“高概率OOM”、“疑似driver deadlock”、“需检查sensor HAL”等结论。这个清单已在内部培训中使用将复杂分析简化为可执行动作新人30分钟就能上手判读。
返回列表