一:背景
準備開個系列來聊一下 PerfView 這款工具,熟悉我的朋友都知道我喜歡用 WinDbg,這東西雖然很牛,但也不是萬能的,也有一些場景他解決不了或者很難解決,這時候借助一些其他的工具來輔助,是一個很不錯的主意。
很多朋友喜歡在項目中以記錄日志的方式來監控項目的流轉情況,其實 CoreCLR 也是這樣的,參考如下代碼:
void?gc_heap::fix_allocation_context?(alloc_context*?acontext,?BOOL?for_gc_p,BOOL?record_ac_p)
{dprintf?(3,?("Fixing?allocation?context?%Ix:?ptr:?%Ix,?limit:?%Ix",(size_t)acontext,(size_t)acontext->alloc_ptr,?(size_t)acontext->alloc_limit));
}void?gc_heap::background_sweep()
{//concurrent_print_time_delta?("finished?with?mark?and?start?with?sweep");concurrent_print_time_delta?("Sw");dprintf?(2,?("----?(GC%d)Background?Sweep?Phase?----",?VolatileLoad(&settings.gc_index)));//block?concurrent?allocation?for?large?objectsdprintf?(3,?("lh?state:?planning"));
}void?gc_heap::background_ephemeral_sweep()
{dprintf?(3,?("bgc?ephemeral?sweep"));
}
那這些日志會送到哪里去呢,當然是 Windows 的 ETW 了,那有什么工具可以方便提取呢?PerfView 就是這么其中一款。
這一篇我們做一個 CPU 爆高的場景下如何尋找 熱點函數
的例子,看看如何用 PerfView 去挖。
二:PerfView 尋找熱點函數
很多場景下的 CPU 高,是因為某個或者某幾個線程在高頻的執行某個方法,有可能是死循環,有可能是陷入了CPU密集型方法內,解決這個問題一個好的思路就是對 CPU 進行采樣,比如我的 12 核電腦。
0:000>?!cpuid
CP??F/M/S??Manufacturer?????MHz0??6,5,2??????????????????25921??6,5,2??????????????????25922??6,5,2??????????????????25923??6,5,2??????????????????25924??6,5,2??????????????????25925??6,5,2??????????????????25926??6,5,2??????????????????25927??6,5,2??????????????????25928??6,5,2??????????????????25929??6,5,2??????????????????2592
10??6,5,2??????????????????2592
11??6,5,2??????????????????2592
1. 如何采樣
采樣的原理就是周期性的去看下當前的 CPU 核中運行的幾個線程正在執行什么方法, 當采樣到了幾萬個或者幾十萬個樣本之后,就可以對這些采集到的方法進行分組排序來找到 topN,那些 TopN 的方法自然就是導致 CPU 爆高可能的誘因。
windbg 有一個 !running
命令可以用來顯示當前處理器中正在運行的線程。
lkd>?!runningSystem?Processors:??(0000000000000fff)Idle?Processors:??(000000000000065e)Prcbs?????????????Current?????????(pri)?Next????????????(pri)?Idle0????fffff80268a33180??ffffaf8ec9bd8080?(15)???????????????????????fffff8026b526600??................5????ffffd900e1700180??ffffaf8eca36b080?(?8)???????????????????????ffffd900e170b340??................7????ffffd900e1900180??ffffaf8ec2f18080?(?8)???????????????????????ffffd900e190b340??................8????ffffd900e1a00180??ffffd900e1a0b340?(?0)???????????????????????ffffd900e1a0b340??................11????ffffd900e1d00180??ffffaf8eb6bee080?(?8)???????????????????????ffffd900e1d0b340??................
接下來寫一個程序,讓其中一個線程無限循環,然后通過 PerfView 去找這個熱點。
internal?class?Program{static?void?Main(string[]?args){Task.Run(()?=>?Test1());????//Test1?故意死循環Task.Run(()?=>?Test2());????//Test2?是一個正常函數Console.WriteLine("我是主線程!");Console.ReadLine();}static?void?Test1(){var?i?=?10;var?b?=?true;while?(i?>?0){b?=?!b;}}static?void?Test2(){for?(int?i?=?0;?i?<?10000;?i++){var?j?=?string.Join(",",?Enumerable.Range(0,?100));}Console.WriteLine("Test執行結束");}}
2. 使用 PerfView 采樣
點擊菜單中的 Collect -> Collect
,彈出如下面板。

在這個面板中,選中如下幾項。
1)CPU Samples:
設置對 CPU 進行采樣。
2)CPU Sample Interval Msec
設置采樣的頻次是 1ms/次。
3)Max Collect Sec
設置總共采樣多少秒,這里設置為 15 秒。
4).NET Symbol Collection
用來從微軟符號服務器上拉取符號,和采樣無關哈。
上面都設置完畢后,就可以點擊 Start Collection
采集了,不出意外的話,15s 之后你就會看到如下的截圖。

接下來點擊 CPU Stacks
,在彈出的面板中選中我們的 程序
,雙擊之后就可以打開如下面板。

從圖中可以看到,當前采樣了 15622
個樣本,符合 15 * 1000
,接下來把上面的 GroupPats
默認分組給清掉,截圖如下:

從圖中可以看到當前 Test1()
方法在 15622 個樣本中占比 97.9%
,命中次數高達 15290 次,很明顯這是一個絕對的 熱點函數
,接下來就是翻源碼為什么 Test1 這么高頻?
如果你想看雞肋的 火焰圖
,可以點擊 Flame Graph
列表項。

好了,本篇就先聊這么多吧。