近來連續(xù)調(diào)試了好幾天的代碼,樂趣無窮,:),在純凈的人和機(jī)器對(duì)話的時(shí)間中,充分的和機(jī)器不斷的交流,最終共同實(shí)現(xiàn)功能,和同事說:“我喜愛調(diào)試代碼勝過了寫代碼”,怎么說呢,我覺得調(diào)試代碼能夠充分讓你將所掌握的知識(shí)發(fā)揮出來,考察自己解決問題的能力以及學(xué)習(xí)知識(shí)的能力,在這篇blog中來閑聊下調(diào)試代碼。
調(diào)試代碼是一種編碼所需的基本能力,相信沒有多少人寫出來的代碼能夠是沒有bug的,雖然傳聞是有過這樣的人,因此普通的我們只能通過調(diào)試代碼來查找和修復(fù)代碼中的問題,需要調(diào)試代碼的場(chǎng)景有很多種,在這些場(chǎng)景中,也會(huì)有很多種不同的調(diào)試技巧可采用。
最典型的需要調(diào)試代碼的場(chǎng)景是單元測(cè)試的場(chǎng)景,在單元測(cè)試時(shí)會(huì)碰到代碼的執(zhí)行不符合預(yù)期或拋出意料外的異常,在碰到拋出意料外的異常時(shí),通常現(xiàn)在的高級(jí)語言都會(huì)提示是由于哪行代碼造成的異常,于是首先的做法都是去看看對(duì)應(yīng)的那行代碼是什么個(gè)狀況,然后評(píng)估大概是什么原因造成的,如果在這種推測(cè)情況下無法判斷問題在哪了的話,在沒有支持程序調(diào)試時(shí),通常只能是在原始代碼中輸出一堆的信息到console,例如java中就是System.out.println或System.out.err,于是運(yùn)行,看看console中一堆的信息,然后慢慢的來推測(cè)問題,當(dāng)然,這也是一種可選的方案,甚至在某些場(chǎng)景中是一種不錯(cuò)的方案,但在各種IDE支持程序調(diào)試后,更多的時(shí)候調(diào)試都可以通過IDE來進(jìn)行,發(fā)明這個(gè)的人真的太偉大了,雖然我不知道是誰,但是還是想膜拜下的,:),有些看似很小的功能,往往非常的重要,于是現(xiàn)在的我們可以幸福的設(shè)個(gè)斷點(diǎn),然后開始逐行跟蹤、跳行跟蹤、跟蹤進(jìn)入函數(shù)內(nèi)部、跳出函數(shù)、跟蹤變量甚至修改變量等等N多種的方式幸福的調(diào)試著代碼,看著代碼在運(yùn)行時(shí)的狀態(tài),很容易的就讓我們發(fā)現(xiàn)代碼中的問題,這個(gè)絕對(duì)是節(jié)省了非常非常多的時(shí)間,所以我說我很佩服那些號(hào)稱用記事本寫代碼的高手們,難道他們的代碼都是零bug的?要么就是出了bug后也可以一眼判定問題所在的?那實(shí)在太強(qiáng)了點(diǎn),對(duì)于這樣的高手,確實(shí)可以不需要IDE這種現(xiàn)代化的武器,對(duì)于我而言,用記事本寫就像停留在原始時(shí)代,而IDE差不多應(yīng)該到帝王時(shí)代了,:),開玩笑,調(diào)侃下用記事本寫代碼的高手而言,這是單元測(cè)試中的場(chǎng)景和通常采用的技巧。
還有需要調(diào)試的場(chǎng)景通常會(huì)是集成測(cè)試場(chǎng)景,通常,集成測(cè)試會(huì)復(fù)雜很多,于是要用到的調(diào)試技巧會(huì)復(fù)雜一些,就像下面這樣的兩種場(chǎng)景:
1、并發(fā)程序
并發(fā)程序向來就是最最復(fù)雜的,沒有人能知道在運(yùn)行時(shí)到底是怎么個(gè)執(zhí)行順序,否則就不叫并發(fā)了,:),于是,在并發(fā)程序中,N多種人腦無法想象的復(fù)雜場(chǎng)景就出現(xiàn)了,畢竟人腦的思考應(yīng)該不支持并發(fā)的吧,至少我的貌似支持不了,也許是我笨,呵呵,而且通常并發(fā)程序中的錯(cuò)誤是不一定能每次都重現(xiàn)的,這是最麻煩的,至于借助IDE調(diào)試,同樣是不行的,因?yàn)椴l(fā)程序由于斷點(diǎn)的進(jìn)入可能完全被打亂,于是,對(duì)于并發(fā)程序,通常能采用的方法,比較靠譜的方法,我覺得還是打日志,當(dāng)然,你可以選擇繼續(xù)System.out.println、System.out.err,或者采用更加高級(jí)和優(yōu)雅點(diǎn)的log.debug這樣的方法,然后就看著時(shí)間戳來慢慢的運(yùn)用自己的大腦來思考復(fù)雜的并發(fā)的問題,:),這絕對(duì)是一種挑戰(zhàn),但因此也會(huì)帶來充分的樂趣,于是慢慢的享受這個(gè)過程吧。
多說一句,還好現(xiàn)在java有了更高精度的時(shí)間戳:System.nanoTime,用System.currentTimeInMills根本就沒法分析并發(fā)程序,因?yàn)樗木炔粔颉?#160;
2、所依賴的程序有問題
這種場(chǎng)景嘛,相對(duì)而言就復(fù)雜很多了,因?yàn)橥ǔ_@個(gè)時(shí)候能做的多數(shù)是通知所依賴的程序方去查找問題,但如果手頭有所依賴的程序的代碼的話,多數(shù)可以采取跟入其源碼的方式,盡管不一定能修復(fù)其源碼,但對(duì)于查找出問題還是會(huì)提供很大的幫助,例如跟蹤框架代碼、jdk代碼等等,對(duì)于訪問的遠(yuǎn)程程序而言,則不太相同,java嘛,還好,可以支持遠(yuǎn)程調(diào)試,我相信現(xiàn)在的大部分語言都支持的,遠(yuǎn)程調(diào)試那是相當(dāng)?shù)闹匾剑谑俏覀兙涂梢栽诒镜卣{(diào)試著遠(yuǎn)程某臺(tái)服務(wù)器上執(zhí)行時(shí)的bug,:),偷著樂吧。
最后一種最痛苦的大家最想調(diào)試的場(chǎng)景,就莫過于生產(chǎn)環(huán)境了,估計(jì)有N多人都想直接在生產(chǎn)環(huán)境中調(diào)試,看看生產(chǎn)環(huán)境中的問題是怎么產(chǎn)生的,但生產(chǎn)環(huán)境嘛,是不太可能拿來調(diào)試玩的,而有些時(shí)候線下要模擬也不是什么簡(jiǎn)單的事,說到這,又要天馬行空的瞎扯下了,記得在云風(fēng)的blog上以前有寫過一篇游戲中對(duì)于出錯(cuò)場(chǎng)景的記錄以及回放的功能,這功能聽著是相當(dāng)?shù)膸浹剑茫^續(xù)回到正題,在自己的代碼還沒有如此強(qiáng)大的錯(cuò)誤記錄和回放功能時(shí),也許能做的選擇就是在代碼中多寫一點(diǎn)log.debug了,在生產(chǎn)環(huán)境有問題時(shí),則打開相應(yīng)的日志的debug項(xiàng),然后繼續(xù)靠人肉分析了,這個(gè)時(shí)候還是體現(xiàn)了log.debug的強(qiáng)大作用滴,根據(jù)這點(diǎn)可以看出,在代碼中還是有必要寫些合適的log.debug的,除了為了自己外,對(duì)于其他使用的人調(diào)試bug也是可以給予很大的幫助的。
嗯,沒想到稍微扯了下,也寫了不少,從這稍微寫的內(nèi)容中,其實(shí)也能看出,調(diào)試可不是鬧著玩的事,絕對(duì)需要相當(dāng)完整的知識(shí)體系,這也難怪張銀奎的《軟件調(diào)試》寫了三年,而且那么的厚,看來還是值得看看的,:),小廣告,大家別在意。
而且這里還沒說到,通常需要調(diào)試的時(shí)候,多數(shù)都是出問題的時(shí)候,那么這個(gè)時(shí)候還會(huì)面臨很大的壓力,怎么樣在緊急的時(shí)間內(nèi)還冷靜的做好人肉分析的工作,這可是相當(dāng)?shù)目简?yàn)抗壓能力、技術(shù)基本功、邏輯分析能力和學(xué)習(xí)能力的過程,想想,要能在短時(shí)間內(nèi)查出問題的原因,通常需要對(duì)代碼運(yùn)行的場(chǎng)景做出冷靜的分析,進(jìn)而需要具備從頭到尾的知識(shí),例如所采用的框架、java包、甚至到JVM、操作系統(tǒng)、硬件,實(shí)在不行的話,還得臨時(shí)學(xué)習(xí)下這些知識(shí),這樣才能在短時(shí)間內(nèi)解決,因此想來想去,越來越覺得如果在面試的時(shí)候讓面試者現(xiàn)場(chǎng)調(diào)試段程序貌似還是挺靠譜的,更有助于全方位的考察,貌似只有很少數(shù)的幾家公司會(huì)這么干,當(dāng)然,這有客觀條件的原因,但要克服貌似也不是很難。