在內核方面,人們的興趣五花八門,有些內核開發人員熱衷於尋找整個內核中的熱點代碼;另一些則只關注某一個主題,比如 slab 分配器,對於其餘部分則不感興趣。對這些人而言,perf 的一些奇怪用法更受歡迎。當然,諸如perf top,perf stat, perf record 等也是內核調優的基本手段,但用法和一樣,無需重述。
2.使用 tracepoint
當 perf 根據 tick 時間點進行採樣後,人們便能夠得到內核代碼中的 hot spot。那什麼時候需要使用 tracepoint 來採樣呢?
使用 tracepoint 的基本需求是對內核的運行時行爲的關心,如前所述,有些內核開發人員需要專注於特定的子系統,比如內存管理模塊。
這便需要統計相關內核函數的運行情況。另外,內核行爲對應用程序性能的影響也是不容忽視的:
以之前的遺憾爲例,假如時光倒流,我想我要做的是統計該應用程序運行期間究竟發生了多少次系統調用。在哪裏發生的?
下面我用 ls 命令來演示 sys_enter 這個 tracepoint 的使用:
[root@ovispoly /]# perf stat -e raw_syscalls:sys_enter ls
bin dbg etc lib media opt root selinux sys usr
boot dev home lost+found mnt proc sbin srv tmp var
Performance counter stats for 'ls':
101 raw_syscalls:sys_enter
0.003434730 seconds time elapsed
[root@ovispoly /]# perf record -e raw_syscalls:sys_enter ls
[root@ovispoly /]# perf report
Failed to open .lib/ld-2.12.so, continuing without symbols
# Samples: 70
#
# Overhead Command Shared Object Symbol
# ........ ............... ............... ......
#
97.14% ls ld-2.12.so [.] 0x0000000001629d
2.86% ls [vdso] [.] 0x00000000421424
#
# (For a higher level overview, try: perf report --sort comm,dso)
#
這個報告詳細說明了在 ls 運行期間發生了多少次系統調用 ( 上例中有 101 次 ),多數系統調用都發生在哪些地方 (97% 都發
生在 ld-2.12.so 中 )。
有了這個報告,或許我能夠發現更多可以調優的地方。比如函數 foo() 中發生了過多的系統調用,那麼我就可以思考是否有辦法減少其中有些不必要的系統調用。
您可能會說 strace 也可以做同樣事情啊,的確,統計系統調用這件事完全可以用 strace 完成,但 perf 還可以幹些別的,
您所需要的就是修改 -e 選項後的字符串。
羅列 tracepoint 實在是不太地道,本文當然不會這麼做。但學習每一個 tracepoint 是有意義的,類似背單詞之於學習英語
一樣,是一項緩慢痛苦卻不得不做的事情。
3.perf probe
tracepoint 是靜態檢查點,意思是一旦它在哪裏,便一直在那裏了,您想讓它移動一步也是不可能的。內核代碼有多少行?我不知道,100 萬行是至少的吧,但目前 tracepoint 有多少呢?我最大膽的想象是不超過 1000 個。所以能夠動態地在想查看的地方插入動態監測點的意義是不言而喻的。
Perf 並不是第一個提供這個功能的軟件,systemTap 早就實現了。但假若您不選擇 RedHat 的發行版的話,安裝 systemTap 並不是件輕鬆愉快的事情。perf 是內核代碼包的一部分,所以使用和維護都非常方便。
我使用的 Linux 版本爲 2.6.33。因此您自己做實驗時命令參數有可能不同。
[root@ovispoly perftest]# perf probe schedule:12 cpu
Added new event:
probe:schedule (on schedule+52 with cpu)
You can now use it on all perf tools, such as:
perf record -e probe:schedule -a sleep 1
[root@ovispoly perftest]# perf record -e probe:schedule -a sleep 1
Error, output file perf.data exists, use -A to append or -f to overwrite.
[root@ovispoly perftest]# perf record -f -e probe:schedule -a sleep 1
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.270 MB perf.data (~11811 samples) ]
[root@ovispoly perftest]# perf report
# Samples: 40
#
# Overhead Command Shared Object Symbol
# ........ ............... ................. ......
#
57.50% init 0 [k] 0000000000000000
30.00% firefox [vdso] [.] 0x0000000029c424
5.00% sleep [vdso] [.] 0x00000000ca7424
5.00% perf.2.6.33.3-8 [vdso] [.] 0x00000000ca7424
2.50% ksoftirqd/0 [kernel] [k] 0000000000000000
#
# (For a higher level overview, try: perf report --sort comm,dso)
#
上例利用 probe 命令在內核函數 schedule() 的第 12 行處加入了一個動態 probe 點,和 tracepoint 的功能一樣,內核一旦運
行到該 probe 點時,便會通知 perf。可以理解爲動態增加了一個新的 tracepoint。
此後便可以用 record 命令的 -e 選項選擇該 probe 點,最後用 perf report 查看報表。如何解讀該報表便是見仁見智了,既然
您在 shcedule() 的第 12 行加入了 probe 點,想必您知道自己爲什麼要統計它吧?
4.perf lock
鎖是內核同步的方法,一旦加了鎖,其他準備加鎖的內核執行路徑就必須等待,降低了並行。因此對於鎖進行專門分析應該是調優的一項重要工作。
我運行 perf lock 後得到如下輸出:
Name acquired contended total wait (ns) max wait (ns) min
&md->map_lock 396 0 0 0
&(&mm->page_tabl... 309 0 0 0
&(&tty->buf.lock... 218 0 0 0
&ctx->lock 185 0 0 0
key 178 0 0 0
&ctx->lock 132 0 0 0
&tty->output_loc... 126 0 0 0
。。。
&(&object->lock)... 1 0 0 0
&(&object->lock)... 0 0 0 0
&(&object->lock)... 0 0 0 0
&p->cred_guard_m... 0 0 0 0
=== output for debug===
bad: 28, total: 664
bad rate: 4.216867 %
histogram of events caused bad sequence
acquire: 8
acquired: 0
contended: 0
release: 20
對該報表的一些解釋如下:
“Name”: 鎖的名字,比如 md->map_lock,即定義在 dm.c 結構 mapped_device 中的讀寫鎖。
“acquired”: 該鎖被直接獲得的次數,即沒有其他內核路徑擁有該鎖的情況下得到該鎖的次數。
“contended”衝突的次數,即在準備獲得該鎖的時候已經被其他人所擁有的情況的出現次數。
“total wait”:爲了獲得該鎖,總共的等待時間。
“max wait”:爲了獲得該鎖,最大的等待時間。
“min wait”:爲了獲得該鎖,最小的等待時間。
目前 perf lock 還處於比較初級的階段,我想在後續的內核版本中,還應該會有較大的變化,因此當您開始使用 perf lock 時,
恐怕已經和本文這裏描述的有所不同了。不過我又一次想說的是,命令語法和輸出並不是最重要的,重要的是瞭解什麼時候我們需要用這個工具,以及它能幫我們解決怎樣的問題。
5.perf kmem [<options>] {record|stat}
Perf Kmem 專門收集內核 slab 分配器的相關事件。比如內存分配,釋放等。可以用來研究程序在哪裏分配了大量內存,或者在什麼地方產生碎片之類的和內存管理相關的問題。
Perf kmem 和 perf lock 實際上都是 perf tracepoint 的特例,您可以先用用 Perf record – e kmem:* 或者 perf record
– e lock:* 來完成同樣的功能。但重要的是,這些工具在內部對原始數據進行了彙總和分析,因而能夠產生信息更加明確更加有用的統計報表。
perf kmem 的輸出結果如下:
[root@ovispoly perf]# perf kmem --alloc -l 10 --caller stat
---------------------------------------------------------------------------
Callsite | Total_alloc/Per | Total_req/Per | Hit | Ping-pong| Frag
---------------------------------------------------------------------------
perf_mmap+1a8 | 1024/1024 | 572/572|1 | 0 | 44.141%
seq_open+15| 12384/96 | 8772/68 |129 | 0 | 29.167%
do_maps_open+0| 1008/16 | 756/12 |63 | 0 | 25.000%
...| ... | ...| ... | ... | ...
__split_vma+50| 88/88 | 88/88 | 1 | 0 | 0.000%
---------------------------------------------------------------------------
Alloc Ptr | Total_alloc/Per | Total_req/Per | Hit |Ping-pong| Frag
---------------------------------------------------------------------------
0xd15d4600|64/64 | 33/33 1 | 0 | 48.438%
0xc461e000|1024/1024 | 572/572 |1 | 0 | 44.141%
0xd15d44c0| 64/64 | 38/38 |1 | 0 | 40.625%
... | ... | ... | ... | ... | ...
---------------------------------------------------------------------------
SUMMARY
=======
Total bytes requested: 10487021
Total bytes allocated: 10730448
Total bytes wasted on internal fragmentation: 243427
Internal fragmentation: 2.268563%
Cross CPU allocations: 0/246458
該報告有三個部分:根據 Callsite 顯示的部分,所謂 Callsite 即內核代碼中調用 kmalloc 和 kfree 的地方。比如上圖中的函數 perf_mmap,Hit 欄爲 1,表示該函數在 record 期間一共調用了 kmalloc 一次,假如如第三行所示數字爲 653,則表示函數
sock_alloc_send_pskb 共有 653 次調用 kmalloc 分配內存。
對於第一行 Total_alloc/Per 顯示爲 1024/1024,第一個值 1024 表示函數 perf_mmap 總共分配的內存大小,Per 表示平均值。
比較有趣的兩個參數是 Ping-pong 和 Frag。Frag 比較容易理解,即內部碎片。雖然相對於 Buddy System,Slab 正是要解決內部碎片問題,但 slab 依然存在內部碎片,比如一個 cache 的大小爲 1024,但需要分配的數據結構大小爲 1022,那麼有 2 個字節成爲碎片。Frag 即碎片的比例。
Ping-pong 是一種現象,在多 CPU 系統中,多個 CPU 共享的內存會出現”乒乓現象”。一個 CPU 分配內存,其他 CPU 可能訪問該內存對象,也可能最終由另外一個 CPU 釋放該內存對象。而在多 CPU 系統中,L1 cache 是 per CPU 的,CPU2 修改了內存,那麼其他的 CPU 的 cache 都必須更新,這對於性能是一個損失。Perf kmem 在 kfree 事件中判斷 CPU 號,如果和 kmalloc 時的不同,則視爲一次 ping-pong,理想的情況下 ping-pone 越小越好。Ibm developerworks 上有一篇講述 oprofile 的文章,其中關於 cache 的調優可以作爲很好的參考資料。