我正在嘗試編寫一個未處理的異常過濾器(請參閱SetUnhandledExceptionFilter())以與Windows SEH配合使用來報告無效的浮點操作。我想捕捉異常,打印堆棧跟蹤,然後禁用浮點異常,並重新執行,生成非有限或非數值的值。在WIN32和WIN64中配置浮點單元上下文
我寫了下面的簡單程序來演示捕捉異常並恢復執行的能力。注意使用#ifdef _WIN64,因爲ContextRecord的定義根據目標體系結構而變化(WIN32使用「FloatSave」,WIN64使用「FltSave」)。
#include "stdafx.h"
#include <float.h>
#include <Windows.h>
double zero = 0.0;
LONG WINAPI myfunc(EXCEPTION_POINTERS * ExceptionInfo){
/* clear the exception */
unsigned int stat = _clear87();
/* disable fp exceptions*/
unsigned int ctrl1 = _control87(_MCW_EM, _MCW_EM);
/* Disable and clear fp exceptions in the exception context */
#if _WIN64
ExceptionInfo->ContextRecord->FltSave.ControlWord = ctrl1;
ExceptionInfo->ContextRecord->FltSave.StatusWord = 0;
#else
ExceptionInfo->ContextRecord->FloatSave.ControlWord = ctrl1;
ExceptionInfo->ContextRecord->FloatSave.StatusWord = 0;
#endif
printf("#########Caught Ya#####!\n");
return EXCEPTION_CONTINUE_EXECUTION;
}
int _tmain(int argc, _TCHAR* argv[])
{
double a;
/* enable fp exceptions*/
_controlfp(0, _MCW_EM);
/* Setup our unhandled exception filter */
SetUnhandledExceptionFilter(myfunc);
/* do something bad */
a = 5.0/zero;
printf("a = %f\n",a);
return 0;
}
當作爲WIN32 .EXE運行,上述程序運行正常,與輸出:
#########Caught Ya#####!
a = -1.#IND00
然而,當作爲WIN64 .EXE運行,該程序進入無限循環,不斷地重新捕捉浮點異常:
#########Caught Ya#####!
#########Caught Ya#####!
#########Caught Ya#####!
#########Caught Ya#####!
#########Caught Ya#####!
#########Caught Ya#####!
...
這似乎表明,我不是成功的配置浮點單元,這是我想象有關的Contex的不同定義在WIN32和WIN64之間的tRecord,但我一直無法找到任何關於如何設置浮點上下文的相關文檔。
關於如何正確設置WIN64的浮點上下文的任何想法?
在此先感謝
默認情況下,MSC總是編譯浮點指令以將SSE單元用於64位代碼。根據您運行的CPU,_control87'調用可能不會爲SSE單元設置相應的MXCSR標誌。 [本問答]中的一些更多詳細信息(http://stackoverflow.com/q/20045968/1889329)。 – IInspectable
@IInspectable,謝謝你的回覆。事實上,在64位平臺上,MXCSR顯示在ContextRecord下:ContextRecord-> FltSave.MxCsr; 'ContextRecord-> FltSave.MxCsr_Mask';最後是'ContextRecord-> MxCsr'。我想我已經讀過MXCSR與_control87具有相同的定義,所以我嘗試將上述上下文記錄的各種組合分配給我的ctrl1變量,但是迄今爲止沒有成功 – jHops
我想我現在看到現在發生了什麼:使用FPU代碼,當操作失敗時,它將設置異常標誌,但僅在** next ** FPU指令中引發浮點異常。由於你的異常過濾器指示控制流返回到觸發異常的操作碼(可能是'FST',而不是實際的分區),異常位被清除,它將繼續執行。有了SSE代碼,我相信這個例外是由失敗的操作碼引起的,在您的案例中這個分支。該指令一遍又一遍地執行。通過組裝來驗證這是否正確。 – IInspectable