【问题标题】:Unhandled Exception in Rad Studio Debugger ThreadRad Studio 调试器线程中未处理的异常
【发布时间】:2011-09-18 20:40:29
【问题描述】:

我有一个大型应用程序,最近在调试器中运行时开始表现出相当奇怪的行为。一、基础:

OS: Windows 7 64-bit.
Application: Multithreaded VCL app with many dlls, bpls, and other components.
Compiler/IDE: Embarcadero RAD Studio 2010.

观察到的症状是:当调试器附加到我的应用程序时,某些任务会导致应用程序崩溃。细节更令人困惑:我的应用程序停止并显示一条 Windows 消息,“YourApplication 已停止工作”。它还提供了向 Microsoft 发送 minidump 的帮助。

需要注意的是:不附加调试器时应用程序不会崩溃。此外,调试器不会在应用程序运行时指示任何异常或其他问题。

设置和单步执行断点似乎会影响应用程序崩溃的点,但我怀疑这是调试线程而不是有问题的线程的症状。

这些崩溃也发生在我同事的计算机上,与我观察到的行为相同。这使我不怀疑在我的计算机上安装某些东西失败了。遇到此问题的同事也在运行 Windows 7 64 位。我没有同事没有遇到过这个问题。

我从崩溃中收集了一些经过分析的完整转储。我发现失败实际上每次都发生在同一个地方。这是来自转储的异常数据(它总是相同的,当然除了 ThreadId):

Exception Information

ThreadId:         0x000014C0
Code:             0x4000001F Unknown (4000001F)
Address:          0x773F2507
Flags:            0x00000000
NumberParameters: 0x00000001
    0x00000000

Google 发现代码 0x4000001F 实际上是 STATUS_WX86_BREAKPOINT。 Microsoft 毫无帮助地将其描述为“Win32 x86 仿真子系统使用的异常状态代码”。

以下是堆栈详细信息(似乎没有变化):

0x773F2507: ntdll.dll+0x000A2507: RtlQueryCriticalSectionOwner + 0x000000E8
0x773F3DAB: ntdll.dll+0x000A3DAB: RtlQueryProcessLockInformation + 0x0000020D
0x773D2ED9: ntdll.dll+0x00082ED9: RtlUlonglongByteSwap + 0x00005C69
0x773F3553: ntdll.dll+0x000A3553: RtlpQueryProcessDebugInformationRemote + 0x00000044
0x74F73677: kernel32.dll+0x00013677: BaseThreadInitThunk + 0x00000012
0x77389F02: ntdll.dll+0x00039F02: RtlInitializeExceptionChain + 0x00000063
0x77389ED5: ntdll.dll+0x00039ED5: RtlInitializeExceptionChain + 0x00000036

值得注意的是,在 0x773F24ED 处似乎有一个函数 Epilog,这表明 RtlQueryCriticalSectionOwner 是一个红鲱鱼。同样,函数 Epilog 对 RtlQueryProcessLockInformation 产生了怀疑。 0x5C69 偏移量对 RtlUlonglongByteSwap 产生了怀疑。不过,其他符号看起来是合法的。

具体来说,RtlpQueryProcessDebugInformationRemote 看起来是合法的。网上有些人(http://www.cygwin.com/ml/cygwin-talk/2006-q2/msg00050.html)似乎认为它是由调试器创建的,用于收集调试信息。这个理论对我来说似乎是合理的,因为它似乎只在附加调试器时出现。

与往常一样,当某些事情发生故障时,某些变化会破坏它。在这种情况下,某些东西正在动态加载一个新的 dll。我可以通过不动态加载特定的 dll 来停止崩溃。我不相信 dll 加载是相关的,但这里有详细信息,以防万一:

dll源为C,以下是未设置为默认的编译选项:

Language Compliance: ANSI
Merge duplicate strings: True
Read-only strings: True
PCH usage: Do not use
Dynamic RTL: False

(项目选项说 False 是动态 RTL 的默认值,尽管在我创建 dll 项目时它被设置为 True。)

DLL 使用 LoadLibrary 加载并使用 FreeLibrary 释放。模块的加载和卸载似乎一切都很好。然而,在库被卸载后不久(使用 FreeLibrary),上述线程使程序崩溃。为了调试,我删除了对库的所有实际调用(包括,为了进行更多测试,DllMain)。调用或不调用、DllMain 或没有 DllMain 或其他任何东西的组合似乎都不会以任何方式改变崩溃的行为。简单地加载和卸载 dll 会在稍后调用崩溃。

此外,将 dll 更改为使用动态 RTL 也会导致调试器线程崩溃停止。这是不可取的,因为编译的 dll 在没有 CodeGear Runtime 可用的情况下确实应该可用。此外,dll 的大小也很重要。 dll 中包含的 C 代码不使用任何库。 (它不包括头文件,甚至标准库头文件。没有 malloc/free,没有 printf,什么都没有。它只包含完全依赖于其输入且不需要动态分配的函数。)它也是不可取的,因为“修复”a通过改变东西直到它工作而不理解它为什么工作的错误真的不是一个好计划。 (这往往会导致错误重现和奇怪的编码习惯。但实际上,在这一点上,如果我找不到其他任何东西,我可能会承认这一点失败。)

最后,我的问题可能与以下问题之一有关:

任何想法或建议将不胜感激。

【问题讨论】:

  • 卸载DLL后线程是否自行终止?如果是这样,您应该调用 FreeLibraryAndExitThread 而不是进行两次不同的调用。此外,您是否为 DLL 和应用程序的运行时库使用多线程调试 (/MTd)?
  • 对前者,没有。 DLL 在主线程中加载和卸载。到第二个,我想是的。
  • 这是一个难题。似乎问题出在 C 运行时库启动代码中。确保 C++Builder 没有在某些库/包中静态链接。链接器的命令行参数是什么?我不得不解决 TChart 启动代码中的错误(在调用 main 函数之前)。好痛啊……

标签: c++ exception dll c++builder c++builder-2010


【解决方案1】:

我通过使用 PatchINT3 解决方法的修改版本解决了上述问题,该解决方法于 2007 年为 BDS 2006 发布:

procedure PatchINT3;
const
  INT3: Byte = $CC;
  NOP: Byte = $90;
var
  NTDLL: THandle;
  BytesWritten: DWORD;
  Address: PByte;
begin
  if Win32Platform <> VER_PLATFORM_WIN32_NT then
    Exit;
  NTDLL := GetModuleHandle('NTDLL.DLL');
  if NTDLL = 0 then
    Exit;
  Address := GetProcAddress(NTDLL, 'RtlQueryCriticalSectionOwner');
  if Address = nil then
    Exit;
  Inc(Address, $E8);
  try
    if Address^ <> INT3 then
      Exit;

    if WriteProcessMemory(GetCurrentProcess, Address, @NOP, 1, BytesWritten)
      and (BytesWritten = 1) then
      FlushInstructionCache(GetCurrentProcess, Address, 1);
  except
    //Do not panic if you see an EAccessViolation here, it is perfectly harmless!
    on EAccessViolation do
      ;
  else
    raise;
  end;
end;

在线程中加载 DLL 后调用此例程一次。该补丁修复了 ntdll.dll 版本 6.1.7601.17725 中的用户断点并将其更改为 NOP。

如果在预期地址处没有用户断点(INT3 (=$CC) 操作码),则补丁例程不执行任何操作并退出。

希望对您有所帮助,
安德烈亚斯

脚注
PatchINT3的原始来源可以在这里找到:
http://coding.derkeiler.com/Archive/Delphi/borland.public.delphi.non-technical/2007-01/msg04431.html

脚注2
C++ 中的相同函数:

void PatchINT3()
{
   unsigned char INT3   = 0xCC;
   unsigned char NOP    = 0x90;

   if (Win32Platform != VER_PLATFORM_WIN32_NT)
   {
      return;
   }

   HMODULE ntdll = GetModuleHandle(L"NTDLL.DLL");
   if (ntdll == NULL)
   {
      return;
   }

   unsigned char *address = (unsigned char*)GetProcAddress(ntdll,
      "RtlQueryCriticalSectionOwner");
   if (address == NULL)
   {
      return;
   }

   address += 0xE8;

   try
   {
      if (*address != INT3)
      {
         return;
      }

      unsigned long bytes_written = 0;
      if (WriteProcessMemory(GetCurrentProcess(), address, &NOP, 1,
         &bytes_written) && (bytes_written == 1))
      {
         FlushInstructionCache(GetCurrentProcess, address, 1);
      }
   }
   catch (EAccessViolation &e)
   {
      //Do not panic if you see an EAccessViolation
      //here, it is perfectly harmless!
   }
   catch(...)
   {
      throw;
   }
}

【讨论】:

  • 你有这个来源的链接吗?
  • 我在使用 Delphi2010 和 apache 共享模块时遇到了类似的问题。该补丁似乎工作正常!
【解决方案2】:

只是一个想法......

也许您需要关闭崩溃的线程。您观察到的状态似乎在实际错误之后。

首先,您的堆栈跟踪对我来说似乎不完整。该线程堆栈的基本根是什么?该线程的起源是什么?

而且,在 VS 调试器中,有可能中断异常(Debug->Exceptions...->[Add])。然后所有线程将在异常发生时冻结。我不知道 RAD,但以编程方式执行此操作的技巧似乎是 WaitForDebugEvent()。

我可能是错的,但我认为错误很可能在调试器中,而不是在您的代码中。在这种情况下,恕我直言,一个丑陋的解决方法是完全可以原谅的。祝你好运!

【讨论】:

    【解决方案3】:

    我无法回答这个问题,因为我看不到代码...

    但是……

    1) 在 Borland C++ 中,至少在 BDS 的 C++ 中,多线程库中的 realloc 函数可能存在可证明的问题。您的 C++ 代码是否使用 realloc?

    2) 您所显示的堆栈很可能由于您的代码实际遇到“CALL BAD_ADRESS”而被调用,而这可能是由于您自己的代码中的错误而发生的。换句话说,在您加载的 DLL 中,可能有一个函数正在执行某些操作,该函数正在用垃圾覆盖您程序中的可执行代码,然后当 now junk 部分运行时,它会崩溃。

    另一种方法是,如果 C++ dll 中的某些内容正在修改它运行的下面的堆栈,然后您的代码稍后会遇到这个问题。

    3) 检查 DLL 的 CPU 标志设置。 Borland 库有时在进入时使用冲突的 CPU 标志,您可能需要在调用 DLL 之前保存和恢复。例如,如果您从 Delphi 调用使用 C++ 制作的 VST 插件,但未正确设置标志,您可能会在关闭该异常的情况下编译的 VST 插件获得后续除以零的错误。

    【讨论】:

      【解决方案4】:

      我们今天遇到了同样的问题。在我们的例子中,如果在调用 TOpenDialog->Execute() 后有一个断点(我认为它使用的是 shell32.dll 中的对话框),则会发生崩溃(Windows 7 x64,C++ Builder XE2)

      卸载 iCloud (v2.1.0.39) 后,问题得到解决。

      不幸的是,我们仍在调查我们的客户在 Windows Vista 下使用我们的发布产品时遇到的类似问题。使用 TOpenDialog 选择文件后,应用程序在 gdiplus.dll 中因访问冲突而崩溃,删除 iCloud 似乎也解决了问题。

      【讨论】:

        猜你喜欢
        • 2015-06-22
        • 2020-11-11
        • 2012-07-20
        • 2014-09-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多