天天看點

用 perfcollect 洞察 Linux 上.NET程式 CPU爆高

作者:一線碼農聊技術

一:背景

1. 講故事

如果要分析 Linux上的 .NET程式 CPU 爆高,按以往的個性我肯定是抓個 dump 下來做事後分析,這種分析模式雖然不重但也不輕,還需要一定的底層知識,那有沒有傻瓜式的 CPU 爆高分析方式呢?

相信有很多朋友知道 B站713事件,最終就是用 perf 找到了那個讓 cpu 100% 的 lua 函數,截圖如下:

用 perfcollect 洞察 Linux 上.NET程式 CPU爆高

這裡我們也借助 perf 這款工具實作 .NET程式的 cpu 爆高洞察, perf 就不過多介紹了,它是Linux系統中提供的一款性能分析工具,類似 Windows 的 ETW 跟蹤,是以對他的了解是非常重要的。

這裡要注意的是我們并不直接使用,而是用微軟提供的基于 perf 的高層封裝工具 perfCollect,它不僅能收集 perf 能收集的事件,還能收集 .NET 中的 EventSource 事件,簡直是福音哈。

PerfCollect 跟蹤

1. 測試代碼

為了能夠讓 CPU 爆高,我們故意讓其中一個方法死循環,一個方法運作一段時間正常結束,參考代碼如下:

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Task.Run(() =>
            {
                Test1();
            });

            Task.Run(() =>
            {
                Test2();
            });

            Console.ReadLine();
        }

        static void Test1()
        {
            int i = 1;
            bool b = false;

            while (i > 0)
            {
                b = !b;
            }
        }

        static void Test2()
        {
            for (int i = 0; i < short.MaxValue; i++)
            {

            }
        }
    }
}

           

代碼有了就可以 publish 到 centos 上,接下來在 /etc/profile 中增加一個環境變量 export COMPlus_PerfMapEnabled=1 ,目的是讓 RIP 能夠成功解析到 C# 的方法名,截圖如下:

用 perfcollect 洞察 Linux 上.NET程式 CPU爆高

有了這些前置基礎,接下來就是把程式跑起來,用 htop 觀察下 CPU 的使用率。

[root@localhost data2]# vim /etc/profile
[root@localhost data2]# source /etc/profile
[root@localhost data2]# ls
ConsoleApp1  ConsoleApp1.deps.json  ConsoleApp1.dll  ConsoleApp1.pdb  ConsoleApp1.runtimeconfig.json
[root@localhost data2]# dotnet ConsoleApp1.dll

           
用 perfcollect 洞察 Linux 上.NET程式 CPU爆高

2. 安裝 PerfCollect

剛才也說了 PerfCollect 是微軟提供的一款工具,內建了 perf + LTTng 兩塊,前者用于捕獲Linux系統級事件,後者用于捕獲 CoreCLR 以及 EventSource 事件,接下來就是下載下傳,賦權限,安裝。

[root@localhost data3]# curl -OL https://aka.ms/perfcollect
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:02 --:--:--     0
100 68590  100 68590    0     0  17540      0  0:00:03  0:00:03 --:--:-- 72658
[root@localhost data3]# chmod +x perfcollect
[root@localhost data3]# sudo ./perfcollect install
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
epel/x86_64/metalink                                                              |  28 kB  00:00:00     
 * base: ftp.sjtu.edu.cn
 * epel: d2lzkl7pfhq30w.cloudfront.net
 * extras: mirror.lzu.edu.cn
 * updates: mirror.lzu.edu.cn
base                                                                              | 3.6 kB  00:00:00     
docker-ce-stable                                                                  | 3.5 kB  00:00:00     
extras                                                                            | 2.9 kB  00:00:00     
packages-microsoft-com-prod                                                       | 1.5 kB  00:00:00     
updates                                                                           | 2.9 kB  00:00:00     
Package perf-3.10.0-1160.92.1.el7.x86_64 already installed and latest version
Package zip-3.0-11.el7.x86_64 already installed and latest version
Package unzip-6.0-24.el7_9.x86_64 already installed and latest version
Nothing to do
LTTng already installed.

           

安裝好之後進行 10s 采集,采集完之後就會生成一個 ConsoleApp.trace.zip 檔案,輸出如下:

[root@localhost data3]# ./perfcollect collect ConsoleApp -collectsec 10
Collection started. Collection will automatically stop in 10 second(s). Press CTRL+C to stop early.

...STOPPED.

Starting post-processing. This may take some time.

Generating native image symbol files
...FINISHED
Saving native symbols
...FINISHED
Resolving JIT and R2R symbols
...FINISHED
Exporting perf.data file
...FINISHED
Compressing trace files
...FINISHED
Cleaning up artifacts
...FINISHED

Trace saved to ConsoleApp.trace.zip

           

最後把 ConsoleApp.trace.zip 複制到 Windows 平台上用 PerfView 分析。

3. Perfview 分析

說句良心話,Perfview 真的是太強大了,什麼檔案都能從中提取有用資訊,比如 .dmp,.nettrace 還有這裡的 .zip ,用 Perfview 打開 zip 之後,輕按兩下 CPU Stacks 選項,找到我們的 PID 程序即(.NET ThreadPool),截圖如下:

用 perfcollect 洞察 Linux 上.NET程式 CPU爆高
[root@localhost data3]# ps -ef | grep dotnet
root       6027   3171 99 23:33 pts/1    00:02:01 dotnet ConsoleApp1.dll
root       6529   5240  0 23:35 pts/2    00:00:00 grep --color=auto dotnet

           

輕按兩下打開之後,去掉 GroupPats 資訊,可以看到占比最高的是 Program::Test1() 方法。

用 perfcollect 洞察 Linux 上.NET程式 CPU爆高

有朋友可能要問這個資訊怎麼解讀呢?其實非常簡單,perf 也是按照 1ms 采樣一次的方式,是以 10s 的樣本數: 1w =10 * 1000。

從上圖中可以看到,總的采樣到了 9999 個樣本,其中 Program::Test1() 占據了 9993,占比高達 99.9%,到這裡我們就定位出了原來這個函數就是 hot 函數。

三:總結

不知道大家發現沒有,在 Windows 上很容易監控的東西,在 Linux 上就要麻煩的多,其實很容易了解,Windows 是微軟的, .NET 也是微軟的,自然是一等公民的存在。

繼續閱讀