How to catch a panic
http://wiki.forum.nokia.com/index.php/How_to_catch_a_panic
http://wiki.forum.nokia.com/index.php/%E5%A6%82%E4%BD%95%E6%8D%95%E6%8D%89panic
From Forum Nokia Wiki
Yes, a panic. Although panics, by their nature, are uncatchable there is a way to prevent them from severely influencing your program.
First off, why would you do it? What is the use-case? Typically a panic draws attention to a programmatic error that should have already been corrected in the program. It always terminates the program execution and it's on purpose: programs panic from an unrecoverable stage. So why would we want to catch a panic if we were able to do so?
Well, there is at least one special use-case: in a test framework used for automated testing. In a typical unit testing framework, for example, where test suites containing test cases are being performed it's always a nice feature if the execution of tests doesn't get interrupted even in case of a fatal error. If the severe programming error doesn't harm other test cases, then it's ideal if the termination of a test case doesn't influence the life-time of other test cases.
Can panics be caught? At first sight, it may seem that User::SetExceptionHandler() would do. But it doesn't: exceptions are not the same as panics. So installing a new exception handler will not enable us to catch panics, even though it makes it possible to catch a small portion of ALL panics (e.g. an access violation aka KERN-EXE 3).
The ultimate solution is to let your test case run in a new thread. A new thread that you have control over: you create, destroy and most importantly monitor it. You can request notification when this thread dies normally or otherwise via RThread::Logon(). Combined this with RThread::ExitReason(), you can get a clue when and how a thread has exited. Even in case of a panic your main thread will not be affected at all so your - test - framework can continue to run.
The following example shows a console based exe which monitors other thread's death, displaying type and reason:
對,說的就是Panic。儘管就其本性來說,panic是“不可捕獲的”,但仍有一種途徑可以阻止它嚴重影響你的程式。
首先,為什麼你要這樣做呢?用況是什嗎?典型地,panic引起對應該已經在程式中更正的編程錯誤的注意。panic總是終止程式運行,並是故意這樣設計的: 程式從“不可恢複的”運行階段發生panic。因此,為什麼我們需要捕獲panic(假定我們能夠這樣做)?。
好了,至少有一個特殊的用處: 在測試架構中用於自動化的測試。在一個典型的單元測試架構中,比如,執行含有測試案例的測試集,若即使發生致命錯誤測試也不中斷,這總是一個很好的功能。如果嚴重的編程錯誤不傷害其它的測試案例,那麼某個測試案例的終止不影響其它用例的生命週期將是很理想的。
panic能被捕獲嗎?乍看起來,似乎User::SetExceptionHandler()能辦到。但它卻辦不到。異常與panic不同。因此,安裝一個新異常處理器不會使我們能捕獲到panic,即使它使捕獲一小部分異常成為可能(比如,訪問違規或者說KERN-EXE 3)。
最終的解決方案: 讓你的測試案例'運行在一個新線程中。一個你控制的新線程: 建立、銷毀它,最重要的是監視它。經由RThread::Logon()你能夠請求獲知線程死亡正常與否。
下例示範了基於一個控制台的Exe,檢測其他線程的死亡,顯示類型及原因:
#ifndef THREADNOTIFIER_H<br />#define THREADNOTIFIER_H</p><p>#include <e32base.h></p><p>class CThreadNotifier : public CActive<br />{<br />public:<br /> CThreadNotifier();<br /> ~CThreadNotifier();<br /> void ConstructL();</p><p> void IssueRequest();</p><p>protected:<br /> void RunL();<br /> void DoCancel();<br /> TInt RunError(TInt aError);</p><p> RUndertaker iUndertaker;<br /> TInt iThreadHandle;<br />};</p><p>#endif
#include "ThreadNotifier.h"</p><p>#include <e32cons.h></p><p>_LIT(KPanicMsg, "THREAD-NOTIFIER");</p><p>LOCAL_D CConsoleBase* console;</p><p>CThreadNotifier::CThreadNotifier()<br /> : CActive(CActive::EPriorityStandard)<br />{<br /> CActiveScheduler::Add(this);<br />}</p><p>void CThreadNotifier::ConstructL()<br />{<br /> User::LeaveIfError(iUndertaker.Create());<br />}</p><p>CThreadNotifier::~CThreadNotifier()<br />{<br /> Cancel();<br /> iUndertaker.Close();<br />}</p><p>void CThreadNotifier::IssueRequest()<br />{<br /> __ASSERT_ALWAYS(!IsActive(), User::Panic(KPanicMsg, 0));</p><p> iUndertaker.Logon(iStatus, iThreadHandle);<br /> SetActive();<br />}</p><p>void CThreadNotifier::RunL()<br />{<br /> if (iStatus == KErrDied)<br /> {<br /> RThread thread;<br /> thread.SetHandle(iThreadHandle);<br /> console->Printf(_L("Thread %S (%d) died (Type: %d, reason %d)/n"),<br />&thread.Name(), (int)thread.Id(), thread.ExitType(), thread.ExitReason());<br /> thread.Close();<br /> }</p><p> IssueRequest();<br />}</p><p>void CThreadNotifier::DoCancel()<br />{<br /> iUndertaker.LogonCancel();<br />}</p><p>TInt CThreadNotifier::RunError(TInt /*aError*/)<br />{<br /> return KErrNone;<br />}</p><p>LOCAL_C void callExampleL()<br />{<br /> console = Console::NewL(_L("Thread Notifier"), TSize(KDefaultConsWidth,<br /> KDefaultConsHeight));<br /> CleanupStack::PushL(console);</p><p> CActiveScheduler* scheduler = new(ELeave) CActiveScheduler;<br /> CleanupStack::PushL(scheduler);<br /> CActiveScheduler::Install(scheduler);</p><p> CThreadNotifier* notifier = new(ELeave) CThreadNotifier;<br /> CleanupStack::PushL(notifier);<br /> notifier->ConstructL();<br /> notifier->IssueRequest();</p><p> CActiveScheduler::Start();</p><p> CleanupStack::PopAndDestroy(3, console); // console, scheduler, notifier<br />}</p><p>GLDEF_C TInt E32Main()<br />{<br /> __UHEAP_MARK;<br /> CTrapCleanup* cleanup = CTrapCleanup::New();<br /> TRAPD(error, callExampleL());<br /> __ASSERT_ALWAYS(!error, User::Panic(KPanicMsg, error));<br /> delete cleanup;<br /> __UHEAP_MARKEND;<br /> return 0;<br />}