當(dāng)前位置:首頁 > 芯聞號(hào) > 充電吧
[導(dǎo)讀]做為程序員,最怕什么?Bug?大家都清楚,調(diào)試期的 Bug 并不可怕,那怕是那些神龍見首不見尾的 INT(隨機(jī)、沒有規(guī)律) Bug。做為嵌入式程序員,也是一樣的。一般來說嵌入式系統(tǒng)都提供了異常分析的方

做為程序員,最怕什么?Bug?大家都清楚,調(diào)試期的 Bug 并不可怕,那怕是那些神龍見首不見尾的 INT(隨機(jī)、沒有規(guī)律) Bug。

做為嵌入式程序員,也是一樣的。一般來說嵌入式系統(tǒng)都提供了異常分析的方法,特別是強(qiáng)大的調(diào)試工具,這些工具使用在 PC 上編程使用的工具是一樣的,例如:Visual Studio 系列。但是一些專用的、或小的嵌入式系統(tǒng),可能會(huì)提供專用的調(diào)試工具。雖然從功能上來說,沒有微軟提供的 VS 功能強(qiáng)大,使用起來也不太方便,但也會(huì)提供類似的調(diào)試功能。這里我主要討論的還是微軟提供的工具。
目前,在車載與 PND 市場,使用 WinCE 系統(tǒng)的比較多。在 WinCE6.0 系統(tǒng)中,如果應(yīng)用發(fā)生較嚴(yán)重的錯(cuò)誤時(shí),一般都會(huì)彈出系統(tǒng)標(biāo)準(zhǔn)的、令人十分討論的應(yīng)用錯(cuò)誤對(duì)話框。大概提示:XXX.exe出現(xiàn)嚴(yán)重錯(cuò)誤,必須被關(guān)閉。
如何解決此類問題呢?
只要能接上調(diào)試串口,或與調(diào)試工具連接,如VS2008等,獲取出錯(cuò)時(shí)的異常信息后,就可以來分析異??赡艿脑颉?br />但如果設(shè)備已經(jīng)處于量產(chǎn)狀態(tài),無法連接輸出 LOG 的串口和調(diào)試 USB 口時(shí),如何能捕捉到異常信息呢?
在無法徹底解決此類問題的情況下,有人就想能不能不讓系統(tǒng)顯示那個(gè)錯(cuò)誤對(duì)話框。為了能使應(yīng)用“優(yōu)美”的退出(網(wǎng)絡(luò)上的說法),即程序退出時(shí)不出現(xiàn)述的錯(cuò)誤對(duì)話框,有人曾試著去修改 WinCE 提供的內(nèi)核代碼,但這部分應(yīng)該是屬于未開源的部分。所以此方法也行不通的!


解決此類問題的根本辦法當(dāng)然是提高編碼的質(zhì)量,然后加強(qiáng)質(zhì)量保證(即測試),盡量將 Bug 消滅在研發(fā)階段。因?yàn)檠邪l(fā)階段,有大量的調(diào)試工具可以使用,如下述的第一種方法。
在沒有調(diào)試工具可以依賴時(shí),有沒有辦法獲取到異常信息呢?方法當(dāng)然是有的,如下述第二種和第三種方法。為什么要說第一種方法呢,因?yàn)樗峁┑男畔⑹亲罨A(chǔ)的,是后面兩種方法都要用到的基礎(chǔ)。在這里,我重點(diǎn)推薦的是第三種方法。因?yàn)樗奶幚肀容^獨(dú)立、在 WinCE 系統(tǒng)中比較有效、且方便集成到已有代碼中,實(shí)現(xiàn)異常捕獲。


第一種方法:如果有輸出 LOG 串口可說時(shí),串口輸出的異常信息,加 MAP 文件一起分析錯(cuò)誤的出處,可以到函數(shù)一級(jí)。所以要求在調(diào)試時(shí)一定要將對(duì)應(yīng)版本的 MAP 文件一起保留,用于后繼異常問題的分析。

對(duì)于如下的測試代碼:


void?TestCrashFunc(void)
{
??int?*pNullPoint?=?NULL;

??RETAILMSG(1,(L"-----------------------------%drn",pNullPoint));
??*pNullPoint?=?0;
??RETAILMSG(1,(L"-----------------------------%d,%drn",pNullPoint,*pNullPoint));
}

void?CallCrashFunc(void)
{
??TestCrashFunc();
}

void?CSmartDeviceMFCDlg::OnTimer(UINT_PTR?nIDEvent)
{
??//?TODO:?在此添加消息處理程序代碼和/或調(diào)用默認(rèn)值
??if(1?==?nIDEvent)
??{
????KillTimer(1);
????CallCrashFunc();
????
????//?其它的功能
??}
??CDialog::OnTimer(nIDEvent);
}


在 WinCE6.0 和 WinCE7.0 下運(yùn)行時(shí),串口的輸出內(nèi)容基本上是相同的,但在 WinCE7.0 下沒有出錯(cuò)的對(duì)話框。
串口中輸出的 Crash 信息如下:



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


從對(duì)應(yīng)的 MAP 文件中查到是 TestCrashFunc 函數(shù)出錯(cuò)(PC 指針 0x00001738 + MAP 文件中的 Preferred load address 偏移量),此例中出錯(cuò)時(shí)位置為:0x00001738 + 00010000 = 00011738:



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


由此可見 WinCE7.0 系統(tǒng)對(duì)這種對(duì)空指針賦值等異常是做了一些處理的,至少不再彈出那個(gè)令人十分討厭的對(duì)話框,也不影響后繼其它功能的執(zhí)行。在 WinCE6.0 下如果出現(xiàn)類似的對(duì)話框,則應(yīng)用就會(huì)退出。


第二種方法:使用 __try 和 __except。在開源的多媒體播放器 TCPMP 中,就有如下的用法。
先定義兩個(gè)宏,然后將重要的處理線程代碼包含在定義的這兩個(gè)宏中,以捕捉兩個(gè)宏之間代碼出現(xiàn)的異常。這樣做有一個(gè)缺點(diǎn):但代碼量很大時(shí),就需要增加很多對(duì)這兩個(gè)宏的調(diào)用。



#define?SAFE_BEGIN?__try?{
#define?SAFE_END?;}?__except?(SafeException(_exception_info()))?{}


可以看到 WinCE 下的使用方法,與 PC 上 SEH(Structured Exception Handling)是一樣的。如下所示:



__try?
{
???//?guarded?code
}
__except?(?expression?)
{
???//?exception?handler?code
}


以下是 TCPMP 中一個(gè)關(guān)鍵線程的異常處理代碼(TCPMP 線程的代碼,沒有完整的給出,有興趣的童鞋請(qǐng)自己去看 TCPMP 的源代碼),其中兩個(gè)定義的異常處理宏,將線程的所有代碼包含在內(nèi)。



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


此種實(shí)現(xiàn)方法,最最關(guān)鍵是 SafeException() 函數(shù)中分析與記錄異常信息的辦法。
但由于在 TCPMP 中,獲取異常的信息與 TCPMP 的軟件框架結(jié)合在一起。需要移植此部分代碼到其它工程時(shí),需要將有用的代碼分離出來,其實(shí)這個(gè)也比較簡單。
只要將與 EXCEPTION_POINTERS 相關(guān)的代碼拿出來即可。



int?SafeException(void*?p)
{
??EXCEPTION_POINTERS*?Data?=?(EXCEPTION_POINTERS*)p;

??//?刪除了無關(guān)的代碼?-?此部分代碼是?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");
??????}
??????
??????//......?關(guān)鍵是處理?EXCEPTION_POINTERS?結(jié)構(gòu)體相關(guān)的成員
??????//?其它一些相關(guān)的,如可執(zhí)行程序文件名等,根據(jù)需要來獲取
??}
}


第三種方法:使用函數(shù) AddVectoredExceptionHandler()。
在 WinCE 下使用此函數(shù),需要包含頭文件: TlHelp32.h 和庫文件: toolhelp.lib。由于此函數(shù)屬于 WinCE 示公開的 API,所以幫忙只要以 PC 上為準(zhǔn)。
使用此函數(shù),是向 WinCE 系統(tǒng)注冊(cè)一個(gè)矢量異常處理程序,但有異常發(fā)生時(shí)會(huì)調(diào)用此處理程序。
函數(shù)的原型如下(MSDN),各參數(shù)具體的含義,請(qǐng)參考 MSDN。這里就不做翻譯了。



PVOID?WINAPI?AddVectoredExceptionHandler(__in?ULONG?FirstHandler,?__in?PVECTORED_EXCEPTION_HANDLER?VectoredHandler);

以下代碼,演示了如何使用 AddVectoredExceptionHandler() 函數(shù):
(1) AddVectoredExceptionHandler(1,MyVectoredExceptionHandler);


(2) 定義異常處理程序

LONG?WINAPI?MyVectoredExceptionHandler(struct?_EXCEPTION_POINTERS?*pExceptionInfo)
{
??typedef?ULONG?(WINAPI?*lpGetThreadCallStack)(HANDLE,ULONG,LPVOID,DWORD,DWORD);
??/*?在使用時(shí),必須包含一些頭文件。這些頭文件,需要從?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;
??//?獲取線程堆棧調(diào)用
??HMODULE?hCore?=?LoadLibrary(L"coredll.dll");
??if(NULL?!=?hCore)
??{
????lpGetThreadCallStack?pGetThreadCallStack?=?(lpGetThreadCallStack)GetProcAddress(hCore,L"GetThreadCallStack");
????if(NULL?!=?pGetThreadCallStack)
????{
????}
??}
??//?獲取進(jìn)程內(nèi)?dll?信息
??MODULEENTRY32?CurrentModule;
??HANDLE?hSnapShot?=?CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,GetCurrentProcessId());
??if((HANDLE)-1?!=?hSnapShot)
??{
????//?調(diào)用??Module32First??和?Module32Next?完成?Module?枚舉
??}
}


是否還需要其它信息來分析程序出現(xiàn)異常的原因,可以參考 MSDN 中對(duì)結(jié)構(gòu) _EXCEPTION_POINTERS 中各成員的說明。然后將有用的信息,寫到 SD 卡等可永久存貯的設(shè)備中。這樣就不必為擔(dān)心找不到用于分析異常的資源,且此記錄的文件中的信息遠(yuǎn)大于串口輸出的異常信息。同時(shí),也可以根據(jù)需要輸入一些應(yīng)用(進(jìn)程)的相關(guān)信息。
可以將此異常捕獲功能的代碼,封裝成一個(gè) LIB 來供使用程序調(diào)用。


相對(duì)于 PC Windows 下感知程序崩潰(其實(shí)就是運(yùn)行時(shí)的嚴(yán)重錯(cuò)誤)的方法,WinCE 還是比較少的,且真正被用的更是少之又少。
PC 下除了以上第二和第三種方法外,還有以下 3 個(gè)核心的函數(shù)可以感知程序的異常,分別是:
SetUnhandledExceptionFilter(HandleException),功能是確定出現(xiàn)沒有控制的異常發(fā)生時(shí)調(diào)用的函數(shù)為 HandleException;函數(shù)在 WinCE 上是不可用的,無100%替代函數(shù)。
_set_invalid_parameter_handler(HandleInvalidParameter),功能是確定出現(xiàn)無效參數(shù)調(diào)用發(fā)生時(shí)調(diào)用的函數(shù)為 HandleInvalidParameter;
_set_purecall_handler(HandlePureVirtualCall),功能是確定純虛函數(shù)調(diào)用發(fā)生時(shí)調(diào)用的函數(shù)為 HandlePureVirtualCall。


充分利用 Bug 的解決方法,是一個(gè)程序員成長的必由之路。因?yàn)槌绦騿T的工作不只是編碼,還包括前期設(shè)計(jì)與后期的產(chǎn)品問題的修復(fù)等。
好的應(yīng)用,就應(yīng)該像 TCPMP 一樣,在異常來臨時(shí)能正確的提示用戶,這樣的程序的崩潰也朝“優(yōu)美”邁進(jìn)了一步。同時(shí),也提供的開發(fā)人員分析異常的信息:記錄在文件中。


本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動(dòng)力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉