最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Linux使用perf跟蹤.NET程序的mmap泄露的流程步驟

 更新時間:2025年05月20日 10:00:25   作者:心上之秋  
如何跟蹤.NET程序的mmap泄露,這個問題困擾了我差不多一年的時間,即使在官方的github庫中也找不到切實可行的方案,這幾天我仔仔細細的研究這個問題,結(jié)合大模型的一些幫助,算是找到了一個相對可行的方案,本文將介紹Linux使用perf跟蹤.NET程序的mmap泄露的流程步驟

一:背景

1. 講故事

如何跟蹤.NET程序的mmap泄露,這個問題困擾了我差不多一年的時間,即使在官方的github庫中也找不到切實可行的方案,更多海外大佬只是推薦valgrind這款工具,但這款工具底層原理是利用模擬器,它的地址都是虛擬出來的,你無法對valgrind 監(jiān)控的程序抓dump,并且valgrind顯示的調(diào)用棧無法映射出.NET函數(shù)以及地址,這幾天我仔仔細細的研究這個問題,結(jié)合大模型的一些幫助,算是找到了一個相對可行的方案。

二:mmap 導致的內(nèi)存泄露

1. 一個測試案例

為了方便講述,我們通過 C 調(diào)用 mmap 方法分配256個 4M 的內(nèi)存塊,即總計 1G 的內(nèi)存泄露,參考代碼如下:

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

#define BLOCK_SIZE (4096 * 1024)            // 每個塊 4096KB (4MB)
#define TOTAL_SIZE (1 * 1024 * 1024 * 1024) // 總計 1GB
#define BLOCKS (TOTAL_SIZE / BLOCK_SIZE)    // 計算需要的塊數(shù)

void mmap_allocation() {
    uint8_t* blocks[BLOCKS]; // 存儲每個塊的指針

    // 使用 mmap 分配 1GB 內(nèi)存,分成多個 4MB 塊
    for (size_t i = 0; i < BLOCKS; i++) {
        blocks[i] = (uint8_t*)mmap(NULL, BLOCK_SIZE,
            PROT_READ | PROT_WRITE,
            MAP_PRIVATE | MAP_ANONYMOUS,
            -1, 0);

        if (blocks[i] == MAP_FAILED) {
            perror("mmap 失敗");
            return;
        }

        // 確保每個塊都被實際占用
        memset(blocks[i], 20, BLOCK_SIZE);
    }

    printf("已經(jīng)使用 mmap 分配 1GB 內(nèi)存(分成 %d 個 %dKB 塊)!\n", 
           BLOCKS, BLOCK_SIZE/1024);
    printf("程序?qū)和?10 秒,可以使用 top/htop 查看內(nèi)存使用情況...\n");
    sleep(10);
}

int main() {
    mmap_allocation();
    return 0;
}

為了能夠讓 C# 調(diào)用,我們將這個 c 編譯成 so 庫,即 windows 中的 dll 文件,參考命令如下:

root@ubuntu2404:/data2/c# gcc -shared -o Example_18_1_5.so -fPIC -g -O0 Example_18_1_5.c
root@ubuntu2404:/data2/c# ls -lh
total 24K
-rw-r--r-- 1 root root 1.2K May  7 10:47 Example_18_1_5.c
-rwxr-xr-x 1 root root  18K May  7 10:47 Example_18_1_5.so

接下來創(chuàng)建一個名為 MyConsoleApp 的 Console控制臺項目。

root@ubuntu2404:/data2# dotnet new console -n MyConsoleApp --framework net8.0 --use-program-main
The template "Console App" was created successfully.

Processing post-creation actions...
Restoring /data2/MyConsoleApp/MyConsoleApp.csproj:
  Determining projects to restore...
  Restored /data2/MyConsoleApp/MyConsoleApp.csproj (in 1.73 sec).
Restore succeeded.

root@ubuntu2404:/data2# cd MyConsoleApp
root@ubuntu2404:/data2/MyConsoleApp# dotnet run
Hello, World!

項目創(chuàng)建好之后,接下來就可以調(diào)用 Example_18_1_5.so 中的mmap_allocation方法了,在真正調(diào)用之前故意用Console.ReadLine();攔截,主要是方便用 perf 去介入監(jiān)控,最后不要忘了將生成好的 Example_18_1_5.so文件丟到 bin 目錄下,參考代碼如下:

using System.Runtime.InteropServices;

namespace MyConsoleApp;

class Program
{
    [DllImport("Example_18_1_5.so", CallingConvention = CallingConvention.Cdecl)]
    public static extern void mmap_allocation();

    static void Main(string[] args)
    {
        MyTest();

        for (int i = 0; i < int.MaxValue; i++)
        {
            Console.WriteLine($"{DateTime.Now} :i={i} 執(zhí)行完畢,自我輪詢中...");

            Thread.Sleep(1000);
        }

        Console.ReadLine();
    }

    static void MyTest()
    {
        Console.WriteLine("MyTest 已執(zhí)行,準備執(zhí)行 mmap_allocation 方法");
        Console.ReadLine();
        mmap_allocation();
        Console.WriteLine("MyTest 已執(zhí)行,準備執(zhí)行 mmap_allocation 方法");
    }
}

2. 使用 perf 監(jiān)控mmap事件

Linux 上的 perf 你可以簡單的理解成 Windows 上的 perfview,前者是基于 perf_events 子系統(tǒng),后者是基于 etw事件,這里就不做具體介紹了,這里我們用它監(jiān)控 mmap 的調(diào)用,因為拿到調(diào)用線程棧之后,就可以知道到底是誰導致的泄露。

為了能夠讓 perf 識別到 .NET 的托管棧,微軟做了一些特別支持,即開啟 export DOTNET_PerfMapEnabled=1 環(huán)境變量,截圖如下:

  • 在 終端1 上啟動 C# 程序。
root@ubuntu2404:/data2/MyConsoleApp/bin/Debug/net8.0# export DOTNET_PerfMapEnabled=1
root@ubuntu2404:/data2/MyConsoleApp/bin/Debug/net8.0# dotnet MyConsoleApp.dll
MyTest 已執(zhí)行,準備執(zhí)行 mmap_allocation 方法
  • 終端2 上開啟 perf 對dontet程序的mmap進行跟蹤。
root@ubuntu2404:/data2/MyConsoleApp# ps -ef | grep Console
root        3074    2197  0 11:14 pts/1    00:00:00 dotnet MyConsoleApp.dll
root        3241    3106  0 11:56 pts/3    00:00:00 grep --color=auto Console
root@ubuntu2404:/data2/MyConsoleApp# perf record -p 3074 -g -e syscalls:sys_enter_mmap

啟動跟蹤之后記得在 終端1 上按下Enter回車讓程序繼續(xù)執(zhí)行,當跟蹤差不多(大量的內(nèi)存泄露)的時候,我們在 終端2 上按下 Ctrl+C 停止跟蹤,截圖如下:

root@ubuntu2404:/data2/MyConsoleApp# perf record -p 3074 -g -e syscalls:sys_enter_mmap
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.139 MB perf.data (333 samples) ]

從輸出看當前的 perf.data 有 333 個樣本,0.13M 的大小,由于在 linux 上分析不方便,而且又是二進制的,所以我們將 perf.data 轉(zhuǎn)成 perf.txt 然后傳輸?shù)?windows 上分析,參考命令如下:

root@ubuntu2404:/data2/MyConsoleApp# ls
MyConsoleApp.csproj  Program.cs  bin  obj  perf.data
root@ubuntu2404:/data2/MyConsoleApp# perf script > perf.txt
root@ubuntu2404:/data2/MyConsoleApp# sz perf.txt

經(jīng)過仔細的分析 perf.txt 的 mmap 調(diào)用棧,很快就會發(fā)現(xiàn)有人調(diào)了 256 次 4M 的 mmap 分配吃掉了絕大部分內(nèi)存,那個上層的 memfd:doublemapper 就是 JIT 代碼所存放的內(nèi)存臨時文件,由于有 DOTNET_PerfMapEnabled=1 的加持,可以看到 [unknown] 前面的方法返回地址,截圖如下:

3. 這些地址對應(yīng)的 C# 方法是什么

本來我以為 JIT很給力,在 perf 生成的 /tmp/perf-3074.map 文件中弄好了符號信息,結(jié)果搜了下沒有對應(yīng)的方法名,比較尷尬。

root@ubuntu2404:/data2/MyConsoleApp# grep "7f42f3f11967" /tmp/perf-3074.map
root@ubuntu2404:/data2/MyConsoleApp# grep "7f42f3f11a90" /tmp/perf-3074.map
root@ubuntu2404:/data2/MyConsoleApp#

那怎么辦呢?只能抓dump啦,這也是我非常擅長的,可以用 dotnet-dump抓一個,然后使用 !ip2md 觀察便知。

root@ubuntu2404:/data2/MyConsoleApp# dotnet-dump collect -p 3074

Writing full to /data2/MyConsoleApp/core_20250507_113516
Complete
root@ubuntu2404:/data2/MyConsoleApp# ls -lh 
total 1.2G
-rw-r--r-- 1 root root  242 May  7 10:50 MyConsoleApp.csproj
-rw-r--r-- 1 root root  769 May  7 11:05 Program.cs
drwxr-xr-x 3 root root 4.0K May  7 10:51 bin
-rw------- 1 root root 1.2G May  7 11:35 core_20250507_113516
drwxr-xr-x 3 root root 4.0K May  7 10:51 obj
-rw------- 1 root root 164K May  7 11:16 perf.data
-rw-r--r-- 1 root root 874K May  7 11:21 perf.txt
root@ubuntu2404:/data2/MyConsoleApp# dotnet-dump analyze core_20250507_113516
Loading core dump: core_20250507_113516 ...
Ready to process analysis commands. Type 'help' to list available commands or 'help [command]' to get detailed help on a command.
Type 'quit' or 'exit' to exit the session.
> ip2md 7f42f3f11967                                                                                                                            
MethodDesc:   00007f42f3f9f320
Method Name:          MyConsoleApp.Program.Main(System.String[])
Class:                00007f42f3fbb648
MethodTable:          00007f42f3f9f368
mdToken:              0000000006000002
Module:               00007f42f3f9cec8
IsJitted:             yes
Current CodeAddr:     00007f42f3f11920
Version History:
  ILCodeVersion:      0000000000000000
  ReJIT ID:           0
  IL Addr:            00007f437307e250
     CodeAddr:           00007f42f3f11920  (MinOptJitted)
     NativeCodeVersion:  0000000000000000
Source file:  /data2/MyConsoleApp/Program.cs @ 12
> ip2md 7f42f3f11a90                                                                                                                            
MethodDesc:   00007f42f3f9f338
Method Name:          MyConsoleApp.Program.MyTest()
Class:                00007f42f3fbb648
MethodTable:          00007f42f3f9f368
mdToken:              0000000006000003
Module:               00007f42f3f9cec8
IsJitted:             yes
Current CodeAddr:     00007f42f3f11a50
Version History:
  ILCodeVersion:      0000000000000000
  ReJIT ID:           0
  IL Addr:            00007f437307e2d2
     CodeAddr:           00007f42f3f11a50  (MinOptJitted)
     NativeCodeVersion:  0000000000000000
Source file:  /data2/MyConsoleApp/Program.cs @ 28
> ip2md 7f42f3f13557                                                                                                                            
MethodDesc:   00007f42f42f42b8
Method Name:          ILStubClass.IL_STUB_PInvoke()
Class:                00007f42f42f41e0
MethodTable:          00007f42f42f4248
mdToken:              0000000006000000
Module:               00007f42f3f9cec8
IsJitted:             yes
Current CodeAddr:     00007f42f3f134d0
Version History:
  ILCodeVersion:      0000000000000000
  ReJIT ID:           0
  IL Addr:            0000000000000000
     CodeAddr:           00007f42f3f134d0  (MinOptJitted)
     NativeCodeVersion:  0000000000000000
>

從 dotnet-dump 給的輸出看,可以清楚的看到調(diào)用關(guān)系為: Main -> MyTest -> ILStubClass.IL_STUB_PInvoke -> mmap_allocation -> mmap 。

至此真相大白于天下。

三:總結(jié)

這類問題的泄露真的費了我不少心思,曾經(jīng)讓我糾結(jié)過,迷茫過,我也搗鼓過 strace,最終都無法找出棧上的托管函數(shù),真的,目前 .NET 在 Linux 調(diào)試生態(tài)上還是很弱,好無奈,這篇文章我相信彌補了國內(nèi),甚至國外在這一塊領(lǐng)域的空白,也算是這一年來對自己的一個交代。

以上就是Linux使用perf跟蹤.NET程序的mmap泄露的流程步驟的詳細內(nèi)容,更多關(guān)于Linux perf跟蹤mmap泄露的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • linux下制作ISO文件

    linux下制作ISO文件

    本文給大家分享的是在linux系統(tǒng)中制作ISO文件的2種方法,非常簡單實用,有需要的小伙伴可以參考下
    2018-04-04
  • 詳解Linux中如何安全地抹去磁盤數(shù)據(jù)

    詳解Linux中如何安全地抹去磁盤數(shù)據(jù)

    離過職的小伙伴都知道,離職的時候需要上交公司電腦,但是電腦里面有許多我們的個人信息,所以我們就需要先把這些信息都刪除,確保無法恢復之后才上交,下面我們來看一下在 Linux 中如何安全地抹去磁盤數(shù)據(jù)吧
    2023-10-10
  • 詳解CentOS7使用firewalld打開關(guān)閉防火墻與端口

    詳解CentOS7使用firewalld打開關(guān)閉防火墻與端口

    本篇文章主要介紹了CentOS7使用firewalld打開關(guān)閉防火墻與端口,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-01-01
  • Linux環(huán)境下nodejs的安裝圖文教程

    Linux環(huán)境下nodejs的安裝圖文教程

    這篇文章主要介紹了Linux環(huán)境下nodejs的安裝圖文教程,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-12-12
  • 騰訊云服務(wù)器搭建Jenkins詳細介紹

    騰訊云服務(wù)器搭建Jenkins詳細介紹

    大家好,本篇文章主要講的是騰訊云服務(wù)器搭建Jenkins詳細介紹,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下
    2021-12-12
  • Apache?RocketMQ?Docker?容器化部署方案

    Apache?RocketMQ?Docker?容器化部署方案

    本文將詳細介紹如何通過Docker容器化方式部署Apache?RocketMQ,包括環(huán)境準備、鏡像拉取、容器部署、功能測試、生產(chǎn)環(huán)境優(yōu)化及故障排查等內(nèi)容,為用戶提供一套完整的容器化部署方案,感興趣的朋友跟隨小編一起看看吧
    2025-12-12
  • 在Linux中配置和使用CAN通信的詳細指南

    在Linux中配置和使用CAN通信的詳細指南

    CAN是一種廣泛用于嵌入式系統(tǒng)、汽車和工業(yè)控制中的通信協(xié)議,Linux 支持 CAN 協(xié)議棧,并通過 SocketCAN 實現(xiàn)對 CAN 總線的訪問,在這篇博客中,我們將深入講解如何在 Linux 系統(tǒng)中配置和使用 CAN 通信,需要的朋友可以參考下
    2025-08-08
  • Linux多塊硬盤如何掛載到同一目錄

    Linux多塊硬盤如何掛載到同一目錄

    文章介紹了如何使用LVM(邏輯卷管理)將多塊硬盤合并為一個邏輯卷,并詳細描述了操作流程,包括創(chuàng)建物理卷、卷組和邏輯卷,以及格式化和掛載邏輯卷
    2025-02-02
  • CentOS7服務(wù)器中apache、php7以及mysql5.7的安裝配置代碼

    CentOS7服務(wù)器中apache、php7以及mysql5.7的安裝配置代碼

    這篇文章主要介紹了CentOS7服務(wù)器中apache、php7以及mysql5.7的安裝配置代碼,需要的朋友可以參考下
    2017-10-10
  • Centos下安裝Ansible的示例代碼

    Centos下安裝Ansible的示例代碼

    本篇文章主要介紹了Centos下安裝Ansible的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04

最新評論

高邮市| 梓潼县| 磴口县| 娄烦县| 尼玛县| 开原市| 同德县| 宿州市| 南岸区| 抚顺县| 金秀| 聊城市| 贺州市| 客服| 青岛市| 嘉鱼县| 云阳县| 黑水县| 宜宾县| 盐亭县| 邯郸市| 壶关县| 高邑县| 福贡县| 宣威市| 汕尾市| 南安市| 台安县| 孟津县| 张家港市| 绥化市| 宁城县| 墨玉县| 孝昌县| 岳阳市| 静海县| 江阴市| 九龙县| 都兰县| 宜都市| 杭锦后旗|