.net ocre 程式崩潰自動dump在多平臺中的實現

China Soft發表於2024-10-15

前言

經常排查問題的朋友都知道,我們在遇到CPU或者記憶體高的時候,有時會生成dump檔案來做分析。但是我們也會遇到一些場景,應用程式直接崩潰退出,這個時候我們已經沒法使用常規方式dump了,因為整個程序樹已經退出了,那麼我們有沒有辦法讓系統自動做dump,答案是肯定的:讓系統在程式崩潰時自動建立Dump。

無論是 .net framework還是 .net core 專案,都支援應用程式崩潰時生成轉儲,本篇我們重點看下 .net core應用在docker、macOS(lunix類似)環境下是如何實現的。

配置

設定一些變數引數:

  • COMPlus_DbgEnableMiniDump 或 DOTNET_DbgEnableMiniDump: 如果設定為 1,則發生故障時啟用CoreDump生成。預設值為:0
  • COMPlus_DbgMiniDumpType 或 DOTNET_DbgMiniDumpType: 要收集的轉儲型別。 有關詳細資訊,請看下文的說明。預設值為:2
  • COMPlus_DbgMiniDumpName 或 DOTNET_DbgMiniDumpName: 寫入轉儲的檔案路徑。 確保執行 dotnet 程序的使用者具有指定目錄的寫入許可權。預設值為:/tmp/coredump.
  • COMPlus_CreateDumpDiagnostics 或 DOTNET_CreateDumpDiagnostics: 如果設定為 1,則啟用轉儲程序的診斷日誌記錄。預設值為:0
  • COMPlus_EnableCrashReport 或 DOTNET_EnableCrashReport:(需要 .NET 6 或更高版本,目前僅Linux和MacOS可用)如果設為 1,執行時會生成 JSON 格式的故障報表,其中包括有關故障應用程式的執行緒和堆疊幀的資訊。 故障報表名稱是追加了 .crashreport.json 的轉儲路徑/名稱。
  • COMPlus_CreateDumpVerboseDiagnostics 或 DOTNET_CreateDumpVerboseDiagnostics:(需要 .NET 7 或更高版本)如果設為 1,則啟用轉儲程序的詳細診斷日誌記錄。
  • COMPlus_CreateDumpLogToFile 或 DOTNET_CreateDumpLogToFile:(需要 .NET 7 或更高版本)應寫入診斷訊息的檔案路徑。 如果未設定,則將診斷訊息寫入故障應用程式的控制檯。

對於這些環境變數,.NET 7 標準化字首 DOTNET_,而不是 COMPlus_。 但是,COMPlus_ 字首仍將繼續正常工作。 如果使用的是早期版本的 .NET 執行時,則環境變數仍應該使用 COMPlus_ 字首。

關於DOTNET_DbgMiniDumpType的說明如下所示:

1: Mini 小型Dump,其中包含模組列表、執行緒列表、異常資訊和所有堆疊。
2: Heap 大型且相對全面的Dump,其中包含模組列表、執行緒列表、所有堆疊、異常資訊、控制代碼資訊和除對映影像以外的所有記憶體。
3: Triage 與 Mini 相同,但會刪除個人使用者資訊,如路徑和密碼。
4: Full 最大的轉儲,包含所有記憶體(包括模組映像)。
一般情況下,我們會配置下面的環境變數:

DOTNET_DbgEnableMiniDump = 1
DOTNET_DbgMiniDumpName = [有許可權的Path目錄]
DOTNET_CreateDumpDiagnostics = 1
DOTNET_EnableCrashReport = 1

用一段程式碼試試

由於筆者所使用的是 .net 6.0,所以這裡我們先在程式啟動的時候列印出字首為COMPlus_的環境變數的值。(這一段不要省,因為很多時候,你以為設定了環境變數,但是其實沒生效。)

foreach (DictionaryEntry environmentVariable in Environment.GetEnvironmentVariables())
{
    if (environmentVariable.Key.ToString()?.StartsWith("COMPlus") == true)
    {
        Console.WriteLine($"{environmentVariable.Key}={environmentVariable.Value}");
    }
}

然後我們在Thread中丟擲一個異常,這會導致應用程式退出或者容器退出。

public static void Bar()
{
    var thread = new Thread(() => throw new Exception("Crash"));
    thread.Start();
}

配置

macOS

注意:.net5 開始才支援macOS的dump。
可以在powershell中執行命令用於測試,注意這種方式設定的環境變數僅限於當前會話中有效,設定完成後啟動你的應用即可。

$env:DOTNET_DbgEnableMiniDump = 1
$env:DOTNET_DbgMiniDumpType = 4 
$env:DOTNET_CreateDumpDiagnostics = 1
$env:DOTNET_EnableCrashReport = 1 
$env:DOTNET_DbgMiniDumpName = "有許可權的路徑" 

docker

容器部署,需要做以下幾點:

  1. dockerfile中新增環境變數。
  2. 需要一個有寫入許可權的目錄
  3. docker 容器中的核心轉儲生成需要 ptrace 功能(--cap-add=SYS_PTRACE 或 --privileged)

docker中需要把目錄掛載到宿主機,否則容器退出,dump檔案也會刪除。

下方是一個dockerfile示例,以作參考。

FROM mcr.microsoft.com/dotnet/aspnet:6.0
COPY . /publish
WORKDIR /publish
EXPOSE 5100
ENV ASPNETCORE_URLS http://*:5100
# 設定環境變數
ENV COMPlus_DbgEnableMiniDump=1
ENV COMPlus_CreateDumpDiagnostics=1
ENV COMPlus_DbgMiniDumpType=4
ENV COMPlus_DbgMiniDumpName=/publish/dump/dumptest.dmp
ENV COMPlus_EnableCrashReport=1
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' > /etc/timezone
# 建立資料夾組給許可權
RUN mkdir -p /publish/dump/ && chmod 777 /publish/dump/
CMD ["dotnet", "WebApplication7.dll"]

在建立容器時,帶引數--cap-add=SYS_PTRACE使我們容器擁有相應的許可權

sudo docker run --cap-add=SYS_PTRACE -d --name apitest -p 5101:5100  apitest\:v1

執行

  1. 使用docker的方式啟動應用程式後,在下圖中顯示了環境變數的值,這說明設定是成功的。

  2. 接下來呼叫我們拋異常的api後,就會看到容器雖然退出了,但是dump的還是產生了,我們也可以在檔案中檢視生成的dump檔案。

注意事項

如果你在測試的過程沒有成功,檢測下環境變數和所使用的版本是否對應。

參與文獻

  1. https://www.cnblogs.com/InCerry/p/how_to_automic_create_dump_when_app_crash.html
  2. https://github.com/dotnet/coreclr/blob/master/Documentation/botr/xplat-minidump-generation.md#configurationpolicy

相關文章