Linux 上的 .NET 崩溃了怎么抓 Dump
|
一:背景
1. 讲故事
训练营中有朋友问在 Linux 上如何抓 crash dump,在我的系列文章中演示的大多是在 Windows 平台上,这也没办法要跟着市场走,谁让 .NET 的主战场在工控 和 医疗 呢,上一张在 合肥 分享时的一个统计图。
这就导致总有零星的朋友问 Linux 平台上如何生成 crash dump,这一篇就来整理下来减少后续的沟通成本。
二:如何生成
1. 案例代码
为了方便演示,写了一段简单的 C# 代码,故意抛异常让程序崩溃。- static void Main(string[] args)
- {
- throw new Exception("OutOfMemory");
- Console.ReadLine();
- }
复制代码 2. 操作系统层面捕获
一般来说操作系统层面都支持当一个进程异常退出时自动捕获Crash Dump,Linux 如此,Windows 也如此,当然默认是不支持的,需要用 ulimit 开启,这个命令可以用来配置当前系统资源的使用额度,用 limit -a 观察。- [root@localhost data]# ulimit -a
- core file size (blocks, -c) 0
- data seg size (kbytes, -d) unlimited
- scheduling priority (-e) 0
- file size (blocks, -f) unlimited
- pending signals (-i) 14950
- max locked memory (kbytes, -l) 64
- max memory size (kbytes, -m) unlimited
- open files (-n) 1024
- pipe size (512 bytes, -p) 8
- POSIX message queues (bytes, -q) 819200
- real-time priority (-r) 0
- stack size (kbytes, -s) 8192
- cpu time (seconds, -t) unlimited
- max user processes (-u) 14950
- virtual memory (kbytes, -v) unlimited
- file locks (-x) unlimited
复制代码 卦中的 core file size 就是用来指定生成 dump 文件的大小,默认为 0,即表示不生成,我们可以将其改成 unlimited ,即不限文件大小。- [root@localhost data]# ulimit -c unlimited
复制代码 如果你想永久保存,可以修改环境变量文件 /etc/profile, 在末尾新增 ulimit -c unlimited 即可。- [root@localhost data]# vim /etc/profile
- [root@localhost data]# source /etc/profile
复制代码
接下来将程序在 CentOS7 上跑起来,从输出看马上就产生了崩溃文件,默认在应用程序目录下。- [root@localhost data]# dotnet Example_1_1.dll
- hello world!
- Unhandled exception. System.Exception: OutOfMemory
- at Example_1_1.Program.Main(String[] args) in D:\skyfly\1.20230528\src\Example\Example_1_1\Program.cs:line 13
- Aborted (core dumped)
- [root@localhost data]# ls
- core.39653 Example_1_1.deps.json Example_1_1.pdb
- Example_1_1 Example_1_1.dll Example_1_1.runtimeconfig.json
复制代码 core.39653 生成好了之后,可以 copy 到 Windows 平台上使用 windbg 分析,这里有一点要注意,linux 上的 dump,windbg 默认不自动加载 sos 的,需要你手工 .load 一下。- 0:000> .load C:\Users\Administrator\.dotnet\sos64\sos.dll
- 0:000> !t
- ThreadCount: 3
- UnstartedThread: 0
- BackgroundThread: 2
- PendingThread: 0
- DeadThread: 0
- Hosted Runtime: no
- Lock
- DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
- 0 1 9ae5 000055DF29280340 20020 Preemptive 00007F352C01EE20:00007F352C01FFD0 000055df29267810 -00001 Ukn <Invalid Object> (00007f352c0138c8)
- 7 2 9aea 000055DF2928D990 21220 Preemptive 0000000000000000:0000000000000000 000055df29267810 -00001 Ukn (Finalizer)
- 1 3 9aeb 000055DF292B99E0 21220 Preemptive 0000000000000000:0000000000000000 000055df29267810 -00001 Ukn
- 0:000> !pe
- Exception object: 00007f352c0138c8
- Exception type: <Unknown>
- Message: OutOfMemory
- InnerException: <none>
- StackTrace (generated):
- SP IP Function
- 00007FFE584563C0 00007F3561BE2E86 Example_1_1.dll!Unknown+0x96
- StackTraceString: <none>
- HResult: 80131500
复制代码 从卦中看,虽然异常信息有,但看不到默认的托管函数名 Main,而是用 Unknown 替代的,这就比较尴尬了,毕竟 Linux 不是微软弄的,很多地方水土不服。
可这是无数 Linux 人及官方首推生成 crash dump 的方式,在 .netcore 上总会有这样和那样的问题,那怎么办呢?问题总得要解决,针对这种场景,微软巧用开机启动的 dotnet 进程为载体,在程序崩溃的时候通过读取 环境变量 的方式来生成 crash dump。- [root@localhost ~]# ps -ef | grep dotnet
- root 6566 6520 0 12:06 pts/2 00:00:00 grep --color=auto dotnet
复制代码 3. 使用 dotnet 环境变量捕获
微软的 MSDN:https://learn.microsoft.com/en-us/dotnet/core/diagnostics/collect-dumps-crash 上详细的记录了如何通过读取环境变量来生成 crash dump。
大体分如下三个参数:
- COMPlus_DbgEnableMiniDump
- COMPlus_DbgMiniDumpType
- COMPlus_DbgMiniDumpName
看到这三个变量,我敢断定它是借助了 Windows WER 生成 crash dump 的思想,不过载体不一样,前者是 dontet父进程,后者是 wer系统服务。
接下来将这三个变量配置到环境变量文件中,然后把程序跑起来了,参考如下:
- [root@localhost data]# vim /etc/profile
- [root@localhost data]# source /etc/profile[root@localhost data]# dotnet Example_1_1.dllhello world!Unhandled exception. System.Exception: OutOfMemory at Example_1_1.Program.Main(String[] args) in D:\skyfly\1.20230528\src\Example\Example_1_1\Program.cs:line 13[createdump] Gathering state for process 40422 dotnet[createdump] Crashing thread 9de6 signal 6 (0006)[createdump] Writing full dump to file /data2/coredump.dmp[createdump] Written 119734272 bytes (29232 pages) to core file[createdump] Target process is alive[createdump] Dump successfully writtenAborted (core dumped)[root@localhost data]# cd /data2 ; lscoredump.dmp
复制代码 有了这个 coredump.dmp 之后,再把它拖到 windbg 中观察。- 0:000> .load C:\Users\Administrator\.dotnet\sos64\sos.dll
- 0:000> !t
- ThreadCount: 3
- UnstartedThread: 0
- BackgroundThread: 2
- PendingThread: 0
- DeadThread: 0
- Hosted Runtime: no
- Lock
- DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
- 0 1 9de6 00005603DBF7C520 20020 Preemptive 00007FF13801EE20:00007FF13801FFD0 00005603dbf639f0 -00001 Ukn System.Exception 00007ff1380138c8
- 5 2 9deb 00005603DBF89B70 21220 Preemptive 0000000000000000:0000000000000000 00005603dbf639f0 -00001 Ukn (Finalizer)
- 6 3 9dec 00005603DBFB5C50 21220 Preemptive 0000000000000000:0000000000000000 00005603dbf639f0 -00001 Ukn
- 0:000> !pe
- Exception object: 00007ff1380138c8
- Exception type: System.Exception
- Message: OutOfMemory
- InnerException: <none>
- StackTrace (generated):
- SP IP Function
- 00007FFC7A324A10 00007FF16F852E86 Example_1_1!Example_1_1.Program.Main(System.String[])+0x96
- StackTraceString: <none>
- HResult: 80131500
复制代码 从卦中看,这次终于有了,不容易,所以在 Linux 平台上,首推环境变量的模式,如果你对 coredump 的名字有自定义要求,也可以修改根据下图中的模板参数修改。
- export COMPlus_DbgEnableMiniDump=1
- export COMPlus_DbgMiniDumpType=4
- export COMPlus_DbgMiniDumpName=/data2/%p-%e-%h-%t.dmp
- [root@localhost data2]# ls
- 41758-dotnet-localhost.localdomain-1685332206.dmp
复制代码 三:总结
这篇大概介绍了两个抓 dump 的方式:前者适合非托管程序,后者适合托管程序,相信这篇文章能极大的节省各方的沟通成本,花点时间整理下很值。
来源:https://www.cnblogs.com/huangxincheng/archive/2023/05/29/17440153.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|
|
|
发表于 2023-5-29 20:24:11
举报
回复
分享
|
|
|
|