一:背景 準備開個系列來聊一下 PerfView 這款工具,熟悉我的朋友都知道我喜歡用 WinDbg,這東西雖然很牛,但也不是萬能的,也有一些場景他解決不了或者很難解決,這時候藉助一些其他的工具來輔助,是一個很不錯的主意。 很多朋友喜歡在項目中以記錄日誌的方式來監控項目的流轉情況,其實 CoreCL ...
一:背景
準備開個系列來聊一下 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 objects
dprintf (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 MHz
0 6,5,2 2592
1 6,5,2 2592
2 6,5,2 2592
3 6,5,2 2592
4 6,5,2 2592
5 6,5,2 2592
6 6,5,2 2592
7 6,5,2 2592
8 6,5,2 2592
9 6,5,2 2592
10 6,5,2 2592
11 6,5,2 2592
1. 如何採樣
採樣的原理就是周期性的去看下當前的 CPU 核中運行的幾個線程正在執行什麼方法, 當採樣到了幾萬個或者幾十萬個樣本之後,就可以對這些採集到的方法進行分組排序來找到 topN,那些 TopN 的方法自然就是導致 CPU 爆高可能的誘因。
windbg 有一個 !running
命令可以用來顯示當前處理器中正在運行的線程。
lkd> !running
System Processors: (0000000000000fff)
Idle Processors: (000000000000065e)
Prcbs Current (pri) Next (pri) Idle
0 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
列表項。
好了,本篇就先聊這麼多吧。