检查跟踪

CPU 分析器中的跟踪视图提供了多种查看已记录跟踪信息的方法。

对于方法跟踪和函数跟踪,您可以直接在**线程**时间线中查看**调用图表**,以及在**分析**窗格的**火焰图**、**自上而下**、**自下而上**和**事件**选项卡中查看。对于调用栈帧,您可以查看已执行的代码部分以及调用它的原因。对于系统跟踪,您可以直接在**线程**时间线中查看**跟踪事件**,以及在**分析**窗格的**火焰图**、**自上而下**、**自下而上**和**事件**选项卡中查看。

鼠标和键盘快捷键 可用于更轻松地导航**调用图表**或**跟踪事件**。

使用调用图表检查跟踪

调用图表以图形方式显示方法跟踪或函数跟踪,其中调用的时间段和时间显示在横轴上,被调用者显示在纵轴上。对系统 API 的调用以橙色显示,对您应用自身方法的调用以绿色显示,对第三方 API(包括 Java 语言 API)的调用以蓝色显示。图 4 显示了一个调用图表示例,并说明了给定方法或函数的自用时间、子项时间和总时间的概念。您可以在有关如何使用自顶向下和自底向上检查跟踪的部分中了解有关这些概念的更多信息。

图 1. 一个调用图表示例,说明了方法 D 的自用时间、子项时间和总时间。

提示:要跳转到方法或函数的源代码,请右键单击它并选择跳转到源代码。这适用于“分析”窗格的任何选项卡。

使用火焰图选项卡检查跟踪

火焰图选项卡提供一个反向调用图表,该图表聚合相同的调用堆栈。也就是说,共享相同调用者序列的相同方法或函数将被收集并表示为火焰图中的一个较长的条形图(而不是像调用图表中显示的那样显示为多个较短的条形图)。这使得更容易看出哪些方法或函数消耗了最多的时间。但是,这也意味着横轴不代表时间线;相反,它表示每个方法或函数执行所需时间的相对量。

为了帮助说明这个概念,请考虑图 2 中的调用图表。请注意,方法 D 对 B(B1、B2 和 B3)进行了多次调用,其中一些对 B 的调用会调用 C(C1 和 C3)。

图 2. 一个包含多个共享公共调用者序列的方法调用的调用图表。

因为 B1、B2 和 B3 共享相同的调用者序列(A → D → B),所以它们被聚合,如图 3 所示。类似地,C1 和 C3 被聚合,因为它们共享相同的调用者序列(A → D → B → C);请注意,C2 没有包含在内,因为它具有不同的调用者序列(A → D → C)。

图 3. 聚合共享相同调用堆栈的相同方法。

聚合的调用用于创建火焰图,如图 4 所示。请注意,对于火焰图中的任何给定调用,消耗最多 CPU 时间的被调用者会首先出现。

图 4. 图 5 中所示调用图表的火焰图表示。

使用自顶向下和自底向上检查跟踪

自顶向下选项卡显示一个调用列表,其中展开方法或函数节点会显示其被调用者。图 5 显示了图 1 中调用图表的自顶向下图。图中的每个箭头都指向从调用者到被调用者。

如图 5 所示,在自顶向下选项卡中展开方法 A 的节点会显示其被调用者,方法 B 和 D。之后,展开方法 D 的节点会显示其被调用者,方法 B 和 C,依此类推。与火焰图选项卡类似,自顶向下树会聚合共享相同调用堆栈的相同方法的跟踪信息。也就是说,火焰图选项卡提供自顶向下选项卡的图形表示。

自顶向下选项卡提供以下信息,以帮助描述每个调用上花费的 CPU 时间(时间也表示为线程在所选范围内的总时间的百分比)

  • 自身:方法或函数调用花费在其自身代码上执行的时间,而不是在其被调用者上执行的时间,如图 1 中方法 D 所示。
  • 子项:方法或函数调用花费在其被调用者上执行的时间,而不是在其自身代码上执行的时间,如图 1 中方法 D 所示。
  • 总计:方法自身时间和子项时间的总和。这表示应用执行调用所花费的总时间,如图 1 中方法 D 所示。

图 5. 自顶向下树。

图 6. 图 5 中方法 C 的自底向上树。

自底向上选项卡显示一个调用列表,其中展开函数或方法的节点会显示其调用者。使用图 5 中所示的示例跟踪,图 6 提供了方法 C 的自底向上树。在自底向上树中打开方法 C 的节点会显示其每个唯一的调用者,方法 B 和 D。请注意,尽管 B 调用 C 两次,但在自底向上树中展开方法 C 的节点时,B 只出现一次。之后,展开 B 的节点会显示其调用者,方法 A 和 D。

自底向上选项卡可用于根据消耗最多(或最少)CPU 时间的方法或函数对其进行排序。您可以检查每个节点以确定哪些调用者花费最多的 CPU 时间来调用这些方法或函数。与自顶向下树相比,自底向上树中每个方法或函数的时间信息是相对于每棵树顶部的树(顶部节点)的方法而言的。CPU 时间也表示为记录期间线程总时间的百分比。下表有助于解释如何解释顶部节点及其调用者(子节点)的时间信息。

自身 子项 总计
自底向上树顶部的函数或方法(顶部节点) 表示该方法或函数花费在其自身代码上执行的时间,而不是在其被调用者上执行的时间。与自顶向下树相比,此时间信息表示在记录期间对该方法或函数的所有调用的总和。 表示该方法或函数花费在其被调用者上执行的时间,而不是在其自身代码上执行的时间。与自顶向下树相比,此时间信息表示在记录期间对该方法或函数的被调用者的所有调用的总和。 自身时间和子项时间的总和。
调用者(子节点) 表示被调用者在被调用者调用时自身的总时间。例如,使用图 6 中的自底向上树,方法 B 的自身时间将等于 B 调用方法 C 的每次执行的自身时间的总和。 表示被调用者在被调用者调用时子项的总时间。例如,使用图 6 中的自底向上树,方法 B 的子项时间将等于 B 调用方法 C 的每次执行的子项时间的总和。 自身时间和子项时间的总和。

注意:对于给定的记录,当探查器达到文件大小限制时,Android Studio 会停止收集新数据(但是,这不会停止记录)。在执行检测跟踪时,这种情况通常会更快发生,因为与采样跟踪相比,这种类型的跟踪在较短时间内会收集更多数据。如果您将检查时间延长到达到限制后发生的记录期间,则跟踪窗格中的时间数据不会更改(因为没有新的数据可用)。此外,当您仅选择记录中没有可用数据的部分时,跟踪窗格会为时间信息显示NaN

使用事件表检查跟踪

事件表列出了当前选定线程中的所有调用。您可以通过单击列标题对其进行排序。通过选择表中的一行,您可以将时间线导航到所选调用的开始时间和结束时间。这允许您准确地在时间线上定位事件。

图 7. 查看分析窗格中的事件选项卡。

检查调用堆栈帧

调用堆栈有助于了解已执行代码的哪一部分以及为什么调用它。如果为 Java/Kotlin 程序收集了调用堆栈采样记录,则调用堆栈通常不仅包括 Java/Kotlin 代码,还包括来自 JNI 本机代码、Java 虚拟机(例如,android::AndroidRuntime::start)和系统内核([kernel.kallsyms]+offset)的帧。这是因为 Java/Kotlin 程序通常通过 Java 虚拟机执行。需要本机代码来运行程序本身以及程序与系统和硬件进行通信。探查器提供这些帧是为了提高精度;但是,根据您的调查,您可能会发现这些额外的调用帧有用,也可能不会发现它们有用。探查器提供了一种方法来折叠您不感兴趣的帧,以便您可以隐藏与您的调查无关的信息。

在下面的示例中,下面的跟踪有很多标记为[kernel.kallsyms]+offset的帧,这些帧目前对开发没有用。

Example call trace

要将这些帧折叠成一个,您可以从工具栏中选择折叠帧按钮,选择要折叠的路径,然后选择应用按钮以应用更改。在此示例中,路径为[kernel.kallsyms]

Example of simpleperf menu

这样做会折叠左右面板上与所选路径对应的帧,如下所示。

Example of simpleperf collapsed frames

检查系统跟踪

检查系统跟踪时,您可以检查线程时间线中的跟踪事件,以查看每个线程上发生的事件的详细信息。将鼠标指针悬停在事件上,以查看事件的名称以及在每个状态下花费的时间。单击事件以在分析窗格中查看更多信息。

检查系统跟踪:CPU 内核

除了 CPU 调度数据外,系统跟踪还包括按内核划分的 CPU 频率。这显示了每个内核上的活动量,并可以让您了解现代移动处理器中哪些是"大"内核或"小"内核

图 8. 查看渲染线程的 CPU 活动和跟踪事件。

CPU 内核窗格(如图 8 所示)显示在每个内核上调度的线程活动。将鼠标指针悬停在线程活动上,以查看该内核在特定时间运行的线程。

有关检查系统跟踪信息的更多信息,请参阅systrace文档的调查 UI 性能问题部分。

检查系统跟踪:帧渲染时间线

您可以检查应用在主线程和RenderThread上渲染每个帧所需的时间,以调查导致 UI 卡顿和帧率低的瓶颈。要了解如何使用系统跟踪来调查并帮助减少 UI 卡顿,请参阅UI 卡顿检测

检查系统跟踪:进程内存 (RSS)

对于部署到运行 Android 9 或更高版本的设备上的应用,进程内存 (RSS) 部分显示应用当前正在使用的物理内存量。

图 9. 在探查器中查看物理内存。

总计

这是您的进程当前正在使用的物理内存的总量。在基于 Unix 的系统上,这被称为“驻留集大小”,它是匿名分配、文件映射和共享内存分配使用的所有内存的组合。

对于 Windows 开发人员,驻留集大小类似于工作集大小。

已分配

此计数器跟踪进程的普通内存分配当前使用了多少物理内存。这些分配是匿名的(不由特定文件支持)且私有的(不共享)。在大多数应用程序中,这些分配由堆分配(使用mallocnew)和栈内存组成。当从物理内存交换出去时,这些分配将写入系统交换文件。

文件映射

此计数器跟踪进程为文件映射使用的物理内存量,即由内存管理器将文件映射到内存区域。

共享

此计数器跟踪正在使用多少物理内存来在此进程和系统中的其他进程之间共享内存。