高级故障排除
高级故障排除方法用于系统级别。这些方法对于构建开发板和跨所有层级集成软件以生产最终产品至关重要。
启动时间
启动时间阶段和启动时间日志标记有助于调试和优化启动过程。
高通 Linux 启动链可分为两个阶段:
- 内核之前:启动引导加载程序并加载内核。
- Linux 系统初始化:初始化内核、驱动程序和用户空间服务。
第一阶段时间线(内核之前)
在设备启动序列中,收集串行日志。解析这些日志可以更好地理解这个阶段的里程碑。
可以使用下表中列出的相应时间戳来测量各个模块所花费的时间:
序号 | 模块 | 调试行打印 |
---|---|---|
1 | PBL + XBL | “UEFI Start” 时间戳 |
2 | Core UEFI | “UEFI Total” – 以毫秒为单位打印消耗的时间 |
3 | 内核加载 | “UEFI End” - “OS Loader” 时间戳之间的差值 |
有关如何收集串行日志的更多信息,请参见启动时间测量。
以下是示例串行日志和时间线的示例:
第二阶段时间线(Linux 系统初始化)
要捕获系统启动期间的性能统计信息,请使用 system-analyze 工具。
要安装 systemd-analyze 工具,请参见分析工具。
要分析内核内驱动程序的初始化,请在内核启动命令行中启用 initcall_debug 标志。systemd-analyze 工具可用于进一步分析用户空间服务和应用程序的初始化细节。
以下是使用 systemd-analyze 工具的示例命令:
-
运行以下命令以获得内核和用户空间启动时间:
systemd-analyze time
以下是命令的输出:
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
-
运行以下命令以获得启动过程中每个子系统所消耗的时间:
systemd-analyze blame
以下是命令的输出:
4.982s android-tools-adbd.service 3.013s dev-disk-by-partlabel-system.device 1.418s systemd-modules-load.service 1.179s sshdgenkeys.service
系统初始化时间的图形视图
systemd-analyze 命令提供了已启动系统服务及其初始化时间的图形化细分。
要获取系统服务的图形细分,请运行以下命令:
systemd-analyze plot > /var/lib/systemd-plot.svg
要在这个阶段可视化模块间的时间消耗并分析性能,请在任何网页浏览器中打开 systemd-plot.svg 文件。下图显示了示例图表:
验证 CPU 高负载用例
要验证任务是否在最强大的 CPU 上以其最大频率运行,捕获调度器和频率的 ftrace。
以下是一段使用 while 循环加载 CPU 的示例代码:
#include <stdlib.h>
#include <unistd.h>
int main() {
int i = 0;
while(1) {
i++;
}
return 0;
}
使用 Trace Compass 加载 ftrace。这使您可以检查测试线程是否以 2.7 GHz 的最大 CPU 频率在主核心上运行,如下图所示:
验证 I/O 密集用例
要获取 I/O 统计信息,请使用 /proc/diskstats
。
有关更多信息,请参见 kernel.org/doc/Documentation/ABI/testing/procfs-diskstats。
以下是针对 I/O 密集型用例运行 lmdd 的示例:
在执行用例之前,请运行以下命令:
cat /proc/diskstats
您应该看到类似以下的输出:
8 10 sda10 715 544 15056 250 4394 413 4199944 135729 0 5508 135979 0 0 0 0 0 0
接下来,从 vmstat 获取 Pgpgin 和 pgpgout:
cat /proc/vmstat
您应该看到类似以下的输出:
pgpgin 348632 pgpgout 2100056
-
要使 lmdd 在构建上工作,请编译 lmbench,见 编译性能工具。
对于 I/O 密集型用例,请运行以下 lmdd 命令:
lmdd if=/mnt/overlay/2GB.file of=/mnt/overlay/2GB.file.copy fsync=1 bs=1M
-
执行用例后,请运行以下命令:
cat /proc/diskstats
您应该看到类似以下的输出:
8 10 sda10 4822 544 4209448 13018 8530 451 8394624 300094 0 11836 313112 0 0 0 0 0 0
接下来,再次检查 Pgpgin 和 pgpgout:
cat /proc/vmstat
您应该看到类似以下的输出:
pgpgin 2446172 pgpgout 4197396
以下是 I/O 密集型用例的统计信息示例:
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
您也可以使用 Trace Compass 来检查任务状态是否主要是黄色,表示它被阻塞在 I/O 上,如下图所示:
有关 I/O 统计信息字段的更多信息,请参见 kernel.org/doc/Documentation/iostats.txt。
Vmstat
Vmstat 是一个用于收集关于块设备输入(bi)和块设备输出(bo)信息的 Linux 命令。下图显示了 vmstat 输出的示例:
有关 vmstat 的更多信息,请访问 Vmstat 文档。
使用大核心处理高负载用例
当一个高负载任务在 Silver 核心上运行并且运行时间较长时,它可能会影响性能。可以使用 sched_setaffinity() 将这些任务分配给更大的(Gold)核心。这有助于减少 CPU 运行时间并提高性能。
注意:
对节点所做的任何修改都可能影响设备的功耗和性能。在对节点进行任何修改之前,必须在所有相关用例中验证影响。
下图显示了在频率为 1.9 GHz 的 CPU0 上运行了 12.9 毫秒的线程测试的示例。
要使用 sched_setaffinity() 将任务分配给 Gold 核心,请参见 sched_setaffinity() 手册页。以下是将任务连接/分配给 Gold 核心 7 的示例代码:
#include <sched.h>
#include <unistd.h>
#include <sys/syscall.h>
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(cpu, &mask); // 这里的 `cpu` 应该替换为实际的核心编号
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() 属性后减少的时间: