性无码一区二区三区在线观看,少妇被爽到高潮在线观看,午夜精品一区二区三区,无码中文字幕人妻在线一区二区三区,无码精品国产一区二区三区免费

  • 回復(fù)
  • 收藏
  • 點贊
  • 分享
  • 發(fā)新帖
  • 論壇首頁
  • 嵌入式軟件設(shè)計中查找缺陷的幾個技巧

嵌入式軟件設(shè)計中查找缺陷的幾個技巧

結(jié)構(gòu)測試或白盒測試能有效地發(fā)現(xiàn)代碼中的邏輯、控制流、計算和數(shù)據(jù)錯誤。這項測試要求對軟件的內(nèi)部工作能夠一覽無遺(因此稱為“白盒”或“玻璃盒”),以便了解軟件結(jié)構(gòu)的詳細(xì)情況。它檢查每個條件表達(dá)式、數(shù)學(xué)操作、輸入和輸出。由于需要測試的細(xì)節(jié)眾多,結(jié)構(gòu)測試每次檢查一個軟件單元,通常為一個函數(shù)或類。

  代碼審查也使用與實現(xiàn)缺陷和潛在問題查找同樣復(fù)雜的技術(shù)。與白盒測試一樣,審查通常針對軟件的各個單元進(jìn)行,因為一個有效的審查過程要求的是集中而詳盡的檢查。

  與審查和白盒測試不同,功能測試或黑盒測試假設(shè)對軟件的實現(xiàn)一無所知,它測試由受控輸入所驅(qū)動的輸出。功能測試由測試人員或開發(fā)人員所編寫的測試過程組成,它們規(guī)定了一組特定程序輸入對應(yīng)的預(yù)期程序輸出。測試運行之后,測試人員將實際輸出與預(yù)期輸出進(jìn)行比較,查找問題。黑盒測試可以有效地找出未能實現(xiàn)的需求、接口問題、性能問題和程序最常用功能中的錯誤。

  雖然將這些技術(shù)結(jié)合起來可以找出隱藏在一個特定軟件程序中的大部分錯誤,但它們也有局限。代碼審查和白盒測試每次只針對一小部分代碼,忽視了系統(tǒng)的其它部分。黑盒測試通常將系統(tǒng)作為一個整體來處理,忽視了實現(xiàn)的細(xì)節(jié)。一些重要的問題只有在集中考察它們在整個系統(tǒng)內(nèi)相互作用時的細(xì)節(jié)才能被發(fā)現(xiàn);傳統(tǒng)的方法無法可靠地找出這些問題。必須整體地檢查軟件系統(tǒng),查找具體問題的特定原因。由于詳盡徹底地分析程序中的每個細(xì)節(jié)和它與代碼中所有其它部分之間的相互作用通常是不大可能的,因此分析應(yīng)該針對程序中已經(jīng)知道可能導(dǎo)致問題的特定方面。本文將探討其中三個潛在的問題領(lǐng)域:

  * 堆棧溢出

  * 競爭條件

  * 死鎖

  讀者可在網(wǎng)上閱讀本文的第二部分,它將探討下列問題:

  * 時序問題

  * 可重入條件

  在采用多任務(wù)實時設(shè)計技術(shù)的系統(tǒng)中,以上所有問題都相當(dāng)普遍。

  處理器使用堆棧來存儲臨時變量、向被調(diào)函數(shù)傳遞參數(shù)、保存線程“狀態(tài)”,等等。如果系統(tǒng)不使用虛擬內(nèi)存(換句話說,它不能將內(nèi)存頁面轉(zhuǎn)移到磁盤上以釋放內(nèi)存空間供其它用途),堆棧將固定為產(chǎn)品出廠時的大小。如果由于某種原因堆棧越出了編程人員所分配的數(shù)量范圍,程序?qū)⒆兊貌淮_定。這種不穩(wěn)定可能導(dǎo)致系統(tǒng)發(fā)生嚴(yán)重故障。因此,確保系統(tǒng)在最壞情況下能夠分配到足夠的堆棧至關(guān)重要。

  確保永不發(fā)生堆棧溢出的唯一途徑就是分析代碼,確定程序在各種可能情況下的最大堆棧用量,然后檢查是否分配了足夠的堆棧。測試不大可能觸發(fā)特定的瞬時輸入組合進(jìn)而導(dǎo)致系統(tǒng)出現(xiàn)最壞情況。

  堆棧深度分析的概念比較簡單:

  1. 為每個獨立的線程建立一棵調(diào)用樹。

  2. 確定調(diào)用樹中每個函數(shù)的堆棧用量。

  3. 檢查每棵調(diào)用樹,確定從樹根到外部“樹葉”的哪條調(diào)用路徑需要使用的堆棧最多。

  4. 將每個獨立線程調(diào)用樹的最大堆棧用量相加。

  5. 確定每個中斷優(yōu)先級內(nèi)各中斷服務(wù)程序(ISR)的最大堆棧用量并計算其總和。但是,如果ISR本身沒有堆棧而使用被中斷線程的堆棧,則應(yīng)將ISR使用的最大堆棧數(shù)加到各線程堆棧之上。

  6. 對于每個優(yōu)先級,加上中斷發(fā)生時用來保存處理器狀態(tài)的堆棧數(shù)。

  7.如果使用RTOS,則加上RTOS自身內(nèi)部用途需要的最大堆棧數(shù)(與應(yīng)用代碼引發(fā)的系統(tǒng)調(diào)用不同,后者已包含在步驟2中)。

  除此之外,還有兩個重要事項需要考慮。首先,僅僅從高級語言源代碼建立的調(diào)用樹很可能并不完善。大部分編譯器采用運行時庫(run-time library)來優(yōu)化常用計算任務(wù),如大值整數(shù)的乘除、浮點運算等,這些調(diào)用只在編譯器產(chǎn)生的匯編語言中才可見。運行時庫函數(shù)本身可能使用大量的堆??臻g,在分析時必須將它們包括進(jìn)去。如果使用的是C++語言,則以下所有類型的函數(shù)(方法)也都必須包含到調(diào)用樹內(nèi):結(jié)構(gòu)器、析構(gòu)器、重載運算符、復(fù)制結(jié)構(gòu)器和轉(zhuǎn)換函數(shù)。所有的函數(shù)指針也都必須進(jìn)行解析,并且將它們調(diào)用的函數(shù)包含進(jìn)分析之中。

第二,編譯器使用一個C庫來實現(xiàn)memcpy()、cos()和atof ()等標(biāo)準(zhǔn)函數(shù),而這些例程的源代碼可能無法得到。如果能夠得到它們的源代碼,就有可能確定程序用到的每個庫調(diào)用在最壞情況下的堆棧使用數(shù)量。如果這些庫只包含在目標(biāo)文件中,則編譯器廠商必須提供每個庫例程使用的堆棧數(shù)。如果沒有這些信息,就無法通過分析來確定最壞情況下程序使用的最大堆棧數(shù)。幸運的是,許多面向嵌入式系統(tǒng)的編譯器廠商都提供這些信息。

  通常,每次一個函數(shù)被調(diào)用時,編譯器將使用堆棧來保存返回地址并傳遞函數(shù)參數(shù)。函數(shù)的自動(局部)變量通常也在堆棧當(dāng)中。不過,由于編譯器會盡可能通過將參數(shù)或局部變量放入寄存器來優(yōu)化代碼,因此檢查匯編語言以精確地確定堆棧用量非常重要。編譯器也有可能在代碼中的其它地方選擇使用堆棧,如用堆棧來保存中間計算結(jié)果。

  有些與編譯器一起打包銷售的開發(fā)環(huán)境包含生成調(diào)用樹的工具,還有許多第三方的調(diào)用樹生成工具。但是,除非它們能夠?qū)R編語言進(jìn)行分析,否則這些工具可能會遺漏運行時庫和C庫的調(diào)用。不過無論在哪種情況下,開發(fā)分析匯編語言文件并提取函數(shù)名稱以及各函數(shù)內(nèi)部調(diào)用的腳本都比較簡單。分析的結(jié)果可寫入一個文件,而這個文件能夠方便地輸入到表格之中。

  確定了各個函數(shù)的堆棧用量之后,必須計算每個線程所需的最大堆棧數(shù)。由于一般程序通常涉及數(shù)百個函數(shù),調(diào)用跨越多層深度,處理這些信息的一種簡便方法就是采用分析表格。如表1所示,表格的各行包含了函數(shù)名稱、該函數(shù)使用的最大堆棧數(shù)(包括調(diào)用其它函數(shù)所需的堆棧數(shù)),以及它調(diào)用的所有函數(shù)的清單。通過編程控制,這個表格從每個函數(shù)的“根”開始迭代循環(huán),計算該函數(shù)及其調(diào)用的所有函數(shù)需要的堆棧。這些信息存放在堆棧路徑列中,這樣,采用每個線程根函數(shù)(如main)的堆棧路徑數(shù)據(jù)就可以方便地計算出需要的最大堆棧數(shù)了。這個過程包含了先前介紹的堆棧分析過程中的前四個步驟。

  有時候,采用堆棧深度分析過程可能是無法做到,或者是不實際的。如果無法得到運行時庫或C庫的源代碼,而編譯器廠商又沒有提供任何堆棧使用信息,就不可能進(jìn)行完整的堆棧分析。在這種情況下,有兩種選擇:

  1. 在測試期間,觀察堆棧所能達(dá)到的深度,并保證有較大的堆??臻g余量。

  2. 檢測堆棧溢出,并采取改進(jìn)措施。

  觀察堆棧深度的方法很簡單:

  * 向整個內(nèi)存堆棧區(qū)寫入一個特定的數(shù)據(jù)圖案符號,如55AA。

  * 在預(yù)期使用最大堆??臻g的條件下運行系統(tǒng)。

  * 使用仿真器或其它工具檢查堆棧存儲區(qū),看有多少符號圖案由于堆棧的使用而被改寫了。

  當(dāng)然,這些步驟并不能保證在一些不同條件下不會需要更多的堆棧,但確實可以表明所需要的最小堆棧數(shù)。

  使用帶內(nèi)存管理單元(MMU)的處理器時,有可能檢測出運行時的堆棧溢出現(xiàn)象。MMU將內(nèi)存劃分為多個區(qū)域,用一個受保護(hù)的內(nèi)存段來“警戒”堆棧區(qū)域。發(fā)生堆棧溢出時,處理器將訪問這個受保護(hù)段。這個操作將引發(fā)一個異常事件(如產(chǎn)生SIGSEGV信號),可被程序捕獲到。創(chuàng)建線程時,與實時POSIX標(biāo)準(zhǔn)兼容的RTOS提供有這種堆棧警戒功能選項,大大簡化了編程人員的工作。GNU工具等其它開發(fā)環(huán)境包含有編譯器開關(guān),可在程序中添加實現(xiàn)堆棧警戒功能所需的代碼,但它們?nèi)匀灰揽康讓硬僮飨到y(tǒng)來有效地處理堆棧溢出。但是,按照這種方式檢測溢出還只是問題的一部分。為了使這類設(shè)計更為有效,系統(tǒng)必須能夠從堆棧溢出中恢復(fù)過來并繼續(xù)正確地工作。

  在一個對安全或任務(wù)要求嚴(yán)格的應(yīng)用中,系統(tǒng)運行時在測試或檢測堆棧溢出期間監(jiān)視堆棧的深度可能并不是一項足夠的風(fēng)險控制措施。對于一些應(yīng)用,必須確保系統(tǒng)絕對不會越出所分配的堆棧范圍;只有通過完整的堆棧深度分析才能證明這一點。這意味著,如果整個程序在同一內(nèi)存空間運行,則必須對所有代碼執(zhí)行這項分析。不過,如果使用MMU,分析??珊喕T谠O(shè)計系統(tǒng)時,可將所有關(guān)鍵代碼置于一個或多個獨立線程內(nèi),而這些線程分別在各自的保護(hù)內(nèi)存段中運行。這樣,只要對這些關(guān)鍵線程進(jìn)行堆棧使用分析就可以了。當(dāng)然,這項簡化設(shè)計假定當(dāng)非關(guān)鍵線程溢出其堆棧并失效時,關(guān)鍵線程仍可正確執(zhí)行。

  由于分析工作所需的堆棧使用數(shù)據(jù)來自匯編語言清單,因此修改代碼時,相應(yīng)模塊的堆棧使用信息必須予以更新。如果使用不同的編譯器版本,或者改變了優(yōu)化設(shè)置,也必須復(fù)核整個分析過程。在理想情況下,編譯器將提供每個函數(shù)(如果不是每個線程的話)的堆棧使用數(shù)量,因為它擁有計算需要的所有信息。例如,瑞薩公司提供有Call Walker,這是該公司高性能的Embedded Workshop開發(fā)環(huán)境的一部分。這個工具可以圖形化地顯示每個函數(shù)使用的調(diào)用樹和堆棧,包括運行時庫和C庫的函數(shù)。Call Walker也能找出使用堆棧數(shù)量最大的路徑。使用這樣的工具可以實現(xiàn)步驟1到步驟3的自動化。

全部回復(fù)(0)
正序查看
倒序查看
現(xiàn)在還沒有回復(fù)呢,說說你的想法
發(fā)