當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 程序員寫(xiě)個(gè)解
[導(dǎo)讀]在我看來(lái)最不值得一提的BUG是那種可以重復(fù)復(fù)現(xiàn)的,他的穩(wěn)定復(fù)現(xiàn)通常排查起來(lái)沒(méi)啥技術(shù)含量, 早些年我處理一個(gè)不值得一提的BUG,BUG也很好復(fù)現(xiàn),難點(diǎn)是復(fù)現(xiàn)時(shí)間固定在4小時(shí)左右,BUG由于文件資源未釋放引起進(jìn)程訪(fǎng)問(wèn)文件數(shù)目受限而崩潰,早期A(yíng)ndroid系統(tǒng)用該BUG獲取到root權(quán)限, 本文向你分享,如何根據(jù)錯(cuò)誤提示和參考手冊(cè)找到故障點(diǎn),指導(dǎo)新碼農(nóng)如何正確閱讀Linux幫助手冊(cè)(man page), 最后總結(jié)我的排查過(guò)程給小白一點(diǎn)實(shí)用的建議。好下面開(kāi)始不如步入正題。需要調(diào)試的是一個(gè)監(jiān)控程序,代碼非常簡(jiǎn)單,2個(gè)線(xiàn)程執(zhí)行不同的任務(wù),每個(gè)任務(wù)都是間隔15秒執(zhí)行一次,程序固定在大約4小時(shí)后崩潰。代碼簡(jiǎn)單到用不著任何同步機(jī)制、沒(méi)有任何通信,極少的內(nèi)存訪(fǎng)問(wèn),按理來(lái)說(shuō)他就不應(yīng)該存在BUG,然而還是發(fā)生了。

最不值得一提的BUG

在我看來(lái)最不值得一提的BUG是那種可以重復(fù)復(fù)現(xiàn)的,他的穩(wěn)定復(fù)現(xiàn)通常排查起來(lái)沒(méi)啥技術(shù)含量, 早些年我處理一個(gè)不值得一提的BUG,BUG也很好復(fù)現(xiàn),難點(diǎn)是復(fù)現(xiàn)時(shí)間固定在4小時(shí)左右,BUG由于文件資源未釋放引起進(jìn)程訪(fǎng)問(wèn)文件數(shù)目受限而崩潰,早期A(yíng)ndroid系統(tǒng)用該BUG獲取到root權(quán)限, 本文向你分享,如何根據(jù)錯(cuò)誤提示和參考手冊(cè)找到故障點(diǎn),指導(dǎo)新碼農(nóng)如何正確閱讀Linux幫助手冊(cè)(man page), 最后總結(jié)我的排查過(guò)程給小白一點(diǎn)實(shí)用的建議。好下面開(kāi)始不如步入正題。需要調(diào)試的是一個(gè)監(jiān)控程序,代碼非常簡(jiǎn)單,2個(gè)線(xiàn)程執(zhí)行不同的任務(wù),每個(gè)任務(wù)都是間隔15秒執(zhí)行一次,程序固定在大約4小時(shí)后崩潰。代碼簡(jiǎn)單到用不著任何同步機(jī)制、沒(méi)有任何通信,極少的內(nèi)存訪(fǎng)問(wèn),按理來(lái)說(shuō)他就不應(yīng)該存在BUG,然而還是發(fā)生了。

第1個(gè)4小時(shí):縮小排查范圍,是什么引起段錯(cuò)誤

在源碼若干位置加上打印執(zhí)行的函數(shù)、行號(hào), 打開(kāi)調(diào)試選項(xiàng)重新編譯應(yīng)用程序,開(kāi)啟coredump選項(xiàng),耐心等待4小時(shí)后故障復(fù)現(xiàn)。gdb打開(kāi)coredump 確認(rèn)段錯(cuò)誤(Segmentation fault),棧溯確認(rèn)崩潰現(xiàn)場(chǎng)調(diào)用棧。段錯(cuò)誤位于ti_ck_mutil函數(shù)第266行之后。
TickStatusIO():105ti_ck_mutil():266Segmentation fault (core dumped) (gdb) bt#0  0x401b28e0 in vfwprintf () from /lib/libc.so.6#1  0x00009d10 in ti_ck_mutil (cmdstr=0xbebffa4c, len=1) at src/ti.c:268#2  0x00008e2c in TickStatusIO () at src/initgpio.c:106#3  0x00009238 in main (argc=1, argv=0xbebffbf4) at src/initgpio.c:304

審查ti_ck_mutil函數(shù)內(nèi)226行之后的代碼,結(jié)合棧底位置是vfwprintf函數(shù)入口,基本可以確定導(dǎo)致崩潰位置是fread函數(shù),fread可能會(huì)有什么錯(cuò)誤呢?
int ti_ck_mutil(char *cmdstr, int count){ FILE *stream; char strout[256]; int ret, failcount = 0;  for (int i = 0; i < count; i++) { printf("%s()%d\n", __FUNCTION__, __LINE__);//226行 stream = popen(cmdstr, "r");//未檢查文件是否成功 ret = fread(strout, sizeof(char), sizeof(strout), stream); // 228行  strout[ret] = '\0'; pclose(stream); // ... } return failcount;}
fread輸入?yún)?shù)只有4個(gè),猜測(cè)可能存在的失敗原因有3點(diǎn):
1、被編譯器優(yōu)化后strout的緩存不是256

但后面用的是算數(shù)表達(dá)式sizeof,就算被優(yōu)化也不會(huì)造成錯(cuò)誤。
觀(guān)點(diǎn):暫時(shí)不去瞎想。2、fread寫(xiě)入最后一個(gè)字符時(shí)溢出。

strout后第256地址也被填寫(xiě)了,實(shí)際我讀寫(xiě)的文件不超過(guò)64byte,不應(yīng)該超過(guò)256。
即使第256地址被fread寫(xiě)了,相當(dāng)于內(nèi)存訪(fǎng)問(wèn)越接。訪(fǎng)問(wèn)越接發(fā)生什么錯(cuò)誤都不奇怪,輕微越接會(huì)影響附近變量的值,比如ret和stream的值改變,大范圍越界破壞調(diào)用棧。觀(guān)點(diǎn):猜測(cè)fread可能訪(fǎng)問(wèn)越限,但絕對(duì)沒(méi)破壞調(diào)用棧。若破壞調(diào)用棧,那么棧不會(huì)是整整齊齊打印4個(gè)函數(shù),而是輸出若干問(wèn)號(hào)(“?? ()”),找不到函數(shù)名稱(chēng)標(biāo)簽。
#0  0x000028e0 in ?? () #1  0x000038e8 in ?? () #2  0x000048ec in ?? () #3  0x000068e0 in ?? () #4  0x00009d10 in ti_ck_mutil (cmdstr=0xbebffa4c, len=1) at src/ti.c:268#5  0x00008e2c in TickStatusIO () at src/initgpio.c:106#6  0x00009238 in main (argc=1, argv=0xbebffbf4) at src/initgpio.c:304

3、stream文件描述符無(wú)效觀(guān)點(diǎn):有可能,源碼未對(duì)popen返回結(jié)果做判斷。

第2個(gè)4小時(shí):是內(nèi)存越界?還是資源不足?

于是結(jié)合猜測(cè)2和3,對(duì)源碼做2處理修改:1、不向fread傳遞完整內(nèi)存長(zhǎng)度,保證最后一個(gè)字符不被fread填寫(xiě) 2、判斷popen返回值
stream = popen(cmdstr, "r");ret = fread(strout, sizeof(char), sizeof(strout), stream); 修改后 stream = popen(cmdstr, "r");if (stream == 0) { perror("popen error:");}ret = fread(strout, sizeof(char), sizeof(strout) - 1, stream);

繼續(xù)等待4小時(shí),程序依舊崩潰,輸出崩潰前提示執(zhí)行popen失敗,返回值0,錯(cuò)誤原因記錄在errno里,errno指示打開(kāi)太多文件,資源不足。
	
popen error:: Too many open files

機(jī)理分析:為什么文件打開(kāi)太多?

進(jìn)一步定位到故障點(diǎn)在popen函數(shù)上,問(wèn)題是:啥叫文件打開(kāi)太多?查看popen幫助介紹:man popen?;蛟S能給我解釋
RETURN VALUEThe popen() function returns NULL if the fork(2) or pipe(2) calls fail, or if it cannot allocate memory.
本質(zhì)上popen是個(gè)“殼",它返回0的原因有兩個(gè):1、它間接調(diào)用fork()創(chuàng)建子進(jìn)程執(zhí)行腳本,間接調(diào)用pipe()創(chuàng)建管道,子進(jìn)程輸出信息從管道傳遞到父進(jìn)程。2、沒(méi)有足夠的內(nèi)存分配。從第2點(diǎn):沒(méi)有足夠的內(nèi)存方向去排查,無(wú)非是內(nèi)存泄漏咯,通常是申請(qǐng)內(nèi)存有釋放干凈導(dǎo)致。c語(yǔ)言標(biāo)準(zhǔn)內(nèi)存分配函數(shù)有malloc、calloc、realloc、reallocarray,對(duì)應(yīng)的釋放函數(shù)只有free。我應(yīng)該在源碼上搜索,是否所有“分配函數(shù)和釋放函數(shù)都一一配對(duì)”,哦~別忘了,小白可能還不清楚,除了常用的malloc外,還有像mmap這樣的內(nèi)存分配函數(shù),它有專(zhuān)用的釋放函數(shù)munmap。從搜索結(jié)果上看,數(shù)目是能對(duì)得上的,暫且粗略的判定不存內(nèi)存泄漏。更仔細(xì)的排查方向應(yīng)該是:確定代碼執(zhí)行流真的執(zhí)行到釋放函數(shù),而不是單純地看數(shù)目是否匹配。



在繼續(xù)閱讀popen的errors段落描述。
ERRORSThe popen() function does not set errno if memory allocation fails. If the underlying fork(2) or pipe(2) fails, errno is set appropriately. If the type argument is invalid, and this condition is detected, errno is set to EINVAL.
popen不會(huì)因?yàn)閮?nèi)存分配失敗而在errno記錄錯(cuò)誤碼,如果是fork()或pipe()函數(shù)執(zhí)行失敗則在errno設(shè)置相應(yīng)錯(cuò)誤碼。忙半天忙個(gè)寂寞,年輕人,別學(xué)會(huì)寫(xiě)一、二、三,就自以為無(wú)師自通懂得寫(xiě)四、寫(xiě)一萬(wàn)。讀完man全文再入手好不好!既然errno提示具體錯(cuò)誤信息,就不可能是內(nèi)存泄漏,執(zhí)行失敗原因一定是Too many open files的字面意思。回想以前初學(xué)Linux時(shí)有個(gè)知識(shí)點(diǎn):為了防止某用戶(hù)打開(kāi)過(guò)多的文件,系統(tǒng)對(duì)進(jìn)程件訪(fǎng)問(wèn)數(shù)目有限制,默認(rèn)是1024。2016年4月參加宋寶華的線(xiàn)下培訓(xùn),他說(shuō)Android剛出來(lái)時(shí)有個(gè)提權(quán)的方法(root權(quán)限):創(chuàng)建1024個(gè)無(wú)用子進(jìn)程資源且不釋放,第1025個(gè)進(jìn)程就能得到root權(quán)限。命令查看應(yīng)用程序運(yùn)行一段時(shí)間后,有多少文件描述符號(hào)(file descriptor)沒(méi)有釋放。果然,每間隔15秒文件描述符就多一個(gè)。256分鐘后達(dá)到1024個(gè)文件描述符,時(shí)間上和軟件4小時(shí)崩潰很接近。
watch -n 1 ls -l /proc/PID/fd

再用之前的篩選方法:排查open和close的函數(shù)是一一匹配。發(fā)現(xiàn)open關(guān)鍵詞篩選出
6行,close作為關(guān)鍵詞篩出5行。opendir沒(méi)有對(duì)應(yīng)的close。


捂臉!?。?/span>“Linux下一切皆是文件”我還沒(méi)理解透徹,沒(méi)意識(shí)到打開(kāi)目錄(opendir)也是文件資源,應(yīng)用程序某線(xiàn)程每間隔15秒就訪(fǎng)問(wèn)一次目錄。man opendir確認(rèn)closedir是它的配對(duì)關(guān)閉函數(shù)。
SEE ALSOopen(2), closedir(3), dirfd(3), readdir(3), rewinddir(3), scandir(3), seekdir(3), telldir(3)
添加上closedir后故障得以修復(fù)。

順帶提一下

貼圖用的搜索工具不是grep而是我自己寫(xiě)的腳本jgrep,它的用法和grep完全一樣,輸入前面的數(shù)字能打開(kāi)對(duì)于文件所在行,對(duì)于搜索源碼、系統(tǒng)配置文件檢索、跳轉(zhuǎn)特別適用。如果你對(duì)jgrep感興趣的話(huà),在我的公眾號(hào)“程序員寫(xiě)個(gè)解”發(fā)送 “20220411” 可獲取。




總結(jié)建議

BUG成功得以修復(fù),它本是不應(yīng)該犯的錯(cuò)誤,在這里我給自己和讀者建議:1、以后使用不熟悉的API,首先查閱他的幫助手冊(cè)2、對(duì)于內(nèi)存分配函數(shù)有相互獨(dú)立的API,比如malloc對(duì)應(yīng)free、mmap對(duì)應(yīng)munmap。跟著手冊(cè)建議的API去掉釋放資源,避免不可預(yù)知的故障發(fā)生。最后,如果你覺(jué)得文章對(duì)你有所幫助,有啟發(fā)作用。歡迎點(diǎn)擊,把今天的內(nèi)容分享給你的好友,和他一起討論學(xué)習(xí)。
本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀(guān)點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專(zhuān)欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車(chē)的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車(chē)技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車(chē)工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車(chē)。 SODA V工具的開(kāi)發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車(chē) 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶(hù)希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開(kāi)幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱(chēng),數(shù)字世界的話(huà)語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱(chēng)"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉