版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
嵌入式軟件調(diào)試、測試、問題定位
經(jīng)驗分享AEBELL陸燦2014-11-18內(nèi)容:嵌入式軟件開發(fā)概述常見問題及分析實例分析總結(jié)
嵌入式軟件開發(fā),是指針對嵌入式硬件平臺帶嵌入式操作系統(tǒng)的軟件開發(fā),硬件平臺包括:單片機、ARM、PowerPC、DSP、MIPS等;主流嵌入式操作系統(tǒng)有:嵌入式linux、VxWorks、wince等。廣義的嵌入式軟件開發(fā)還包括單片機軟件開發(fā)。
嵌入式軟件開發(fā)根據(jù)不同的硬件平臺、不同的操作系統(tǒng)、不同的開發(fā)環(huán)節(jié)有著多個的方向,如不帶操作系統(tǒng)的前后臺程序開發(fā);帶操作系統(tǒng)的可以有bootloader開發(fā)、內(nèi)核開發(fā)、驅(qū)動開發(fā)、應(yīng)用開發(fā)等。
相對于強大的PC,嵌入式硬件平臺資源非常有限,低端的CPU其片內(nèi)ram一般為K級至100K級,ROM為10K級至1M級,如單片機、ARM7、Cortex-M3。高端的CPU使用片外ram、rom,能達(dá)到G級,如A8、A9處理器。
由于嵌入式硬件資源的限制,嵌入式軟件不能直接在板子上開發(fā),而是以通用平臺(PC)作為宿主機,板子作為目標(biāo)機,在PC上進行代碼編輯,用交叉編譯器生成目標(biāo)碼,將目標(biāo)碼下載到板子中去才能運行。常見問題及分析錯用運算符運算符優(yōu)先級歧義大小端倒置內(nèi)存越界內(nèi)存泄露任務(wù)優(yōu)先級問題常見問題:
錯用運算符 1)條件判定“==”和賦值“=”: if(i=j) { … } 2)邏輯與&&、或||和位運算與&、或|。 if(a&b) { … }2.運算優(yōu)先級歧義
如果代碼行中運算符比較多,如:if(a|b&&a&c);看起來比較艱澀,搞不好容易出錯。要熟記所有運算符優(yōu)先級比較困難。為防止歧義并提高可讀性,應(yīng)當(dāng)用括號確定表達(dá)式的操作順序,如上式可寫為:if((a|b)&&(a&c));
運算符的優(yōu)先級與結(jié)合律3.大小端倒置
大端,是指數(shù)據(jù)的高位,保存在內(nèi)存的低地址中,而數(shù)據(jù)的低
位,保存在內(nèi)存的高地址中;小端與大端相反。
例如0x11223344,
在大端系統(tǒng)的內(nèi)存中如下:
在小端系統(tǒng)中是相反順序:
…11223344……44332211…內(nèi)存增長方向系統(tǒng)與外界交互數(shù)據(jù)時通常會涉及到大小端問題,例如上位機(PC)將某歌曲信息傳給下位機(設(shè)備),歌曲信息如下,規(guī)定傳輸高位在前,低位在后(大端方式)。
設(shè)備一般會定義相應(yīng)結(jié)構(gòu)體:typedefstruct{ chartype;//分類 shortrate;//采樣率 intlen;//長度}SONG_INFO,*pSONG_INFO;inthandle_song_info(uchar*buf){chartype;shortrate;intlen;pSONG_INFOpsong_info;psong_info=(pSONG_INFO)buf;type=psong_info->type;rate=psong_info->rate;len=psong_info->len;……}buf是接收PC數(shù)據(jù)的緩沖區(qū),假設(shè)接收到數(shù)據(jù)在buf中如下:在大端的CPU得到正確結(jié)果:rate:0x2233,len:0x44556677在小端的CPU得到的是:rate:0x3322,len:0x77665544顯然與協(xié)議規(guī)定的不一致,要做如下大小端轉(zhuǎn)換:rate=htons(psong_info->rate);len=htonl(psong_info->len);單字節(jié)不用考慮大小端問題,如例中的type變量。11223344556677常見問題:4.內(nèi)存越界
內(nèi)存越界難以被發(fā)現(xiàn),往往會導(dǎo)致離奇古怪的問題,如一些變量值無故被修改、程序跑飛、系統(tǒng)重啟等。
內(nèi)存分配有3種:棧內(nèi)存、堆內(nèi)存和靜態(tài)內(nèi)存(全局變量、靜態(tài)變量),相應(yīng)內(nèi)存越界有3種情況:棧內(nèi)存越界、堆內(nèi)存越界、靜態(tài)內(nèi)存越界。
棧內(nèi)存堆內(nèi)存靜態(tài)內(nèi)存 1)棧內(nèi)存越界
深入了解棧,對提高程序的效率、程序調(diào)試、問題的定位都有很大的幫助。棧實際就是一塊連續(xù)內(nèi)存,用于開辟局部變量、傳遞參數(shù)、進入子程序前保存現(xiàn)場,入棧是從高地址向低地址增長。CPU內(nèi)部有一個寄存器作為棧指針SP,指向當(dāng)前棧地址。開始時SP指向棧頂,也就是??臻g的最高地址處。每當(dāng)入棧n字節(jié)數(shù)據(jù),硬件自動將SP減n,出棧時SP自動加n,棧后進先出?!璖P棧空間高地址低地址2023/2/3Socket模型介紹CPU內(nèi)部有一組寄存器R0~R15,其中:R13:就是棧指針SP。R15:為程序計數(shù)寄存器PC,指向當(dāng)前執(zhí)行指令的地址。R14:為連接寄存器LR,在調(diào)用子程序時,由R14保存返回地址。R0~R12:為通用寄存器,用于數(shù)據(jù)操作。內(nèi)存中的數(shù)據(jù)不能在內(nèi)存中作運算,只能先加載到通用寄存器中,在寄存器里完成運算,結(jié)果再存回內(nèi)存。例如:buf[10]++;要先將buf[10]的值加載到寄存器,寄存器作自加,結(jié)果再存回buf[10]。雖然只有一個語句,但并非原子操作,需要執(zhí)行多個指令。這就是在臨界區(qū)有時一個語句都需要加鎖的原因。棧內(nèi)存越界分析:intfun1(void){ … fun2(a,b,c,d,e,f); …}intfun2(inta,intb,intc,intd,inte,intf){ inti; charbuf1[10]; charbuf2[10]; charbuf3[10]; memset(buf2,0,100); … return0;}分析一下函數(shù)fun1調(diào)用子函數(shù)fun2后會產(chǎn)生什么后果intfun1(void){ … fun2(a,b,c,d,e,f); … R0R1R2R3}intfun2(inta,intb,intc,intd,inte,intf){ inti; charbuf1[10]; charbuf2[10]; charbuf3[10]; … memset(buf2,0,100); … return0;}??臻g高地址低地址……fePCRxbuf1buf2buf3……前4個參數(shù)通過R0~R4傳遞超過4個參數(shù)通過棧傳遞進入子函數(shù)PC先入棧調(diào)用前SPbuf2執(zhí)行清空操作越界了,誰先遭殃,有什么后果???intfun2(inta,intb,intc,intd,inte,intf){ inti; charbuf1[10]; charbuf2[10]; charbuf3[10]; … memset(buf2,0,100); … return0;}不要以為buf2越界是向buf3越界,看??臻g布局,buf2后面(高地址)緊跟的是buf1,執(zhí)行memset(buf2,0,100);把buf2地址起后面的100字節(jié)??臻g都清0,包括buf1和入棧的Rx、PC值,當(dāng)子函數(shù)返回時從棧中彈出返回地址給PC,這時PC值為0,程序跳到絕對地址為0處執(zhí)行,程序跑飛!高地址低地址……fePCRxbuf1buf2buf3……SP高地址100字節(jié)清02)堆內(nèi)存越界:
堆空間實際也是一塊連續(xù)內(nèi)存,內(nèi)存分配函數(shù)以鏈表方式將分配的內(nèi)存塊手拉手鏈接起來,因而每塊內(nèi)存都有個鏈表節(jié)點(如下圖)。每次調(diào)用內(nèi)存分配函數(shù),遍歷鏈表,找到空閑的空間足夠的內(nèi)存塊則返回給調(diào)用程序。如果某塊內(nèi)存越界就會把下一塊內(nèi)存的鏈表節(jié)點給沖掉,鏈表指針“指飛”,堆空間崩潰。prenextprenextprenext…堆內(nèi)存結(jié)構(gòu)示意圖3)靜態(tài)內(nèi)存越界:charbuf1[4];charbuf2[4];inta;intb;intmain(void){ … memset(buf2,0,100); …}memset(buf2,0,100);操作使buf2越界,從右邊內(nèi)存布局圖可以看到,變量a、b被沖掉。當(dāng)測試發(fā)現(xiàn)一些全局變量值莫名變掉,首先查看它附近是否有內(nèi)存越界。高地址低地址……babuf2buf1……高地址0x2000000c0x200000080x200000040x20000000內(nèi)存越界總結(jié):
前面例子是為了說明問題明顯制造的內(nèi)存越界,實際編程中不會這么明顯,很多時候是子函數(shù)對傳入的指針操作引起。intfun(char*buf){ inti; charstr[100]; … … strcpy(buf,str); sprintf(buf,“%s%d”,str,i); …}在調(diào)試、測試中如出現(xiàn)變量無故被修改、設(shè)備無故重啟、程序跑分現(xiàn)象,第一反應(yīng)應(yīng)該是內(nèi)存越界。5.內(nèi)存泄露
內(nèi)存泄露就是申請的內(nèi)存在不使用后沒有釋放掉。如果系統(tǒng)在運行過程中不斷的申請內(nèi)存,用完又沒釋放,勢必造成內(nèi)存耗盡而無法工作。指針被修改:intfun(void){ charstr[10]; char*p;
p=(char*)malloc(128); …
p=str; … free(p);}漏掉釋放:intfun(void){ … char*p; p=(char*)malloc(128); … if(xx){
return0; } … free(p);}6.任務(wù)優(yōu)先級問題
對于搶占式操作系統(tǒng),任務(wù)的優(yōu)先級不同,相同的操作有不同的表現(xiàn),例如uc_os系統(tǒng),每個任務(wù)的優(yōu)先級都不同,高優(yōu)先級的任務(wù)就緒后就搶占低優(yōu)先級任務(wù)獲得運行。如下假設(shè)系統(tǒng)中有兩個任務(wù)task1和task2,在創(chuàng)建任務(wù)時指定的優(yōu)先級不同,得到的執(zhí)行結(jié)果就不一樣。OS_EVENT*pevent;inta;void*task1(void*parg){ … while(1) {a=0;OSSemPost(pevent);printf(“a=%d\r\n”,a);OSTimeDly(1000); }}void*task2(void*parg){ … while(1) {a=0;OSSemPend(pevent);a=10;OSTimeDly(500); }}OS_EVENT*pevent;inta;void*task1(void*parg){ … while(1) {a=0;OSSemPost(pevent);
printf(“a=%d\r\n”,a);
OSTimeDly(1000); … }}從上面執(zhí)行軌跡可以看出,執(zhí)行結(jié)果為:a=0void*task2(void*parg){ … while(1) {a=0;OSSemPend(pevent);
a=10;
OSTimeDly(500); }}如果task1優(yōu)先級比task2高,執(zhí)行流程如下:task1休眠,主動放棄CPU,task2將獲得CPU運行task1拋出信號,OSSemPost系統(tǒng)調(diào)用發(fā)現(xiàn)task2在等待該信號但優(yōu)先級低,不切換任務(wù),繼續(xù)執(zhí)行task1。OS_EVENT*pevent;inta;void*task1(void*parg){ … while(1) {a=0;OSSemPost(pevent);
printf(“a=%d\r\n”,a);
OSTimeDly(1000); … }}從上面執(zhí)行軌跡可以看出,a被task2賦值10,所以執(zhí)行結(jié)果為:a=10void*task2(void*parg){ … while(1) {a=0;OSSemPend(pevent);
a=10;
OSTimeDly(500); }}如果task2優(yōu)先級比task1高,執(zhí)行流程如下:task2得到信號資源搶占CPU獲得運行task1拋出信號,OSSemPost系統(tǒng)調(diào)用發(fā)現(xiàn)task2在等待該信號并且優(yōu)先級高,切換到task2運行task2休眠,主動放棄CPU,task1將獲得CPU運行OSSemPost(pevent)系統(tǒng)調(diào)用流程至此任務(wù)暫停運行軟中斷中實現(xiàn)任務(wù)切換主要操作是:1.保存當(dāng)前任務(wù)的環(huán)境(R0~R15、xPSR)到該任務(wù)的棧;2.將前面找到的新任務(wù)OSPrioHighRdy賦給當(dāng)前任務(wù)OSPrioCur變量,新任務(wù)成為當(dāng)前任務(wù);3.從新任務(wù)的棧中彈出其之前的運行環(huán)境。至此,任務(wù)環(huán)境切換完畢,中斷子程序退出后新任務(wù)就得以運行,實現(xiàn)了任務(wù)切換?!璓CR0~R3R4~R11SP……PCR0~R3R4~R11SP……當(dāng)前任務(wù)環(huán)境入棧新任務(wù)環(huán)境出棧OSPrioCur=OSPrioHighRdy當(dāng)前任務(wù)棧空間新任務(wù)??臻g123實例分析丟包問題死機問題爆音問題
1.丟包問題現(xiàn)象:IP廣播設(shè)備連續(xù)播放10分鐘到半小時內(nèi)出現(xiàn)不可自動回復(fù)的丟包,大約每5隔5秒丟100包,造成音頻播放嚴(yán)重卡頓。解決過程:首先問題定位以縮小查找范圍,將問題出處設(shè)定在:服務(wù)器、設(shè)備端應(yīng)用層代碼、tcp/ip協(xié)議棧。觀察設(shè)備端應(yīng)用層代碼和telnet打印信息,發(fā)現(xiàn)應(yīng)用層從協(xié)議接收到的數(shù)據(jù)包都沒有丟失,基本排除應(yīng)用層;服務(wù)器日志顯示確實有數(shù)據(jù)包發(fā)不出,范圍再次縮?。菏欠?wù)器問題導(dǎo)致發(fā)不出還是設(shè)備端接收有問題。懷疑可能設(shè)備端協(xié)議棧分配不到內(nèi)存導(dǎo)致接收不了數(shù)據(jù),調(diào)試發(fā)現(xiàn)確實出現(xiàn)分配內(nèi)存失敗。后來開啟協(xié)議棧的打印信息,發(fā)現(xiàn)協(xié)議棧的內(nèi)存池分配失敗。查看協(xié)議棧代碼得知分配內(nèi)存有2種方式:內(nèi)存池和內(nèi)存堆,原來默認(rèn)的是內(nèi)存池方式。后來嘗試改成內(nèi)存堆方式,不再丟包。tcp/ip協(xié)議棧底層接收數(shù)據(jù)時申請緩沖內(nèi)存,程序片段:if(len>0){
//p=pbuf_alloc(PBUF_RAW,len,PBUF_POOL); p=pbuf_alloc(PBUF_RAW,len,PBUF_RAM); /*Copyreceivedframefromethernetdriverbuffertostackbuffer*/ if(p!=NULL) { for(q=p;q!=NULL;q=q->next) { memcpy((u8_t*)q->payload,(u8_t*)&buffer[l],q->len); l=l+q->len; } }}
原來的內(nèi)存池方式改后的內(nèi)存堆方式2.死機問題現(xiàn)象:IP廣播設(shè)備連續(xù)播放4~10小時不等,基本出現(xiàn)與服務(wù)器失聯(lián),CPU指示燈不閃,telnet不通,ping不通。解決過程:這種問題一般是程序進入了某個死循環(huán)出不來,非常難定位,即使把全部代碼都仔細(xì)去看一遍也難以看出來,況且開源的tcp/ip協(xié)議棧代碼不大可能去通讀。這時最好的方法就是硬件調(diào)試,讓調(diào)試器一直開著,直到出現(xiàn)問題,暫停運行看程序停在哪個代碼段,那里一般就是死循環(huán)的地方。while(pcb!=NULL){ if(pcb->last_timer!=tcp_timer_ctr){ structtcp_pcb*next; pcb->last_timer=tcp_timer_ctr; /*senddelayedACKs*/ if(pcb->flags&TF_ACK_DELAY){ LWIP_DEBUGF(TCP_DEBUG,("delayedACK\n")); tcp_ack_now(pcb); tcp_output(pcb); pcb->flags&=~(TF_ACK_DELAY|TF_ACK_NOW); } next=pcb->next; …… pcb=next; } else{ pcb=pcb->next; }}當(dāng)這個條件不成立時,這段程序就死循環(huán)了修正,if條件不成立時使pcb指向鏈表下一節(jié)點3.爆音問題現(xiàn)象:IP廣播設(shè)備在播放,突然音量自動變得非常大,出現(xiàn)很隨機。解決過程:首先查看所有修改音量的代碼,沒發(fā)現(xiàn)異常。懷疑是不是服務(wù)器發(fā)指令修改音量,修改音量telnet會打印相關(guān)信息,爆音時telnet沒有打印修改音量信息,排除掉服務(wù)器引起。偶爾觀察到爆音時打?。骸敖邮諗?shù)據(jù)xx,有效數(shù)據(jù)xxx”。搜索代碼出現(xiàn)在接收音頻數(shù)據(jù)的代碼段如下:
AvaDataSize=GetAudioDataAvaSpaD
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024年度大產(chǎn)權(quán)房屋買賣合同范本:二手房交易稅費減免政策3篇
- 2024年度事業(yè)單位附屬食堂租賃服務(wù)協(xié)議3篇
- 2024年地產(chǎn)項目廣告合作效果跟蹤與優(yōu)化合同3篇
- 2024年標(biāo)準(zhǔn)夫妻分居合同模板版B版
- 2024年度特種貨物運輸安全生產(chǎn)管理合同3篇
- 2024年度養(yǎng)殖類企業(yè)養(yǎng)殖廢棄物資源化利用項目投資合同3篇
- 2024年度環(huán)保型地磅采購及售后服務(wù)協(xié)議3篇
- 2024年度人工智能輔助職業(yè)規(guī)劃服務(wù)勞動合同3篇
- 2024年度文化設(shè)備租賃及活動支持合同3篇
- 2024年度國際貿(mào)易擔(dān)保合同范本6篇
- 露天臺階爆破設(shè)計
- 中式婚禮PPT幻燈片課件
- 初中生作文批改評語
- 淮海工學(xué)院數(shù)據(jù)庫原理與技術(shù)復(fù)習(xí)題及答案
- 建龍資料備案正版表格
- 解碼萬達(dá)商管運作架構(gòu):組織結(jié)構(gòu)圖、各部管理職責(zé)
- 走進無字書圖書館(課堂PPT)
- 網(wǎng)絡(luò)安全運維培訓(xùn)測試題
- 民政部主管社團管理辦法
- 內(nèi)蒙古自治區(qū)重大建設(shè)項目檔案驗收實施細(xì)則
- 四方合作協(xié)議合同書(共4頁)
評論
0/150
提交評論