公司的产品一直紧跟 .net core 3.0 preview 不断升级, 部署到 Linux 服务器后, 偶尔会出现某个进程CPU占用100%.
  由于服务部署在云上, 不能使用远程调试; 在局域网内的Linux 服务器 或 Windows开发机上又不能重现这个问题, 联想到Java的jstack, 很是羡慕啊. 想到.net core 已经出来这么久了, 还是试着找找看吧, 结果还真找到一篇博客Introducing diagnostics improvements in .NET Core 3.0

  这篇文章介绍了3个工具

  • dotnet-counters: 实时统计runtime的状况, 包括 CPU、内存、GC、异常等
  • dotnet-trace: 类似性能探测器
  • dotnet-dump: 程序崩溃时使用该工具

  这次使用的是dotnet-dump, 即使程序没有崩溃, 也可以dump程序快照, 用于分析

实验环境

ubuntu-16.04.5-desktop-amd64
SDK 3.0.100-preview6-012264

1. 新建一个简单Console程序(只能是 .net core 3.0的程序, 不支持 .net core 2.2), 模拟CPU占用100%的情况

mkdir NetCoreDumpTest && cd NetCoreDumpTest
dotnet new console

编辑Program.cs

namespace NetCoreDumpTest
{
using System;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
Task.Factory.StartNew(() => PrintNumber("Print", ));
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
} static void PrintNumber(string message, int startNumber)
{
var number = startNumber;
while (true)
Console.WriteLine($"{message} {number++}");
}
}
}

2. 安装dotnet-dump

dotnet tool install --global dotnet-dump --version 1.0.-preview6.19311.1

提示

If you are using bash, you can add it to your profile by running the following command:

cat << \EOF >> ~/.bash_profile
# Add .NET Core SDK tools
export PATH="$PATH:/home/****/.dotnet/tools"
EOF You can add it to the current session by running the following command: export PATH="$PATH:/home/****/.dotnet/tools" You can invoke the tool using the following command: dotnet-dump
Tool 'dotnet-dump' (version '1.0.4-preview6.19311.1') was successfully installed.

建议将 $HOME/.dotnet/tools加入到PATH, 好吧, 照着做吧, 记得使用下面的命令使设置立即生效

source ~/.bash_profile

3. 使用 dotnet NetCoreDumpTest.dll 启动我们的问题程序, 然后使用  ps -ef | grep dotnet  查看程序的进程ID, 可以看到进程ID是 3411

ps -ef | grep dotnet
z*****e : pts/ :: dotnet NetCoreDumpTest.dll
z*****e : pts/ :: grep --color=auto dotnet

针对进程3411, 我们还需要知道是哪个线程占CPU, 使用 top -Hp 3411 可以列出所有线程, 由于top每隔3秒刷新一次, 所以可能需要多观察几秒才能看到具体是哪个线程占用CPU比较高, 这里我们可以看到是PID=3418的线程(Linux的进程ID和线程ID请自行了解一下).

top -Hp
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
z*****e R 10.3 1.4 :20.68 dotnet
z*****e S 0.0 1.4 :00.11 dotnet
z*****e S 0.0 1.4 :00.02 dotnet
z*****e S 0.0 1.4 :00.00 dotnet
z*****e S 0.0 1.4 :00.00 dotnet
z*****e S 0.0 1.4 :00.01 dotnet
z*****e S 0.0 1.4 :00.00 dotnet
z*****e S 0.0 1.4 :00.00 dotnet
z*****e S 0.0 1.4 :00.00 dotnet

获取dump, 只能正对进程进行dump, 所以我们输入的是 3411

dotnet-dump collect -p
Writing minidump with heap to /tmp/core_20190623_075649
Complete

4. 分析

dotnet-dump analyze core_20190623_075649

使用clrthreads 查看所有线程

>clrthreads
ThreadCount:
UnstartedThread:
BackgroundThread:
PendingThread:
DeadThread:
Hosted Runtime: no
Lock
DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
d53 0000000001307D80 Preemptive : Ukn
d57 000000000135BBD0 Preemptive : Ukn (Finalizer)
d59 00007F666C0009F0 Preemptive : Ukn (Threadpool Worker)
d5a 000000000130DA40 Preemptive 00007F6678106860:00007F6678106F20 Ukn (Threadpool Worker)

我们关心的线程3418的16进制是d5a, 也就是最后一行, 它的DBG是7, 我们需要使用 setthread 7, 将其设置为  当前操作的线程

然后使用 clrstack 获取线程调用信息

> setthread
> clrstack
OS Thread Id: 0xd5a ()
Child SP IP Call Site
00007F6715561558 00007f671a2bd4bd [InlinedCallFrame: 00007f6715561558] Interop+Sys.Write(System.Runtime.InteropServices.SafeHandle, Byte*, Int32)
00007F6715561558 00007f669f669a9e [InlinedCallFrame: 00007f6715561558] Interop+Sys.Write(System.Runtime.InteropServices.SafeHandle, Byte*, Int32)
00007F6715561540 00007F669F669A9E ILStubClass.IL_STUB_PInvoke
00007F67155615E0 00007F669F67333E System.ConsolePal.Write(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Boolean)
00007F67155616A0 00007F669F67360C System.ConsolePal.Write(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Boolean) [/_/src/System.Console/src/System/ConsolePal.Unix.cs @ ]
00007F67155616C0 00007F669F672B2A System.IO.StreamWriter.Flush(Boolean, Boolean) [/_/src/System.Private.CoreLib/shared/System/IO/StreamWriter.cs @ ]
00007F6715561710 00007F669F6729F3 System.IO.StreamWriter.WriteLine(System.String) [/_/src/System.Private.CoreLib/shared/System/IO/StreamWriter.cs @ ]
00007F6715561760 00007F669F6727D3 System.IO.TextWriter+SyncTextWriter.WriteLine(System.String) [/_/src/System.Private.CoreLib/shared/System/IO/TextWriter.cs @ ]
00007F67155617A0 00007F669F672770 System.Console.WriteLine(System.String) [/_/src/System.Console/src/System/Console.cs @ ]
00007F67155617C0 00007F669F663791 NetCoreDumpTest.Program.PrintNumber(System.String, Int32) [/home/zhouke/NetCoreDumpTest/Program.cs @ ]
00007F6715561800 00007F669F6636D9 NetCoreDumpTest.Program+<>c.<Main>b__0_0()
00007F6715561820 00007F669F1872A1 System.Threading.Tasks.Task.InnerInvoke() [/_/src/System.Private.CoreLib/shared/System/Threading/Tasks/Task.cs @ ]
00007F6715561840 00007F669F18CBC2 System.Threading.Tasks.Task+<>c.<.cctor>b__274_0(System.Object) [/_/src/System.Private.CoreLib/shared/System/Threading/Tasks/Task.cs @ ]
00007F6715561850 00007F669F171AF2 System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(System.Threading.Thread, System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/_/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ ]
00007F6715561890 00007F669F187111 System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef, System.Threading.Thread) [/_/src/System.Private.CoreLib/shared/System/Threading/Tasks/Task.cs @ ]
00007F6715561910 00007F669F186F28 System.Threading.Tasks.Task.ExecuteEntryUnsafe(System.Threading.Thread) [/_/src/System.Private.CoreLib/shared/System/Threading/Tasks/Task.cs @ ]
00007F6715561930 00007F669F186EBB System.Threading.Tasks.Task.ExecuteFromThreadPool(System.Threading.Thread)
00007F6715561940 00007F669F17B754 System.Threading.ThreadPoolWorkQueue.Dispatch() [/_/src/System.Private.CoreLib/shared/System/Threading/ThreadPool.cs @ ]
00007F67155619C0 00007F669F169A5B System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() [/_/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs @ ]
00007F6715561D50 00007f6718a1ccaf [DebuggerU2MCatchHandlerFrame: 00007f6715561d50]

哗啦啦一大片, 有点Java调用堆栈的味道, 不过我们还是找到了我们的问题代码

NetCoreDumpTest.Program.PrintNumber(System.String, Int32)

有时候我们想知道传入的什么参数导致CPU占用高, 可以给clrstack加上参数 -a

> clrstack -a
..............
00007F0DD6FFC7C0 00007F0D6EEF3791 NetCoreDumpTest.Program.PrintNumber(System.String, Int32) [/home/zhouke/NetCoreDumpTest/Program.cs @ ]
PARAMETERS:
message (0x00007F0DD6FFC7E8) = 0x00007f0d4800b8b0
startNumber (0x00007F0DD6FFC7E4) = 0x0000000000000005
LOCALS:
0x00007F0DD6FFC7E0 = 0x000000000014e42b
0x00007F0DD6FFC7DC = 0x0000000000000001
...............

可以看到PARAMETERS里, startNumber作为值类型, 可以直接看到数值为5, 而message是引用类型, 指向0x00007f0d4800b8b0, 这时候需要用到 dumpobj 命令

> dumpobj 0x00007f0d4800b8b0
Name: System.String
MethodTable: 00007f0d6ef70f90
EEClass: 00007f0d6eede1c0
Size: (0x20) bytes
File: /home/zhouke/dotnet/shared/Microsoft.NETCore.App/3.0.-preview6--/System.Private.CoreLib.dll
String: Print
Fields:
MT Field Offset Type VT Attr Value Name
00007f0d6ef6a138 400022b System.Int32 instance _stringLength
00007f0d6ef66f38 400022c c System.Char instance _firstChar
00007f0d6ef70f90 400022d System.String static 00007f0d47fff360 Empty

好了, 可以看到它是一个字符串, 内容为 "Print"

假如message是一个复杂类型, 可以查看Fields下面的信息进一步查看

clrstack 还有一个实验性质的参数 -i, 协助查看各种变量信息, 需要用到lldb, 按照官方教程, 我暂时没有实验成功.

查看进程ID和线程ID, 更方便的方法是 htop(需要安装), 然后按 F4 进行过滤, 输入dotnet 即可

这张图是重新运行问题程序的结果, 进程ID和线程ID与前面不一样

第二行白色的是进程ID=1650, 第一行CPU占用高, 是问题线程ID=1658

End

使用dotnet-dump 查找 .net core 3.0 占用CPU 100%的原因的更多相关文章

  1. linux下查找java进程占用CPU过高原因

    1. 查找进程 top查看进程占用资源情况 明显看出java的两个进程22714,12406占用过高cpu.   2.查找线程 使用top -H -p <pid>查看线程占用情况   3. ...

  2. ASP.NET Core 1.0 基础与应用启动

    .NET Core http://dotnet.github.io/[https://github.com/dotnet/coreclr] ASP.NET Core 1.0 https://get.a ...

  3. Professional C# 6 and .NET Core 1.0 - Creating Hello, World! with Visual Studio

    本文为转载,学习研究 Creating Hello, World! with Visual Studio Chapter 1, “.NET Application Architectures,” ex ...

  4. DotNet Core 1.0 集成 CentOS 开发与运行环境部署

    一.     DotNet Core 1.0 开发环境部署 操作系统安装 我们使用CentOS 7.2.1511版本. 安装libunwind库 执行:sudo yum install libunwi ...

  5. 北京时间28号0点以后Scott Hanselman同志台宣布dotnet core 1.0 rtm

    今日占住微信号头条的好消息<终于来了!微软.Net Core 1.0下载放出>.本人立马跑到官网http://dot.net看了一下,仍然是.net core 1.0 Preview 1版 ...

  6. centos 7 && dotnet core 2.0 && nginx && supervisor

    前提 系统:centos 7 目录:/home/wwwroot/www.wuball.com dotnet core 2.0 官方指引 sudo rpm --import https://packag ...

  7. dotnet core 3.0 linux 部署小贴士

    dotnet core 3.0 目前还是测试版,在linux下安装 sdk 需要有一些注意事项 1.下载url https://dotnet.microsoft.com/download/thank- ...

  8. DotNet Core 2.0使用MySql实现Code First

    本教程使用vs2017 + dotnet core2.0 + MySql5.7.19 1.打开vs2017,文件>新建>项目,选择Asp.Net Core Web应用程序. 2.项目名称可 ...

  9. 卸载.net 5.0后使用dotnet提示Found .NET Core SDK

    之前安装了预览版本的vs2019后试了下,然后卸载了.但发现控制台执行dotnet相关命令提示Found .NET Core SDK, but did not find dotnet.dll at [ ...

随机推荐

  1. PAT天梯赛L3-004 肿瘤诊断

    题目链接:点击打开链接 在诊断肿瘤疾病时,计算肿瘤体积是很重要的一环.给定病灶扫描切片中标注出的疑似肿瘤区域,请你计算肿瘤的体积. 输入格式: 输入第一行给出4个正整数:M.N.L.T,其中M和N是每 ...

  2. sqllocaldb 的使用记录

    sql脚本是一个文件大约也就几MB,要是有一种文件存储数据就好了,后来了解到了sqllocaldb,但是没有深入研究大概知道有这么一个东西.这次翻出来还是自家小姑姑要做winfrom但是苦于sqlse ...

  3. 用POST方法上传文件

    文件上传分为客户端和服务器端 客户端可以通过form表单进行上传 客户端使用html表单进行上传 enctype = "multipart/form-data"用来指定表单编码数据 ...

  4. kolla-ansible-----rally模块

    Rally简介 Rally是OpenStack社区推出开源测试工具,可用于对OpenStack各个组件进行性能测试. 通过使用Rally组件,用户可完成OpenStack云计算平台的安装部署.功能验证 ...

  5. smarty模板引擎之if, elseif else

    Smarty 中的 if 语句和 php 中的 if 语句一样灵活易用,并增加了几个特性以适宜模板引擎. if 必须于 /if 成对出现. 可以使用 else 和 elseif 子句. 可以使用以下条 ...

  6. 《zabbix监控的搭建》centos5.8 32

    系统环境centos5.8 32位操作系统   这里以zabbix-2.2.7为例: 下载官方的软件包: http://pan.baidu.com/s/1ntuTRYh 官方的参考文档: https: ...

  7. SQLServer2008 开启远程连接

    关闭防火墙 基本的设置可以参考下面的链接: http://wenku.baidu.com/link?url=qjZKZCCoa5T3EGd_rqSjl6Tuhb1wYjIHyXri630QxuAIKu ...

  8. ADO学习笔记之注入漏洞与参数化查询

    ADO学习笔记之注入漏洞与参数化查询 作为新手,在学习ADO程序时,使用 sql 语言查询数据时,很容易写类似如下代码: using (SqlConnection con = new SqlConne ...

  9. WPF使用Aspose.Words导出Word文档

    一.创建Word文档模板 分析需要导出的word文档,将固定的内容和由程序生成的内容分开; 创建一个word(例如:Template.doc)文档,将固定的内容按照一定的格式写入当前文档中; 打开Te ...

  10. 基于FCM的消息推送功能

    需求背景 我方项目需要支持客户端消息推送,iOS终端可以借由苹果本身的apns很方便的实现,但是对于Android来说,必须集成第三方的SDK来处理.考虑到项目需要以及成本,我们选择使用谷歌的FCM框 ...