使用 SEH 时如何解释 GetExceptionCode 结果?
How do I interpret GetExceptionCode results when using SEH?
我一直在尝试编写一些错误保护条款来识别第三方提供给我们的dll 中的问题。此dll中可能存在问题(内存异常、浮点错误等),最好能在不访问源代码的情况下识别这些错误。
我从各种 SEH 错误处理例程中收集了一些东西,但尽管它有效,但有几个...与它不一致。我试图将每一个都隔离开来,我将针对每一个单独问一个问题。
这个是和GetExceptionCode 做的,用在SEH __try/__except 子句中来识别错误。它似乎并不可靠。
这是一个明显的被零除的情况:
#include <float.h> // defines of _EM_OVERFLOW, etc.
#include <string.h> // strncpy_s & strncat_s
#include <stdlib.h> // malloc
#include <excpt.h> // EXCEPTION_EXECUTE_HANDLER
#include <iostream> // cout
#include <bitset> // bitset
#include <conio.h> // _kbhit
#pragma fenv_access (on)
const unsigned int SERIOUS_FP_EXCEPTIONS = _EM_DENORMAL | _EM_ZERODIVIDE | _EM_INVALID;
const unsigned int MINOR_FP_EXCEPTIONS = _EM_OVERFLOW | _EM_UNDERFLOW | _EM_INEXACT;
int main(int argc, char[])
{
double numerator = 1.0;
double denominator = 0.0;
double result = 0.0;
unsigned int _previous_floating_point_control;
_controlfp_s(&_previous_floating_point_control, 0, 0);
_controlfp_s(nullptr, MINOR_FP_EXCEPTIONS, _MCW_EM);
__try {
result = numerator / denominator;
_controlfp_s(NULL, _previous_floating_point_control, _MCW_EM);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
std::cout << "_EM_INEXACT = " << std::bitset<32>(_EM_INEXACT) << std::endl;
std::cout << "_EM_UNDERFLOW = " << std::bitset<32>(_EM_UNDERFLOW) << std::endl;
std::cout << "_EM_OVERFLOW = " << std::bitset<32>(_EM_OVERFLOW) << std::endl;
std::cout << "_EM_ZERODIVIDE = " << std::bitset<32>(_EM_ZERODIVIDE) << std::endl;
std::cout << "_EM_INVALID = " << std::bitset<32>(_EM_INVALID) << std::endl;
std::cout << "_EM_DENORMAL = " << std::bitset<32>(_EM_DENORMAL) << std::endl;
std::cout << "_EM_AMBIGUOUS = " << std::bitset<32>(_EM_AMBIGUOUS) << std::endl;
std::cout << std::endl;
std::cout << " divide-by-zero" << std::endl;
std::cout << " |" << std::endl;
std::cout << " ambiguous code? underflow" << std::endl;
std::cout << " | : |" << std::endl;
std::cout << " v v v" << std::endl;
std::cout << "Exception code = " << std::bitset<32>(GetExceptionCode()) << std::endl;
std::cout << " ^ ^ ^ ^" << std::endl;
std::cout << " | : : |" << std::endl;
std::cout << " denormal number inexact number" << std::endl;
std::cout << " : |" << std::endl;
std::cout << " overflow" << std::endl;
std::cout << " |" << std::endl;
std::cout << " invalid number" << std::endl;
if (GetExceptionCode() & _EM_ZERODIVIDE)
std::cout << "ERROR! Divide By Zero!" << std::endl;
else
std::cout << "No divide by zero found here!" << std::endl;
_controlfp_s(NULL, _previous_floating_point_control, _MCW_EM);
}
std::cout << "result = " << result << std::endl;
while (!_kbhit()) // Wait until a key is pressed to close console.
{ }
}
这将打印以下内容:
_EM_INEXACT = 00000000000000000000000000000001
_EM_UNDERFLOW = 00000000000000000000000000000010
_EM_OVERFLOW = 00000000000000000000000000000100
_EM_ZERODIVIDE = 00000000000000000000000000001000
_EM_INVALID = 00000000000000000000000000010000
_EM_DENORMAL = 00000000000010000000000000000000
_EM_AMBIGUOUS = 10000000000000000000000000000000
divide-by-zero
|
ambiguous code? underflow
| : |
v v v
Exception code = 11000000000000000000001010110101
^ ^ ^ ^
| : : |
denormal number inexact number
: |
overflow
|
invalid number
No divide by zero found here!
result = 0
它发现了一个问题(很好),但还没有完全正确地诊断它。
更糟糕的是,当该子句被替换为对缺少依赖项的 dll 的调用时,我得到:
f.p. exceptions
denormal number |
| _|_
v / \
11000000011011010000000001111110
^^ ^ ^ ^^
|| | | ||
\________________/
unknown codes
在 SIGSEV 错误(分段错误)的情况下会返回类似的结果。这意味着我们将其他问题误诊为浮点异常。
所以我的问题是:
- 这个一般方法是否正确,还是我误解了什么
基础?
- 为什么这不采用被零除的简单情况?是否依赖于硬件?
- 我能否找出来自 GetExceptionCode() 的其余错误位是什么 - 这将真的有用。
PS: 请不要评论或回复说我应该检查分母是否为 0 - 我知道,我在我控制的所有代码中都这样做。
你将需要一些类似
的东西
DWORD exception_filter(DWORD dwExceptionCode)
{
// use dwExceptionCode to handle only the types of exceptions you want
// if you want to use it inside your handler, you'll need to save it.
return EXCEPTION_EXECUTE_HANDLER; // or other value depending on ExceptionCode
}
Your exception handler...
__try
{
something();
}
__except (exception_filter(GetExceptionCode())
{
// DO NOT CALL GetExceptionCode() or GetExceptionInfo() here. If you need
// Exception Info as well, pass it to the filter, and save the values you need.
switch (dwSavedExceptionCode)
{
case EXCEPTION_FLT_OVERFLOW:
ItWasAFloatingPointOverflow();
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
ItWasAFloatingDivideByZero();
break;
case ***Other Exception Types You Want handled (==the ones you returned execute_handler for in the filter) ***
break;
}
}
Exception code = 11000000000000000000001010110101
那个值是 0xC00002B5
,又名 STATUS_FLOAT_MULTIPLE_TRAPS
。
参见。
我一直在尝试编写一些错误保护条款来识别第三方提供给我们的dll 中的问题。此dll中可能存在问题(内存异常、浮点错误等),最好能在不访问源代码的情况下识别这些错误。
我从各种 SEH 错误处理例程中收集了一些东西,但尽管它有效,但有几个...与它不一致。我试图将每一个都隔离开来,我将针对每一个单独问一个问题。
这个是和GetExceptionCode 做的,用在SEH __try/__except 子句中来识别错误。它似乎并不可靠。
这是一个明显的被零除的情况:
#include <float.h> // defines of _EM_OVERFLOW, etc.
#include <string.h> // strncpy_s & strncat_s
#include <stdlib.h> // malloc
#include <excpt.h> // EXCEPTION_EXECUTE_HANDLER
#include <iostream> // cout
#include <bitset> // bitset
#include <conio.h> // _kbhit
#pragma fenv_access (on)
const unsigned int SERIOUS_FP_EXCEPTIONS = _EM_DENORMAL | _EM_ZERODIVIDE | _EM_INVALID;
const unsigned int MINOR_FP_EXCEPTIONS = _EM_OVERFLOW | _EM_UNDERFLOW | _EM_INEXACT;
int main(int argc, char[])
{
double numerator = 1.0;
double denominator = 0.0;
double result = 0.0;
unsigned int _previous_floating_point_control;
_controlfp_s(&_previous_floating_point_control, 0, 0);
_controlfp_s(nullptr, MINOR_FP_EXCEPTIONS, _MCW_EM);
__try {
result = numerator / denominator;
_controlfp_s(NULL, _previous_floating_point_control, _MCW_EM);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
std::cout << "_EM_INEXACT = " << std::bitset<32>(_EM_INEXACT) << std::endl;
std::cout << "_EM_UNDERFLOW = " << std::bitset<32>(_EM_UNDERFLOW) << std::endl;
std::cout << "_EM_OVERFLOW = " << std::bitset<32>(_EM_OVERFLOW) << std::endl;
std::cout << "_EM_ZERODIVIDE = " << std::bitset<32>(_EM_ZERODIVIDE) << std::endl;
std::cout << "_EM_INVALID = " << std::bitset<32>(_EM_INVALID) << std::endl;
std::cout << "_EM_DENORMAL = " << std::bitset<32>(_EM_DENORMAL) << std::endl;
std::cout << "_EM_AMBIGUOUS = " << std::bitset<32>(_EM_AMBIGUOUS) << std::endl;
std::cout << std::endl;
std::cout << " divide-by-zero" << std::endl;
std::cout << " |" << std::endl;
std::cout << " ambiguous code? underflow" << std::endl;
std::cout << " | : |" << std::endl;
std::cout << " v v v" << std::endl;
std::cout << "Exception code = " << std::bitset<32>(GetExceptionCode()) << std::endl;
std::cout << " ^ ^ ^ ^" << std::endl;
std::cout << " | : : |" << std::endl;
std::cout << " denormal number inexact number" << std::endl;
std::cout << " : |" << std::endl;
std::cout << " overflow" << std::endl;
std::cout << " |" << std::endl;
std::cout << " invalid number" << std::endl;
if (GetExceptionCode() & _EM_ZERODIVIDE)
std::cout << "ERROR! Divide By Zero!" << std::endl;
else
std::cout << "No divide by zero found here!" << std::endl;
_controlfp_s(NULL, _previous_floating_point_control, _MCW_EM);
}
std::cout << "result = " << result << std::endl;
while (!_kbhit()) // Wait until a key is pressed to close console.
{ }
}
这将打印以下内容:
_EM_INEXACT = 00000000000000000000000000000001
_EM_UNDERFLOW = 00000000000000000000000000000010
_EM_OVERFLOW = 00000000000000000000000000000100
_EM_ZERODIVIDE = 00000000000000000000000000001000
_EM_INVALID = 00000000000000000000000000010000
_EM_DENORMAL = 00000000000010000000000000000000
_EM_AMBIGUOUS = 10000000000000000000000000000000
divide-by-zero
|
ambiguous code? underflow
| : |
v v v
Exception code = 11000000000000000000001010110101
^ ^ ^ ^
| : : |
denormal number inexact number
: |
overflow
|
invalid number
No divide by zero found here!
result = 0
它发现了一个问题(很好),但还没有完全正确地诊断它。
更糟糕的是,当该子句被替换为对缺少依赖项的 dll 的调用时,我得到:
f.p. exceptions
denormal number |
| _|_
v / \
11000000011011010000000001111110
^^ ^ ^ ^^
|| | | ||
\________________/
unknown codes
在 SIGSEV 错误(分段错误)的情况下会返回类似的结果。这意味着我们将其他问题误诊为浮点异常。
所以我的问题是:
- 这个一般方法是否正确,还是我误解了什么 基础?
- 为什么这不采用被零除的简单情况?是否依赖于硬件?
- 我能否找出来自 GetExceptionCode() 的其余错误位是什么 - 这将真的有用。
PS: 请不要评论或回复说我应该检查分母是否为 0 - 我知道,我在我控制的所有代码中都这样做。
你将需要一些类似
的东西DWORD exception_filter(DWORD dwExceptionCode)
{
// use dwExceptionCode to handle only the types of exceptions you want
// if you want to use it inside your handler, you'll need to save it.
return EXCEPTION_EXECUTE_HANDLER; // or other value depending on ExceptionCode
}
Your exception handler...
__try
{
something();
}
__except (exception_filter(GetExceptionCode())
{
// DO NOT CALL GetExceptionCode() or GetExceptionInfo() here. If you need
// Exception Info as well, pass it to the filter, and save the values you need.
switch (dwSavedExceptionCode)
{
case EXCEPTION_FLT_OVERFLOW:
ItWasAFloatingPointOverflow();
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
ItWasAFloatingDivideByZero();
break;
case ***Other Exception Types You Want handled (==the ones you returned execute_handler for in the filter) ***
break;
}
}
Exception code = 11000000000000000000001010110101
那个值是 0xC00002B5
,又名 STATUS_FLOAT_MULTIPLE_TRAPS
。
参见