行业资讯

Android 7系统异常问题排查(十)实战—异常问题定位方法论

发布时间:2026/8/6 12:37:05
Android 7系统异常问题排查(十)实战—异常问题定位方法论 系列目录第一篇异常机制全景图 | 第二篇Kernel Panic 与系统重启 | 第三篇Tombstone 机制 | 第四篇System Server Watchdog | 第五篇System Server 崩溃 | 第六篇ANR 机制 | 第七篇Java 层崩溃 | 第八篇Trace 机制 | 第九篇日志系统 | 第十篇实战方法论一、问题定位思维框架在深入案例之前先建立一套通用的问题定位方法论┌──────────────────────────────────────────────┐ │ 第一步现象识别 │ │ ├─ 用户/测试反馈了什么 │ │ ├─ 有异常对话框吗ANR / FC │ │ ├─ 系统重启了吗Kernel Panic / Watchdog │ │ └─ 进程消失了吗Native Crash / OOM │ ├──────────────────────────────────────────────┤ │ 第二步异常分类 │ │ ├─ 根据现象 → 确定异常类型 │ │ └─ 对照第 1 篇全景图 → 定位到具体层次 │ ├──────────────────────────────────────────────┤ │ 第三步日志收集 │ │ ├─ 首选adb bugreportz最全 │ │ ├─ 针对性收集按第 9 篇路径映射 │ │ └─ 特别注意重启类问题先抓 pstore │ ├──────────────────────────────────────────────┤ │ 第四步时间轴还原 │ │ ├─ 从 logcat 找到异常发生的精确时间点 │ │ ├─ 向前回溯 1-2 分钟寻找异常征兆 │ │ └─ 交叉验证多种日志源确认同一时间点 │ ├──────────────────────────────────────────────┤ │ 第五步根因分析 │ │ ├─ 调用栈定位 → 代码审查 │ │ ├─ 系统状态分析 → 环境/资源问题 │ │ └─ 对比分析 → 正常 vs 异常时间点的差异 │ ├──────────────────────────────────────────────┤ │ 第六步修复验证 │ │ ├─ 本地复现 → 修改代码 → 回归测试 │ │ ├─ 增加防御性日志便于后续排查 │ │ └─ 压力测试 Monkey 测试验证 │ └──────────────────────────────────────────────┘二、案例 1系统随机重启 —— Kernel Panic问题描述测试反馈手机在正常使用中偶尔黑屏重启重启频率约每天 1-2 次无明显规律。定位过程Step 1识别异常类型手机重启无任何提示 → 大概率是 Kernel Panic → 需要查看last_kmsg。Step 2日志收集# 重启后第一时间抓取adb shellcat/proc/last_kmsglast_kmsg.txt# 或从 pstore 读取adb shellcat/sys/fs/pstore/console-ramoopslast_kmsg.txtStep 3分析 last_kmsg[ 1234.567890] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [ 1234.567900] pgd c0004000 [ 1234.567920] Internal error: Oops: 805 [#1] PREEMPT SMP ARM [ 1234.567930] CPU: 1 PID: 234 Comm: mmcqd/0 [ 1234.567950] PC is at mmc_blk_issue_rq0x18/0x50 [ 1234.567960] LR is at mmc_queue_thread0x2c/0x48 [ 1234.567980] [c0123456] (mmc_blk_issue_rq) from [c0234567] (mmc_queue_thread0x2c/0x48)关键信息提取错误类型NULL pointer dereference空指针解引用崩溃进程mmcqd/0eMMC 命令队列线程崩溃函数mmc_blk_issue_rq0x18/0x50Step 4根因分析mmc_blk_issue_rq是 eMMC 块设备驱动的请求处理函数空指针解引用说明传入了一个空的数据结构。结合 “随机发生” 的特征判断可能是 eMMC 在特定条件下如高频读写、温度变化进入异常状态驱动未做空值保护。Step 5修复// drivers/mmc/card/block.cstaticintmmc_blk_issue_rq(structmmc_queue*mq,structrequest*req){structmmc_blk_data*mdmq-data;// 增加空指针保护if(!md||!req){pr_err(%s: null pointer detected\n,__func__);return-EINVAL;}// 原有逻辑...}经验总结要点说明重启类问题优先抓 pstore重启后 logcat 缓冲区已丢失只有 pstore 保留内核日志进程名 函数名 关键线索mmcqd/0→ eMMC 驱动mmc_blk_issue_rq→ 块设备请求处理随机问题考虑时序/环境如果 100% 复现可能是代码逻辑错误如果随机可能是硬件/时序问题三、案例 2App 频繁 ANR —— Binder 阻塞问题描述某 App 新版本上线后用户反馈频繁弹出应用无响应对话框尤其在首页加载时。定位过程Step 1识别异常类型弹出 ANR 对话框 → 判断为 ANR 问题 → 需要查看traces.txt。Step 2日志收集adb pull /data/anr/traces.txtStep 3分析 traces.txt----- pid 12345 at 2024-01-01 12:00:00 ----- Cmd line: com.example.app main prio5 tid1 Native | stateS at android.os.BinderProxy.transactNative(Native Method) at android.os.BinderProxy.transact(Binder.java:456) at com.example.service.IRemoteService$Stub$Proxy.fetchData(IRemoteService.java:100) at com.example.app.MainActivity.loadHomeData(MainActivity.java:150) at com.example.app.MainActivity.onCreate(MainActivity.java:80) ... Binder:12345_2 prio5 tid10 Native | stateS at android.os.BinderProxy.transactNative(Native Method) at android.os.BinderProxy.transact(Binder.java:456) at com.example.service.IOtherService$Stub$Proxy.getConfig(IOtherService.java:200) at com.example.service.RemoteService.fetchData(RemoteService.java:80) ...关键信息提取主线程状态Native在 Binder 调用中等待主线程调用链onCreate → loadHomeData → fetchDataBinder 跨进程调用目标进程com.example.service远程服务Step 4进一步分析查看远程服务com.example.service的线程状态Binder_1 prio5 tid5 Blocked at com.example.service.DatabaseHelper.query(DatabaseHelper.java:50) - waiting to lock 0x12345678 held by pool-1-thread-1 tid15 pool-1-thread-1 tid15 Sleeping at java.lang.Thread.sleep(Native Method) at com.example.service.DataSyncManager.syncData(DataSyncManager.java:200) - locked 0x12345678结论远程服务的 Binder_1 线程等待数据库锁 → 数据库锁被线程池线程持有正在 sleep 模拟长时间操作 → 主线程的 Binder 调用也被阻塞 → ANR。Step 5修复// 修复方案数据库操作异步化// 将同步 Binder 调用改为异步publicclassMainActivityextendsActivity{OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);// 将 Binder 调用移到后台线程newAsyncTaskVoid,Void,Data(){OverrideprotectedDatadoInBackground(Void...params){returnmRemoteService.fetchData();}OverrideprotectedvoidonPostExecute(Datadata){updateUI(data);}}.execute();}}经验总结要点说明主线程 Native 状态大多与 Binder 相关跨进程同步调用是主线程阻塞的常见原因跨进程 ANR 需要分析多进程不仅要看 ANR 的 App还要看对端服务锁竞争 Binder 调用 间接阻塞即使 App 代码没问题依赖的服务出问题也会导致 ANR四、案例 3SurfaceFlinger 崩溃 —— Native Crash问题描述开发机在运行 GPU 密集型测试时屏幕突然黑屏几秒后恢复但壁纸消失。定位过程Step 1识别异常类型屏幕黑屏后恢复 → 系统服务崩溃重启 → 可能是 SurfaceFlinger 崩溃 → 需要查看 tombstone 和 logcat。Step 2日志收集# 从 logcat 确认崩溃进程adb logcat-d|grep-EFatal signal|DEBUG# 输出# F DEBUG : pid: 234, tid: 234, name: surfaceflinger /system/bin/surfaceflinger # 提取 tombstoneadb pull /data/tombstones/tombstone_00Step 3分析 tombstonesignal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 x0 0000000000000000 x1 0000007f8c3a4b10 ... pc 0000007f8c3a4b50 lr 0000007f8c3a4b40 backtrace: #00 pc 0000000000004b50 /system/lib64/libsurfaceflinger.so (android::Layer::drawWithOpenGL(android::RenderEngine)128) #01 pc 0000000000004c20 /system/lib64/libsurfaceflinger.so (android::SurfaceFlinger::doComposition()256) #02 pc 0000000000004d00 /system/lib64/libsurfaceflinger.so (android::SurfaceFlinger::handleMessageRefresh()64)关键信息提取fault addr 0x0→ 空指针解引用x0 0x0000000000000000→ 第一个参数为 NULL崩溃函数Layer::drawWithOpenGL→ 图层绘制时触发场景GPU 密集型测试Step 4还原源码位置aarch64-linux-android-addr2line-esymbols/libsurfaceflinger.so-f-C0x4b50# 输出android::Layer::drawWithOpenGL# /path/to/source/services/surfaceflinger/Layer.cpp:500查看对应源码// frameworks/native/services/surfaceflinger/Layer.cppvoidLayer::drawWithOpenGL(RenderEngineengine){// 第 500 行附近constspGraphicBufferbuffermActiveBuffer;// 问题mActiveBuffer 可能为 NULL但未做检查GLuint textureNamebuffer-getTextureName();// buffer 为 NULL → 崩溃}Step 5修复voidLayer::drawWithOpenGL(RenderEngineengine){constspGraphicBufferbuffermActiveBuffer;// 增加空指针保护if(buffernullptr){ALOGE(drawWithOpenGL: mActiveBuffer is null);return;}GLuint textureNamebuffer-getTextureName();// ...}经验总结要点说明x0 寄存器 函数第一个参数ARM64 中 x0 为 NULL 直接说明第一个参数是空指针结合 tombstone 源码PC 地址 addr2line 是 Native crash 定位的黄金组合系统进程崩溃影响面广SurfaceFlinger 崩溃 → 所有显示异常 → 自动恢复需要时间五、案例 4System Server 卡死 —— Watchdog问题描述手机在充电时偶尔出现屏幕无响应几十秒后自动重启。重启后系统正常。定位过程Step 1识别异常类型屏幕无响应 自动重启 → 大概率是 Watchdog → 需要查看 DropBox 和 traces。Step 2日志收集# 从 DropBox 获取 Watchdog 记录adb shell dumpsys dropbox system_server_watchdog--printwatchdog.txt# 同时获取 ANR tracesWatchdog 也会 dumpadb pull /data/anr/traces.txtStep 3分析 DropBox 记录Tag: system_server_watchdog Subject: Watchdog: *** WATCHDOG KILLING SYSTEM PROCESS: Blocked in monitor com.android.server.am.ActivityManagerService main prio5 tid1 Blocked at com.android.server.am.ActivityManagerService.monitor(AMS.java:12345) - waiting to lock 0x12345678 held by Binder:1234_A tid8 Binder:1234_A tid8 Native at android.os.BinderProxy.transactNative(Native Method) at android.os.IPowerManager$Stub$Proxy.acquireWakeLock(IPowerManager.java:300) ... - locked 0x12345678 Binder:1234_F tid15 Blocked (power manager service thread) at com.android.server.power.PowerManagerService.acquireWakeLockInternal(...) - waiting to lock 0x87654321 held by PowerManagerSer tid20Step 4问题分析锁依赖关系main 线程持有 → ? 等待锁 A (0x12345678) Binder:1234_A 持有锁 A → 调用 PowerManagerService Binder → 等待锁 B (0x87654321) PowerManagerSer 持有锁 B → 等待某个底层操作完成Step 5进一步分析结合充电场景检查 PowerManagerService 的底层操作PowerManagerSer线程可能在等待setScreenBrightness的底层驱动响应充电时电池温度升高 → 触发 thermal 保护 → 亮度受限 → 驱动响应异常Step 6修复// 方案1将 Binder 调用改为异步// 方案2给 PowerManager 的锁操作增加超时保护// 方案3优化 thermal 策略避免在充电时频繁调整亮度经验总结要点说明Watchdog 堆栈看锁依赖画出锁依赖图找到循环依赖就是根因场景关联很重要充电 Watchdog → 可能和电源/thermal 管理相关Binder 调用 锁 高风险组合持有锁后做 Binder 调用是最容易出问题的地方六、案例 5App 启动闪退 —— Java Crash问题描述新版 App 发布后部分用户反馈打开 App 即闪退但没有弹出 FC 对话框。定位过程Step 1识别异常类型闪退无 FC 对话框 → 可能是 crashCount 2 被静默处理或 Native crash → 优先查看 DropBox 和 logcat。Step 2日志收集# 从 DropBox 获取最近崩溃adb shell dumpsys dropbox data_app_crash--print# 或从 logcat 搜索adb logcat-d|grepFATAL EXCEPTIONStep 3分析 DropBoxTag: data_app_crash Process: com.example.app Subject: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.app/com.example.app.MainActivity}: java.lang.NullPointerException: ... ... Caused by: java.lang.NullPointerException: Attempt to invoke virtual method void android.content.SharedPreferences.getString(...) on a null object reference at com.example.app.AppConfig.init(AppConfig.java:50) at com.example.app.MainApplication.onCreate(MainApplication.java:30) ...Step 4根因分析异常链 RuntimeException: Unable to start activity Caused by: NullPointerException: SharedPreferences.getString() at AppConfig.init(AppConfig.java:50) at MainApplication.onCreate(MainApplication.java:30)问题出在Application.onCreate()中调用了AppConfig.init()该方法尝试从SharedPreferences读取配置但SharedPreferences的获取方式有问题// 错误代码publicclassAppConfig{privatestaticSharedPreferencessPrefs;publicstaticvoidinit(){// 问题在 Application 的 onCreate 中调用时// 某些厂商定制 ROM 上 Context 的某些方法可能返回 nullsPrefsPreferenceManager.getDefaultSharedPreferences(sContext);StringvaluesPrefs.getString(key,default);// sPrefs 可能为 null}}Step 5修复publicclassAppConfig{privatestaticSharedPreferencessPrefs;publicstaticvoidinit(Contextcontext){// 确保使用 ApplicationContextsPrefscontext.getApplicationContext().getSharedPreferences(app_config,Context.MODE_PRIVATE);if(sPrefsnull){// 防御性编程Log.e(AppConfig,Failed to get SharedPreferences);return;}StringvaluesPrefs.getString(key,default);}}经验总结要点说明DropBox 是追溯历史崩溃的利器即使 logcat 已丢失DropBox 仍保留记录注意Caused by异常链最外层可能是系统异常真正根因在Caused byApplication.onCreate()中的异常是致命的一旦这里崩溃App 无法启动且可能连续崩溃触发 crashCount 保护七、排障 Checklist重启类问题Kernel Panic / Watchdog抓取last_kmsg/pstore查看ro.boot.bootreason属性检查 DropBox 中是否有SYSTEM_BOOT记录分析内核栈回溯定位崩溃函数检查是否有soft lockup或hard lockup日志结合 logcat 查看重启前的关键事件ANR 类问题抓取/data/anr/traces.txt定位主线程状态Blocked/Native/Runnable如果主线程 Blocked → 找到锁的持有者如果主线程 Native → 分析 Binder 调用链检查 CPU 使用率traces 文件头部检查是否有跨进程的间接阻塞检查 DropBox 中data_app_anr条目Native Crash 类问题抓取/data/tombstones/中最新的文件确认信号类型SIGSEGV/SIGABRT/…提取fault addr和x0寄存器用addr2line还原 PC 地址用ndk-stack还原完整堆栈反汇编确认必要时检查 DropBox 中SYSTEM_TOMBSTONE条目Java Crash 类问题搜索 logcat 中FATAL EXCEPTION检查 DropBox 中data_app_crash/system_server_crash提取异常类名 消息 堆栈注意Caused by异常链混淆堆栈需用mapping.txt还原检查崩溃线程main? 子线程?性能/卡顿类问题抓取 systracesched freq gfx检查 CPU 调度是否饱和检查主线程是否有长时间 Running 段检查 Binder 调用耗时检查 VSYNC 与渲染时间线检查是否有锁竞争导致的紫色 Blocked 段八、工具链速查# 信息收集 adb bugreportz# 全量收集首选adb logcat-ball-dlogcat.txt# 所有日志缓冲区adb shelldmesgdmesg.txt# 内核日志adb shellcat/proc/last_kmsglast_kmsg.txt# 上次内核日志adb pull /data/anr/traces.txt# ANR 线程堆栈adb pull /data/tombstones/tombstone_00# Native 崩溃墓碑adb shell dumpsys dropboxdropbox.txt# DropBox 异常记录# 状态查询 adb shell dumpsys activity activities# Activity 栈adb shell dumpsys meminfo# 内存状态adb shell dumpsys procstats# 进程统计adb shell dumpsys batterystats# 电池状态adb shell getprop|grepbootreason# 重启原因adb shellcat/proc/version# 内核版本adb shellcat/proc/cpuinfo# CPU 信息# 原生崩溃还原 aarch64-linux-android-addr2line-elib.so-f-Caddrndk-stack-sym./symbols/-dumptombstone_00 ./development/scripts/stacktombstone_00# 性能分析 python systrace.py-t10-otrace.html sched freq gfx input view adb shell atrace-t10sched freq gfxtrace.out# 日志过滤 grep-EFATAL|ANR|WATCHDOG|Fatal signal|paniclogcat.txtgrep-Ecrash|Crash|CRASHlogcat.txt九、系列总结本系列 10 篇博客从 AOSP 7 源码出发覆盖了 Android 异常机制的完整体系篇次主题核心收获1全景图建立了四层架构 六大异常的全局认知2Kernel Panic理解了内核崩溃的 panic 链路和 pstore 现场保存3Tombstone掌握了 Native 崩溃的信号处理和墓碑还原4Watchdog理解了 System Server 看门狗的双重检测机制5System Server Crash掌握了系统服务崩溃的 Zygote 重启和 RescueParty6ANR深入三类 ANR 的触发条件和 traces.txt 精读7App Crash理解了 Java 崩溃的 KillApplicationHandler 链路8Trace掌握了 systrace 性能分析和卡顿定位9日志系统建立了 logcat → dropbox → bugreport 的日志全景10实战用 5 个真实案例串联了全部知识三条核心原则现象 → 分类 → 产物 → 分析 → 根因这是不变的排障公式。日志路径映射是基本功什么异常对应什么日志刻在脑子里。源码是最好的老师AOSP 源码不会骗你深入源码才能理解机制的边界。系列到此完结。希望能帮助你在面对 Android 系统异常时不再迷茫不再瞎猜而是有章法、有工具、有信心地解决问题。本文基于 AOSP 7Android Nougat源码编写。系列所有文章均基于 AOSP 7文中涉及的源码路径和实现细节可能因 Android 版本和厂商定制而有所差异但核心架构和定位思路是通用的。