概述
一次 IIS w3wp 崩溃 dump 的完整分析记录:AjaxPro 同步请求触发业务层 E_FAIL 异常,经 AOP 与 AjaxPro 两层反射 invoke 包装成三层 TargetInvocationException 冒泡至宿主顶层。全程使用 cdb + SOS 完成定位,含 DAC 版本不匹配的实用 workaround。
现场
| 项 | 值 |
|---|---|
| 进程 | w3wp.exe(IIS 工作进程),81+ 线程 |
| 宿主系统 | Windows 10 19044 (21H2) x64,12 逻辑处理器 |
| .NET 运行时 | .NET Framework 4.8,clr.dll 4.8.4614.0(NET48REL1LAST_B),webengine4 4.8.4494.0 |
| 业务应用 | 用友 Ufida.T.EAP / Ufida.T.BAP(门户 Portal,AjaxPro AJAX 框架) |
| 触发异常 | CLR exception e0434352(未处理托管异常),内嵌 HRESULT 0x80131604 = COR_E_TARGETINVOCATION |
| Dump | minidump,6.3MB(由进程内安装的崩溃收集组件生成) |
异常链:三层包裹剥洋葱
dump 记录的最终异常是 COR_E_TARGETINVOCATION,但它只是最外层包装。通过 db 直读异常对象内存 + UTF-16 解码 _stackTraceString,还原出完整三层链:
第 0 层(根因):ExternalException,HResult 0x80004005 (E_FAIL)
在 Ufida.T.BAP.BusinessApplication.PagePreCondition.PagePreCondition.get_Type()
在 Ufida.T.BAP.BusinessApplication.PagePreCondition.PagePreConditionFactory.Create(String url, String code, String[] busOperations, Object[] pars)
在 Ufida.T.BAP.BusinessApplication.PagePreCondition.Service.PagePreConditionService.Validate(String url, String code, String[] busOperations, Object[] pars)
异常抛在业务应用的 PagePreCondition.get_Type() 属性 getter 里。E_FAIL 0x80004005「外部组件发生异常」是 COMException/ExternalException 的默认文案——典型来源是 getter 内部的 COM/interop 调用失败。
第 1 层:AOP 反射包装
System.RuntimeMethodHandle.InvokeMethod → RuntimeMethodInfo.Invoke → MethodBase.Invoke
Ufida.T.EAP.Aop.Util.ServiceMethodResvol.SimpleAutoTranInvoke
Ufida.T.EAP.Aop.Util.ServiceMethodResvol.InvokeNormalMethod
Ufida.T.EAP.Aop.Dynamic.LocalCallDynamicProxyImpl.NormalInvoke / Invoke
System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke
... → Portal.GetMutexInfo → Portal.ReregMutex
应用的 AOP 动态代理通过反射 invoke 调用服务方法,异常被自动包了一层 TargetInvocationException。
第 2 层:AjaxPro 反射包装(dump 记录的最终异常)
AjaxPro.AjaxProcHelper.Run()
AjaxPro.AjaxSyncHttpHandler.ProcessRequest(HttpContext)
System.Web.HttpApplication.*(ExecuteStep/PipelineStepManager.ResumeSteps 等)
System.Web.HttpRuntime.ProcessRequestNotificationPrivate → PipelineRuntime.ProcessRequestNotification(IIS 集成管线)
AjaxPro 的同步 handler 再次反射调用,又包一层。最终异常沿 IIS 集成管线冒泡到宿主顶层,无人 catch。
原生栈(.ecxr)
KERNELBASE!RaiseException+0x69
clr!…(异常分发内部)
webengine4+0x18bf(IIS ASP.NET 宿主)
结论与责任判定
- 崩溃性质:一次 AjaxPro 同步请求触发的业务异常。业务代码
get_Type()抛 E_FAIL,经 AOP/AjaxPro 两层反射 invoke 各包一层 TIE 后冒泡到宿主顶层。 - 与进程内安全组件的关系:托管栈全文 + 原生栈中没有任何安全组件帧——进程内安装的崩溃收集组件(dump 即其产物)只扮演了 crash dump 收集者角色,对未处理 CLR 异常(宿主重抛阶段)做了捕获采样。
- 责任判定:可归因为第三方应用自身未处理异常,与进程内其他组件无关。
给业务的排查方向
get_Type()内部若有 COM 组件/原生互操作(E_FAIL 是 COMException 默认 HResult),检查对应 COM 组件注册、许可、依赖 DLL 是否完好;- 关注
ReregMutex/GetMutexInfo(互斥锁重新注册)请求的入参是否触发了特定的前置条件校验分支; - 异常冒泡到管线顶层说明 AjaxPro handler 与应用层均未 catch——应用侧应加异常处理,避免请求级失败演变成宿主级事件。
附:分析过程关键命令与坑
DAC 版本不匹配 workaround
本机 SOS/DAC 与 dump 的 CLR 版本不一致时,msdl 平名查找(mscordacwks_AMD64_AMD64_<ver>.dll)可能失效。Workaround:
- symsrv 缓存目录里找到已下载的
mscordacwks_AMD64_AMD64_4.8.4614.00.dll\<timestamp>\文件 - 复制为
dac\mscordacwks.dll(去掉平名目录结构) .cordll -ve -lp <dac目录>直连
⚠️ 注意 .cordll -u 是禁用 CLR 调试,别带。
minidump 里 SOS 显示字段失败时:裸内存解析
mscorlib.ni 无镜像导致 !do/!pe 显示失败时,直接 db 异常对象内存裸解析。.NET 4.8 x64 Exception 对象布局(实测):
| 偏移 | 字段 |
|---|---|
| +0x08 | _message |
| +0x30 | _innerException(TIE 链沿此走) |
| +0x58 | _stackTraceString(文本) |
| +0x8C | HResult |
栈文本是 UTF-16LE,用 Python 解码绕开控制台代码页丢字。
其他
- 镜像 key = 时间戳+裁剪后 Size(如
63995BC5b34000);.reload拉不动镜像时.exepath+ <缓存目录>直指 - clr.pdb 的 GUID 可用 Python 解析 PE debug directory 拿到后手工从 msdl 拉
- msdl 不托管 mscorlib.ni.dll 二进制(404),只托管其 pdb