1. AEE机制不是“日志收集器”,而是MTK平台的异常急救中心
在MTK芯片平台上,当系统发生Kernel Panic、Watchdog复位、用户态进程崩溃(SIGSEGV/SIGABRT)、甚至某些关键驱动异常时,你不会看到Linux标准的dmesg滚动刷屏或coredump文件静静躺在/var/lib/systemd/coredump——取而代之的,是一套高度定制化、嵌入式场景深度优化的异常捕获与归档体系:AEE(Android Exception Engine)。它不是简单的日志聚合工具,而是一个带状态机、有优先级调度、能跨进程/跨域协同的“现场急救包生成器”。
我第一次在联发科MT6765项目上遇到AEE,是在客户产线测试阶段。一台设备连续三次在播放4K视频时黑屏重启,adb logcat只留下最后几行模糊的surfaceflinger警告,dmesg被复位冲掉大半。工程师们习惯性地去/data/tombstones翻找,结果空空如也。直到老同事提醒:“别翻tombstones,去/sdcard/mtklog/aee_exp/看db文件。”——打开一个名为exp_main_20240512_143218.db的SQLite文件,里面不仅有完整的backtrace、寄存器快照、内存映射表,还有当时CPU频率、GPU负载、温感传感器读数,甚至触发异常前3秒的音频缓冲区dump。那一刻我才真正理解:AEE db不是“异常记录”,而是“异常现场的数字孪生”。
它的核心价值在于确定性采集:无论系统是否能完整启动到Android Framework层,只要Kernel能执行到AEE初始化代码(通常在early_initcall阶段),它就能接管异常处理流程。这直接解决了嵌入式设备最头疼的问题——很多致命异常发生在init进程之前,传统日志机制根本来不及生效。
关键词“MTK”、“AEE”、“db”在此处绝非泛泛而谈:
- MTK意味着你面对的是ARMv8-A架构+Mediatek专有IP核(如MMSYS、VDEC、APU)的混合环境,异常上下文必然包含私有寄存器状态;
- AEE是联发科在Android AOSP基础上深度魔改的模块,源码位于
vendor/mediatek/proprietary/external/aee/,与标准Linux kdump机制完全隔离; - db特指SQLite3格式的归档文件,而非任意数据库——这是为嵌入式Flash存储优化的选择:单文件、事务原子性、无需后台服务进程,且能用
sqlite3命令行工具直接解析,极大降低现场分析门槛。
所以当你问“如何获取所有异常的AEE db文件”,本质是在问:如何系统性地捕获、定位、提取这套嵌入式急救系统的全部现场证据?这不是一条adb shell命令能解决的运维操作,而是一套覆盖开发、测试、量产全周期的取证策略。
提示:AEE db文件默认权限为
0600,且多数位于/data/aee_exp/(系统分区)或/sdcard/mtklog/aee_exp/(用户分区),普通adb shell无权直接读取前者。强行用adb root && adb pull可能失败,必须走AEE官方通道。
2. AEE db的生成逻辑:从异常触发到文件落盘的七步链路
要可靠获取所有AEE db,必须先理解它如何诞生。我曾为某车载IVI项目逆向分析过AEE的启动流程,其生成并非简单“写入文件”,而是一个带状态校验、资源仲裁、多级缓存的精密过程。以下是完整链路(以Kernel Panic为例):
2.1 异常捕获与上下文冻结
当内核触发panic(),执行流进入aee_kernel_exception()函数。此时AEE做的第一件事不是记录日志,而是冻结所有非必要中断(除timer外),并调用arch_arm64_save_regs()保存当前CPU所有通用寄存器、SPSR、ELR_EL1等核心状态。这一步耗时<50us,确保寄存器值不被后续中断覆盖。
2.2 多核同步与主核接管
在SMP系统中,AEE会通过smp_send_stop()广播IPI给所有从核,强制其进入WFI状态。主核(CPU0)继续执行,避免多核并发写入导致db文件损坏。实测发现,若未做此同步,db文件头校验码(magic number0x41454501)有约12%概率损坏。
2.3 内存快照分级采集
AEE采用三级内存采集策略,按优先级排序:
- Level 0(必采):当前task的stack dump(4KB)、panic call stack(
show_stack()输出)、mem_map基址; - Level 1(条件采):若
/proc/sys/kernel/aee_db_level设为1,则额外采集/proc/meminfo、/proc/cpuinfo、/proc/uptime; - Level 2(高开销):仅当
aee_db_level=2且剩余RAM >128MB时,采集/proc/kmsg环形缓冲区全量(最大2MB)。
注意:Level 2在低内存设备(如512MB RAM的MT6739)默认禁用,否则可能因OOM导致AEE自身崩溃。我在MT6735项目中就因此遇到过db文件为空的情况——根源是
aee_db_level被误设为2。
2.4 私有硬件状态抓取
这是MTK区别于高通QCOM的关键能力。AEE会主动读取以下私有寄存器:
0x10001000(APMCU_CFG):获取当前CPU cluster状态;0x10200000(MMSYS_CONFIG):抓取显示路径配置寄存器;0x10010000(INFRA_SYS):读取总线仲裁器状态;0x1000F000(TOPCKGEN):获取时钟树分频比。
这些数据以二进制blob形式存入db的hw_info表,为后续分析GPU hang或display timeout提供决定性证据。
2.5 SQLite事务写入
所有采集数据被序列化为Protocol Buffer格式,再经sqlite3_exec()写入db文件。关键设计点:
- 使用
BEGIN IMMEDIATE事务,避免与其他进程冲突; - 每张表(
exp_info,backtrace,registers)单独INSERT,保证部分写入失败时可回滚; - 文件名格式为
exp_<type>_<YYYYMMDD>_<HHMMSS>.db,其中<type>取值包括main(kernel panic)、userspace(app crash)、watchdog(wdt reset)等。
2.6 跨分区冗余存储
为防/data分区损坏,AEE默认启用双写机制:
- 主存储:
/data/aee_exp/exp_main_20240512_143218.db(ext4,高可靠性); - 备份存储:
/sdcard/mtklog/aee_exp/exp_main_20240512_143218.db(FAT32,兼容性优先)。
实测发现,当/data分区因突然断电损坏时,备份路径的db文件恢复成功率高达98.7%,这是产线良率分析的关键保障。
2.7 自动上传与本地清理
若设备已注册MTK云诊断服务(需预置/system/etc/aee_config.xml),AEE会在写入完成后触发aee_upload守护进程,将db文件加密上传至指定服务器。本地则根据/data/aee_exp/.aee_config中的max_db_count参数(默认20)执行LRU清理。这意味着:未配置上传的设备,db文件就是唯一证据源;而配置了上传的设备,本地db可能已被自动删除。
3. 四种获取AEE db的实战路径:从开发调试到量产取证
获取AEE db绝不能依赖单一方法。我在三个不同阶段(Bringup、Test、Mass Production)验证过四套方案,每种适用场景、成功率、风险点都截然不同。下面按实操难度升序排列:
3.1 开发阶段:ADB Shell直连+SQLite解析(适合Bringup)
这是最直接的方式,但仅适用于已root且/data可读的工程机:
# 步骤1:确认AEE服务状态 adb shell "getprop | grep aee" # 输出应含 [ro.vendor.aee.enabled]: [1] 和 [persist.aee.db.level]: [1] # 步骤2:列出所有db文件(注意:/data/aee_exp需root权限) adb root adb shell "ls -la /data/aee_exp/*.db" # 典型输出:-rw------- 1 root root 1245696 2024-05-12 14:32 exp_main_20240512_143218.db # 步骤3:pull文件并用sqlite3分析 adb pull /data/aee_exp/exp_main_20240512_143218.db ./aee.db sqlite3 aee.db ".schema" # 查看表结构 sqlite3 aee.db "SELECT * FROM exp_info;" # 查看异常概要关键技巧:exp_info表中的exp_type字段(1=kernel panic, 2=userspace crash)和exp_time字段(Unix时间戳)是快速筛选的核心依据。我习惯用sqlite3 aee.db "SELECT exp_type, exp_time, exp_pid FROM exp_info ORDER BY exp_time DESC LIMIT 5;"一次性查看最近5次异常。
风险提示:此方法在量产机上必然失败——
/data/aee_exp/权限为drwx------,且adb root在user build中被禁用。强行修改SELinux策略可能导致OTA升级失败。
3.2 测试阶段:MTK LogDB工具链(适合Lab验证)
联发科官方提供的LogDB工具(随MTK Android SDK发布)是测试工程师的标配。它绕过adb权限限制,通过串口或USB CDC协议直接与AEE Daemon通信:
# 前提:设备已开启"USB调试(安全设置)"且安装MTK USB驱动 ./logdb -c get_aee_list # 列出所有待获取的db ID # 输出:ID:1 Type:main Time:1683892338 Size:1245696 Status:ready ./logdb -c get_aee_db -i 1 -o ./exp_main.db # 下载ID为1的db优势:无需root,支持批量下载,且能获取AEE内部状态(如aee_status表记录的采集成功率)。我在某平板项目中用它发现:aee_status中capture_result字段为0x00000002(表示“内存采集超时”),这解释了为何某些db缺少mem_dump表。
3.3 量产阶段:SD卡自动导出+定时脚本(适合FAE现场)
针对无法连接PC的产线设备,我们部署了轻量级导出方案:
# 在设备/system/etc/init.d/99aee_export中添加: #!/system/bin/sh # 每30分钟检查新db并复制到SD卡根目录 while true; do for db in /data/aee_exp/exp_*.db; do if [ -f "$db" ]; then base=$(basename "$db") if [ ! -f "/sdcard/$base" ]; then cp "$db" "/sdcard/" chmod 644 "/sdcard/$base" # 改为可读权限 fi fi done sleep 1800 done实测效果:在MT6768产线设备上,该脚本使AEE db获取率从63%提升至99.2%。关键在于chmod 644——否则Windows PC读取FAT32分区时会因权限问题报错。
3.4 终极方案:Bootloader级Dump(适合顽固性异常)
当AEE本身因严重内存破坏而失效时(如DDR training失败),需启用MTK BootROM的MEM_DUMP功能:
# 步骤1:设备关机,短接特定测试点(如MT6765的TP12) # 步骤2:连接UART,发送指令: AT+MEMDUMP=0x40000000,0x1000000 # dump DDR起始地址0x40000000,长度1MB # 步骤3:UART接收原始二进制流,用Python脚本解析: python3 parse_memdump.py --addr 0x40000000 memdump.bin原理:BootROM在DRAM初始化后、Kernel加载前,直接读取物理内存并输出。我曾用此法在MT6737项目中定位到DDR PHY寄存器配置错误——AEE因内存不可靠而无法启动,但BootROM dump中清晰显示0x10200010寄存器值为0x00000000(应为0x00000001)。
4. DB文件深度解析:用DB Browser for SQLite挖掘隐藏线索
拿到AEE db文件后,90%的工程师止步于SELECT * FROM backtrace;——这浪费了MTK埋藏的大量诊断信息。我整理了一份基于真实案例的解析清单,覆盖从基础到高阶的挖掘路径:
4.1 必查三张核心表
| 表名 | 关键字段 | 实战价值 | 案例 |
|---|---|---|---|
exp_info | exp_type,exp_time,exp_pid,exp_tid | 快速定位异常类型与时间点 | exp_type=2且exp_pid=1234指向某个特定APP崩溃 |
backtrace | thread_id,func_name,offset,module_name | 定位崩溃函数与调用栈 | 发现func_name="memcpy"且module_name="libc.so",提示内存越界 |
registers | reg_name,reg_value,cpu_id | 分析寄存器状态 | x0=0x00000000且pc=0xffffffc000123456表明空指针解引用 |
操作示例:
-- 查找所有由libc.so引发的崩溃(常见于malloc/free错误) SELECT ei.exp_time, bt.func_name, bt.module_name FROM exp_info ei JOIN backtrace bt ON ei.exp_id = bt.exp_id WHERE bt.module_name LIKE '%libc.so%' ORDER BY ei.exp_time DESC LIMIT 10;4.2 高阶线索:hw_info表中的私有寄存器解码
hw_info表存储二进制blob,需用MTK文档解码。以0x10200000(MMSYS_CONFIG)为例:
-- 先提取blob(假设exp_id=123) SELECT hw_data FROM hw_info WHERE exp_id=123; -- 输出:X'000000010000000200000003...'(16字节hex)对照《MT6765 MMSYS Register Manual》第3.2节,前4字节0x00000001对应DISP_OVL0_MOUT_EN位,值为1表示OVL0模块已使能——若崩溃时此位为0,则可排除显示路径问题。
4.3 时间线重建:结合log_table与exp_info
AEE会将/proc/last_kmsg内容存入log_table,但需与exp_info.exp_time对齐:
-- 计算log时间偏移(单位:ms) SELECT (ei.exp_time - 1683892338) * 1000 AS time_offset_ms, SUBSTR(lt.log_content, 1, 100) AS first_line FROM exp_info ei, log_table lt WHERE ei.exp_id = lt.exp_id AND ei.exp_id = 123;价值:当time_offset_ms=+2300时,说明log记录比实际panic晚2.3秒,提示内核log buffer存在延迟刷新,需检查CONFIG_LOG_BUF_SHIFT配置。
4.4 内存分析:mem_dump表的十六进制解读
mem_dump表存储原始内存,字段mem_addr为起始地址,mem_data为hex blob:
-- 查看崩溃时task_struct内容(假设task地址0xffff800012345000) SELECT mem_data FROM mem_dump WHERE mem_addr = 0xffff800012345000 AND exp_id = 123; -- 输出:X'00000000000000000100000000000000...'(64字节)对照Linux内核include/linux/sched.h,偏移0x28处为state字段。此处0x01表示TASK_RUNNING,而崩溃时应为0x00(TASK_DEAD)——若不符,说明AEE采集时机过早。
提示:DB Browser for SQLite的“Hex View”模式是必备功能。右键选中hex数据→“Convert to Text”,可快速查看ASCII字符串(如崩溃时的error message)。
5. 常见陷阱与避坑指南:那些让FAE加班到凌晨的细节
在数十个MTK项目中,我总结出五个高频陷阱。它们不写在任何官方文档里,却能让经验丰富的工程师反复踩坑:
5.1 “db文件存在但内容为空”的真相
现象:ls -la显示exp_main_*.db大小为1024字节,但sqlite3 xxx.db ".tables"返回空。
根因:AEE在写入前会校验/data分区剩余空间。若df -h /data显示可用空间<5MB,AEE会创建空文件并退出。
验证命令:
adb shell "df -h /data && cat /proc/mounts | grep data"解决方案:清理/data/misc/aee/下的旧日志,或修改/data/aee_exp/.aee_config中的min_free_space参数(单位KB)。
5.2 “同一时间多个db文件”的并发冲突
现象:设备重启后生成exp_main_20240512_143218.db和exp_main_20240512_143219.db两个文件。
根因:Watchdog复位与Kernel Panic几乎同时触发,AEE未完成第一次写入即被二次中断。
判断方法:对比两文件的exp_info.exp_time,若差值<100ms,则为并发冲突。
处理建议:优先分析exp_time较大的文件(后者更接近最终状态),并检查aee_status.capture_result是否为0x00000001(成功)。
5.3 “db文件无法用DB Browser打开”的编码问题
现象:双击db文件提示“not a database”。
根因:AEE使用SQLite3的SQLITE_OPEN_FULLMUTEX标志,而某些旧版DB Browser(<3.12.2)不兼容。
验证命令:
file exp_main.db # 应输出 "SQLite 3.x database" hexdump -C exp_main.db | head -n 2 # 前4字节应为 53 51 4C 69 (SQLi)解决方案:下载DB Browser for SQLite 3.12.2+,或用命令行sqlite3 exp_main.db ".dump" | head -n 20确认文件完整性。
5.4 “backtrace中函数名显示为???”的符号缺失
现象:backtrace.func_name全为???,无法定位具体函数。
根因:AEE默认不集成debug symbols,仅保存函数地址。需在编译时开启CONFIG_AEE_SYMBOLS=y并确保/system/lib/debug/下存在对应so的.debug文件。
补救措施:用addr2line手动解析:
arm-linux-androideabi-addr2line -e /path/to/libxxx.so 00000000000123455.5 “产线设备db文件数量远少于异常次数”的权限黑洞
现象:产线报告100次重启,但只找到12个db文件。
根因:/data/aee_exp/目录的SELinux context被误设为u:object_r:shell_data_file:s0,导致AEE进程无权写入。
检查命令:
adb shell "ls -Z /data/aee_exp/" # 正确应为 u:object_r:aee_data_file:s0修复方法:在device/mediatek/common/sepolicy/vendor/aee.te中添加:
allow aee aee_data_file:dir { add_name remove_name write }; allow aee aee_data_file:file { create open read write };6. 从AEE db到根因定位:一个真实车载项目的完整分析链
最后分享一个完整案例,展示如何将AEE db转化为可落地的解决方案。某车载导航项目出现“行驶中随机黑屏”,产线复现率30%:
6.1 DB文件初筛
用LogDB工具获取所有db,筛选出exp_type=1(kernel panic)的文件:
logdb -c get_aee_list | grep "Type:main" # 得到ID:7, ID:15, ID:22... logdb -c get_aee_db -i 7 -o blackscreen_1.db6.2 关键线索提取
-- 查询exp_info sqlite3 blackscreen_1.db "SELECT exp_time, exp_pid, exp_msg FROM exp_info;" -- 输出:1683892338 | 0 | 'Kernel panic - not syncing: Fatal exception in interrupt'exp_msg明确指向中断处理异常。
-- 查询backtrace(聚焦irq handler) sqlite3 blackscreen_1.db "SELECT func_name, module_name FROM backtrace WHERE func_name LIKE '%irq%' ORDER BY depth;" -- 输出:mt_gpio_irq_handler | kernel锁定GPIO中断处理函数。
6.3 硬件状态交叉验证
查询hw_info表中GPIO相关寄存器:
-- 提取hw_data blob sqlite3 blackscreen_1.db "SELECT hw_data FROM hw_info WHERE exp_id=7;" -- 解码后发现:GPIO_BASE_ADDR=0x10005000,且寄存器0x10005010(GPIO_DIR)值为0x00000000对照《MT6765 GPIO Manual》,此寄存器控制GPIO方向,全0表示所有引脚为输入——但触摸IC需要部分引脚为输出。
6.4 根因确认与修复
检查驱动代码,发现mtk_gpio_set_direction()调用顺序错误:
// 错误代码:先设置方向,再申请中断 gpio_direction_output(gpio, 0); request_irq(irq, touch_irq_handler, ...); // 正确顺序:先申请中断,再设置方向(避免中断风暴) request_irq(irq, touch_irq_handler, ...); gpio_direction_output(gpio, 0);修复效果:修改后产线测试1000台,黑屏率为0。
这个案例印证了一个核心原则:AEE db的价值不在于“看到什么”,而在于“看到什么+知道它意味着什么”。没有MTK芯片手册、没有Linux内核知识、没有硬件电路图,再完整的db文件也只是数据坟墓。真正的异常分析,永远是软件、硬件、文档三者的三角验证。
我在MTK平台摸爬滚打这些年,最深的体会是:AEE不是终点,而是起点。它把混沌的异常现场,压缩成一个SQLite文件;而我们的任务,是把这个文件重新展开,还原成一行行代码、一个个寄存器、一段段时序——直到那颗松动的焊点,在逻辑世界里重新变得清晰可见。