记一次 .NET某企业数字化平台 崩溃分析
一:背景
1. 讲故事
前些天群里有一个朋友说他们软件会偶发崩溃,想分析看看是怎么回事,所幸的是自己会抓dump文件,有了dump就比较好分析了,接下来我们开始吧。
二:WinDbg 分析
1. 程序为什么会崩溃
windbg 还是非常强大的,当你双击打开的时候会自动帮你定位过去展示崩溃时刻的寄存器和线程栈上下文,都省了 !analyze -v
命令分析了,输出如下:
Loading unloaded module list
...............
This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
(1dc.774): Stack overflow - code c00000fd (first/second chance not available)
For analysis of this file, run !analyze -v
000007f8`93111989 837c243000 cmp dword ptr [rsp+30h],0 ss:0000007b`e7894160=00000000
从卦中可以看到有一个 Stack overflow
异常,说明当前栈溢出了,有点意思。
2. 栈溢出了吗
如果你想探究下栈溢出也是可以的,用 rsp 比较下 !teb 中的 StackLimit 值。
0:019> r rsp
rsp=0000007be7894130
0:019> !teb
TEB at 000007f6cd664000
ExceptionList: 0000000000000000
StackBase: 0000007be7a10000
StackLimit: 0000007be7891000
SubSystemTib: 0000000000000000
FiberData: 0000000000001e00
ArbitraryUserPointer: 0000000000000000
Self: 000007f6cd664000
EnvironmentPointer: 0000000000000000
ClientId: 00000000000001dc . 0000000000000774
RpcHandle: 0000000000000000
Tls Storage: 0000007be84b5b90
PEB Address: 000007f6cd7af000
LastErrorValue: 0
LastStatusValue: c0000302
Count Owned Locks: 0
HardErrorMode: 0
0:019> !address -f:Stack
BaseAddress EndAddress+1 RegionSize Type State Protect Usage
--------------------------------------------------------------------------------------------------------------------------
7b`e7890000 7b`e7891000 0`00001000 MEM_PRIVATE MEM_RESERVE Stack [~19; 1dc.774]
7b`e7891000 7b`e7a10000 0`0017f000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE Stack [~19; 1dc.774]
从卦中看 PAGE_GUARD 页已经抹掉了,这就表示当前的 rsp 已经进入到这个 0x3000
大小的 PAGE_GUARD 页面里去了。
有些朋友可能会有一个疑问,这个异常是怎么被界定为 StackOverflowException
的呢? 如果你了解哨兵页就比较简单了,一旦rsp进了这个哨兵页,在这里抛出的异常会被界定为 c00000fd
,最后这个异常会被 coreclr 的 MapWin32FaultToCOMPlusException 方法强制转为托管的 StackOverflowException
异常,这个都是有源码支撑的。
EXCEPTION_RECORD: (.exr -1)
ExceptionAddress: 000007f8ed571a90 (coreclr!MetaDataImport::Enum+0x0000000000000030)
ExceptionCode: c00000fd (Stack overflow)
ExceptionFlags: 00000001
NumberParameters: 2
Parameter[0]: 0000000000000001
Parameter[1]: 0000007be7893f38
DWORD MapWin32FaultToCOMPlusException(EXCEPTION_RECORD *pExceptionRecord)
{
switch (pExceptionRecord->ExceptionCode)
{
...
case STATUS_STACK_OVERFLOW:
return (DWORD) kStackOverflowException;
....
default:
return kSEHException;
}
}
3. 到底谁给弄溢出了
现在我们定位到的线程就是栈溢出线程,使用 kc 观察调用栈,输出如下:
0:019> kc
# Call Site
00 System_Private_CoreLib!System.Reflection.RuntimeCustomAttributeData.GetCustomAttributeRecords
01 System_Private_CoreLib!System.Reflection.CustomAttribute.AddCustomAttributes
02 System_Private_CoreLib!System.Reflection.CustomAttribute.GetCustomAttributes
03 System_Private_CoreLib!System.Attribute.GetCustomAttributes
...
0c SqlSugar!SqlSugar.MemberExpressionResolve..ctor
0d SqlSugar!SqlSugar.BaseResolve.Start
0e SqlSugar!SqlSugar.BinaryExpressionResolve.Right
0f SqlSugar!SqlSugar.BinaryExpressionResolve.DefaultBinary
10 SqlSugar!SqlSugar.BinaryExpressionResolve.Other
11 SqlSugar!SqlSugar.BinaryExpressionResolve..ctor
12 SqlSugar!SqlSugar.BaseResolve.Start
13 SqlSugar!SqlSugar.BinaryExpressionResolve.Right
14 SqlSugar!SqlSugar.BinaryExpressionResolve.DefaultBinary
15 SqlSugar!SqlSugar.BinaryExpressionResolve.Other
16 SqlSugar!SqlSugar.BinaryExpressionResolve..ctor
17 SqlSugar!SqlSugar.BaseResolve.Start
...
默认的 kc 只能显示 255 个线程栈,在栈溢出场景下没办法完全展开,不管怎么样从栈看貌似是 SqlSugar 导致的栈溢出,那它是这次灾难的罪魁祸首吗?
4. SqlSugar 是祸首吗
要想找到这个答案,需要看下 SqlSugar 是被怎样的用户代码调用的,有两种办法,要么在 k 上设置 StackPtr,要么设置最大的栈个数 0xffff ,这里选择后者。
0:019> kc 0xffff
# Call Site
....
145b SqlSugar!SqlSugar.ExpressionContext.Resolve
145c SqlSugar!SqlSugar.QueryBuilder.GetExpressionValue
145d SqlSugar!SqlSugar.QueryableProvider<xxx>._Where
145e SqlSugar!SqlSugar.QueryableProvider<xxxx>.Where
145f SqlSugar!SqlSugar.SimpleClient<xxx>.GetListAsync
1460 xxx!xxx.TSqlSugar.xxx<xxx.Entities.Quality.xxxSummaryEntity>.<QueryAsync>d__37.MoveNext
1461 System_Private_CoreLib!System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start<<QueryAsync>d__37>
1462 xxx!xxx.TSqlSugar.BaseRepository<xxx.xxxSummaryEntity>.QueryAsync
1463 xxx!xxxxCalculateService.<xxxnRate>d__26.MoveNext
...
从卦中可以看到有一个 xxxxCalculateService 用户类调用了 QueryAsync 方法,接下来直接到源码定位,截图如下:
这段代码乍一看貌似没有问题,但仔细看还是有一些端倪的,对,就是当 diffMonth
很大时, expressionable 就会累计出很多的 And 条件,在QueryAsync的时候底层的 SqlSugar 在拆解 expressionable 的过程中抛出了异常。
5. SqlSugar 真的在拆解中异常了吗
拆解表达式树的代码太难了,我真的看不懂,在这种情况下如何寻找突破口呢?这里可以逆向的想一想,既然是拆解,自然就会产生很多小段sql,所以直接到 托管堆中看下当前的 string 情况即可。
0:019> !strings
Address Gen Length Value
---------------------------------------
...
0000007bc15a0240 LOH 97005 ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((...
0000007bc15cf850 LOH 97005 ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((...
0000007bc15fee60 LOH 97009 ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((...
0000007bc162e478 LOH 97009 ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((...
0000007bc165da90 LOH 97074 ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((...
0000007bc168d130 LOH 97099 ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((...
0000007bc16bc800 LOH 97099 ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((...
0000007bc16ebed0 LOH 97103 ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((...
0000007bc171b5a8 LOH 97103 ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((...
0000007bc174ac80 LOH 97113 ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((...
....
---------------------------------------
39498 strings
从卦中看真厉害,有很多 近10w 左右的 string,拆开 string 看正是And中的表达式树里的字段,这里就不展示了。
三:总结
这次程序崩溃主要是朋友的奇葩写法导致 SqlSugar 在拆解表达式树的时候抛了异常,个人觉得底层最好把 递归
改成 循环
之类的避免栈溢出,看了下SqlSugar版本 File version: 5.1.4.143
还是比较新的,所以先建议朋友换写法观察看看。
记一次 .NET某企业数字化平台 崩溃分析的更多相关文章
- 如何通过免费开源ERP Odoo实现企业数字化转型深度分析(一)
本文来自<开源智造企业数字化转型报告白皮书>的精选内容章节.请勿转载.欢迎您反馈阅读意见. 引言 在由消费者驱动的数字经济时代,创新之势锐不可挡.变革步伐从未如此迅速,并且还会越来越快.对 ...
- 记一次 .NET 某医疗器械 程序崩溃分析
一:背景 1.讲故事 前段时间有位朋友在微信上找到我,说他的程序偶发性崩溃,让我帮忙看下怎么回事,上面给的压力比较大,对于这种偶发性崩溃,比较好的办法就是利用 AEDebug 在程序崩溃的时候自动抽一 ...
- 记一次 .NET 某医疗住院系统 崩溃分析
一:背景 1. 讲故事 最近收到了两起程序崩溃的dump,查了下都是经典的 double free 造成的,蛮有意思,这里就抽一篇出来分享一下经验供后面的学习者避坑吧. 二:WinDbg 分析 1. ...
- 企业数字化转型与SAP云平台
我们生活在一个数字化时代.信息领域里发展迅猛的数字技术和成本不断降低的硬件设备,正以前所未有的方式改变着我们工作和生活的方式. Digital Mesh 美国一家著名的从事信息技术研究和提供咨询服务的 ...
- MADP(移动应用开发平台)推动企业数字化转型
移动互联网时代,企业对于移动应用程序的需求呈现爆炸式增长,移动解决方案供应商一直致力于寻找解决方案帮助企业完成这些移动集成需求,MADP(移动应用开发平台)因此产生,MADP允许提供一种解决方案,可以 ...
- BPM助力企业数字化转型
自九十年代末,流程管理开始引入国内,至今已经有20多年的历史了,由最初的部门级应用向企业级应用转变,大家的认知也经历了一系列的发展变化.不同阶段的信息化水平对企业的流程以及BPM平台也提出了不同的需求 ...
- 灵雀云受邀加入VMware 创新网络,共同助力企业数字化进程
11月15日,在VMware主办的“VMware创新网络”2018高峰论坛上,VMware发布了VMware创新网络(VMwareInnovation Network,VIN)的长期发展规划和 ...
- ActiveReports 大数据分析报告:贸易争端与中国企业数字化转型
2018年11月12日至18日,亚太经合组织(APEC)领导人非正式会议首次在南太平洋最大岛国巴布亚新几内亚的首都莫尔兹比港举行,本次会议的主题是:“把握包容性机遇,拥抱数字化未来”. 面对全球不断变 ...
- .NET CORE 2.0小白笔记(三):数字化平台之微信平台策略
当下,互联网技术正在深刻地重构我们的社会,各大企事业单位——大到万人集团公司,小到图文复印店——都在争先恐后地从所谓的“传统行业”中脱胎换骨一番以完成数字化转型. 在这个过程中,“企业即IT”.“科技 ...
- 华为云PaaS首席科学家:Cloud Native +AI,企业数字化转型的最佳拍档
近日,在2019华为全球分析师大会期间,华为云PaaS首席科学家熊英博士在+智能,见未来(华为云&大数据)的分论坛上,从云计算行业发展谈起,深入云原生发展趋势,对华为云智能应用平台做了深度解读 ...
随机推荐
- openGauss数据库将磁盘表转换为MOT
openGauss 数据库将磁盘表转换为 MOT 一.将磁盘表转换为 MOT 方法 磁盘表直接转换为 MOT 尚不能实现,这意味着尚不存在将基于磁盘的表转换为 MOT 的 ALTER TABLE 语句 ...
- 重新点亮shell————测试命令[六]
前言 我们写好脚本之后希望有一个测试. 正文 介绍一下exit,如果exit 0,那么表示正常退出. 如果exit 10,也就是非0,那么就是异常退出. 然后这个test 标准为[]和 [[]]这样子 ...
- locust分布式压测的Step Load及no web模式下的报表自动生成
Running Locust in Step Load ModeIf you want to monitor your service performance with different user ...
- 【笔记】Oracle 窗口函数
Oracle 窗口函数 简单来说,窗口函数是分析函数的一种,通常可以理解成over()函数 构成:函数名①() over(partition by 分组的列名 order by 排序的列名 XXX) ...
- 力扣577(MySQL)-员工奖金(简单)
题目: 问题:选出所有 bonus < 1000 的员工的 name 及其 bonus.Employee 表单,empId 是这张表单的主关键字 Bonus 表单,empId 是这张表单的主关键 ...
- Istio 从懵圈到熟练:二分之一活的微服务
作者 | 声东 阿里云售后技术专家 <关注阿里巴巴云原生公众号,回复 排查 即可下载电子书> <深入浅出 Kubernetes>一书共汇集 12 篇技术文章,帮助你一次搞懂 ...
- 探秘RocketMQ源码——Series1:Producer视角看事务消息
简介: 探秘RocketMQ源码--Series1:Producer视角看事务消息 1. 前言 Apache RocketMQ作为广为人知的开源消息中间件,诞生于阿里巴巴,于2016年捐赠给了Apac ...
- [FAQ] Vue 如何控制标签元素的某个属性的显示 ?
这需要借助 v-model 的用法,动态决定元素的展示. <q-btn :disable="2 > 1">按钮</q-btn> 展示结果是:<q ...
- WPF 使用 Silk 的 Direct2D 入门
在上一篇博客的基础上,使用 dotnet 基金会新开源的 Silk.NET 库,让 Silk.NET 创建的 DX 设备和 WPF 对接渲染.接下来本文将告诉大家如何使用 Silk.NET 提供的 D ...
- 【2023微博评论爬虫】用python爬上千条微博评论,突破15页限制!
目录 一.爬取目标 二.展示爬取结果 三.爬虫代码 四.同步视频 五.获取完整源码 您好,我是@马哥python说,一枚10年程序猿. 一.爬取目标 前些天我分享过一篇微博的爬虫: 马哥python说 ...