科學化除錯方法
科學化除錯方法
Steve McConnel 所著的軟體建構之道(Code Complete) 中, 第 23 章提到一個科學化的除錯方法,步驟如下:
- 可以穩定產生錯誤
- 找出錯誤的根因
a. 收集造成此錯誤的相關資訊
b. 分析找到的資料並給出一個對根因的假設
c. 證明或反證該假設 - 修正該錯誤
- 測試該修正
- 尋找其他同樣的錯誤
我就以這套步驟當作基本原則來說明怎麼在使用 ZK 時除錯。
1.可以穩定產生錯誤
這階段目標是找到能產生該錯誤的明確、固定、最少步驟。
如果你的步驟不明確、不固定、或步驟很多,這代表似乎每個元件都可能造成問題,那問題範圍就會牽涉太廣,難以找到根因。如果能夠除去一個步驟,仍然能夠重現該錯誤,那就代表該步驟與錯誤無關,可以移除。因此經由反覆測試來減少步驟,就能縮小問題範圍,可以讓你接下來比較容易找到根因。
2.找出錯誤的根因
a. 收集造成此錯誤的相關資訊
伺服器 log 中的(錯誤)訊息
很多人會忽略伺服器印出的 log 內容,但其實它常常明確的表示出問題的根因,如顯示 exception stack trace,我們就可以根據呼叫的 stack 去找出錯誤發生的程式來推測出錯誤根因。
瀏覽器上的錯誤訊息
主流的桌上瀏覽器按 F12 都可以開啟開發者工具,其中 Console 頁會顯示錯誤訊息,這通常也能幫助我們找到根因。 以下是 Chrome 中的開發者工具:
縮小問題範圍
為了找到錯誤的根因,縮小問題的範圍將會非常有幫助,因為問題牽涉的範圍越大,潛在造成問題的因素就越多,必須耗費非常多時間一一檢視這範圍裡的每個因素來找出根因,因此縮小問題範圍可以增進除錯的效率。你可用以下作法縮小問題:
- 移除(不相關的)元件。 通常 zul 上會有很多元件,但其實真正與錯誤有關的元件不多,因此將其他無關的元件從 zul 中移除可以讓你專注在真正的問題上。
- 減少重製步驟 如果原本要 3 個步驟能重現 bug,若是少做某一個步驟仍然能重現該 bug,就代表那一步跟該 bug 無關,可以省略。
- 移除(不相關的)程式碼 如果一個按鈕按下去,相對應的 event listener 有 500 行,那就得在這 500 行中找問題。如果能移除不相關的程式碼,減少成 100 行,就只需要在這 100 行中找出根因,就可以減少除錯時間。
b. 分析找到的資料並給出一個對根因的假設
在取得資料之後,就要針對這些資料提出一個假設。如果對 ZK 的內部運作越了解,就越能夠提出越正確的假設。
讓我們概略地看一次事件處理流程:
- 使用者點擊按鈕,觸發 DOM 事件
- ZK widget 發出事件通知 client engine
- client engine 將事件透過 AJAX 傳遞到伺服器
- 伺服器收到事件發給 ZK 元件
- 元件處理完事件,發給系統
- 系統呼叫該事件的傾聽器 (event listener)
- 事件的傾聽器執行所實作的應用程式邏輯,可能包含存取 ZK 元件、存取資料庫、或是再發出事件
- 當所有的事件都被處理完之後,元件被變更的部分都會被收集起來並轉換成對 ZK widget 的命令
- 將命令透過 HTTP response 傳回前端
- ZK client engine 收到 response 之後執行其中的命令,這些命令就會更改瀏覽器的畫面
以上步驟可以簡化為:
- 瀏覽器發出 ZK AJAX 請求 (或稱 asynchrounous update,簡稱 AU)
- 伺服器事件傾聽器執行
- 瀏覽器收到 AJAX 回應 (AU response)
每次當你跟元件互動但結果不如預期的時候,就可以依次檢查以上三個步驟有無正確執行:
- 是否發出預期的 ZK AU
- 傾聽器是否執行
- AU 回應內容是否如預期
c. 證明或反證該假設
當你提出一個對問題根因的假設之後,如果能夠證明假設成立,恭喜你,已經發現問題根源,可以開始思考怎麼解決。如果假設無法證明或可反證它,那就代表先前的假設錯誤,你要回到前一步,重新提出另一個假設。如此反覆到找到根因為止。以下提供幾個方法幫助你檢查 ZK 運作是否正常來驗證你的假設:
是否發出預期的 ZK AU
你可以透過 developer tool 來觀察,以 Chrome 為例,按 F12 打開,選 Network 頁,當你操作某個 ZK 元件而發出 event 時,會有一個路徑為 zkau 的請求發出如下:
確認 AU 的確有發出之後,就該檢查是否是你預期的事件發出,因著使用者與元件互動的行為,就應產生對應的事件,例如按了按鈕就會發出 onClick
、打開 popup 就發出 onOepn
。請看AU 請求與回應來了解怎麼檢查。
傾聽器(event listener)是否執行
如果使用者觸發的事件,在伺服器端有註冊對應的傾聽器 method,則 ZK 會呼叫該 method。要確認該 method 是否有被呼叫,可以印 log或透過 IDE 在 Java class 中設定中斷點。
AU 回應內容是否如預期
當 event listener 執行完後,其中所中呼叫的元件 API(主要是 setter)會產生相對的 AU 回應,這些回應內容包含對 client widget 的命令。因此不管是設定屬性、新增/刪除子元件,回應內容都會包含這些命令。你可以透過 developer tool 檢查 AU 回應的內容是否符合你事件傾聽器的實作。AU 回應的格式請參考 AU 請求與回應。
效能分析步驟
請參考 Step by Step Trouble Shooting ,雖然內容較多,但說明的非常完整。
簡單說效能瓶頸可分為三類:
- 瀏覽器端
- 網路
- 伺服器端
一個簡便的分析法是,以 developer tool 檢查對應事件的 AU request 的 timing,如果 waiting time 過長,瓶頸可判定為「伺服器端」,如果 waiting time 很短,但是畫面繪製仍然過久,可判定為「瀏覽器端」。不管使哪一端的瓶頸,都需要用 profiler 工具進一步測量時間,才能找出哪一個 method/function 執行時間超過預期。
MVVM 除錯日誌
MVVM 的運作有一大部分是在 framework 內部執行,因此從 ViewModel 中的程式,有時觀察不出根因,這時可設定 zk.xml 啟用除錯日誌 (org.zkoss.bind.DebuggerFactory.enable),該日誌將會印出 ZK 寫入、載入哪些屬性到元件上,可以藉此觀察是否符合預期。
Comments