2023年1月

一个失败的终止

撞车事故时有发生。任何比“Hello world”更复杂的程序都可能有一些bug。专业软件开发的一个衡量标准是如何处理这些崩溃。程序应该保存一个崩溃转储,然后自杀(TerminateProcess()或_exit(),而不是ExitProcess()或exit())。

你不想让这个注定要失败的进程弹出一个对话框,说“嘿,我是一个注定要失败的进程”。但不幸的是,这是VisualC++C运行时间(VC++)在某些情况下所做的,正如我们看到的那样。

 

如果您不小心调用了一个纯虚拟函数,则此处理程序将打开一个对话框。如果你是一个开发人员,那么你可以附加一个调试器并获得一个调用堆栈,但世界上大多数人都不是开发人员。他们不知道什么是纯虚函数调用,也不在乎。显示此对话框只会减慢崩溃恢复过程,同时让用户感到困惑。

但更糟糕的是。如果你有一群异常处理程序准备捕捉Win32异常(访问冲突等),那么你会失望的,因为他们不会捕捉到纯粹的调用错误,即使有人按了OK。所以,你的内部崩溃转储记录系统对这个错误无能为力,这意味着它需要更长的时间来修复。


更更糟糕的是,如果这个错误发生在服务器上(我见过),那么您的无头服务器现在有一个挂起的进程正在等待某人单击“确定”。单元测试最终会超时,如果您有看门狗,服务器可能会超时,但是整个过程会被这个对话框延迟。


除非我能提供解决方案,否则我不会写这篇文章。上面的对话框是默认行为,但是一旦您知道应该更改默认值,就很容易了。您所要做的就是使用一个故意崩溃的函数调用_set_purecall_handler()。我最喜欢的实现是在TerminateProcess()后面执行一个_debugbreak()。如果我在调试器下运行,这会很好地将我放入其中,如果我不是,那么我未处理的异常过滤器将捕捉异常并写出一个小转储。TerminateProcess()用于阻止在调试器中捕获异常的用户尝试继续。

无效参数在技术上不是崩溃


VC++CRT检测到CRT函数的一些无效参数,并将它们视为致命错误。如果您使用更安全的CRT函数(并且您没有请求截断),这包括缓冲区溢出检测,但是触发这些检查的最简单方法是使用“printf(NULL);”。

不会弹出任何对话框(至少在发布版本中没有),并且进程被终止,但不会通过调用精心编制的异常处理程序来终止。Windows错误报告(WER)会收到问题的通知,这很好,但我希望这些无效参数像崩溃一样处理,以便调用异常处理程序。幸运的是,这个问题也有一个简单的解决方案。如果调用_set_invalid_parameter_handler(),则可以为它提供与纯调用处理程序相同的代码(只是带有不同的签名),以便异常处理程序注意到发生了错误。现在你的程序会比以前更糟糕。这是件好事。示例代码中也演示了这种技术。

 

WER是你的朋友


Windows错误报告(WER)是Windows内置的一个方便的功能。大多数开发人员都知道WER会在数百万用户的计算机上记录崩溃转储并将其存储起来,而且大多数开发人员也知道可以访问您的软件的崩溃转储。这是一个很好的方法,可以找出你的软件在实际客户的实际机器上实际崩溃的地方。虽然有一些困难需要克服,但还是值得一试的。不过,我不知道如何安排这样的访问,所以我就不多说了。

WER的一个鲜为人知的特性是,你可以让它记录你自己机器上的崩溃。你所要做的就是设置一些注册表项。我要在这里走开,说Windows上的每个C++开发者都应该配置这个。它非常简单,而且WER有时会捕捉到其他系统没有的崩溃。WER非常擅长捕捉进程的启动和关闭崩溃,忘记添加小转储处理的进程崩溃,甚至记录纯虚拟函数调用和无效CRT参数的小转储。

Windbg偶尔会自动缩减新开的窗口,这有时会让我们烦恼。如果你想回到默认窗口布局,可以通过如下操作:

菜单File\Delete Workspace删除被调试进程所保持的工作区

 

如果你不满意所有会话窗口布局,但又不无法直接关闭程序,可以手动删除如下注册表项后重启Windbg:

USER\Software\Microsoft\Windbg\Workspaces

上面说的方法都会删除工作区其他数据。

简介

STATUS_FLOAT_MULTIPLE_TRAPS---浮点数多重陷阱异常。值为0xC00002B5。定义如下

//
// MessageId: STATUS_FLOAT_MULTIPLE_TRAPS
//
// MessageText:
//
// {EXCEPTION}
// Multiple floating point traps.
//
#define STATUS_FLOAT_MULTIPLE_TRAPS      ((NTSTATUS)0xC00002B5L)    // winnt

说明

当进行浮点数运算时,且设置了浮点异常模式,当遇到比如除0.0操作时,抛此异常。

异常结构说明

ExceptionAddress: 00f515dc
   ExceptionCode: c00002b5
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000000
   Parameter[1]: 0000e144---浮点 MXCSR寄存器的值,包含设置的浮点异常掩码、当前异常状态值、清零模式和四舍五入模式等

 

简介

STATUS_FLOAT_MULTIPLE_FAULTS---多浮点错误。值为0xC00002B4。定义如下

//
// MessageId: STATUS_FLOAT_MULTIPLE_FAULTS
//
// MessageText:
//
// {EXCEPTION}
// Multiple floating point faults.
//
#define STATUS_FLOAT_MULTIPLE_FAULTS     ((NTSTATUS)0xC00002B4L)    // winnt

说明

当进行浮点数运算时,且设置了浮点异常模式,当遇到溢出时,抛此异常。

异常结构说明

ExceptionAddress: 0091146f (ConsoleApplication2!_Pow_int<double>+0x0000005f)
   ExceptionCode: c00002b4
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000000
   Parameter[1]: 00001ba8----浮点 MXCSR寄存器的值,包含设置的浮点异常掩码、当前异常状态值、清零模式和四舍五入模式等