优化应用程序的CPU使用率对系统有很大帮助,例如提供更流畅的用户体验以及节省设备电池寿命。在与应用程序交互时,可以使用CPU Profiler实时检查应用程序的CPU使用率和线程活动,也可以检查方法跟踪、功能跟踪和系统跟踪中的详细信息。CPU Profiler记录和显示的信息类型取决于您的配置:

  • 系统跟踪:捕获细粒度的详细细心,允许您检查应用程序如何与系统资源交互。
  • 方法和函数跟踪:对于应用程序进程中的每个线程,您可以发现在一段时间内执行哪些方法(Java)或函数(C/C++)以及每个方法或函数在执行期间消耗的CPU资源。还可以使用方法和函数跟踪来标识调用方和被调用方。调用方是调用另一个方法或函数的方法或函数,被调用方是由另一个方法或函数调用的方法或函数。您可以使用这些信息来确定哪些方法或函数频繁调用资源繁重任务,并优化应用程序的代码以避免不必要的工作。

CPU Profiler简介

可以通过下面的步骤打开CPU Profiler:

  1. 选择View > Tool Windows > Profiler ,或单击工具栏中的Profile

如果弹出Select Deployment Target对话框,请选择要进行分析的设备。如果已通过USB连接设备,但未看到列出的设备,请确保已开启USB调试。

  1. 单击CPU时间线中的任意位置以打开CPU Profiler。

打开CPU Profiler时,它会立即开始显示应用程序的CPU使用率和线程活动。您应该看到类似下图的内容。

cpu_profiler_L2-2X.png

如上图所示,CPU Profiler默认视图包括以下时间线:

  1. Event时间线:显示应用程序活动及在其生命周期中的不同状态之间的转换,并指示用户与设备的交互,包括屏幕旋转事件。如果运行在Android7.1(API级别25)及更低版本的设备上,可以听过高级配置来开启Event时间线。
  2. CPU时间线:显示应用程序的实时CPU使用率和正在使用的线程总数。时间线还显示其他进程(如系统进程或其他应用程序)的CPU使用情况,因此您可以将其与应用程序的使用情况进行比较。您可以通过沿着时间轴的水平轴移动鼠标来检查历史CPU使用数据。
  3. Thread activity时间线:列出属于应用程序进程的每个线程,并使用下面列出的颜色沿时间线指示其活动。记录跟踪后,可以从此时间线中选择一个线程,以在跟踪窗中检查其数据。

    • 绿色:线程处于活动状态或准备使用CPU。也就是说,它处于运行或可运行状态。
    • 黄色:线程处于活动状态,但它正在等待I/O操作(如磁盘或网络I/O)才能完成工作。
    • 灰色:线程正在休眠,不占用任何CPU时间。当线程需要访问尚不可用的资源时,有时会发生这种情况。要么线程自愿进入睡眠,要么内核将线程置于睡眠状态,直到所需资源可用为止。

CPU Profiler还报告Android Studio和Android平台添加到应用程序进程中的线程(如JDWP、Profile Saver、Studio:VMStats、Studio:Perfa和Studio:Heartbeat)的CPU使用情况(不过在线程活动时间线中显示的确切名称可能有所不同)。Android Studio报告这些数据方便您确定线程活动和CPU使用情况实际上是由应用程序的代码引起的。

记录跟踪数据

要开始记录跟踪,请从CPU Profiler顶部的下拉菜单中选择一个记录配置,然后单击Record。如下图,CPU Profiler会显示正在进行的记录的状态、持续时间和类型。

cpu-profiler-recording_2x.png

与应用程序交互,完成后单击Stop 。Profiler自动选择记录的时间范围,并在跟踪窗格中显示其跟踪信息,如下图所示。如果要检查其他线程的跟踪,请从线程活动时间线中选择它。

cpu_profiler_L3-2X.png

  1. 选定范围:选定在跟踪窗格中需要检查的记录时间。当您第一次记录跟踪时,CPU Profiler会自动选择您在CPU时间轴中记录的整个长度。要仅检查记录时间范围的一部分跟踪数据,请拖动高亮显示区域的边缘。
  2. 时间戳:指示记录的跟踪的开始和结束时间,与Profiler开始收集CPU使用信息的时间有关。要选择整个录制,请单击时间戳。
  3. 跟踪窗口:显示所选时间范围和线程的跟踪数据。此窗口仅在您记录至少一个跟踪后出现。在此窗口中,可以选择如何查看每个堆栈跟踪(使用跟踪选项卡)和如何测量执行时间(使用时间参考菜单)。
  4. 跟踪选项卡:选择如何显示跟踪详细信息。有关每个选项的详细信息,在下面“检查跟踪”会描述。
  5. 时间参考菜单:选择以下选项确定如何测量每个调用的计时信息,

    • 墙面时间:计时信息表示实际经过的时间。
    • 线程时间:计时信息表示实际运行时间减去线程不消耗CPU资源时该时间的任何部分。对于任何给定的调用,其线程时间总是小于或等于墙面时钟时间。使用线程时间可以让您更好地了解给定方法或函数消耗了线程的实际CPU使用量。
  6. 筛选:按函数、方法、类或包名称筛选跟踪数据。例如,如果要快速识别与特定调用相关的跟踪数据,请单击 Filter 或按Ctrl+F,然后在搜索字段中输入名称。在Call chartFlame chart选项卡中,将强调包含与搜索查询匹配的调用、包或类的调用堆栈。在Top downBottom up选项卡中,这些调用堆栈的优先级高于其他跟踪结果。还可以通过选中搜索字段旁边的选择框来启用以下选项:

    • Regex:要在搜索中包含正则表达式,请使用此选项。
    • Match case:如果搜索区分大小写,请使用此选项。

选择记录配置

在开始记录跟踪信息之前,请为要捕获的分析信息选择适当的记录配置:

  • Sample Java Methods:在应用程序基于Java的代码执行期间,频繁的捕获应用程序的调用堆栈。Profiler比较捕获的数据,以获取有关应用程序基于Java的代码执行的时间和资源使用信息。基于采样的跟踪的一个固有问题是,如果应用程序在捕获调用堆栈之后进入方法并在下一次捕获之前退出该方法,则该方法调用不被Profiler记录。如果您对具有如此短生命周期的跟踪方法感兴趣,则应使用检测跟踪。
  • Trace Java Methods:在运行时对应用程序进行检测,以在每个方法调用的开始和结束处记录时间戳。收集并比较时间戳以生成方法跟踪数据,包括时间信息和CPU使用情况。请注意,检测每个方法相关联的开销会影响运行时性能,并可能影响分析数据;这对于生命周期相对较短的方法更为明显。此外,如果应用程序在短时间内执行大量方法,探查器可能会很快超出其文件大小限制,并且可能无法记录任何进一步的跟踪数据。
  • Sample C/C++ Functions:捕获应用程序Native线程的采样跟踪。要使用此配置,必须将应用程序安装到运行Android 8.0(API级别26)或更高版本的设备上。在内部,此配置使用simpleperf跟踪应用程序的Native代码。如果要为simpleperf指定其他选项,例如采样特定设备cpu或高精度指定采样持续时间,可以从命令行使用simpleperf。
  • Trace System Calls:捕获细粒度的详细信息,允许您检查应用程序与系统资源的交互方式。您可以检查线程状态的精确计时和持续时间,可视化CPU所有核瓶颈位置,并添加自定义跟踪事件进行分析。在解决性能问题时,这些信息可能非常重要。要使用此配置,必须将应用程序安装到运行Android7.0(API级别24)或更高版本的设备上。使用此跟踪配置时,可以通过检测代码来直观地标记探查器时间线中的重要代码。检测C/C++代码时,使用Trace.h提供的Native跟踪API。检测Java代码时,使用跟踪类。此跟踪配置构建在Systrace上,您可以使用Systrace命令行指定CPU Profiler中提供的选项之外的选项。Systrace提供的附加系统级数据可以帮助您检查native系统进程和丢失或延迟帧的故障。

修改记录配置

您可以在CPU Recording Configurations对话框中创建、编辑和查看记录配置,该对话框通过从CPU Profiler顶部的记录配置的下拉菜单中选择Edit configurations”打开。

若要查看现有记录配置的设置,请在CPU Recording Configurations对话框的左窗格中选择它。要创建新的录制配置,请执行以下操作:

  1. 单击对话框左上角的Add 。这将创建具有某些默认设置的新配置。
  2. 命名您的配置。
  3. 选择Trace Technology
  4. 对于采样记录配置,以微秒(μs)为单位指定Sampling interval 。此值表示应用程序调用堆栈的每个示例之间的时间。指定的间隔越短,达到记录数据的文件大小限制的速度就越快。
  5. 指定设备中记录数据的File size limit(MB)。当您停止录制时,Android Studio会解析这些数据并将其显示在profiler窗口中。因此,如果增加限制并记录大量数据,Android Studio解析文件的时间会更长,并且可能会变得无响应。
注意:如果您使用的连接设备搭载的是 Android 8.0(API 级别 26)或更高版本,那么对跟踪数据的文件大小没有限制,系统会忽略此值。不过,您仍需留意每次记录后设备收集了多少数据,Android Studio 可能难以解析大型跟踪文件。例如,如果您记录的是采样时间间隔很短的采样跟踪数据,或是在应用短时间内调用许多方法的情况下记录检测跟踪数据,那么很快就会生成大型跟踪文件。
  1. 若要接受更改并继续对其他配置进行更改,请单击Apply。若要接受所有应用的更改并关闭对话框,请单击OK

使用Debug API记录CPU活动

您可以使用Debug API让应用程序能够启动和停止CPU Profiler中的活动记录。当你在应用程序中调用startMethodTracing(String tracePath)时,CPU Profiler开始记录,当你的应用调用stopMethodTracing()时,CPU Profiler停止记录。当使用Debug API做为触发CPU的活动记录时,CPU Profiler会将Debug API显示为活动CPU的记录配置。

要使用Debug API控制CPU活动的记录,请将插入指令的应用程序安装到运行Android 8.0(API级别26)或更高版本的设备上。关于Debug API使用的详细信息,会在后面描述。

记录应用程序启动期间的CPU活动

要在应用程序启动期间自动开始记录CPU活动,请执行以下操作:

  1. 选择Run > Edit Configurations
  2. Profiling选项卡中,选中Start recording a method trace on startup旁边的复选框。
  3. 从菜单中选择CPU记录配置。
  4. 单击Apply
  5. 通过选择Run > Profile将应用程序安装到运行Android 8.0(API级别26)或更高版本的设备上。
重要提示: Debug API 应该与用于开始和停止 CPU 活动记录的其他方法(如 CPU Profiler 图形界面中的按钮,以及应用启动时自动记录的记录配置中的设置)分开使用。

导出跟踪数据

使用CPU Profiler记录CPU活动后,可以将数据导出为.trace文件以与其他人共享或稍后检查。要从CPU时间轴导出跟踪文件,请执行以下操作:

  1. 在CPU时间轴中,右键单击要导出的已记录方法跟踪或系统跟踪。
  2. 从菜单中选择Export trace
  3. 选择要保存文件的位置,指定文件名,然后单击OK

要从Sessions窗格导出跟踪文件,请执行以下操作:

  1. Sessions窗格中,右键单击要导出的跟踪。
  2. 单击会话项右侧的Export method traceExport system trace按钮。
  3. 选择要保存文件的位置,指定文件名,然后单击OK

导入跟踪数据

可以导入Debug API或CPU Profiler创建的.trace文件用来分析。通过在Profiler的Sessions 窗格中单击Start new profiler session,然后选择Load from file,导入跟踪文件。与直接在CPU Profiler中捕获的跟踪一样,您可以分析在CPU Profiler中导入的跟踪,但要注意下列的不同:

  • CPU活动不是沿着CPU时间线表示的。
  • 线程活动时间线仅指示每个线程的跟踪数据可用的位置,而不是实际的线程状态(如运行、等待或休眠)。

检查跟踪数据

CPU Profiler中的跟踪窗格提供了几个选项卡,允许您选择如何从记录的跟踪中查看信息。对于方法跟踪和函数跟踪,可以从Call ChartFlame ChartTop DownBottom Up中进行选择。对于系统跟踪,可以从Trace EventsFlame ChartTop DownBottom Up中进行选择。

使用Call Chart检查跟踪

Call Chart选项提供了方法跟踪或函数跟踪的图形表示,其中调用的周期和时间在水平轴上表示,并且其被调用方沿垂直轴示出。对系统APIs的调用显示为橙色,对应用程序自身方法的调用显示为绿色,对第三方APIs(包括Java APIs)的调用显示为蓝色。下图显示了一个示例调用图,并说明了给定方法或函数的self time、children time和total time的概念。在“使用Top Down和Bottom Up检查跟踪”一节会进一步解释这些概念。

call_chart_1-2X.png

提示:要跳转到某个方法或函数的源代码,请右键点击该方法或函数,然后选择 Jump to Source。从任何跟踪数据窗格标签中均可执行此操作。

使用Flame Chart检查跟踪

Flame Chart选项提供一个反向调用图,用于聚合相同的调用堆栈。也就是说,将收集共享相同调用方序列的相同方法或函数,并将其表示为flame图表中的一个长条形图(而不是显示为调用图中的多个短条形图)。这样可以更容易地看到哪些方法或函数消耗的时间最多。但是,这也意味着水平轴并不代表时间线,而是指示每个方法或函数执行的相对时间量。

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

call_chart_2-2X.png

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

flame_chart_aggregation-2X.png

聚合调用用于创建flame图表,如下图所示。注意,对于flame图表中的任何给定调用,消耗最多CPU时间的被调用方都会首先出现。

flame_chart-2X.png

使用Top Down和Bottom Up检查跟踪

Top Down选项显示调用列表,其中展开方法或函数节点将显示其被调用方。图“Top Down”显示了图“Call Chart”中调用图的Top Down。图中的每个箭头都重调用者指向被调用者。

如图“Top Down”所示,在Top Down选项中展开方法A的节点将显示其被调用方B和D。之后,展开方法D的节点将显示其被调用方B和C等。与Flame chart选项类似,自顶向下的树为共享相同调用堆栈的相同方法的聚合信息。也就是说,Flame chart选项提供了Top down选项的图形表示。

Top Down选项提供以下信息,帮助描述每次调用所用的CPU时间(时间也表示为所选范围内线程总时间的百分比):

  • Self:方法或函数调用执行自己的代码而不是被调用方的代码所花费的时间,如图“Call Chart”中方法D所示。
  • Children:方法或函数调用执行其被调用方而不是其自身代码所花费的时间,如图“Call Chart”中方法D所示。
  • Total:方法的自身时间和子时间的总和。这表示应用程序执行调用所花费的总时间,如图“Call Chart”中方法D所示。

top_bottom_tree-2X.png

Bottom Up选项显示调用列表,其中展开函数或方法的节点将显示其调用方。根据图“Top Down”所示的示例跟踪,图“Bottom Up”为方法C提供了一个自底向上的树。在自底向上树中打开方法C的节点将显示其每个唯一的调用方B和D。请注意,尽管B调用了C两次,但在自底向上树中展开方法C的节点时,B只出现一次。之后,展开B的节点将显示其调用方A和D。

Bottom Up的选项对于按占用最多(或最少)CPU时间的方法或函数排序非常有用。您可以检查每个节点,以确定哪些调用方在调用这些方法或函数时花费的CPU时间最多。与自顶向下的树相比,自下而上的树中的每个方法或函数的计时信息都参考每个树顶部(顶部节点)的方法。CPU时间也表示为该记录期间线程总时间的百分比。下表有助于解释如何解释顶级节点及其调用方(子节点)的计时信息。

Self Children Total
位于自下而上树的顶部的方法或函数(顶节点) 表示方法或函数执行其自己的代码而不是其被调用者所花费的总时间。与自上而下的树相比,此计时信息表示在记录期间对该方法或函数的所有调用的总和。 表示方法或函数花费在执行其被调用方而不是其自身代码上的总时间。与自上而下的树相比,此计时信息表示在记录时间内对该方法或函数的所有调用的总和。 self time和children time的总和。
调用者 (子节点) 表示被调用者得到调用时花费的总时间。以图“Bottom Up”中的自底向上的树为例,方法B的自身时间等于每次执行B调用C时的自身时间之和。 表示被调用方的子级时间的总计。以图“Bottom Up”中的自底向上的树为例,方法B的子级时间等于每次执行B调用C时的子级时间之和 。 self time和children time的总和。
注意:对于给定的记录,当分析器达到文件大小限制时,Android Studio 会停止收集新数据(不过,不会停止记录)。在执行检测跟踪时,这种情况通常发生得更快,因为与采样跟踪相比,此类跟踪会在更短的时间内收集更多的数据。如果您将检查时间范围延长至达到限制后的记录期间,则跟踪数据窗格中的时间数据不会发生变化(因为没有新数据可用)。此外,当您仅选择没有数据可用的记录部分时,对于时间信息,跟踪数据窗格将显示 NaN

使用Trace Events检查系统跟踪

检查系统跟踪时,可以使用Trace Events选项查看每个线程上发生的事件的详细信息。要查看线程的详细信息,请在Threads窗格中选择该线程。这将在Kernel窗格中突出显示线程在每个CPU核心上的活动,并在Trace Events选项中显示线程的事件。将鼠标指针悬停在Trace Events选项中的事件上,可以查看事件的名称和在每个状态下花费的时间。

例如,下显示了在Threads窗格中选择的RenderThread,其活动在Kernel窗格中的CPU 0和CPU 1上突出显示,以及在Trace Events选项中的特定事件上花费的时间。

trace-events_2x.png

检查帧渲染数据

您可以检查应用程序渲染主线程上的每个帧所需的时间,并使用RenderThread来调查导致UI抖动和低帧速率的瓶颈。要查看帧渲染数据,请使用Trace System Calls配置记录跟踪。记录跟踪后,在名为FRAMESS的部分下查找有关每个帧的信息,如下图所示。

frame-rendering_2x.png

在应用程序生成跟踪日志

要在应用程序执行时进行方法跟踪,可以使用Debug类在应用程序中加入代码。通过这种方式,您可以更好地控制设备何时启动和停止记录跟踪信息。设备还可以使用指定的名称保存跟踪日志,以便以后可以轻松标识每个日志。然后可以使用Android Studio CPU Profiler查看每个跟踪日志。 在开始生成跟踪日志之前,请确保您的应用具有写入外部存储(WRITE_EXTERNAL_STORAGE)的权限,以便它可以将跟踪日志保存到设备。

在应用中加入跟踪指令

要创建跟踪日志,请在希望系统开始记录跟踪数据的位置调用startMethodTracing()。在调用中,可以指定.trace文件的名称,系统将其保存到目标设备上应用数据的特定目录中,也就是getExternalFilesDir()返回的目录,该目录位于大多数设备上的~/sdcard/中。trace文件包含二进制方法跟踪数据和带有线程和方法名称的映射表。要停止跟踪,请调用stopMethodTracing()。下面示例了开始并停止记录名为sample.trace的跟踪日志:

// Starts recording a trace log with the name you provide. For example, the
// following code tells the system to start recording a .trace file to the
// device with the name "sample.trace".
Debug.startMethodTracing("sample");
...
// The system begins buffering the generated trace data, until your
// application calls <code><a href="/reference/android/os/Debug.html#stopMethodTracing()">stopMethodTracing()</a></code>, at which time it writes
// the buffered data to the output file.
Debug.stopMethodTracing();

注意,如果您的应用程序在不更改跟踪日志名称的情况下再次调用StestMeMeTraceTrn(),它会覆盖保存到设备的现有日志。下节会介绍如何动态更改每个跟踪日志的名称。

如果系统在调用StestMeTraceTracle()之前达到最大缓冲区大小,系统将停止跟踪并向控制台发送通知。启动和停止跟踪的方法可以在整个应用程序过程中工作。也就是说,可以在Activity的onCreate(Bundle)方法中调用startMethodTracing(),在Activity的onDestroy()方法中调用stopMethodTracing()。请注意,启用配置文件时,应用程序的运行速度会更慢。也就是说,不应该使用分析数据来确定绝对时间(例如,“method foo()需要2.5秒才能运行”)。跟踪日志中的计时信息仅在与以前的跟踪日志进行比较时才有用,因此您可以查看最近的更改是否使应用程序更快或更慢。

当安装到运行Android5.0(API级别21)及更高版本的设备时,您可以使用sample-based profiling来跟踪,从而减小运行时对性能的影响。要启用sample profiling,请使用特定采样间隔的startMethodTracingSampling()(而不是调用startMethodTracing())。系统会定期收集样本,直到应用程序调用stopMethodTracing()。

保存多个日志

如果应用程序在不指定跟踪日志新名称的情况下多次启动和停止方法跟踪,则设备将用新的跟踪日志覆盖旧的跟踪日志,即只保留最新的跟踪日志。要将多个跟踪日志保存到设备,请在应用程序每次调用startMethodTracing()时动态重命名跟踪日志。下面的示例使用SimpleDateFormat类在每个跟踪日志名字中加入当前日期和时间:

// Uses the <code><a href="/reference/java/text/SimpleDateFormat.html">SimpleDateFormat</a></code> class to create a String with
// the current date and time.
SimpleDateFormat dateFormat =
        new SimpleDateFormat("dd_MM_yyyy_hh_mm_ss", Locale.getDefault());
String logDate = dateFormat.format(new Date());
// Applies the date and time to the name of the trace log.
Debug.startMethodTracing(
        "sample-" + logDate);

获取设备上的跟踪日志

系统在设备上创建跟踪日志后,可以通过以下方式之一访问该文件:

  • 使用Device File Explorer。要打开设备文件资源管理器,请单击“View > Tool Windows > Device File Explorer”(或单击工具窗口栏中的Device File Explorer按钮)。如下图所示,您可以通过应用程序的包特定目录来找到.trace文件。

locating_log_with_device_explorer-2X.png

  • 使用adb pull命令将文件复制到本地计算机。下面的命令将名为sample.trace的跟踪日志从设备复制到本地计算机的~/Documents/trace logs/目录。

    adb pull path-on-device/sample.trace ~/Documents/trace-logs/

参考文档:

Android Developers: cpu profiler


戈壁老王
143 声望64 粉丝

做为一个不称职的老年码农,一直疏忽整理笔记,开博记录一下,用来丰富老年生活,