我现在正在调试一个使用许多不同线程的程序。
时不时会抛出异常。问题是没有办法知道哪个线程导致了问题......
有没有一种简单的方法可以在抛出异常后获取堆栈跟踪?
我想过简单地编写一条调试消息,但这将是一个巨大的:-)我想有比这个更好的技术......
我正在使用 Visual Studio 2008 - 本机 C++ 项目....
除非我大错特错,否则您需要知道哪个线程触发了异常,才能使用 Visual Studio 调试器的调用堆栈视图,这显然是您目前所处的 catch-22 情况。
我要尝试的一件事是看看是否可以让调试器在引发异常时中断(使用“调试”>“异常”)。您必须显式启用此功能,但如果您知道抛出什么类型的异常,这可能会让您找出抛出异常的位置。
除此之外,在异常的构造函数中放置一个断点(如果它是你自己的)也应该允许你找出它是从哪里触发的。
如果这些方法对您不起作用,我会按照您的建议查看调试消息。
使用 Microsoft 免费提供的 WinDBG,这非常简单。如果您还没有安装适合您的 Windows 版本的符号,您还需要安装它们。
只需将 WinDBG 设置为故障转储工具即可。我使用此注册表设置:(您可能希望编辑路径)
CrashDumpSettings.reg
:Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug]
"Auto"="1"
"Debugger"="C:\\progra~1\\debugg~1\\cdb.exe -p %ld -e %ld -g -y SRV*c:\\mss*http://msdl.microsoft.com/download/symbols -c \"$<c:\\Dumps\\CrashDump.cdbscript\""
您的
CrashDump.cdbscript
可能如下所示:(这基本上是我使用的...根据需要编辑路径。)
CrashDump.cdbscript
:.sympath+ c:\windows\symbols;c:\some\path\to\symbols\for\your\project
as /c CrashFirstModule .printf "%mu", @@c++((*(ntdll!_LDR_DATA_TABLE_ENTRY**)&@$peb->Ldr->InLoadOrderModuleList.Flink)->BaseDllName.Buffer)
.logopen /t c:\dumps\${CrashFirstModule}_process.log
.kframes 100
!analyze -v
~*kv
lmv
.logclose
.dump /mhi /u /b c:\dumps\${CrashFirstModule}_mini.cab
.dump /mhia /u /b c:\dumps\${CrashFirstModule}_full.cab
q
您会得到一个不错的日志文件和一些转储,您可以使用它们来查看 WinDBG 发生异常时进程的状态。日志文件将对发生的错误进行分析,包括导致错误的代码行。它还会列出每个线程的调用堆栈。在调用堆栈列表中,编号旁边带有
#
的线程是导致异常的线程。这些文件中有大量信息。我建议选择 John Robbins 撰写的调试 Microsoft .Net 和 Microsoft Windows 的应用程序。这是一本关于调试的好书,即使它是几年前的书。您可以从亚马逊购买,价格约为 20.00 美元。
这个库看起来很符合要求:
http://www.codeproject.com/KB/threads/StackWalker.aspx
Jochen Kalmbach 看起来在封装低级 dbghelp.dll 接口的复杂性方面做了非常彻底的工作。
您可以在异常构造函数中放置一个断点(即您要抛出的对象)。
这当然假设您有一个通用的异常层次结构。
如果您无法让调试器捕获正在发生的事情...并且您无法打印堆栈跟踪和线程...
我的猜测是,你必须付出一些努力和努力。从了解系统开始。一旦了解了系统,请尝试将系统分成两半。有效的部分和无效的部分。然后继续尝试这样做,直到深入解决问题。
当您向下钻取足够远时,尝试使用 try/catch 包围可疑代码...希望您可以使用调试器停止执行并查看发生了什么。
您可以使用“例外”对话框(
Debug
| Exceptions...
菜单项,或 Ctrl + Alt + E 或 Ctrl + D、E,具体取决于您的键盘绑定)在抛出特定异常时将正在运行的代码分解为调试。
异常本身具有 StackTrace 属性...所以你只需要返回 ToString( ) 即可。
但我相信你的问题更像是如何捕获随机异常。如果是这样,我所做的就是将所有主要代码放在 try/catch 中。我真的不太确定异常是否在其他线程中抛出,这种技术是否有效。
您还应该捕获一个不是从 Exception 派生的 ApplicationException。