|
j4k04nybbbz6406399925.gif (519.05 KB, 下載次數(shù): 3)
下載附件
保存到相冊(cè)
j4k04nybbbz6406399925.gif
2024-9-16 11:48 上傳
引言友情提示本文接近三千字,預(yù)計(jì)閱讀時(shí)間為10-15分鐘。建議您在空閑時(shí)細(xì)細(xì)閱讀,享受閱讀的樂趣。
難以復(fù)現(xiàn)的Bug之痛你是否曾為那些難以復(fù)現(xiàn)的Bug而頭疼不已?本文將揭秘一種通過堆棧分析來定位并解決這類問題的神奇方法。
作為一名開發(fā)人員,在開發(fā)過程中會(huì)碰到各式各樣的問題,如果能通過一些操作復(fù)現(xiàn)問題的,通過對(duì)目標(biāo)板進(jìn)行調(diào)試還能夠逐步分析。
但是,如果由于某些原因不能對(duì)目標(biāo)板進(jìn)行調(diào)試,這種情況分析可就比較復(fù)雜了。
這不,前一陣子就碰到了一個(gè)問題,在調(diào)試過程中,怎樣都無法復(fù)現(xiàn)問題。直到后來才發(fā)現(xiàn)一個(gè)現(xiàn)象,只有在對(duì)目標(biāo)板上電時(shí)才有一定幾率復(fù)現(xiàn)問題,即頻繁的對(duì)目標(biāo)板進(jìn)行斷電-上電測(cè)試。
降臣:“你難道不好奇你的功力是從何而來嘛?”
李星云:“不好奇”
降臣:“你難道不好奇為何換心之后 無法壓制這突增的內(nèi)力嗎?”
李星云:“不好奇”
降臣:“你難道不好奇為何你只能活半年?”
李星云:“不好奇”
降臣:“你好奇”
李星云:“不好奇”
降臣:“你好奇”
李星云:“不好奇”
如何對(duì)這種問題進(jìn)行分析,你難道不好奇嘛?
寄存器(SP、LR)詳解這里介紹兩個(gè)寄存器。SP(Stack Pointer)寄存器和LR(Link Register)寄存器是非常重要的寄存器。
SP寄存器:堆棧的指路明燈SP寄存器用于指向當(dāng)前堆棧的棧頂位置。在函數(shù)調(diào)用時(shí),SP寄存器會(huì)調(diào)整以反映堆棧的變化,確保數(shù)據(jù)正確地存儲(chǔ)和取出。
LR寄存器:函數(shù)調(diào)用與異常處理的橋梁LR寄存器有兩個(gè)作用。函數(shù)調(diào)用:當(dāng)一個(gè)函數(shù)調(diào)用另一個(gè)函數(shù)時(shí),LR寄存器保存當(dāng)前函數(shù)的返回地址。異常處理:當(dāng)發(fā)生異常時(shí),LR寄存器保存異常處理程序的返回地址。[/ol]問題分析與解決流程揭秘保存現(xiàn)場(chǎng)在處理難以復(fù)現(xiàn)的Bug時(shí),保存現(xiàn)場(chǎng)數(shù)據(jù)是至關(guān)重要的一步。這就像是在犯罪現(xiàn)場(chǎng)拍照留證一樣,能夠?yàn)槲覀兒罄m(xù)的分析提供寶貴的線索。
當(dāng)上電出現(xiàn)死機(jī)問題后,插上Jlink,使用J-Link commander軟件連接目標(biāo)板,然后通過命令將芯片RAM中的區(qū)域保存成二進(jìn)制文件存放到電腦本地。
如果應(yīng)用程序的版本號(hào)不確定,也可以將ROM中的程序保存到本地,操作方法同保存RAM,后面會(huì)說到如何保存。
接下來,我們會(huì)分析RAM中的數(shù)據(jù)。
幸運(yùn)的情況下,可以直接找到最后一次被調(diào)用的函數(shù),然后分析某個(gè)函數(shù)的功能,即可找到問題。而有的情況下就可能還需要根據(jù)函數(shù)的前后調(diào)用關(guān)系分析出問題所在。
分析堆棧數(shù)據(jù)我之前也不知道如何分析堆棧數(shù)據(jù),第一次分析的時(shí)候就感覺進(jìn)了一個(gè)迷宮,繞著繞著就把自己繞進(jìn)去了。
你可以想象一下,拿著一份二進(jìn)制文件,去分析函數(shù)的調(diào)用關(guān)系,想想就腦殼疼...
分析堆棧時(shí)需要知道下面幾個(gè)知識(shí)點(diǎn),才能正確分析,我接下來會(huì)解釋一下。
堆棧結(jié)構(gòu)堆棧結(jié)構(gòu)主要有四種類型,分別是滿遞減堆棧、滿遞增堆棧、空遞減堆棧和空遞增堆棧。
遞增/遞減是指棧向高地址還是低地址增長,滿是指棧指針(SP)總是指向堆棧中的最后一個(gè)元素,即最后壓入的數(shù)據(jù)?帐侵笚V羔槪⊿P)總是指向下一個(gè)將要放入數(shù)據(jù)的空位置。
常用的可能還是滿遞減堆棧比較多一點(diǎn)。
入棧順序因?yàn)槲覀円治鰲V械臄?shù)據(jù),所以我們通過匯編查看依次有哪些數(shù)據(jù)入棧,然后分析出當(dāng)前的LR寄存器中的值。例如下方是一個(gè)入棧的匯編指令。我們需要知道入棧的順序,是從右往左入棧的還是從左往右入棧的。
0x08000644 B570 PUSH {r4-r6,lr}案例J-Link Commande工具首先需要安裝J-Link軟件,去官網(wǎng)https://www.segger.com/downloads/jlink/下載,這里是一個(gè)套件,安裝后會(huì)有若干個(gè)獨(dú)立的小軟件。
我們需要使用的是J-Link Commande軟件。打開軟件之后,可以輸入?來查看支持的命令,如下圖:
dvty5oehynf6406400025.png (38.36 KB, 下載次數(shù): 2)
下載附件
保存到相冊(cè)
dvty5oehynf6406400025.png
2024-9-16 11:48 上傳
感興趣的可以研究這些命令,會(huì)對(duì)自己有所幫助的。
分析;拘畔⑦@里需要分析的是棧屬于上面說的四種結(jié)構(gòu)的哪一種,以及數(shù)據(jù)入棧的順序是如何的。
這里我們需要將ARM仿真器連接目標(biāo)板進(jìn)行調(diào)試,通過單步調(diào)試定位到PUSH匯編指令中,如下圖所示:
qpzv4dhuz4l6406400126.png (249.22 KB, 下載次數(shù): 2)
下載附件
保存到相冊(cè)
qpzv4dhuz4l6406400126.png
2024-9-16 11:48 上傳
當(dāng)未執(zhí)行 0x08000E0C B510 PUSH {r4,1r} 入棧操作時(shí),當(dāng)前的棧指針SP指向0x20000658地址,并且該地址中值不為空。從而說明當(dāng)前的SP指向的是最后一個(gè)入棧的元素,即可判定為滿堆棧。
隨后我們單步執(zhí)行,讓其執(zhí)行入棧操作,再來看堆棧中的數(shù)據(jù),如下圖所示:
xcdi0mqwqiy6406400226.png (263.33 KB, 下載次數(shù): 2)
下載附件
保存到相冊(cè)
xcdi0mqwqiy6406400226.png
2024-9-16 11:48 上傳
此時(shí)我們看到SP的地址為0x20000650,執(zhí)行了入棧操作,SP的地址減小了,從而判定堆棧的生長方向是遞減的。根據(jù)上述兩個(gè)判斷從而能得出堆棧結(jié)構(gòu)為滿遞減堆棧。
接下來我們要判斷數(shù)據(jù)入棧的順序,這個(gè)匯編是將r4以及l(fā)r寄存器中的值入棧。分析入棧后的數(shù)據(jù)得知,第一個(gè)入棧的數(shù)據(jù)為0x08000DE1,剛好是lr寄存器中的值。我在圖片中也標(biāo)注了入棧數(shù)據(jù)對(duì)應(yīng)的寄存器。從而可以得出結(jié)論,入棧的順序是從右往左的,先入棧lr后入棧r4。
保存RAM數(shù)據(jù)到本地需要執(zhí)行如下幾個(gè)步驟,即可連接到目標(biāo)板。
當(dāng)系統(tǒng)死機(jī)后,需要將目標(biāo)板連接到ARM仿真器。使用管理員的身份打開J-Link Commande工具(否則后面保存數(shù)據(jù)會(huì)提示寫入文件失敗)[/ol]connect命令準(zhǔn)備連接目標(biāo)板選擇目標(biāo)芯片型號(hào)選擇調(diào)試接口JTAGSWDcJTAG選擇接口速度連接成功提示SaveBin命令保存棧數(shù)據(jù)[/ol]SaveBin c:/ram.bin 0x20000000 0x5000我這里是將整個(gè)RAM區(qū)域的數(shù)據(jù)保存起來了這里用到了一個(gè)SaveBin的命令,命令的原型如下:
SaveBin SaveBin , , Save target memory range into binary file.
如下圖所示,是我連接目標(biāo)板到保存RAM數(shù)據(jù)的所有操作,此時(shí)C盤根目錄就會(huì)出現(xiàn)一個(gè)ram.bin的文件。
fcki3xb2jil6406400326.png (61.73 KB, 下載次數(shù): 2)
下載附件
保存到相冊(cè)
fcki3xb2jil6406400326.png
2024-9-16 11:48 上傳
分析棧數(shù)據(jù)當(dāng)我們使用jlink連接目標(biāo)板后,輸入命令h可以看到一些關(guān)鍵信息,如下圖:
isobie43dzd6406400426.png (11.08 KB, 下載次數(shù): 2)
下載附件
保存到相冊(cè)
isobie43dzd6406400426.png
2024-9-16 11:48 上傳
可以看到SP(R13)= 20000654, PC = 08000E1E, IPSR = 000 (NoException),那我們先去看pc指向的地址是屬于哪一個(gè)函數(shù)的。我們可以直接在匯編窗口中輸入地址然后直接跳轉(zhuǎn)過去,如下圖所示:
mr34f15mz5i6406400526.png (47.19 KB, 下載次數(shù): 2)
下載附件
保存到相冊(cè)
mr34f15mz5i6406400526.png
2024-9-16 11:48 上傳
通過定位得知,這個(gè)地址是在InitC函數(shù)內(nèi),如下圖:
ae21igl00tn6406400626.png (73.64 KB, 下載次數(shù): 2)
下載附件
保存到相冊(cè)
ae21igl00tn6406400626.png
2024-9-16 11:48 上傳
而且這個(gè)函數(shù)剛執(zhí)行的時(shí)候,執(zhí)行了一次PUSH操作入棧了三個(gè)元素,根據(jù)之前的分析,入棧的順序是從右往左,所以第一個(gè)入棧的數(shù)據(jù)就是LR,又因?yàn)楫?dāng)前的SP指針指向的地址為20000654,然后去查ram.bin數(shù)據(jù),如下圖:
eb5m01vpqbv6406400726.png (8.59 KB, 下載次數(shù): 2)
下載附件
保存到相冊(cè)
eb5m01vpqbv6406400726.png
2024-9-16 11:48 上傳
從而可以推斷出LR的值為0x08000E13,這里是PC+1的值,所以函數(shù)返回的實(shí)際地址為0x08000E12。然后再在跳轉(zhuǎn)到這個(gè)地址,根據(jù)上面圖片,發(fā)現(xiàn)是一個(gè)出棧三個(gè)元素的指令,同樣找到LR實(shí)際地址0x08000E02然后在跳轉(zhuǎn)到這個(gè)地址,反發(fā)仍然是一個(gè)出棧三個(gè)元素的出棧指令,同樣找到LR實(shí)際地址為0x08001046,再繼續(xù)跳轉(zhuǎn)到這個(gè)地址,發(fā)現(xiàn)是一個(gè)延時(shí)函數(shù)了如下圖:
twqcvh5g5cy6406400826.png (37.49 KB, 下載次數(shù): 2)
下載附件
保存到相冊(cè)
twqcvh5g5cy6406400826.png
2024-9-16 11:48 上傳
至此,就是通過分析棧數(shù)據(jù)分析函數(shù)的調(diào)用關(guān)系,這里寫了一個(gè)簡單的測(cè)試歷程,通過按下按鍵會(huì)執(zhí)行幾層函數(shù)調(diào)用最后進(jìn)入一個(gè)死循環(huán),從而模擬死機(jī)的情況。
怎么樣,看著是不是感覺特簡單,但是在實(shí)際的開發(fā)過程中,真實(shí)情況可能比這復(fù)雜百倍。
結(jié)語為了寫這篇文章真的下了血本,我買了一個(gè)STM32的小開發(fā)板以及一個(gè)ARM仿真器,這對(duì)于原本就不富裕的我來說無疑是雪上加霜。
感謝您的耐心閱讀!如果您覺得本文有幫助,請(qǐng)不要吝嗇您的點(diǎn)贊、分享和收藏!
猜你喜歡:
WiFi6+藍(lán)牙+星閃,三合一開發(fā)板,真香!
Github上熱門 C 語言項(xiàng)目匯總!
嵌入式,可測(cè)試性軟件設(shè)計(jì)!
一些低功耗軟件設(shè)計(jì)的要點(diǎn)!
嵌入式 C 保護(hù)結(jié)構(gòu)體的方式
實(shí)用 | 10分鐘教你通過網(wǎng)頁點(diǎn)燈
談?wù)勄度胧杰浖募嫒菪裕?/strong> |
|