阅读错误报告
原文地址:https://source.android.com/source/read-bug-reports.html
无论是任何类型的开发工作,出错都在所难免,而错误报告对于找出和解决问题至关重要。Android 的所有版本都支持通过 Android 调试桥 (adb) 获取错误报告;Android 4.2 及更高版本支持一个旨在获取错误报告以及通过电子邮件、云端硬盘等分享报告的开发者选项。
Android 错误报告中包含文本 (.txt) 格式的 dumpsys
、dumpstate
和 logcat
数据,以便您轻松搜索特定内容。以下各部分详细说明了错误报告的组成部分、介绍了常见问题,并提供了关于查找与这些错误相关的日志的实用提示和 grep
命令。大多数部分中还包括 grep
命令及输出和/或 dumpsys
输出方面的示例。
logcat
日志是所有 logcat
信息的转储,并采用字符串形式。system(系统)部分专门用于记录框架方面的信息,与包含所有其他内容的 main(主要内容)部分相比,该部分包含更长时间内的记录。每行都以 timestamp PID TID log-level
开头。
该日志中包含将二进制格式转换成了字符串形式的日志消息。它比 logcat
日志要清晰明了,但也有些难以阅读。在查看事件日志时,您可以在这一部分中搜索特定进程 ID (PID),以查看相应进程一直在做什么。基本格式为:timestamp PID TID log-level log-tag tag-values
。
日志级别包括以下几种:
- V:详细
- D:调试
- I:信息
- W:警告
- E:错误
有关其他实用的事件日志标记,请参阅 /services/core/java/com/android/server/EventLogTags.logtags。
错误报告有助于您找出导致应用无响应 (ANR) 错误和死锁事件的原因。
当某个应用在一定时间内没有响应(通常是由于主线程被阻塞或繁忙)时,系统会终止该进程并将堆栈转储到 /data/anr
。要找出 ANR 背后的罪魁祸首,请为二进制事件日志中的 am_anr
执行 grep 命令。
您也可以为 logcat
日志(其中包含关于发生 ANR 时是什么在占用 CPU 的更多信息)中的 ANR in
执行 grep 命令。
通常您可以找到与 ANR 对应的堆栈跟踪。请确保 VM 跟踪上的时间戳和 PID 与您正在调查的 ANR 相符,然后再检查进程的主线程。请注意:
- 主线程只能让您了解发生 ANR 时它在做什么,这可能是导致 ANR 的真正原因,也可能不是。(错误报告中的堆栈可能是无辜的;可能有其他线程在恢复正常之前粘滞了很长时间,但不足以导致 ANR。)
- 可能存在多组堆栈跟踪(
VM TRACES JUST NOW
和VM TRACES AT LAST ANR
)。请确保您查看的是正确的部分。
由于线程出现粘滞,死锁往往首先表现为 ANR。如果系统服务器发生死锁,监控程序最终会将其终止,从而导致日志中出现类似以下的条目:WATCHDOG KILLING SYSTEM PROCESS
。对于用户来说,他们看到的是设备重新启动,但从技术上来讲这是运行时重启,而不是真正的设备重新启动。
- 在运行时重启时,系统服务器已死机并会重启,并且用户会看到设备返回到显示启动动画。
- 在设备重新启动时,内核已崩溃,并且用户会看到设备返回到显示 Google 启动徽标。
要查找死锁,请检查 VM 跟踪部分中是否存在以下模式:线程 A 在等待线程 B 占用的某些资源,而线程 B 也在等待线程 A 占用的某些资源。
Activity 是一种应用组件,可提供一个屏幕,用户能够通过与该屏幕互动来执行某些操作,例如拨打电话号码、拍照、发送电子邮件,等等。从错误报告的角度来看,一个 Activity 是用户可以执行一项明确具体的操作,这使得查找在崩溃期间处于聚焦状态的 Activity 变得非常重要。Activity 通过 ActivityManager 运行进程,因此找出指定 Activity 的所有进程停止和启动事件也有助于进行问题排查。
要查看记录的处于聚焦状态的 Activity,请搜索 am_focused_activity
。
要查看记录的进程启动事件,请搜索 Start proc
。
要确定设备是否发生系统颠簸,请检查 am_proc_died
和 am_proc_start
前后在短时间内是否出现活动异常增加。
由于 Android 设备的物理内存通常都存在限制,因此管理随机存取存储器 (RAM) 至关重要。错误报告中包含一些用于指示内存不足的指标以及一个提供内存快照的 dumpstate。
内存不足可能会导致系统发生颠簸,这是因为虽然内存不足时系统会终止某些进程来释放内存,但又会继续启动其他进程。要查看内存不足的确凿证据,请检查二进制事件日志中 am_proc_died
和 am_proc_start
条目的密集程度。
内存不足还可能会减慢任务切换速度,并且可能会阻止进行返回尝试(因为用户尝试返回到的任务已被终止)。如果启动器被终止,它会在用户触摸主屏幕按钮时重启,并且日志中会显示启动器重新加载其内容。
二进制事件日志中的 am_low_memory
条目表示最后一个缓存的进程已终止。在此之后,系统开始终止各项服务。
关于系统颠簸(分页、直接回收等)的其他指标包括 kswapd
、kworker
和 mmcqd
消耗的 CPU 周期。(请注意,收集错误报告可能会影响系统颠簸指标。)
ANR 日志可以提供类似的内存快照。
内存快照是一种 dumpstate,其中会列出正在运行的 Java 进程和本机进程(有关详情,请参阅查看整体内存分配)。请注意,快照仅提供特定时刻的状态;在此快照之前,系统的状况可能更好,也可能更糟。
- 要了解某个进程的运行时长,请参阅进程运行时。
- 要了解为什么某个进程当前正在运行,请参阅某个进程为什么正在运行?
应用会生成广播,以便在当前应用内发送事件或向其他应用发送事件。广播接收方可以通过过滤器订阅特定消息,以便收听和响应广播。错误报告中包含已发送广播和未发送广播的相关信息,以及关于收听特定广播的所有接收方的 dumpsys。
历史广播是指已发送的广播,按时间逆序排列。
summary(摘要)部分用于提供最近 300 个前台广播和最近 300 个后台广播的概况。
detail(详情)部分包含最近 50 个前台广播和最近 50 个后台广播的完整信息,以及每个广播的接收方。
- 具有
BroadcastRecord
条目的接收方是在运行时注册的,并且只会被发送到已在运行的进程。 - 具有
ResolveInfo
条目的接收方是通过清单条目注册的。ActivityManager 会为每个ResolveInfo
启动相应进程(如果相应进程尚未在运行)。
待发送的广播是指尚未发送的广播。如果队列中存在大量广播,则意味着系统无法足够快地发送广播来跟上进度。
要查看收听某个广播的接收方列表,请查看 dumpsys activity broadcasts
中的 Receiver Resolver Table。以下示例显示了收听 USER_PRESENT
的所有接收方。
显示器争用日志记录有时可以表明实际的显示器争用情况,但通常情况下会表明系统负载过重,从而导致所有进程都变慢了。您可能会在系统日志或事件日志中看到 ART 记录的长时间占用显示器的事件。
在系统日志中:
在事件日志中:
编译可能会占用大量资源,而且会加重设备负载。
下载 Google Play 商店更新时,编译可能会在后台进行。在这种情况下,来自 Google Play 商店应用 (finsky
) 和 installd
的消息会显示在 dex2oat
消息之前。
当某个应用正在加载尚未编译的 dex 文件时,编译也可能会在后台进行。在这种情况下,您将看不到 finsky
或 installd
日志记录。
创建问题叙述(如何开始、发生了什么、系统是如何应对的)需要一个固定的事件时间轴。您可以利用错误报告中的信息来同步多个日志中的时间轴并确定错误报告的确切时间戳。
错误报告会反映多个并行时间轴(系统日志、事件日志、内核日志)以及针对广播、电池统计信息等的多个专用时间轴。遗憾的是,系统通常会使用不同的时间基准来报告时间轴。
系统日志时间戳和事件日志时间戳采用用户所用的时区(与大多数其他时间戳一样)。例如,当用户点按主屏幕按钮时,系统日志会报告以下内容:
对于上述操作,事件日志会报告以下内容:
内核 (dmesg
) 日志采用不同的时间基准,按距离引导加载程序完成的时间来标记日志内容(以秒为单位)。要按照其他时间表的时间基准记录此时间表,请搜索“suspend exit”(暂停退出)和“suspend entry”(暂停进入)消息。
由于内核日志在暂停状态下可能不会包含时间,因此您应该分段记录暂停进入和暂停退出消息之间的日志。此外,内核日志使用 UTC 时区,您必须将其调整为用户时区。
要确定错误报告的生成时间,请先查看系统日志 (Logcat) 中的 dumpstate: begin
:
接下来,查看内核日志 (dmesg
) 时间戳中的 Starting service 'bugreport'
消息:
进行逆向推算以关联这两个事件,同时牢记同步时间轴中提到的注意事项。尽管在初始化错误报告之后发生了很多活动,但大多数活动并不是非常有用,因为生成错误报告这一活动会大大加重系统负载。
事件日志中包含屏幕电源状态,其中 0 表示屏幕关闭,1 表示屏幕打开,2 表示已锁屏。
错误报告中还包含关于唤醒锁的统计信息,唤醒锁是应用开发者采用的一种机制,用于表明其应用需要设备保持开启状态。(要详细了解唤醒锁,请参阅 PowerManager.WakeLock 和使 CPU 保持运行状态。)
唤醒锁总时长统计信息仅跟踪唤醒锁实际负责使设备保持唤醒状态的时间,不包括屏幕处于开启状态的时间。此外,如果同时持有多个唤醒锁,系统会在它们之间分配唤醒锁时长。
如需直观呈现电源状态方面的更多帮助,请使用 Battery Historian(一种 Google 开放源代码工具,能够利用 Android 错误报告文件分析电池消耗进程)。
“DUMP OF SERVICE”程序包中包含应用版本(以及其他实用信息)。
错误报告中包含大量的进程数据,例如启动和停止时间、运行时时长、相关服务、oom_adj
得分等。要详细了解 Android 如何管理进程,请参阅进程和线程。
procstats
部分包含有关进程及相关服务已运行时长的完整统计信息。要快速获得便于用户阅读的摘要,请搜索 AGGREGATED OVER
以查看最近 3 个小时或 24 个小时的数据,然后搜索 Summary:
以查看进程列表、这些进程已以各种优先级运行的时长,以及它们使用 RAM 的情况(格式为“最小-平均-最大 PSS”/“最小-平均-最大 USS”)。
dumpsys activity processes
部分会列出当前正在运行的所有进程,并按 oom_adj
得分排序(Android 通过为进程分配 oom_adj
值来表明进程的重要性,该值可由 ActivityManager 动态更新)。这种输出类似于内存快照的输出,但包含有关是什么导致进程运行的更多信息。在以下示例中,以粗体显示的条目表明 gms.persistent
进程正在以 vis
(可见)优先级运行,因为该系统进程已经与其 NetworkLocationService
绑定。
您可以按照以下步骤来确定过度执行蓝牙低功耗 (BLE) 扫描的应用:
查找
BluetoothLeScanner
的日志消息:在日志消息中找到 PID。在此示例中,“24840”和“24851”分别为 PID(进程 ID)和 TID(线程 ID)。
-
找到与该 PID 关联的应用:
在此示例中,程序包名称为
com.badapp
。 在 Google Play 上查找该程序包名称,以找出相应的应用:https://play.google.com/store/apps/details?id=com.badapp。
注意:对于运行 Android 7.0 的设备,系统会收集 BLE 扫描数据,并会将这些活动与初始应用相关联。有关详情,请参阅低功耗 (LE) 和蓝牙扫描。