简体   繁体   English

使用SEH时如何解释GetExceptionCode结果?

[英]How do I interpret GetExceptionCode results when using SEH?

I have been trying to write some error-protection clauses for identifying problems in a dll which is provided to us by an third party. 我一直在尝试编写一些错误保护子句,以识别由第三方提供给我们的dll中的问题。 There may be problems in this dll (memory exceptions, floating point errors, etc), and it is advantageous to be able to identify these errors without access to the source code. 该dll中可能存在问题(内存异常,浮点错误等),并且能够在不访问源代码的情况下识别这些错误是有利的。

I have something put together from various SEH error handling routines, but although it works, there are several... inconsistencies with it. 我从各种SEH错误处理例程中收集了一些信息,但是尽管可以正常工作,但还是有一些...不一致之处。 I'm trying to isolate each one, and I'm going to ask a question on each one individually. 我正试图隔离每个人,并且我将分别对每个人提出一个问题。

This one is to do with the GetExceptionCode, used in the SEH __try/__except clause to identify the error. 这与在SEH __try / __ except子句中用于识别错误的GetExceptionCode有关。 It doesn't seem to do so reliably. 它似乎不能可靠地做到这一点。

This is a clear divide-by-zero case: 这是一个很明显的被零除的情况:

#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.
    { }
}

And this prints the following: 这将打印以下内容:

_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

It has identified a problem (great), but hasn't diagnosed it quite correctly. 它已识别出一个问题(严重),但尚未完全正确诊断。

Worse still, when the clause is replaced with a call to a dll which is missing a dependency, I get: 更糟糕的是,当子句被缺少依赖项的dll调用所替代时,我得到:

                       f.p. exceptions
     denormal number         |
            |               _|_
            v              /   \
11000000011011010000000001111110
         ^^  ^ ^         ^^
         ||  | |         ||
         \________________/
           unknown codes

A similar result is returned in the case of a SIGSEV error (segmentation fault). 如果发生SIGSEV错误(分段错误),则返回相似的结果。 This means that we're misdiagnosing other problems as floating point exceptions. 这意味着我们将其他问题误诊断为浮点异常。

So my questions are: 所以我的问题是:

  1. Is this general approach correct, or am I misunderstanding something fundamental? 这种一般方法是正确的,还是我误解了一些基本知识?
  2. Why is this not picking up the simple case of a divide-by-zero? 为什么不采用简单的除以零的方法呢? Is it hardware dependent? 是否取决于硬件?
  3. Can I find out what the rest of the error bits are coming from GetExceptionCode() - that would be really useful. 我可以找出其余的错误位来自GetExceptionCode()的情况吗-这真的很有用。

PS: Please don't comment or reply to say that I should check whether the denominator is 0 - I know, and I do this in all the code I have control over. PS:请不要发表评论或回答我应该检查分母是否为0-我知道,我在我可以控制的所有代码中都这样做。

You will need something along the lines of 您将需要一些类似的东西

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 异常代码= 11000000000000000000001010110101

That value is 0xC00002B5 , aka STATUS_FLOAT_MULTIPLE_TRAPS . 该值为0xC00002B5 ,又名STATUS_FLOAT_MULTIPLE_TRAPS

See Why after enabling floating point exceptions I got multiple traps error . 请参阅为什么在启用浮点异常后出现多个陷阱错误

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM