如何在使用SEH时解释GetExceptionCode结果? [英] How do I interpret GetExceptionCode results when using SEH?

查看:164
本文介绍了如何在使用SEH时解释GetExceptionCode结果?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在尝试写一些错误保护条款来识别由第三方提供给我们的dll中的问题。在这个dll中可能会有问题(内存异常,浮点错误等),并且能够识别这些错误而无需访问源代码是有利的。



我从各种SEH错误处理例程中汇集了一些东西,但是虽然它起作用,但是它有一些不一致的地方。我试图隔离每一个,我要单独提出一个问题。



这一个是用GetExceptionCode,用在SEH __try / __除了用来标识错误的子句。它似乎没有这样做是可靠的。



这是一个明确的除零案件:

  #include< float.h> //定义_EM_OVERFLOW等
#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(上)

$ b $常量无符号整数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;
双分母= 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 {
结果=分子/分母;
_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<< 除以零<<的std :: ENDL;
std :: cout<< | <<的std :: ENDL;
std :: cout<< 模糊的代码?下溢<<的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<< 非正规数字不准确数字<<的std :: ENDL;
std :: cout<< :| <<的std :: ENDL;
std :: cout<< 溢出<<的std :: ENDL;
std :: cout<< | <<的std :: ENDL;
std :: cout<< 无效号码<<的std :: ENDL;

if(GetExceptionCode()& _EM_ZERODIVIDE)
std :: cout<< 错误!除以零! <<的std :: ENDL;
else
std :: cout<< 这里找不到零分! <<的std :: ENDL;
_controlfp_s(NULL,_previous_floating_point_control,_MCW_EM);
}

std :: cout<< result =<<结果<的std :: ENDL;

while(!_kbhit())//等到一个键被按下来关闭控制台。




$ b $打印下列内容:



$ $ $ $ $ $ $ $ $ $ $ $ $ $ _ $ _ $ _ $ _ $ _ $ _ 00000000000000000000000000010000
_EM_DENORMAL = 00000000000010000000000000000000
_EM_AMBIGUOUS = 10000000000000000000000000000000

除零
|
含糊不清的代码?下溢
| :|
v v v
例外代码= 11000000000000000000001010110101
^ ^ ^ ^
| ::
非正规数字不准确数字
:|
溢出
|
无效数字
在这里找不到零除数!
结果= 0

确定了一个问题(很好)它是相当正确的。

更糟糕的是,当这个子句被替换为一个缺少依赖的DLL的调用时,我得到:

  fp例外
非正规数|
| _ | _
v / \
11000000011011010000000001111110
^^ ^ ^ ^^
|| | | ||
\ ________________ /
未知的代码

类似的结果返回SIGSEV错误(分段错误)的情况。这意味着我们将其他问题误识为浮点异常。

所以我的问题是:
$ b $ ol

  • 这种一般的方法是正确的,还是我误解了
    基本的东西?

  • 为什么这不是拾取被零除的简单情况?是否依赖于硬件?

  • 我可以找出其余的错误位来自GetExceptionCode() - 这真的很有用。 >

    PS:请不要评论或回复说我应该检查分母是否为0 - 我知道,而且我全部代码我有控制权。

    解决方案

    您将需要沿着

      DWORD exception_filter(DWORD dwExceptionCode)
    {
    //使用dwExceptionCode仅处理您想要的
    类型的异常//如果您想要在你的处理程序中使用它,你需要保存它。
    返回EXCEPTION_EXECUTE_HANDLER; //或取决于ExceptionCode的其他值


    您的异常处理程序...

    __try
    {
    something();
    }
    __except(exception_filter(GetExceptionCode())
    {
    //不要在这里调用GetExceptionCode()或GetExceptionInfo()如果你需要
    // Exception信息以及传递给过滤器,并保存你需要的值。
    switch(dwSavedExceptionCode)
    {
    case EXCEPTION_FLT_OVERFLOW:
    ItWasAFloatingPointOverflow();
    break ;
    case EXCEPTION_FLT_DIVIDE_BY_ZERO:
    ItWasAFloatingDivideByZero();
    break;
    case ***您希望处理的其他异常类型(==您在过滤器中返回的execute_handler)* **
    break;
    }
    }


    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. 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.

    I have something put together from various SEH error handling routines, but although it works, there are several... inconsistencies with it. 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. 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:

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

    A similar result is returned in the case of a SIGSEV error (segmentation fault). 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.

    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.

    解决方案

    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;
        }
    }
    

    这篇关于如何在使用SEH时解释GetExceptionCode结果?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆