1.前言

1.1为什么要开ebpf这个坑?

      ebpf子系统是笔者最早于2022年开始接触的,时至今日,ebpf已经成为内核顶级的热门子系统。在去年召开的由OPPO主持的CLK大会上,ebpf也作为一个重要板块,由与会大牛做了分享。ebpf凭借其强大的特性,越发受到了各大厂商的追捧,但是之前对于ebpf的开发、学习工作,都没有做深入的总结,所以想将其作为2024年的一个目标,多多在ebpf子系统上积累一些,此外,也希望可以帮助到对ebpf感兴趣的朋友:D 。

ebpf子系统专题-01 profile/offcputime + flameGraphe 分析系统性能_性能分析

1.2什么是 eBPF ?

      eBPF 是一项革命性的技术,起源于 Linux 内核,它可以在特权上下文中(如操作系统内核)运行沙盒程序。它用于安全有效地扩展内核的功能,而无需通过更改内核源代码或加载内核模块的方式来实现。

ebpf子系统专题-01 profile/offcputime + flameGraphe 分析系统性能_性能分析_02

      eBPF 从根本上改变了这个方式。通过允许在操作系统中运行沙盒程序的方式,应用程序开发人员可以运行 eBPF 程序,以便在运行时向操作系统添加额外的功能。然后在 JIT 编译器和验证引擎的帮助下,操作系统确保它像本地编译的程序一样具备安全性和执行效率。这引发了一股基于 eBPF 的项目热潮,它们涵盖了广泛的用例,包括下一代网络实现、可观测性和安全功能等领域。

       如今,eBPF 被广泛用于驱动各种用例:在现代数据中心和云原生环境中提供高性能网络和负载均衡,以低开销提取细粒度的安全可观测性数据,帮助应用程序开发人员跟踪应用程序,为性能故障排查、预防性的安全策略执行(包括应用层和容器运行时)提供洞察,等等。可能性是无限的,eBPF 开启的创新才刚刚开始。

1.3特别说明

       ebpf的实现原理,网上其他博主介绍的也比较多,并且干巴巴地介绍其子系统的实现是苍白的,犹记得22年学习的其实现原理、技术细节大多都已经忘记了😓,所以在整个系列上,并不准备将其放在系列第一篇文章进行介绍,待时机合适时,笔者打算直接结合ebpf工源码和子系统内核实现,细细梳理整个子系统的实现原理,想来这样的效果应该最好。话不多说,干货走起!

2.profile && offcputime

2.1实验环境

      目前有如下几个主流的开发工具可以帮助开发和管理 eBPF 程序。它们对应满足用户的不同需求:

bcc

BCC 是一个框架,它允许用户编写 python 程序,并将 eBPF 程序嵌入其中。该框架主要用于应用程序和系统的分析/跟踪等场景,其中 eBPF 程序用于收集统计数据或生成事件,而用户空间中的对应程序收集数据并以易理解的形式展示。运行 python 程序将生成 eBPF 字节码并将其加载到内核中。

bpftrace

bpftrace 是一种用于 Linux eBPF 的高级跟踪语言,可在较新的 Linux 内核(4.x)中使用。bpftrace 使用 LLVM 作为后端,将脚本编译为 eBPF 字节码,并利用 BCC 与 Linux eBPF 子系统以及现有的 Linux 跟踪功能(内核动态跟踪(kprobes)、用户级动态跟踪(uprobes)和跟踪点)进行交互。bpftrace 语言的灵感来自于 awk、C 和之前的跟踪程序,如 DTrace 和 SystemTap。

eBPF Go 语言库

eBPF Go 语言库提供了一个通用的 eBPF 库,它将获取 eBPF 字节码的过程与 eBPF 程序的加载和管理进行了解耦。eBPF 程序通常是通过编写高级语言,然后使用 clang/LLVM 编译器编译成 eBPF 字节码来创建的。

libbpf C/C++ 库

libbpf 库是一个基于 C/ c++ 的通用 eBPF 库,它可以帮助解耦将 clang/LLVM 编译器生成的 eBPF 对象文件的加载到内核中的这个过程,并通过为应用程序提供易于使用的库 API 来抽象与 BPF 系统调用的交互。

      如上的几个项目,笔者考虑到自身的情况,一般是使用libbpf C/C++库实现ebpf程序开发的,并且BCC内也有一部分的ebpf工具是基于libbpf C/C++库实现的,所以后续ebpf系列相关的内容都是基于libbpf C/C++库。

     同时,ebpf工具的使用需要内核开启一些选项才能正常使用,并且内核版本越高,ebpf功能愈强大。在学习阶段,笔者是通过搭建ubuntu环境实现的,如果是嵌入式环境或者低版本的其他OS,则需要重新编译内核,可以参考如下文章加速!

笔者的实验环境(ubuntu-23.10太给力了,内核直升6.5.0 :D):

root@duncan:/# uname -a
Linux duncan 6.5.0-15-generic #15-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan  9 17:03:36 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

笔者的部分硬件信息:

Packages:
        0: Intel Core i7-6700
Microarchitectures:
        4x Sky Lake
Cores:
        0: 2 processors (0-1), Intel Sky Lake
        1: 2 processors (2-3), Intel Sky Lake
        2: 2 processors (4-5), Intel Sky Lake
        3: 2 processors (6-7), Intel Sky Lake
Logical processors (System ID):
        0 (0): APIC ID 0x00000000
        1 (4): APIC ID 0x00000001
        2 (1): APIC ID 0x00000002
        3 (5): APIC ID 0x00000003
        4 (2): APIC ID 0x00000004
        5 (6): APIC ID 0x00000005
        6 (3): APIC ID 0x00000006
        7 (7): APIC ID 0x00000007

推荐参考博客:

https://blog.csdn.net/weixin_43144516/article/details/118770335

Linux探测工具BCC(可观测性) - charlieroro - 博客园 (cnblogs.com)   

[译]用libbpf-bootstrap 构建 BPF 应用程序 - 知乎 (zhihu.com)

2.2火焰图

      说起火焰图,就不得不提到提到性能分析领域的大神brendan,同时他也是ebpf领域的权威。火焰图是其在研究MySQL性能问题时发明的调用栈分析方法,颠覆性地解决了以往分析OS性能问题时陷入调用栈分析的苦海,从全局角度展示了系统发生性能问题时所有调用栈信息以及各个调用栈对资源消耗的占比。

       本篇不是针对火焰图的专门科普,所以不会过多地介绍火焰图的原理、详细地使用方法以及如何看火焰图,如有需要,可以参考Brendan的官方博客,它是对火焰图最权威的介绍网站,链接放在下面了:

https://www.brendangregg.com/flamegraphs.html

2.3profile

      profile是一个定时采样调用栈信息并且汇报调用栈出现频率信息的BCC工具。这是BCC中了解CPU消耗的最有用的工具,因为它总结了消耗CPU资源的几乎所有代码路径。由于事件率固定为可以调整的采样率,因此它也可以以相对可忽略的开销使用。如下是官方对其的描述:

     This is a CPU profiler. It works by taking samples of stack traces at timed intervals. It will help you understand and quantify CPU usage: which code is executing, and by how much, including both user-level and kernel code.

By default this samples at 49 Hertz (samples per second), across all CPUs. This frequency can be tuned using a command line option. The reason for 49, and not 50, is to avoid lock-step sampling.

This is also an efficient profiler, as stack traces are frequency counted in kernel context, rather than passing each stack to user space for frequency counting there. Only the unique stacks and counts are passed to user space at the end of the profile, greatly reducing the kernel<->user transfer.

     profile的用法:

usage: profile.py [-h] [-p PID | -L TID] [-U | -K] [-F FREQUENCY | -c COUNT] [-d] [-a] [-I] [-f]
                  [--hash-storage-size HASH_STORAGE_SIZE] [--stack-storage-size STACK_STORAGE_SIZE] [-C CPU]
                  [--cgroupmap CGROUPMAP] [--mntnsmap MNTNSMAP]
                  [duration]

Profile CPU stack traces at a timed interval

positional arguments:
  duration              duration of trace, in seconds

options:
  -h, --help            show this help message and exit
  -p PID, --pid PID     profile process with one or more comma separated PIDs only
  -L TID, --tid TID     profile thread with one or more comma separated TIDs only
  -U, --user-stacks-only
                        show stacks from user space only (no kernel space stacks)
  -K, --kernel-stacks-only
                        show stacks from kernel space only (no user space stacks)
  -F FREQUENCY, --frequency FREQUENCY
                        sample frequency, Hertz
  -c COUNT, --count COUNT
                        sample period, number of events
  -d, --delimited       insert delimiter between kernel/user stacks
  -a, --annotations     add _[k] annotations to kernel frames
  -I, --include-idle    include CPU idle stacks
  -f, --folded          output folded format, one line per stack (for flame graphs)
  --hash-storage-size HASH_STORAGE_SIZE
                        the number of hash keys that can be stored and (default 40960)
  --stack-storage-size STACK_STORAGE_SIZE
                        the number of unique stack traces that can be stored and displayed (default 16384)
  -C CPU, --cpu CPU     cpu number to run profile on
  --cgroupmap CGROUPMAP
                        trace cgroups in this BPF map only
  --mntnsmap MNTNSMAP   trace mount namespaces in this BPF map only

examples:
    ./profile             # profile stack traces at 49 Hertz until Ctrl-C
    ./profile -F 99       # profile stack traces at 99 Hertz
    ./profile -c 1000000  # profile stack traces every 1 in a million events
    ./profile 5           # profile at 49 Hertz for 5 seconds only
    ./profile -f 5        # output in folded format for flame graphs
    ./profile -p 185      # only profile process with PID 185
    ./profile -L 185      # only profile thread with TID 185
    ./profile -U          # only show user space stacks (no kernel)
    ./profile -K          # only show kernel space stacks (no user)
    ./profile --cgroupmap mappath  # only trace cgroups in this BPF map
    ./profile --mntnsmap mappath   # only trace mount namespaces in the map

      在了解了这些内容后,就需要搭建环境来进行分析了。笔者这里用stress-ng来模拟系统带负载的情况,stress-ng的使用方式,网上介绍的资料也比较多,再此就不再赘述!

root@duncan:/home/duncan/share# stress-ng --cpu 8 --cpu-load 50 --vm 2 --vm-bytes 3G --vm-keep --io 4 --hdd 2 --hdd-bytes 1G
stress-ng: info:  [506741] defaulting to a 1 day, 0 secs run per stressor
stress-ng: info:  [506741] dispatching hogs: 8 cpu, 2 vm, 4 io, 2 hdd
stress-ng: info:  [506752] io: this is a legacy I/O sync stressor, consider using iomix instead

     在将stress-ng拉起来后,通过htop可以看到大量的stress-ng-*开头的task,大量消耗了CPU资源。

root@duncan:/home/duncan/share/FlameGraph# htop

    0[|||||||||||||||||||||||||||||||||||||||||||||||100.0%]   4[||||||||||||||||||||||||||||||||||||            67.6%]
    1[|||||||||||||||||||||||||||||||||||||||||||||||100.0%]   5[||||||||||||||||||||||||||||||||||||||||||      76.3%]
    2[|||||||||||||||||||||||||||||||||||||||||||     78.2%]   6[||||||||||||||||||||||||||||||||||||||||        72.8%]
    3[||||||||||||||||||||||||||||||||||||            65.9%]   7[|||||||||||||||||||||||||||||||||||||           68.1%]
  Mem[||||||||||||||||||||||||||||||||||||||||||4.03G/7.61G] Tasks: 123, 276 thr, 146 kthr; 7 running
  Swp[||                                        61.8M/4.00G] Load average: 8.80 10.31 10.11
                                                             Uptime: 14 days, 02:44:10

  [Main] [I/O]
    PID USER       PRI  NI  VIRT   RES   SHR S  CPU%▽MEM%   TIME+  Command
 509812 root        20   0 1782M 1730M  1280 R 102.8 22.2  0:46.76 stress-ng-vm [run]
 509813 root        20   0 1782M 1730M  1408 R 102.8 22.2  0:46.74 stress-ng-vm [run]
 509802 root        20   0 56104  6616  3712 S  52.8  0.1  0:23.51 stress-ng-cpu [run]
 509797 root        20   0 56104  6616  3712 R  52.2  0.1  0:23.50 stress-ng-cpu [run]
 509803 root        20   0 56104  6872  3840 S  52.2  0.1  0:23.51 stress-ng-cpu [run]
 509800 root        20   0 56104  6616  3712 S  51.7  0.1  0:23.50 stress-ng-cpu [run]
 509801 root        20   0 56104  6616  3712 R  51.7  0.1  0:23.49 stress-ng-cpu [run]
 509799 root        20   0 56104  6616  3712 R  51.1  0.1  0:23.49 stress-ng-cpu [run]
 509796 root        20   0 56104  6616  3712 S  50.5  0.1  0:23.49 stress-ng-cpu [run]
 509798 root        20   0 56104  6616  3712 R  50.5  0.1  0:23.48 stress-ng-cpu [run]
 509811 root        20   0 56104  2776  1536 D  19.7  0.0  0:05.20 stress-ng-hdd [run]
 509810 root        20   0 56104  2648  1408 D   6.4  0.0  0:04.92 stress-ng-hdd [run]
 510030 root        20   0 11392  4992  3584 R   2.9  0.1  0:00.19 htop
    791 avahi       20   0  8948  4480  3968 S   0.6  0.1 15:56.86 avahi-daemon: running [duncan.local]
 124909 root        20   0 19240  9244  6912 S   0.6  0.1  1:00.92 sshd: root@pts/0
      1 root        20   0  166M 13900  8524 S   0.0  0.2  0:44.59 /sbin/init splash
    271 root        19  -1 51292 28560 26772 S   0.0  0.4  0:11.00 /lib/systemd/systemd-journald
    316 root        20   0 29556  6996  3796 S   0.0  0.1  0:03.45 /lib/systemd/systemd-udevd
    747 systemd-oo  20   0 17240  6784  6016 S   0.0  0.1 36:33.20 /lib/systemd/systemd-oomd
    748 systemd-re  20   0 21968 12672  9984 S   0.0  0.2  8:18.27 /lib/systemd/systemd-resolved
    750 systemd-ti  20   0 90560  6912  6144 S   0.0  0.1  0:06.37 /lib/systemd/systemd-timesyncd
    759 systemd-ti  20   0 90560  6912  6144 S   0.0  0.1  0:00.00 /lib/systemd/systemd-timesyncd
    792 messagebus  20   0 11028  5888  4224 S   0.0  0.1  0:17.00 @dbus-daemon --system --address=systemd: --nofork --no
F1Help  F2Setup F3SearchF4FilterF5Tree  F6SortByF7Nice -F8Nice +F9Kill  F10Quit

        接下来开始抓取调用栈并且制作火焰图

root@duncan:/home/duncan/share/FlameGraph# python3 /home/duncan/share/bcc/tools/profile.py -adf 5 > out.profile01.foldstack.log
WARNING: 9 stack traces could not be displayed. Consider increasing --stack-storage-size.
root@duncan:/home/duncan/share/FlameGraph# ./flamegraph.pl --hash -bgcolor=blue --title="profile Flame Graph" < out.profile01.foldstack.log > profile01.svg

ebpf子系统专题-01 profile/offcputime + flameGraphe 分析系统性能_libbpf_03

        受限于51CTO博客的原因,完整的svg格式的文件是传输不了的,所以只能由笔者一部分一部分的截图展示。通过观察profile的火焰图倒数第二层,可以看到,cpu的消耗主要集中在:stress-ng-cpu、stress-ng-vm、stress-ng-hdd,由于编译优化或者OS的libc库还不支持帧指针等原因,可以看到一些symbols呈现unknow,不过通常情况下,可以通过火焰图上下层级的可呈现部分结合源码确认,相较于没有火焰图之前的分析过程,还是方便了许多的。

ebpf子系统专题-01 profile/offcputime + flameGraphe 分析系统性能_BBC_04

         接下来,打开堆栈比较完整的stree-ng-hdd,绿色箭头所指的灰色部分是内核态调用堆栈与用户态调用堆栈的分割线。黑色框所指的就是stress-ng-hdd下各调用栈所占的消耗占比,可以看到主要集中在write()这条调用线,次一级的显示unkonw,不过可以结合stress-ng-hdd的源码进行确认。

ebpf子系统专题-01 profile/offcputime + flameGraphe 分析系统性能_性能分析_05

2.4offcputime

       offcputime是BCC中用于统计线程阻塞和脱离cpu运行的时间,同时输出调用栈信息,以便理解阻塞原因。如下是官方说明:

This program shows stack traces and task names that were blocked and "off-CPU", and the total duration they were not running: their "off-CPU time". It works by tracing when threads block and when they return to CPU, measuring both the time they were off-CPU and the blocked stack trace and the task name. This data is summarized in the kernel using an eBPF map, and by summing the off-CPU time by unique stack trace and task name.

The output summary will help you identify reasons why threads were blocking, and quantify the time they were off-CPU. This spans all types of blocking activity: disk I/O, network I/O, locks, page faults, involuntary context switches, etc.

This is complementary to CPU profiling (e.g., CPU flame graphs) which shows the time spent on-CPU. This shows the time spent off-CPU, and the output, especially the -f format, can be used to generate an "off-CPU time flame graph".

See http://www.brendangregg.com/FlameGraphs/offcpuflamegraphs.html

This tool only works on Linux 4.6+. It uses the new `BPF_STACK_TRACE` table APIs to generate the in-kernel stack traces. For kernels older than 4.6, see the version under tools/old.

Note: this tool only traces off-CPU times that began and ended while tracing.

      offcpu的用法

Usage: offcputime [OPTION...]
Summarize off-CPU time by stack trace.

USAGE: offcputime [--help] [-p PID | -u | -k] [-m MIN-BLOCK-TIME] [-M
MAX-BLOCK-TIME] [--state] [--perf-max-stack-depth] [--stack-storage-size]
[duration]
EXAMPLES:
    offcputime             # trace off-CPU stack time until Ctrl-C
    offcputime 5           # trace for 5 seconds only
    offcputime -m 1000     # trace only events that last more than 1000 usec
    offcputime -M 10000    # trace only events that last less than 10000 usec
    offcputime -p 185      # only trace threads for PID 185
    offcputime -t 188      # only trace thread 188
    offcputime -u          # only trace user threads (no kernel)
    offcputime -k          # only trace kernel threads (no user)

  -k, --kernel-threads-only  Kernel threads only (no user threads)
  -m, --min-block-time=MIN-BLOCK-TIME
                             the amount of time in microseconds over which we
                             store traces (default 1)
  -M, --max-block-time=MAX-BLOCK-TIME
                             the amount of time in microseconds under which we
                             store traces (default U64_MAX)
      --perf-max-stack-depth=PERF-MAX-STACK-DEPTH
                             the limit for both kernel and user stack traces
                             (default 127)
  -p, --pid=PID              Trace this PID only
      --stack-storage-size=STACK-STORAGE-SIZE
                             the number of unique stack traces that can be
                             stored and displayed (default 1024)
      --state=STATE          filter on this thread state bitmask (eg, 2 ==
                             TASK_UNINTERRUPTIBLE) see include/linux/sched.h
  -t, --tid=TID              Trace this TID only
  -u, --user-threads-only    User threads only (no kernel threads)
  -v, --verbose              Verbose debug output
  -?, --help                 Give this help list
      --usage                Give a short usage message
  -V, --version              Print program version

Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.

Report bugs to https://github.com/iovisor/bcc/tree/master/libbpf-tools.

       介绍完了用法,接下来就来用offcputime来抓取在2.3章节profile同样负载情况下OS脱离cpu运行任务的情况,要特别说明的一点是,不同于profile工具已经提供了堆栈折叠功能,offcputime需要利用flamegraph提供的stackcollapse.pl进行折叠后再进行火焰图折叠。

root@duncan:/home/duncan/share/FlameGraph# offcputime -u 5 > out.offcputime01.log
failed to get syms
failed to get syms
    [Missed User Stack]
......
root@duncan:/home/duncan/share/FlameGraph# ./stackcollapse.pl out.offcputime01.log > out.offcputime01.foldstack.log
root@duncan:/home/duncan/share/FlameGraph# ./flamegraph.pl --hash -bgcolor=blue --title="off-CPU Time Flame Graph" < out.offcputime01.foldstack.log > offcputime01.svg

       先来看一下offcputime抓到的原始堆栈信息。因为篇幅原因,笔者直截取了部分的堆栈信息,每个调用栈首先会展示内核态函数,然后是用户态函数,之后依次是进程名字、PID,以及该调用栈出现的全部时间(单位是微秒)。此处关注第二个调用栈(它的脱离cpu时间是展示出来的堆栈中最长的),可以看到,任务stress-ng-cpu脱离cpu是由于调用了select造成的,属于主动阻塞,阻塞时间大约为54ms。

bpf_prog_48152dd096a9f163_sched_switch
    bpf_prog_48152dd096a9f163_sched_switch
    bpf_trace_run4
    __bpf_trace_sched_switch
    __schedule
    schedule
    exit_to_user_mode_loop
    exit_to_user_mode_prepare
    irqentry_exit_to_user_mode
    irqentry_exit
    sysvec_reschedule_ipi
    asm_sysvec_reschedule_ipi
    [unknown]
    [unknown]
    -                stress-ng-cpu (520804)
        31

    bpf_prog_48152dd096a9f163_sched_switch
    bpf_prog_48152dd096a9f163_sched_switch
    bpf_trace_run4
    __bpf_trace_sched_switch
    __schedule
    schedule
    schedule_hrtimeout_range_clock
    schedule_hrtimeout_range
    do_select
    core_sys_select
    do_pselect.constprop.0
    __x64_sys_pselect6
    do_syscall_64
    entry_SYSCALL_64_after_hwframe
    select
    [unknown]
    -                stress-ng-cpu (520797)
        53774

    bpf_prog_48152dd096a9f163_sched_switch
    bpf_prog_48152dd096a9f163_sched_switch
    bpf_trace_run4
    __bpf_trace_sched_switch
    __schedule
    schedule
    schedule_hrtimeout_range_clock
    schedule_hrtimeout_range
    do_select
    core_sys_select
    do_pselect.constprop.0
    __x64_sys_pselect6
    do_syscall_64
    entry_SYSCALL_64_after_hwframe
    select
    [unknown]
    -                stress-ng-cpu (520798)
        48866

    bpf_prog_48152dd096a9f163_sched_switch
    bpf_prog_48152dd096a9f163_sched_switch
    bpf_trace_run4
    __bpf_trace_sched_switch
    __schedule
    schedule
    schedule_hrtimeout_range_clock
    schedule_hrtimeout_range
    do_select
    core_sys_select
    do_pselect.constprop.0
    __x64_sys_pselect6
    do_syscall_64
    entry_SYSCALL_64_after_hwframe
    select
    [unknown]
    -                stress-ng-cpu (520802)
        48279
......
    bpf_prog_48152dd096a9f163_sched_switch
    bpf_prog_48152dd096a9f163_sched_switch
    bpf_trace_run4
    __bpf_trace_sched_switch
    __schedule
    schedule
    exit_to_user_mode_loop
    exit_to_user_mode_prepare
    irqentry_exit_to_user_mode
    irqentry_exit
    sysvec_apic_timer_interrupt
    asm_sysvec_apic_timer_interrupt
    [unknown]
    [unknown]
    -                stress-ng-cpu (520803)
        14439

    bpf_prog_48152dd096a9f163_sched_switch
    bpf_prog_48152dd096a9f163_sched_switch
    bpf_trace_run4
    __bpf_trace_sched_switch
    __schedule
    schedule
    exit_to_user_mode_loop
    exit_to_user_mode_prepare
    irqentry_exit_to_user_mode
    irqentry_exit
    sysvec_apic_timer_interrupt
    asm_sysvec_apic_timer_interrupt
    [unknown]
    -                stress-ng-cpu (520797)
        152

    bpf_prog_48152dd096a9f163_sched_switch
    bpf_prog_48152dd096a9f163_sched_switch
    bpf_trace_run4
    __bpf_trace_sched_switch
    __schedule
    schedule
    exit_to_user_mode_loop
    exit_to_user_mode_prepare
    irqentry_exit_to_user_mode
    irqentry_exit
    sysvec_reschedule_ipi
    asm_sysvec_reschedule_ipi
    [unknown]
    -                stress-ng-cpu (520798)
        9

       接下来看火焰图,可以看到有很多任务,基本没有占比特别大的,基本可以确认系统没有出现卡死或者异常任务,offcputime对于排查系统卡死等问题效果是非常好的。

ebpf子系统专题-01 profile/offcputime + flameGraphe 分析系统性能_libbpf_06

2.5总结

       在cpu问题分析过程中,offcputime可以用来分析任务为什么没有在cpu上运行,这正好是profile工具的对应面,这两个工具结合,可以覆盖任务的整个生命周期,再结合火焰图这个”神器“,对cpu方面的性能问题可谓是手到擒来。

3.尾言

      好了,本次对cpu方面的两大工具:profile/offcputime进行了介绍, 不过限于作者的水平,文章可能存在疏漏之处,希望发现的大神,可以不吝赐教,thanks:)