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

基本故障排查

基本故障排查涉及应用程序级别的基础技术。在使用 Qualcomm 开发套件进行教育和学术目的的应用程序开发时非常有用。基本故障排查可以应用于运行 Qualcomm® Linux® 且无需 root 访问权限的设备。 对于更复杂的问题,请参阅高级故障排查。

分析用户空间和内核跟踪

Function tracer(ftrace)、Trace Compass 和 LTTng 等工具通常用于在 Linux 上分析跟踪以排查性能问题。 您可以使用 -llttng-ust 和 -g -finstrument-functions 编译应用程序,以显示函数调用堆栈。 例如,运行以下命令进行编译:
通过编译性能工具启用后,设备上可以使用以下 GCC 和 G++ 编译器:
  • aarch64-qcom-linux-gcc
  • aarch64-qcom-linux-g++

捕获 LTTng-UST 跟踪

要使用 LTTng 捕获跟踪,请按以下步骤操作:
  1. 要使用 liblttng-ust-cyg-profile.so 显示应用程序的调用堆栈,请使用以下命令创建一个名为 my-session 的会话:
    跟踪数据位于 /tmp/my-trace。
  2. 按以下顺序运行命令以捕获跟踪:
  3. 运行程序时预加载 liblttng-ust-cyg-profile 库:

加载 LTTng 跟踪

  1. 要在 Trace Compass 中加载和可视化 LTTng 跟踪,请使用安全复制协议(SCP)或类似工具将跟踪从目标设备传输到主机。确保在命令中指定目标设备的 IP 地址。以下是示例命令:
  2. 在主机上使用 Trace Compass 加载 LTTng 内核和 UST 跟踪。在 Trace Compass 工具中,使用 File 菜单选项打开跟踪。
    屏幕截图仅供参考。截图中显示的目录结构可能会因 Trace Compass 工具版本而异。
  3. 要选择跟踪类型,请右键单击跟踪,然后选择 Select Trace Type > Ftrace Format > Raw Textual Ftrace,如下图所示:
  1. 在 Trace Compass 中安装 ftrace 分析所需的插件。转到 Menu > Tools > Add-ons,然后选择 Trace Compass ftrace。注意 建议更新 Trace Compass 首选项。要打印与原始 ftrace 匹配的时间,请将 Tracing–Time Format 更改为 TTT(以 epoch 为单位的秒数)。
  2. 要在一个视图中显示内核和 UST 跟踪,请创建 Experiments 并添加两个跟踪。
  1. 选择 Views > LTTng-UST-CallStack > Flame Chart and Views > Linux Kernel > Resources。Trace Compass 可以显示内核资源和用户空间应用程序函数调用堆栈,如下图所示:
  1. 按照步骤 6 打开 CPU 频率的跟踪。选择 Resources 面板和在指定 CPU 上运行的进程的 Timeline 视图。CPU 频率行中有一个频率数字。下图显示 CPU0 到 CPU2 以 2 GHz 运行,CPU3 到 CPU5 以 2.8 GHz 运行。

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

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

Top

Top 是一个检查应用程序 CPU 使用率并显示整体 CPU 使用率的工具。在八核平台上,任务可以消耗 0% 到 800% 的 CPU。 要设置终端环境以运行 top,请在设备上运行以下命令:
下图显示了作为该命令输出的 CPU 使用率:

htop

htop 显示每个进程的每核 CPU 使用率和整体 CPU 使用率。要在构建中编译 htop,请参阅编译性能工具。 要为 htop 设置终端环境,请在设备上运行以下命令:
下图显示了作为该命令输出的每核 CPU 使用率:

Trace Compass 中的 CPU 使用率

  1. 在主机上打开 Trace Compass 工具并加载跟踪。
  2. 右键单击跟踪,然后选择 Select Trace Type > Ftrace Format Type > Raw Textual Ftrace,如下图所示:
  1. 右键单击 Raw Textual Ftrace 并选择 Open。
  2. 双击 CPU usage 以查看系统级 CPU 使用率。在左侧面板中选择一个任务,以检查每个任务的 CPU 使用率,如下图所示:

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

您可以检查各个进程的内存分配和内存使用情况。 要检查某个进程的内存消耗,请在设备上运行以下命令:
下图显示了该命令的输出:

Procrank

Procrank 是一个显示每个进程内存消耗的工具。默认情况下,它显示以下集合大小:
  • VSS:虚拟集大小(Virtual set size)
  • RSS:常驻集大小(Resident set size)
  • PSS:比例集大小(Proportional set size)
  • USS:独占集大小(Unique set size)
PSS 被视为进程的实际内存消耗。

从源代码构建 Procrank

在主机上运行以下命令:
ADB 已包含在 Qualcomm Linux 构建中。要启用 ADB,请执行以下操作:
  1. 启动设备。
  2. 登录串口 shell。
  3. 运行以下命令:
  4. 要启动 ADB,请使用以下选项之一:
    • 选项 1:重启设备。
    • 选项 2:运行以下命令:
启用后,除非删除 /etc/usb-debugging-enabled 文件并重启设备,否则 ADB 将保持活动状态。 使用 Android Debug Bridge(adb)或类似工具将 Procrank 文件从主机传输到设备。以下是示例命令:
确保在命令中指定目标设备的 IP 地址。
Procrank 命令示例:
  • 要查看每个进程分配的匿名内存,请在设备上运行以下命令:
  • 要显示每个进程分配的文件缓存内存,请在设备上运行以下命令:
  • 要查看每个进程分配的匿名内存和文件缓存内存,请在设备上运行以下命令:
下图显示了 procrank -C 命令的示例输出:

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

perf 实用工具使用硬件性能计数器计算应用程序的每周期指令数(IPC)。 要编译 perf 实用工具,请参阅编译性能工具。 要计算 IPC,请在设备上运行以下命令:
下图显示了该命令的示例输出:
  • 如果 IPC 小于 1.0,则很可能是内存停滞。在这种情况下,Qualcomm Linux 调优策略(如减少内存 I/O 工作负载)可以帮助提高性能。
  • 如果 IPC 大于 1.0,则很可能是指令受限。在这种情况下,通过消除不必要的工作和缓存操作来减少代码执行,可以帮助提高性能。

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

perf 实用工具可以生成火焰图,帮助可视化线程的堆栈和 CPU 使用情况,以及在 CPU 上运行的所有函数。 要生成火焰图,请执行以下操作:
  • 在设备上:
    1. 收集日志以生成火焰图。要使用 perf 实用工具收集日志,请运行以下命令:
    2. 使用 SCP 或类似工具运行以下命令,将 perf.script 从目标设备传输到主机。确保在命令中指定目标设备的 IP 地址。以下是示例命令:
  • 在主机上:
    1. 运行以下命令下载火焰图:
      确保在主机上安装了 Perl。
    2. 将 perf.script 复制到 FlameGraph 目录中:
    3. 在浏览器中打开 SVG 文件,查看火焰图以了解 CPU 使用情况:

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

Valgrind 是一个开源工具,提供了名为 massif 的实用程序,可帮助分析程序中每个函数消耗的内存。 以下是内存分配的示例代码:
编译源代码并在设备上运行以下 Valgrind 命令:
以下是示例代码的输出:
有关 Valgrind 的更多信息,请参阅 Valgrind User Manual。

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

要检测进程内的内存泄漏,可以使用启用了 leak-check 功能的 Valgrind 工具。 以下是已分配内存但未释放的示例代码:
要检测内存泄漏,请编译示例代码并在设备上运行以下命令:
以下是示例代码的输出:

高级故障排查

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

启动时间

启动时间的各个阶段和启动时间日志标记有助于调试和优化启动过程。 Qualcomm Linux 启动链可以分为两个阶段:
  • 引导加载程序初始化和内核加载:启动引导加载程序并加载内核。
  • Linux 系统初始化:初始化内核、驱动程序和用户空间服务。

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

在设备启动序列期间,收集串口日志。解析这些日志可以更好地了解此阶段的各个里程碑。 可以使用下表中列出的相应时间戳来测量各模块所耗费的时间: 有关如何收集串口日志的更多信息,请参阅测量启动时间。 以下是示例串口日志和时间线的示例:

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

要在系统启动期间捕获性能统计信息,请使用 systemd-analyze 工具。 要安装该工具,请参阅使用工具分析性能。 要分析内核中驱动程序的初始化,请在内核启动命令行中启用 initcall_debug 标志。使用 systemd-analyze 工具分析用户空间服务和应用程序的初始化详细信息。 以下是可在设备上运行以使用 systemd-analyze 工具的示例命令:
  • 要获取内核和用户空间的启动时间,请运行以下命令:
    以下是该命令的输出:
  • 要获取启动期间每个子系统消耗的时间,请运行以下命令:
    以下是该命令的输出:

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

systemd-analyze plot 命令提供已启动的系统服务及其初始化时间的图形化明细。 要获取系统服务的图形化明细,请在设备上运行以下命令:
要可视化系统初始化阶段各模块的时间消耗并分析性能,请在任意 Web 浏览器中打开 systemd-plot.svg 文件。下图显示了示例图表:

识别 CPU 受限的用例

要验证任务是否在性能最强的 CPU 上以最大频率运行,请捕获调度器和频率 ftrace。 以下是使用 while 循环加载 CPU 的示例代码:
您可以为示例代码收集 ftrace,并使用 Trace Compass 加载 ftrace。这可以让您检查测试线程是否在 Prime 核心上以 2.7 GHz 的最大 CPU 频率运行,如下图所示:

识别 I/O 受限的用例

要获取 I/O 统计信息,请使用 /proc/diskstats。 有关更多信息,请参阅 /proc/diskstats。 以下是在设备上运行 lmdd 进行 I/O 受限用例的示例:
  • 在运行用例之前,运行以下命令:
    以下是该命令的输出:
    接下来,从 vmstat 获取 pgpgin 和 pgpgout:
    以下是该命令的输出:
  • 要运行 lmdd,必须先编译 lmbench,更多信息请参阅编译性能工具。对于 I/O 受限用例,请运行以下 lmdd 命令:
  • 运行用例后,运行以下命令:
    以下是该命令的输出:
  • 接下来,再次检查 pgpgin 和 pgpgout:
    以下是该命令的输出:
以下是 I/O 受限用例的统计信息示例:
有关更多信息,请参阅 I/O statistics fields。

Vmstat

Vmstat 是一个 Linux 命令,用于收集有关块输入(bi)和块输出(bo)的信息。下图显示了 vmstat 输出的示例:
有关更多信息,请参阅 Transparent Hugepage Support。

将大核用于重负载用例

当重负载任务在 Silver 核心上长时间运行时,可能会影响性能。使用 sched_setaffinity() 将此类任务绑定到更大的(Gold)核心。这种任务亲和性可以帮助减少 CPU 运行时间并提升性能。
对节点所做的任何修改都可能影响设备的功耗和性能。在更改节点之前,务必在所有相关用例中验证其影响。
下图来自 Trace Compass,显示了一个线程测试在 CPU0 上以 1.9 GHz 频率运行 12.9 毫秒的示例。
要使用 sched_setaffinity() 将任务亲和性设置到 Gold 核心,请参阅 sched_setaffinity(2) — Linux manual page。 以下是将任务绑定到 Gold 核心 7 的示例代码:
使用 sched_setaffinity() 绑定任务后,该任务在 CPU7 上运行,运行时间从 12.9 毫秒减少到 2.9 毫秒,CPU 频率为 2.7 GHz。 下图显示了设置 sched_setaffinity() 属性后减少的时间:

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

当任务准备就绪但 CPU 不可用时,该任务被视为处于可运行(runnable)状态。当 CPU 处于重负载时,任务会被分配此状态。 要可视化线程的状态,可以使用 Trace Compass 的 Control Flow 视图。 下图以不同颜色显示线程状态:
  • 深红色线表示线程处于可运行状态
  • 黄色线表示睡眠状态
  • 红色线表示 CPU 正忙于处理 irq 或 softirq
可运行状态的类型:
  • 唤醒延迟可运行状态是指已准备就绪的任务从可运行状态转变为实际在 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。 以下是使用 sched_setscheduler() 更改线程优先级以减少可运行时间的示例代码:
第一个参数表示任务 ID。0 表示当前任务。第二个参数表示调度器策略。SCHED_FIFO 用于 RT 线程。sched_priority 等于 1。
默认情况下,进程优先级为 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。您可以通过在设备上运行以下命令来设置此值:
    下图来自 Trace Compass,显示了 CPU 频率的提升过程:
  • 当 sched_util_clamp_min 为 512 时,CPU 频率从 691 MHz 直接提升到 1.9 GHz。您可以通过在设备上运行以下命令来设置此值:
    下图显示了 CPU 频率提升到 1.9 GHz:
  • 当 sched_util_clamp_min 为 1024 时,CPU 频率从 691 MHz 直接提升到最大频率(FMAX)2.4 GHz。您可以通过在设备上运行以下命令来设置此值:
    下图显示了 CPU 频率从 691 MHz 直接提升到 FMAX 2.4 GHz:

确定用例的缓存驻留情况

perf 实用工具用于分析缓存未命中和缓存回填计数器统计信息。此分析有助于确定用例在特定缓存中的驻留情况,例如 L2、L3 和末级缓存控制器(LLCC)DDR 驻留。 有关如何编译 perf 实用工具的说明,请参阅编译性能工具。 要检查目标设备可用的缓存事件,请在设备上运行以下命令:
以下是获取缓存驻留情况的示例命令:
CPU 路径中来自前一级缓存(L1 → L2 → L3 → LLCC → DDR)的缓存未命中计数器,表明用例在后一级缓存中的驻留情况。 以下示例代码提供缓存未命中计数器统计信息:

识别锁争用

锁争用发生在一个线程(thread_1)尝试获取已被另一个线程(thread_2)持有的 Mutex 锁时。 在这种情况下,thread_1 进入睡眠模式,并在 thread_2 释放 Mutex 锁时被唤醒。 要解决此问题,请转到 Trace Compass 并选择 Select Previous State Change,如下图所示:
下图显示了线程 2991 唤醒线程 2993 的实例:

确定抢占禁用的持续时间

内核以抢占方式运行。这意味着任何内核进程都可以在任何时刻被暂停,为更高优先级的进程让路。因此,新任务可以在先前任务被抢占的同一临界区中开始运行。 以下过程概述了如何记录抢占被禁用的持续时间:
  1. 在内核配置中,在源代码里启用 CONFIG_IRQSOFF_TRACER 和 CONFIG_PREEMPT_TRACER。
  2. 要收集跟踪,请运行以下命令:
以下命令应在设备上运行。
如图所示,每次禁用抢占时都会记录时间戳,标记代码中的起点和终点:
有关 function tracer 的更多信息,请参阅 ftrace - Function Tracer。

调试丢帧

丢帧可能由各种子系统(如显示或摄像头)的延迟引起。例如,如果显示刷新率为 60 Hz,则每帧必须在 16.6 毫秒内完成。 下图显示了 Weston 和 SDM_EventThread 每 16.6 毫秒运行一次的跟踪。任何应用程序都必须周期性渲染,并在此 16.6 毫秒的时间范围内完成渲染。如果在此窗口到期之前未完成渲染,帧就会被丢弃。

识别内存抖动

内存抖动发生在系统花费大量时间从 RAM 回收内存,然后又将相同内容重新加载回 RAM 时。 这可能发生在来自磁盘的文件缓存页面和来自 ZRAM 的匿名页面上,导致严重的性能下降。 内存抖动通常发生在可用内存不足以支撑当前用例(称为工作集,workingset)时。这会导致系统难以找到可回收的内存。 您可以通过 /proc/vmstat 中的以下信息识别内存抖动: 要识别内存抖动,请在设备上运行以下命令:
vmstat 字段如下:
这些计数器随时间线性增加。 要检测内存抖动的模式,请定期从这些计数器收集数据。然后,在特定时间段内绘制这些数据以可视化模式。

后续步骤