Showing posts with label debug. Show all posts
Showing posts with label debug. Show all posts

Sunday, March 26, 2006

Debugging Embedded Systems

在 Embedded System 除錯,和在 PC Software 除錯,概念上是相通的,那就是要運用科學的方法,其步驟如下:

  1. 仔細觀察:從可重複的(repeatable)實驗中蒐集資料。
  2. 提出假設:根據蒐集到的資料提出假設、架構理論模型。
  3. 設計實驗:設計實驗以證明或推翻假設。
  4. 進行實驗。
  5. 重複以上過程:如果實驗結果不能支持假設,那假設便是錯的,所以要再重複整個過程。

這個「觀察 -> 假設 -> 實驗 ->……觀察 -> 假設 -> 實驗……」的過程,運用到除錯上就是:

  1. 想辦法使 bug 發生的情況穩定下來,讓 bug 是可重現的(reproducible),把揭發這個 bug 所需要最簡單的情境找出來。
  2. 判斷 bug 的根源。
  3. 根據對 bug 的判斷,修正程式碼。
  4. 測試修正後的程式。
  5. 如果 bug 還在,不是誤判 bug 的成因了,就是還有其他 bug 沒剔除,所以要重複上述過程。

至此,情況已經很明顯了,為了 debug ,我們要讓 Embedded System 很容易被測試,很容易對問題蒐集必要的資料:

在 Programming 方面,要注意的東西跟一般的軟體開發並無不同。在開發階段,就要仔細設計,要有一個方便我們日後測試及除錯的程式架構,要讓程式本身是 testable 及 tunable 。相關的議題可以參考 Extreme Programming 的方法。

此外,由於 Embedded System 跟 hardware 間種種本質性的糾葛,我們還要借助一些下列的工具。

[高階工具]

在閱讀這一章節前,也可以參考 Renesas 的 H8/300L SLP Series Application Note〈Effective Usage of Hardware Development Tool〉。

Simulators

Simulator 跟 Virtual Machine 很像,可以讓我們在 host machine (通常是 PC )上執行 microcontroller, uC 程式。一般 PC 軟體開發環境所用的單步執行及中斷點也都可以在 simulator下用,其最大的缺點是模擬得還不夠多,大多只提供 CPU 的模擬,未包括週邊的模擬。

在 target hardware 還沒 ready ,就必須開始撰寫程式的場合, simulator 可以讓我們對 target uC 作前期的試驗。

此外,如果發現「target processor 的行為與 data book 描述的不同」,也可以讓程式在 simulator 上執行看看。如果在 simulator 上執行無誤,就表示硬體出了問題。

Resident Debuggers

Resident Debugger 又稱作 Debug Monitor 或 ROM monitor ,它在 uC上執行程式時,可以把執行過程秀到 host machine (通常是 PC)上。 跟 simulator 最大的不同是 resident debugger 讓程式在真正的 target machine 上跑。缺點是需要「偷」用 target machine 的資源:包括耗掉一個 communication port 來和 host 溝通;佔用一個 interrupt 來處理單步執行;還要用掉一小塊的記憶體來放 resident debugger 自己。

Resident Debugger 提供程式碼下載、執行控制、中斷點、單步執行、修改 register 和 memory 等。因為 resident deubgger 是 target code 的一部分,只有當你的應用程式執行時,它才會工作。如果你想檢查 CPU 和應用程序的狀態,你就必須停下應用程序,再次進入 resident debugger。

Emulators

Emulator 是連接到 target hardware ,且可以跟 host 通訊的 Embedded System 。和 resident debugger 比起來, emulator 最大的優點是不必佔用 target machine 的資源。

ROM Emulator

用來模仿 target board 上的 ROM 。不但具有 resident debugger 的所有功能,還具備不佔用 target machine 資源的優點。

In-Circuit Emulator, ICE

Resident Debugger 雖然是監看軟體狀態的得力助手,但只有 ICE 可以讓我們在程式執行時檢視處理器的狀態。ICE 有自己的 RAM, ROM, processor 及軟體,本身就是一套嵌入式系統,功能強大,但價錢也頗為昂貴。在高階除錯的場合,沒有其他工具可以與 ICE 匹敵。

resident debugger 只可以設定程式除錯的軟體 break point , ICE 可以讓我們根據多種 hardware event 而設定硬體的 break point 。

On-Chip Emulator, OnCE

On-Chip Emulator, OnCE 又稱作 On-Chip Debugger, OCD 。Background Debug Monitor, BDM 和 Joint Test Action Group, JTAG 是 OnCE 在 target chip 上提供的兩種最常見的介面。在 ICE 太過昂貴下, OnCE 是個不錯的選擇。

[低階工具]

三用電表(Multimeter)

量測電源電壓是否符合標準
判斷某兩點是否相通
測量電阻值

示波器(Oscilloscope)

判斷訊號或電源電壓準位
判斷某一輸出訊號是否與其他輸出訊號短路
量測週期性訊號的波形(通常是類比訊號)
若有 Storage 功能,則可以抓取非週期性訊號(數位訊號大多是非週期的)
若有雙軌跡,還可以比較訊號變化的延遲時間

邏輯筆(Logic Probe)

可以用來判斷 TTL 或 CMOS 電路某個接點是 logic 1, logic 0, 非 1 非 0 或 time pulse 等狀態。

邏輯分析儀(Logic Analyzer)

我們可以把邏輯分析儀想像成具有數位儲存功能的示波器;且有 24, 32 甚至 48, 64 個輸入通道;針對要看的信號,還能設定觸發條件(上升緣、下降緣、低準位、高準位,甚至脈衝的個數等)。

[其他技巧]

如果 uC 能多告訴我們一些事情,那對 debug 是很有幫助的。以下就列出一些有用的途徑讓 uC 開口說話:

Serial Port

當 bug 發生時,如果能在幾個可疑的地方 print out 一些東西,把程式執行的情形 log 起來,我們就可以利用這份 log 來歸納很多事情。

可能的話,請把 uC 的 UART 和 PC 的 serial port 第一時間 link 起來。我們甚至該讓 uC 跟 host 透過 serial port 作雙向的互動,這可以讓我們透過 command 來執行 uC 上特定的程式片段,縮小測試範圍。

GPIO

我們也可以讓 uC 透過 I/O port 向我們招手。常用的技巧是讓 I/O port 在各個情況下丟出不同代碼,再搭配 Logic Analyzer ,作為特定的 trigger 點。

LED

在適當的時機把 LED 點起來,也可以慢慢告訴我們一些事。

LCD

讓 uC 把要說的話,寫到 LCD 上也是不錯的選擇。

Memory

在利用 serial port 作 log 行不通的時候,把東西 log 到 EEPROM 或 battery-backed SRAM 等 memory 中都是可行的作法,如果事後可以解讀出來的話……

Tags: [] []

Friday, September 07, 2001

Re: 請問在何種狀況下會考慮使用exception?

  就大部分的軟體系統而言,想在程式還沒完成時就知道效率的瓶頸在哪?無異是緣木求魚!

  在應該使用 Exception 時就使用,在程式還沒正確之前,效率再高都是枉然。

  在設計之初,最重要的是程式的高階效率,所謂高階效率,就是要求撰寫易於找出錯誤、易於日後陸續修改、具備高度重複使用性的程式碼。

  程式的架構要清爽、明確且具彈性。才可在未來應付以上這些變局。

  以設計一個函式庫為例,函式庫的設計者知道如何偵測例外,卻無法預知函式庫的使用者要如何處理這個例外,這時就是 Exception 使用的時機了。

  換言之,當你設計的軟體系統可以區分為設計者及使用者時,也就是你在設計給未知使用者使用的軟體時,你就可以在偵測到例外時,將之丟出。而且盡量不要使用傳統上應付這種情形的其他任何方法,如:傳回錯誤代碼等。

  在 C++ 中,你還可以利用 assert 敘述,在這裡建議 assert 敘述使用於內部供軟體設計者偵錯用;而 exception 用於公開的介面部分,供軟體元件使用者例外處理用。──當然啦!這也不是絕對的標準,端看您對自己設計的元件作何假設而定。


※ 引述《AskaLee2 (Aska)》之銘言:

請問各位先進,在何種狀況下使用exception機制最適當??
使用exception必須付出一些效能上與空間上的代價,
應該有某些狀況非常適合使用exception。
Tags: [] [] [] []

Tuesday, July 10, 2001

Re: 有沒有好的機制來判斷物件是否已被 delete

  如果整個程式都是由我一個人開發的話(包括使用的 Library),通常是不必動用到這類的工具。

  因為,預防勝於治療嘛!──良好的介面設計,可以杜絕很多記憶體方面的臭蟲。當然啦!再加上正確的運用觀念,那就更萬無一失了。

  如果你發現程式中不使用類似的工具,就很難避免記憶體方面的臭蟲的話。我建議在條件許可的情況下,可以考慮將相關的介面重新設計。

  關於這方面的議題,可以參考這個link:
http://www.research.att.com/~bs/bs_faq2.html#memory-leaks

  當然啦!世界不總是完美的,否則也不會有類似的工具問世:
http://www.andreasen.org/LeakTracer/
ftp://ftp.crpht.lu/pub/sources/memdebug/
http://www.perens.com/FreeSoftware
http://reality.sgi.com/boehm_mti/gc.html

  如果你的開發環境是 VC 的話,強烈建議使用 NuMega 的 BoundsChecker:
http://www.numega.com/devcenter/bc.shtml

Tags: [] [] [] []

Thursday, June 28, 2001

Assertion

Assertion 的使用目的,就是要防止客戶對程式庫的誤用。以 Design by Contract 的原則再加上 OOP 的術語,簡單說來:就是要確保物件在執行操作或行為後還要維持其內部狀態的正確。

假設描述物件這個行為的程式本身正確無誤,而作業系統也沒來找碴,那使物件狀態脫軌的必定來自物件使用者傳入錯誤的引數。

就如同程式行為發生“例外”一樣,程式庫的使用者知道如何偵測錯誤的物件狀態,但卻不知如何處理...

傳統 C 語言的 assert 做法,就是一偵測到要確認的條件不成立了,就立刻終止整個程式的執行,並列印一些偵錯訊息出來。

但是,在這個愈來愈趨向動態的程式世界。有時連客戶都無法確保傳入的引數是正確的,原因可能不是客戶不夠小心,而是在動態的世界裡,這是無法避免的。

這時,在 C++ 中可行的個解法,就是丟出“例外”。被丟出的例外可以被客戶捕捉,並作一些處理措施。萬一客戶不處理,才將程式終結掉。

我這裡使用的,就是裹著 Assertion 的例外,並利用 template 來讓客戶決定要丟出的例外種類:

From: "Jiang Yu-Kwan"
Sent: Monday, June 18, 2001 4:40 PM
Subject: Re: Fw: JavaOne特別報導

我再看了一下,它支援的是 generic programming,而 template 只是在C++下的實作方式。

以 JAVA 設計的哲學,它可能把GP搞得跟 C++ 的 template 語法很像,但骨子裡卻不必preprocessor處理,而是直接在 run time support。

而 assertion 的支援,只是為了支援 Design by Contract ,其支援的完整性,可能更勝 C。

因為 C++ 對 assertion 沒有更進一步的支援(直接沿用C的做法)。所以, JAVA 在這點有很大的改進空間。

以下我就舉例說明一下,如何製造一個 C++ 版本的 assertion 功能:

 1: // assertion.h
 2: //...
 3: 
 4: template <class Exception> void Assert( bool assertion )

 5: {
 6:     if (! assertion) throw Exception();

 7: }
 8: //...
 9: 

經修改後,我現在實際使用的 assertion 版本如下:

1: // Assertion for C++ version.
2: template <class Exception> inline void Assert( bool assertion )

3: {
4: #ifndef NO_CPP_ASSERTION
5:     if (! assertion) throw Exception();

6: #endif
7: }
 1: // user.c
 2: #include "assertion.h"
 3: //...
 4: class Date {

 5: public:
 6:     class Bad_arg{};
 7:     setMonth( int mon );

 8:     //...
 9: private:
10:     //...
11: };
12: 

13: void Date::setMonth( int mon )
14: {

15:     // 確保設定的月數在正確的範圍,否則,
16:     // 就丟出Bad_arg的例外。
17:     Assert<Bad_arg>(1<=mon && mon<=12);

18:     //...
19: }
20: //...
21: 

更進一步的探討可以參考 C++ 之父, Bjarne Stroustrup 所著的 《The C++ Programming Language 3rd Edition》。