CPU 分析器中的跟踪视图提供了多种方法来查看从录制跟踪中获取的信息。
对于方法跟踪和函数跟踪,您可以在“线程”时间轴中直接查看“调用图表”,以及“分析”窗格的“火焰图”、“自上而下”、“自下而上”和“事件”选项卡中。对于调用堆栈帧,您可以查看已执行的代码部分,以及调用它的原因。对于系统跟踪,您可以在“线程”时间轴中直接查看“跟踪事件”,以及“分析”窗格的“火焰图”、“自上而下”、“自下而上”和“事件”选项卡中。
鼠标和键盘快捷键 可用于更轻松地导航“调用图表”或“跟踪事件”。
使用调用图表检查跟踪
调用图表以图形方式显示方法跟踪或函数跟踪,其中调用的时间段和时间点在水平轴上表示,而其被调用者在垂直轴上显示。对系统 API 的调用以橙色显示,对您应用程序自身方法的调用以绿色显示,对第三方 API(包括 Java 语言 API)的调用以蓝色显示。图 4 显示了一个示例调用图表,并说明了给定方法或函数的自用时间、子项时间和总时间的概念。您可以在关于如何使用自顶向下和自底向上检查跟踪的部分中了解更多有关这些概念的信息。
提示:要跳转到方法或函数的源代码,请右键单击它并选择跳转到源代码。这适用于任何分析窗格选项卡。
使用火焰图选项卡检查跟踪
火焰图选项卡提供一个反向调用图表,它聚合了相同的调用堆栈。也就是说,具有相同调用者序列的相同方法或函数被收集并表示为火焰图中的一个更长的条形图(而不是像调用图表中那样显示为多个更短的条形图)。这使得更容易看到哪些方法或函数消耗了最多的时间。但是,这也意味着水平轴不表示时间线;相反,它表示每个方法或函数执行所需时间的相对量。
为了帮助说明这个概念,请考虑图 2 中的调用图表。请注意,方法 D 对 B(B1、B2 和 B3)进行了多次调用,其中一些对 B 的调用会调用 C(C1 和 C3)。
因为 B1、B2 和 B3 共享相同的调用者序列(A → D → B),所以它们被聚合在一起,如图 3 所示。类似地,C1 和 C3 被聚合在一起,因为它们共享相同的调用者序列(A → D → B → C);请注意,C2 没有被包含进来,因为它具有不同的调用者序列(A → D → C)。
聚合后的调用用于创建火焰图,如图 4 所示。请注意,对于火焰图中的任何给定调用,消耗最多 CPU 时间的被调用者会首先出现。
使用自顶向下和自底向上检查跟踪
自顶向下选项卡显示一个调用列表,其中扩展方法或函数节点将显示其被调用者。图 5 显示了图 1 中调用图表的自顶向下图。图中的每个箭头都从调用者指向被调用者。
如图 5 所示,在自顶向下选项卡中扩展方法 A 的节点会显示其被调用者,方法 B 和 D。之后,扩展方法 D 的节点会显示其被调用者,方法 B 和 C,依此类推。与火焰图选项卡类似,自顶向下树会聚合共享相同调用堆栈的相同方法的跟踪信息。也就是说,火焰图选项卡提供自顶向下选项卡的图形表示。
自顶向下选项卡提供以下信息,以帮助描述在每个调用上花费的 CPU 时间(时间也表示为线程在所选范围内总时间的百分比)。
- 自用时间:方法或函数调用在执行自己的代码而不是其被调用者代码时所花费的时间,如图 1 中方法 D 的示例所示。
- 子项时间:方法或函数调用在执行其被调用者而不是自己的代码时所花费的时间,如图 1 中方法 D 的示例所示。
- 总时间:方法的自用时间和子项时间之和。这表示应用程序在执行调用时所花费的总时间,如图 1 中方法 D 的示例所示。
自底向上选项卡显示一个调用列表,其中扩展函数或方法的节点将显示其调用者。使用图 5 中显示的示例跟踪,图 6 提供了方法 C 的自底向上树。在自底向上树中打开方法 C 的节点会显示其每个唯一的调用者,方法 B 和 D。请注意,尽管 B 调用 C 两次,但在自底向上树中扩展方法 C 的节点时,B 只出现一次。之后,扩展 B 的节点会显示其调用者,方法 A 和 D。
自底向上选项卡对于根据消耗最多(或最少)CPU 时间的方法或函数进行排序非常有用。您可以检查每个节点以确定哪些调用者花费最多 CPU 时间来调用这些方法或函数。与自顶向下树相比,自底向上树中每个方法或函数的计时信息是参照每个树顶部的节点(顶层节点)的。CPU 时间也表示为该记录期间线程总时间的百分比。下表有助于解释如何解读顶层节点及其调用者(子节点)的计时信息。
自用时间 | 子项时间 | 总时间 | |
---|---|---|---|
自底向上树顶部的节点(顶层节点)上的方法或函数 | 表示该方法或函数在执行自己的代码而不是其被调用者代码时所花费的总时间。与自顶向下树相比,此计时信息表示在记录持续时间内对该方法或函数的所有调用的总和。 | 表示该方法或函数在执行其被调用者而不是自己的代码时所花费的总时间。与自顶向下树相比,此计时信息表示在记录持续时间内对该方法或函数的被调用者所有调用的总和。 | 自用时间和子项时间之和。 |
调用者(子节点) | 表示被调用者被调用者调用时被调用者的总自用时间。以图 6 中的自底向上树为例,方法 B 的自用时间将等于方法 C 被 B 调用时每次执行的 self 时间的总和。 | 表示被调用者被调用者调用时被调用者的总子项时间。以图 6 中的自底向上树为例,方法 B 的子项时间将等于方法 C 被 B 调用时每次执行的 children 时间的总和。 | 自用时间和子项时间之和。 |
注意:对于给定的记录,Android Studio 在分析器达到文件大小限制时停止收集新数据(但是,这不会停止记录)。这在执行已检测跟踪时通常会快得多,因为这种类型的跟踪在较短的时间内收集更多数据,与采样跟踪相比。如果您将检查时间延长到达到限制后发生的记录的一部分,则跟踪窗格中的计时数据不会改变(因为没有新的数据可用)。此外,当您仅选择记录中没有数据的那一部分时,跟踪窗格会为计时信息显示NaN。
使用事件表检查跟踪
事件表列出了当前选定线程中的所有调用。您可以通过单击列标题对它们进行排序。通过选择表格中的一行,您可以导航到时间线上的选定调用的开始时间和结束时间。这使您能够准确地定位时间线上的事件。
检查调用堆栈帧
调用堆栈有助于理解代码的哪一部分已执行,以及为何执行它。如果为 Java/Kotlin 程序收集调用堆栈样本记录,则调用堆栈通常不仅包括 Java/Kotlin 代码,还包括来自 JNI 本地代码、Java 虚拟机(例如,android::AndroidRuntime::start
)和系统内核 ([kernel.kallsyms]+offset
) 的帧。这是因为 Java/Kotlin 程序通常通过 Java 虚拟机执行。本地代码需要运行程序本身,并使程序与系统和硬件进行通信。分析器提供这些帧以确保精确性;但是,根据您的调查,您可能认为这些额外的调用帧有用,也可能认为它们没有用。分析器提供了一种折叠您不感兴趣的帧的方法,以便您可以隐藏与您的调查无关的信息。
在下面的示例中,下面的跟踪具有许多标记为 [kernel.kallsyms]+offset
的帧,这些帧目前对开发没有用处。
要将这些帧折叠为一个,您可以从工具栏中选择折叠帧按钮,选择要折叠的路径,然后选择应用按钮来应用更改。在本例中,路径为 [kernel.kallsyms]
。
这样做会折叠左面板和右面板上与所选路径相对应的帧,如下所示。
检查系统跟踪
检查系统跟踪时,可以在线程时间线中检查跟踪事件,以查看每个线程上发生的事件的详细信息。将鼠标指针悬停在事件上,以查看事件的名称以及每个状态下花费的时间。单击事件以在分析窗格中查看更多信息。
检查系统跟踪:CPU 内核
除了 CPU 调度数据外,系统跟踪还包括按内核划分的 CPU 频率。这显示了每个内核的活动量,并可能让您了解现代移动处理器中哪些是"大核心"或"小核心"。
CPU 内核窗格(如图 8 所示)显示了调度到每个内核的线程活动。将鼠标指针悬停在线程活动上,以查看该内核在该特定时间正在运行哪个线程。
有关检查系统跟踪信息的更多信息,请参阅systrace
文档中调查 UI 性能问题部分。
检查系统跟踪:帧渲染时间线
您可以检查应用程序在主线程和 RenderThread
上渲染每帧需要多长时间,以调查导致 UI 卡顿和帧率低的瓶颈。要了解如何使用系统跟踪来调查和帮助减少 UI 卡顿,请参阅UI 卡顿检测。
检查系统跟踪:进程内存(RSS)
对于部署到运行 Android 9 或更高版本的设备上的应用程序,进程内存(RSS)部分显示了应用程序当前使用的物理内存量。
总时间
这是您的进程当前使用的物理内存的总量。在基于 Unix 的系统上,这被称为“驻留集大小”,是匿名分配、文件映射和共享内存分配使用的所有内存的组合。
对于 Windows 开发人员来说,常驻集大小类似于工作集大小。
已分配
此计数器跟踪进程的正常内存分配当前使用多少物理内存。这些是匿名(未由特定文件支持)且私有的(未共享)分配。在大多数应用程序中,这些分配由堆分配(使用 malloc
或 new
)和堆栈内存组成。当从物理内存中交换出去时,这些分配将写入系统交换文件。
文件映射
此计数器跟踪进程使用多少物理内存进行文件映射,即内存管理器将文件映射到内存区域中的内存。
共享
此计数器跟踪使用多少物理内存来共享系统中此进程与其他进程之间的内存。