Linux监控&性能调优分析-perf(上)

1 简介

Linux perf一个轻量级命令行工具,用于剖析和监控Linux系统的CPU性能。该工具虽然简单,却能提供有助于分析CPU的丰富信息。该命令包含许多用于收集、跟踪和分析CPU事件数据的子命令。

1.1 安装perf

perf程序并没有预装在Linux系统中

# Ubuntu/Debian
$ sudo apt install linux-tools-$(uname -r) linux-tools-generic

# RHEL/CentOS
$ sudo yum install perf

# Fedora
$ sudo dnf install perf

# 验证
$ perf -v
perf version 5.15.149

1.2 允许普通用户使用perf

perf命令默认需要sudo权限。要允许普通用户使用perf,请执行以下操作:

$ sudo su -
# echo 0 > /proc/sys/kernel/perf_event_paranoid
# nano /etc/sysctl.conf # 添加如下内容
kernel.perf_event_paranoid = 0

Linux perf 命令语法

1.3 perf命令的语法如下

perf <options> subcommand <options/arguments>

perf 工具的工作原理与git类似。它是各种子命令和不同活动的接口。运行不带任何选项或参数的命令会显示可用的子命令列表。

  • perf stat
    该命令提供常见性能事件的总体统计数据,包括执行的指令和消耗的时钟周期。除默认测量事件外,还可通过选项选择其他事件。

  • perf record
    该命令将性能数据记录到 perf.data 文件中,随后可使用 perf report 命令对其进行分析。

  • perf report

该命令从 perf record 创建的 perf.data 文件中读取并显示性能数据。

  • perf list
    该命令列出特定机器上的可用事件。这些事件将根据系统的性能监控硬件和软件配置而有所不同。

  • perf top
    该命令执行与 top 工具类似的功能。它会实时生成并显示性能计数器配置文件。

  • perf trace
    该命令执行与 strace 工具类似的功能。它监控指定线程或进程使用的系统调用以及该应用程序接收的所有信号。

  • perf help
    该命令显示 perf 命令的完整列表。

要显示各个子命令的选项,请运行

perf <subcommand> -h

2 使用perf top实时分析CPU使用

2.1 perf top 的用途

perf top 命令用于实时系统剖析,功能与 top 工具类似。不过,top 工具通常显示某个进程或线程占用了多少 CPU 时间,而 perf top 则显示每个特定函数占用了多少 CPU 时间。在默认状态下,perf top 会告诉你用户空间和内核空间中所有 CPU 正在使用的函数。使用 perf top 需要 root 访问权限。

2.2 使用 perf top 分析 CPU 使用

# perf top
Samples: 8K of event 'cycles', 2000 Hz, Event count (approx.): 4579432780 lost: 0/0 drop: 0/0
Overhead  Shared Object       Symbol
   2.20%  [kernel]            [k] do_syscall_64
   2.17%  [kernel]            [k] module_get_kallsym
   1.49%  [kernel]            [k] copy_user_enhanced_fast_string
   1.37%  libpthread-2.29.so  [.] pthread_mutex_lock 1.31% [unknown] [.] 0000000000000000 1.07% [kernel] [k] psi_task_change 1.04% [kernel] [k] switch_mm_irqs_off 0.94% [kernel] [k] fget
   0.74%  [kernel]            [k] entry_SYSCALL_64
   0.69%  [kernel]            [k] syscall_return_via_sysret
   0.69%  libxul.so           [.] 0x000000000113f9b0
   0.67%  [kernel]            [k] kallsyms_expand_symbol.constprop.0
   0.65%  firefox             [.] moz_xmalloc
   0.65%  libpthread-2.29.so  [.] __pthread_mutex_unlock_usercnt
   0.60%  firefox             [.] free
   0.60%  libxul.so           [.] 0x000000000241d1cd
   0.60%  [kernel]            [k] do_sys_poll
   0.58%  [kernel]            [k] menu_select
   0.56%  [kernel]            [k] _raw_spin_lock_irqsave
   0.55%  perf                [.] 0x00000000002ae0f3

2.3 perf top 输出的解释

  • 开销(Overhead)"列

显示特定函数占用 CPU 的百分比。

  • 共享对象(Shared Object)列

显示使用该函数的程序或库的名称。

  • 符号(Symbol)列

显示函数名称或符号。在内核空间执行的函数用 [k] 标识,在用户空间执行的函数用 [.]标识。

2.4 为什么 perf 将某些函数名显示为原始函数地址

对于内核函数,perf 使用 /proc/kallsyms 文件中的信息将样本映射到相应的函数名或符号。但对于在用户空间执行的函数,由于二进制文件已被剥离,因此可能会显示原始函数地址。

在这种情况下,必须安装可执行文件的 debuginfo 包,或者,如果可执行文件是本地开发的应用程序,则必须在编译应用程序时打开调试信息(GCC 中的 -g 选项),以显示函数名或符号。

注意: 安装与可执行文件相关的调试信息后,无需重新运行 perf record 命令。只需重新运行 perf report 命令即可。

2.5 启用调试和源代码库

Linux 的标准安装不会启用调试和源代码库。这些资源库包含调试系统组件和衡量其性能所需的信息。

# subscription-manager repos --enable rhel-8-for-$(uname -i)-baseos-debug-rpms
# subscription-manager repos --enable rhel-8-for-$(uname -i)-baseos-source-rpms
# subscription-manager repos --enable rhel-8-for-$(uname -i)-appstream-debug-rpms
# subscription-manager repos --enable rhel-8-for-$(uname -i)-appstream-source-rpms

2.6 使用 GDB 获取应用程序或库的调试信息包

调试代码需要调试信息。对于通过软件包安装的代码,GNU 调试器(GDB)会自动识别缺失的调试信息,解析软件包名称,并提供如何获取软件包的具体建议。

启动连接到要调试的应用程序或库的 GDB。GDB 会自动识别缺失的调试信息,并建议运行一条命令。

$ gdb -q /bin/ls
Reading symbols from /bin/ls...Reading symbols from .gnu_debugdata for /usr/bin/ls...(no debugging symbols found)...done.
(no debugging symbols found)...done.
Missing separate debuginfos, use: dnf debuginfo-install coreutils-8.30-6.el8.x86_64
(gdb)
# dnf debuginfo-install coreutils-8.30-6.el8.x86_64

3 使用 perf stat 统计进程执行过程中的事件

3.1 perf stat 的用途

perf stat 命令执行指定的命令,对命令执行期间发生的硬件和软件事件进行持续计数,并生成这些计数的统计数据。如果不指定任何事件,perf stat 会统计一组常见的硬件和软件事件。

3.2 用 perf stat 统计事件

$ perf stat ls
Desktop  Documents  Downloads  Music  Pictures  Public  Templates  Videos

 Performance counter stats for 'ls':

              1.28 msec task-clock:u               #    0.165 CPUs utilized
                 0      context-switches:u         #    0.000 M/sec
                 0      cpu-migrations:u           #    0.000 K/sec
               104      page-faults:u              #    0.081 M/sec
         1,054,302      cycles:u                   #    0.823 GHz
         1,136,989      instructions:u             #    1.08  insn per cycle
           228,531      branches:u                 #  178.447 M/sec
            11,331      branch-misses:u            #    4.96% of all branches

       0.007754312 seconds time elapsed

       0.000000000 seconds user
       0.007717000 seconds sys

如上例所示,当 perf stat 在没有 root 访问权限的情况下运行时,事件名称后跟有 :u,表明这些事件仅在用户空间中计数。

要同时统计用户空间和内核空间的事件,运行 perf stat 时必须有 root 访问权限:

# perf stat ls
Desktop  Documents  Downloads  Music  Pictures  Public  Templates  Videos

 Performance counter stats for 'ls':

              3.09 msec task-clock                #    0.119 CPUs utilized
                18      context-switches          #    0.006 M/sec
                 3      cpu-migrations            #    0.969 K/sec
               108      page-faults               #    0.035 M/sec
         6,576,004      cycles                    #    2.125 GHz
         5,694,223      instructions              #    0.87  insn per cycle
         1,092,372      branches                  #  352.960 M/sec
            31,515      branch-misses             #    2.89% of all branches

       0.026020043 seconds time elapsed

       0.000000000 seconds user
       0.014061000 seconds sys

默认情况下,perf stat 以每线程模式运行。要改用 CPU 范围的事件计数,请在 perf stat 中加入 -a 选项。要计算 CPU 范围内的事件,需要 root 访问权限:

# perf stat -a ls

3.3 perf stat 输出的解释

perf stat 执行指定命令,在命令执行过程中统计事件发生次数,并在三列中显示统计结果:

  • 特定事件的发生次数
  • 被统计事件的名称
  • 如果有相关指标,最右边一列的哈希符号 (#) 后面会显示比率或百分比。

例如,在默认模式下运行时,perf stat 会同时计算周期和指令,因此会在最右边一列计算并显示每个周期的指令数。由于默认情况下这两种事件都被计算在内,因此您可以看到类似的分支错误占所有分支百分比的行为。

3.4 将 perf stat 附加到运行中的进程

您可以将 perf stat 附加到正在运行的进程。这将指示 perf stat 在执行命令期间只计算指定进程中发生的事件。

前提条件

已安装 perf 用户空间工具,如安装 perf 中所述。
操作步骤

将 perf stat 附加到正在运行的进程:

$ perf stat -p ID1,ID2 sleep seconds
上例通过使用 sleep 命令,以秒为单位统计 ID 为 ID1 和 ID2 的进程中的事件。

$ sudo perf stat -p 1913119,4240 sleep 3

 Performance counter stats for process id '1913119,4240':

             26.17 msec task-clock                #    0.009 CPUs utilized
                13      context-switches          #    0.497 K/sec
                 1      cpu-migrations            #    0.038 K/sec
                 1      page-faults               #    0.038 K/sec
        68,245,892      cycles                    #    2.607 GHz
        94,333,646      instructions              #    1.38  insn per cycle
   <not supported>      branches
           189,274      branch-misses

       3.001225574 seconds time elapsed

4 使用 perf 记录和分析性能曲线

4.1 perf record的目的

perf record 命令对性能数据进行采样,并将其存储在 perf.data 文件中,可以使用其他 perf 命令读取和可视化该文件。perf.data 在当前目录下生成,可以在以后访问。

如果您没有为 perf record 指定要记录的命令,它会一直记录,直到您按下 Ctrl+C 手动停止进程。你可以通过 -p 选项和一个或多个进程 ID,将 perf record 附加到特定进程。你可以在没有 root 访问权限的情况下运行 perf record,但这样做只能在用户空间采样性能数据。在默认模式下,perf record 使用 CPU 周期作为采样事件,并以启用继承模式的每线程模式运行。

4.2 在没有root访问权限的情况下记录性能配置文件

$ perf record command

将 command 替换为要采样数据的命令。如果不指定命令,perf record 将采样数据,直到您按下 Ctrl+C 手动停止。

4.3 使用 root 记录性能配置文件

perf record command

4.4 以per-CPU模式记录性能配置文件

在 per-CPU 模式下,您可以使用 perf record 同时采样和记录受监控 CPU 上所有线程在用户空间和内核空间的性能数据。默认情况下,per-CPU模式监控所有在线 CPU。

# perf record -a command# 所有CPU
$ perf record -C 127,1 # CPU1和127

4.5 使用 perf record 捕捉调用图数据

您可以配置 perf record 工具,使其记录哪个函数正在调用性能配置文件中的其他函数。这有助于在多个进程调用同一函数时识别瓶颈。

$ perf record --call-graph method command

method为展开堆栈的方法,有如下几种:

  • fp( frame pointe)

使用帧指针方法。根据编译器优化情况,例如使用 GCC 选项 --fomit-frame-pointer 生成的二进制文件,可能无法解开堆栈。

  • dwarf
    使用 DWARF 调用帧信息来展开堆栈。

  • lbr last branch record)

4.6 使用 perf 报告分析 perf.data

如果 perf.data 文件是以 root 访问权限创建的,则也需要以 root 访问权限运行 perf report。

# perf report
Samples: 2K of event 'cycles', Event count (approx.): 235462960
Overhead  Command          Shared Object                     Symbol
   2.36%  kswapd0          [kernel.kallsyms]                 [k] page_vma_mapped_walk
   2.13%  sssd_kcm         libc-2.28.so                      [.] memset_avx2_erms 2.13% perf [kernel.kallsyms] [k] smp_call_function_single 1.53% gnome-shell libc-2.28.so [.] strcmp_avx2
   1.17%  gnome-shell      libglib-2.0.so.0.5600.4           [.] g_hash_table_lookup
   0.93%  Xorg             libc-2.28.so                      [.] memmove_avx_unaligned_erms 0.89% gnome-shell libgobject-2.0.so.0.5600.4 [.] g_object_unref 0.87% kswapd0 [kernel.kallsyms] [k] page_referenced_one 0.86% gnome-shell libc-2.28.so [.] memmove_avx_unaligned_erms
   0.83%  Xorg             [kernel.kallsyms]                 [k] alloc_vmap_area
   0.63%  gnome-shell      libglib-2.0.so.0.5600.4           [.] g_slice_alloc
   0.53%  gnome-shell      libgirepository-1.0.so.1.0.0      [.] g_base_info_unref
   0.53%  gnome-shell      ld-2.28.so                        [.] _dl_find_dso_for_object
   0.49%  kswapd0          [kernel.kallsyms]                 [k] vma_interval_tree_iter_next
   0.48%  gnome-shell      libpthread-2.28.so                [.] pthread_getspecific 0.47% gnome-shell libgirepository-1.0.so.1.0.0 [.] 0x0000000000013b1d 0.45% gnome-shell libglib-2.0.so.0.5600.4 [.] g_slice_free1 0.45% gnome-shell libgobject-2.0.so.0.5600.4 [.] g_type_check_instance_is_fundamentally_a 0.44% gnome-shell libc-2.28.so [.] malloc 0.41% swapper [kernel.kallsyms] [k] apic_timer_interrupt 0.40% gnome-shell ld-2.28.so [.] _dl_lookup_symbol_x 0.39% kswapd0 [kernel.kallsyms] [k] raw_callee_save___pv_queued_spin_unlock
Additional resources

参考资料

4.7 解释 perf 报告输出

  • 开销(Overhead)"列

表示在该特定功能中采集的样本占总体样本的百分比。

  • 命令(Command)列

告诉你样本是从哪个进程中采集的。

  • 共享对象(Shared Object)列

显示样本来自 ELF 映像的名称(样本来自内核时使用 [kernel.kallsyms] 名称)。

  • 符号(Symbol)列

显示函数名称或符号。

在默认模式下,函数按降序排序,开销最大的函数显示在最前面。

4.8 生成可在不同设备上读取的 perf.data 文件

您可以使用 perf 工具将性能数据记录到 perf.data 文件中,以便在不同设备上进行分析。

# perf record -a --call-graph fp sleep seconds

此示例将生成整个系统的 perf.data 数据,时间为使用 sleep 命令后的几秒钟。它还将使用帧指针方法捕获调用图数据。

生成一个包含所记录数据的调试符号的归档文件:

# perf archive
Now please run:

$ tar xvf perf.data.tar.bz2 -C ~/.debug

wherever you need to run 'perf report' on.
# ls perf.data*
perf.data  perf.data.old  perf.data.tar.bz2

4.9 分析在不同设备上创建的 perf.data 文件

# mkdir -p ~/.debug
# tar xf perf.data.tar.bz2 -C ~/.debug
# perf report