End

性能优化 Systrace 系统跟踪

本文地址


目录

性能优化 Systrace 系统跟踪

官方文档概览

System tracing 就是记录短时间内的设备活动。System tracing 会生成跟踪文件,该文件可用于生成系统报告。此报告可帮助您了解如何最有效地提升应用或游戏的性能。

Android 平台提供了多种不同的跟踪信息获取途径:

  • Android Studio CPU profiler:在您与应用交互时,可实时检查应用的 CPU 使用率和线程活动信息,可详细了解应用正在执行哪些方法及其占用了多少 CPU 资源。
  • System Tracing app:用于将设备活动保存到跟踪文件的 Android 工具。在搭载 Android 10(API 29)或更高版本的设备上,跟踪文件会以 Perfetto 格式保存。在搭载较低版本 Android 系统的设备上,跟踪文件会以 Systrace 格式保存。
  • Systrace command-line tool:Systrace 是平台提供的旧版命令行工具,可记录短时间内的设备活动,并保存在压缩的文本文件中。该工具会生成一份报告,其中汇总了 Android 内核中的数据,例如 CPU 调度程序、磁盘活动和应用线程。
  • Perfetto command-line tool:Perfetto 是 Android 10 中引入的全新平台级跟踪工具。这是适用于 Android、Linux 和 Chrome 的更加通用和复杂的开源跟踪项目。与 Systrace 不同,它提供数据源超集,可让您以 protobuf 编码的二进制流形式记录任意长度的跟踪记录。您可以在 Perfetto 界面中打开这些跟踪记录。

Systrace 简介

Systrace 是 Android 4.1 中新增的性能数据采样和分析工具。它可帮助开发者收集 Android 关键子系统(如 SurfaceFlinger/SystemServer/Kernel/Input/Display 等 Framework 部分关键模块、服务,View 系统等)的运行信息,从而帮助开发者更直观的分析系统瓶颈,改进性能。

Systrace 允许你监视和跟踪 Android 系统的行为,它会告诉你系统都在哪些工作上花费时间、CPU周期都用在哪里,甚至你可以看到每个线程、进程在指定时间内都在干嘛。它同时还会突出观测到的问题,从垃圾回收渲染内容都可能是问题对象,甚至提供给你建议的解决方案。

Systrace 和 Perfetto 不会收集有关应用进程中代码执行情况的详细信息。如需详细了解您的应用正在执行哪些方法及其占用了多少 CPU 资源,请使用 Android Studio CPU profiler。

Systrace 的功能包括跟踪系统的 I/O 操作、内核工作队列、CPU 负载以及 Android 各个子系统的运行状况等。在 Android 平台中,它主要由3部分组成:

  • 内核部分:从本质上说,Systrace 是对 Linux Kernel 中 ftrace 的封装。所以,如果要使用 Systrace 的话,必须开启 kernel 中和 ftrace 相关的模块。
  • 数据采集部分:Android 定义了一个 Trace 类。应用程序可利用该类把统计信息输出给 ftrace。同时,Android 还有一个 atrace 程序(可通过 adb shell atrace 调用),它可以从 ftrace 中读取统计信息然后交给数据分析工具来处理。
  • 数据分析工具:Android 提供一个 systrace.py( python 脚本文件,位于 Android SDK目录 /platform-tools/systrace 中,其内部将调用 atrace 程序)用来配置数据采集的方式(如采集数据的标签、输出文件名等)和收集 ftrace 统计数据并生成一个结果网页文件供用户查看。

systrace.py 工具

Systrace 工具在 Android-SDK 目录下的 /platform-tools/systrace 里面。

Usage: systrace.py [options] [category1 [category2 ...]]
Example: systrace.py -b 32768 -t 15 gfx input view sched freq

注意,使用 systrace.py 时,用户名(C:\Users\...\)一定不要有中文,否则因为python2对中文支持的非常差,会报各种各样奇葩的错误!

环境配置

使用 systrace.py 必须先配置好 python 环境,且 systrace.py 只支持使用 python2.x 执行,如果本地已安装了 python3.x,可以百度一下怎么在电脑上 同时配置 两个 python 版本的环境。

可以在 Bash 中配置好对应的路径和 Alias,例如:

alias est='cd D:/_data/_androidsdk/platform-tools/systrace; explorer .'
alias pst='python2 systrace.py -a com.bqt.test'

使用步骤

  • 在代码中合适的位置添加Trace.beginSection("tag")Trace.endSection()
  • 手机连接好电脑,开启 adb 调试,准备好你要进行抓取的界面
  • 执行 python2 systrace.py 脚本开始抓取
  • 在手机上开始操作,不需要太长时间,一般 5 秒左右即可
  • 设定好的时间到了之后,会自动生成 trace.html 文件
  • 打开 Chrome,打开 chrome://tracing 页面
  • 点击 Load 将生成的 trace.html 文件加载后即可进行分析

常用参数

可以通过 python2 systrace.py -h 查看支持哪些参数。

可能需要先安装模块:pip2 install pypiwin32
也可以通过执行 adb shell atrace --help 查看支持哪些参数,但是两者结果不完全一致

  • -h 显示帮助信息-h–-help, Show the help message.
  • -o 指定输出文件名-o FILE,不指定时默认为trace.html
    • Write the HTML trace report to the specified file
  • -t 限制抓取的时间-t N–time=N,不指定时按 Enter 即可结束
    • Trace for N seconds. The default value is 5 seconds.
  • -a 指定包名-a A—-app=A,调试自定义的 Trace 标记时必须指定包名
    • enable app-level tracing, specified as a comma-separated(逗号分隔) list of package names
    • The apps must contain tracing instrumentation calls from the Trace class
    • * is a wildcard matching any process
  • -l 列出可用的类别-l--list-categories,查看支持的类别
    • List the available tracing category tags
    • 可能需要先安装模块:pip2 install six
    • 也可以通过执行 adb shell atrace --list_categories 查看支持的类别

自定义事件 -a

System tracing 仅在 system level 显示进程的相关信息,因此有时很难知道应用或游戏的哪些方法是在给定时间针对 system events 执行的。

Android 平台提供了一个 tracing API,可用于为特定的代码段添加标签。如果您捕获应用的 debug 版本的 system trace 跟踪并添加 -a 选项,这些自定义事件便会显示在 Systrace 报告中。

可用的类别 -l

如果您未指定任何类别,systrace 会生成包含所有可用类别的报告,可用类别取决于您所使用的已连接设备。

常用的类别有:gfx、input、view、webview、wm、am、pm、dalvik、power、res、webview、freq、disk、sync、memory、aidl、adb...

Trace 文件大小会影响其在 Chrome 中打开后的操作性能,在使用的时候可以根据自己的需求来进行选择和配置。
如果要在跟踪输出中查看任务名称,必须在命令参数中添加 sched 类别。

λ adb shell atrace --list_categories
         gfx - Graphics
       input - Input
        view - View System
     webview - WebView
          wm - Window Manager
          am - Activity Manager
          sm - Sync Manager
       audio - Audio
       video - Video
      camera - Camera
         hal - Hardware Modules
         res - Resource Loading
      dalvik - Dalvik VM
          rs - RenderScript
      bionic - Bionic C Library
       power - Power Management
          pm - Package Manager
          ss - System Server
    database - Database
     network - Network
         adb - ADB
    vibrator - Vibrator
        aidl - AIDL calls
       nnapi - NNAPI
         rro - Runtime Resource Overlay
         pdx - PDX services
       sched - CPU Scheduling
        freq - CPU Frequency
        idle - CPU Idle
        disk - Disk I/O
        sync - Synchronization
  memreclaim - Kernel Memory Reclaim
  binder_driver - Binder Kernel driver
  binder_lock - Binder global lock trace
      memory - Memory

常用快捷键

  • W/S 放大/缩小:,放大可以更好地看清局部细节,缩小以查看整体
  • A/D 左移/右移:左移/右移内容,可以看当前时间前后的信息
  • M 高亮:高亮选中当前鼠标点击的段,可以快速标识出这个方法的左右边界和执行时间

鼠标模式快捷切换: 主要是针对鼠标的工作模式进行切换

  • 数字键1:切换到 Selection 模式(默认),这个模式下鼠标可以点击某一个段查看其详细信息,配合 M 和 ASDW 可以做基本的操作
  • 数字键2:切换到 Pan 模式,这个模式下拖动鼠标可以左右拖动(建议)
  • 数字键3:切换到 Zoom 模式,这个模式下拖动鼠标可以放大和缩小
  • 数字键4:切换到 Timing 模式,这个模式下主要是用来衡量时间的,比如通过拖动选择一个起点和一个终点后, 可以查看起点和终点这中间的操作所花费的时间

Trace 类

Trace 类能够在生成的 trace 文件中,在跟踪的代码段执行对应时间轴区间,打上一个 tag 标记,从而跟踪应用的某些代码的行为。

如果多次调用 beginSection(),调用 endSection() 只会结束最后调用的 beginSection() 方法。因此,对于嵌套调用,请务必将每次对 beginSection() 的调用与一次对 endSection() 的调用正确匹配。此外,您不能在一个线程上调用 beginSection(),而在另一个线程上结束它,您必须在同一个线程上调用这两个方法。

Writes a trace message to indicate that a given section of code has begun. This call must be followed by a corresponding call to endSection() on the same thread.

//sectionName:The name of the code section to appear in the trace
public static void beginSection(@NonNull String sectionName) {
    if (isTagEnabled(TRACE_TAG_APP)) {
        if (sectionName.length() > MAX_SECTION_NAME_LEN) {
            throw new IllegalArgumentException("sectionName is too long, max is ");
        }
        nativeTraceBegin(TRACE_TAG_APP, sectionName);
    }
}

Writes a trace message to indicate that a given section of code has ended. This call must be preceeded by a corresponding call to beginSection(String). Calling this method will mark the end of the most recently begun section of code, so care must be taken to ensure that beginSection / endSection pairs are properly nested and called from the same thread.

public static void endSection() {
    if (isTagEnabled(TRACE_TAG_APP)) {
        nativeTraceEnd(TRACE_TAG_APP);
    }
}

使用技巧

  • 纵向信息记录的是调用关系(流程图)
  • 横向信息记录的是调用顺序(时序图)

线程状态

Systrace 会用不同的颜色来标识不同的线程状态,在每个方法上面都会有对应的线程状态来标识目前线程所处的状态。通过查看线程状态我们可以知道目前的瓶颈是什么,是 CPU 执行慢还是因为 Binder 调用,又或是进行 IO 操作,又或是拿不到 CPU 时间片。

线程状态主要有下面几个:

  • 绿色:正在运行,线程正在完成与某个进程相关的工作或正在响应中断
  • 蓝色:可运行,线程可以运行但目前未进行调度
  • 白色:休眠,线程没有可执行的任务,可能是因为线程在遇到斥锁定时被阻止
  • 橙色:不可中断的休眠,线程在遇到 I/O 操作时被阻止或正在等待磁盘操作完成
  • 紫色:可中断的休眠,线程在遇到另一项内核操作(通常是内存管理)时被阻止

注意:在 Systrace 报告中,您可以点击该线条以确定该线程在给定时间由哪个 CPU 控制。

绿色:运行中 Running

只有在该状态的进程才可能在CPU上运行。而同一时刻可能有多个进程处于可执行状态,这些进程的 task_struct 结构(进程控制块)被放入对应CPU的可执行队列中(一个进程最多只能出现在一个 CPU 的可执行队列中)。进程调度器的任务就是从各个 CPU 的可执行队列中分别选择一个进程在该 CPU 上运行。

作用:我们经常会查看 Running 状态的线程,查看其运行的时间,与竞品做对比,分析快或者慢的原因:

  • 是否频率不够?
  • 是否跑在了小核上?
  • 是否频繁在 Running 和 Runnable 之间切换?为什么?
  • 是否频繁在 Running 和 Sleep 之间切换?为什么?
  • 是否跑在了不该跑的核上面?比如不重要的线程占用了超大核

蓝色:可运行 Runnable

线程可以运行但当前没有安排,在等待 cpu 调度

作用:Runnable 状态的线程状态持续时间越长,则表示 cpu 的调度越忙,没有及时处理到这个任务:

  • 是否后台有太多的任务在跑?
  • 没有及时处理是因为频率太低?
  • 没有及时处理是因为被限制到某个 cpuset 里面,但是 cpu 很满?
  • 此时 Running 的任务是什么?为什么?

白色:休眠中 Sleeping

线程没有工作要做,可能是因为线程在互斥锁上被阻塞。

作用:一般是在等事件驱动

橙色:不可中断的睡眠态

线程在 I/O 上被阻塞或等待磁盘操作完成,一般底线都会标识出此时的 callsite :wait_on_page_locked_killable

作用:这个一般是标示 IO 操作慢,如果有大量的橘色不可中断的睡眠态出现,那么一般是由于进入了低内存状态,申请内存的时候触发 pageFault,linux 系统的 page cache 链表中有时会出现一些还没准备好的 page(即还没把磁盘中的内容完全地读出来) ,而正好此时用户在访问这个 page 时就会出现 wait_on_page_locked_killable 阻塞了。只有系统当 io 操作很繁忙时,每笔的 io 操作都需要等待排队时,极其容易出现且阻塞的时间往往会比较长。

紫色:可中断的睡眠态

线程在另一个内核操作(通常是内存管理)上被阻塞。

作用:一般是陷入了内核态,有些情况下是正常的,有些情况下是不正常的,需要按照具体的情况去分析

Linux 常见的进程状态

  • I 空闲 Idle
  • D 不可中断的休眠状态(ususally IO),Uninterruptible sleep
  • R 正在运行或在运行队列中等待 Runnable
  • S 处于休眠状态 Sleeping
  • T 停止或被追踪 Terminate
  • W 无驻留页,没有足够的记忆体分页可分配
  • X 死掉的进程
  • Z 僵死进程,进程已终止, 但进程描述符存在 Zombie
  • < 优先级高的进程
  • N 优先级较低的进程
  • L 内存锁页,有记忆体分页分配并缩在记忆体内 Lock
  • s 进程的领导者,在它之下有子进程
  • l 多进程的

可通过 adb shell ps 列出当前进程信息,其中S列(State)代表进程状态

进程唤醒信息分析

Systrace 会标识出一个非常有用的信息,可以帮助我们进行跨进程调用相关的分析。

一个进程被唤醒的信息往往比较重要,知道他被谁唤醒,那么我们也就知道了他们之间的调用等待关系,如果出现一段比较长的 sleep 情况,然后被唤醒,那么我们就可以去看是谁唤醒了这个线程,对应的就可以查看唤醒者的信息,看看为什么唤醒者这么晚才唤醒。

一个常见的情况是:应用进程使用 Binder 与 SystemServer 的 AMS 线程进行通信,但是恰好 AMS 的这个函数正在等待锁释放(或者这个函数本身执行时间很长),那么应用进程就需要等待比较长的时间,如果恰好是应用进程的主线程在进行等待,那么就会出现性能问题,比如响应慢或者卡顿,这就是为什么后台有大量的进程在运行,或者跑完 Monkey 之后,整机性能会下降的一个主要原因。

Systrace 可以标示出这个的一个原因是,一个任务在进入 Running 状态之前,会先进入 Runnable 状态进行等待,而 Systrace 会把这个状态也标示在 Systrace 上

拉到最上面查看对应的 cpu 上的 taks 信息,会标识这个 task 在被唤醒之前的状态:

信息区数据解析

线程时间片 Thread Timeslice

函数片 Slice

Counter Sample 采样

Async Slice

CPU Slice

User Expectation

位于整个 Systrace 最上面的部分,标识了 Rendering Response 和 Input Response

2020-12-14

posted @ 2020-12-14 18:14  白乾涛  阅读(2051)  评论(0编辑  收藏  举报