1、背景
1.1 性能分析
系統級性能優化通常包括兩個階段:性能剖析(performance profiling)和代碼優化。性能剖析的目标是尋找性能瓶頸,查找引發性能問題的原因及熱點代碼。代碼優化的目标是針對具體性能問題而優化代碼或編譯選項,以改善軟體性能。一般在工作中比較關心的是性能瓶頸,特别是算法。
當在系統全功能啟動的時候,算法一般需要将裝置的性能用到極限,而在這個過程中不免出現各類性能上的瓶頸,此時需要分析自身的一些性能瓶頸在什麼地方就可以用到專門的性能分析工具perf。
1.2 術語和縮寫
Perf
perf是一款Linux性能分析工具。Linux性能計數器是一個新的基于核心的子系統,它提供一個性能分析架構,比如硬體(CPU、PMU(Performance Monitoring Unit))功能和軟體(軟體計數器、tracepoint)功能。通過perf,應用程式可以利用PMU、tracepoint和核心中的計數器來進行性能統計。它不但可以分析制定應用程式的性能問題(per thread),也可以用來分析核心的性能問題。
2、Perf工具概述
2.1 背景知識
2.1.1 tracepoints
tracepoints是散落在核心源碼中的一些hook,它們可以在特定的代碼被執行到時觸發,這一特定可以被各種trace/debug工具所使用。
perf将tracepoint産生的時間記錄下來,生成報告,通過分析這些報告,可以了解程式運作期間核心的各種細節,對性能症狀做出準确的診斷。
2.1.2 硬體特性之cache
記憶體讀寫是很快的,但是還是無法和處理器指令執行速度相比。為了從記憶體中讀取指令和資料,處理器需要等待,用處理器時間來衡量,這種等待非常漫長。cache是一種SRAM,讀寫速度非常快,能和處理器相比對。是以将常用的資料儲存在cache中,處理器便無需等待,進而提高性能。cache的尺寸一般都很小,充分利用cache是軟體調優非常重要部分
2.2 調優方向
Hardware Event由PMU部件産生,在特定的條件下探測性能事件是否發生以及發生的次數
Software Event是核心産生的事件,分布在各個功能子產品中,統計和作業系統相關性能事件。比如程序切換,ticks等。
Tracepoint Event是核心中靜态tracepoint所觸發的事件,這些tracepoint用來判斷程式運作期間核心的行為細節。比如slab配置設定器的配置設定次數等。
2.3 火焰圖
火焰圖(FlameGraph)是由Linux性能優化大師BrendanGregg發明的,和所有其他的trace和profiling方法不同的是,Flame Graph以一個全局的視野來看待時間分布,它從底部往頂部,列出所有可能的調用棧。其他的呈現方法,一般隻能列出單一的調用棧或者非階層化的時間分布。
以典型的分析CPU時間花費到哪個函數的on-cpu火焰圖為例來展開。CPU火焰圖中的每一個方框是一個函數,方框的長度,代表了它的執行時間,是以越寬的函數,執行越久。火焰圖的樓層每高一層,就是更深一級的函數被調用,最頂層的函數,是葉子函數。
3、Perf檢測原理
perf是利用PMU、tracepoint和核心中的計數器來進行性能統計
3.1 PMU

PerformanceMonitor Unit,性能監視單元,其為CPU提供的一個單元,屬于硬體的範疇。通過通路相關的寄存器能讀取到CPU的一些性能資料,目前大部分CPU都會提供相應的PMU。其包括各種core, offcore和uncore事件
Perf可以對程式進行函數級别的采樣,進而了解程式的性能瓶頸在哪裡。其基本原理是:每隔一個固定時間,就是CPU上産生一個中斷,看目前是哪個程序、哪個函數,然後給對應的程序和函數加一個統計值,這樣就知道CPU有多少時間在某個程序或某個函數上了。具體原作原理就是直接通過系統調用syscall/ioctl或者監聽SW的event來看性能。
3.2 在PC的ubuntu系統上支援perf
在Ubuntu系統中需要安裝以下2個package方可支援perf
sudo apt install linux-tools-common
以及相關的linux的核心工具
sudo apt install linux-tools-5.4.0-56-generic
此時就可以直接使用perf,用于先期的實驗。
4、嵌入式裝置編譯perf
一般核心中的perf工具均在核心根目錄下的tools目錄。
c r o s s c o m p i l e = a r m − l i n u x − g n x x x x x − m a k e C R O S S C O M P I L E = cross_compile = arm-linux-gnxxxxx- make CROSS_COMPILE= crosscompile=arm−linux−gnxxxxx−makeCROSSCOMPILE=cross_compile ARCH=arm tools/perf clean
make CROSS_COMPILE=$cross_compile ARCH=arm tools/perf
完成編譯後會産生相關的工具perf
注意的是要使用該工具必須要一些庫,主要有elfutils這個一般在交叉編譯器中可以找到 zlib也可以在交叉編譯中找到 libunwind
當然,必須在核心中打開perf的events功能:
5、perf的使用
在本文中我們介紹幾個最常用的指令,而更多的指令請到參考網址中的wiki官方網址上查閱
5.1 perf lsit
列出目前系統支援的所有性能事件。包括硬體性能事件、軟體性能事件以及檢查點
List of pre-defined events (to be used in -e):
branch-instructions OR branches [Hardware event]
branch-misses [Hardware event]
cache-misses [Hardware event]
cache-references [Hardware event]
cpu-cycles OR cycles [Hardware event]
instructions [Hardware event]
stalled-cycles-backend OR idle-cycles-backend [Hardware event]
stalled-cycles-frontend OR idle-cycles-fronten [Hardware event]
alignment-faults [Software event]
bpf-output [Software event]
context-switches OR cs [Software event]
cpu-clock [Software event]
cpu-migrations OR migrations [Software event]
dummy [Software event]
emulation-faults [Software event]
major-faults [Software event]
minor-faults [Software event]
page-faults OR faults [Software event]
task-clock [Software event]
L1-dcache-load-misses [Hardware cache event]
L1-dcache-loads [Hardware cache event]
L1-dcache-store-misses [Hardware cache event]
L1-dcache-stores [Hardware cache event]
L1-icache-load-misses [Hardware cache event]
branch-load-misses [Hardware cache event]
branch-loads [Hardware cache event]
dTLB-load-misses [Hardware cache event]
dTLB-store-misses [Hardware cache event]
iTLB-load-misses [Hardware cache event]
armv7_cortex_a9/br_immed_retired/ [Kernel PMU event]
armv7_cortex_a9/br_mis_pred/ [Kernel PMU event]
armv7_cortex_a9/br_pred/ [Kernel PMU event]
armv7_cortex_a9/br_return_retired/ [Kernel PMU event]
armv7_cortex_a9/cid_write_retired/ [Kernel PMU event]
armv7_cortex_a9/cpu_cycles/ [Kernel PMU event]
armv7_cortex_a9/exc_return/ [Kernel PMU event]
armv7_cortex_a9/exc_taken/ [Kernel PMU event]
armv7_cortex_a9/inst_retired/ [Kernel PMU event]
armv7_cortex_a9/l1d_cache/ [Kernel PMU event]
armv7_cortex_a9/l1d_cache_refill/ [Kernel PMU event]
armv7_cortex_a9/l1d_tlb_refill/ [Kernel PMU event]
armv7_cortex_a9/l1i_cache_refill/ [Kernel PMU event]
armv7_cortex_a9/l1i_tlb_refill/ [Kernel PMU event]
armv7_cortex_a9/ld_retired/ [Kernel PMU event]
armv7_cortex_a9/pc_write_retired/ [Kernel PMU event]
armv7_cortex_a9/st_retired/ [Kernel PMU event]
armv7_cortex_a9/sw_incr/ [Kernel PMU event]
armv7_cortex_a9/unaligned_ldst_retired/ [Kernel PMU event]
rNNN [Raw hardware event descriptor]
cpu/t1=v1[,t2=v2,t3 ...]/modifier [Raw hardware event descriptor]
解釋:
HW event 這個是與硬體相關的事件
SW event 這個是跟核心相關的事件
HW cache event 這個是跟cache相關的
5.2 perfbench all
perf中内置的benchmark,主要是對系統性能進行摸底,目前包括兩套針對排程器和記憶體管理子系統的benchmark。
# Executed 1000000 pipe operations between two processes
Total time: 41.530 [sec]
41.530077 usecs/op
24078 ops/sec
# Running mem/memcpy benchmark...
# function 'default' (Default memcpy() provided by glibc)
# Copying 1MB bytes ...
1.511707 GB/sec
# Running mem/memset benchmark...
# function 'default' (Default memset() provided by glibc)
# Copying 1MB bytes ...
1.603551 GB/sec
# Running futex/hash benchmark...
Run summary [PID 1217]: 2 threads, each operating on 1024 [private] futexes for 10 secs.
Averaged 635903 operations/sec (+- 4.24%), total secs = 10
# Running futex/wake benchmark...
Run summary [PID 1217]: blocking on 0 threads (at [private] futex 0x222574), waking up 1 at a time.
[Run 1]: Wokeup 0 of 0 threads in 0.0010 ms
[Run 2]: Wokeup 0 of 0 threads in 0.0020 ms
[Run 3]: Wokeup 0 of 0 threads in 0.0010 ms
[Run 4]: Wokeup 0 of 0 threads in 0.0020 ms
[Run 5]: Wokeup 0 of 0 threads in 0.0020 ms
[Run 6]: Wokeup 0 of 0 threads in 0.0010 ms
[Run 7]: Wokeup 0 of 0 threads in 0.0010 ms
[Run 8]: Wokeup 0 of 0 threads in 0.0020 ms
[Run 9]: Wokeup 0 of 0 threads in 0.0020 ms
[Run 10]: Wokeup 0 of 0 threads in 0.0010 ms
Wokeup 0 of 0 threads in 0.0015 ms (+-11.11%)
# Running futex/wake-parallel benchmark...
Run summary [PID 1217]: blocking on 2 threads (at [private] futex 0x22265c), 2 threads waking up 1 at a time.
[Run 1]: Avg per-thread latency (waking 1/2 threads) in 0.0105 ms (+-14.29%)
[Run 2]: Avg per-thread latency (waking 1/2 threads) in 0.1815 ms (+-95.04%)
[Run 3]: Avg per-thread latency (waking 1/2 threads) in 0.0075 ms (+-6.67%)
[Run 4]: Avg per-thread latency (waking 1/2 threads) in 0.2210 ms (+-8.60%)
[Run 5]: Avg per-thread latency (waking 1/2 threads) in 0.0090 ms (+-11.11%)
[Run 6]: Avg per-thread latency (waking 1/2 threads) in 0.0755 ms (+-89.40%)
[Run 7]: Avg per-thread latency (waking 1/2 threads) in 0.0080 ms (+-12.50%)
[Run 8]: Avg per-thread latency (waking 1/2 threads) in 0.0870 ms (+-87.36%)
[Run 9]: Avg per-thread latency (waking 1/2 threads) in 0.1920 ms (+-95.31%)
[Run 10]: Avg per-thread latency (waking 1/2 threads) in 0.0090 ms (+-22.22%)
Avg per-thread latency (waking 1/2 threads) in 0.0801 ms (+-34.14%)
# Running futex/requeue benchmark...
Run summary [PID 1217]: Requeuing 2 threads (from [private] 0x222764 to 0x222774), 1 at a time.
[Run 1]: Requeued 2 of 2 threads in 0.0130 ms
[Run 2]: Requeued 2 of 2 threads in 0.0150 ms
[Run 3]: Requeued 2 of 2 threads in 0.0150 ms
[Run 4]: Requeued 2 of 2 threads in 0.0130 ms
[Run 5]: Requeued 2 of 2 threads in 0.0130 ms
[Run 6]: Requeued 2 of 2 threads in 0.0130 ms
[Run 7]: Requeued 2 of 2 threads in 0.0130 ms
[Run 8]: Requeued 2 of 2 threads in 0.0120 ms
[Run 9]: Requeued 2 of 2 threads in 0.0140 ms
[Run 10]: Requeued 2 of 2 threads in 0.0140 ms
Requeued 2 of 2 threads in 0.0135 ms (+-2.28%)
# Running futex/lock-pi benchmark...
Run summary [PID 1217]: 2 threads doing pi lock/unlock pairing for 10 secs.
Averaged 73 operations/sec (+- 0.00%), total secs = 10
5.3 perf top
可以實時檢視目前系統程序函數占用率情況
PerfTop: 43 irqs/sec kernel:100.0% exact: 0.0% [4000Hz cpu-clock:pppH], (all, 2 CPUs)
-------------------------------------------------------------------------------
95.50% [kernel] [k] arch_cpu_idle
1.29% [kernel] [k] read_current_ti
0.58% [kernel] [k] gt_read_long
0.58% [kernel] [k] _raw_spin_unloc
0.48% [kernel] [k] console_unlock
0.45% [kernel] [k] __timer_delay
0.25% [kernel] [k] _raw_spin_unlock_irqres
0.09% [kernel] [k] do_raw_spin_lock
0.09% doris (deleted) pa:14004000~14725000 [.] 0x000cce08
0.07% [kernel] [k] hrtimer_nanosleep
0.07% [kernel] [k] walk_stackframe
0.07% libc.so.6 pa:04f7b000~050a8000 [.] 0x00076abc
0.05% [kernel] [k] memset
0.04% [kernel] [k] __fget
0.04% doris (deleted) pa:14004000~14725000 [.] 0x000539f8
0.04% [kernel] [k] n_tty_poll
0.04% [kflow_videoprocess] [k] $a
0.03% [kernel] [k] _raw_write_unlock_irq
0.03% [kernel] [k] do_select
0.03% [kernel] [k] put_timespec64
Mapped keys:
[d] display refresh delay. (2)
[e] display entries (lines). (20)
[f] profile display filter (count). (5)
[F] annotate display filter (percent). (5%)
[s] annotate symbol. (NULL)
[S] stop annotation.
[K] hide kernel symbols. (no)
[U] hide user symbols. (no)
[z] toggle sample zeroing. (0)
[qQ] quit.
Enter selection, or unmapped key to continue
第一行是CPU占用比 第二行是屬性 第三行是運作的函數名 [k]是指核心空間 [.]是指使用者空間
第一列:符号引發的性能事件的比例,指占用的cpu周期比例。
第二列:符号所在的DSO(Dynamic Shared Object),可以是應用程式、核心、動态連結庫、子產品。
第三列:DSO的類型。[.]表示此符号屬于使用者态的ELF檔案,包括可執行檔案與動态連結庫;[k]表述此符号屬于核心或子產品。
第四列:符号名。有些符号不能解析為函數名,隻能用位址表示。
除了上面的還有以下幾個指令也被常常用到,在此不一一運作查閱了。
perf kmem針對slab子系統性能分析
perf kvm針對kvm虛拟化分析
perf lock分析鎖性能
perf mem分析記憶體slab性能
perf sched分析核心排程器性能
perf trace記錄系統調用軌迹
5.4 perfrecord與perf report
record會将資料儲存到perf.data中。随後,可以使用perf report進行分析。
perf record和perf report可以更精确的分析一個應用,perf record可以精确到函數級别。并且在函數裡面混合顯示彙編語言和代碼。
這裡要注意如果是要分析應用程式,請務必不要strip并加上CFLAG+=-g
分析整個系統的
perf record -F 99 -ag --call-graph dwarf -o /mnt/mmc01/perf.data – sleep 30
perf report --stdio --no-children -g graph,0.5,callee -i perf.data > perfreport.txt
record 進行記錄 然後report進行報告
報告截取部分如下:
65.10% swapper [kernel.kallsyms] [k] arch_cpu_idle
|
---arch_cpu_idle
default_idle_call
do_idle
cpu_startup_entry
|
|--36.12%--secondary_start_kernel
| 0x10248c
|
--28.97%--rest_init
start_kernel
0
3.78% ALG [kernel.kallsyms] [k] _raw_spin_unlock_irqrestore
|
---_raw_spin_unlock_irqrestore
|
--3.77%--grph_platform_spin_unlock
graph_enqueue
kdrv_grph_trigger
gfx_dma_copy
$a
proc_ioctl
proc_reg_unlocked_ioctl
vfs_ioctl
do_vfs_ioctl
ksys_ioctl
__se_sys_ioctl
__hyp_idmap_text_start
2.25% ctl_ipp_buf_tsk [kernel.kallsyms] [k] _raw_spin_unlock_irqrestore
|
---_raw_spin_unlock_irqrestore
|
--1.86%--vk_spin_unlock_irqrestore
|
--0.51%--hwclock_get_longcounter
1.39% NMR_VdoTrig_D2D [kernel.kallsyms] [k] _raw_spin_unlock_irqrestore
|
---_raw_spin_unlock_irqrestore
|
--1.15%--vk_spin_unlock_irqrestore
1.32% kdrv_ise_proc_t [kernel.kallsyms] [k] v7_dma_clean_range
|
---v7_dma_clean_range
fmem_dcache_sync
vos_cpu_dcache_sync
$a
kdrv_ise_job_process_ll
kdrv_ise_proc_task
kthread
ret_from_fork
......
可以看到占用CPU最高的程式是什麼,運作在哪一個函數了
解釋:
perf record -F 99 -p 13204 -g – sleep 30
perf record表示記錄,-F 99表示每秒99次,-p 13204是程序号,即對哪個程序進行分析,-g表示記錄調用棧,sleep 30則是持續30秒
6、如何生成火焰圖
必須使用相關的工具,該工具為FlameGraph,可以在https://github.com/brendangregg/FlameGraph上下載下傳到該工具
6.1 recode資料
使用perf recode記錄采樣資料
6.2 解析recode的資料
perf script -i perf.data &> perf.unfold
6.3 将perf.unfold中的符号進行折疊
stackcollapse-perf.pl perf.unfold &> perf.folded
6.4 最後生成svg圖
flamegraph.pl perf.folded > perf.svg
這樣就能總覽整個性能了
y 軸表示調用棧,每一層都是一個函數。調用棧越深,火焰就越高,頂部就是正在執行的函數,下方都是它的父函數。
x 軸表示抽樣數,如果一個函數在 x 軸占據的寬度越寬,就表示它被抽到的次數多,即執行的時間長。注意,x 軸不代表時間,而是所有的調用棧合并後,按字母順序排列的。
火焰圖就是看頂層的哪個函數占據的寬度最大。隻要有"平頂"(plateaus),就表示該函數可能存在性能問題。
顔色沒有特殊含義,因為火焰圖表示的是 CPU 的繁忙程度,是以一般選擇暖色調。