引子
前一篇文章介紹了堆棧和內存的一些背景知識。本次介紹如何使用 Polyspace Code Prover來統計堆棧,如何使用這些數據為軟件優化服務。
Polyspace Code Prover 堆棧統計
前面三類堆棧統計工具,在統計堆棧使用的時候,其需要編譯并運行代碼,這意味著其需要定義測試激勵,這種情況的堆棧統計是特定測試激勵的,其他時候的堆棧大小則需要定義合適的測試激勵。而要統計軟件的最大堆棧需求,則需要設計合適的測試用例。這無疑對測試用例有比較高的要求。
形式化工具 Polyspace Code Prover 使用抽象解釋法,能夠深入探測到每一層函數的調用,統計每個函數本身的局部變量消耗和因為函數調用需要的棧消耗。另外,由于形式化的方法的使用,Polyspace Code Prover 能夠分析代碼中的分支是否因為上下文的原因不可達。這也會影響到實際程序中堆棧的大小。
Polyspace Code Prover能夠統計以下信息:
最大棧使用量
最小棧使用量
程序最大棧使用量
程序最小棧使用量
最大局部變量使用量
最小局部變量使用量
其中局部變量使用量統計包括本函數的局部變量,函數的參數和返回值的開銷,以及因為內存對齊導致的額外開銷等,而棧使用量則包括局部變量使用量以及函數需要調用其他函數導致的開銷。
程序的最大最小堆棧使用。當程序中有main函數或者其他的入口函數,Polyspace 可以統計主入口函數的總資源,其包括了調用其他函數需要的資源。
Polyspace 能夠提供函數的調用關系圖,據此可以看到一個函數的入口占用的資源是由于其調用了哪些函數帶來的。
如上圖我們知道入口函數 ps_main 調用了 SysTick_Handler 函數,也就是堆棧使用量 31 是 SysTick_Handler 調用引發的。
轉到 SysTick_Handler 也能看到的確如此。
更復雜的在 scheduler_executive 調用,從下面調用圖看到,其調用了多個函數,而虛的三角型則代表是通過函數指針這類方式進行非顯式調用的。
那么如何知道各個調用的函數的資源開銷呢,在上圖點擊轉到定義,然后可以立刻查看該函數的堆棧使用
從上面幾個圖可以很明顯的看到,update_shared_variables 占用了最多的資源。隨后我們可以繼續往下跟蹤。
何時執行堆棧分析?
執行堆棧分析是軟件開發生命周期中的一個連續過程。如果僅在軟件開發生命周期結束時由單獨的質量評估團隊估計堆棧使用量,則可能會使整個開發工作面臨風險。此外,在開發周期的后期解決問題可能會出錯且耗時;在確定是更改硬件還是軟件設計時,這種做法可能還會造成混亂。
執行堆棧分析的最佳時點是:
在添加新功能時
在軟件中每添加一項新功能,都會使堆棧使用量增加。開發人員必須密切關注新功能的堆棧使用情況。
執行堆棧分析、進行調試和修復復雜代碼:在每個主要功能實現后,開發人員可以在本地對特定軟件組件或軟件模塊應用靜態分析器,以評估基礎軟件和已實現軟件之間堆棧使用量的增加情況。
在整個開發過程中監控堆棧分析:QA 團隊和產品負責人可以使用靜態分析器對持續集成 (CI) 管道進行堆棧估計,以在控制板上顯示結果。此過程有助于在軟件開發生命周期中跟蹤堆棧分析。
執行良好實踐以確保堆棧使用量最低:質量門有助于避免違反 MISRA 和 AUTOSAR 編碼規范。這些規范要求強制有條件地使用動態內存分配。
在軟件發布前
靜態分析器執行的堆棧估計提供了有力的證據,表明堆棧使用量處于控制之中。在每次軟件發布之前,都應在標準工作負載、最小負載和最大負載下,對真實目標運行堆棧分析,以全面了解堆棧的使用情況。驗證堆棧上溢和下溢事件的故障安全例程也至關重要。
根據堆棧統計進行資源使用優化
有了基礎的統計信息,結合 Polyspace 提供的函數調用圖,可以了解到在哪個位置,導致某個分支的堆棧使用量大增。
通過函數調用圖,我們知道整個程序的情況:
我們可以定位相對消耗較大的模塊,然后根據上述調用圖,定位到其調用的模塊,查看堆棧資源的消耗。
比如:
再到具體的函數中,查看其局部變量的消耗。
執行良好實踐以確保堆棧使用量最低
對于產品級代碼,推薦遵循行業的編碼規范,如 MISRA C、MISRA C++、AUTOSAR C++ 等。這些編碼標準要求強制禁止動態內存分配,并推薦特定用例來優化靜態內存分配。Polyspace Bug Finder 有助于識別任何違反最佳實踐的行為,開發人員可以在本地監控這些行為,而產品負責人可通過 Polyspace Access 監控這些行為。以下編碼規則詳細說明了靜態內存分配的最佳實踐。靜態內存分配可以使用 Polyspace Bug Finder 進行分析。
以下是常見的編碼規范中關于內存方面的部分。
編碼規范 | 規則 | 描述 |
---|---|---|
MISRA C:2004 | 20.4 | 不能使用動態堆內存分配。 |
MISRA C:2012 | 21.3 | 不能使用 的內存分配和取消分配函數。 |
MISRA C++:2008 | 18-4-1 | 不能使用動態堆內存分配。 |
AUTOSAR C++14 | A18-5-1 | 不能使用函數 malloc、calloc、realloc 和 free。 |
AUTOSAR C++14 | A18-5-2 | 不能使用非定位 new 或 delete 表達式。 |
AUTOSAR C++14 | A18-5-3 | delete 表達式的形式應與用于分配內存的 new 表達式的形式保持一致。 |
AUTOSAR C++14 | A18-5-4 | 如果為某個項目全局定義了運算符“delete”的有大小或無大小版本,則應同時定義有大小和無大小版本。 |
AUTOSAR C++14 | A18-5-5 | 內存管理函數應確保以下各項:(a) 行為是確定的,能夠預測出在最差情形下的執行時間,(b) 避免內存碎片化,(c) 避免運行時出現內存不足,(d) 避免不匹配的分配或取消分配,以及 (e) 不依賴對內核的非確定性調用。 |
AUTOSAR C++14 | A18-5-7 | 如果項目中使用動態內存管理函數的非實時實現,則只應在非實時程序階段分配和取消分配內存。 |
AUTOSAR C++14 | A18-5-8 | 存活期不超過函數的對象應具有自動存儲期。 |
AUTOSAR C++14 | A18-5-9 | 動態內存分配和取消分配函數的自定義實現應滿足 C++ 標準中相應“必要行為”條款中指定的語義要求。 |
AUTOSAR C++14 | A18-5-10 | 定位 new 運算符只能與對齊正確且指向足夠存儲容量的指針結合使用。 |
AUTOSAR C++14 | A18-5-11 | 運算符“new”和運算符“delete”應一起定義。 |
Polyspace 提供常見內存問題的檢查
前一篇文章列舉的一些缺陷,Polyspace Bug Finder 和 Polyspace Code Prover 提供了許多針對靜態和動態內存分配的運行時檢查。解決所有高、中和低優先級缺陷有助于降低內存分配帶來的風險。
Polyspace Bug Finder 關于內存方面的檢查項
此外,Polyspace Code Prover提供內存方面的形式化證明,包括
指針類型轉換錯誤
數組越界
結構體指針越界
空指針或零地址解引用
對空指針偏移操作
位字段類型錯誤
malloc 返回值未檢查是否為 NULL
聯合體指針內存分配不足
結構體部分內存分配
結構體字段指針錯誤
函數返回局部變量指針
使用了已釋放的內存無論使用何種方法來計算堆棧使用量,稍微增大堆棧大小都不失為一個好辦法。這種方法有助于避免測試期間可能未檢測到的堆棧溢出導致的系統漏洞。
堆棧溢出漏洞是許多嵌入式應用程序在實際運行中表現出不可定義行為的一個重要原因。在正確的時間使用正確的工具并遵循最佳實踐,可以增強對軟件防止堆棧溢出的信心。
-
程序
+關注
關注
117文章
3787瀏覽量
81043 -
函數
+關注
關注
3文章
4331瀏覽量
62618 -
堆棧
+關注
關注
0文章
182瀏覽量
19761
原文標題:堆棧知多少(二)如何進行堆棧統計和優化?
文章出處:【微信號:MATLAB,微信公眾號:MATLAB】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論