Symbian調試技術
若Panic發生了,除非在指定的位置有一個名叫“ErrRd”的檔案,否則模擬器不顯示Panic的細節。這使得很難知道是什麼引起Panic。
在SDK 3rd版以前,ErrRd檔案必須手工建立,但從3rd版以後,這個檔案可以預設在目錄“C:/Symbian/9.2/S60_3rd_FP1/Epoc32/winscw/c/resource”下找到。有了ErrRd,Panic發生時的輸出像這樣:
''提示: 如果即使使用3rd版的SDK,ErrRd檔案也找不到,那就啟動模擬器,選擇 Tools > Preferences'',然後勾上Extended panic code file。
使用斷言檢測所做的“代碼是正確的”的假設,例如: 對象的狀態,期望的函數參數和傳回值等。Symbian OS中定義了兩個斷言宏: __ASSERT_ALWAYS 和__ASSERT_DEBUG。它們之間的區別是__ASSERT_DEBUG不會影響產品代碼而__ASSERT_ALWAYS會。
這是一個如何使用__ASSERT_DEBUG宏的例子:
view plaincopy to clipboardprint?
- void TestValue(TInt aValue)
- {
- _LIT(KPanicCategory, "TestValue");
- __ASSERT_DEBUG((aValue >= 0), User::Panic(KPanicCategory, 99));
- // Do something with aValue
- // ...
- }
void TestValue(TInt aValue){ _LIT(KPanicCategory, "TestValue"); __ASSERT_DEBUG((aValue >= 0), User::Panic(KPanicCategory, 99)); // Do something with aValue // ...}
上例中,如果參數aValue小於0,拋出"Panic -99"。
''註: 斷言宏預設不拋Panic, 允許你來決定宣告失敗時調用什麼過程。儘管如此,這種情況下你應該總是拋出Panic而不是返回錯誤或Leave。''
因為上例使用__ASSERT_DEBUG宏,只在debug編譯時間才檢測aValue。如果有必要在產品代碼中也檢測參數,就應當用__ASSERT_ALWAYS。
當你不希望外部調用者需要跟蹤Panic時,使用__ASSERT_DEBUG的一個替代品: ASSERT宏。ASSERT完全象是斷言宏,除了它不要要你提供panic類別或描述符。
這裡是該宏的定義,來自e32def.h檔案:
view plaincopy to clipboardprint?
- #define ASSERT(x) __ASSERT_DEBUG(x, User::Invariant())
#define ASSERT(x) __ASSERT_DEBUG(x, User::Invariant())
這是一個如何使用ASSERT宏的例子:
view plaincopy to clipboardprint?
- void TestValue(TInt aValue)
- {
- ASSERT(aValue >= 0);
- // Do something with aValue
- // ...
- }
void TestValue(TInt aValue){ ASSERT(aValue >= 0); // Do something with aValue // ...}
- == 使用__UHEAP_MARK和__UHEAP_MARKEND宏檢測記憶體流失 ==
檢測你的代碼正確地管理堆記憶體(換言之,不泄漏記憶體)的一個可能性是使用__UHEAP_MARK和__UHEAP_MARKEND宏。
view plaincopy to clipboardprint?
- GLDEF_C TInt E32Main()
- {
- // Start checking memory leaks
- __UHEAP_MARK;
-
- // Create a fixed-length, flat array, which contains 10 integers
- CArrayFixFlat<TInt>* fixFlatArray;
- fixFlatArray = new(ELeave) CArrayFixFlat<TInt>(10);
- // Array is not deleted, so memory will leak
-
- // Stop checking memory leaks and cause a panic if there is a leak
- __UHEAP_MARKEND;
-
- return KErrNone;
- }
GLDEF_C TInt E32Main(){ // Start checking memory leaks __UHEAP_MARK; // Create a fixed-length, flat array, which contains 10 integers CArrayFixFlat<TInt>* fixFlatArray; fixFlatArray = new(ELeave) CArrayFixFlat<TInt>(10); // Array is not deleted, so memory will leak // Stop checking memory leaks and cause a panic if there is a leak __UHEAP_MARKEND; return KErrNone;}
由於資料未被刪除和記憶體流失檢測宏,上例代碼在應用程式關閉時將引起一個Panic,如所示:
值得一提的是堆檢測宏只編譯進debug版,因此可以安全地留在產品代碼中而不會影響代碼的大小或速度。
有兩個宏允許你檢查對象的狀態: __DECLARE_TEST 和 __TEST_INVARIANT。在實踐中,它們被用來使程式員先建立一個不變性測試函數,然後在需要檢測對象狀態的函數的開頭和結尾調用之,這是典型的做法。
view plaincopy to clipboardprint?
- class CLivingPerson : public CBase
- {
- public:
- enum TGender {EMale, EFemale};
- public:
- CLivingPerson(TGender aGender);
- ~CLivingPerson();
- public:
- void SetAge(const TInt aAge);
- private:
- TGender iGender;
- TInt iAgeInYears;
- __DECLARE_TEST; // Object invariance testing
- };
-
- CLivingPerson::CLivingPerson(TGender aGender) : iGender(aGender) {}
- CLivingPerson::~CLivingPerson() {}
-
- void CLivingPerson::SetAge(const TInt aAge)
- {
- // Set age and check object invariance
- __TEST_INVARIANT;
- iAgeInYears = aAge;
- __TEST_INVARIANT;
- }
-
- void CLivingPerson::__DbgTestInvariant() const
- {
- #ifdef _DEBUG // Built into debug code only
- // Person should be either male or female
- ASSERT((iGender == EMale) || (iGender == EFemale));
-
- // Person's age shouldn't be negative
- ASSERT(iAgeInYears >= 0);
- #endif
- }
class CLivingPerson : public CBase{public: enum TGender {EMale, EFemale};public: CLivingPerson(TGender aGender); ~CLivingPerson();public: void SetAge(const TInt aAge);private: TGender iGender; TInt iAgeInYears; __DECLARE_TEST; // Object invariance testing};CLivingPerson::CLivingPerson(TGender aGender) : iGender(aGender) {}CLivingPerson::~CLivingPerson() {}void CLivingPerson::SetAge(const TInt aAge){ // Set age and check object invariance __TEST_INVARIANT; iAgeInYears = aAge; __TEST_INVARIANT;}void CLivingPerson::__DbgTestInvariant() const{ #ifdef _DEBUG // Built into debug code only // Person should be either male or female ASSERT((iGender == EMale) || (iGender == EFemale)); // Person's age shouldn't be negative ASSERT(iAgeInYears >= 0); #endif}
由於上例使用ASSERT宏,若對象狀態不正確時就拋出"USER 0"Panic。
清除棧中的對象在不再有機會被Leave成孤兒時應被彈出。因此,彈出通常恰好發生在對象被刪除之前。一般使用PopAndDestroy函數代替Pop函數,因為它確保對象一彈出就被刪除,避免了記憶體流失的可能性。CleanupStack::Pop 和 CleanupStack::PopAndDestroy都有一個重載版本,允許調用者聲明“期望的彈出項”,指明該項應從棧中彈出。在期望的彈出項不匹配所彈出的項時,將拋出"E32USER-CBase 90" Painc。推薦使用這兩個重載版本,因為它們協助檢測清除棧的不正確使用。
view plaincopy to clipboardprint?
- CClass* obj = new(ELeave) CClass;
- CleanupStack::PushL(obj);
- // ...
- CleanupStack::PopAndDestroy(obj); // Panics if ‘obj’ not on top
CClass* obj = new(ELeave) CClass;CleanupStack::PushL(obj);// ...CleanupStack::PopAndDestroy(obj); // Panics if ‘obj’ not on top
''註: 在發行版編譯時間,期望彈出項的檢測將被禁用,因此使用它們在二進位大小和效率方面都不會影響發行版編譯。''
- Debugging techniques
- 調試技術
轉貼:http://wiki.forum.nokia.com/index.php/%E8%B0%83%E8%AF%95%E6%8A%80%E6%9C%AF