ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Android系统异常排查:死机重启与日志分析实战方法

Android系统异常排查:死机重启与日志分析实战方法 干Android系统问题排查这行时间越长越觉得最难的不是修Bug而是不知道问题到底在哪。死机、重启、卡顿、自动关机这些系统级异常一冒出来用户第一句话基本是“手机坏了”但工程师心里都清楚设备不见得是真坏关键是把现场日志保住。问题是日志怎么抓、抓哪些、从几千行里怎么找到决定性的那一两行很多人并没有形成一套清晰的方法。这篇文章围绕Android系统异常问题处理从死机重启这类最棘手的现象讲起一直延伸到日志分析的方法和一套可复用的排查体系。我会把常见异常的类型、日志体系的分层逻辑、完整的抓取姿势、关键字解读以及我这些年踩过的坑一并写出来。不管你是刚接触Android开发的初学者还是已经在做系统稳定性测试的工程师按这套思路走排查效率能提升一大截。1. 先给系统异常“定性”死机、重启、卡死不是一码事很多人一碰到设备没反应就笼统说“死机了”但真正动手排查时如果把所有异常混在一起找原因方向很容易跑偏。我给团队定的第一条规矩就是先把现象定性再谈日志分析否则后面全是白费功夫。1.1 死机、重启、卡死别把它们混为一谈先说死机。真正的死机是整机完全没反应画面定格触摸失效按电源键亮屏也没用只能长按强制重启。这种问题大概率出在内核层或者底层硬件状态上比如某个内核线程死锁、中断处理卡死、DDR读写异常、电源管理状态错乱。死机发生的那一瞬间用户态logcat往往已经断片了关键证据更多在kernel log和pstore里。重启又分软重启和硬重启。软重启表现是桌面重新加载、系统服务重启但设备没经历完整的关机开机画面这种多半是SystemServer被Watchdog杀掉或者核心服务崩溃触发了zygote重启。硬重启则是设备像断电一样黑屏然后过几秒重新出现开机logo这类基本是kernel panic或者硬件看门狗超时复位。两者的日志抓取重点完全不一样。卡死则更“温和”一点通常指系统UI或某个应用长时间无响应也就是ANR。这类问题根因多半在应用进程内部主线程被阻塞、Binder调用长时间不返回、锁竞争、IO卡顿等等。虽然不至于让设备重启但体验极其糟糕而且trace文件里能直接看到凶手是谁。1.2 开机流程就是异常定位的地图做系统异常排查心里必须装着Android的开机流程否则看到日志也不知道自己处在哪个阶段。简化来说就是BootROM加载BootloaderBootloader拉起kernelkernel初始化驱动和内存管理后启动init进程init解析init.rc拉起ZygoteZygote孵化SystemServerSystemServer注册一堆系统服务最后Launcher启动整个设备才算进入可用状态。这张“地图”在排查时非常有用。如果设备卡在开机logo说明kernel可能已经起来了但init或者Zygote阶段出了问题这时候logcat里很可能什么都抓不到要看串口日志、last_kmsg或者pstore。如果开机到一半SystemServer崩了那logcat的system buffer里通常会有FATAL EXCEPTION或者Watchdog杀进程的记录。如果你能快速判断异常发生在哪个阶段日志检索范围会大幅缩小。1.3 动手前先问清楚三个问题我每次接到稳定性问题先不急着连设备而是把用户或测试同事的基本信息问全。第一问题是在什么具体操作之后出现的比如亮屏瞬间、充电时、打开相机时复现路径直接决定怀疑方向。第二是自动重启还是自动关机自动重启看watchdog和panic自动关机多半要查电池、温控和PMIC相关日志。第三出现时设备是正在充电、电量很低还是高温环境这些背景条件对判断功耗、温控、充电策略引起的问题极其关键。这个习惯帮我避免过很多弯路。比如有一次反馈“静置一晚上自动重启”看着像系统服务崩溃但一查充着电最后发现是夜间充电时的温控策略卡死触发了保护方向和一开始猜的完全不同。2. 构建日志视角Android日志体系与工具选型日志是系统问题排查的唯一“监控探头”但探头的画面是分层的。很多新人拿到logcat就翻翻了半天找不到原因是因为他只盯着一个屏幕不知道真正有用的证据可能在其他地方。2.1 日志分析工具选型从adb命令行到可视化平台工具一定要按场景选。单机复现问题一套adb命令就够adb shell、adb logcat、adb shell dmesg、adb bugreport再加一个Android Studio的Logcat窗口基本能cover日常调试。Android Studio的好处是图形化支持按包名、进程过滤还能高亮关键字适合开发阶段边跑边看。到了整机稳定性测试和量产阶段脑海里必须有一张更大的图成百上千台设备如果都要靠人肉adb去拉日志效率太低。这时候要上ELK日志分析系统设备端把日志定期上传Logstash做清洗Elasticsearch做索引Kibana做检索和可视化配合告警规则能直接在一堆设备日志里按关键字聚合。我在团队里搭过一套轻量的ELK白天跑长稳晚上自动出报告十分省心。还有一个常被忽略的工具是Perfetto早期叫systrace。它抓的是系统级trace包括CPU调度、线程状态、Binder调用、渲染管线对分析卡顿和ANR这类时间敏感问题几乎是神器。死机重启这类瞬间崩掉的问题不一定有用但凡是“慢”“卡”“无响应”它比logcat好用太多。2.2 日志分层main、system、events、crash、kernel各管什么logcat默认抓的是多个buffer的混合但不同buffer里的信息价值完全不同。main buffer放的是应用层写入的日志也就是Log.i/v/d/w/e输出的内容system buffer放的是系统服务进程的日志比如SystemServer、PackageManager、ActivityManagerevents buffer放的是系统事件是二进制格式比如进程启动、Activity切换、ANR发生这类关键节点crash buffer专门记录Java崩溃堆栈也就是FATAL EXCEPTION还有kernel buffer对应的是内核日志不过它通常要单独用dmesg抓。可以这样理解main是“员工日志”system是“管理层日志”events是“考勤打卡记录”crash是“事故报告”kernel是“设备底层监控”。不同问题看不同层。比如查ANRevents buffer里有一条am_anrcrash buffer里可能没有内容因为ANR不是崩溃查进程被系统杀掉要去看lowmemorykiller相关日志和kernel log。上来就只抓默认混合日志很容易把events和kernel里的关键证据漏掉。2.3 日志抓取的关键开关与配置日志不是天生就开全的很多情况需要提前调开关。基础命令是adb logcat -b main -b system -b events -b crash指定多个buffer一起抓-v threadtime可以带线程时间戳这是分析问题时的标配。有时候应用日志被系统级别的日志淹没了可以用--pid过滤进程或者用adb logcat -s TagName:V *:S只看某个Tag。更底层的开关是系统属性。比如persist.log.tag.*系列属性可以动态调整某个模块的日志级别像setprop persist.log.tag.MyApp VERBOSE设置后即使没有root权限也能生效。kernel.dmesg_restrict属性控制普通用户能不能读内核日志量产机上默认是受限的调试机上需要adb root之后才能完整读取。日志缓冲区大小也要顺手看一下adb logcat -g能显示各buffer容量默认值往往不够长稳测试用可以通过persist.logd.size调整到4M、8M甚至更大。这一步是很多“日志抓晚了”问题的根源改大之后能明显减少关键日志被冲掉的概率。3. 死机重启场景的完整排查实操从现象到根因这一章是全文最核心的实操部分。我把死机、自动重启、ANR三类高频问题的完整排查流程拆开讲每类问题对应不同的日志抓取路径和阅读方法。3.1 死机现场第一时间保护现场死机问题最重要的原则是“保护现场”。设备死住以后第一件事不是摁重启而是赶紧连着USB线执行adb devices如果adb还能通说明系统内核还没彻底崩溃只是上层服务僵死这时候立刻抓adb shell dmesg kernel.txt和adb logcat -b all -v threadtime logcat.txt两份都抓完后再尝试adb shell dumpstate dumpstate.txt它会汇总系统所有状态信息包括进程列表、CPU占用、内存信息、Binder状态等。如果adb完全不通死机已经是内核级或者硬件级的那就只能靠重启之后的残留日志。重启后第一时间进/sys/fs/pstore/目录这个目录在兼容内核上通常挂载在/sys/fs/pstore早期设备也叫/sys/fs/pstore/console-ramoops。里面保存了上一次内核panic或异常时的最后一段打印是死机定责的头号证据。同时检查/data/tombstones和/data/anr目录看看是否有native崩溃或ANR痕迹。 注意死机发生后不要长按电源键强制重启后马上继续用设备先把pstore目录内容拉出来因为下次正常重启或再次崩溃都可能覆盖掉上次的现场。这个动作我建议写成标准操作流程的第一步。3.2 自动重启先翻pstore再查dropbox自动重启和死机不一样它有个“重启”的结果反而让日志保存多了一层机会。第一站还是/sys/fs/pstore/重点看dmesg-ramoops-0如果里面有Kernel panic - not syncing或者BUG: soft lockup字样基本能确认内核侧触发panic发生了复位。这时候下一件事就是看panic消息最后面的调用栈它通常能直接指出是哪个驱动、哪个模块、哪一行代码触发的。如果没有pstore记录或者记录里干干净净那大概率是用户态重启重点转向/data/system/dropbox/目录。dropbox是系统用来存各种异常条目的地方比如system_app_crash、system_server_watchdog、SYSTEM_RECOVERY等用adb shell ls -l /data/system/dropbox/按时间排一下找到重启时间点附近新增的文件打开看内容。SystemServer被Watchdog杀掉时dropbox里会保存当时所有系统关键线程的栈能非常直观地看到哪个系统服务block住了。3.3 ANR问题用trace和Perfetto还原卡顿时间线ANR问题不建议直接看logcat大海捞针而是按照“先看trace再看trace再用Perfetto验证”的顺序来。设备出现ANR弹窗后系统会自动把当时的进程线程栈dump到/data/anr/目录下文件名类似anr_20250101_120000。用adb pull /data/anr/拉出来先看主线程状态如果是BLOCKED那就在等一把锁顺着往下看锁被谁持有如果是RUNNABLE且CPU占用特别高多半是在死循环或疯狂GC。只看静态trace还不够因为ANR往往持续了好几秒trace只是某个瞬间的快照。更完整的做法是提前用Perfetto抓一段trace在抓取期间复现ANR然后回放时间线看主线程在那几秒内到底在等什么是在等Binder响应、等IO、还是在等锁。用我的经验来说trace是“案发现场的照片”Perfetto是“监控录像”两个配合才能拼出完整的案发过程。3.4 关键日志关键字速查与解读示范日志分析到一定程度看的就是关键字。以下这几类关键字在我排过的绝大部分系统级异常里都会出现建议直接刻进脑子里。Kernel panic - not syncing: Fatal exception BUG: soft lockup - CPU#0 stuck for 22s! INFO: task kworker/u16:3 blocked for more than 120 seconds. lowmemorykiller: Killing com.example.app (PID 1234) am_anr: [0,1234,com.example.app,123456,input dispatching timed out] WATCHDOG KILLING SYSTEM PROCESS: Blocked in monitor on ActivityManager FATAL EXCEPTION: mainKernel panic和soft lockup基本指向内核问题常见原因有驱动bug、硬件不稳定、DDR或存储芯片访问异常。task blocked是内核的hung task检测机制说明某个内核线程超过120秒没被调度通常意味着IO卡死或者驱动死锁这类问题在存储芯片故障和文件系统异常时特别常见。lowmemorykiller表示系统内存不足开始杀进程说明内存压力过大但杀到系统关键进程往往会导致重启。am_anr和WATCHDOG KILLING SYSTEM PROCESS则是用户态问题前者是应用无响应后者是系统进程看门狗触发。FATAL EXCEPTION就是Java层崩溃一般在crash buffer和dropbox里都有完整堆栈。4. 把排查方法沉淀成体系标准流程、脚本自动化与知识库单次排查牛不牛靠个人经验但一个团队能不能持续高效地处理系统异常靠的是体系。这一章我重点讲怎么把前面那些零散的招数整理成一套可复用的完整流程。4.1 五步排查法从现象到闭环我习惯把整个排查过程压成五步。第一步现象记录明确异常类型、触发场景、环境参数产出物是一段五十字以内的准确描述第二步日志收集按异常类型选择对应的抓取方案特别是要在第一次复现时就尽可能完整第三步特征匹配把日志里的关键字和已有知识库里的模式做比对先看有没有已知问题第四步根因定位结合源码、反编译、系统状态把问题落到具体模块甚至具体代码第五步回归验证修复后跑多轮压测确认问题不再复现并补充到知识库里。这五步看似简单但真正能坚持做下来的团队不多。常见的问题是第一步糊弄第三步靠记忆第五步缺失结果就是同类问题反复排查、反复踩坑。体系的力量在于它让经验可以被复制。4.2 一条命令抓全套日志写一个可复用的抓取脚本我日常调试用的日志抓取脚本经过无数轮迭代基本可以做到一条命令搞定绝大多数场景。脚本逻辑并不复杂但有几个关键顺序不能乱先抓内核日志死机问题最珍贵再抓logcat最后抓dumpstate和文件目录。#!/bin/bash # collect_android_logs.sh # 用法: ./collect_android_logs.sh [设备序列号] DEVICE$1 OUTDIRandroid_logs_$(date %Y%m%d_%H%M%S) mkdir -p $OUTDIR ADBadb if [ -n $DEVICE ]; then ADBadb -s $DEVICE fi # 检查设备在线 $ADB devices | grep -q device$ || { echo 设备未连接; exit 1; } # 1. 内核日志注意权限必要时先 adb root $ADB shell dmesg $OUTDIR/kernel_dmesg.txt 2/dev/null # 2. ramoops / pstore重启后必看 $ADB shell ls /sys/fs/pstore/ $OUTDIR/pstore_list.txt 2/dev/null mkdir -p $OUTDIR/pstore for f in $($ADB shell ls /sys/fs/pstore/ 2/dev/null | tr -d \r); do $ADB shell cat /sys/fs/pstore/$f $OUTDIR/pstore/$f 2/dev/null done # 3. logcat 各 buffer带threadtime时间戳 $ADB logcat -b main -b system -b events -b crash -v threadtime $OUTDIR/logcat_all.txt 2/dev/null # 4. 系统综合状态 $ADB shell dumpstate $OUTDIR/dumpstate.txt 2/dev/null # 5. ANR 与 tombstone 目录 mkdir -p $OUTDIR/anr $OUTDIR/tombstones $ADB pull /data/anr/ $OUTDIR/anr/ 2/dev/null $ADB pull /data/tombstones/ $OUTDIR/tombstones/ 2/dev/null wait tar czf $OUTDIR.tar.gz $OUTDIR echo 日志已打包: $OUTDIR.tar.gz脚本里有几个细节值得说一下。dmesg要放在最前面是因为如果后面操作触发了二次问题前面的现场还能保留。pstore拉取要循环处理因为文件名在不同厂商设备上不一样但用通配符直接pull有时候会失败。logcat用后台运行和wait的方式是为了让dumpstate执行期间logcat也在持续记录能抓到更完整的时间线。实际使用时按CtrlC终止脚本后再手动确认logcat进程正常退出。4.3 建问题知识库让经验变成组织资产我见过太多团队问题排查完就完了方案只存在于某一位资深工程师的聊天记录里。沉淀知识库这件事短期看是成本长期看是杠杆。我的知识库格式很简单每个问题一条记录字段是异常现象、复现环境、日志关键字、根因分析、解决方案、验证结果。举个例子某个设备偶发重启关键字是DDR frequency scaling failed根因是DDR调频驱动在特定温度区间不稳定。这条记录沉淀下来之后下次量产测试再出现类似重启直接搜关键词就能定位方向。知识库不需要做得特别重一份大家都看得懂的表格就够但一定要持续有人维护否则半年就荒废了。4.4 生产环境规模化ELK日志分析系统落地要点如果设备量上去了人工拉日志完全跟不上节奏。我建议分两步走先做“日志自动上传”设备端将logcat和kernel日志按文件滚动在WiFi环境下自动压缩上传到一个共享存储再做“中央检索”用ELK建立索引。ELK里的核心字段一般包括设备序列号、系统版本、时间、进程名、日志级别、日志内容设备侧采集用轻量Agent定期上传Logstash解析后写入ElasticsearchKibana里就能按时间、设备、关键字组合查询。ELK真正厉害的地方是聚合分析。比如最近一批机器开机即重启那就在Kibana里按Kernel panic关键字聚合按异常栈聚类一眼能看到是共性还是偶发。还可以配置Watcher告警error级别的日志在30秒内达到某个阈值就推送通知。这套体系搭建起来虽然要花几天时间但一旦跑起来稳定性问题的响应速度会有质的提升。5. 常见问题与排查技巧实录那些文档里不写的东西最后这部分我把这些年积累的高频问题速查、避坑心得和一些进阶技巧整理出来。很多内容是踩坑踩出来的官方文档里不一定找得到但对提升排查效率非常有用。5.1 高频问题速查表现象、关键字与方向直接给一份我常用的速查表按现象和关键字两个维度交叉索引秒查问题优先级。现象日志关键字可能方向建议动作玩大型游戏重启thermal、hotplug、GPU温控保护、GPU驱动压力抓thermal engine日志和GPU频率表充电时自动关机PMIC、battery、over voltage充电IC异常、电池老化抓power_supply状态和BMS日志待机黑屏死机suspend、resume、dpm内核挂起/唤醒bug、DDR不稳定抓kernel log和pstore随机软重启WATCHDOG KILLING SYSTEM PROCESSSystemServer某个服务block抓dropbox和系统服务线程栈应用频繁被杀lowmemorykiller、am_kill内存压力过大、内存泄漏抓meminfo和进程内存占用屏幕卡死但按键有效InputDispatcher、ANR in ...主线程阻塞、输入调度超时抓anr trace和Perfetto这张表只能当起点不能当结论。排查时遇到没见过的关键字第一件事不是上网搜而是看日志周围的上下文很多时候真相藏在关键字前后那几十行里。5.2 排查中最容易踩的五个坑第一坑是日志时间不对齐。手机内部RTC和电脑时间经常差几分钟导致日志里的报错时间点和操作记录对不上。脚本里我通常在抓日志前先执行一次adb shell date把设备时间记录到文件名里方便后面换算。第二坑是只抓了logcat忽略了pstore。自动重启尤其严重重启后logcat里基本是干净的因为用户态日志在复位后没有落盘真正的panic证据全在pstore不抓等于白干。第三坑是在分析ANR时只看了一个线程的栈。ANR的trace文件里往往有好几十个线程看主线程是本能反应但真正持锁的线程可能叫Binder:1234_5或者某个后台线程不把整个文件扫一遍很容易漏。第四坑是应用自己把异常吞了。很多应用会用try-catch包住所有异常或者自定义UncaughtExceptionHandler崩溃堆栈根本不进系统logcat只在应用私有目录里存一份。遇到这类应用需要用adb shell run-as去读它的私有目录。第五坑是调试权限不足。量产机默认锁了dmesg和logcat -b kernel读权限插上也没法用。有条件的话在测试机上预留root或adb root权限或者在工程版本里放开kernel.dmesg_restrict0否则核心日志根本拿不到。5.3 进阶技巧用网络连接状态排查异常流量与耗电系统异常不只是“卡死重启”还有一种特别让用户反感的场景是后台偷偷跑流量、异常耗电。排查这类问题时我一般先用adb shell dumpsys netstats看各应用的实时和历史流量统计很快能发现某个应用在后台持续上传下载。接着用adb shell netstat -t或者查看/proc/net/tcp确认当前活动的网络连接和目标地址。如果还看不出所以然就用tcpdump在设备上抓包分析进出的数据包。抓包文件用Wireshark打开后能直接看到是哪个进程在什么时机发了请求甚至能发现应用在后台高频轮询导致设备网络异常频发、待机耗电大幅上升。这类问题虽然不是严格意义上的死机重启但处理思路完全是同一套现象定性、日志抓取、特征定位、修复验证。5.4 一些长期养成的排查习惯最后说几个我坚持了很多年的小习惯。测试机永远保持支持adb root并且关掉锁屏省得半夜跑长稳的时候设备休眠后串口断开。每次发版前在系统里固化一组日志配置包括logcat buffer大小、内核日志权限、dropbox保存条数这样量产问题出现时设备端的“监控探头”一定是开着的。做长稳测试时脚本里自动抓取top、free、dumpsys meminfo的定时快照这样内存泄漏、CPU异常这类慢性病才能被追踪到。还有一条复现路径、软件版本、客户反馈的原始描述一字不改地记录下来这些细节在后期复盘时往往会变成最关键的信息。我个人在实际操作中的体会是Android系统异常排查没有银弹最强的武器就是一套覆盖“现象-日志-分析-修复-沉淀”的完整闭环。死机、重启、卡顿、异常流量表面上是不同问题底层全是同一套方法在起作用。只要把这套体系跑顺了再诡异的系统异常也只是一次按图索骥的过程。
返回列表