1. 問題描述:
最近在支持BLE KW客戶時(shí)就遇到一個(gè)很隱蔽的問題,讓我對ARM斷點(diǎn)又有了顛覆性的認(rèn)識,此處聊作記錄,也為讀者分析問題提供一個(gè)思路。
客戶在同一批次的KW36板子上燒錄同樣的代碼,都可以正常運(yùn)行,但有幾塊板子會莫名其妙Hard fault,而且這幾塊板出現(xiàn)Hard fault的地點(diǎn)各不相同,其中一塊KW36板子是在收到某個(gè)固定UDS診斷服務(wù)執(zhí)行處理時(shí)出現(xiàn)Hard fault,而且一旦問題出現(xiàn)該板子一直能重復(fù)復(fù)現(xiàn)問題。借助之前的一篇舊文《KW36 MCU Hard Fault問題查找和破解方法》通過Attach方式在Ozone IDE(Segger 出品的一個(gè)Debug神器)中連接上去Debug,很快查到導(dǎo)致Hard fault問題現(xiàn)場(沒白瞎之前花時(shí)間研究,大有一番練就打狗棒法,終于遇到一條狗的喜出望外),發(fā)現(xiàn)每次出錯(cuò)的位置都在同一行C代碼,仔細(xì)檢查該段代碼只是個(gè)平淡無奇的一個(gè)函數(shù)調(diào)用,檢查過stack空間也確認(rèn)沒有溢出,那為何會出錯(cuò)呢?同樣的代碼為何只有這幾個(gè)板子出錯(cuò)呢?
2. 問題分析:
本著花里胡哨的問題都是隱藏在最樸實(shí)無華角落的真理,去對比了下兩者的匯編(實(shí)際上是真的沒啥思路),偶然發(fā)現(xiàn)在Attach到問題板分別執(zhí)行全速運(yùn)行到出錯(cuò)代碼行和單步運(yùn)行到出錯(cuò)代碼行時(shí),兩者匯編竟然差異。如下圖右側(cè)是正常執(zhí)行Attach到芯片得到的匯編代碼,左邊是出錯(cuò)時(shí)的匯編代碼,發(fā)現(xiàn)出問題時(shí)匯編竟然出現(xiàn)了DC16和CDP2。DC16對應(yīng)常量聲明,顯然DC16不應(yīng)該在此處出現(xiàn),CDP2是協(xié)處理器數(shù)據(jù)處理指令,KW36作為一個(gè)單核芯片,肯定無協(xié)處理器,更談不上協(xié)處理器指令。
考慮到上述的調(diào)試操作都是在Attach方式,不存在因?yàn)榇a下載導(dǎo)致Flash內(nèi)容被修改的可能,那可能只有兩個(gè):IDE解析錯(cuò)誤以及代碼被意外修改,顯然前者是個(gè)死胡同,無從查起。于是順著第二條路Dump芯片內(nèi)二進(jìn)制(左側(cè))和原始二進(jìn)制文件(右側(cè))對比發(fā)現(xiàn)兩者Flash確實(shí)有差異。如下圖所示,F(xiàn)000的值被修改成了BE00,同時(shí)處理CRC校驗(yàn)值也被修改,So什么原因?qū)е翭lash被修改了呢?
以前有概念說斷點(diǎn)會程序代碼做修改,但這個(gè)二進(jìn)制碼的修改如何和斷點(diǎn)扯上關(guān)系呢?于是開始了漫長的Google/Bing/度娘/ARM各種文檔里面輪番挖呀挖呀挖,直到挖出Cortex-M3權(quán)威指南中提到如下一句"BKPT 指令的機(jī)器碼是 0xBE00",頓時(shí)豁然開朗,坐實(shí)了斷點(diǎn)導(dǎo)致Flash內(nèi)容被修改。
可新的問題來了,斷點(diǎn)是如何修改到這段Flash內(nèi)容的呢?什么類型的斷點(diǎn)會修改Flash?斷點(diǎn)改了Flash內(nèi)容為何不會被還原?打斷點(diǎn)要注意什么才能保證修改后的Flash內(nèi)容被正確還原?打軟件斷點(diǎn)是否會修改Flash內(nèi)容?Flash斷點(diǎn),硬件斷點(diǎn)以及程序斷點(diǎn)的區(qū)別?芯片明明寫著支持2個(gè)breakpoint,為何同一個(gè)IDE配合不同的調(diào)試工具實(shí)際上可以打出的斷點(diǎn)個(gè)數(shù)有差異?帶著一堆問號繼續(xù)探尋...
3. 斷點(diǎn)類型和實(shí)現(xiàn)原理分析
斷點(diǎn)的實(shí)現(xiàn)原理網(wǎng)上有很多介紹,大致意思都是說在程序運(yùn)行的某個(gè)地方提前設(shè)置一個(gè)停止操作,CPU運(yùn)行到該位置之后就會自動(dòng)暫停,此時(shí)開發(fā)者就可以查看CPU寄存器的工作狀態(tài)。可這里的”設(shè)置一個(gè)停止操作“是如何實(shí)現(xiàn)的呢?又意味著什么呢?網(wǎng)上找到的都是基于X86的實(shí)現(xiàn)細(xì)節(jié)介紹,針對ARM的實(shí)現(xiàn)細(xì)節(jié)找了半天也都是語焉不詳。發(fā)揮絕知此事要躬行的精神(客戶需要復(fù)現(xiàn)現(xiàn)場),筆者使用JLINK/I-JET+IAR在KW38 demo板子上做了一番測試,得出幾個(gè)很有意思的結(jié)論,完全顛覆了以前對斷點(diǎn)的一些認(rèn)識,此處先拋出結(jié)論。
根據(jù)IDE和調(diào)試器的不同,斷點(diǎn)類型也被分為很多種包括硬件斷點(diǎn)、軟件斷點(diǎn)、Flash斷點(diǎn)、數(shù)據(jù)斷點(diǎn)、程序斷點(diǎn)、條件斷點(diǎn)、代碼斷點(diǎn)以及Trace斷點(diǎn)等。從實(shí)現(xiàn)原理上來講,一共有四大類:硬件斷點(diǎn)、軟件斷點(diǎn)、數(shù)據(jù)斷點(diǎn)以及Flash斷點(diǎn),因?yàn)槌绦驍帱c(diǎn)和代碼斷點(diǎn)從原理上講可以歸屬于硬件斷點(diǎn)或者Flash斷點(diǎn)(具體哪一種取決于控制器支持的硬件斷點(diǎn)個(gè)數(shù)),條件斷點(diǎn)屬于數(shù)據(jù)斷點(diǎn),Trace斷點(diǎn)是結(jié)合Trace工具的程序斷點(diǎn)。下面分別深入介紹這四種斷點(diǎn)的實(shí)現(xiàn)機(jī)制:
3.1 硬件斷點(diǎn):
硬件斷點(diǎn)需要MCU內(nèi)部支持,其數(shù)量一般是有限的,具體取決于芯片廠商的實(shí)現(xiàn),對應(yīng)KW38、KW45、S32K1以及S32K3斷點(diǎn)支持個(gè)數(shù)統(tǒng)計(jì)如下表所示。從實(shí)現(xiàn)原理上講硬件斷點(diǎn)是通過FPB模塊來實(shí)現(xiàn)的,盡管這塊是芯片廠商自行實(shí)現(xiàn)的,但是ARM架構(gòu)已為其分配固定的地址區(qū)域,如下圖紅色方框。所以不論哪個(gè)公司的cortex芯片,用戶都可以通過該地址查看已設(shè)置的硬件斷點(diǎn)所在地址。FPB提供了兩個(gè)功能:斷點(diǎn)功能和Flash地址重載, Flash地址重載是其一個(gè)高階功能,此處用到的是其斷點(diǎn)功能。當(dāng)在程序中設(shè)置了硬件斷點(diǎn)后,該斷點(diǎn)處的地址就會被寫入到FPB的存儲區(qū)域,即E000_2000 – E000_3FFF區(qū)域,當(dāng)CPU訪問該地址時(shí),因?yàn)樵摰刂吩贔PB中已經(jīng)注冊過,硬件就會自動(dòng)觸發(fā)一個(gè)斷點(diǎn)事件。
芯片型號 | KW38(M0+) | KW45(M33) | S32K1(M4) | S32K3(M7) |
---|---|---|---|---|
硬件斷點(diǎn)個(gè)數(shù) | 2個(gè) | 8個(gè) | 6個(gè) | 8個(gè) |
3.2 Flash斷點(diǎn):
Flash斷點(diǎn)也就是本次問題的罪魁禍?zhǔn)琢?,也是從原理上來講最最容易出現(xiàn)問題的斷點(diǎn)。其實(shí)現(xiàn)原理是在設(shè)置Flash斷點(diǎn)時(shí),IDE和仿真器會配合修改控制器Flash上的內(nèi)容,進(jìn)一步說就是將斷點(diǎn)處的指令修改為斷點(diǎn)指令0xBE00,相應(yīng)地就會牽涉到對Flash的擦除和編程操作。由于Flash內(nèi)容是非易失的,所以有可能在調(diào)試器意外斷開后對Flash斷點(diǎn)處插入的斷點(diǎn)指令還保留著,在用戶復(fù)位直接運(yùn)行程序可能就會導(dǎo)致程序無法正常運(yùn)行。
目前市場上各種宣稱支持Unlimited Breakpoint無限斷點(diǎn)的仿真器采用的就是這個(gè)原理,如下截圖的JLINK工具。需要提出的是Flash斷點(diǎn)不是任意仿真器+IDE的組合都支持的,需要IDE和仿真器配合起來,譬如Ozone/Embeded Studio配合JLINK,IAR需要配合I-JET才能在斷點(diǎn)設(shè)置時(shí)指定使用Flash斷點(diǎn),如果IAR配合JLINK只能選擇軟件斷點(diǎn)(實(shí)際使用的其實(shí)也是Flash斷點(diǎn),只是標(biāo)注上不帶F標(biāo)記)。I-JET和JLINK在支持Flash斷點(diǎn)的差別主要是對Flash執(zhí)行修改的策略,譬如一個(gè)Sector地址區(qū)域的多個(gè)Flash斷點(diǎn)是執(zhí)行一次還是多次的Flash操作,以及Flash插入的斷點(diǎn)恢復(fù)策略等,需要使用時(shí)自行體會。
3.3 軟件斷點(diǎn):
軟件斷點(diǎn)最開始主要用于RAM中設(shè)置斷點(diǎn),它的實(shí)現(xiàn)機(jī)制是將RAM中原有的指令替換為一個(gè)斷點(diǎn)指令,當(dāng)CPU執(zhí)行到該指令之后就會自動(dòng)暫停,這個(gè)時(shí)候調(diào)試器便會將原有的指令放回原來位置。這個(gè)過程都是調(diào)試器自動(dòng)實(shí)現(xiàn)的,對RAM的更新速度很快,所以用戶一般覺察不到。但實(shí)際情況是現(xiàn)在很多MCU受限于RAM大小,程序都是存放在Flash區(qū)域的,所以當(dāng)斷點(diǎn)大于硬件斷點(diǎn)數(shù)量后,即便新的斷點(diǎn)選成軟件斷點(diǎn),很多時(shí)候也會被設(shè)置為Flash斷點(diǎn),意味著Flash斷點(diǎn)有的問題在使用軟件斷點(diǎn)時(shí)也需要注意,這也是為何將軟件斷點(diǎn)的講解放在Flash斷點(diǎn)之后。
3.4 數(shù)據(jù)斷點(diǎn):
數(shù)據(jù)斷點(diǎn)又稱為條件斷點(diǎn),在ARM內(nèi)核手冊上稱為watchpoint斷點(diǎn),是在CPU對特定地址訪問時(shí)會觸發(fā)的一種斷點(diǎn),在芯片內(nèi)部實(shí)現(xiàn)上需要DWT和FPB共同配合完成,在設(shè)置斷點(diǎn)的時(shí)候可以設(shè)置觸發(fā)的條件,比如對特定地址的讀訪問還是寫訪問。這種斷點(diǎn)對于追蹤堆棧溢出相關(guān)的問題有奇效。數(shù)據(jù)斷點(diǎn)同樣需要硬件支持,數(shù)量一般也是有限的,如對應(yīng)KW38、KW45、S32K1以及S32K3數(shù)據(jù)斷點(diǎn)支持個(gè)數(shù)統(tǒng)計(jì)如下表所示。
芯片型號 | KW38(M0+) | KW45(M33) | S32K1(M4) | S32K3(M7) |
---|---|---|---|---|
數(shù)據(jù)斷點(diǎn)個(gè)數(shù) | 2個(gè) | 4個(gè) | 4個(gè) | 4個(gè) |
4. 斷點(diǎn)實(shí)驗(yàn)證明:
為了證明以上觀點(diǎn),筆者在KW38板子上做了一系列的測試如下:
4.1 IAR+I-JET 強(qiáng)制使用2個(gè)Flash斷點(diǎn),無硬件斷點(diǎn):
如下截圖可以看到,在匯編和memory窗口中的數(shù)據(jù)都是原始的binary文件,但是通過額外的JLINK同時(shí)去Attach上去讀取可以看到對應(yīng)0x16996地址和0x169A0物理地址的數(shù)據(jù)已經(jīng)被修改為了0xBE00;此處要特別鳴謝下IAR的Wen hao兄給的啟發(fā),確實(shí)沒想到IARMemory窗口讀取的數(shù)據(jù)和匯編窗口的指令居然都是假數(shù)據(jù)。
Note: IAR需要配合IAR公司自己的I-JET才能選擇Flash斷點(diǎn),如果選擇JLINK等其他工具只能設(shè)置軟件斷點(diǎn);
4.2 IAR+JLINK 使用2個(gè)硬件斷點(diǎn)+1個(gè)Flash斷點(diǎn):
如下圖1 可以看到,兩個(gè)硬件斷點(diǎn)地址0x1FA2以及0x1FA6都被注冊到了FPB寄存器的0xE0002000地址了。要指出的是,此處提到的Flash斷點(diǎn)在IAR中顯示為軟件斷點(diǎn),因?yàn)镮AR中默認(rèn)僅支持使用I-JET的工具去打Flash斷點(diǎn),其標(biāo)志就是會在紅點(diǎn)里面顯示一個(gè)F圖標(biāo)。所以這也就是前文說到的,對于Flash運(yùn)行的代碼軟件斷點(diǎn)在實(shí)現(xiàn)上本質(zhì)也還是Flash斷點(diǎn)。
下圖2 可以看到,強(qiáng)制設(shè)置為line 38和line40處為硬件斷點(diǎn)之后,因?yàn)镵W38只支持2個(gè)硬件斷點(diǎn),main處這個(gè)斷點(diǎn)就被強(qiáng)制設(shè)定為了Flash斷點(diǎn),分別去讀取0x1F98, 0x1FA2以及0x1FA6地址的值,可以看到main處斷點(diǎn)所在的地址內(nèi)容被修改為了0xBE00,0x1FA2以及0x1FA6地址是正常的原始數(shù)據(jù),存儲在硬件斷點(diǎn)地址,完全符合預(yù)期。
4.3 IAR+JLINK使用2個(gè)硬件斷點(diǎn)+2個(gè)軟件斷點(diǎn):
可以看到,line36,line40被強(qiáng)行設(shè)置為硬件斷點(diǎn),line38被設(shè)置為軟件斷點(diǎn),main斷點(diǎn)也自動(dòng)被分配為了軟件斷點(diǎn),對比0x1F98, 0x1F9A, 0x1FA2, 0x1FA6地址的內(nèi)容,只有0x1F98,0x1FA2兩個(gè)軟件斷點(diǎn)地址處的內(nèi)容被修改為了0xBE00,即插入BKPT指令。0x1F9A和0x1FA6兩個(gè)硬件地址的內(nèi)容沒有被修改,再次證明軟件斷點(diǎn)對于在Flash調(diào)試的控制器,本質(zhì)上就是Flash斷點(diǎn)。
4.4 IAR+JLINK 使用6個(gè)軟件斷點(diǎn)+1個(gè)硬件斷點(diǎn):
因?yàn)镮AR中默認(rèn)優(yōu)先使用硬件斷點(diǎn),而當(dāng)前debug中設(shè)置了6個(gè)軟件斷點(diǎn),所以main出的斷點(diǎn)被自動(dòng)分配為硬件斷點(diǎn)??梢钥吹?,全部6個(gè)軟件斷點(diǎn)對應(yīng)地址區(qū)域的值都被修改為了0xBE00,也就是前文提到的軟件斷點(diǎn)其實(shí)也變成了Flash斷點(diǎn),main地址處內(nèi)容沒有發(fā)生變化,符合預(yù)期。
5. 如何預(yù)防Flash斷點(diǎn)帶來的潛在風(fēng)險(xiǎn)
對于硬件斷點(diǎn)和數(shù)據(jù)斷點(diǎn)來說,他們是基于寄存器的實(shí)現(xiàn),脫離Debug狀態(tài)無法繼續(xù)生效,不會影響用戶代碼的脫機(jī)運(yùn)行。但是Flash斷點(diǎn)因?yàn)闋可娴叫薷姆且资У腇lash數(shù)據(jù),就有風(fēng)險(xiǎn)導(dǎo)致程序意外被修改無法運(yùn)行,譬如說突然斷電,不合理退出debug狀態(tài)等。
而且對于該類問題,如果斷點(diǎn)斷在main函數(shù)的主任務(wù)上還容易觀察到,因?yàn)榇a很直觀的無法運(yùn)行,而如果斷在了某個(gè)不常調(diào)用的但又會用到的子任務(wù)的某個(gè)switch case中,就容易埋下隱蔽的bug(本案例遇到的就是這個(gè)狀況)。還一種情況是實(shí)車現(xiàn)場分析過程中,常用的一種方式就是Attach觀察分析問題,如果使用到Flash斷點(diǎn)(尤其是Flash運(yùn)行的軟件斷點(diǎn)),且退出時(shí)沒注意清除該類型斷點(diǎn),可能會帶來新的潛在問題。
設(shè)置完Flash斷點(diǎn),對于突然斷電這種方式,自然是無解的,只能重新下載代碼。而在debug狀態(tài)時(shí)如何退出才是最優(yōu)雅的方式(還原原本Flash內(nèi)容)呢?筆者使用IAR+JLINK在KW38 demo板上上做了一系列測試,結(jié)論如下;
序號 | 測試條件(此處軟件斷點(diǎn)使用的是Flash斷點(diǎn)) | 運(yùn)行結(jié)果 |
---|---|---|
1 | 使用6個(gè)軟件斷點(diǎn)+突然斷電+上電重啟: | 無法運(yùn)行 |
2 | 使用6個(gè)軟件斷點(diǎn)+IDE中正常stop debug后+上電重啟: | 無法運(yùn)行 |
3 | 使用6個(gè)軟件斷點(diǎn)+在breakpoint窗口中Disable 所有Flash斷點(diǎn)+上電重啟: | 無法運(yùn)行 |
4 | 使用6個(gè)軟件斷點(diǎn)+在breakpoint窗口中Disable 所有Flash斷點(diǎn)+執(zhí)行一次SW reset+上電重啟: | 正常運(yùn)行 |
5 | IAR+JLINK+KW38 使用6個(gè)軟件斷點(diǎn)+在breakpoint串口中Delete所有Flash斷點(diǎn)+上電重啟 | 正常運(yùn)行 |
總的來說,為了避免Flash斷點(diǎn)帶來的Flash修改后沒有恢復(fù)的問題,需要在退出Debug模式時(shí),先delete掉所有斷點(diǎn),再去stop debug,或者先在在breakpoint窗口中Disable 所有Flash斷點(diǎn)后,再debug狀態(tài)下執(zhí)行一次SW reset,讓Flash恢復(fù)回來,前者的缺點(diǎn)是下次debug時(shí)原來的斷點(diǎn)都丟失了,需要重新設(shè)置,優(yōu)點(diǎn)是更徹底。
6. 結(jié)論:
基于以上分析可以得出幾個(gè)結(jié)論:
Flash斷點(diǎn)是會修改到Flash內(nèi)容的,而且IDE和匯編中是體現(xiàn)不出該變化的,比較隱蔽;
Flash斷點(diǎn)導(dǎo)致Flash內(nèi)容修改后未恢復(fù)的問題需要尤其重視,需要在退出debug前按章節(jié)5的兩種辦法加以處理;
軟件斷點(diǎn)對于非RAM中調(diào)試來說,IAR配合Jlink使用時(shí)本質(zhì)上也是Flash斷點(diǎn);
不是所有的調(diào)試器都支持Flash斷點(diǎn),當(dāng)設(shè)置的斷點(diǎn)個(gè)數(shù)超過硬件斷點(diǎn)個(gè)數(shù),就會提示無法設(shè)置新的斷點(diǎn),例如CMSIS DAP; 硬件斷點(diǎn)的支持個(gè)數(shù)有限,會在調(diào)試時(shí)默認(rèn)采用,超過硬件斷點(diǎn)個(gè)數(shù)后的斷點(diǎn)JLINK默認(rèn)會采用Flash斷點(diǎn), IJET提示超過上限,需要選擇Flash斷點(diǎn),斷點(diǎn)小紅點(diǎn)標(biāo)志帶有F標(biāo)志;
IAR+JLINK中JLINK支持Flash斷點(diǎn)是JLINK仿真器和其驅(qū)動(dòng)的支持,IAR+IJET支持Flash斷點(diǎn)是IAR工具+I-JET硬件的支持,和IAR+JLINK的區(qū)別是其可以直接設(shè)置Flash類型斷點(diǎn);
如果是Flash斷點(diǎn)打在了主函數(shù)導(dǎo)致重啟后代碼無法運(yùn)行倒容易排查,如果是不小心打在了某個(gè)不常調(diào)用的子函數(shù)分支上,而功能測試時(shí)覆蓋度如果不夠,就可能導(dǎo)致埋下一個(gè)潛在的隱患;
審核編輯:劉清
-
處理器
+關(guān)注
關(guān)注
68文章
19381瀏覽量
230454 -
mcu
+關(guān)注
關(guān)注
146文章
17197瀏覽量
351903 -
ARM
+關(guān)注
關(guān)注
134文章
9121瀏覽量
368220 -
NAND
+關(guān)注
關(guān)注
16文章
1686瀏覽量
136281 -
寄存器
+關(guān)注
關(guān)注
31文章
5359瀏覽量
120786 -
FlaSh
+關(guān)注
關(guān)注
10文章
1640瀏覽量
148289 -
仿真器
+關(guān)注
關(guān)注
14文章
1019瀏覽量
83841 -
ARM處理器
+關(guān)注
關(guān)注
6文章
361瀏覽量
41825 -
MCU控制
+關(guān)注
關(guān)注
0文章
48瀏覽量
6763 -
NAND Flash芯片
+關(guān)注
關(guān)注
0文章
3瀏覽量
1317
原文標(biāo)題:ARM各種斷點(diǎn)到底有什么區(qū)別?
文章出處:【微信號:TopSemic,微信公眾號:TopSemic嵌入式】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論