perf是linux上的性能分析工具,perf可以對(duì)event進(jìn)行統(tǒng)計(jì)得到event的發(fā)生次數(shù),或者對(duì)event進(jìn)行采樣,得到每次event發(fā)生時(shí)的相關(guān)數(shù)據(jù)(cpu、進(jìn)程id、運(yùn)行棧等),利用這些數(shù)據(jù)來(lái)對(duì)程序性能進(jìn)行分析。
perf可以統(tǒng)計(jì)或采樣的event有很多,如果我們要分析cpu,那么我們可以使用cpu-cycles
、cpu-clock
來(lái)衡量占用cpu的程序的分布情況,還可以通過(guò)cache-misses
、page-faults
、branch-misses
等event來(lái)分析造成cpu占用高的底層原因,確定原因后方便優(yōu)化。
如果我們要分析內(nèi)存、io、網(wǎng)絡(luò)等,也可以通過(guò)其他event來(lái)進(jìn)行分析,perf可以使用的event非常多,如果要使用perf來(lái)分析問(wèn)題,就需要了解問(wèn)題相關(guān)的event有哪些,作用是什么,這是使用perf的一個(gè)門檻。
perf工作大致可以分成三種模式:
counter
-
- 計(jì)數(shù)模式,記錄perf執(zhí)行過(guò)程中,統(tǒng)計(jì)的目標(biāo)程序或者整個(gè)系統(tǒng)范圍內(nèi),event的出現(xiàn)次數(shù)。
sampling
- 采樣模式,按照指定頻率去采樣event,記錄每次采樣時(shí),采樣事件輸出的信息(cpu、進(jìn)程id、運(yùn)行棧等)。這種方式由于每次都記錄信息,所以額外的資源消耗是比較大的,需要權(quán)衡一下采樣頻率。同時(shí)產(chǎn)生的數(shù)據(jù)量也容易很大,可能需要大量的硬盤空間。bpf 可以對(duì)對(duì)應(yīng)的event執(zhí)行用戶自己設(shè)計(jì)的代碼,也就是說(shuō)記錄的信息、執(zhí)行的操作可以由用戶定制
perf可以使用的event非常多,上圖是Brendan Gregg的文章中找到的一張圖,畫(huà)出了perf可以使用的event的結(jié)構(gòu)圖,大致可以分為以下幾類:
- Hardware Events: CPU的PMU(performance monitoring unit)觸發(fā)的事件,也叫performance monitoring counters (PMCs),例如cpu-cycles、cache missSoftware Events: 一些比較底層的軟件event,例如缺頁(yè)、timer(定時(shí))Kernel Tracepoint Events: 內(nèi)核中的tracepointUser Statically-Defined Tracing (USDT): 用戶態(tài)的tracepointDynamic Tracing: 動(dòng)態(tài)設(shè)置的event,例如使用內(nèi)核的kprobe,可以在大部分函數(shù)動(dòng)態(tài)增加eventTimed Profiling: 定時(shí)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)核上進(jìn)行開(kāi)發(fā)工作,進(jìn)入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í)候,有報(bào)錯(cuò)大概是由于平臺(tái)問(wèn)題,數(shù)據(jù)類型不匹配,導(dǎo)致所有的warning都被當(dāng)作error對(duì)待:出現(xiàn)這問(wèn)題的原因是-Werror這個(gè)gcc編譯選項(xiàng)。只要在makefile中找到包含這個(gè)-Werror選項(xiàng)的句子,將-Werror刪除,或是注釋掉就行了
編譯完成后將會(huì)在當(dāng)前目錄下生成perf可執(zhí)行文件,拷貝到設(shè)備上即可運(yùn)行。
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二進(jìn)制文件perf annotate/perf report/perf script —— 分析perf.data文件,annotate可以查看代碼,report可以統(tǒng)計(jì)分析,script是直接轉(zhuǎn)化成文本格式perf stat —— counter,統(tǒng)計(jì)event的出現(xiàn)次數(shù)perf top —— 整個(gè)系統(tǒng)的分析,類似于top命令,但可以具體到函數(shù),可以指定event
下面我們介紹一些常用的使用方法。
help
perf --help
之后可以看到perf的一級(jí)命令。
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
可以看到二級(jí)命令的幫助命令。
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
下面對(duì)一級(jí)命令作一個(gè)解釋
序號(hào) | 命令 | 作用 |
---|---|---|
1 | annotate | 解析perf record生成的perf.data文件,顯示被注釋的代碼。 |
2 | archive | 根據(jù)數(shù)據(jù)文件記錄的build-id,將所有被采樣到的elf文件打包。利用此壓縮包,可以再任何機(jī)器上分析數(shù)據(jù)文件中記錄的采樣數(shù)據(jù)。 |
3 | bench | perf中內(nèi)置的benchmark,目前包括兩套針對(duì)調(diào)度器和內(nèi)存管理子系統(tǒng)的benchmark。 |
4 | buildid-cache | 管理perf的buildid緩存,每個(gè)elf文件都有一個(gè)獨(dú)一無(wú)二的buildid。buildid被perf用來(lái)關(guān)聯(lián)性能數(shù)據(jù)與elf文件。 |
5 | buildid-list | 列出數(shù)據(jù)文件中記錄的所有buildid。 |
6 | diff | 對(duì)比兩個(gè)數(shù)據(jù)文件的差異。能夠給出每個(gè)符號(hào)(函數(shù))在熱點(diǎn)分析上的具體差異。 |
7 | evlist | 列出數(shù)據(jù)文件perf.data中所有性能事件。 |
8 | inject | 該工具讀取perf record工具記錄的事件流,并將其定向到標(biāo)準(zhǔn)輸出。在被分析代碼中的任何一點(diǎn),都可以向事件流中注入其它事件。 |
9 | kmem | 針對(duì)內(nèi)核內(nèi)存(slab)子系統(tǒng)進(jìn)行追蹤測(cè)量的工具 |
10 | kvm | 用來(lái)追蹤測(cè)試運(yùn)行在KVM虛擬機(jī)上的Guest OS。 |
11 | list | 列出當(dāng)前系統(tǒng)支持的所有性能事件。包括硬件性能事件、軟件性能事件以及檢查點(diǎn)。 |
12 | lock | 分析內(nèi)核中的鎖信息,包括鎖的爭(zhēng)用情況,等待延遲等。 |
13 | mem | 內(nèi)存存取情況 |
14 | record | 收集采樣信息,并將其記錄在數(shù)據(jù)文件中。隨后可通過(guò)其它工具對(duì)數(shù)據(jù)文件進(jìn)行分析。 |
15 | report | 讀取perf record創(chuàng)建的數(shù)據(jù)文件,并給出熱點(diǎn)分析結(jié)果。 |
16 | sched | 針對(duì)調(diào)度器子系統(tǒng)的分析工具。 |
17 | script | 執(zhí)行perl或python寫的功能擴(kuò)展腳本、生成腳本框架、讀取數(shù)據(jù)文件中的數(shù)據(jù)信息等。 |
18 | stat | 執(zhí)行某個(gè)命令,收集特定進(jìn)程的性能概況,包括CPI、Cache丟失率等。 |
19 | test | perf對(duì)當(dāng)前軟硬件平臺(tái)進(jìn)行健全性測(cè)試,可用此工具測(cè)試當(dāng)前的軟硬件平臺(tái)是否能支持perf的所有功能。 |
20 | timechart | 針對(duì)測(cè)試期間系統(tǒng)行為進(jìn)行可視化的工具 |
21 | top | 類似于linux的top命令,對(duì)系統(tǒng)性能進(jìn)行實(shí)時(shí)分析。 |
22 | trace | 關(guān)于syscall的工具。 |
23 | probe | 用于定義動(dòng)態(tài)檢查點(diǎn)。 |
全局性概況:
perf list查看當(dāng)前系統(tǒng)支持的性能事件;
perf bench對(duì)系統(tǒng)性能進(jìn)行摸底;
perf test對(duì)系統(tǒng)進(jìn)行健全性測(cè)試;
perf stat對(duì)全局性能進(jìn)行統(tǒng)計(jì);
全局細(xì)節(jié):
perf top可以實(shí)時(shí)查看當(dāng)前系統(tǒng)進(jìn)程函數(shù)占用率情況;
perf probe可以自定義動(dòng)態(tài)事件;
特定功能分析:
perf kmem針對(duì)slab子系統(tǒng)性能分析;
perf kvm針對(duì)kvm虛擬化分析;
perf lock分析鎖性能;
perf mem分析內(nèi)存slab性能;
perf sched分析內(nèi)核調(diào)度器性能;
perf trace記錄系統(tǒng)調(diào)用軌跡;
最常用功能perf record,可以系統(tǒng)全局,也可以具體到某個(gè)進(jìn)程,更甚具體到某一進(jìn)程某一事件;可宏觀,也可以很微觀。
pref record記錄信息到perf.data;
perf report生成報(bào)告;
perf diff對(duì)兩個(gè)記錄進(jìn)行diff;
perf evlist列出記錄的性能事件;
perf annotate顯示perf.data函數(shù)代碼;
perf archive將相關(guān)符號(hào)打包,方便在其它機(jī)器進(jìn)行分析;
perf script將perf.data輸出可讀性文本;
可視化工具perf timechart
perf timechart record記錄事件;
perf timechart生成output.svg文檔;
list
使用perf之前肯定要知道perf能監(jiān)控哪些性能指標(biāo)吧?那么就要使用perf list進(jìn)行查看,通常使用的指標(biāo)是cpu-clock/task-clock等,具體要根據(jù)需要來(lái)判斷。
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)控哪個(gè)變量的話,譬如使用后面的perf report工具,則加**-e 監(jiān)控指標(biāo)**,如監(jiān)控運(yùn)行l(wèi)s命令時(shí)的cpu時(shí)鐘占用:
perf?report?-e?cpu-clock?ls
event
不同內(nèi)核版本列出的結(jié)果不一樣多。不過(guò)基本是夠用的,但是無(wú)論多少,我們可以基本將其分為三類
一些事件只是單純的內(nèi)核計(jì)數(shù)器,這種情況下,他們被稱為software events。例如,上下文切換。
事件的另一個(gè)來(lái)源是處理器本身及其性能監(jiān)控單元(Performance Monitoring Unit,PMU)。它提供了一個(gè)事件列表來(lái)衡量微架構(gòu)事件,如周期數(shù)、指令異常、L1緩存未命中等。這些事件被稱為PMU硬件事件( PMU hardware events)或簡(jiǎn)稱為硬件事件(hardware events)。這些事件因每種處理器類型和型號(hào)而異。
perf_events接口還提供了一小組常見(jiàn)的硬件事件名字對(duì)象。在每個(gè)處理器上,這些事件被映射到CPU提供的實(shí)際事件上,只有映射成立即實(shí)際事件存在時(shí),這些事件才能被使用。這些事件也被稱為硬件事件(hardware events)和硬件緩存事件( hardware cache events)。
還有一些 tracepoint events ?是依賴于ftrace架構(gòu)實(shí)現(xiàn)的,這些只有在2.6.3x以上的內(nèi)核才可以使用。
一個(gè)事件可以有子事件(或 unit masks)。在某些處理器上,對(duì)于某些事件,可以將 unit masks
組合 使用并測(cè)量任一子事件發(fā)生的時(shí)間。
/sys/kernel/debug/tracing/available_events
,可查看當(dāng)前系統(tǒng)的所有tracepoint分成了幾大類:
ext4:文件系統(tǒng)的tracepoint?events,如果是其它文件系統(tǒng),比如XFS,也有對(duì)應(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)用
屬性
用戶如果想要使用高精度采樣,需要在指定性能事件時(shí),在事件名后添加后綴:p
或:pp
。Perf在采樣精度上定義了4個(gè)級(jí)別,如下所示。
0?:無(wú)精度保證
1?:采樣指令與觸發(fā)性能事件的指令之間的偏差為常數(shù)(:p)
2?:需要盡量保證采樣指令與觸發(fā)性能事件的指令之間的偏差為0(:pp)
3?:保證采樣指令與觸發(fā)性能事件的指令之間的偏差必須為0(:ppp)
目前的X86處理器,包括Intel處理器與AMD處理器均僅能實(shí)現(xiàn)前 3 個(gè)精度級(jí)別。
除了精度級(jí)別以外,性能事件還具有其它幾個(gè)屬性,均可以通過(guò)event:X
的方式予以指定。
u?僅統(tǒng)計(jì)用戶空間程序觸發(fā)的性能事件
k?僅統(tǒng)計(jì)內(nèi)核觸發(fā)的性能事件
h?僅統(tǒng)計(jì)Hypervisor觸發(fā)的性能事件
G?在KVM虛擬機(jī)中,僅統(tǒng)計(jì)Guest系統(tǒng)觸發(fā)的性能事件
H?僅統(tǒng)計(jì)?Host?系統(tǒng)觸發(fā)的性能事件
p?精度級(jí)別
stat
perf stat
分析系統(tǒng)/進(jìn)程的整體性能概況。
命令解析
-a,?--all-cpus??采集所有CPU的信息
-A,?--no-aggr???不要在system-wide(-a)模式下匯集所有CPU的計(jì)數(shù)信息
-B,?--big-num???保留三位小數(shù)
-C,?--cpu?<cpu>??指定某個(gè)cpu
-D,?--delay?<n>??在測(cè)試程序開(kāi)始后,在測(cè)量前等等?n?ms
-d,?--detailed???打印更詳細(xì)的統(tǒng)計(jì)數(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>??事件選擇??梢詤⒖紁erf?list。
-G,?--cgroup?<name>??僅在name為cgroup時(shí)有效。
-g,?--group????????將計(jì)數(shù)器放到一個(gè)計(jì)數(shù)組中???
-I,?--interval-print?<n>??每n毫秒打印計(jì)數(shù)增量(最小值:10ms).在某些情況下,開(kāi)銷可能很高,例如小于100毫秒的間隔。
-i,?--no-inherit??禁止子任務(wù)繼承父任務(wù)的性能計(jì)數(shù)器。
-M,?--metrics?<metric/metric?group?list>??監(jiān)視指定的?metrics?或???metric?groups,以逗號(hào)分隔。
-n,?--null???僅輸出目標(biāo)程序的執(zhí)行時(shí)間,而不開(kāi)啟任何性能計(jì)數(shù)器。
-o,?--output?<file>???輸出文件的名字
-p,?--pid?<pid>??????指定待分析的進(jìn)程id
-r,?--repeat?<n>??重復(fù)執(zhí)行?n?次目標(biāo)程序,并給出性能指標(biāo)在n?次執(zhí)行中的變化范圍。
-S,?--sync????????在開(kāi)始前調(diào)用sync()?
-t,?--tid?<tid>???指定待分析的線程id
-T,?--transaction????如果支持,打印事務(wù)執(zhí)行的統(tǒng)計(jì)數(shù)據(jù)。
-v,?--verbose??????顯示詳細(xì)信息
-x,?--field-separator?<separator>???使用CSV樣式的輸出打印計(jì)數(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
如果沒(méi)有指定那個(gè)事件,perf stat
將收集上面列出的常見(jiàn)事件。比如,上下文切換,CPU遷移次數(shù),缺頁(yè)故障等。
task‐clock:事件表示目標(biāo)任務(wù)真正占用處理器的時(shí)間,單位是毫秒。也稱任務(wù)執(zhí)行時(shí)間。CPUs utilized = task-clock / time elapsed,CPU的占用率。
context-switches:程序在運(yùn)行過(guò)程中上下文的切換次數(shù)。
CPU-migrations:程序在運(yùn)行過(guò)程中發(fā)生的處理器遷移次數(shù)。Linux為了維持多個(gè)處理器的負(fù)載均衡,在特定條件下會(huì)將某個(gè)任務(wù)從一個(gè)CPU遷移到另一個(gè)CPU。
CPU遷移和上下文切換:發(fā)生上下文切換不一定會(huì)發(fā)生CPU遷移,而發(fā)生CPU遷移時(shí)肯定會(huì)發(fā)生上下文切換。發(fā)生上下文切換有可能只是把上下文從當(dāng)前CPU中換出,下一次調(diào)度器還是將進(jìn)程安排在這個(gè)CPU上執(zhí)行。
page-faults:缺頁(yè)異常的次數(shù)。當(dāng)應(yīng)用程序請(qǐng)求的頁(yè)面尚未建立、請(qǐng)求的頁(yè)面不在內(nèi)存中,或者請(qǐng)求的頁(yè)面雖然在內(nèi)存中,但物理地址和虛擬地址的映射關(guān)系尚未建立時(shí),都會(huì)觸發(fā)一次缺頁(yè)異常。另外TLB不命中,頁(yè)面訪問(wèn)權(quán)限不匹配等情況也會(huì)觸發(fā)缺頁(yè)異常。
cycles:消耗的處理器周期數(shù)。如果把被ls使用的cpu cycles看成是一個(gè)處理器的,那么它的主頻為2.486GHz??梢杂胏ycles / task-clock算出。
stalled-cycles-frontend:指令讀取或解碼的質(zhì)量步驟,未能按理想狀態(tài)發(fā)揮并行左右,發(fā)生停滯的時(shí)鐘周期。
stalled-cycles-backend:指令執(zhí)行步驟,發(fā)生停滯的時(shí)鐘周期。
instructions:執(zhí)行了多少條指令。IPC為平均每個(gè)cpu cycle執(zhí)行了多少條指令。
branches:遇到的分支指令數(shù)。branch-misses是預(yù)測(cè)錯(cuò)誤的分支指令數(shù)。
branch‐misses:是預(yù)測(cè)錯(cuò)誤的分支指令數(shù)。
XXX seconds time elapsed:系程序持續(xù)時(shí)間
每次運(yùn)行性能工具時(shí),可以測(cè)量一個(gè)或多個(gè)事件。事件使用其符號(hào)名稱,后跟可選的單元掩碼和修飾符來(lái)指定。事件名稱、單元掩碼和修飾符不區(qū)分大小寫。
perf?stat?-e?cpu-clock?dd?if=/dev/zero?of=/dev/null?count=100000
默認(rèn)情況下,會(huì)在用戶和內(nèi)核級(jí)別測(cè)量事件。如果僅在用戶級(jí)別進(jìn)行測(cè)量,需要傳遞一個(gè)修飾符:
perf?stat?-e?cpu-clock:u?dd?if=/dev/zero?of=/dev/null?count=100000
如果即在用戶態(tài)測(cè)量,又在內(nèi)核態(tài)測(cè)量,則可以同時(shí)傳遞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í)間,不顯示性能計(jì)數(shù)器
?perf?stat?-n?ls?>?/dev/null????
record
記錄一段時(shí)間內(nèi)系統(tǒng)/進(jìn)程的性能時(shí)間。
命令解析
-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>??????在測(cè)試程序開(kāi)始后,在測(cè)量前等等?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毫秒打印計(jì)數(shù)增量(最小值:10ms).在某些情況下,開(kāi)銷可能很高,例如小于100毫秒的間隔。
-i,?--no-inherit?????禁止子任務(wù)繼承父任務(wù)的性能計(jì)數(shù)器。
-j,?--branch-filter?<branch?filter?mask>??啟用分支堆棧采樣。每個(gè)樣本捕獲一系列連續(xù)的采樣分支。
-k,?--clockid?<clockid>?設(shè)置用于perf_event_type中各種時(shí)間字段的時(shí)鐘id記錄。請(qǐng)參見(jiàn)clock_gettime()。
-m,?--mmap-pages?<pages[,pages]>?mmap數(shù)據(jù)頁(yè)面和輔助區(qū)域跟蹤mmap頁(yè)面的數(shù)量
-N,?--no-buildid-cache?不要更新buildid緩存
-n,?--no-samples?????不要采樣
-o,?--output?<file>??指定輸出文件,默認(rèn)為perf.data
-P,?--period?????????采樣周期
-p,?--pid?<pid>?????指定進(jìn)程id
-q,?--quiet?????????不打印任何信息
-R,?--raw-samples???從所有打開(kāi)的計(jì)數(shù)器收集原始樣本記錄
-r,?--realtime?<n>???以?SCHED_FIFO?優(yōu)先級(jí)實(shí)時(shí)收集數(shù)據(jù)
-S,?--snapshot[=<opts>]?快照模式
-s,?--stat?????????記錄每個(gè)線程的事件計(jì)數(shù),使用perf?report?-T?查看可選值
-t,?--tid?<tid>????在現(xiàn)有線程ID上記錄事件(逗號(hào)分隔列表)
-T,?--timestamp?????記錄采樣時(shí)間戳。使用?perf?report?-D查看更詳細(xì)信息
-u,?--uid?<user>???指定用戶id
-W,?--weight???????啟用加權(quán)采樣
舉例
記錄執(zhí)行l(wèi)s時(shí)的性能數(shù)據(jù)
perf?record?ls?-g????
記錄執(zhí)行l(wèi)s時(shí)的系統(tǒng)調(diào)用,可以知道哪些系統(tǒng)調(diào)用最頻繁
perf?record?-e?syscalls:sys_enter?ls???
report
讀取perf record
生成的數(shù)據(jù)文件,并顯示分析數(shù)據(jù)。
-p<regex>:用指定正則表達(dá)式過(guò)濾調(diào)用函數(shù)
-e?<event>:指定性能事件(可以是多個(gè),用,分隔列表)
-p?<pid>:指定待分析進(jìn)程的?pid(可以是多個(gè),用,分隔列表)
-t?<tid>:指定待分析線程的?tid(可以是多個(gè),用,分隔列表)
-u?<uid>:指定收集的用戶數(shù)據(jù),uid為名稱或數(shù)字
-a:從所有?CPU?收集系統(tǒng)數(shù)據(jù)
-C?<cpu-list>:只統(tǒng)計(jì)指定?CPU?列表的數(shù)據(jù),如:0,1,3或1-2
-r?<RT?priority>:perf?程序以SCHED_FIFO實(shí)時(shí)優(yōu)先級(jí)RT?priority運(yùn)行這里填入的數(shù)值越大,進(jìn)程優(yōu)先級(jí)越高(即?nice?值越?。?
-c?<count>:?事件每發(fā)生?count?次采一次樣
-F?<n>:每秒采樣?n?次
-o?<output.data>:指定輸出文件output.data,默認(rèn)輸出到perf.data
-i:輸入的數(shù)據(jù)文件
-v:顯示每個(gè)符號(hào)的地址
-d?<dos>:只顯示指定dos的符號(hào)
-S:只考慮指定符號(hào)
-U:只顯示已解析的符號(hào)
-g[type,min,order]:顯示調(diào)用關(guān)系,具體等同于perf?top命令中的-g
-c:只顯示指定cpu采樣信息
-M:以指定匯編指令風(fēng)格顯示
–source:以匯編和source的形式進(jìn)行顯示
舉例
記錄執(zhí)行l(wèi)s時(shí)的性能數(shù)據(jù)
perf?record?ls?-g????
顯示
perf?report?-i?perf.data
overhead:cpu-clock占用百分比
command:當(dāng)前執(zhí)行的命令
shared object :依賴的共享庫(kù)
symbol:當(dāng)前占用比下對(duì)應(yīng)的符號(hào)
[.]代表該調(diào)用屬于用戶態(tài),若自己監(jiān)控的進(jìn)程為用戶態(tài)進(jìn)程,那么這些即主要為用戶態(tài)的cpu-clock占用的數(shù)值,[k]代表屬于內(nèi)核態(tài)的調(diào)用。
也許有的人會(huì)奇怪為什么自己完全是一個(gè)用戶態(tài)的程序?yàn)槭裁催€會(huì)統(tǒng)計(jì)到內(nèi)核態(tài)的指標(biāo)?
一是用戶態(tài)程序運(yùn)行時(shí)會(huì)受到內(nèi)核態(tài)的影響,若內(nèi)核態(tài)對(duì)用戶態(tài)影響較大,統(tǒng)計(jì)內(nèi)核態(tài)信息可以了解到是內(nèi)核中的哪些行為導(dǎo)致對(duì)用戶態(tài)產(chǎn)生影響;二則是有些用戶態(tài)程序也需要依賴內(nèi)核的某些操作,譬如I/O操作
annotate
perf annotate提供指令級(jí)別的record文件定位。使用調(diào)試信息-g編譯的文件能夠顯示匯編和本身源碼信息。
但要注意, annotate命令并不能夠解析內(nèi)核image中的符號(hào),必須要傳遞未壓縮的內(nèi)核image給annotate才能正常的解析內(nèi)核符號(hào),比如:
perf?annotate?-k?/tmp/vmlinux?-d?symbol
命令解析
-i:輸入文件名
-d:只考慮這些DSO中的符號(hào)
-f:強(qiáng)制讀取
-D:轉(zhuǎn)儲(chǔ)ASCII中的原始跟蹤
-k:?vmlinux路徑名
-m:加載模塊符號(hào)表.僅與-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信息,保留符號(hào)表等;-O0表示不進(jìn)行優(yōu)化處理
統(tǒng)計(jì)命令:
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
實(shí)時(shí)顯示系統(tǒng)/進(jìn)程的性能統(tǒng)計(jì)信息
命令解析
-e:指定性能事件
-a:顯示在所有CPU上的性能統(tǒng)計(jì)信息
-d:界面的刷新周期,默認(rèn)為2s。
-C:顯示在指定CPU上的性能統(tǒng)計(jì)信息
-p:指定進(jìn)程PID
-t:指定線程TID
-K:隱藏內(nèi)核統(tǒng)計(jì)信息
-k:帶符號(hào)表的內(nèi)核映像所在的路徑。
-U:隱藏用戶空間的統(tǒng)計(jì)信息
-s:指定待解析的符號(hào)信息
-g:得到函數(shù)的調(diào)用關(guān)系圖。
‘‐G’?or‘‐‐call‐graph’?<output_type,min_percent,call_order>
graph:?使用調(diào)用樹(shù),將每條調(diào)用路徑進(jìn)一步折疊。這種顯示方式更加直觀。
每條調(diào)用路徑的采樣率為絕對(duì)值。也就是該條路徑占整個(gè)采樣域的比率。
fractal
默認(rèn)選項(xiàng)。類似與?graph,但是每條路徑前的采樣率為相對(duì)值。
flat
不折疊各條調(diào)用
選項(xiàng)?call_order?用以設(shè)定調(diào)用圖譜的顯示順序,該選項(xiàng)有?2個(gè)取值,分別是
callee?與caller。
將該選項(xiàng)設(shè)為callee?時(shí),perf按照被調(diào)用的順序顯示調(diào)用圖譜,上層函數(shù)被下層函數(shù)所調(diào)用。
該選項(xiàng)被設(shè)為caller?時(shí),按照調(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?
第一列:符號(hào)引發(fā)的性能事件的比例,默認(rèn)指占用的cpu周期比例。
第二列:符號(hào)所在的DSO(Dynamic Shared Object),可以是應(yīng)用程序、內(nèi)核、動(dòng)態(tài)鏈接庫(kù)、模塊。
第三列:DSO的類型。[.]
表示此符號(hào)屬于用戶態(tài)的ELF文件,包括可執(zhí)行文件與動(dòng)態(tài)鏈接庫(kù))。[k]
表述此符號(hào)屬于內(nèi)核或模塊。
第四列:符號(hào)名。有些符號(hào)不能解析為函數(shù)名,只能用地址表示。
bench
perf bench作為benchmark工具的通用框架,包含sched/mem/numa/futex等子系統(tǒng),all可以指定所有。
perf bench可用于評(píng)估系統(tǒng)sched/mem等特定性能。
命令解析
-f,?--format?<default|simple>?選擇輸出格式,simple模式下只顯示測(cè)量時(shí)間
-r,?--repeat?<n>??????指定重復(fù)運(yùn)行的次數(shù)
子系統(tǒng)包括
sched:調(diào)度器和IPC機(jī)制。包含messaging和pipe兩個(gè)功能。
mem:內(nèi)存存取性能。包含memcpy和memset兩個(gè)功能。
numa:NUMA架構(gòu)的調(diào)度和內(nèi)存處理性能。包含mem功能。
futex:futex壓力測(cè)試。包含hash/wake/wake-parallel/requeue/lock-pi功能。
all:所有bench測(cè)試的集合
舉例
sched messaging
評(píng)估進(jìn)程調(diào)度和核間通信,sched message
是從經(jīng)典的測(cè)試程序 hackbench
移植而來(lái),用來(lái)衡量調(diào)度器的性能,overhead
以及可擴(kuò)展性。
該 benchmark
啟動(dòng) N 個(gè) reader/sender
進(jìn)程或線程對(duì),通過(guò) IPC(socket 或者 pipe) 進(jìn)行并發(fā)的讀寫。一般人們將 N 不斷加大來(lái)衡量調(diào)度器的可擴(kuò)展性。
sched message
的用法及用途和 hackbench
一樣,可以通過(guò)修改參數(shù)進(jìn)行不同目的測(cè)試:
-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()
對(duì)測(cè)試影響:
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#?
可見(jiàn)socketpair()性能要明顯低于pipe()。
使用perf分析完整例子
下面我們舉一個(gè)具體的例子來(lái)看下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
通過(guò)概括精簡(jiǎn)的方式提供被調(diào)試程序運(yùn)行的整體情況和匯總數(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 :指程序運(yùn)行期間占用了8,659.24 msec的任務(wù)時(shí)鐘周期,該值高,說(shuō)明程序的多數(shù)時(shí)間花費(fèi)在 CPU 計(jì)算上而非 IO。
context-switches ?:表示程序運(yùn)行期間進(jìn)行了21次上下文切換。記錄了程序運(yùn)行過(guò)程中發(fā)生了多少次進(jìn)程切換。
page-faults :是指程序發(fā)生了 43次缺頁(yè)錯(cuò)誤。
通過(guò)perf stat獲得了程序性能瓶頸類型后,已經(jīng)知道哪個(gè)進(jìn)程需要優(yōu)化,若不知道則需要使用perf top進(jìn)行進(jìn)一步監(jiān)控。
精準(zhǔn)導(dǎo)航
下一步就是對(duì)該進(jìn)程進(jìn)行細(xì)粒度的分析,分析在長(zhǎng)長(zhǎng)的程序代碼中究竟是哪幾段代碼、哪幾個(gè)函數(shù)需要修改呢?
perf?record?-e?cpu-clock?-g??./perf_test
-g
選項(xiàng)是告訴perf record
額外記錄函數(shù)的調(diào)用關(guān)系,-e cpu-clock
指perf record
監(jiān)控的指標(biāo)為cpu周期,程序運(yùn)行完之后,perf record
會(huì)生成一個(gè)名為perf.data
的文件。
可視化分析
前面通過(guò)perf record
工具獲得了某一進(jìn)程的指標(biāo)監(jiān)控?cái)?shù)據(jù)perf.data
,下面就需要使用perf report
工具查看該文件。
perf?report?-i?perf.data
Self:是最后一列的符號(hào)(可以理解為函數(shù))本身所占比例。
Children :是這個(gè)符號(hào)調(diào)用的其他符號(hào)(可以理解為子函數(shù),包括直接和間接調(diào)用)占用的比例之和。
[.]:代表該調(diào)用屬于用戶態(tài),若自己監(jiān)控的進(jìn)程為用戶態(tài)進(jìn)程,那么這些即主要為用戶態(tài)的cpu-clock占用的數(shù)值,[k]代表屬于內(nèi)核態(tài)的調(diào)用。
我們可以看到longa符號(hào)占用了perf_test程序的99%的CPU資源。
通過(guò)方向鍵和回車,可以看到函數(shù)的調(diào)用關(guān)系,同時(shí)以匯編代碼的形式展示資源的消耗情況。
addl?$0x1,-0x8(%rbp)
cmpl?$0xf423f,-0x8(%rbp)
這兩句匯編代碼,先將0x8(%rbp)
加一,然后和一個(gè)常數(shù)進(jìn)行比較,占據(jù)了63.5%的資源。
查看源代碼可以發(fā)現(xiàn)做了一次1000000次的for循環(huán)。接著以同樣的方法, 可以發(fā)現(xiàn)foo1() 也是一個(gè)潛在的調(diào)優(yōu)對(duì)象,為什么要調(diào)用 100 次那個(gè)無(wú)聊的 longa() 函數(shù)呢。
火焰圖
on-cpu火焰圖可以用于分析cpu是被哪些線程、哪些函數(shù)占用的,可以方便的找到熱點(diǎn)代碼便于后續(xù)分析優(yōu)化。下面我們介紹下火焰圖的生成和使用方法。
使用方法
- 準(zhǔn)備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)前目錄下會(huì)生成采樣數(shù)據(jù)perf.data。
- 用perf script工具對(duì)perf.data進(jìn)行解析。
perf?script?-i?perf.data?&>?perf.unfold
- 將perf.unfold中的符號(hào)進(jìn)行折疊。
./stackcollapse-perf.pl?perf.unfold?&>?perf.folded
- 最后生成svg圖。
./flamegraph.pl?perf.folded?>?perf.svg
perf.svg 用瀏覽器就可以打開(kāi)
火焰圖解讀
y 軸表示調(diào)用棧,每一層都是一個(gè)函數(shù)。調(diào)用棧越深,火焰就越高,頂部就是正在執(zhí)行的函數(shù),下方都是它的父函數(shù)。
x 軸表示抽樣數(shù),如果一個(gè)函數(shù)在 x 軸占據(jù)的寬度越寬,就表示它被抽到的次數(shù)多,即執(zhí)行的時(shí)間長(zhǎng)。注意,x 軸不代表時(shí)間,而是所有的調(diào)用棧合并后,按字母順序排列的。
火焰圖就是看頂層的哪個(gè)函數(shù)占據(jù)的寬度最大。只要有"平頂"(plateaus),就表示該函數(shù)可能存在性能問(wèn)題。比如圖中的longa()
函數(shù)正是問(wèn)題所在點(diǎn)。
顏色沒(méi)有特殊含義,因?yàn)榛鹧鎴D表示的是 CPU 的繁忙程度,所以一般選擇暖色調(diào)。
互動(dòng)
火焰圖是SVG 圖片,可以與用戶互動(dòng)。
- 火焰的每一層都會(huì)標(biāo)注函數(shù)名,鼠標(biāo)懸浮時(shí)會(huì)顯示完整的函數(shù)名、抽樣抽中的次數(shù)、占據(jù)總抽樣次數(shù)的百分比。下面是一個(gè)例子。
在某一層點(diǎn)擊,火焰圖會(huì)水平放大,該層會(huì)占據(jù)所有寬度,顯示詳細(xì)信息。
按下 Ctrl + F 會(huì)顯示一個(gè)搜索框,用戶可以輸入關(guān)鍵詞或正則表達(dá)式,所有符合條件的函數(shù)名會(huì)高亮顯示。
其他
還有幾個(gè)火焰圖,就不介紹了,可以去看brendang regg的網(wǎng)站,簡(jiǎn)單說(shuō)一下:
off-cpu相關(guān):
- off-cpu flame graphs —— 與on-cpu互補(bǔ),on-cpu只能看到運(yùn)行情況,但如果某個(gè)請(qǐng)求運(yùn)行慢,可能是被阻塞導(dǎo)致,那么就需要分析阻塞點(diǎn)在代碼的哪個(gè)位置,off-cpu就是畫(huà)出每個(gè)阻塞點(diǎn)的阻塞時(shí)間,用于分析這個(gè)問(wèn)題。Wakeup flame graphs —— off-cpu的進(jìn)一步,off-cpu畫(huà)出了阻塞點(diǎn),但不知道阻塞是被誰(shuí)喚醒的,wakeup通過(guò)分析喚醒阻塞點(diǎn)的線程棧,就可以知道是在哪里進(jìn)行的喚醒,從而分析喚醒慢的原因。Chain graphs —— off-cpu和wakeup火焰圖畫(huà)出了阻塞點(diǎn)、喚醒點(diǎn),但兩者之間的關(guān)系并沒(méi)有,也就是不知道喚醒點(diǎn)是喚醒哪個(gè)阻塞點(diǎn),chain graph就是解決這個(gè)問(wèn)題
其他
- Hot/Cold Flame Graphs —— 就是講on-cpu與off-cpu結(jié)合,在一張圖上顯示,這樣可以清晰的看到on和off各自的比例Differential Flame Graphs —— 對(duì)比兩個(gè)數(shù)據(jù),畫(huà)出來(lái)的圖上顯示變化情況,也就是相對(duì)之前的數(shù)據(jù),每個(gè)部分占用是變高還是變低
總結(jié)
使用perf+FlameGraph可以清晰的了解程序on-cpu運(yùn)行時(shí)間占比,可以高效的了解程序性能,這種方法對(duì)我們了解程序運(yùn)行過(guò)程具有重要指導(dǎo)作用。要善于使用工具幫助我們分析復(fù)雜問(wèn)題。
本文參考
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