读取错误报告

在任何类型的开发中,bug 都是客观存在的,而错误报告对于发现和解决问题至关重要。所有 Android 版本都支持使用 Android 调试桥 (adb) 捕获错误报告;Android 4.2 及更高版本支持使用开发者选项来获取错误报告,并通过电子邮件、云端硬盘等方式共享。

Android 错误报告包含文本 (.txt) 格式的 dumpsysdumpstatelogcat 数据,方便您轻松搜索特定内容。以下部分详细介绍了错误报告组件,描述了常见问题,并提供了实用技巧和 grep 命令,用于查找与这些错误关联的日志。大多数部分还包含 grep 命令和输出和/或 dumpsys 输出示例。

Logcat

logcat 日志是所有 logcat 信息的基于字符串的转储。system 部分专供框架使用,其历史记录比包含所有其他内容的 main 更长。每行通常以 timestamp UID PID TID log-level 开头,但较旧版本的 Android 中可能未列出 UID

查看事件日志

此日志包含二进制格式日志消息的字符串表示形式。它比 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 和死锁

Bugreport 可以帮助您识别导致 应用无响应 (ANR) 错误和死锁事件的原因。

识别无响应的应用

当应用在一定时间内没有响应时(通常是由于主线程被阻塞或繁忙),系统会终止该进程并将堆栈转储到 /data/anr。要找出 ANR 背后的原因,请在二进制事件日志中 grep am_anr

您也可以在 logcat 日志中 grep ANR in,其中包含有关 ANR 发生时 CPU 使用情况的更多信息。

查找堆栈跟踪

您通常可以找到与 ANR 相对应的堆栈跟踪。确保 VM 跟踪上的时间戳和 PID 与您正在调查的 ANR 匹配,然后检查进程的主线程。请记住

  • 主线程仅告诉您线程在 ANR 发生时正在执行的操作,这可能与 ANR 的真正原因相符,也可能不相符。(bugreport 中的堆栈可能是无辜的;在解除阻塞之前,其他东西可能卡住很长时间 - 但时间不够长,不足以导致 ANR。)
  • 可能存在多组堆栈跟踪(VM TRACES JUST NOWVM TRACES AT LAST ANR)。请确保您正在查看正确的部分。

查找死锁

死锁通常首先表现为 ANR,因为线程被卡住。如果死锁影响到系统服务器,看门狗最终会将其终止,从而在日志中产生类似于 WATCHDOG KILLING SYSTEM PROCESS 的条目。从用户的角度来看,设备会重启,但从技术上讲,这实际上是运行时重启,而不是真正的重启。

  • 运行时重启中,系统服务器终止并重新启动;用户看到设备返回到启动动画。
  • 重启中,内核崩溃;用户看到设备返回到 Google 启动徽标。

要查找死锁,请检查 VM 跟踪部分,查找线程 A 等待线程 B 持有的某些内容,而线程 B 又在等待线程 A 持有的某些内容的模式。

Activity

Activity 是一种应用组件,它提供用户与之交互的屏幕,以执行诸如拨打电话号码、拍照、发送电子邮件等操作。从 bugreport 的角度来看,activity 是用户可以执行的单个、专注的事情,这使得定位崩溃期间处于焦点中的 activity 非常重要。Activity(通过 ActivityManager)运行进程,因此定位给定 activity 的所有进程停止和启动也可以帮助进行故障排除。

查看焦点 Activity

要查看焦点 activity 的历史记录,请搜索 am_focused_activity

查看进程启动

要查看进程启动的历史记录,请搜索 Start proc

确定设备是否正在抖动

要确定设备是否正在抖动,请检查在短时间内 am_proc_diedam_proc_start 周围的活动是否异常增加。

内存

由于 Android 设备通常具有受限的物理内存,因此管理随机存取存储器 (RAM) 至关重要。Bugreport 包含多个内存不足的指示器以及提供内存快照的 dumpstate。

识别内存不足

内存不足可能会导致系统抖动,因为它会终止一些进程以释放内存,但会继续启动其他进程。要查看内存不足的佐证,请检查二进制事件日志中 am_proc_diedam_proc_start 条目的集中情况。

内存不足也可能减慢任务切换速度并阻碍返回尝试(因为用户尝试返回的任务已被终止)。如果 Launcher 被终止,则当用户触摸主屏幕按钮时,它会重新启动,并且日志显示 Launcher 重新加载其内容。

查看历史指示器

二进制事件日志中的 am_low_memory 条目指示最后一个缓存进程已终止。在此之后,系统开始终止服务。

查看抖动指示器

系统抖动(分页、直接回收等)的其他指示器包括 kswapdkworkermmcqd 消耗周期。(请记住,正在收集的 bugreport 可能会影响抖动指示器。)

ANR 日志可以提供类似的内存快照。

获取内存快照

内存快照是一个 dumpstate,其中列出了正在运行的 Java 和原生进程(有关详细信息,请参阅 查看总体内存分配)。请记住,快照仅提供特定时刻的状态;在快照之前,系统可能处于更好(或更差)的状态。

广播

应用程序生成广播,以在当前应用程序内或向另一个应用程序发送事件。广播接收器订阅特定消息(通过过滤器),使它们能够侦听和响应广播。Bugreport 包含有关已发送广播和未发送广播的信息,以及侦听特定广播的所有接收器的 dumpsys。

查看历史广播

历史广播是那些已发送的广播,按反向时间顺序排列。

摘要”部分概述了最近 300 个前台广播和最近 300 个后台广播。

详细信息”部分包含最近 50 个前台广播和最近 50 个后台广播的完整信息,以及每个广播的接收器。具有以下内容的接收器

  • BroadcastFilter 条目在运行时注册,并且仅发送到已运行的进程。
  • ResolveInfo 条目通过清单条目注册。如果 ActivityManager 尚未运行,则会为每个 ResolveInfo 启动进程。

查看活动广播

活动广播是那些尚未发送的广播。队列中数量过多意味着系统无法足够快地调度广播以跟上。

查看广播侦听器

要查看侦听广播的接收器列表,请检查 dumpsys activity broadcasts 中的“接收器解析器表”。以下示例显示了侦听 USER_PRESENT 的所有接收器。

监视器争用

监视器争用日志记录有时可以指示实际的监视器争用,但大多数情况下指示系统负载过重,导致一切都变慢了。您可能会在系统日志或事件日志中看到 ART 记录的长监视器事件。

在系统日志中

10-01 18:12:44.343 29761 29914 W art     : Long monitor contention event with owner method=void android.database.sqlite.SQLiteClosable.acquireReference() from SQLiteClosable.java:52 waiters=0 for 3.914s

在事件日志中

10-01 18:12:44.364 29761 29914 I dvm_lock_sample: [com.google.android.youtube,0,pool-3-thread-9,3914,ScheduledTaskMaster.java,138,SQLiteClosable.java,52,100]

后台编译

编译可能很耗费资源并且会增加设备负载。

当 Google Play 商店更新正在下载时,可能会在后台进行编译。在这种情况下,来自 Google Play 商店应用 (finsky) 和 installd 的消息会出现在 dex2oat 消息之前。

当应用程序加载尚未编译的 dex 文件时,也可能在后台进行编译。在这种情况下,您不会看到 finskyinstalld 日志记录。

叙述

建立问题的叙述(问题是如何开始的、发生了什么、系统如何反应)需要可靠的事件时间线。您可以使用 bugreport 中的信息来同步多个日志中的时间线,并确定 bugreport 的确切时间戳。

同步时间线

Bugreport 反映了多个并行时间线:系统日志、事件日志、内核日志以及广播、电池统计信息等的多个专用时间线。不幸的是,时间线通常使用不同的时间基准报告。

系统日志和事件日志时间戳与用户处于同一时区(大多数其他时间戳也是如此)。例如,当用户点击主屏幕按钮时,系统日志报告

10-03 17:19:52.939  1963  2071 I ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10200000 cmp=com.google.android.googlequicksearchbox/com.google.android.launcher.GEL (has extras)} from uid 1000 on display 0

对于同一操作,事件日志报告

10-03 17:19:54.279  1963  2071 I am_focused_activity: [0,com.google.android.googlequicksearchbox/com.google.android.launcher.GEL]

内核 (dmesg) 日志使用不同的时间基准,用自引导加载程序完成以来的秒数标记日志项。要将此时间尺度注册到其他时间尺度,请搜索suspend exitsuspend entry 消息

<6>[201640.779997] PM: suspend exit 2015-10-03 19:11:06.646094058 UTC
…
<6>[201644.854315] PM: suspend entry 2015-10-03 19:11:10.720416452 UTC

由于内核日志可能不包含挂起期间的时间,因此您应该在挂起进入和退出消息之间分段注册日志。此外,内核日志使用 UTC 时区,必须调整为用户时区。

识别 bugreport 时间

要确定何时获取 bugreport,请首先检查系统日志 (Logcat) 中的 dumpstate: begin

10-03 17:19:54.322 19398 19398 I dumpstate: begin

接下来,检查内核日志 (dmesg) 时间戳中的 Starting service 'bugreport' 消息

<5>[207064.285315] init: Starting service 'bugreport'...

向后回溯以关联这两个事件,同时牢记 同步时间线 中提到的注意事项。虽然在 bugreport 启动后会发生很多事情,但大多数活动并不是很有用,因为获取 bugreport 的行为会大大增加系统负载。

电源

事件日志包含屏幕电源状态,其中 0 表示屏幕关闭,1 表示屏幕开启,2 表示 Keyguard 完成。

Bugreport 还包含有关唤醒锁的统计信息,唤醒锁是应用程序开发者用来指示其应用程序需要设备保持开启状态的机制。(有关唤醒锁的详细信息,请参阅 PowerManager.WakeLock保持 CPU 开启。)

聚合的唤醒锁持续时间统计信息跟踪唤醒锁实际负责保持设备唤醒的时间,包括屏幕开启的时间。此外,如果同时持有多个唤醒锁,则唤醒锁持续时间将分配到这些唤醒锁中。

为了更轻松地可视化电源状态,请使用 Battery Historian,这是一个 Google 开源工具,用于使用 Android bugreport 文件分析电池消耗。

软件包

DUMP OF SERVICE package 部分包含应用程序版本(和其他有用信息)。

进程

Bugreport 包含大量关于进程的数据,包括启动和停止时间、运行时长、关联服务、oom_adj 分数等。有关 Android 如何管理进程的详细信息,请参阅 进程和线程

确定进程运行时

procstats 部分包含有关进程和关联服务已运行多长时间的完整统计信息。为了获得快速、人类可读的摘要,请搜索 AGGREGATED OVER 以查看过去三小时或 24 小时的数据,然后搜索 Summary: 以查看进程列表、这些进程在各种优先级下运行了多长时间,以及它们的 RAM 使用情况,格式为最小-平均-最大 PSS/最小-平均-最大 USS。

进程正在运行的原因

dumpsys activity processes 部分列出了所有当前正在运行的进程,并按 oom_adj 分数排序(Android 通过为进程分配 oom_adj 值来指示进程的重要性,该值可以由 ActivityManager 动态更新)。输出类似于 内存快照 的输出,但包括有关导致进程运行的其他信息。在下面的示例中,粗体条目指示 gms.persistent 进程以 vis(可见)优先级运行,因为系统进程绑定到其 NetworkLocationService

扫描

使用以下步骤识别执行过多蓝牙低功耗 (BLE) 扫描的应用程序

  • 查找 BluetoothLeScanner 的日志消息
    $ grep 'BluetoothLeScanner' ~/downloads/bugreport.txt
    07-28 15:55:19.090 24840 24851 D BluetoothLeScanner: onClientRegistered() - status=0 clientIf=5
    
  • 在日志消息中找到 PID。在此示例中,“24840”和“24851”是 PID(进程 ID)和 TID(线程 ID)。
  • 找到与 PID 关联的应用程序
    PID #24840: ProcessRecord{4fe996a 24840:com.badapp/u0a105}
    

    在此示例中,包名称为 com.badapp

  • 在 Google Play 上查找包名称以识别负责的应用程序:https://play.google.com/store/apps/details?id=com.badapp

注意:对于运行 Android 7.0 的设备,系统会收集 BLE 扫描的数据并将这些活动与启动应用程序关联起来。有关详细信息,请参阅 低功耗 (LE) 和蓝牙扫描