一:背景

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某企业数字化平台 崩溃分析的更多相关文章

  1. 如何通过免费开源ERP Odoo实现企业数字化转型深度分析(一)

    本文来自<开源智造企业数字化转型报告白皮书>的精选内容章节.请勿转载.欢迎您反馈阅读意见. 引言 在由消费者驱动的数字经济时代,创新之势锐不可挡.变革步伐从未如此迅速,并且还会越来越快.对 ...

  2. 记一次 .NET 某医疗器械 程序崩溃分析

    一:背景 1.讲故事 前段时间有位朋友在微信上找到我,说他的程序偶发性崩溃,让我帮忙看下怎么回事,上面给的压力比较大,对于这种偶发性崩溃,比较好的办法就是利用 AEDebug 在程序崩溃的时候自动抽一 ...

  3. 记一次 .NET 某医疗住院系统 崩溃分析

    一:背景 1. 讲故事 最近收到了两起程序崩溃的dump,查了下都是经典的 double free 造成的,蛮有意思,这里就抽一篇出来分享一下经验供后面的学习者避坑吧. 二:WinDbg 分析 1. ...

  4. 企业数字化转型与SAP云平台

    我们生活在一个数字化时代.信息领域里发展迅猛的数字技术和成本不断降低的硬件设备,正以前所未有的方式改变着我们工作和生活的方式. Digital Mesh 美国一家著名的从事信息技术研究和提供咨询服务的 ...

  5. MADP(移动应用开发平台)推动企业数字化转型

    移动互联网时代,企业对于移动应用程序的需求呈现爆炸式增长,移动解决方案供应商一直致力于寻找解决方案帮助企业完成这些移动集成需求,MADP(移动应用开发平台)因此产生,MADP允许提供一种解决方案,可以 ...

  6. BPM助力企业数字化转型

    自九十年代末,流程管理开始引入国内,至今已经有20多年的历史了,由最初的部门级应用向企业级应用转变,大家的认知也经历了一系列的发展变化.不同阶段的信息化水平对企业的流程以及BPM平台也提出了不同的需求 ...

  7. 灵雀云受邀加入VMware 创新网络,共同助力企业数字化进程

        11月15日,在VMware主办的“VMware创新网络”2018高峰论坛上,VMware发布了VMware创新网络(VMwareInnovation Network,VIN)的长期发展规划和 ...

  8. ActiveReports 大数据分析报告:贸易争端与中国企业数字化转型

    2018年11月12日至18日,亚太经合组织(APEC)领导人非正式会议首次在南太平洋最大岛国巴布亚新几内亚的首都莫尔兹比港举行,本次会议的主题是:“把握包容性机遇,拥抱数字化未来”. 面对全球不断变 ...

  9. .NET CORE 2.0小白笔记(三):数字化平台之微信平台策略

    当下,互联网技术正在深刻地重构我们的社会,各大企事业单位——大到万人集团公司,小到图文复印店——都在争先恐后地从所谓的“传统行业”中脱胎换骨一番以完成数字化转型. 在这个过程中,“企业即IT”.“科技 ...

  10. 华为云PaaS首席科学家:Cloud Native +AI,企业数字化转型的最佳拍档

    近日,在2019华为全球分析师大会期间,华为云PaaS首席科学家熊英博士在+智能,见未来(华为云&大数据)的分论坛上,从云计算行业发展谈起,深入云原生发展趋势,对华为云智能应用平台做了深度解读 ...

随机推荐

  1. C++调用Python-2:调用Python函数,返回数字

    # mytest.py def hello(): print("this is test python print hello world") return 123 #includ ...

  2. Django Admin:自动选择当前用户

    --转载自:林肯李 该文章写的很好,特转载留待后期备用 背景 在开发 CMS 时,经常需要标记谁创建了记录.Django 为我们提供了一个很好的管理界面.但是当我们只使用默认值时,用户需要自己选择他们 ...

  3. K8S 性能优化-K8S Node 参数调优

    前言 K8S 性能优化系列文章,本文为第四篇:Kubernetes Node 性能优化参数最佳实践. 系列文章: <K8S 性能优化 - OS sysctl 调优> <K8S 性能优 ...

  4. VSCode如何通过Ctrl+P快速打开node_modules中的文件

    背景 咱们新建一个NodeJS项目,必然会安装许多依赖包,因此经常需要查阅某些依赖包的源码文件.但是,由于node_modules目录包含的文件太多,出于性能考虑,在VSCode中默认情况下是禁止搜索 ...

  5. The First 寒假集训の小总结

    转眼间十五天的寒假集训已经结束,也学习到了许多新知识,dp,线段树,单调栈和单调队列......,假期过得还是很有意义的,虽然我的两次考试成绩不尽人意(只能怪我自己没有好好理解知识点还有好好做题),但 ...

  6. How to install Django-Install Python Django | Django 安装指南【官方版】

    How to install Django¶ This document will get you up and running with Django. Install Python--Linux ...

  7. redis 简单整理——阻塞问题[二十五]

    前言 简单介绍一下redis的阻塞问题. 正文 Redis是典型的单线程架构,所有的读写操作都是在一条主线程中完成 的.当Redis用于高并发场景时,这条线程就变成了它的生命线.如果出现 阻塞,哪怕是 ...

  8. leetcode:655. 输出二叉树

    655. 输出二叉树 在一个 m*n 的二维字符串数组中输出二叉树,并遵守以下规则: 1> 行数 m 应当等于给定二叉树的高度. 2> 列数 n 应当总是奇数. 3> 根节点的值(以 ...

  9. 初识大数据技术之Hadoop

    先上一张图: 看到这张图,我脑子里出现的第一个东西就是:这货太像旅行商问题了 有限的输入与有限的输出,当输入大于一定数值时,输出趋向于无法计算.... 其实要我说啊,旅行商问题其实没必要管他,因为这个 ...

  10. Vue3.0里为什么要用 Proxy API 替代 defineProperty API

    一.Object.defineProperty 定义:Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象 为什么能实现响应式 ...