gpt4 book ai didi

c# - 诊断/调试潜在的堆栈损坏.NET应用程序

转载 作者:行者123 更新时间:2023-12-03 02:07:42 30 4
gpt4 key购买 nike

我想我这里有个卷曲的地方...我有一个WinForms应用程序,当它作为x64进程运行时,它每小时大约会定期崩溃。我怀疑这是由于堆栈损坏引起的,并且想知道是否有人看到过类似的问题,或者是否有诊断和检测问题的建议。

该程序没有可见的UI。它只是一个位于后台的消息窗口,并充当我们其他客户端程序和服务器之间的一种“中间件”。

它在不同的机器上以不同的方式死亡。有时,这是一个“ APPCRASH”对话框,报告ntdll.dll中的错误。有时,这是一个“ APPCRASH”,将我们自己的dll报告为罪魁祸首。有时只是无声的死亡。有时,我们未处理的异常钩子会记录错误,有时却不会。

在Windows错误报告启动的情况下,我检查了几种不同崩溃情况下的内存转储,并每次都在内存中找到相同的托管异常。在我们死亡之前记录的情况下,这是我认为报告为未处理异常的相同异常。

我也很幸运(?)在使用Visual Studio进行积极调试时导致应用程序崩溃-并看到相同的异常使程序崩溃。

现在是踢球者。在程序生命的最初几秒钟,就抛出,捕获并吞下了这个特殊异常。我已经使用其他跟踪日志记录验证了这一点,并且在应用程序启动后几分钟提取了应用程序的内存转储,并验证了异常仍然位于堆中的某个位置。我还在应用程序上运行了一个内存探查器,并使用它来验证没有其他.NET对象引用它。

有问题的代码看起来像这样(已大大简化,但保留了流控制的关键点)

public class AClass
{
public object FindAThing(string key)
{
object retVal = null;
Collection<Place> places= GetPlaces();

foreach (Place place in places)
{
try
{
retval = place.FindThing(key);
break;
}
catch {} // Guaranteed to only be a 'NotFound' exception
}

return retval;
}
}

public class Place
{
public object FindThing(string key)
{
bool found = InternalContains(key); // <snip> some complex if/else logic

if (code == success)
return InternalFetch(key);

throw new NotFoundException(/*UsefulInfo*/);
}
}


我在事件日志中以及使用windbg查看堆时看到的堆栈跟踪看起来都像这样。

Company.NotFoundException:
Place.FindThing()
AClass.FindAThing()


现在...对我来说,有点像堆栈损坏。在应用程序启动时引发并捕获异常。但是指向它的指针在堆栈中可以存活一个小时或更长时间,就像大脑中的子弹一样,然后突然突破关键动脉,应用程序死于水坑中。

额外线索:


“ InternalFetch”中的代码使用了一些Marshal。[Alloc / Free] CoTask和pinvoke代码。我已经在其上运行FxCop,以寻找可移植性问题,但未发现任何问题。
该问题的这种特殊表现仅影响以发布模式(启用代码优化)构建的x64代码。我为“ Place.Find”方法列出的代码反映了优化的.NET代码。未优化的代码将找到的对象作为最后一条语句返回,而不是“引发异常”。
在启动上述代码之前,我们在启动过程中进行了一些COM调用...并且在上述问题将变得明显的情况下,第一个COM调用失败。 (捕获并吞下了异常)。我已经注释掉了该特定的COM调用,它并没有阻止异常停留在堆上。
该问题可能还会影响32位系统,但如果确实如此,则该问题不会在同一位置出现。仅向我(通常是用户!)发送了一些像素值的“ APP CRASH”对话框的屏幕快照,但是我能识别出的一件事是有故障的模块字段中的“ StackHash_2264”。


编辑:

突破!

我已将问题缩小为对 SetTimer的特定调用。
pInvoke看起来像这样:

[DllImport("user32")]
internal static extern IntPtr SetTimer(IntPtr hwnd, IntPtr nIDEvent, int uElapse, TimerProc CB);

internal delegate void TimerProc(IntPtr hWnd, uint nMsg, IntPtr nIDEvent, int dwTime);


有一个特殊的类在其构造函数中启动计时器。在构造该对象之前设置的所有计时器都可以工作。在构造该对象之后设置的所有计时器都将起作用。在该构造函数中设置的任何计时器都会导致应用程序崩溃,这种情况多半会发生。 (我的笔记本电脑可能在95%的时间内崩溃,但我的台式机仅在10%的时间内崩溃)。

间隔设置为1小时还是1秒似乎没有什么不同。当计时器到期时,应用程序会死机-通常是通过抛出一些如上所述的先前处理的异常。回调实际上不会执行。如果在构造函数返回后在托管代码的下一行设置相同的计时器,一切都会很好。

当错误的计时器即将触发时,我已经连接了调试器,这在'DispatchMessage'中导致了访问冲突。从未调用过计时器回调。我已经启用了与垃圾回收相关的托管回调相关的MDA,并且它不会触发。我用sos检查了对象,并验证了回调仍存在于内存中,并且它所指向的地址是正确的回调函数。

如果我此时运行'!analyze -v',它通常(但不总是)报告某些内容,类似于'ERROR_SXS_CORRUPT_ACTIVATION_STACK'

用Microsoft的'System.Windows.Forms.Timer'类替换对SetTimer的调用也可以停止崩溃。我在类上使用了Reflector,可以看到它在内部仍在调用SetTimer-但未注册过程。相反,它具有接收回调的本机窗口。它的pInvoke定义实际上看起来是错误的...它对eventId使用'ints',其中MSDN文档说它应该是UIntPtr。

我们自己的代码最初也为nIDEvent而不是IntPtr使用了'int'-我在研究过程中对其进行了更改-但此声明更改前后,崩溃仍然持续。因此,我能看到的唯一真正的区别是我们正在注册一个回调,而Windows类不是。

因此...在此阶段,我可以通过将对SetTimer的特定调用改组到稍微不同的位置来“解决”问题。但是我仍然无法真正理解在导致该错误的构造函数中启动计时器的特别之处。我非常想了解此问题的根本原因。

最佳答案

只是简单地考虑一下就听起来像是x64互操作问题(即从x64托管代码调用x32本机函数充满危险)。如果您从项目属性中强制将应用程序编译为x32平台,问题是否会解决?

您可以阅读有关在Dotnetrocks上进行x32 / x64开发期间强制执行x32编译的建议。 Richard Campbell的建议是Visual Studio应该默认为x32平台,而不是AnyCPU。
http://www.dotnetrocks.com/default.aspx?showNum=341transcript)。

关于高级调试,我还没有机会调试x64互操作代码,但是我听说这本书是很棒的资源:Advanced .NET Debugging

最后,您可以尝试的一件事是force Visual Studio to break when an exception is thrown

关于c# - 诊断/调试潜在的堆栈损坏.NET应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6351329/

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