perf是linux上的性能分析工具,perf可以對event進行統(tǒng)計得到event的發(fā)生次數(shù),或者對event進行采樣,得到每次event發(fā)生時的相關(guān)數(shù)據(jù)(cpu、進程id、運行棧等),利用這些數(shù)據(jù)來對程序性能進行分析。
perf可以統(tǒng)計或采樣的event有很多,如果我們要分析cpu,那么我們可以使用cpu-cycles
、cpu-clock
來衡量占用cpu的程序的分布情況,還可以通過cache-misses
、page-faults
、branch-misses
等event來分析造成cpu占用高的底層原因,確定原因后方便優(yōu)化。
如果我們要分析內(nèi)存、io、網(wǎng)絡(luò)等,也可以通過其他event來進行分析,perf可以使用的event非常多,如果要使用perf來分析問題,就需要了解問題相關(guān)的event有哪些,作用是什么,這是使用perf的一個門檻。
perf工作大致可以分成三種模式:
counter
-
- 計數(shù)模式,記錄perf執(zhí)行過程中,統(tǒng)計的目標程序或者整個系統(tǒng)范圍內(nèi),event的出現(xiàn)次數(shù)。
sampling
- 采樣模式,按照指定頻率去采樣event,記錄每次采樣時,采樣事件輸出的信息(cpu、進程id、運行棧等)。這種方式由于每次都記錄信息,所以額外的資源消耗是比較大的,需要權(quán)衡一下采樣頻率。同時產(chǎn)生的數(shù)據(jù)量也容易很大,可能需要大量的硬盤空間。bpf 可以對對應(yīng)的event執(zhí)行用戶自己設(shè)計的代碼,也就是說記錄的信息、執(zhí)行的操作可以由用戶定制
perf可以使用的event非常多,上圖是Brendan Gregg的文章中找到的一張圖,畫出了perf可以使用的event的結(jié)構(gòu)圖,大致可以分為以下幾類:
- Hardware Events: CPU的PMU(performance monitoring unit)觸發(fā)的事件,也叫performance monitoring counters (PMCs),例如cpu-cycles、cache missSoftware Events: 一些比較底層的軟件event,例如缺頁、timer(定時)Kernel Tracepoint Events: 內(nèi)核中的tracepointUser Statically-Defined Tracing (USDT): 用戶態(tài)的tracepointDynamic Tracing: 動態(tài)設(shè)置的event,例如使用內(nèi)核的kprobe,可以在大部分函數(shù)動態(tài)增加eventTimed Profiling: 定時event
安裝
x86安裝
sudo?apt?install?linux-tools-common
sudo?apt?install?linux-tools-generic
sudo?apt?install?linux-tools-5.4.0-137-generic?
交叉編譯
由于我們經(jīng)常是在自己編譯的內(nèi)核上進行開發(fā)工作,進入linux內(nèi)核源碼目錄linux/tools/perf
。
???tools?git:(firefly)???make?CROSS_COMPILE=/home/zhongyi/code/rk3399_linux_release_v2.5.1_20210301/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-?ARCH=arm?WERROR=0?perf?V=1
可能在編譯的時候,有報錯大概是由于平臺問題,數(shù)據(jù)類型不匹配,導(dǎo)致所有的warning都被當(dāng)作error對待:出現(xiàn)這問題的原因是-Werror這個gcc編譯選項。只要在makefile中找到包含這個-Werror選項的句子,將-Werror刪除,或是注釋掉就行了
編譯完成后將會在當(dāng)前目錄下生成perf可執(zhí)行文件,拷貝到設(shè)備上即可運行。
root@firefly:~/mnt#?./perf?--help
?usage:?perf?[--version]?[--help]?[OPTIONS]?COMMAND?[ARGS]
?The?most?commonly?used?perf?commands?are:
???annotate????????Read?perf.data?(created?by?perf?record)?and?display?annotated?code
???archive?????????Create?archive?with?object?files?with?build-ids?found?in?perf.data?file
???bench???????????General?framework?for?benchmark?suites
???buildid-cache???Manage?build-id?cache.
???buildid-list????List?the?buildids?in?a?perf.data?file
???data????????????Data?file?related?processing
???diff????????????Read?perf.data?files?and?display?the?differential?profile
???evlist??????????List?the?event?names?in?a?perf.data?file
???inject??????????Filter?to?augment?the?events?stream?with?additional?information
???kmem????????????Tool?to?trace/measure?kernel?memory?properties
???kvm?????????????Tool?to?trace/measure?kvm?guest?os
???list????????????List?all?symbolic?event?types
???lock????????????Analyze?lock?events
???mem?????????????Profile?memory?accesses
???record??????????Run?a?command?and?record?its?profile?into?perf.data
???report??????????Read?perf.data?(created?by?perf?record)?and?display?the?profile
???sched???????????Tool?to?trace/measure?scheduler?properties?(latencies)
???script??????????Read?perf.data?(created?by?perf?record)?and?display?trace?output
???stat????????????Run?a?command?and?gather?performance?counter?statistics
???test????????????Runs?sanity?tests.
???timechart???????Tool?to?visualize?total?system?behavior?during?a?workload
???top?????????????System?profiling?tool.
???trace???????????strace?inspired?tool
?See?'perf?help?COMMAND'?for?more?information?on?a?specific?command.
使用方法
總覽
上圖整理了perf的子命令之間的關(guān)系,常用的有:
- perf record —— 采樣,生成perf.data二進制文件perf annotate/perf report/perf script —— 分析perf.data文件,annotate可以查看代碼,report可以統(tǒng)計分析,script是直接轉(zhuǎn)化成文本格式perf stat —— counter,統(tǒng)計event的出現(xiàn)次數(shù)perf top —— 整個系統(tǒng)的分析,類似于top命令,但可以具體到函數(shù),可以指定event
下面我們介紹一些常用的使用方法。
help
perf --help
之后可以看到perf的一級命令。
root@firefly:~/mnt#?./perf?--help
?usage:?perf?[--version]?[--help]?[OPTIONS]?COMMAND?[ARGS]
?The?most?commonly?used?perf?commands?are:
???annotate????????Read?perf.data?(created?by?perf?record)?and?display?annotated?code
???archive?????????Create?archive?with?object?files?with?build-ids?found?in?perf.data?file
???bench???????????General?framework?for?benchmark?suites
???buildid-cache???Manage?build-id?cache.
???buildid-list????List?the?buildids?in?a?perf.data?file
???data????????????Data?file?related?processing
???diff????????????Read?perf.data?files?and?display?the?differential?profile
???evlist??????????List?the?event?names?in?a?perf.data?file
???inject??????????Filter?to?augment?the?events?stream?with?additional?information
???kmem????????????Tool?to?trace/measure?kernel?memory?properties
???kvm?????????????Tool?to?trace/measure?kvm?guest?os
???list????????????List?all?symbolic?event?types
???lock????????????Analyze?lock?events
???mem?????????????Profile?memory?accesses
???record??????????Run?a?command?and?record?its?profile?into?perf.data
???report??????????Read?perf.data?(created?by?perf?record)?and?display?the?profile
???sched???????????Tool?to?trace/measure?scheduler?properties?(latencies)
???script??????????Read?perf.data?(created?by?perf?record)?and?display?trace?output
???stat????????????Run?a?command?and?gather?performance?counter?statistics
???test????????????Runs?sanity?tests.
???timechart???????Tool?to?visualize?total?system?behavior?during?a?workload
???top?????????????System?profiling?tool.
???trace???????????strace?inspired?tool
?See?'perf?help?COMMAND'?for?more?information?on?a?specific?command.
perf command --help
可以看到二級命令的幫助命令。
root@firefly:~/mnt#?./perf?stat?-h
?Usage:?perf?stat?[<options>]?[<command>]
????-a,?--all-cpus????????system-wide?collection?from?all?CPUs
????-A,?--no-aggr?????????disable?CPU?count?aggregation
????-B,?--big-num?????????print?large?numbers?with?thousands'?separators
????-C,?--cpu?<cpu>???????list?of?cpus?to?monitor?in?system-wide
????-c,?--scale???????????scale/normalize?counters
????-D,?--delay?<n>???????ms?to?wait?before?starting?measurement?after?program?s
????-d,?--detailed????????detailed?run?-?start?a?lot?of?events
????-e,?--event?<event>???event?selector.?use?'perf?list'?to?list?available?even
????-G,?--cgroup?<name>???monitor?event?in?cgroup?name?only
????-g,?--group???????????put?the?counters?into?a?counter?group
????-I,?--interval-print?<n>
??????????????????????????print?counts?at?regular?interval?in?ms?(>=?10)
????-i,?--no-inherit??????child?tasks?do?not?inherit?counters
????-n,?--null????????????null?run?-?dont?start?any?counters
????-o,?--output?<file>???output?file?name
????-p,?--pid?<pid>???????stat?events?on?existing?process?id
????-r,?--repeat?<n>??????repeat?command?and?print?average?+?stddev?(max:?100,?f
????-S,?--sync????????????call?sync()?before?starting?a?run
????-t,?--tid?<tid>???????stat?events?on?existing?thread?id
????-T,?--transaction?????hardware?transaction?statistics
下面對一級命令作一個解釋
序號 | 命令 | 作用 |
---|---|---|
1 | annotate | 解析perf record生成的perf.data文件,顯示被注釋的代碼。 |
2 | archive | 根據(jù)數(shù)據(jù)文件記錄的build-id,將所有被采樣到的elf文件打包。利用此壓縮包,可以再任何機器上分析數(shù)據(jù)文件中記錄的采樣數(shù)據(jù)。 |
3 | bench | perf中內(nèi)置的benchmark,目前包括兩套針對調(diào)度器和內(nèi)存管理子系統(tǒng)的benchmark。 |
4 | buildid-cache | 管理perf的buildid緩存,每個elf文件都有一個獨一無二的buildid。buildid被perf用來關(guān)聯(lián)性能數(shù)據(jù)與elf文件。 |
5 | buildid-list | 列出數(shù)據(jù)文件中記錄的所有buildid。 |
6 | diff | 對比兩個數(shù)據(jù)文件的差異。能夠給出每個符號(函數(shù))在熱點分析上的具體差異。 |
7 | evlist | 列出數(shù)據(jù)文件perf.data中所有性能事件。 |
8 | inject | 該工具讀取perf record工具記錄的事件流,并將其定向到標準輸出。在被分析代碼中的任何一點,都可以向事件流中注入其它事件。 |
9 | kmem | 針對內(nèi)核內(nèi)存(slab)子系統(tǒng)進行追蹤測量的工具 |
10 | kvm | 用來追蹤測試運行在KVM虛擬機上的Guest OS。 |
11 | list | 列出當(dāng)前系統(tǒng)支持的所有性能事件。包括硬件性能事件、軟件性能事件以及檢查點。 |
12 | lock | 分析內(nèi)核中的鎖信息,包括鎖的爭用情況,等待延遲等。 |
13 | mem | 內(nèi)存存取情況 |
14 | record | 收集采樣信息,并將其記錄在數(shù)據(jù)文件中。隨后可通過其它工具對數(shù)據(jù)文件進行分析。 |
15 | report | 讀取perf record創(chuàng)建的數(shù)據(jù)文件,并給出熱點分析結(jié)果。 |
16 | sched | 針對調(diào)度器子系統(tǒng)的分析工具。 |
17 | script | 執(zhí)行perl或python寫的功能擴展腳本、生成腳本框架、讀取數(shù)據(jù)文件中的數(shù)據(jù)信息等。 |
18 | stat | 執(zhí)行某個命令,收集特定進程的性能概況,包括CPI、Cache丟失率等。 |
19 | test | perf對當(dāng)前軟硬件平臺進行健全性測試,可用此工具測試當(dāng)前的軟硬件平臺是否能支持perf的所有功能。 |
20 | timechart | 針對測試期間系統(tǒng)行為進行可視化的工具 |
21 | top | 類似于linux的top命令,對系統(tǒng)性能進行實時分析。 |
22 | trace | 關(guān)于syscall的工具。 |
23 | probe | 用于定義動態(tài)檢查點。 |
全局性概況:
perf list查看當(dāng)前系統(tǒng)支持的性能事件;
perf bench對系統(tǒng)性能進行摸底;
perf test對系統(tǒng)進行健全性測試;
perf stat對全局性能進行統(tǒng)計;
全局細節(jié):
perf top可以實時查看當(dāng)前系統(tǒng)進程函數(shù)占用率情況;
perf probe可以自定義動態(tài)事件;
特定功能分析:
perf kmem針對slab子系統(tǒng)性能分析;
perf kvm針對kvm虛擬化分析;
perf lock分析鎖性能;
perf mem分析內(nèi)存slab性能;
perf sched分析內(nèi)核調(diào)度器性能;
perf trace記錄系統(tǒng)調(diào)用軌跡;
最常用功能perf record,可以系統(tǒng)全局,也可以具體到某個進程,更甚具體到某一進程某一事件;可宏觀,也可以很微觀。
pref record記錄信息到perf.data;
perf report生成報告;
perf diff對兩個記錄進行diff;
perf evlist列出記錄的性能事件;
perf annotate顯示perf.data函數(shù)代碼;
perf archive將相關(guān)符號打包,方便在其它機器進行分析;
perf script將perf.data輸出可讀性文本;
可視化工具perf timechart
perf timechart record記錄事件;
perf timechart生成output.svg文檔;
list
使用perf之前肯定要知道perf能監(jiān)控哪些性能指標吧?那么就要使用perf list進行查看,通常使用的指標是cpu-clock/task-clock等,具體要根據(jù)需要來判斷。
root@firefly:~/mnt#?perf?list
List?of?pre-defined?events?(to?be?used?in?-e):
??rNNN???????????????????????????????????????????????[Raw?hardware?event?descrip
??cpu/t1=v1[,t2=v2,t3?...]/modifier??????????????????[Raw?hardware?event?descrip
???(see?'man?perf-list'?on?how?to?encode?it)
??mem:<addr>[/len][:access]??????????????????????????[Hardware?breakpoint]
??android_fs:android_fs_dataread_end?????????????????[Tracepoint?event]
??android_fs:android_fs_dataread_start???????????????[Tracepoint?event]
??android_fs:android_fs_datawrite_end????????????????[Tracepoint?event]
??android_fs:android_fs_datawrite_start??????????????[Tracepoint?event]
??asoc:snd_soc_bias_level_done???????????????????????[Tracepoint?event]
??asoc:snd_soc_bias_level_start??????????????????????[Tracepoint?event]
??asoc:snd_soc_dapm_connected????????????????????????[Tracepoint?event]
??asoc:snd_soc_dapm_done?????????????????????????????[Tracepoint?event]
??asoc:snd_soc_dapm_path?????????????????????????????[Tracepoint?event]
??asoc:snd_soc_dapm_start????????????????????????????[Tracepoint?event]
??asoc:snd_soc_dapm_walk_done????????????????????????[Tracepoint?event]
??asoc:snd_soc_dapm_widget_event_done????????????????[Tracepoint?event]
??asoc:snd_soc_dapm_widget_event_start???????????????[Tracepoint?event]
??asoc:snd_soc_dapm_widget_power?????????????????????[Tracepoint?event]
??asoc:snd_soc_jack_irq??????????????????????????????[Tracepoint?event]
??asoc:snd_soc_jack_notify???????????????????????????[Tracepoint?event]
??asoc:snd_soc_jack_report???????????????????????????[Tracepoint?event]
??block:block_bio_backmerge??????????????????????????[Tracepoint?event]
??block:block_bio_bounce?????????????????????????????[Tracepoint?event]
??block:block_bio_complete???????????????????????????[Tracepoint?event]
??block:block_bio_frontmerge?????????????????????????[Tracepoint?event]
??block:block_bio_queue??????????????????????????????[Tracepoint?event]
??block:block_bio_remap??????????????????????????????[Tracepoint?event]
??block:block_dirty_buffer???????????????????????????[Tracepoint?event]
??block:block_getrq??????????????????????????????????[Tracepoint?event]
??......
具體監(jiān)控哪個變量的話,譬如使用后面的perf report工具,則加**-e 監(jiān)控指標**,如監(jiān)控運行l(wèi)s命令時的cpu時鐘占用:
perf?report?-e?cpu-clock?ls
event
不同內(nèi)核版本列出的結(jié)果不一樣多。不過基本是夠用的,但是無論多少,我們可以基本將其分為三類
一些事件只是單純的內(nèi)核計數(shù)器,這種情況下,他們被稱為software events。例如,上下文切換。
事件的另一個來源是處理器本身及其性能監(jiān)控單元(Performance Monitoring Unit,PMU)。它提供了一個事件列表來衡量微架構(gòu)事件,如周期數(shù)、指令異常、L1緩存未命中等。這些事件被稱為PMU硬件事件( PMU hardware events)或簡稱為硬件事件(hardware events)。這些事件因每種處理器類型和型號而異。
perf_events接口還提供了一小組常見的硬件事件名字對象。在每個處理器上,這些事件被映射到CPU提供的實際事件上,只有映射成立即實際事件存在時,這些事件才能被使用。這些事件也被稱為硬件事件(hardware events)和硬件緩存事件( hardware cache events)。
還有一些 tracepoint events ?是依賴于ftrace架構(gòu)實現(xiàn)的,這些只有在2.6.3x以上的內(nèi)核才可以使用。
一個事件可以有子事件(或 unit masks)。在某些處理器上,對于某些事件,可以將 unit masks
組合 使用并測量任一子事件發(fā)生的時間。
/sys/kernel/debug/tracing/available_events
,可查看當(dāng)前系統(tǒng)的所有tracepoint分成了幾大類:
ext4:文件系統(tǒng)的tracepoint?events,如果是其它文件系統(tǒng),比如XFS,也有對應(yīng)的tracepoint?event;
jbd2?:文件日志的tracepoint?events;
skb:?內(nèi)存的tracepoint?events;
net,napi,sock,udp:網(wǎng)絡(luò)的tracepoint?events;
scsi,?block,?writeback:磁盤IO
kmem:內(nèi)存
sched:?調(diào)度
syscalls:?系統(tǒng)調(diào)用
屬性
用戶如果想要使用高精度采樣,需要在指定性能事件時,在事件名后添加后綴:p
或:pp
。Perf在采樣精度上定義了4個級別,如下所示。
0?:無精度保證
1?:采樣指令與觸發(fā)性能事件的指令之間的偏差為常數(shù)(:p)
2?:需要盡量保證采樣指令與觸發(fā)性能事件的指令之間的偏差為0(:pp)
3?:保證采樣指令與觸發(fā)性能事件的指令之間的偏差必須為0(:ppp)
目前的X86處理器,包括Intel處理器與AMD處理器均僅能實現(xiàn)前 3 個精度級別。
除了精度級別以外,性能事件還具有其它幾個屬性,均可以通過event:X
的方式予以指定。
u?僅統(tǒng)計用戶空間程序觸發(fā)的性能事件
k?僅統(tǒng)計內(nèi)核觸發(fā)的性能事件
h?僅統(tǒng)計Hypervisor觸發(fā)的性能事件
G?在KVM虛擬機中,僅統(tǒng)計Guest系統(tǒng)觸發(fā)的性能事件
H?僅統(tǒng)計?Host?系統(tǒng)觸發(fā)的性能事件
p?精度級別
stat
perf stat
分析系統(tǒng)/進程的整體性能概況。
命令解析
-a,?--all-cpus??采集所有CPU的信息
-A,?--no-aggr???不要在system-wide(-a)模式下匯集所有CPU的計數(shù)信息
-B,?--big-num???保留三位小數(shù)
-C,?--cpu?<cpu>??指定某個cpu
-D,?--delay?<n>??在測試程序開始后,在測量前等等?n?ms
-d,?--detailed???打印更詳細的統(tǒng)計數(shù)據(jù),最多可以指定3次
?????-d:??????????detailed?events,?L1?and?LLC?data?cache
????????-d?-d:?????more?detailed?events,?dTLB?and?iTLB?events
?????-d?-d?-d:?????very?detailed?events,?adding?prefetch?events
?????
-e,?--event?<event>??事件選擇。可以參考perf?list。
-G,?--cgroup?<name>??僅在name為cgroup時有效。
-g,?--group????????將計數(shù)器放到一個計數(shù)組中???
-I,?--interval-print?<n>??每n毫秒打印計數(shù)增量(最小值:10ms).在某些情況下,開銷可能很高,例如小于100毫秒的間隔。
-i,?--no-inherit??禁止子任務(wù)繼承父任務(wù)的性能計數(shù)器。
-M,?--metrics?<metric/metric?group?list>??監(jiān)視指定的?metrics?或???metric?groups,以逗號分隔。
-n,?--null???僅輸出目標程序的執(zhí)行時間,而不開啟任何性能計數(shù)器。
-o,?--output?<file>???輸出文件的名字
-p,?--pid?<pid>??????指定待分析的進程id
-r,?--repeat?<n>??重復(fù)執(zhí)行?n?次目標程序,并給出性能指標在n?次執(zhí)行中的變化范圍。
-S,?--sync????????在開始前調(diào)用sync()?
-t,?--tid?<tid>???指定待分析的線程id
-T,?--transaction????如果支持,打印事務(wù)執(zhí)行的統(tǒng)計數(shù)據(jù)。
-v,?--verbose??????顯示詳細信息
-x,?--field-separator?<separator>???使用CSV樣式的輸出打印計數(shù),以便直接導(dǎo)入表格。列由SEP中指定的字符串分隔。
舉例
ubuntu#?perf?stat?-B?dd?if=/dev/zero?of=/dev/null?count=1000000
1000000+0?records?in
1000000+0?records?out
512000000?bytes?(512?MB,?488?MiB)?copied,?0.868718?s,?589?MB/s
?Performance?counter?stats?for?'dd?if=/dev/zero?of=/dev/null?count=1000000':
????????????869.31?msec?task-clock????????????????#????0.999?CPUs?utilized??????????
?????????????????2??????context-switches??????????#????0.002?K/sec??????????????????
?????????????????0??????cpu-migrations????????????#????0.000?K/sec??????????????????
????????????????71??????page-faults???????????????#????0.082?K/sec??????????????????
???<not?supported>??????cycles??????????????????????????????????????????????????????
???<not?supported>??????instructions????????????????????????????????????????????????
???<not?supported>??????branches????????????????????????????????????????????????????
???<not?supported>??????branch-misses???????????????????????????????????????????????
???????0.870022180?seconds?time?elapsed
???????0.450870000?seconds?user
???????0.418950000?seconds?sys
如果沒有指定那個事件,perf stat
將收集上面列出的常見事件。比如,上下文切換,CPU遷移次數(shù),缺頁故障等。
task‐clock:事件表示目標任務(wù)真正占用處理器的時間,單位是毫秒。也稱任務(wù)執(zhí)行時間。CPUs utilized = task-clock / time elapsed,CPU的占用率。
context-switches:程序在運行過程中上下文的切換次數(shù)。
CPU-migrations:程序在運行過程中發(fā)生的處理器遷移次數(shù)。Linux為了維持多個處理器的負載均衡,在特定條件下會將某個任務(wù)從一個CPU遷移到另一個CPU。
CPU遷移和上下文切換:發(fā)生上下文切換不一定會發(fā)生CPU遷移,而發(fā)生CPU遷移時肯定會發(fā)生上下文切換。發(fā)生上下文切換有可能只是把上下文從當(dāng)前CPU中換出,下一次調(diào)度器還是將進程安排在這個CPU上執(zhí)行。
page-faults:缺頁異常的次數(shù)。當(dāng)應(yīng)用程序請求的頁面尚未建立、請求的頁面不在內(nèi)存中,或者請求的頁面雖然在內(nèi)存中,但物理地址和虛擬地址的映射關(guān)系尚未建立時,都會觸發(fā)一次缺頁異常。另外TLB不命中,頁面訪問權(quán)限不匹配等情況也會觸發(fā)缺頁異常。
cycles:消耗的處理器周期數(shù)。如果把被ls使用的cpu cycles看成是一個處理器的,那么它的主頻為2.486GHz??梢杂胏ycles / task-clock算出。
stalled-cycles-frontend:指令讀取或解碼的質(zhì)量步驟,未能按理想狀態(tài)發(fā)揮并行左右,發(fā)生停滯的時鐘周期。
stalled-cycles-backend:指令執(zhí)行步驟,發(fā)生停滯的時鐘周期。
instructions:執(zhí)行了多少條指令。IPC為平均每個cpu cycle執(zhí)行了多少條指令。
branches:遇到的分支指令數(shù)。branch-misses是預(yù)測錯誤的分支指令數(shù)。
branch‐misses:是預(yù)測錯誤的分支指令數(shù)。
XXX seconds time elapsed:系程序持續(xù)時間
每次運行性能工具時,可以測量一個或多個事件。事件使用其符號名稱,后跟可選的單元掩碼和修飾符來指定。事件名稱、單元掩碼和修飾符不區(qū)分大小寫。
perf?stat?-e?cpu-clock?dd?if=/dev/zero?of=/dev/null?count=100000
默認情況下,會在用戶和內(nèi)核級別測量事件。如果僅在用戶級別進行測量,需要傳遞一個修飾符:
perf?stat?-e?cpu-clock:u?dd?if=/dev/zero?of=/dev/null?count=100000
如果即在用戶態(tài)測量,又在內(nèi)核態(tài)測量,則可以同時傳遞uk參數(shù)
perf?stat?-e?cpu-clock:uk?dd?if=/dev/zero?of=/dev/null?count=100000
ls命令執(zhí)行了多少次系統(tǒng)調(diào)用
perf?stat?-e?syscalls:sys_enter_exit?ls
只顯示任務(wù)執(zhí)行時間,不顯示性能計數(shù)器
?perf?stat?-n?ls?>?/dev/null????
record
記錄一段時間內(nèi)系統(tǒng)/進程的性能時間。
命令解析
-a,?--all-cpus????????system-wide?collection?from?all?CPUs
-b,?--branch-any??????sample?any?taken?branches
-B,?--no-buildid??????do?not?collect?buildids?in?perf.data
-c,?--count?<n>?????事件的采樣周期
-C,?--cpu?<cpu>??????只采集指定CPU數(shù)據(jù)
-d,?--data???????????記錄采樣地址
-D,?--delay?<n>??????在測試程序開始后,在測量前等等?n?ms
-F??????指定采樣頻率
-e,?--event?<event>??選擇性能事件
-F,?--freq?<freq?or?'max'>?指定頻率
-g????????記錄函數(shù)間的調(diào)用關(guān)系
-G,?--cgroup?<name>???僅僅監(jiān)視指定的cgroup?name
-I,?--intr-regs[=<any?register>]??每n毫秒打印計數(shù)增量(最小值:10ms).在某些情況下,開銷可能很高,例如小于100毫秒的間隔。
-i,?--no-inherit?????禁止子任務(wù)繼承父任務(wù)的性能計數(shù)器。
-j,?--branch-filter?<branch?filter?mask>??啟用分支堆棧采樣。每個樣本捕獲一系列連續(xù)的采樣分支。
-k,?--clockid?<clockid>?設(shè)置用于perf_event_type中各種時間字段的時鐘id記錄。請參見clock_gettime()。
-m,?--mmap-pages?<pages[,pages]>?mmap數(shù)據(jù)頁面和輔助區(qū)域跟蹤mmap頁面的數(shù)量
-N,?--no-buildid-cache?不要更新buildid緩存
-n,?--no-samples?????不要采樣
-o,?--output?<file>??指定輸出文件,默認為perf.data
-P,?--period?????????采樣周期
-p,?--pid?<pid>?????指定進程id
-q,?--quiet?????????不打印任何信息
-R,?--raw-samples???從所有打開的計數(shù)器收集原始樣本記錄
-r,?--realtime?<n>???以?SCHED_FIFO?優(yōu)先級實時收集數(shù)據(jù)
-S,?--snapshot[=<opts>]?快照模式
-s,?--stat?????????記錄每個線程的事件計數(shù),使用perf?report?-T?查看可選值
-t,?--tid?<tid>????在現(xiàn)有線程ID上記錄事件(逗號分隔列表)
-T,?--timestamp?????記錄采樣時間戳。使用?perf?report?-D查看更詳細信息
-u,?--uid?<user>???指定用戶id
-W,?--weight???????啟用加權(quán)采樣
舉例
記錄執(zhí)行l(wèi)s時的性能數(shù)據(jù)
perf?record?ls?-g????
記錄執(zhí)行l(wèi)s時的系統(tǒng)調(diào)用,可以知道哪些系統(tǒng)調(diào)用最頻繁
perf?record?-e?syscalls:sys_enter?ls???
report
讀取perf record
生成的數(shù)據(jù)文件,并顯示分析數(shù)據(jù)。
-p<regex>:用指定正則表達式過濾調(diào)用函數(shù)
-e?<event>:指定性能事件(可以是多個,用,分隔列表)
-p?<pid>:指定待分析進程的?pid(可以是多個,用,分隔列表)
-t?<tid>:指定待分析線程的?tid(可以是多個,用,分隔列表)
-u?<uid>:指定收集的用戶數(shù)據(jù),uid為名稱或數(shù)字
-a:從所有?CPU?收集系統(tǒng)數(shù)據(jù)
-C?<cpu-list>:只統(tǒng)計指定?CPU?列表的數(shù)據(jù),如:0,1,3或1-2
-r?<RT?priority>:perf?程序以SCHED_FIFO實時優(yōu)先級RT?priority運行這里填入的數(shù)值越大,進程優(yōu)先級越高(即?nice?值越?。?
-c?<count>:?事件每發(fā)生?count?次采一次樣
-F?<n>:每秒采樣?n?次
-o?<output.data>:指定輸出文件output.data,默認輸出到perf.data
-i:輸入的數(shù)據(jù)文件
-v:顯示每個符號的地址
-d?<dos>:只顯示指定dos的符號
-S:只考慮指定符號
-U:只顯示已解析的符號
-g[type,min,order]:顯示調(diào)用關(guān)系,具體等同于perf?top命令中的-g
-c:只顯示指定cpu采樣信息
-M:以指定匯編指令風(fēng)格顯示
–source:以匯編和source的形式進行顯示
舉例
記錄執(zhí)行l(wèi)s時的性能數(shù)據(jù)
perf?record?ls?-g????
顯示
perf?report?-i?perf.data
overhead:cpu-clock占用百分比
command:當(dāng)前執(zhí)行的命令
shared object :依賴的共享庫
symbol:當(dāng)前占用比下對應(yīng)的符號
[.]代表該調(diào)用屬于用戶態(tài),若自己監(jiān)控的進程為用戶態(tài)進程,那么這些即主要為用戶態(tài)的cpu-clock占用的數(shù)值,[k]代表屬于內(nèi)核態(tài)的調(diào)用。
也許有的人會奇怪為什么自己完全是一個用戶態(tài)的程序為什么還會統(tǒng)計到內(nèi)核態(tài)的指標?
一是用戶態(tài)程序運行時會受到內(nèi)核態(tài)的影響,若內(nèi)核態(tài)對用戶態(tài)影響較大,統(tǒng)計內(nèi)核態(tài)信息可以了解到是內(nèi)核中的哪些行為導(dǎo)致對用戶態(tài)產(chǎn)生影響;二則是有些用戶態(tài)程序也需要依賴內(nèi)核的某些操作,譬如I/O操作
annotate
perf annotate提供指令級別的record文件定位。使用調(diào)試信息-g編譯的文件能夠顯示匯編和本身源碼信息。
但要注意, annotate命令并不能夠解析內(nèi)核image中的符號,必須要傳遞未壓縮的內(nèi)核image給annotate才能正常的解析內(nèi)核符號,比如:
perf?annotate?-k?/tmp/vmlinux?-d?symbol
命令解析
-i:輸入文件名
-d:只考慮這些DSO中的符號
-f:強制讀取
-D:轉(zhuǎn)儲ASCII中的原始跟蹤
-k:?vmlinux路徑名
-m:加載模塊符號表.僅與-k和一起使用
-l:打印匹配到的源代碼行
-P:顯示完整路徑名
-M?指定反匯編程序樣式
-stdio:使用stdio接口
-gtk:使用GTK接口
舉例
main.c內(nèi)容如下:
#include?<stdio.h>
#include?<time.h>
void?func_a()?{
???unsigned?int?num?=?1;
???int?i;
???for?(i?=?0;i?<?10000000;?i++)?{
??????num?*=?2;
??????num?=?1;
???}
}
void?func_b()?{
???unsigned?int?num?=?1;
???int?i;
???for?(i?=?0;i?<?10000000;?i++)?{
??????num?<<=?1;
??????num?=?1;
???}
}
int?main()?{
???func_a();
???func_b();
???return?0;
}
編譯命令:
gcc?-g?-O0?main.c?#-g是debug信息,保留符號表等;-O0表示不進行優(yōu)化處理
統(tǒng)計命令:
perf?record?-a?-g?./a.out
perf report查看結(jié)果:
Samples:?73??of?event?'cpu-clock',?Event?count?(approx.):?18250000???????
??Children??????Self??Command??Shared?Object??????Symbol????
+???97.26%?????0.00%??a.out????a.out??????????????[.]?main?
+???97.26%?????0.00%??a.out????libc-2.19.so???????[.]?__libc_start_main?
+???49.32%????49.32%??a.out????a.out??????????????[.]?func_a?
+???47.95%????47.95%??a.out????a.out??????????????[.]?func_b?
+????1.37%?????1.37%??perf?????[kernel.kallsyms]??[k]?finish_task_switch??
+????1.37%?????0.00%??a.out????ld-2.19.so?????????[.]?dl_main?
perf annotate查看結(jié)果:
func_a??/home/goodboy/tmp/a.out???????????
???????│????void?func_a()?{
???????│??????push???%rbp
???????│??????mov????%rsp,%rbp
???????│???????unsigned?int?num?=?1;
???????│??????movl???$0x1,-0x8(%rbp)
???????│???????int?i;
???????│???????for?(i?=?0;i?<?10000000;?i++)?{
???????│??????movl???$0x0,-0x4(%rbp)
???????│????↓?jmp????22
???????│??????????num?*=?2;
?11.11?│14:┌─→shll???-0x8(%rbp)
???????│???│??????num?=?1;
???????│???│??movl???$0x1,-0x8(%rbp)
???????│???│#include?<stdio.h>
???????│???│#include?<time.h>
???????│???│void?func_a()?{
???????│???│???unsigned?int?num?=?1;
???????│???│???int?i;
???????│???│???for?(i?=?0;i?<?10000000;?i++)?{
??5.56?│???│??addl???$0x1,-0x4(%rbp)
?33.33?│22:│??cmpl???$0x98967f,-0x4(%rbp)
?50.00?│???└──jle????14
???????│??????????num?*=?2;
???????│??????????num?=?1;
???????│???????}
???????│????}
???????│??????pop????%rbp
???????│????←?retq
top
實時顯示系統(tǒng)/進程的性能統(tǒng)計信息
命令解析
-e:指定性能事件
-a:顯示在所有CPU上的性能統(tǒng)計信息
-d:界面的刷新周期,默認為2s。
-C:顯示在指定CPU上的性能統(tǒng)計信息
-p:指定進程PID
-t:指定線程TID
-K:隱藏內(nèi)核統(tǒng)計信息
-k:帶符號表的內(nèi)核映像所在的路徑。
-U:隱藏用戶空間的統(tǒng)計信息
-s:指定待解析的符號信息
-g:得到函數(shù)的調(diào)用關(guān)系圖。
‘‐G’?or‘‐‐call‐graph’?<output_type,min_percent,call_order>
graph:?使用調(diào)用樹,將每條調(diào)用路徑進一步折疊。這種顯示方式更加直觀。
每條調(diào)用路徑的采樣率為絕對值。也就是該條路徑占整個采樣域的比率。
fractal
默認選項。類似與?graph,但是每條路徑前的采樣率為相對值。
flat
不折疊各條調(diào)用
選項?call_order?用以設(shè)定調(diào)用圖譜的顯示順序,該選項有?2個取值,分別是
callee?與caller。
將該選項設(shè)為callee?時,perf按照被調(diào)用的順序顯示調(diào)用圖譜,上層函數(shù)被下層函數(shù)所調(diào)用。
該選項被設(shè)為caller?時,按照調(diào)用順序顯示調(diào)用圖譜,即上層函數(shù)調(diào)用了下層函數(shù)路徑,也不顯示每條調(diào)用路徑的采樣率
舉例
顯示分配高速緩存最多的函數(shù)
perf?top?-e?kmem:kmem_cache_alloc??
顯示內(nèi)核和模塊中,消耗最多CPU周期的函數(shù)
perf?top?-e?cycles:k?
第一列:符號引發(fā)的性能事件的比例,默認指占用的cpu周期比例。
第二列:符號所在的DSO(Dynamic Shared Object),可以是應(yīng)用程序、內(nèi)核、動態(tài)鏈接庫、模塊。
第三列:DSO的類型。[.]
表示此符號屬于用戶態(tài)的ELF文件,包括可執(zhí)行文件與動態(tài)鏈接庫)。[k]
表述此符號屬于內(nèi)核或模塊。
第四列:符號名。有些符號不能解析為函數(shù)名,只能用地址表示。
bench
perf bench作為benchmark工具的通用框架,包含sched/mem/numa/futex等子系統(tǒng),all可以指定所有。
perf bench可用于評估系統(tǒng)sched/mem等特定性能。
命令解析
-f,?--format?<default|simple>?選擇輸出格式,simple模式下只顯示測量時間
-r,?--repeat?<n>??????指定重復(fù)運行的次數(shù)
子系統(tǒng)包括
sched:調(diào)度器和IPC機制。包含messaging和pipe兩個功能。
mem:內(nèi)存存取性能。包含memcpy和memset兩個功能。
numa:NUMA架構(gòu)的調(diào)度和內(nèi)存處理性能。包含mem功能。
futex:futex壓力測試。包含hash/wake/wake-parallel/requeue/lock-pi功能。
all:所有bench測試的集合
舉例
sched messaging
評估進程調(diào)度和核間通信,sched message
是從經(jīng)典的測試程序 hackbench
移植而來,用來衡量調(diào)度器的性能,overhead
以及可擴展性。
該 benchmark
啟動 N 個 reader/sender
進程或線程對,通過 IPC(socket 或者 pipe) 進行并發(fā)的讀寫。一般人們將 N 不斷加大來衡量調(diào)度器的可擴展性。
sched message
的用法及用途和 hackbench
一樣,可以通過修改參數(shù)進行不同目的測試:
-g,?--group?<n>?Specify?number?of?groups
-l,?--nr_loops?<n>?Specify?the?number?of?loops?to?run?(default:?100)
-p,?--pipe?Use?pipe()?instead?of?socketpair()
-t,?--thread?Be?multi?thread?instead?of?multi?process
ubuntu#?perf?bench?sched?all
#?Running?sched/messaging?benchmark...
#?20?sender?and?receiver?processes?per?group
#?10?groups?==?400?processes?run
?????Total?time:?0.077?[sec]
#?Running?sched/pipe?benchmark...
#?Executed?1000000?pipe?operations?between?two?processes
?????Total?time:?27.550?[sec]
??????27.550083?usecs/op
??????????36297?ops/sec
使用pipe()
和socketpair()
對測試影響:
ubuntu#?perf?bench?sched?messaging
#?Running?'sched/messaging'?benchmark:
#?20?sender?and?receiver?processes?per?group
#?10?groups?==?400?processes?run
?????Total?time:?0.071?[sec]
ubuntu#?perf?bench?sched?messaging?-p
#?Running?'sched/messaging'?benchmark:
#?20?sender?and?receiver?processes?per?group
#?10?groups?==?400?processes?run
?????Total?time:?0.069?[sec]
ubuntu#?
可見socketpair()性能要明顯低于pipe()。
使用perf分析完整例子
下面我們舉一個具體的例子來看下perf的使用方法。
//t1.c?
void?longa()?
{
???
??int?i,j;?
??for(i?=?0;?i?<?1000000;?i++)?
??j=i;?//am?I?silly?or?crazy??I?feel?boring?and?desperate.?
}??
?
void?foo2()?
{
???
??int?i;?
??for(i=0?;?i?<?10;?i++)?
???????longa();?
}?
?
void?foo1()?
{
???
??int?i;?
??for(i?=?0;?i<?100;?i++)?
?????longa();?
}?
?
int?main(void)?
{
while(1)
{
??foo1();?
??foo2();
}?
}
總攬全局
先用 perf stat
查看下程序整體性能情況,該工具主要是從全局上監(jiān)控,可以看到程序?qū)е滦阅芷款i主要是什么原因。perf stat
通過概括精簡的方式提供被調(diào)試程序運行的整體情況和匯總數(shù)據(jù)。
ubuntu#?perf?stat?./perf_test
^C./perf_test:?Interrupt
?Performance?counter?stats?for?'./perf_test':
??????????8,659.24?msec?task-clock????????????????#????1.000?CPUs?utilized??????????
????????????????21??????context-switches??????????#????0.002?K/sec??????????????????
?????????????????0??????cpu-migrations????????????#????0.000?K/sec??????????????????
????????????????43??????page-faults???????????????#????0.005?K/sec??????????????????
???<not?supported>??????cycles??????????????????????????????????????????????????????
???<not?supported>??????instructions????????????????????????????????????????????????
???<not?supported>??????branches????????????????????????????????????????????????????
???<not?supported>??????branch-misses???????????????????????????????????????????????
???????8.660065455?seconds?time?elapsed
???????8.659661000?seconds?user
???????0.000000000?seconds?sys
task-clock :指程序運行期間占用了8,659.24 msec的任務(wù)時鐘周期,該值高,說明程序的多數(shù)時間花費在 CPU 計算上而非 IO。
context-switches ?:表示程序運行期間進行了21次上下文切換。記錄了程序運行過程中發(fā)生了多少次進程切換。
page-faults :是指程序發(fā)生了 43次缺頁錯誤。
通過perf stat獲得了程序性能瓶頸類型后,已經(jīng)知道哪個進程需要優(yōu)化,若不知道則需要使用perf top進行進一步監(jiān)控。
精準導(dǎo)航
下一步就是對該進程進行細粒度的分析,分析在長長的程序代碼中究竟是哪幾段代碼、哪幾個函數(shù)需要修改呢?
perf?record?-e?cpu-clock?-g??./perf_test
-g
選項是告訴perf record
額外記錄函數(shù)的調(diào)用關(guān)系,-e cpu-clock
指perf record
監(jiān)控的指標為cpu周期,程序運行完之后,perf record
會生成一個名為perf.data
的文件。
可視化分析
前面通過perf record
工具獲得了某一進程的指標監(jiān)控數(shù)據(jù)perf.data
,下面就需要使用perf report
工具查看該文件。
perf?report?-i?perf.data
Self:是最后一列的符號(可以理解為函數(shù))本身所占比例。
Children :是這個符號調(diào)用的其他符號(可以理解為子函數(shù),包括直接和間接調(diào)用)占用的比例之和。
[.]:代表該調(diào)用屬于用戶態(tài),若自己監(jiān)控的進程為用戶態(tài)進程,那么這些即主要為用戶態(tài)的cpu-clock占用的數(shù)值,[k]代表屬于內(nèi)核態(tài)的調(diào)用。
我們可以看到longa符號占用了perf_test程序的99%的CPU資源。
通過方向鍵和回車,可以看到函數(shù)的調(diào)用關(guān)系,同時以匯編代碼的形式展示資源的消耗情況。
addl?$0x1,-0x8(%rbp)
cmpl?$0xf423f,-0x8(%rbp)
這兩句匯編代碼,先將0x8(%rbp)
加一,然后和一個常數(shù)進行比較,占據(jù)了63.5%的資源。
查看源代碼可以發(fā)現(xiàn)做了一次1000000次的for循環(huán)。接著以同樣的方法, 可以發(fā)現(xiàn)foo1() 也是一個潛在的調(diào)優(yōu)對象,為什么要調(diào)用 100 次那個無聊的 longa() 函數(shù)呢。
火焰圖
on-cpu火焰圖可以用于分析cpu是被哪些線程、哪些函數(shù)占用的,可以方便的找到熱點代碼便于后續(xù)分析優(yōu)化。下面我們介紹下火焰圖的生成和使用方法。
使用方法
- 準備FlameGraph工具。
git?clone?https://github.com/brendangregg/FlameGraph.git
- 用perf record采集CPU信息。
perf?record?-e?cpu-clock?-g??./perf_test
Ctrl+c結(jié)束執(zhí)行后,在當(dāng)前目錄下會生成采樣數(shù)據(jù)perf.data。
- 用perf script工具對perf.data進行解析。
perf?script?-i?perf.data?&>?perf.unfold
- 將perf.unfold中的符號進行折疊。
./stackcollapse-perf.pl?perf.unfold?&>?perf.folded
- 最后生成svg圖。
./flamegraph.pl?perf.folded?>?perf.svg
perf.svg 用瀏覽器就可以打開
火焰圖解讀
y 軸表示調(diào)用棧,每一層都是一個函數(shù)。調(diào)用棧越深,火焰就越高,頂部就是正在執(zhí)行的函數(shù),下方都是它的父函數(shù)。
x 軸表示抽樣數(shù),如果一個函數(shù)在 x 軸占據(jù)的寬度越寬,就表示它被抽到的次數(shù)多,即執(zhí)行的時間長。注意,x 軸不代表時間,而是所有的調(diào)用棧合并后,按字母順序排列的。
火焰圖就是看頂層的哪個函數(shù)占據(jù)的寬度最大。只要有"平頂"(plateaus),就表示該函數(shù)可能存在性能問題。比如圖中的longa()
函數(shù)正是問題所在點。
顏色沒有特殊含義,因為火焰圖表示的是 CPU 的繁忙程度,所以一般選擇暖色調(diào)。
互動
火焰圖是SVG 圖片,可以與用戶互動。
- 火焰的每一層都會標注函數(shù)名,鼠標懸浮時會顯示完整的函數(shù)名、抽樣抽中的次數(shù)、占據(jù)總抽樣次數(shù)的百分比。下面是一個例子。
在某一層點擊,火焰圖會水平放大,該層會占據(jù)所有寬度,顯示詳細信息。
按下 Ctrl + F 會顯示一個搜索框,用戶可以輸入關(guān)鍵詞或正則表達式,所有符合條件的函數(shù)名會高亮顯示。
其他
還有幾個火焰圖,就不介紹了,可以去看brendang regg的網(wǎng)站,簡單說一下:
off-cpu相關(guān):
- off-cpu flame graphs —— 與on-cpu互補,on-cpu只能看到運行情況,但如果某個請求運行慢,可能是被阻塞導(dǎo)致,那么就需要分析阻塞點在代碼的哪個位置,off-cpu就是畫出每個阻塞點的阻塞時間,用于分析這個問題。Wakeup flame graphs —— off-cpu的進一步,off-cpu畫出了阻塞點,但不知道阻塞是被誰喚醒的,wakeup通過分析喚醒阻塞點的線程棧,就可以知道是在哪里進行的喚醒,從而分析喚醒慢的原因。Chain graphs —— off-cpu和wakeup火焰圖畫出了阻塞點、喚醒點,但兩者之間的關(guān)系并沒有,也就是不知道喚醒點是喚醒哪個阻塞點,chain graph就是解決這個問題
其他
- Hot/Cold Flame Graphs —— 就是講on-cpu與off-cpu結(jié)合,在一張圖上顯示,這樣可以清晰的看到on和off各自的比例Differential Flame Graphs —— 對比兩個數(shù)據(jù),畫出來的圖上顯示變化情況,也就是相對之前的數(shù)據(jù),每個部分占用是變高還是變低
總結(jié)
使用perf+FlameGraph可以清晰的了解程序on-cpu運行時間占比,可以高效的了解程序性能,這種方法對我們了解程序運行過程具有重要指導(dǎo)作用。要善于使用工具幫助我們分析復(fù)雜問題。
本文參考
https://www.cnblogs.com/arnoldlu/p/6241297.html
https://www.cnblogs.com/lizhaolong/p/16437171.html
https://www.coonote.com/vim-note/perf-usage.html
https://developer.aliyun.com/article/131443
https://blog.csdn.net/qq_15437667/article/details/50724330
https://perf.wiki.kernel.org/index.php/Tutorial#Sample_analysis_with_perf_report
https://blog.csdn.net/ggsyxhhhh/article/details/104739296/
https://blog.csdn.net/runafterhit/article/details/107801860