gpt4 book ai didi

c# - 调试 CLR 挂起

转载 作者:行者123 更新时间:2023-12-02 04:49:06 25 4
gpt4 key购买 nike

我上传了一个 WinDBG session 的日志,我将引用:https://pastebin.com/TvYD9500

因此,我正在调试客户报告的挂起。复制器是一个小的 C# 程序:

using System;
using System.Data.Odbc;
using System.Threading;

namespace ConnectionPoolingTest
{
class Program
{
static void Main(string[] args)
{
String connString = "DSN=DotNetUltraLightDSII";
using (OdbcConnection connnection = new OdbcConnection(connString))
{
connnection.Open();
connnection.Close();
}
}
}
}

我们销售用于构建 ODBC 驱动程序的框架,客户正在测试使用该框架构建的 ODBC 驱动程序。一个可能相关的细节是,他们使用的组件允许用 C# 编写他们的业务逻辑,并且该组件是用 C++/CLI 编写的,以在我们的 native 代码和客户代码之间架起桥梁(因此,ODBC 驱动程序DLL 是一个混合模式的 DLL,它向 ODBC 驱动程序管理器公开了一个 C 接口(interface))。

(如果需要,我也可以上传驱动程序二进制文件。)

在这个复制器(它必须在使用的 DSN 上启用连接池的情况下运行)中发生的事情是,该进程最终挂起一个带有堆栈的线程,看起来像:
RetAddr           : Args to Child                                                           : Call Site
000007fe`fcea10dc : 00000000`00470000 00000000`770d0290 00000000`00000000 00000000`009ae8e0 : ntdll!ZwWaitForSingleObject+0xa
000007fe`f0298407 : 00000000`00999a98 00000000`770d5972 00000000`00000000 00000000`00000250 : KERNELBASE!WaitForSingleObjectEx+0x79
000007fe`f0294d04 : 00000000`00999a98 00000000`00a870e0 00000000`00999a68 00000000`00991a10 : comsvcs!UTSemReadWrite::LockWrite+0x90
000007fe`f0294ca8 : 00000000`00999a68 00000000`00999a98 00000000`00999a20 00000000`7717ba58 : comsvcs!CDispenserManager::~CDispenserManager+0x2c
000007fe`f02932a8 : 00000000`00999a20 00000000`00a871c0 00000000`77182e70 00000000`7717ba58 : comsvcs!ATL::CComObjectCached<ATL::CComClassFactorySingleton<CDispenserManager> >::`scalar deleting destructor'+0x68
000007fe`f0293a00 : 000007fe`f0290000 00000000`00000001 00000000`00000001 00000000`00a87198 : comsvcs!ATL::CComObjectCached<ATL::CComClassFactorySingleton<CDispenserManager> >::Release+0x20
000007fe`f02949aa : 00000000`00000000 00000000`00a87188 00000000`00992c20 00000000`00992c30 : comsvcs!ATL::CComModule::Term+0x35
000007fe`f0293543 : 00000000`00000000 00000000`00a87190 00000000`00000001 00000000`00a87278 : comsvcs!`dynamic atexit destructor for 'g_ModuleWrapper''+0x22
000007fe`f029355b : 00000000`00000001 00000000`00000000 000007fe`f0290000 00000000`76f515aa : comsvcs!CRT_INIT+0x96
00000000`7708778b : 000007fe`f0290000 00000000`00000000 00000000`00000001 00000000`7717ba58 : comsvcs!__DllMainCRTStartup+0x187
00000000`7708c1e0 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!LdrShutdownProcess+0x1db
000007fe`efb4ee58 : 00000000`00486b10 00000000`00000001 00000000`00482460 00000000`00000000 : ntdll!RtlExitUserProcess+0x90
000007fe`efb4efd4 : 00000000`00000000 000007fe`efb4efc0 ffffffff`00000000 00000000`004868a0 : mscoreei!RuntimeDesc::ShutdownAllActiveRuntimes+0x287
000007fe`eefa9535 : 00000000`0042f4b8 000007fe`ef53d6c0 00000000`0042f488 00000000`004868a0 : mscoreei!CLRRuntimeHostInternalImpl::ShutdownAllRuntimesThenExit+0x14
000007fe`eefa9495 : 00000000`00000000 00000000`0042f488 00000000`00000000 00000000`00000000 : clr!EEPolicy::ExitProcessViaShim+0x95
000007fe`eee83336 : 00000000`00000006 00000000`0042f870 00000000`00000000 00000000`00000000 : clr!SafeExitProcess+0x9d
000007fe`eee61c51 : 00000000`01000000 00000000`0042f870 00000000`00000000 00000000`00000000 : clr!HandleExitProcessHelper+0x3e
000007fe`eee62034 : ffffffff`ffffffff 000007fe`eee62020 00000000`00000000 00000000`00000000 : clr!_CorExeMainInternal+0x101
000007fe`efb47b2d : 00000000`00000000 00000000`00000091 00000000`00000000 00000000`0042f7c8 : clr!CorExeMain+0x14
000007fe`efbe5b21 : 00000000`00000000 000007fe`eee62020 00000000`00000000 00000000`00000000 : mscoreei!CorExeMain+0x112
00000000`76f4556d : 000007fe`efb40000 00000000`00000000 00000000`00000000 00000000`00000000 : MSCOREE!CorExeMain_Exported+0x57
00000000`770a385d : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0xd
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x1d

我能够找到 UTSemReadWrite 类的一些源代码(但它似乎与我实际运行的有点不同): https://github.com/dotnet/coreclr/blob/616fea550548af750b575f3c304d1a9b4b6ef9a6/src/utilcode/utsem.cpp

在 UTSemReadWrite::LockWrite 中放置一个断点,我能够调试最后一个挂起的调用,发现原因是 m_dwFlag(用于原子性)非零,因此它等待事件(对于拥有线程在释放它时发出信号),它通过调用 UTSemReadWrite::GetWriteWaiterEvent 来实现,但该调用会创建事件(此时没有其他线程......),然后等待它.轰隆隆,僵局。

从调试到程序集,我能够推断出 m_dwFlag 偏移了 4 个字节到对象中,并在 UTSemReadWrite::UTSemReadWrite 中放置了一个断点,我能够获得挂起中涉及的 UTSemReadWrite 实例的地址,并且在 m_dwFlag 上放置一个数据断点。

这样做,我确实可以看到,具有线程函数 comsvc​​s!PingThread 的线程在调用 comsvc​​s!UTSemReadWrite::UnlockRead 之前被杀死之前调用了 comsvc​​s!UTSemReadWrite::LockRead(并且可能获得了锁)。我以前见过这样的事情,它是由一个未处理的 SEH 异常杀死 PingThread 引起的,但是应用程序使用 setunhandledexceptionfilter() 防止崩溃,所以我认为可能是一些异常杀死了线程,但结果是它是 CLR 本身!
RetAddr           : Args to Child                                                           : Call Site
00000000`7708c198 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!ZwTerminateProcess+0xa
000007fe`efb4ee58 : 00000000`00486b10 00000000`00000001 00000000`00482460 00000000`00000000 : ntdll!RtlExitUserProcess+0x48
000007fe`efb4efd4 : 00000000`00000000 000007fe`efb4efc0 ffffffff`00000000 00000000`004868a0 : mscoreei!RuntimeDesc::ShutdownAllActiveRuntimes+0x287
000007fe`eefa9535 : 00000000`0042f4b8 000007fe`ef53d6c0 00000000`0042f488 00000000`004868a0 : mscoreei!CLRRuntimeHostInternalImpl::ShutdownAllRuntimesThenExit+0x14
000007fe`eefa9495 : 00000000`00000000 00000000`0042f488 00000000`00000000 00000000`00000000 : clr!EEPolicy::ExitProcessViaShim+0x95
000007fe`eee83336 : 00000000`00000006 00000000`0042f870 00000000`00000000 00000000`00000000 : clr!SafeExitProcess+0x9d
000007fe`eee61c51 : 00000000`01000000 00000000`0042f870 00000000`00000000 00000000`00000000 : clr!HandleExitProcessHelper+0x3e
000007fe`eee62034 : ffffffff`ffffffff 000007fe`eee62020 00000000`00000000 00000000`00000000 : clr!_CorExeMainInternal+0x101
000007fe`efb47b2d : 00000000`00000000 00000000`00000091 00000000`00000000 00000000`0042f7c8 : clr!CorExeMain+0x14
000007fe`efbe5b21 : 00000000`00000000 000007fe`eee62020 00000000`00000000 00000000`00000000 : mscoreei!CorExeMain+0x112
00000000`76f4556d : 000007fe`efb40000 00000000`00000000 00000000`00000000 00000000`00000000 : MSCOREE!CorExeMain_Exported+0x57
00000000`770a385d : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0xd
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x1d

(这带来了一个问题;所以 ntdll!ZwTerminateProcess 实际上并没有终止进程?因为它显然是返回并调用 atexit 处理程序......我猜这是一个同名的不同函数? https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntddk/nf-ntddk-zwterminateprocess)

所以,我的问题是,我是否正确地解释了调试器向我显示的内容?这实际上是 CLR 中的错​​误吗? CLR 不应该先优雅地结束线程吗?

客户注意到的一点是,如果他在驱动程序中创建一个线程作为后台线程,则不会发生挂起,这很奇怪,因为在卸载驱动程序时(通过终结器),即使是前台线程也应该很快停止在驱动程序的句柄上调用 SQLFreeHandle()),除非终结器线程被某些东西减慢了,我猜?

发送给我们的复制驱动程序中的后台线程基本上是
public Driver()
{
this.tokenSource= new CancellationTokenSource();
this.token = this.tokenSource.Token;
this.worker= new Thread(this.DoWork) { IsBackground = false };
this.worker.Start();
}

public override void Dispose()
{
this.tokenSource.Cancel();
this.worker.Join();
this.tokenSource.Dispose();

base.Dispose();
}

private void DoWork() {
while (!this.token.WaitHandle.WaitOne(200)) {
log(this.Log, "Doing some work....");
}
log(this.Log, "Done with work.");
}

并且 Dispose() 被正确调用并退出。

我不知道接下来如何处理这个问题。

编辑:阅读后 this ,我感觉这是 CLR 的一个错误/“怪癖”。在我的场景中,最后一个前台 .NET 线程位于 ODBC 驱动程序中。当 ODBC 驱动程序管理器调用 SQLFreeHandle 时卸载驱动程序(从 Windows 线程池中的某个线程或驱动程序管理器本身拥有的某个线程,不确定),这会导致驱动程序终止最后一个前台线程。根据我从那篇文章中获得的对 CLR 关闭过程的理解,CLR 最终会杀死调用 SQLFreeHandle 的线程。在它有机会真正从中返回之前,这是预期的行为。

但是那个线程似乎持有 UTSemReadWrite 锁,所以稍后在 atexit 处理期间它会死锁。

我关于如何解决这个问题的唯一想法,如果它实际上是 CLR 的错误,是在对 SQLFreeHandle 的最终调用时启动另一个(前台).NET 线程。这将在一些超时后结束(希望足够长的时间让 SQLFreeHandle 线程释放它持有的任何锁),以延迟 CLR 关闭。如果这最终导致应用程序关闭,那就不是很理想了...

编辑:实际上,即使这个想法也行不通,因为这意味着 ODBC 驱动程序管理器实际上可能会在线程执行代码时卸载驱动程序,从而导致崩溃。

最佳答案

我已经与微软支持人员谈过,他们已经确认这是 comsvc​​s 组件的问题,他们可能会在 future 版本的 Windows 中修复该问题。如果他们告诉我他们已经修复了它,我会更新它。

关于c# - 调试 CLR 挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57120511/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com