> ## Documentation Index
> Fetch the complete documentation index at: https://dragonwingdocs-staging.qualcomm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 排查性能问题

要解决性能问题,您可以使用基本和高级两种故障排查方法。

## **基本故障排查**

基本故障排查涉及应用程序级别的基础技术。在使用 Qualcomm 开发套件进行教育和学术目的的应用程序开发时非常有用。基本故障排查可以应用于运行 Qualcomm<sup>®</sup> Linux<sup>®</sup> 且无需 root 访问权限的设备。

对于更复杂的问题,请参阅[高级故障排查](#advanced-troubleshooting)。

### **分析用户空间和内核跟踪**

Function tracer(ftrace)、Trace Compass 和 LTTng 等工具通常用于在 Linux 上分析跟踪以排查性能问题。

| **性能调试工具**    | **参考**                                                                                                                            |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| Trace Compass | [Trace Compass User Guide](https://archive.eclipse.org/tracecompass/doc/stable/org.eclipse.tracecompass.doc.user/User-Guide.html) |
| LTTng         | [LTTng Documentation](https://lttng.org/docs/v2.13/)                                                                              |

您可以使用 `-llttng-ust` 和 `-g -finstrument-functions` 编译应用程序,以显示函数调用堆栈。

例如,运行以下命令进行编译:

```text theme={null}
aarch64-qcom-linux-g++ <cpp source file> -o <output file> -llttng-ust -g -finstrument-functions
```

通过[编译性能工具](./get-started-with-performance-tuning-and-optimization#compile-performance-tools)启用后,设备上可以使用以下 GCC 和 G++ 编译器:

* `aarch64-qcom-linux-gcc`
* `aarch64-qcom-linux-g++`

### **捕获 LTTng-UST 跟踪**

要使用 LTTng 捕获跟踪,请按以下步骤操作:

1. 要使用 `liblttng-ust-cyg-profile.so` 显示应用程序的调用堆栈,请使用以下命令创建一个名为 my-session 的会话:
   ```text theme={null}
   lttng create my-session --output=/tmp/my-trace
   ```
   跟踪数据位于 `/tmp/my-trace`。
2. 按以下顺序运行命令以捕获跟踪:
   ```text theme={null}
   lttng enable-event -u -a
   ```
   ```text theme={null}
   lttng enable-event -k -a
   ```
   ```text theme={null}
   lttng start
   ```
3. 运行程序时预加载 `liblttng-ust-cyg-profile` 库:
   ```text theme={null}
   LD_PRELOAD=/usr/lib/liblttng-ust-cyg-profile.so ./test_executable
   ```
   ```text theme={null}
   lttng stop
   ```
   ```text theme={null}
   lttng destroy my-session
   ```

### **加载 LTTng 跟踪**

1. 要在 Trace Compass 中加载和可视化 LTTng 跟踪,请使用安全复制协议(SCP)或类似工具将跟踪从目标设备传输到主机。确保在命令中指定目标设备的 IP 地址。以下是示例命令:
   ```text theme={null}
   scp -r root@10.92.162.185:/home/root/lttng-traces/ <store trace path>
   ```
2. 在主机上使用 Trace Compass 加载 LTTng 内核和 UST 跟踪。在 Trace Compass 工具中,使用 **File** 菜单选项打开跟踪。

   <Note>屏幕截图仅供参考。截图中显示的目录结构可能会因 Trace Compass 工具版本而异。</Note>

   <div className="flex flex-col items-center gap-1">
     <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-1-trace-compass.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=34c35dcb6859fe7b345771bf804d3522" width="299" height="276" data-path="System/Performance/media/k2l-performance/fig-6-1-trace-compass.jpg" />
   </div>
3. 要选择跟踪类型,请右键单击跟踪,然后选择 **Select Trace Type** > **Ftrace Format** > **Raw Textual Ftrace**,如下图所示:

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-7.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=95d0cae86714a2278e617ebad5b426f4" width="901" height="375" data-path="System/Performance/media/k2l-performance/fig-6-7.jpg" />
</div>

4. 在 Trace Compass 中安装 ftrace 分析所需的插件。转到 **Menu** > **Tools** > **Add-ons**,然后选择 **Trace Compass ftrace**。**注意** 建议更新 Trace Compass 首选项。要打印与原始 ftrace 匹配的时间,请将 **Tracing–Time Format** 更改为 **TTT**(以 epoch 为单位的秒数)。
5. 要在一个视图中显示内核和 UST 跟踪,请创建 Experiments 并添加两个跟踪。

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-2.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=82a6e3914f2f7fb9eaa79fe46ee3a42a" width="515" height="742" data-path="System/Performance/media/k2l-performance/fig-6-2.jpg" />
</div>

6. 选择 **Views** > **LTTng-UST-CallStack** > **Flame Chart and Views** > **Linux Kernel** > **Resources**。Trace Compass 可以显示内核资源和用户空间应用程序函数调用堆栈,如下图所示:

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-3.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=6534933212294ee12ee669cb23a87d2f" width="1290" height="692" data-path="System/Performance/media/k2l-performance/fig-6-3.jpg" />
</div>

7. 按照步骤 6 打开 CPU 频率的跟踪。选择 **Resources** 面板和在指定 CPU 上运行的进程的 **Timeline** 视图。CPU 频率行中有一个频率数字。下图显示 CPU0 到 CPU2 以 2 GHz 运行,CPU3 到 CPU5 以 2.8 GHz 运行。

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-4.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=75013045a3d947fd9d94bcf53fa47a32" width="1270" height="470" data-path="System/Performance/media/k2l-performance/fig-6-4.jpg" />
</div>

### **监控用户空间应用程序的 CPU 消耗**

可以使用多个 Linux 实用工具(如 top 和 htop)来监控 CPU 使用率。

### **Top**

Top 是一个检查应用程序 CPU 使用率并显示整体 CPU 使用率的工具。在八核平台上,任务可以消耗 0% 到 800% 的 CPU。

要设置终端环境以运行 top,请在设备上运行以下命令:

```text theme={null}
export TERM=xterm
```

```text theme={null}
top
```

下图显示了作为该命令输出的 CPU 使用率:

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-5.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=c9a62fbf3a2c3382d61b9a8aff1f08b0" width="687" height="299" data-path="System/Performance/media/k2l-performance/fig-6-5.jpg" />
</div>

### **htop**

htop 显示每个进程的每核 CPU 使用率和整体 CPU 使用率。要在构建中编译 htop,请参阅[编译性能工具](./get-started-with-performance-tuning-and-optimization#compile-performance-tools)。

要为 htop 设置终端环境,请在设备上运行以下命令:

```text theme={null}
export TERM=xterm
```

```text theme={null}
htop
```

下图显示了作为该命令输出的每核 CPU 使用率:

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-6.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=978fdf62f5189051e5343d78749a7940" width="680" height="466" data-path="System/Performance/media/k2l-performance/fig-6-6.jpg" />
</div>

### **Trace Compass 中的 CPU 使用率**

1. 在主机上打开 Trace Compass 工具并加载跟踪。
2. 右键单击跟踪,然后选择 **Select Trace Type** > **Ftrace Format Type** > **Raw Textual Ftrace**,如下图所示:

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-7.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=95d0cae86714a2278e617ebad5b426f4" width="901" height="375" data-path="System/Performance/media/k2l-performance/fig-6-7.jpg" />
</div>

3. 右键单击 **Raw Textual Ftrace** 并选择 **Open**。
4. 双击 **CPU usage** 以查看系统级 CPU 使用率。在左侧面板中选择一个任务,以检查每个任务的 CPU 使用率,如下图所示:

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-8.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=d7c3a4829e98382066c3164121b13ce6" width="1055" height="274" data-path="System/Performance/media/k2l-performance/fig-6-8.jpg" />
</div>

### **监控用户空间应用程序的内存消耗**

您可以检查各个进程的内存分配和内存使用情况。

要检查某个进程的内存消耗,请在设备上运行以下命令:

```text theme={null}
cat /proc/<pid>/smaps_rollup
```

下图显示了该命令的输出:

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-9.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=ffdf8c028d9ab1298f03e0106d2d5e53" width="612" height="411" data-path="System/Performance/media/k2l-performance/fig-6-9.jpg" />
</div>

### **Procrank**

Procrank 是一个显示每个进程内存消耗的工具。默认情况下,它显示以下集合大小:

* VSS:虚拟集大小(Virtual set size)
* RSS:常驻集大小(Resident set size)
* PSS:比例集大小(Proportional set size)
* USS:独占集大小(Unique set size)

PSS 被视为进程的实际内存消耗。

### **从源代码构建 Procrank**

在主机上运行以下命令:

```text theme={null}
sudo apt install -y gcc-aarch64-linux-gnu
```

```text theme={null}
git clone https://github.com/cglmcu/procrank.git
```

```text theme={null}
cd procrank
```

```text theme={null}
export CC=aarch64-linux-gnu-gcc
```

```text theme={null}
aarch64-linux-gnu-gcc *.c -Os -o procrank -I.
```

ADB 已包含在 Qualcomm Linux 构建中。要启用 ADB,请执行以下操作:

1. 启动设备。
2. 登录串口 shell。
3. 运行以下命令:
   ```text theme={null}
   touch /etc/usb-debugging-enabled
   ```
4. 要启动 ADB,请使用以下选项之一:
   * 选项 1:重启设备。
   * 选项 2:运行以下命令:
     ```text theme={null}
     systemctl start android-tools-adbd
     ```

启用后,除非删除 `/etc/usb-debugging-enabled` 文件并重启设备,否则 ADB 将保持活动状态。

使用 Android Debug Bridge(adb)或类似工具将 Procrank 文件从主机传输到设备。以下是示例命令:

```text theme={null}
adb shell mount -o remount,rw /usr
```

```text theme={null}
adb push procrank /usr/bin
```

```text theme={null}
adb shell chmod a+x /usr/bin/procrank
```

<Note>
  确保在命令中指定目标设备的 IP 地址。
</Note>

Procrank 命令示例:

* 要查看每个进程分配的匿名内存,请在设备上运行以下命令:
  ```text theme={null}
  procrank -C
  ```
* 要显示每个进程分配的文件缓存内存,请在设备上运行以下命令:
  ```text theme={null}
  procrank -c
  ```
* 要查看每个进程分配的匿名内存和文件缓存内存,请在设备上运行以下命令:
  ```text theme={null}
  procrank
  ```

下图显示了 `procrank -C` 命令的示例输出:

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-10.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=2fed6495a7a16fad38ada5e656fbea41" width="796" height="698" data-path="System/Performance/media/k2l-performance/fig-6-10.jpg" />
</div>

### **检查应用程序的每周期指令数**

perf 实用工具使用硬件性能计数器计算应用程序的每周期指令数(IPC)。

要编译 perf 实用工具,请参阅[编译性能工具](./get-started-with-performance-tuning-and-optimization#compile-performance-tools)。

要计算 IPC,请在设备上运行以下命令:

```text theme={null}
perf stat -e cycles,instructions sleep 5
```

下图显示了该命令的示例输出:

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-check-instruction-per-cycle-one.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=9e68f5710cc2d7569a5df267eff76167" width="725" height="171" data-path="System/Performance/media/k2l-performance/fig-6-check-instruction-per-cycle-one.jpg" />
</div>

* 如果 IPC 小于 1.0,则很可能是内存停滞。在这种情况下,Qualcomm Linux 调优策略(如减少内存 I/O 工作负载)可以帮助提高性能。
* 如果 IPC 大于 1.0,则很可能是指令受限。在这种情况下,通过消除不必要的工作和缓存操作来减少代码执行,可以帮助提高性能。

### **检查消耗 CPU 最多的代码部分**

perf 实用工具可以生成火焰图,帮助可视化线程的堆栈和 CPU 使用情况,以及在 CPU 上运行的所有函数。

要生成火焰图,请执行以下操作:

* 在设备上:
  1. 收集日志以生成火焰图。要使用 perf 实用工具收集日志,请运行以下命令:
     ```text theme={null}
     perf record -g -o /tmp/perf.data -p <process pid> sleep 5
     ```
     ```text theme={null}
     cd /tmp
     ```
     ```text theme={null}
     perf script > /tmp/perf.script
     ```
  2. 使用 SCP 或类似工具运行以下命令,将 `perf.script` 从目标设备传输到主机。确保在命令中指定目标设备的 IP 地址。以下是示例命令:
     ```text theme={null}
     scp -r root@10.92.162.185:/tmp/perf.script /local/mnt/workspace/logs
     ```
* 在主机上:
  1. 运行以下命令下载火焰图:

     ```text theme={null}
     git clone https://github.com/brendangregg/FlameGraph.git
     ```

     确保在主机上安装了 Perl。
  2. 将 `perf.script` 复制到 `FlameGraph` 目录中:

     ```text theme={null}
     cd FlameGraph
     ```

     ```text theme={null}
     perl stackcollapse-perf.pl perf.script > out.folded
     ```

     ```text theme={null}
     perl flamegraph.pl out.folded > perf.svg
     ```
  3. 在浏览器中打开 SVG 文件,查看火焰图以了解 CPU 使用情况:

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-11.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=962827c19d8711c965cae11885a9f6a5" width="798" height="421" data-path="System/Performance/media/k2l-performance/fig-6-11.jpg" />
</div>

### **检查用户空间应用程序代码中各函数消耗的内存**

[Valgrind](https://valgrind.org/docs/manual/ms-manual.html) 是一个开源工具,提供了名为 massif 的实用程序,可帮助分析程序中每个函数消耗的内存。

以下是内存分配的示例代码:

```text theme={null}
    #include <stdlib.h>

void g(void) {
   malloc(4000);
}

void f(void) {
   malloc(2000);
   g();
}

int main(void) {
   int i;
   int* a[10];
   for (i = 0; i < 10; i++) {
      a[i] = malloc(1000);
   }
   f();
   g();
   for (i = 0; i < 10; i++) {
      free(a[i]);
   }
   return 0;
}
```

编译源代码并在设备上运行以下 Valgrind 命令:

```text theme={null}
valgrind --tool=massif ./test
```

以下是示例代码的输出:

```text theme={null}
cat massif.out.1587\
…\
n3: 20000 (heap allocation functions) malloc/new/new\[\], --alloc-fns, etc.\
n0: 10000 0x10882B: main (in /home/root/valgrind/test)\
n2: 8000 0x1087E7: g (in /home/root/valgrind/test)\
n1: 4000 0x108807: f (in /home/root/valgrind/test)\
n0: 4000 0x10885B: main (in /home/root/valgrind/test)\
n0: 4000 0x10885F: main (in /home/root/valgrind/test)\
n1: 2000 0x108803: f (in /home/root/valgrind/test)\
n0: 2000 0x10885B: main (in /home/root/valgrind/test)
```

有关 Valgrind 的更多信息,请参阅 [Valgrind User Manual](https://valgrind.org/docs/manual/ms-manual.html)。

### **检测用户空间应用程序中的内存泄漏**

要检测进程内的内存泄漏,可以使用启用了 leak-check 功能的 Valgrind 工具。

以下是已分配内存但未释放的示例代码:

```text theme={null}
    #include <stdlib.h>

void do_alloc() {
    int *x = malloc(10 * sizeof(int)); /* here simulate a leak */
    x[10] = 0; /* here write to invalid memory address */
}

int main() {
    do_alloc();
    return 0;
}
```

要检测内存泄漏,请编译示例代码并在设备上运行以下命令:

```text theme={null}
valgrind --leak-check=yes ./test
```

以下是示例代码的输出:

```text theme={null}
==1512== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==1512== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==1512== Command: ./test
==1512==
==1512== Invalid write of size 4
==1512==    at 0x1087B4: do_alloc (in /home/root/valgrind/test)
==1512==    by 0x1087CF: main (in /home/root/valgrind/test)
==1512==  Address 0x4a36068 is 0 bytes after a block of size 40 alloc'd
==1512==    at 0x486551C: malloc (vg_replace_malloc.c:381)
==1512==    by 0x1087A7: do_alloc (in /home/root/valgrind/test)
==1512==    by 0x1087CF: main (in /home/root/valgrind/test)
==1512==
==1512==
==1512== HEAP SUMMARY:
==1512==     in use at exit: 40 bytes in 1 blocks
==1512==   total heap usage: 1 allocs, 0 frees, 40 bytes allocated
==1512==
==1512== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==1512==    at 0x486551C: malloc (vg_replace_malloc.c:381)
==1512==    by 0x1087A7: do_alloc (in /home/root/valgrind/test)
==1512==    by 0x1087CF: main (in /home/root/valgrind/test)
```

## **高级故障排查**

高级故障排查方法用于系统级别。这些方法对于构建 Qualcomm 参考设备以及在所有层面集成 Qualcomm Linux 以生产最终产品至关重要。

有关相关信息,请参阅[基本故障排查](#basic-troubleshooting)。

### **启动时间**

启动时间的各个阶段和启动时间日志标记有助于调试和优化启动过程。

Qualcomm Linux 启动链可以分为两个阶段:

* 引导加载程序初始化和内核加载:启动引导加载程序并加载内核。
* Linux 系统初始化:初始化内核、驱动程序和用户空间服务。

### **第一阶段时间线(引导加载程序初始化和内核加载)**

在设备启动序列期间,收集串口日志。解析这些日志可以更好地了解此阶段的各个里程碑。

可以使用下表中列出的相应时间戳来测量各模块所耗费的时间:

| **模块**    | **打印的调试行**                       |
| --------- | -------------------------------- |
| PBL + XBL | "UEFI Start" 时间戳                 |
| Core UEFI | "UEFI Total" – 消耗的时间以毫秒为单位打印     |
| 内核加载      | "UEFI End" - OS Loader" 时间戳之间的差值 |

有关如何收集串口日志的更多信息,请参阅[测量启动时间](./performance-dashboards#measure-boot-time)。

以下是示例串口日志和时间线的示例:

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-boot-time-example.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=48aea4fd588cb184dcc7889d09a6af2a" width="649" height="337" data-path="System/Performance/media/k2l-performance/fig-6-boot-time-example.jpg" />
</div>

### **第二阶段时间线(Linux 系统初始化)**

要在系统启动期间捕获性能统计信息,请使用 [systemd-analyze 工具](https://www.freedesktop.org/software/systemd/man/latest/systemd-analyze.html)。

要安装该工具,请参阅[使用工具分析性能](./analyze-performance-with-tools)。

要分析内核中驱动程序的初始化,请在内核启动命令行中启用 `initcall_debug` 标志。使用 systemd-analyze 工具分析用户空间服务和应用程序的初始化详细信息。

以下是可在设备上运行以使用 systemd-analyze 工具的示例命令:

* 要获取内核和用户空间的启动时间,请运行以下命令:
  ```text theme={null}
  systemd-analyze time
  ```
  以下是该命令的输出:
  ```text theme={null}
  Linux QCS6490 (Linux 6.6.0 #1 SMP PREEMPT Sun Feb 4 18:35:47 UTC 2024) arm64. Startup finished in 4.238s (kernel) \+ 15.620s (userspace) = 19.859s multi-user.target reached after 15.594s in userspace
  ```
* 要获取启动期间每个子系统消耗的时间,请运行以下命令:
  ```text theme={null}
  systemd-analyze blame
  ```
  以下是该命令的输出:
  ```text theme={null}
  4.982s android-tools-adbd.service\
  3.013s dev-disk-byx2dpartlabel-system.device\
  1.418s systemd-modules-load.service\
  1.179s sshdgenkeys.service
  ```

### **系统初始化时间的图形视图**

`systemd-analyze plot` 命令提供已启动的系统服务及其初始化时间的图形化明细。

要获取系统服务的图形化明细,请在设备上运行以下命令:

```text theme={null}
systemd-analyze plot > /var/lib/systemd-plot.svg
```

要可视化系统初始化阶段各模块的时间消耗并分析性能,请在任意 Web 浏览器中打开 `systemd-plot.svg` 文件。下图显示了示例图表:

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-12.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=799c73f9644c9eefbbdb39ff63367673" width="1497" height="810" data-path="System/Performance/media/k2l-performance/fig-6-12.jpg" />
</div>

### **识别 CPU 受限的用例**

要验证任务是否在性能最强的 CPU 上以最大频率运行,请捕获调度器和频率 ftrace。

以下是使用 `while` 循环加载 CPU 的示例代码:

```text theme={null}
    #include <stdlib.h>
#include <unistd.h>

int main() {
     int i = 0;
     while(1)
    {
        i++;
    }
    return 0;
  }
```

您可以为示例代码收集 ftrace,并使用 Trace Compass 加载 ftrace。这可以让您检查测试线程是否在 Prime 核心上以 2.7 GHz 的最大 CPU 频率运行,如下图所示:

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-13.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=fc242acdc7f91075ba033cbe6cacae9a" width="1688" height="684" data-path="System/Performance/media/k2l-performance/fig-6-13.jpg" />
</div>

### **识别 I/O 受限的用例**

要获取 I/O 统计信息,请使用 `/proc/diskstats`。

有关更多信息,请参阅 [/proc/diskstats](https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats)。

以下是在设备上运行 lmdd 进行 I/O 受限用例的示例:

* 在运行用例之前,运行以下命令:
  ```text theme={null}
  cat /proc/diskstats
  ```
  以下是该命令的输出:
  ```text theme={null}
  8 10 sda10 715 544 15056 250 4394 413 4199944 135729 0 5508 135979 0 0 0 0 0 0 
  ```
  接下来,从 vmstat 获取 `pgpgin` 和 `pgpgout`:
  ```text theme={null}
  cat /proc/vmstat
  ```
  以下是该命令的输出:
  ```text theme={null}
  pgpgin 348632pgpgout 2100056
  ```
* 要运行 lmdd,必须先编译 lmbench,更多信息请参阅[编译性能工具](./get-started-with-performance-tuning-and-optimization#compile-performance-tools)。对于 I/O 受限用例,请运行以下 lmdd 命令:
  ```text theme={null}
  lmdd if=/mnt/overlay/2GB.file of=/mnt/overlay/2GB.file.copy fsync=1 bs=1M
  ```
* 运行用例后,运行以下命令:
  ```text theme={null}
  cat /proc/diskstats
  ```
  以下是该命令的输出:
  ```text theme={null}
  8 10 sda10 4822 544 4209448 13018 8530 451 8394624 300094 0 11836 313112 0 0 0 0 0 0
  ```
* 接下来,再次检查 `pgpgin` 和 `pgpgout`:
  ```text theme={null}
  cat /proc/vmstat
  ```
  以下是该命令的输出:
  ```text theme={null}
  pgpgin 2446172pgpgout 4197396
  ```

以下是 I/O 受限用例的统计信息示例:

```text theme={null}
Sectors read = (4209448 – 15056) = 4194392 sectors = 2GB
Time spent reading = (13018 – 250) = 12768 ms
Sectors written = (8394624 - 4199944) = 4194680 sectors = 2GB
Time spent writing = (300094 -135729) = 164365 ms
Time spend IO = (11836 – 5508) = 6328 ms

pgpgin gap = (2446172-348632) = 2GB
pgpgout gap = (4197396 – 2100056) = 2GB
```

有关更多信息,请参阅 [I/O statistics fields](https://www.kernel.org/doc/Documentation/iostats.txt)。

### **Vmstat**

`Vmstat` 是一个 Linux 命令,用于收集有关块输入(bi)和块输出(bo)的信息。下图显示了 vmstat 输出的示例:

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-14.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=153d9e4a9a20a062d12741dad3b4f29d" width="694" height="255" data-path="System/Performance/media/k2l-performance/fig-6-14.jpg" />
</div>

有关更多信息,请参阅 [Transparent Hugepage Support](https://www.kernel.org/doc/html/latest/admin-guide/mm/transhuge.html)。

### **将大核用于重负载用例**

当重负载任务在 Silver 核心上长时间运行时,可能会影响性能。使用 `sched_setaffinity()` 将此类任务绑定到更大的(Gold)核心。这种任务亲和性可以帮助减少 CPU 运行时间并提升性能。

<Warning>
  对节点所做的任何修改都可能影响设备的功耗和性能。在更改节点之前,务必在所有相关用例中验证其影响。
</Warning>

下图来自 Trace Compass,显示了一个线程测试在 CPU0 上以 1.9 GHz 频率运行 12.9 毫秒的示例。

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-15.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=b7b9226b7cf0fb865e1fb8a692fd56be" width="926" height="375" data-path="System/Performance/media/k2l-performance/fig-6-15.jpg" />
</div>

要使用 `sched_setaffinity()` 将任务亲和性设置到 Gold 核心,请参阅 [sched\_setaffinity(2) — Linux manual page](https://man7.org/linux/man-pages/man2/sched_setaffinity.2.html)。

以下是将任务绑定到 Gold 核心 7 的示例代码:

```text theme={null}
#include <sched.h>
#include <unistd.h>
#include <sys/syscall.h>
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(7, &mask);
pid_t tid = syscall(__NR_gettid);
int result = sched_setaffinity(tid, sizeof(mask), &mask);
```

使用 `sched_setaffinity()` 绑定任务后,该任务在 CPU7 上运行,运行时间从 12.9 毫秒减少到 2.9 毫秒,CPU 频率为 2.7 GHz。

下图显示了设置 `sched_setaffinity()` 属性后减少的时间:

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-16.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=9fb4210479cb3a7261a7d07562d1503c" width="948" height="296" data-path="System/Performance/media/k2l-performance/fig-6-16.jpg" />
</div>

### **减轻可运行状态对用例的影响**

当任务准备就绪但 CPU 不可用时,该任务被视为处于可运行(runnable)状态。当 CPU 处于重负载时,任务会被分配此状态。

要可视化线程的状态,可以使用 Trace Compass 的 **Control Flow** 视图。

下图以不同颜色显示线程状态:

* 深红色线表示线程处于可运行状态
* 黄色线表示睡眠状态
* 红色线表示 CPU 正忙于处理 `irq` 或 `softirq`

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/mitigate_impact_runnables.png?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=5a3fa0029618448cd6e2d0a13c4714bc" width="1416" height="316" data-path="System/Performance/media/k2l-performance/mitigate_impact_runnables.png" />
</div>

可运行状态的类型:

* 唤醒延迟可运行状态是指已准备就绪的任务从可运行状态转变为实际在 CPU 上运行所需的时间。可以通过调优调度器或禁用 CPU 的低功耗模式来减少这种延迟。
* 普通可运行状态发生在 CPU 选择运行更高优先级的进程而不是当前进程时。提高任务的优先级可以帮助减少可运行状态。

线程的优先级取决于其类型:

* 实时(RT)线程的优先级范围为 0 到 99,数字越大表示优先级越高。要更改实时线程优先级,请在 `sched_setscheduler()` 中使用 `SCHED_FIFO` 策略。
* 普通线程的优先级范围为 100 到 139,数字越小表示优先级越高。要更改普通线程优先级,请使用 `renice` Linux 命令以及带有 `SCHED_OTHER` 策略的 `sched_setscheduler()`。–20 到 +19 范围内的值映射到 100 到 139 范围内的线程优先级。

要通过更改线程优先级来减少可运行时间,请使用 `sched_setscheduler()`。

有关 `sched_setscheduler()`,请参阅 [sched\_setscheduler(2)—Linux manual page](https://man7.org/linux/man-pages/man2/sched_setscheduler.2.html)。

以下是使用 `sched_setscheduler()` 更改线程优先级以减少可运行时间的示例代码:

```text theme={null}
struct sched_param param = {0};
param.sched_priority = 1;
int ret=0;
ret = sched_setscheduler(0, SCHED_FIFO, &param);
```

第一个参数表示任务 ID。0 表示当前任务。第二个参数表示调度器策略。`SCHED_FIFO` 用于 RT 线程。`sched_priority` 等于 1。

```text theme={null}
0--> 99 ( RT class highest priority)
1 --> 99-1 --> 98
2 --> 99-2 --> 97
..
99 --> 99-99 --> 0 (RT least priority)
```

默认情况下,进程优先级为 120。它继承自 shell。可运行时间为 225 毫秒,运行时间为 267 毫秒。通过将进程优先级从 120 提高到 98(实时优先级),可运行持续时间减少到 2 毫秒以内。

### **加快 CPU 频率提升时间**

延迟切换到所需的更高 CPU 频率可能会影响性能。您可以调整 `sched_util_clamp_min` 调度器节点以加快 CPU 频率提升。

在 0 到 1024 范围内调整 `sched_util_clamp_min`。较高的值可以提升性能,但也可能增加功耗。

以下是测试线程在核心 4 上运行表现的示例:

* 当 `sched_util_clamp_min` 为 0 时,CPU 频率从 691 MHz 缓慢提升到 1.5 GHz,然后到 1.7 GHz。您可以通过在设备上运行以下命令来设置此值:
  ```text theme={null}
  echo 0 > /proc/sys/kernel/sched_util_clamp_min
  ```
  下图来自 Trace Compass,显示了 CPU 频率的提升过程:
  <div className="flex flex-col items-center gap-1">
    <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-18.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=ee654efc6c3e55d8df88b82a1697f9d6" width="1231" height="99" data-path="System/Performance/media/k2l-performance/fig-6-18.jpg" />
  </div>
* 当 `sched_util_clamp_min` 为 512 时,CPU 频率从 691 MHz 直接提升到 1.9 GHz。您可以通过在设备上运行以下命令来设置此值:
  ```text theme={null}
  echo 512 > /proc/sys/kernel/sched_util_clamp_min
  ```
  下图显示了 CPU 频率提升到 1.9 GHz:
  <div className="flex flex-col items-center gap-1">
    <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-19.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=a26be901a9eb99425d016d8f40b18173" width="1178" height="98" data-path="System/Performance/media/k2l-performance/fig-6-19.jpg" />
  </div>
* 当 `sched_util_clamp_min` 为 1024 时,CPU 频率从 691 MHz 直接提升到最大频率(FMAX)2.4 GHz。您可以通过在设备上运行以下命令来设置此值:
  ```text theme={null}
  echo 1024 > /proc/sys/kernel/sched_util_clamp_min
  ```
  下图显示了 CPU 频率从 691 MHz 直接提升到 FMAX 2.4 GHz:
  <div className="flex flex-col items-center gap-1">
    <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-20.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=933c8964d80f322c9e816c01fbeab493" width="947" height="102" data-path="System/Performance/media/k2l-performance/fig-6-20.jpg" />
  </div>

### **确定用例的缓存驻留情况**

perf 实用工具用于分析缓存未命中和缓存回填计数器统计信息。此分析有助于确定用例在特定缓存中的驻留情况,例如 L2、L3 和末级缓存控制器(LLCC)DDR 驻留。

有关如何编译 perf 实用工具的说明,请参阅[编译性能工具](./get-started-with-performance-tuning-and-optimization#compile-performance-tools)。

要检查目标设备可用的缓存事件,请在设备上运行以下命令:

```text theme={null}
perf list | grep cache
```

以下是获取缓存驻留情况的示例命令:

```text theme={null}
perf stat -e l1d_cache_lmiss_rd -e l1i_cache_lmiss -e l2d_cache_lmiss_rd -e l3d_cache_lmiss_rd -e ll_cache_miss_rd  sleep 5
```

CPU 路径中来自前一级缓存(L1 → L2 → L3 → LLCC → DDR)的缓存未命中计数器,表明用例在后一级缓存中的驻留情况。

以下示例代码提供缓存未命中计数器统计信息:

```text theme={null}
Performance counter stats for '5 duration':

           5797      l1d_cache_lmiss_rd
          26699      l1i_cache_lmiss
          16200      l2d_cache_lmiss_rd
           8634      l3d_cache_lmiss_rd
           9710      ll_cache_miss_rd

    5.004388332 seconds time elapsed

    0.001599000 seconds user
    0.000000000 seconds sys
```

### **识别锁争用**

锁争用发生在一个线程(thread\_1)尝试获取已被另一个线程(thread\_2)持有的 Mutex 锁时。

在这种情况下,thread\_1 进入睡眠模式,并在 thread\_2 释放 Mutex 锁时被唤醒。

要解决此问题,请转到 **Trace Compass** 并选择 **Select Previous State Change**,如下图所示:

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-21.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=952b7a372d726fc0fadea94c60147661" width="280" height="96" data-path="System/Performance/media/k2l-performance/fig-6-21.jpg" />
</div>

下图显示了线程 2991 唤醒线程 2993 的实例:

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-22.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=c38befc3b909b210f68e6db944cdfc23" width="983" height="378" data-path="System/Performance/media/k2l-performance/fig-6-22.jpg" />
</div>

### **确定抢占禁用的持续时间**

内核以抢占方式运行。这意味着任何内核进程都可以在任何时刻被暂停,为更高优先级的进程让路。因此,新任务可以在先前任务被抢占的同一临界区中开始运行。

以下过程概述了如何记录抢占被禁用的持续时间:

1. 在内核配置中,在源代码里启用 `CONFIG_IRQSOFF_TRACER` 和 `CONFIG_PREEMPT_TRACER`。
2. 要收集跟踪,请运行以下命令:

<Note>以下命令应在设备上运行。</Note>

```text theme={null}
echo preemptoff > /sys/kernel/tracing/current_tracer
```

```text theme={null}
echo 1 > /sys/kernel/tracing/tracing_on
```

```text theme={null}
cat /sys/kernel/tracing/trace
```

如图所示,每次禁用抢占时都会记录时间戳,标记代码中的起点和终点:

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-print-timestamp.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=a02382601aecf207e275e6b643b44da1" width="643" height="452" data-path="System/Performance/media/k2l-performance/fig-6-print-timestamp.jpg" />
</div>

有关 function tracer 的更多信息,请参阅 [ftrace - Function Tracer](https://www.kernel.org/doc/Documentation/trace/ftrace.txt)。

### **调试丢帧**

丢帧可能由各种子系统(如显示或摄像头)的延迟引起。例如,如果显示刷新率为 60 Hz,则每帧必须在 16.6 毫秒内完成。

下图显示了 `Weston` 和 `SDM_EventThread` 每 16.6 毫秒运行一次的跟踪。任何应用程序都必须周期性渲染,并在此 16.6 毫秒的时间范围内完成渲染。如果在此窗口到期之前未完成渲染,帧就会被丢弃。

<div className="flex flex-col items-center gap-1">
  <img src="https://mintcdn.com/qualcomm-staging/uWQLnBcS9sxlZhgB/System/Performance/media/k2l-performance/fig-6-23.jpg?fit=max&auto=format&n=uWQLnBcS9sxlZhgB&q=85&s=e6ad1887079e6d41e2d06804f629514c" width="896" height="174" data-path="System/Performance/media/k2l-performance/fig-6-23.jpg" />
</div>

### **识别内存抖动**

内存抖动发生在系统花费大量时间从 RAM 回收内存,然后又将相同内容重新加载回 RAM 时。

这可能发生在来自磁盘的文件缓存页面和来自 ZRAM 的匿名页面上,导致严重的性能下降。

内存抖动通常发生在可用内存不足以支撑当前用例(称为工作集,workingset)时。这会导致系统难以找到可回收的内存。

您可以通过 `/proc/vmstat` 中的以下信息识别内存抖动:

| **vmstat 节点**                                         | **描述**                                                                                                 |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `workingset_refault_anon`/`workingset_refault_file`   | 这些节点表示在回收后立即被再次请求的已回收页面数。这些数字越低越好。                                                                     |
| `workingset_activate_anon`/`workingset_activate_file` | 这些节点表示在回收后立即被激活的已回收页面数。这些数字越低越好。                                                                       |
| `pgpgin`/`pswpin`                                     | 这些节点表示从交换区读取并换回 RAM 内存的页面数。                                                                            |
| `pgpgout`/`pswpout`                                   | 这些节点表示作为回收的一部分写入交换区的页面数。如果 `pgpg*` 和 `pswp*` 与 `workingset_refaults` 同时增加,则表明存在内存抖动情况。                 |
| `pgsteal_kswapd`/`pgsteal_direct`                     | 这些节点表示系统回收的页面数。                                                                                        |
| `pgscan_kswapd`/`pgscan_direct`                       | 这些节点表示系统为寻找可回收内存而扫描的页面数。`pgsteal`/`pgscan` 的比率表示系统的回收效率。较高的值表示更好的系统性能,而较低的回收效率表示系统难以找到可回收内存,这是内存抖动的表现。 |

要识别内存抖动,请在设备上运行以下命令:

```text theme={null}
cat /proc/vmstat
```

vmstat 字段如下:

```text theme={null}
workingset_refault_anon 984111
workingset_refault_file 1838690
workingset_activate_anon 502428
workingset_activate_file 499034
pgpgin 17488312
pgpgout 3398036
pswpin 984141
pswpout 2101230
pgsteal_kswapd 3946686
pgsteal_direct 59226
pgscan_kswapd 4660928
pgscan_direct 73719
```

这些计数器随时间线性增加。

要检测内存抖动的模式,请定期从这些计数器收集数据。然后,在特定时间段内绘制这些数据以可视化模式。

## **后续步骤**

* [性能仪表板](./performance-dashboards)
