前言
在本地開發調試的時候,基於VS的工具能方便看出內存泄露的信息:
但到了線上,一般都是在linux環境中,並且服務運行在docker上,這時出現內存泄露,CPU異常等情況,就無法直接調試,只能把docker中在允許的服務內存下來dump信息,進行分析,思路上就這幾步:
- 把運行時的內存給dump下來,傳到本地
- 能正確讀取出dump信息
- 分析原因
1. 生成dump文件
生成dump文件,依賴createdump工具,這個工具是.net core runtime自帶的,所以只要運行在.net core上的項目, 就可以直接使用該工具。
- 進行容器內部,dockername 可以通過docker ps得到
docker exec -it dockername /bin/bash
- 找到工具對應的目錄:
find /usr/share -name createdump
- 通過上面的路徑執行createdump,就會保存當前容器在運行時的信息。
1代表容器內要保存的服務進程號, 如果只是1個服務,默認一般默認爲1,最終得到一個createdump.1的文件
/usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.0/createdump 1
如果出現以下信息:
Writing minidump with heap to file /tmp/coredump.1
ptrace(ATTACH, 1) FAILED Operation not permitted
說明沒有權限,需要在docker run的時候帶上命令 --privileged=true 獲取到root權限。
- 從容器中移出, 並放到自己希望的位置上。
docker cp dockername:/tmp/coredump.1 ./
2. 讀取dump文件的三個方式
現在已經有了coredump.1,要想辦法讀取coredump.1進行下一步的分析。
方案有多種,但是最終都是通過sos命令來查看dump信息。
有以下三條路線:
通過windbg+sos讀取
讀取dump時需所處環境:windows
這塊沒有嘗試,理論上似乎可以。雖然windbg是只能在windows上運行,但結合sos應該可以在windows中讀取dump。
dotnet自帶的dotnet-dump進行讀取
測試讀取dump時需所處環境:linux
這個工具的好處,相比下面lldb+sos的方案,安裝方便, 官方文檔說明點此。
- 安裝dotnet-dump
dotnet tool install -g dotnet-dump
設置路徑, 結合實際dotnet所在目錄
export PATH=$PATH:xxx/.dotnet/tools
export DOTNET_ROOT="xxx/dotnet"
- 載入dump文件
dotnet-dump analyze coredump.1
進入後,執行clrthreads,就會列出當時正在運行的託管線程。
clrthreads
如果無法得到預期結果,出現了:
Failed to load data access module, 0x80004005
Can not load or initialize libmscordaccore.so. The target runtime may not be initialized.
說明必須依賴libmscordaccore.so,但是沒有被加載。可惜dotnet-dump工具還不支持單獨setclrpath功能,無法載入這個so文件。
通過github看3.1.57502之後的版本應該能支持單獨setclrpath了, 因爲代碼已經改了。
使用lldb+sos讀取(推薦)
測試讀取dump時需所處環境:Ubuntu
爲什麼是Ubuntu?因爲lldb版本在Ubuntu環境下安裝會比較簡單。
- 安裝lldb
分析.net core2.1及以上的需要裝lldb-3.9,2.1之前的需要裝3.6版本了。
apt-get update && apt-get install -y \
cmake llvm-3.9 \
clang-3.9 \
lldb-3.9 \
liblldb-3.9-dev \
libunwind8 \
libunwind8-dev \
gettext \
libicu-dev \
liblttng-ust-dev \
libcurl4-openssl-dev \
libssl-dev \
uuid-dev \
libnuma-dev \
libkrb5-dev
安裝後通過lldb-3.9命令,能進入就代表正常。
2. 確認sos插件可以使用:
進入lldb後,輸入soshelp,能看到以下信息確認soshelp可以使用:
如果看不到:
版本是.net core3及之後的新版:退出lldb,安裝dotnet-sos後就能顯示
版本是.net core3之前的舊版本:在lldb里加載下sos插件,然後再調用下soshelp就能顯示了。
plugin load 你的路徑/dotnet/shared/Microsoft.NETCore.App/對應版本/libsosplugin.so
- 都安裝完了,帶上dump文件測試下
lldb-3.9 $(which dotnet) --core 你的dump文件路徑/coredump.1
進入到lldb後
如果是.net core3之前的版本,需要:
plugin load 你的路徑/dotnet/shared/Microsoft.NETCore.App/對應版本/libsosplugin.so
查看下當前dump文件裏的線程,輸入sos Threads後就能看到:
比如想看ID 11的線程詳情,可以先setsostid 14 11鎖定線程:
然後再sos ClrStack查看線程內部信息
如果sos Threads後無法得到預期結果,出現了:
Failed to load data access module, 0x80004005
Can not load or initialize libmscordaccore.so. The target runtime may not be initialized.
說明必須依賴libmscordaccore.so,執行setclrpath:
setclrpath 你的路徑/dotnet/shared/Microsoft.NETCore.App/對應的.net core版本
3.分析
我們已經能通過sos的命令顯示出堆棧信息,通過這些信息就可以進行分析。
首先一定要熟悉sos裏面的每個命令,因爲上面不管哪種讀取dump方式,其實最終都是依靠sos插件。
然後根據遇到的不同情況再做不同角度的分析, 這裏順便連接一些通用的分析方法:
內存使用過多的情況分析
CPU過高的情況分析