本文正在参与 CSDN 博文大赛,如果你觉得本文对您有有,请投一票:投票WWW网址(投票按键在文章最后)
做为程序员,最怕什么?Bug?大家都清楚,调试期的 Bug 并不可怕,那怕是那些神龙见首不见尾的 INT(随机、没有规律) Bug。
做为嵌入式程序员,也是一样的。一般来说嵌入式系统都提供了异常分析的方法,特别是强大的调试工具,这些工具使用在 PC 上编程使用的工具是一样的,例如:Visual Studio 系列。但是一些专用的、或小的嵌入式系统,可能会提供专用的调试工具。虽然从功能上来说,没有微软提供的 VS 功能强大,使用起来也不太方便,但也会提供类似的调试功能。这里我主要讨论的还是微软提供的工具。对于如下的测试代码:
void TestCrashFunc(void) { int *pNullPoint = NULL; RETAILMSG(1,(L"-----------------------------%d\r\n",pNullPoint)); *pNullPoint = 0; RETAILMSG(1,(L"-----------------------------%d,%d\r\n",pNullPoint,*pNullPoint)); } void CallCrashFunc(void) { TestCrashFunc(); } void CSmartDeviceMFCDlg::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if(1 == nIDEvent) { KillTimer(1); CallCrashFunc(); // 其它的功能 } CDialog::OnTimer(nIDEvent); }
Exception 'Data Abort' (0x4): Thread-Id=0780000a(pth=c08e24e0), Proc-Id=077e000a(pprc=c088da7c) 'SmartDeviceMFC.exe', VM-active=077e000a(pprc=c088da7c) 'SmartDeviceMFC.exe' PC=00011738(SmartDeviceMFC.exe+0x00001738) RA=4002ac4c(coredll.dll+0x0001ac4c) SP=0004f6a8, BVA=00000000 Exception 'Raised Exception' (0x116): Thread-Id=0780000a(pth=c08e24e0), Proc-Id=00400002(pprc=8360b5e0) 'NK.EXE', VM-active=077e000a(pprc=c088da7c) 'SmartDeviceMFC.exe' PC=eff6ed60(k.coredll.dll+0x0001ed60) RA=8052a62c(kernel.dll+0x0000e62c) SP=d9bbf3b4, BVA=ffffffff
SmartDeviceMFC Timestamp is 539fa9e3 (Tue Jun 17 10:37:23 2014) Preferred load address is 00010000 ...... 0001:000006a8 ?InitInstance@CSmartDeviceMFCApp@@UAAHXZ 000116a8 f SmartDeviceMFC.obj 0001:00000708 ?OnCbnDropdownCombo1@CSmartDeviceMFCDlg@@QAAXXZ 00011708 f SmartDeviceMFCDlg.obj 0001:00000714 ?TestCrashFunc@@YAXXZ 00011714 f SmartDeviceMFCDlg.obj 0001:0000075c ?BeginModalState@CWnd@@UAAXXZ 0001175c f i SmartDeviceMFCDlg.obj 0001:00000768 ?EndModalState@CWnd@@UAAXXZ 00011768 f i SmartDeviceMFCDlg.obj 0001:00000774 ??_GCComboBox@@UAAPAXI@Z 00011774 f i SmartDeviceMFCDlg.obj
#define SAFE_BEGIN __try { #define SAFE_END ;} __except (SafeException(_exception_info())) {}
__try { // guarded code } __except ( expression ) { // exception handler code }
static int ProcessThread(player_base* p) { int Result = ERR_NONE; #ifdef MULTITHREAD SAFE_BEGIN while (p->Wnd) { ...... if (p->RunProcess) { processstate State; State.Fill = p->Fill; p->Timer->Get(p->Timer,TIMER_TIME,&State.Time,sizeof(tick_t)); //DEBUG_MSG1(DEBUG_PLAYER,T("Process Time:%d"),State.Time); Result = p->Format->Process(p->Format,&State); if (Result == ERR_SYNCED) { ...... } else if (p->Fill && (Result == ERR_END_OF_FILE || Result == ERR_BUFFER_FULL || (Result == ERR_NEED_MORE_DATA && (p->NoMoreInput || State.BufferUsedAfter >= p->CurrBufferSize2-2)))) { ...... } ...... } ...... } SAFE_END return 0; }
int SafeException(void* p) { EXCEPTION_POINTERS* Data = (EXCEPTION_POINTERS*)p; // 删除了无关的代码 - 此部分代码是 TCPMP 中的代码,所以未做排版。 { { const uint8_t* ContextRecord = (const uint8_t*) Data->ContextRecord; EXCEPTION_RECORD* Record = Data->ExceptionRecord; switch (Record->ExceptionCode) { case STATUS_ACCESS_VIOLATION: Name = T("Access violation"); break; case STATUS_BREAKPOINT: Name = T("Breakpoint"); break; case STATUS_DATATYPE_MISALIGNMENT: Name = T("Datatype misalignment"); break; case STATUS_ILLEGAL_INSTRUCTION: Name = T("Illegal instruction"); break; case STATUS_INTEGER_DIVIDE_BY_ZERO: Name = T("Int divide by zero"); break; case STATUS_INTEGER_OVERFLOW: Name = T("Int overflow"); break; case STATUS_PRIVILEGED_INSTRUCTION: Name = T("Priv instruction"); break; case STATUS_STACK_OVERFLOW: Name = T("Stack overflow"); break; default: Name = T("Unknown"); break; } if (Record->ExceptionCode == STATUS_ACCESS_VIOLATION) { if (Record->ExceptionInformation[0]) Name = T("Write to"); else Name = T("Read from"); } //...... 关键是处理 EXCEPTION_POINTERS 结构体相关的成员 // 其它一些相关的,如可执行程序文件名等,根据需要来获取 } }
PVOID WINAPI AddVectoredExceptionHandler(__in ULONG FirstHandler, __in PVECTORED_EXCEPTION_HANDLER VectoredHandler);以下代码,演示了如何使用 AddVectoredExceptionHandler() 函数:
(2) 定义异常处理程序
LONG WINAPI MyVectoredExceptionHandler(struct _EXCEPTION_POINTERS *pExceptionInfo) { typedef ULONG (WINAPI *lpGetThreadCallStack)(HANDLE,ULONG,LPVOID,DWORD,DWORD); /* 在使用时,必须包含一些头文件。这些头文件,需要从 WinCE 的安装目录中获得。 OS Versions: Windows CE 5.0 and later. Header: Pkfuncs.h. */ typedef struct _CallSnapshotEx { DWORD dwReturnAddr; DWORD dwFramePtr; DWORD dwCurProc; DWORD dwParams[4]; }CallSnapshotEx; // 打印 Dump 信息 ...... // 打印 SP 堆栈 ...... ULONG *punSp = (ULONG *)pExceptionInfo->ContextRecord->Sp; // 获取线程堆栈调用 HMODULE hCore = LoadLibrary(L"coredll.dll"); if(NULL != hCore) { lpGetThreadCallStack pGetThreadCallStack = (lpGetThreadCallStack)GetProcAddress(hCore,L"GetThreadCallStack"); if(NULL != pGetThreadCallStack) { } } // 获取进程内 dll 信息 MODULEENTRY32 CurrentModule; HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,GetCurrentProcessId()); if((HANDLE)-1 != hSnapShot) { // 调用 Module32First 和 Module32Next 完成 Module 枚举 } }