Linux后臺(tái)開(kāi)發(fā)調(diào)試經(jīng)驗(yàn)分享

畢業(yè)超過(guò)十年了,感慨歲月無(wú)情。做了若干年后臺(tái)開(kāi)發(fā)(之前做電信領(lǐng)域),大致說(shuō)一下常見(jiàn)的開(kāi)發(fā)心得和調(diào)試手段。使用互聯(lián)網(wǎng)這么多年,收獲的很多,總結(jié)的很少。本著互聯(lián)網(wǎng)精神,希望可以幫到互聯(lián)網(wǎng)另一端的你。由于本人是做 C 語(yǔ)言的開(kāi)發(fā),陳述的經(jīng)驗(yàn)也是 C 常用的調(diào)試手段。
調(diào)試很麻煩,困擾著無(wú)數(shù)程序員們。很難有人保證自己寫(xiě)的代碼一行錯(cuò)誤都沒(méi)有,有問(wèn)題你就要查。怎么查?高手者,反匯編,看 2 進(jìn)制;low 一點(diǎn)的就 gdb、看統(tǒng)計(jì);再 low 就加打印。還可以再 low 嗎?可以,自己寫(xiě) bug,別人查。方法林林總總,長(zhǎng)期掌握總可以找到適合自己的。
而調(diào)試的目的是什么,找到 BUG。想當(dāng)年一個(gè)高手比喻的好:你找 BUG 其實(shí)你就是福爾摩斯,為啥是福爾莫斯呢?想想你看到 BUG 案發(fā)現(xiàn)場(chǎng)--合格的程序都有日志、dump 內(nèi)存、計(jì)數(shù)等基本案發(fā)現(xiàn)場(chǎng)吧。嗯,什么都沒(méi)有,找寫(xiě)代碼的人自己查。找問(wèn)題就是在眾多信息中,抽絲剝繭,找到疑點(diǎn)、反復(fù)推演程序運(yùn)行的代碼,最終找到作案的那一行或者幾行代碼。
這個(gè)過(guò)程很折磨人,沒(méi)有任何眉目時(shí),令人茶不思飯不想。找到問(wèn)題問(wèn)題后,如打雞血般興奮,自己也會(huì)陶醉般飄飄然。真正受過(guò)折磨的人,才能體會(huì)到修改問(wèn)題的滋味一二。
開(kāi)發(fā)的程序大致要經(jīng)過(guò)一下兩個(gè)階段,最終才可以上線發(fā)布。
功能開(kāi)發(fā)階段
本階段的主要目標(biāo)是根據(jù)業(yè)務(wù)要求,開(kāi)發(fā)程序。僅僅是 coder,僅僅是寫(xiě) if else 嗎?寫(xiě)程序真的是這樣嗎?如果是這樣,那么 coder 會(huì)更加工廠話(huà),枯燥化。
做事都講究未雨綢繆,做程序更應(yīng)該這樣。大學(xué) C 語(yǔ)言經(jīng)典教材中定義程序?yàn)椋撼绦?= 數(shù)據(jù)結(jié)構(gòu) + 算法。而實(shí)際生產(chǎn)的過(guò)程中,將商業(yè)程序做如下的補(bǔ)充定義,我覺(jué)得更合適:程序 = 數(shù)據(jù)結(jié)構(gòu) + 算法 + 業(yè)務(wù)邏輯(計(jì)算邏輯)+ 框架;
先說(shuō)說(shuō)為什么補(bǔ)充業(yè)務(wù)邏輯,有意義的程序本身就是某種業(yè)務(wù)邏輯(計(jì)算邏輯)的抽象。完成這個(gè)業(yè)務(wù)邏輯才是最終的目的,請(qǐng)不要拿一些算法研究的 code 和我抬杠。
其實(shí)作為開(kāi)發(fā)人員,測(cè)試驅(qū)動(dòng)開(kāi)發(fā)(TDD)很好思考問(wèn)題的思路。也許有人聽(tīng)過(guò),也許有同學(xué)用過(guò),如果感覺(jué)使用不好的兄弟,我可以告訴大家:應(yīng)該是測(cè)試場(chǎng)景 + 場(chǎng)景驅(qū)動(dòng)開(kāi)發(fā)。對(duì),僅僅是里面融入“場(chǎng)景”這個(gè)賓語(yǔ),大家在做開(kāi)發(fā)的時(shí)候,就有目的性和針對(duì)性。
任何一個(gè)業(yè)務(wù)邏輯,都可以拆分為多個(gè)業(yè)務(wù)場(chǎng)景。場(chǎng)景逐一解決,場(chǎng)景逐一測(cè)試,我們開(kāi)發(fā)其實(shí)很簡(jiǎn)單。說(shuō)的很簡(jiǎn)單,但是整個(gè)過(guò)程,需要 50%的時(shí)間思考解決問(wèn)題場(chǎng)景, 20%的編碼,30%的測(cè)試。其實(shí)思考問(wèn)題的 50%的時(shí)間,可以放在任何時(shí)間去做(休息時(shí),地鐵上,班車(chē)上...),只要讓自己足夠的靜,你就會(huì)將整個(gè)業(yè)務(wù)邏輯思考的很清楚,分解為多少個(gè)業(yè)務(wù)場(chǎng)景也很明確。對(duì)于復(fù)雜的業(yè)務(wù)場(chǎng)景,建議適當(dāng)?shù)淖龉P記,多從全局的業(yè)務(wù)邏輯考慮:自己細(xì)化的得到結(jié)論是否符合所有的業(yè)務(wù)場(chǎng)景。反復(fù)的修正,直到正確。
具體編碼的時(shí)候,經(jīng)過(guò)我們前面的深思熟慮,每個(gè)細(xì)節(jié)都已經(jīng)很清楚了,采用迭代的方式,批量的交付小的功能點(diǎn)就可以了。
開(kāi)發(fā)階段的總結(jié)兩個(gè)關(guān)鍵字:TDD + 迭代。需要詳情的同學(xué)自行 baidu,google。

功能調(diào)試階段
調(diào)試的手段很多,走讀代碼,打日志,gdb,統(tǒng)計(jì),coredump 等,如果有精力也可以搞搞的白盒測(cè)試什么的。測(cè)試的意圖也很明顯,確認(rèn)代碼是否按照正確的編碼意圖在運(yùn)行!其實(shí)自己寫(xiě)的代碼,自己還是可以輕松駕馭調(diào)試的,原因就是自己清楚代碼的本意該如何運(yùn)行,現(xiàn)在出現(xiàn)了什么問(wèn)題。
程序員的三大悲劇之一,就是不知道什么時(shí)候需要定位一個(gè)其他人寫(xiě)的 bug。定位前也需要必須要理解另外一位程序員寫(xiě)這段代碼的意圖是什么,否則沒(méi)有辦法定位。理解其他的人寫(xiě)的代碼途徑也就是通過(guò)閱讀代碼了解大致思路,通過(guò)日志、gdb、或者統(tǒng)計(jì)信息補(bǔ)充代碼意圖的更多細(xì)節(jié),或者修正理解不對(duì)的思路。
這個(gè)過(guò)程可能很枯燥,也可能很有挑戰(zhàn),試圖通過(guò)種種跡象去了解另外一個(gè)程序員寫(xiě)代碼的初衷和意圖,會(huì)不會(huì)有窺探人家隱私的趕腳!
其實(shí),上面說(shuō)了這么多只是告訴大家調(diào)試好的前提,和調(diào)試的初衷。
一個(gè)優(yōu)秀的程序員,你會(huì)發(fā)現(xiàn)他有很多調(diào)試技巧,也就是很多調(diào)試手段獲取自己想得到信息。信息獲取的多,自然就很容易清除程序本身的意圖。
調(diào)試工具的使用細(xì)節(jié)和說(shuō)明,同學(xué)們可以自行 baidu,google。
我在這里簡(jiǎn)單的闡述一下自己是怎么調(diào)試程序的,怎么理解各種工具的,歡迎大蝦門(mén)指點(diǎn)交流?
1) 關(guān)于日志
如何打好日志絕對(duì)是門(mén)學(xué)問(wèn)。日志打印多了,自然會(huì)影響后臺(tái)程序的性能;同樣打印的少了,沒(méi)有辦法定位問(wèn)題;更苦逼的是打印到空指針,更有可能 coredump 掉自己的程序;
所以日志的技巧就是:少,且內(nèi)容豐富。
如何少,其實(shí)就是匯聚。
能不能將表達(dá)同一個(gè)意思的打印減少?
能不能在關(guān)鍵異常的地方加上統(tǒng)計(jì)(輸出統(tǒng)計(jì))?
能不能不打?
能不能內(nèi)存中記錄關(guān)鍵信息,在想要的時(shí)候,控制其打印時(shí)機(jī)?
如何豐富,其實(shí)就是少打描述性詞匯,多打有用的程序運(yùn)行信息。
方法很多,大家多多思考。并且打印的優(yōu)化,是反復(fù)優(yōu)化的過(guò)程,不是一蹴而就的。曾經(jīng)遇見(jiàn)一個(gè)大牛,測(cè)試部提問(wèn)題了,這哥們從來(lái)不去定位。直接告訴測(cè)試的兄弟,幫忙執(zhí)行以下軟調(diào),將收集的日志給他分析一下就可以解決問(wèn)題。
2) 關(guān)于 gdb
還有大牛說(shuō)過(guò):“我就是程序,程序就是我”。我常用 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)該有什么樣的行為,我自己的必須清楚。必須知道關(guān)鍵變量的信息是否正確,周期 gdb 出來(lái),確認(rèn)變量的信息是否正確,然后決定程序是否符合預(yù)期在執(zhí)行。
可靠性的程序都有類(lèi)似的保護(hù)機(jī)制,但是通常需要繁瑣的構(gòu)造測(cè)試條件,觸發(fā)保護(hù)機(jī)制(如檢測(cè)到丟包率很高,要告警等)。其實(shí)大多數(shù)的保護(hù)機(jī)制都是通過(guò)記錄一些狀態(tài)后,程序然后觸發(fā)的保護(hù)機(jī)制。
其實(shí),可以 gdb 構(gòu)造出異常狀態(tài),確認(rèn)告警機(jī)制是否生效。gdb 很好的補(bǔ)充這方面的測(cè)試和驗(yàn)證工作。
3) 關(guān)于統(tǒng)計(jì)
統(tǒng)計(jì)信息,是關(guān)鍵信息匯集的最好的例子。數(shù)據(jù)少,切信息明了。
電信軟件中,很多模塊都通過(guò)這樣的信息:自證清白。也很容易發(fā)現(xiàn)問(wèn)題出現(xiàn)哪里。
統(tǒng)計(jì)的實(shí)質(zhì),就是通過(guò)全局變量,記錄一些程序正常,異常點(diǎn)的統(tǒng)計(jì)信息。然后通過(guò)某種手段輸出出來(lái)。
4) 關(guān)于 coredump
大家看到 coredump 都會(huì)頭痛,其實(shí) coredump 也是很好的定位手段。
首先程序 coredump 后,會(huì)有詳細(xì)的 coredump 文件,該文件詳細(xì)的記錄了程序在 core 之前的運(yùn)行信息。gdb 這個(gè) coredump 文件,你想看什么都可以。這只是簡(jiǎn)單的使用 coredump。
如果碰到復(fù)雜的問(wèn)題,難搞的問(wèn)題,其實(shí)也可以使用 coredump 來(lái)定位。
比如程序執(zhí)行到一個(gè)十分不常見(jiàn)的代碼分支,然后程序就 core 掉了,但是目前輸出信息(日志等),根本沒(méi)有辦法進(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)確定程序是如何異常的。
5) 關(guān)于代碼修改
這個(gè)也是我常用的手段之一,反復(fù)的對(duì)比修改前后的代碼,確認(rèn)修改代碼的準(zhǔn)確性,全面性,反思自己代碼修改是否全面?其實(shí)這里面工具就是 beyondcompare。
coding 數(shù)載,偶有所得,記錄于此,望各位有所得!
作者:beck
本文來(lái)自博客園,作者:古道輕風(fēng),轉(zhuǎn)載請(qǐng)注明原文鏈接:http://www.rzrgm.cn/88223100/p/Sharing-of-Linux-backend-development-and-debugging-experience.html

浙公網(wǎng)安備 33010602011771號(hào)