PerfView 洞察那些 C# 代码中的短命线程

news2024/11/28 2:43:44

一:背景

1. 讲故事

这篇文章源自于分析一些疑难dump的思考而产生的灵感,在dump分析中经常要寻找的一个答案就是如何找到死亡线程的生前都做了一些什么?参考如下输出:


0:001> !t
ThreadCount:      22
UnstartedThread:  0
BackgroundThread: 1
PendingThread:    0
DeadThread:       20
Hosted Runtime:   no
                                                                         Lock  
       ID OSID ThreadOBJ    State GC Mode     GC Alloc Context  Domain   Count Apt Exception
   0    1 3a74 00efb368     2a020 Preemptive  02F2AF48:00000000 00ec2fa0 1     MTA 
   5    2 6758 00f07a48     2b220 Preemptive  00000000:00000000 00ec2fa0 0     MTA (Finalizer) 
XXXX    3    0 00f31df0   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX    4    0 00f34080   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX    5    0 00f363a8   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX    6    0 00f372e8   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX    7    0 00f39f80   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX    8    0 00f3cbd0   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX    9    0 00f3d128   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   10    0 00f40630   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   11    0 00f43310   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   12    0 00f42db8   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   13    0 00f49180   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   14    0 00f4a228   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   15    0 00f53a28   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   16    0 00f56598   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   17    0 00f59180   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   18    0 00f59b28   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   19    0 00f5e8a0   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   20    0 00f5f248   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   21    0 00f63fc0   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 
XXXX   22    0 00f66b50   1039820 Preemptive  00000000:00000000 00ec2fa0 0     Ukn (Threadpool Worker) 

前面的 XXXX 代表线程已死亡,那谁能告诉我 ID=22 的线程生前执行了什么代码呢?其实去年我写了一篇如何用 WinDbg 去寻找程序中的短命线程。 TTD 专题 (第一篇):C# 那些短命线程都在干什么?

虽然可以用 WinDbg 的 TTD 来解决,但也有很多的限制,诸如:

  • 生产环境不能安装 windbg 或者 安装不上
  • 不能对生产程序进行附加

所以这两点也制约了 TTD 的强大威力,那有没有轻量级以及无侵入的方式洞察呢?最近在看 perfview 的文档,发现完全可以使用内核中Thread 的 ETW相关事件来搞定。

二:Thread 的ETW事件

1. 使用 Thread 的短命线程

如果死亡线程背后没有标记 Threadpool Worker 的话,那就说明是代码自己用 new Thread 创建出来的线程,这种比较简单,观察 Windows Kernel/Thread/Start 或者 Microsoft-Windows-DotNETRunning/Thread/Creating 的ETW事件即可。

接下来写一段简单的案例代码:


    internal class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 5000; i++)
            {
                Test1();
            }

            Console.ReadLine();
        }
        public static int index = 1;

        public static void Test1()
        {
            new Thread(() => { Test2(); }).Start();
        }

        public static void Test2()
        {
            Thread.Sleep(10);

            var i = 10;
            var j = 20;

            var sum = i + j;

            Console.WriteLine($"i={index++},sum={sum}");
        }
    }

代码非常简单,用 new Thread 创建了一个短命线程,接下来打开 Perfview 使用默认配置,完整的 Command 命令如下:


PerfView.exe  "/DataFile:PerfViewData.etl" /BufferSizeMB:256 /StackCompression /CircularMB:500 /ClrEvents:GC,Binder,Security,AppDomainResourceManagement,Contention,Exception,Threading,JITSymbols,Type,GCHeapSurvivalAndMovement,GCHeapAndTypeNames,Stack,ThreadTransfer,Codesymbols,Compilation /NoGui /NoNGenRundown /Merge:True /Zip:True collect

程序跑完后可以用 WinDbg 的 !t 去看看凌乱现场,可以发现有大量的 XXX 线程。


0:008> !t
ThreadCount:      1386
UnstartedThread:  0
BackgroundThread: 2
PendingThread:    0
DeadThread:       1383
Hosted Runtime:   no
                                                                             Lock  
 DBG   ID     OSID ThreadOBJ    State GC Mode     GC Alloc Context  Domain   Count Apt Exception
   0    1     4114 02CAC9C8     2a020 Preemptive  0559F108:0559FFEC 02c9c488 -00001 MTA 
   6    2     31b4 02CBA5F0     2b220 Preemptive  00000000:00000000 02c9c488 -00001 MTA (Finalizer) 
XXXX    3        0 02CCEC48     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX  694        0 116C5B18     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX  695        0 116C0578     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX  696        0 116C1250     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX  697        0 116BF8A0     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX  698        0 116C5F60     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX  699        0 116C38D8     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX  700        0 116C74C8     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
...
XXXX 1380        0 115097C0     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX 1381        0 115079C8     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX 1382        0 1150B170     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX 1383        0 1150AD28     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX 1384        0 11508258     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
XXXX 1385        0 11505BD0     39820 Preemptive  00000000:00000000 02c9c488 -00001 Ukn 
   7 1386     6114 1150CF68     20220 Preemptive  055A07A8:055A1FEC 02c9c488 -00001 Ukn 

收集一会之后停止收集,选中Thread的内核事件 Thread/Start,截图如下:

从卦中可以看到有大量的 Start 事件,我们挑选其中一个观察下线程栈,右键 Open Any Stacks 看看到底是什么代码发出了这个 ETW 事件,截图如下:

从卦中可以清晰的看到,原来是 Main() -> Test1() 方法创建的哈,终于水落石出。

2. 使用 ThreadPool 的短命线程

在真实场景中也有很多代码是用 ThreadPool 创建出来的短命线程,这种短命线程其实有一个特点,那就是曾经有大量的任务进队列,导致 ThreadPool 被迫生成很多的线程来应付,当任务全部被消灭后,ThreadPool 就会把那些被迫生成的线程全部给裁掉

卸磨杀驴,真的好像我们的职场/(ㄒoㄒ)/~~。

所以突破点就是统计下 ThreadPoolEnqueueWork 事件,有了思路之后修改下测试代码。


        public static void Test1()
        {
            Task.Run(() => { Test2(); });
        }

这里有一个注意点,程序跑完之后还要稍等一两分钟,就是让ThreadPool把多余的Thread给灭掉,用 windbg 观察到的效果图就是 讲故事 那一节的,停止 perfview 收集后,寻找 ThreadPoolEnqueueWork 事件,截图如下:

从卦中可以看到有大量的 ThreadPoolEnqueueWork 事件,接下来可以选择右键菜单 Save View as Excel 导出到 Excel 中,然后对 Time Msec 进行分组排序,看下哪一个时间段有大量的任务进队列,指标高的时间段自然就是重点怀疑的。

这里要说一点 Time MSecTrace Start Time 基础上的毫秒级偏移值。

举个例子: 4377.032 (4.37s) + 15:56:25.566 = 15:56:29.866

有了这些概念之后,找到问题区域的进队任务,观察下调用栈,大概率也能找到问题,从调用栈来看,原来是 Test1() 所致哈。。。 截图如下:

三:总结

相比WinDbg TTD的重模式,Perfiew真的很轻,而且无侵入性,这两个工具真的是珠联璧合,相得益彰。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/769570.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浏览器打开新的页面时自动打开控制台

需求 打开浏览器新tab时自动打开控制台&#xff0c;捕捉初次的网络请求 解决 在浏览器图标属性中加入以下代码&#xff0c;再次打开浏览器 --auto-open-devtools-for-tabs

Django实现接口自动化平台(十四)测试用例模块Testcases序列化器及视图【持续更新中】

相关文章&#xff1a; Django实现接口自动化平台&#xff08;十三&#xff09;接口模块Interfaces序列化器及视图【持续更新中】_做测试的喵酱的博客-CSDN博客 本章是项目的一个分解&#xff0c;查看本章内容时&#xff0c;要结合整体项目代码来看&#xff1a; python django…

1000+设计施工模型免费下载,助力设计方案制作和汇报场景搭建!

作为一名工程设计、施工人员&#xff0c;设计方案制作、工程汇报场景搭建的情景再常见不过。日常需要的模型是必不可少的&#xff0c;但最令人头大的问题是如何寻找方案素材。想要表达的信息越多&#xff0c;素材获取就越是苦恼&#xff01; 有没有一款软件能够集方案三维汇报…

边缘计算:连接物理与数字世界的智能桥梁

引言&#xff1a; 边缘计算&#xff08;Edge Computing&#xff09;作为一种分布式计算模型&#xff0c;旨在将数据处理和分析推向网络边缘设备。随着物联网和大数据的快速发展&#xff0c;边缘计算成为了解决数据处理延迟、网络带宽压力和隐私安全等问题的重要技术。本文将深入…

OLED拼接屏采购指南:如何选择最佳方案?

OLED拼接屏作为一种创新的大屏幕显示设备&#xff0c;正在成为各行各业信息展示和传播的重要工具。 然而&#xff0c;面对市场上众多的品牌和型号&#xff0c;如何选择最佳的OLED拼接屏方案成为一项关键任务。 本文将为您提供一份全面且实用的OLED拼接屏采购指南&#xff0c;…

pdf怎么转换为word格式?这5个实用的方法分享!

在现代数字化时代&#xff0c;PDF&#xff08;Portable Document Format&#xff09;文件已经成为广泛使用的文件格式之一。由于其固定的格式和可移植性&#xff0c;PDF文件在共享和传输文档时非常方便。然而&#xff0c;当我们需要编辑或修改PDF文件时&#xff0c;PDF的固定格…

菜品管理模块开发 -- 手把手教你做ssm+springboot入门后端项目黑马程序员瑞吉外卖(五)

文章目录 前言一、文件上传下载1. 文件上传介绍2. 文件下载介绍3. 实现文件上传功能4. 实现文件下载功能5. 上传下载图片效果预览 二、新增菜品1. 需求分析2. 数据模型3. 代码开发4. 功能测试 三、菜品信息分页查询1. 需求分析2. 代码开发3. 功能测试 四、修改菜品1. 需求分析2…

3.JAVA BIO深入剖析

highlight: arduino-light 1.JAVA BIO深入剖析 1.1 Java BIO 基本介绍 Java BIO 就是传统的 java io 编程&#xff0c;其相关的类和接口在 java.ioBIO(blocking I/O) &#xff1a; 同步阻塞&#xff0c;服务器实现模式为一个连接一个线程&#xff0c;即客户端有连接请求时服务器…

东莞-戴尔R540服务器故障告警处理方法

DELL PowerEdge R540服务器故障维修案例&#xff1a;&#xff08;看到文章就是缘分&#xff09; 客户名称&#xff1a;东莞市某街道管理中心 故障机型&#xff1a;DELL R540服务器 故障问题&#xff1a;DELL R540服务器无法开机&#xff0c;前面板亮黄灯&#xff0c;工程师通过…

PatchMatchNet运行dtu数据集、

文章目录 1 准备工作2 dtu数据集重建演示2.1 bash文件超参数解释2.2 lists/dtu解释1 准备工作 MVSNet、PatchMatchNet环境配置 数据集下载,准备好数据集,我的dtu数据集放在/PatchmatchNet-main/Data/testData/dtu/下,如图所示 进入mvs 环境:source activate mvs 2 dtu数据…

Appium 安卓环境的配置

目录 前言&#xff1a; 环境准备 写个脚本玩玩 前言&#xff1a; 在使用Appium进行安卓自动化测试之前&#xff0c;需要配置相应的安卓环境。 环境准备 为了避免走弯路&#xff0c;我们先要确保三点&#xff1a; Android SDK API > 17 (Additional features require …

14matlab数理统计 多项式的求根和根据根求多项式(matlab程序)

1.简述 分享一下通过多种不同的方法计算多项式的根。 数值根 使用代换法求根 特定区间内的根 符号根 数值根 roots 函数用于计算系数向量表示的单变量多项式的根。 例如&#xff0c;创建一个向量以表示多项式 x2−x−6&#xff0c;然后计算多项式的根。 p [1 -1 -6]; r …

利用Python和Selenium编程,实现定时自动检索特定网页,发现特定网页内容发生变化后,向管理员发送提醒邮件(一)

一、项目需求 要求爬取某单位网站&#xff0c;登录后台查看是否有新增“网友提问”&#xff0c;如果有新的提问&#xff0c;向特定邮箱发出提醒邮件。 二、项目分析 &#xff08;一&#xff09;判断是否可用爬虫爬取相关内容 首先查看该网站的robots.txt文件&#xff0c;发现…

SpringCloud(四)Hystrix服务降级、熔断、监控页面

一、服务熔断 官方文档&#xff1a;https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/1.3.5.RELEASE/single/spring-cloud-netflix.html#_circuit_breaker_hystrix_clients 我们知道&#xff0c;微服务之间是可以进行相互调用的&#xff0c;那么如果出现了…

YUM报错:Could not retrieve mirrorlist

​​​​​​背景说明 ESXI新安装CentOS7&#xff0c;无法执行yum命令 报错内容解决方案 1.报错说明&#xff1a;问题在于yum无法获取CentOS的软件仓库地址&#xff0c;导致无法找到合法的baseurl2.检查网络连接&#xff1a;确保服务器可以访问互联网&#xff0c;以及能够解析D…

C++ CEF库 源码编译及使用(VS2019)

源码编译 官网下载源码 CEF Automated Builds

linux下mmdetection安装

linux下mmdetection安装 使用 MIM 安装 MMEngine 和 MMCV。安装 MMDetection 前提条件是配置了pytorch18的linux环境 参考流程&#xff1a;配置pycharm环境 拷贝pycharm环境为mmdet conda create -n mmdet --clone torch18进入 conda activate mmdet使用 MIM 安装 MMEngine …

量化基础 PTQ QAT

简介 固定bit下的量化始终无法在Accuracy和 (FLOPs & Parameters)之间达到一个非常细粒度的trade-off&#xff0c;所以就需要混合精度量化(Mixed-Precision Quantization, MPQ)来对模型实现进一步的高效压缩 混合精度量化区别于混合精度训练这个概念&#xff0c;后者指的是…

c基本数据类型

关键字 charshort intintlong intfloatdouble 常量和变量 常量&#xff1a;在程序运行过程中&#xff0c;其值不可改变的量变量&#xff1a;其值可以改变的量称为变量 字符数据 字符常量 直接常量&#xff1a;用单引号括起来&#xff0c;如&#xff1a;‘a’,‘b’.转义字…

7.4Java EE——Bean的作用域

一、singleton作用域 Spring支持的5种作用域 作用域名城 描述 singleton 单例模式。在单例模式下&#xff0c;Spring 容器中只会存在一个共享的Bean实例&#xff0c; 所有对Bean的请求&#xff0c;只要请求的id&#xff08;或name&#xff09;与Bean的定义相匹配&#xff0…