在线看毛片网站电影-亚洲国产欧美日韩精品一区二区三区,国产欧美乱夫不卡无乱码,国产精品欧美久久久天天影视,精品一区二区三区视频在线观看,亚洲国产精品人成乱码天天看,日韩久久久一区,91精品国产91免费

<menu id="6qfwx"><li id="6qfwx"></li></menu>
    1. <menu id="6qfwx"><dl id="6qfwx"></dl></menu>

      <label id="6qfwx"><ol id="6qfwx"></ol></label><menu id="6qfwx"></menu><object id="6qfwx"><strike id="6qfwx"><noscript id="6qfwx"></noscript></strike></object>
        1. <center id="6qfwx"><dl id="6qfwx"></dl></center>

            新聞中心

            EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > 嵌入式ARM平臺調(diào)試方法的討論

            嵌入式ARM平臺調(diào)試方法的討論

            作者: 時間:2016-11-21 來源:網(wǎng)絡(luò) 收藏
            1. 當前使用的調(diào)試方法
            通常情況下我們直接使用JTAG進行嵌入式設(shè)備的調(diào)試和開發(fā)。此方式最簡單和直接,且功能強大,能夠隨時中斷處理器,檢查程序狀態(tài)。但是此方式也有缺點:無法長時間跟蹤程序的執(zhí)行情況,對于客戶處一些難復現(xiàn)的死機問題很難處理,基本只能依靠靜態(tài)代碼分析。且金融POS來說,由于防拆機制的存在,編寫應(yīng)用時沒有辦法直接使用JTAG進行調(diào)試。因此我們討論幾種新的輔助調(diào)試方法。
            2. 幾種新的調(diào)試方法
            2.1. 打印寄存器信息
            此種方法是最簡單的輔助調(diào)試方法。在需要打印調(diào)試信息的地方加入一個打印函數(shù)(或串口打印或屏幕打?。?。在程序出錯時可以打印當前所有寄存器的數(shù)據(jù)。這樣可以根據(jù)PC或LR的值得出當前正在運行的函數(shù)和上一個運行的函數(shù),進一步通過編譯器輸出的Listing文件還可以得到當前和上一個函數(shù)C源代碼中的行號。更進一步可以編寫一個PC應(yīng)用輔助進行錯誤分析。
            2.2. 打印調(diào)用棧
            集成調(diào)用棧打印比上一種方式能提供更多的信息,在出錯時除了當前寄存器的數(shù)值,還可以輸出完整的調(diào)用堆棧。經(jīng)過實際驗證發(fā)現(xiàn),我司目前使用的keil環(huán)境下的c編譯器默認沒有啟用frame_pointer機制。即沒有一個寄存器指定棧幀開始的位置,這樣就無法通過簡單的代碼實現(xiàn)調(diào)用棧的回溯。解決方法是:修改編譯選項,在編譯時添加參數(shù)“--use_frame_pointer”。這樣生成的匯編代碼會在寄存器R11中保存frame_pointer,也就可以使用簡單的代碼實現(xiàn)調(diào)用棧的回溯和輸出。由于嵌入式設(shè)備中的運行代碼中并沒有存儲調(diào)試信息,因此種方法輸出的調(diào)用棧就是地址,需要結(jié)合map文件或listing文件將其轉(zhuǎn)化為c函數(shù)名和行號。同樣也可以編寫PC軟件輔助調(diào)試信息的解析和顯示。
            2.3. 完整棧轉(zhuǎn)儲
            完整棧轉(zhuǎn)儲有比上一種調(diào)試方式更高級,使用此種調(diào)試方式時應(yīng)該在設(shè)備內(nèi)部的SPI Flash中開辟出一塊固定的存儲區(qū)域,在程序出錯時可以將全部棧數(shù)據(jù)保存進Flash中。在合適時機(下次開機時或出錯的時候直接輸出)將保存的棧輸出。這樣可以結(jié)合編譯器生成的Listing文件和map文件進行堆棧的分析。由于Listing文件中有每個函數(shù)使用棧的大小信息,因此不啟用frame_pointer也可以進行調(diào)用棧的分析,同時還能還原局部變量的數(shù)值。此種方式還有一個巨大的優(yōu)勢,對于程序跑飛的情況,可以從棧底開始正向分析調(diào)用棧,這樣在堆棧破壞不是太嚴重的情況下,能夠大致找到程序跑飛之前執(zhí)行的函數(shù),可以很大程度縮小分析跑飛問題時關(guān)注函數(shù)代碼的范圍,方便更快找到問題。
            2.4. 完整內(nèi)存轉(zhuǎn)儲
            此種方法是輔助調(diào)試的終極大招,由于嵌入式設(shè)備的內(nèi)存普遍比較小,在KB級別。因此可以在出錯時將整個內(nèi)存保存進設(shè)備內(nèi)部的SPI Flash中,在合適時進行輸出,在PC端進行分析。分析得到的數(shù)據(jù)除了上述所有內(nèi)容,還可以知道所有全局變量的數(shù)值。
            此種方法除了以上所述,一定還有更多分析使用方法,受限于我的知識范圍,當前僅能想到這些分析方法。歡迎其他同學提出更多的內(nèi)存轉(zhuǎn)儲使用方法。
            3. 進行錯誤處理的時機
            剛才在描述調(diào)試方式的時候,僅提到在“程序出錯時”進行錯誤處理。實際使用時是程序出錯的時機一般有兩個:
            各種異常處理函數(shù)中。對于非法地址指針訪問,對齊問題,權(quán)限問題,以及在程序跑飛時一般都會觸發(fā)硬件異常。因此在異常處理函數(shù)中進行錯誤處理是十分自然的。
            對于軟件死循環(huán)的情形,根據(jù)程序架構(gòu)的不同,檢測有多種情形:對于某些不開啟搶占的多任務(wù)環(huán)境,可以利用看門狗機制,單獨使用一個線程喂狗,如果有某個線程死鎖,會造成喂狗線程得不到調(diào)度,因此就可以觸發(fā)看門口中斷,在中斷中打印當前線程的調(diào)用棧即可發(fā)現(xiàn)死鎖問題。對于單線程運行的前后臺系統(tǒng),可以在每次大循環(huán)的最后進行喂狗,如果狗叫則打印堆棧也可以起到同樣的效果。對于開啟搶占的多任務(wù)環(huán)境(比如我司售飯機的情況),暫沒有想到什么方法能夠進行通用的死循環(huán)檢測。因此只能自行根據(jù)代碼邏輯在循環(huán)中增加喂狗機制和看門口配合使用上述方法發(fā)現(xiàn)死鎖。
            4. 新調(diào)試方法的運行原理
            上述文字描述了各種輔助調(diào)試方法的優(yōu)缺點和實際,最關(guān)鍵的原理問題并沒有介紹,這里我們簡單描述一下。
            4.1. 棧的作用
            棧是實現(xiàn)C語言函數(shù)調(diào)用的基石。對于每一次C語言的函數(shù)調(diào)用,匯編代碼執(zhí)行的流程基本上是這樣的:
            1、調(diào)用者將調(diào)用子函數(shù)時需要的參數(shù)放入寄存器或壓入堆棧(根據(jù)參數(shù)數(shù)量和大小而定);
            2、調(diào)用者將返回地址放入LR寄存器,然后跳轉(zhuǎn)到子函數(shù)處開始執(zhí)行。
            3、子函數(shù)在棧中備份用到的寄存器(用于退出前恢復其原內(nèi)容,包括LR和通用寄存器),并在棧中開辟空間(用于局部變量或返回值)。
            4、子函數(shù)完成自己的功能,恢復之前寄存器的數(shù)值(第三步備份的寄存器)并返回調(diào)用者。
            因此對于每一級函數(shù)調(diào)用,C語言編譯器都會在棧中生成一個固定的結(jié)構(gòu)。這個結(jié)構(gòu)就是傳說中的“棧幀”。
            4.2. 棧的結(jié)構(gòu)
            一圖勝千言,如上結(jié)構(gòu)是ARMv5的棧幀結(jié)構(gòu),對于現(xiàn)在我司常用的ARMv7 M系列而言,結(jié)構(gòu)有點不同,但是還是可以解釋如何使用棧來實現(xiàn)函數(shù)調(diào)用和參數(shù)、返回值的傳遞的。
            4.3. 關(guān)于frame pointer
            如上圖所示,在函數(shù)執(zhí)行的過程中除了SP固定指示當前的棧頂之外,還有一個FP指針,固定指定棧幀的起始位置。通過FP指針,我們就可以像遍歷鏈表一樣回溯整個調(diào)用堆棧。
            但是對于FP指針的使用,在新的v7系統(tǒng)山是可選的,且默認情況下編譯器不適用FP指針,而是根據(jù)SP寄存器間接的計算存儲在棧中數(shù)據(jù)的位置。且由于每個函數(shù)使用的寄存器數(shù)量不同,使用棧的大小不同,因此根據(jù)SP查找棧幀起始位置就必須結(jié)合匯編代碼。因此在不使用FP的情況下,要實現(xiàn)棧的回溯必須依賴對反匯編代碼的分析(自行計算每個函數(shù)中對棧的使用,然后計算下一層函數(shù)的棧幀的偏移),因此就無法在設(shè)備端直接進行了。
            啟用棧幀時針對Cortext-M4處理器,armcc生成的代碼:
            編譯器使用r11保存frame pointer,棧中保存有frame pointer。
            ;;;209 void GPIO_EnableOpenDrain(GPIO_PortEnum Port, uint32_t Pins)
            0002ae e92d4810 PUSH {r4,r11,lr}
            ;;;210 {
            0002b2 f10d0b08 ADD r11,sp,#8
            0002b6 4602 MOV r2,r0
            ;;;211 PORT_MemMapPtr PortBase;
            ;;;212 int i;
            ;;;213
            ;;;214 PortBase = g_PortBase[Port];
            0002b8 4c57 LDR r4,|L1.1048
            0002ba f8543022 LDR r3,[r4,r2,LSL #2]
            ;;;215 for (i = 0; i < 32; ++i){
            0002be 2000 MOVS r0,#0
            0002c0 e00a B |L1.728
            L1.706
            ;;;216 if (Pins & (0x01 << i)){
            0002c2 2401 MOVS r4,#1
            0002c4 4084 LSLS r4,r4,r0
            0002c6 400c ANDS r4,r4,r1
            0002c8 b12c CBZ r4,|L1.726
            ;;;217 PortBase->PCR[i] |= PORT_PDD_OPEN_DRAIN_ENABLE;
            0002ca f8534020 LDR r4,[r3,r0,LSL #2]
            0002ce f0440420 ORR r4,r4,#0x20
            0002d2 f8434020 STR r4,[r3,r0,LSL #2]
            L1.726
            0002d6 1c40 ADDS r0,r0,#1 ;215
            L1.728
            0002d8 2820 CMP r0,#0x20 ;215
            0002da dbf2 BLT |L1.706
            ;;;218 }
            ;;;219 }
            ;;;220
            ;;;221 return;
            0002dc 46dd MOV sp,r11
            0002de b082 SUB sp,sp,#8
            ;;;222 }
            0002e0 e8bd8810 POP {r4,r11,pc}
            ;;;223
            ENDP
            不啟用棧幀時針對Cortext-M4處理器,armcc生成的代碼:
            棧中僅有備份的通用寄存器和返回地址,并沒有FP。
            ;;;209 void GPIO_EnableOpenDrain(GPIO_PortEnum Port, uint32_t Pins)
            00022e b510 PUSH {r4,lr}
            ;;;210 {
            000230 4602 MOV r2,r0
            ;;;211 PORT_MemMapPtr PortBase;
            ;;;212 int i;
            ;;;213
            ;;;214 PortBase = g_PortBase[Port];
            000232 4c4e LDR r4,|L1.876
            000234 f8543022 LDR r3,[r4,r2,LSL #2]
            ;;;215 for (i = 0; i < 32; ++i){
            000238 2000 MOVS r0,#0
            00023a e00a B |L1.594
            L1.572
            ;;;216 if (Pins & (0x01 << i)){
            00023c 2401 MOVS r4,#1
            00023e 4084 LSLS r4,r4,r0
            000240 400c ANDS r4,r4,r1
            000242 b12c CBZ r4,|L1.592
            ;;;217 PortBase->PCR[i] |= PORT_PDD_OPEN_DRAIN_ENABLE;
            000244 f8534020 LDR r4,[r3,r0,LSL #2]
            000248 f0440420 ORR r4,r4,#0x20
            00024c f8434020 STR r4,[r3,r0,LSL #2]
            L1.592
            000250 1c40 ADDS r0,r0,#1 ;215
            L1.594
            000252 2820 CMP r0,#0x20 ;215
            000254 dbf2 BLT |L1.572
            ;;;218 }
            ;;;219 }
            ;;;220
            ;;;221 return;
            ;;;222 }
            000256 bd10 POP {r4,pc}
            ;;;223
            ENDP
            5. 在實際項目中的應(yīng)用情況
            當前幾種新調(diào)試方法中,第一種“出錯時打印寄存器信息”已經(jīng)在現(xiàn)有設(shè)備中得到應(yīng)用。其余調(diào)試方法,經(jīng)過初步的調(diào)研是可行的,但是項目進度和實現(xiàn)難度的綜合考量,暫沒有在實踐中投入使用。但如果項目時間允許,我們會將實驗上述集中調(diào)試方式。
            Plus,最后補充一句,如上這些調(diào)試方式在當今程序的操作系統(tǒng)上(Linux、Windows等)已經(jīng)悉數(shù)實現(xiàn),但在嵌入式設(shè)備中的應(yīng)用較少。隨著嵌入式設(shè)備性能的增強,軟件復雜度的提升,對先進調(diào)試方式的需求也會愈發(fā)強烈。


            評論


            技術(shù)專區(qū)

            關(guān)閉