導(dǎo)航菜單

十年開(kāi)發(fā)心得與調(diào)試手段:分享經(jīng)驗(yàn)助你寫(xiě)出更優(yōu)質(zhì)的內(nèi)容

導(dǎo)讀 畢業(yè)超過(guò)十年了,感慨歲月無(wú)情。作為從事后臺(tái)開(kāi)發(fā)多年的程序員(之前在電信領(lǐng)域工作),我想簡(jiǎn)要分享一些常見(jiàn)的開(kāi)發(fā)心得和調(diào)試手段。在互聯(lián)...

畢業(yè)超過(guò)十年了,感慨歲月無(wú)情。作為從事后臺(tái)開(kāi)發(fā)多年的程序員(之前在電信領(lǐng)域工作),我想簡(jiǎn)要分享一些常見(jiàn)的開(kāi)發(fā)心得和調(diào)試手段。在互聯(lián)網(wǎng)這么多年,我積累了很多經(jīng)驗(yàn),但總結(jié)得卻很少。出于互聯(lián)網(wǎng)精神,我希望這些經(jīng)驗(yàn)?zāi)軐?duì)你在互聯(lián)網(wǎng)的另一端有所幫助。由于我主要從事C語(yǔ)言開(kāi)發(fā),所以本文主要介紹C語(yǔ)言常用的調(diào)試手段。

調(diào)試對(duì)于無(wú)數(shù)程序員來(lái)說(shuō)都是一件麻煩的事情。幾乎沒(méi)有人能夠保證自己寫(xiě)的代碼沒(méi)有任何錯(cuò)誤,所以有問(wèn)題就需要進(jìn)行調(diào)試。那么如何進(jìn)行調(diào)試呢?對(duì)于高手來(lái)說(shuō),可以進(jìn)行反匯編,查看二進(jìn)制代碼;稍微低級(jí)一點(diǎn)的可以使用gdb調(diào)試工具,查看統(tǒng)計(jì)信息;再低級(jí)一點(diǎn)就是添加打印語(yǔ)句。還有更低級(jí)的方法嗎?當(dāng)然可以,自己寫(xiě)一個(gè)bug,讓其他人來(lái)查找。調(diào)試的方法有很多種,長(zhǎng)期掌握下來(lái)總能找到適合自己的方法。

而調(diào)試的目的是什么?就是要找到問(wèn)題所在。一個(gè)高手曾經(jīng)做過(guò)一個(gè)很好的比喻:你在找問(wèn)題實(shí)際上就像福爾摩斯一樣,為什么是福爾摩斯呢?想一想,在找問(wèn)題的現(xiàn)場(chǎng),合格的程序都有日志、內(nèi)存轉(zhuǎn)儲(chǔ)、計(jì)數(shù)等基本的現(xiàn)場(chǎng)調(diào)查記錄吧。嗯,如果什么都沒(méi)有,那就是讓寫(xiě)代碼的人自己去查找。找問(wèn)題就是在眾多信息中抽絲剝繭,找到疑點(diǎn),反復(fù)推演程序運(yùn)行的代碼,最終找到問(wèn)題所在的那一行或幾行代碼。

這個(gè)過(guò)程很折磨人,當(dāng)一無(wú)所獲的時(shí)候,讓人心煩意亂,食不知味。但是一旦找到問(wèn)題,就像打了雞血一樣興奮,自己也會(huì)陶醉其中。只有真正經(jīng)歷過(guò)折磨的人,才能體會(huì)到修改問(wèn)題的滋味。

一個(gè)程序的開(kāi)發(fā)大致需要經(jīng)歷以下兩個(gè)階段,才能最終上線發(fā)布。

功能開(kāi)發(fā)階段

在這個(gè)階段,主要目標(biāo)是根據(jù)業(yè)務(wù)需求進(jìn)行程序開(kāi)發(fā)。我們不僅僅是碼農(nóng),僅僅是寫(xiě)一些if-else語(yǔ)句嗎?寫(xiě)程序真的就是這么簡(jiǎn)單嗎?如果是這樣,那編程就會(huì)變得工廠化、枯燥無(wú)味。

做事情都應(yīng)該有預(yù)見(jiàn)性,尤其是在編寫(xiě)程序時(shí)更應(yīng)如此。經(jīng)典的大學(xué)C語(yǔ)言教材中將程序定義為:程序 = 數(shù)據(jù)結(jié)構(gòu) + 算法。但在實(shí)際生產(chǎn)過(guò)程中,我們可以對(duì)商業(yè)程序進(jìn)行以下補(bǔ)充定義,我認(rèn)為更加合適:程序 = 數(shù)據(jù)結(jié)構(gòu) + 算法 + 業(yè)務(wù)邏輯(計(jì)算邏輯)+ 框架。

首先來(lái)說(shuō)為什么需要補(bǔ)充業(yè)務(wù)邏輯,因?yàn)橛幸饬x的程序本身就是某種業(yè)務(wù)邏輯(計(jì)算邏輯)的抽象。完成這個(gè)業(yè)務(wù)邏輯才是最終的目的,請(qǐng)不要拿一些算法研究的代碼和我爭(zhēng)論。

實(shí)際上,作為開(kāi)發(fā)人員,測(cè)試驅(qū)動(dòng)開(kāi)發(fā)(TDD)是一種很好的思考問(wèn)題的方法。你可能聽(tīng)說(shuō)過(guò)它,也可能有同事使用過(guò),如果你覺(jué)得使用不好,我可以告訴你:應(yīng)該是測(cè)試場(chǎng)景 + 場(chǎng)景驅(qū)動(dòng)開(kāi)發(fā)。對(duì),只是在其中加入了"場(chǎng)景"這個(gè)賓語(yǔ),當(dāng)你進(jìn)行開(kāi)發(fā)時(shí),就有了目的性和針對(duì)性。

任何一個(gè)業(yè)務(wù)邏輯都可以拆分為多個(gè)業(yè)務(wù)場(chǎng)景。逐個(gè)解決這些場(chǎng)景,逐個(gè)進(jìn)行測(cè)試,開(kāi)發(fā)其實(shí)就很簡(jiǎn)單了。聽(tīng)起來(lái)很簡(jiǎn)單,但整個(gè)過(guò)程中,需要將50%的時(shí)間用于思考解決問(wèn)題的場(chǎng)景,20%的時(shí)間用于編碼,30%的時(shí)間用于測(cè)試。實(shí)際上,你可以在任何時(shí)間進(jìn)行思考(在休息時(shí),地鐵上,班車(chē)上等等),只要能保持足夠的寧?kù)o,你就能清晰地思考整個(gè)業(yè)務(wù)邏輯,并明確分解為多少個(gè)業(yè)務(wù)場(chǎng)景。對(duì)于復(fù)雜的業(yè)務(wù)場(chǎng)景,建議適當(dāng)?shù)刈龉P記,從整體的業(yè)務(wù)邏輯考慮問(wèn)題:你細(xì)化的結(jié)論是否符合所有的業(yè)務(wù)場(chǎng)景?不斷修正,直到正確為止。

在具體編碼時(shí),根據(jù)我們之前的深思熟慮,每個(gè)細(xì)節(jié)已經(jīng)很清楚了,采用迭代的方式,批量交付小的功能點(diǎn)就可以了。

對(duì)于功能開(kāi)發(fā)階段的總結(jié)可以用兩個(gè)關(guān)鍵詞來(lái)概括:TDD和迭代。需要更詳細(xì)了解的同學(xué)可以自行使用百度或谷歌進(jìn)行搜索。

功能調(diào)試階段

調(diào)試的手段很多,可以閱讀代碼、打印日志、使用gdb、查看統(tǒng)計(jì)信息、使用coredump等,如果你有足夠的精力,還可以進(jìn)行白盒測(cè)試等。測(cè)試的目的很明確,就是確認(rèn)代碼是否按照正確的編碼意圖運(yùn)行。對(duì)于自己編寫(xiě)的代碼,自己調(diào)試起來(lái)是相對(duì)容易的,因?yàn)槟闱宄a的本意以及出現(xiàn)了什么問(wèn)題。

作為程序員的悲劇之一,就是不知道何時(shí)需要定位其他人編寫(xiě)的bug。在進(jìn)行定位之前,你必須理解另外一個(gè)程序員編寫(xiě)這段代碼的意圖,否則就無(wú)法進(jìn)行定位。了解其他人編寫(xiě)的代碼的途徑就是通過(guò)閱讀代碼了解大致思路,并通過(guò)日志、gdb或統(tǒng)計(jì)信息來(lái)補(bǔ)充對(duì)代碼意圖的更多細(xì)節(jié),或者修正自己對(duì)代碼的理解。

這個(gè)過(guò)程可能很枯燥,也可能很具有挑戰(zhàn)性。試圖通過(guò)各種跡象了解另外一個(gè)程序員編寫(xiě)代碼的初衷和意圖,有時(shí)候可能讓人感覺(jué)像窺探別人的隱私一樣!

實(shí)際上,我之前提到的只是調(diào)試的前提和初衷。

一個(gè)優(yōu)秀的程序員會(huì)有很多調(diào)試技巧,也就是很多調(diào)試手段,用來(lái)獲取所需的信息。信息獲取越多,自然就越容易理解程序本身的意圖。

關(guān)于調(diào)試工具的使用細(xì)節(jié)和說(shuō)明,你可以自行使用百度或谷歌進(jìn)行搜索。

我在這里簡(jiǎn)單地闡述一下我是如何調(diào)試程序的,以及我是如何理解各種工具的。歡迎大家指點(diǎn)和交流!

關(guān)于日志

如何打好日志絕對(duì)是一門(mén)學(xué)問(wèn)。日志打印過(guò)多會(huì)影響后臺(tái)程序的性能,而打印過(guò)少則無(wú)法定位問(wèn)題。更糟糕的是,如果打印到空指針,有可能導(dǎo)致自己的程序崩潰。

因此,日志的技巧就是要少而且內(nèi)容豐富。

如何做到少?其實(shí)就是要合并表達(dá)相同意思的打印語(yǔ)句。

能否減少打印語(yǔ)句,但仍能得到相同的表達(dá)結(jié)果?

能否在關(guān)鍵的異常位置添加統(tǒng)計(jì)信息(輸出統(tǒng)計(jì))?

能否不進(jìn)行打???

能否在內(nèi)存中記錄關(guān)鍵信息,在需要時(shí)控制打印時(shí)機(jī)?

而如何做到內(nèi)容豐富?就是減少描述性詞匯的打印,增加有用的程序運(yùn)行信息。

方法有很多,大家可以多多思考。打印的優(yōu)化是一個(gè)不斷改進(jìn)的過(guò)程,并非一蹴而就。我曾經(jīng)遇到過(guò)一個(gè)高手,測(cè)試部門(mén)提出問(wèn)題,這個(gè)高手從不去定位,他直接告訴測(cè)試人員:幫我執(zhí)行一下軟調(diào),將收集的日志交給他分析,問(wèn)題就能解決。

關(guān)于gdb

還有一個(gè)大牛說(shuō)過(guò):“我就是程序,程序就是我。”我經(jīng)常使用gdb來(lái)檢驗(yàn)我對(duì)程序的理解。常用的gdb功能是打印程序的運(yùn)行信息、修改內(nèi)部運(yùn)行信息以及構(gòu)造復(fù)雜的場(chǎng)景。

實(shí)際上很簡(jiǎn)單,只需要知道程序在什么場(chǎng)景下應(yīng)該有什么行為,我對(duì)程序的理解必須清晰。我需要知道關(guān)鍵變量的信息是否正確,周期性地使用gdb來(lái)確認(rèn)變量的信息是否正確,然后決定程序是否按預(yù)期執(zhí)行。

可靠的程序都有類(lèi)似的保護(hù)機(jī)制,但通常需要繁瑣的測(cè)試條件構(gòu)造來(lái)觸發(fā)保護(hù)機(jī)制(例如檢測(cè)到丟包率很高時(shí)進(jìn)行警告等)。實(shí)際上,大多數(shù)的保護(hù)機(jī)制都是在記錄一些狀態(tài)后觸發(fā)的。

實(shí)際上,可以使用gdb構(gòu)造異常狀態(tài),確認(rèn)警告機(jī)制是否起作用。gdb非常好地補(bǔ)充了這方面的測(cè)試和驗(yàn)證工作。

關(guān)于統(tǒng)計(jì)

統(tǒng)計(jì)信息是匯集關(guān)鍵信息的最佳例子。數(shù)據(jù)量少,但信息明了。

在電信軟件中,許多模塊都通過(guò)統(tǒng)計(jì)信息來(lái)進(jìn)行自證清白。通過(guò)統(tǒng)計(jì)信息很容易發(fā)現(xiàn)問(wèn)題出現(xiàn)的位置。

統(tǒng)計(jì)的實(shí)質(zhì)就是通過(guò)全局變量記錄程序正常和異常點(diǎn)的統(tǒng)計(jì)信息,然后通過(guò)某種手段進(jìn)行輸出。

關(guān)于coredump

面對(duì)coredump很多人都會(huì)感到頭痛,但實(shí)際上coredump也是定位問(wèn)題的很好手段。

首先,程序在產(chǎn)生coredump之前會(huì)有詳細(xì)的coredump文件,該文件詳細(xì)記錄了程序在coredump之前的運(yùn)行信息。使用gdb可以查看coredump文件中的任何信息。這只是簡(jiǎn)單使用coredump的方法。

如果遇到復(fù)雜的問(wèn)題,難以解決的問(wèn)題,實(shí)際上也可以使用coredump進(jìn)行定位。

比如程序執(zhí)行到一個(gè)非常罕見(jiàn)的代碼分支,然后出現(xiàn)coredump,但目前輸出的信息(如日志等)根本無(wú)法進(jìn)一步定位問(wèn)題。

怎么辦?有沒(méi)有考慮過(guò)在復(fù)現(xiàn)問(wèn)題的環(huán)節(jié),提供一個(gè)調(diào)試版本的程序,在異常分支上主動(dòng)觸發(fā)內(nèi)存異常,產(chǎn)生coredump,然后利用coredump信息來(lái)確定程序出現(xiàn)異常的方式。

關(guān)于代碼修改

這也是我經(jīng)常使用的一種手段,反復(fù)比較修改前后的代碼,確認(rèn)修改的準(zhǔn)確性和全面性,反思自己的代碼修改是否全面?這里使用的工具是Beyond Compare。

免責(zé)聲明:本文由用戶(hù)上傳,如有侵權(quán)請(qǐng)聯(lián)系刪除!

猜你喜歡:

最新文章: