應用在開發(fā)過程中,可能會因為API使用錯誤时捌、變量未及時釋放怒医、異常頻繁創(chuàng)建/釋放內(nèi)存等情況引發(fā)各種內(nèi)存問題。
華為官方提供了多種方案來方便各位開發(fā)者分析解決內(nèi)存相關(guān)問題奢讨,比如Allocation (獲取native調(diào)用棧profiler)稚叹、SnapShot、ASan拿诸、HWASan扒袖,本文重點對這些工具的特點做些說明,同時結(jié)合實際案例對這些工具的應用做進一步講解亩码。
(1)Allocation的應用
Allocation是DevEco Studio 開發(fā)工具的 Profiler提供的內(nèi)存場景分析工具季率,開發(fā)過程中可以使用Allocation來分析應用或服務在運行時的內(nèi)存分配及使用情況,識別和定位內(nèi)存泄漏蟀伸、內(nèi)存抖動以及內(nèi)存溢出等問題蚀同,對應用或服務的內(nèi)存使用進行優(yōu)化。
調(diào)試方式
打開 DevEco Studio 找到 Profiler啊掏,DevEco Profiler提供Launch蠢络、ArkUI、Frame迟蜜、Concurrency刹孔、ArkWeb、Network、Time髓霞、Allocation卦睹、Snapshot、CPU等場景化分析任務類型方库,在設備列表中選擇設備(目前只支持真機)结序,在進程列表中選擇要調(diào)測的應用,選擇Allocation并點擊Create Session即可創(chuàng)建一次會話纵潦。右側(cè)任務分析窗口可以選擇Memory徐鹤、Native Allocation具體分析。
Memory泳道
可以看到Memory泳道包含三個標識:PSS代表進程獨占內(nèi)存和按比例分配共享庫占用內(nèi)存之和邀层,RSS是進程獨占內(nèi)存和相關(guān)共享庫占用內(nèi)存之和返敬,USS代表進程獨占內(nèi)存。
在右邊錄制詳情區(qū)域寥院,工具控制欄上有很多小圖標劲赠,鼠標放上去會有一些功能提示,可以添加一些錄制選項秸谢,各泳道區(qū)域也有下拉框選項凛澎,下拉選擇不同的設置可以調(diào)整錄制功能。
單擊任務窗口左上角的 钮追,啟動錄制预厌,也可以選擇左側(cè)的任務列表中的阿迈,啟動錄制后元媚,等待任務狀態(tài)由“initializing”變?yōu)椤皉ecording”。錄制過程中整個DevEco Profiler不能再點擊其他的模板進行操作苗沧,如果想錄制其他模板可以結(jié)束本次錄制重新選擇其他模板開始錄制刊棕。
錄制過程中,右側(cè)任務分析窗口顯示未recording狀態(tài)待逞,先要結(jié)束此次錄制時甥角,點擊左側(cè)Allocation按鈕的stop按鈕即可,結(jié)束錄制之后可以看到當前session的各個不同內(nèi)存類型對應的變化情況识樱。
默認展示其中的五個子泳道嗤无,如要顯示其他子泳道,可以點擊主泳道的options標簽并勾選其他泳道來查看怜庸。
選擇具體的子泳道可以在details模塊看到不同的時間點当犯,對應的內(nèi)存值變化情況,方便開發(fā)者進一步定位問題割疾。
特別提示:
● 由于隱私安全政策嚎卫,已上架應用市場的應用不支持錄制此泳道。
● 建議避免同時錄制ArkTS Allocation及Native Allocation泳道宏榕,避免影響分析準確性拓诸。
Native Allocation泳道
Native Allocation泳道主要顯示具體的Native內(nèi)存分配情況侵佃,包括靜態(tài)統(tǒng)計數(shù)據(jù)、分配棧、每層函數(shù)棧消耗的Native內(nèi)存等信息抽活。由于隱私安全政策墩新,已上架應用市場的應用不支持錄制此泳道。通過操作對應的options同樣可以選擇展示的類型首有,框選子泳道后顯示具體的內(nèi)存分配,包括靜態(tài)統(tǒng)計數(shù)據(jù)枢劝、分配棧等井联。
如下圖所示,為選中Native Allocation后的某種展示場景您旁。
● Statistics顯示該段時間內(nèi)的靜態(tài)分配情況烙常。包括分配方式(Malloc或Mmap)、總分配內(nèi)存大小鹤盒、總分配次數(shù)蚕脏、尚未釋放的內(nèi)存大小、尚未釋放次數(shù)侦锯、已釋放的內(nèi)存大小驼鞭、已釋放次數(shù)。
點擊任意對象上的跳轉(zhuǎn)按鈕尺碰,可跳轉(zhuǎn)至此類對象的詳細占用/分配信息挣棕。當前統(tǒng)計模式下不支持跳轉(zhuǎn)。
● Call Trees頁簽顯示線程的內(nèi)存分配棧情況亲桥。包括函數(shù)地址或符號洛心、分配大小、占比以及函數(shù)棧幀的類別等题篷。單擊任一行棧幀词身,“More”區(qū)域?qū)@示經(jīng)過該棧幀的分配內(nèi)存最大的調(diào)用棧。
● Allocations List顯示內(nèi)存分配的詳細信息番枚。包括內(nèi)存塊起始地址法严、時間戳、當前活動狀態(tài)葫笼、大小深啤、調(diào)用的庫、調(diào)用庫的具體函數(shù)渔欢、事件類型(與Statistics頁簽的分配方式對應)等墓塌。
針對詳情面板中所展示的函數(shù)棧幀信息(如下圖所示),雙擊棧幀結(jié)點,工具便會在編輯器中打開相關(guān)源碼文件苫幢,并定位到對應行號访诱。此功能正常使用的前提是用于抓取性能數(shù)據(jù)的應用,是在DevEco Studio所在的開發(fā)環(huán)境中編譯韩肝,且相關(guān)源文件位置并未改變触菜。
值得注意的是,上圖對應的category列表標識調(diào)用棧的類型哀峻,從語言層面分為ArkTS涡相、NAPI以及Native,從歸屬層面分為開發(fā)者代碼以及系統(tǒng)代碼(如下圖所示)剩蟀。從這兩個方面可以將調(diào)用棧類型歸類如下:
ArkTS:程序正在執(zhí)行ArkTS代碼催蝗;
NAPI:程序正在運行的NAPI代碼;
Native:程序正在執(zhí)行的Native代碼育特;其中每一個類型的亮色和灰色分別代表開發(fā)者和系統(tǒng)的代碼丙号。
Native Allocation泳道的內(nèi)存狀態(tài)信息可以進一步過濾和篩選。選中Native Allocation后缰冤,details底部有兩個默認過濾條件All Allocations犬缨、Native Size。
● All Allocations(默認狀態(tài)):詳情區(qū)域展示當前框選時間段內(nèi)的所有內(nèi)存分配信息
● Created & Existing:詳情區(qū)域展示當前框選時間段內(nèi)分配未釋放的內(nèi)存棉浸。
● Created & Released:詳情區(qū)域展示當前框選時間段內(nèi)分配已釋放的內(nèi)存怀薛。
Native Size:詳情區(qū)域按照對象的原生內(nèi)存進行展示。
● Native Library:詳情區(qū)域按照對象的so庫進行展示迷郑。
此外枝恋,在“Native Allocation”泳道的“Allocations List”頁簽中還可以通過so庫名稱進行篩選、搜索關(guān)鍵詞篩選三热、內(nèi)存分配堆棧進行篩選鼓择,此處不再做過多演示。
關(guān)于Allocation的更多介紹和使用方法可以在官方文檔查看就漾,基礎(chǔ)內(nèi)存之Allocation分析。
(2)Snapshot
Snapshot是DevEco Profiler提供的一個內(nèi)存快照分析工具念搬,通過結(jié)合Memory實時占用情況抑堡,分析不同時刻的方舟虛擬機內(nèi)存對象占用情況及差異,進而進行內(nèi)存優(yōu)化朗徊。
關(guān)于Snapshot具體的應用技巧可以借鑒官網(wǎng)Snapshot模板基本操作首妖,或者借鑒使用Snapshot Insight分析ArkTS內(nèi)存問題,本文不再贅述爷恳。
(3)ASan的應用
ASan(Address-Sanitizer)是內(nèi)存檢測的工具有缆,用于發(fā)現(xiàn)內(nèi)存飛踩第一現(xiàn)場,DevEco Studio為開發(fā)者集成了ASan能力,可以檢測C/C++的地址越界問題棚壁,解決一些踩內(nèi)存導致的異常crash的補充手段杯矩,對于一些明顯不可能crash的場景可以嘗試開啟ASan。
使用約束
● 如果應用內(nèi)的任一模塊使能ASan袖外,那么entry模塊需同時使能ASan史隆。
● 如果entry模塊未使能ASan,該應用在啟動時將閃退曼验,出現(xiàn)CPP Crash報錯泌射。
● ASan、TSan鬓照、HWASan不能同時開啟熔酷,三個只能開啟其中一個。
配置參數(shù)
ASAN_OPTIONS:在運行時配置ASan的行為豺裆,包括設置檢測級別纯陨、輸出格式、內(nèi)存錯誤報告的詳細程度等留储。ASAN_OPTIONS支持在app.json5中配置翼抠,也支持在Run/Debug Configurations中配置。app.json5的優(yōu)先級較高获讳,即兩種方式都配置后阴颖,以app.json5中的配置為準。
以在app.json5中配置環(huán)境變量為例丐膝。打開AppScope > app.json5文件量愧,添加配置示例如下。
{ "app": { "appEnvironments": [ { "name": "ASAN_OPTIONS", "value": "log_exe_name=true abort_on_error=0 print_cmdline=true" // 示例僅供參考帅矗,具體以實際為準 }, ], ... }}
在Run/Debug Configurations中配置環(huán)境變量偎肃,具體可查看配置環(huán)境變量。
使能ASan
可通過以下兩種方式使能ASan浑此。
方式一
1. 在運行調(diào)試窗口(entry --> Edit Configurations)累颂,點擊Diagnostics,勾選Address Sanitizer凛俱。
2. 如果有引用本地library紊馏,需在library模塊的build-profile.json5文件中,配置arguments字段值為“-DOHOS_ENABLE_ASAN=ON”蒲犬,表示以ASan模式編譯so文件朱监。
方式二
1. 修改工程目錄下AppScope/app.json5,添加ASan配置開關(guān)原叮。
"asanEnabled": true
2. 設置模塊級構(gòu)建ASan插樁赫编。
在需要使能ASan的模塊中巡蘸,通過添加構(gòu)建參數(shù)開啟ASan檢測插樁,在對應模塊的模塊級build-profile.json5中添加命令參數(shù):
"arguments": "-DOHOS_ENABLE_ASAN=ON"
啟用ASan
運行或調(diào)試當前應用擂送。
當程序出現(xiàn)內(nèi)存錯誤時悦荒,彈出ASan log信息,點擊信息中的鏈接即可跳轉(zhuǎn)至引起內(nèi)存錯誤的代碼處团甲。
舉例說明:
編寫一段數(shù)組越界的C++代碼逾冬,對外暴露接口,在ArkTs側(cè)調(diào)用該方法躺苦。
Text(this.message) .fontSize(50) .fontWeight(FontWeight.Bold) .onClick(() => { hilog.info(0x0000, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi.add(2, 3)); hilog.info(0x0000, 'testTag', 'HeapBufferOverflow() ', testNapi.HeapBufferOverflow()); // this.memoryLeak() })
運行當前項目身腻,觸發(fā)Button的onClick事件調(diào)用之后,應用崩潰匹厘,彈出FaultLog信息嘀趟。
點擊 Jump to Log跳轉(zhuǎn)到FaultLog模塊。
可以看到log信息有明確的log信息愈诚,報錯原因為“AddressSanitizer: heap-buffer-overflow”她按,同樣還有多個鏈接地址】蝗幔“==appspawn==30149==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x005b7fe7153b at pc 0x007c24fc309c bp 0x007eba84b160 sp 0x007eba84b158
”可以直接跳轉(zhuǎn)到官網(wǎng)關(guān)于“heap-buffer-overflow”的報錯解釋以及解決方案酌泰。另外多個包含“/data/storage/el1/...”的地址能精確定位到C/C++層代碼出錯的具體位置。
然后我們就發(fā)現(xiàn)這是數(shù)組越界造成的匕累,所以解決方案就是“對于已知大小的集合陵刹,注意訪問不要越界,位置大小的集合訪問前先判斷大小欢嘿∷ニ觯”
ASan檢測異常碼
ASan不僅能檢測出“heap-buffer-overflow”異常,還能檢測很多其他類型的內(nèi)存異常炼蹦,關(guān)于這些異常問題及解決方案羡宙,如下所示。
● heap-buffer-overflow
● 背景/原理:訪問越界掐隐。
● 影響/報錯:導致程序存在安全漏洞狗热,并有崩潰風險。
開啟ASan檢測后瑟枫,觸發(fā)demo中的函數(shù),應用閃退報ASan慷妙,包含字段:AddressSanitizer:heap-buffer-overflow
● 修改方法:注意數(shù)組容量不要訪問越界。
● 推薦建議:已知大小的集合注意訪問不要越界允悦,位置大小的集合訪問前先判斷大小膝擂。
● stack-buffer-underflow
● 背景/原理:訪問越下界虑啤。
● 影響/報錯:導致程序存在安全漏洞,并有崩潰風險架馋。
開啟ASan檢測后狞山,觸發(fā)demo中的函數(shù),應用閃退報ASan叉寂,包含字段:AddressSanitizer:stack-buffer-underflow
● 修改方法:訪問索引不應小于下界萍启。
● 推薦建議:訪問索引不應小于下界。
● stack-use-after-scope
● 背景/原理:棧變量在作用域之外被使用屏鳍。
● 影響/報錯:導致程序存在安全漏洞勘纯,并有崩潰風險。
開啟ASan檢測后钓瞭,觸發(fā)demo中的函數(shù)驳遵,應用閃退報ASan,包含字段:AddressSanitizer:stack-use-after-scope
● 修改方法:在作用域內(nèi)使用該變量山涡。
● 推薦建議:注意變量作用域堤结。
● attempt-free-nonallocated-memory
● 背景/原理:嘗試釋放了非堆對象(non-heap object)或未分配內(nèi)存。
● 影響/報錯:導致程序存在安全漏洞鸭丛,并有崩潰風險竞穷。
開啟ASan檢測后,觸發(fā)demo中的函數(shù)鳞溉,應用閃退報ASan瘾带,包含字段:
AddressSanitizer: attempting free on address which was not malloc()-ed
● 修改方法:不要對非堆對象或未分配的內(nèi)存使用free函數(shù)。
● 推薦建議:不要對非堆對象或未分配的內(nèi)存使用free函數(shù)穿挨。
● double-free
● 背景/原理:重復釋放內(nèi)存月弛。
● 影響/報錯:導致程序存在安全漏洞,并有崩潰風險科盛。
開啟ASan檢測后帽衙,觸發(fā)demo中的函數(shù),應用閃退報ASan贞绵,包含字段:
AddressSanitizer: double-free
● 修改方法:已經(jīng)釋放一次的指針厉萝,不要再重復釋放。
● 推薦建議:變量定義聲明時初始化為NULL榨崩,釋放內(nèi)存后也應立即將變量重置為NULL谴垫,這樣每次釋放之前都可以通過判斷變量是否為NULL來判斷是否可以釋放。
● heap-use-after-free
● 背景/原理:當指針指向的內(nèi)存被釋放后母蛛,仍然通過該指針訪問已經(jīng)被釋放的內(nèi)存翩剪,就會觸發(fā)heap-use-after-free。
● 影響/報錯:導致程序存在安全漏洞彩郊,并有崩潰風險前弯。
開啟ASan檢測后蚪缀,觸發(fā)demo中的函數(shù),應用閃退報ASan恕出,包含字段:
AddressSanitizer: heap-use-after-free
● 修改方法:已經(jīng)釋放的指針不要再使用询枚,將指針設置為NULL/nullptr。
● 推薦建議:實現(xiàn)一個free()的替代版本或者 delete析構(gòu)器來保證指針的重置浙巫。
● Other categories
未知的錯誤類型金蜀,持續(xù)更新中。
(4)HWASan的應用
HWASan(Hardware-Assisted Address Sanitizer)是一款類似于ASan的內(nèi)存錯誤檢測工具的畴,目前僅適用于渊抄。 與ASan相比,HWASan使用的內(nèi)存減少很多苗傅,因而更適合用于整個系統(tǒng)的清理抒线。
功能介紹
與ASan相比,HWASan具有如下特征:
● CPU開銷約為2倍渣慕。
● 代碼大小開銷為40% - 50%嘶炭。
● RAM開銷為10% - 35%。
HWASan能檢測到ASan所能檢測到的同一系列錯誤:
● 堆棧和堆緩沖區(qū)上溢/下溢逊桦。
● 釋放之后的堆使用情況眨猎。
● 重復釋放/錯誤釋放。
和ASan相比强经,HWASan具有以下優(yōu)點:
● HWASan不需要安全區(qū)來檢測buffer overflow睡陪,既極大地降低了工具對于內(nèi)存的消耗,也不會出現(xiàn)ASan中某些overflow檢測不到的情況匿情。
● HWASan不需要隔離區(qū)來檢測UseAfterFree兰迫,因此不會出現(xiàn)ASan中某些UseAfterFree檢測不到的情況。
● 此外炬称,HWASan還可以檢測返回之后的堆棧使用情況汁果。
約束條件
● HWASan檢測僅適用于AArch64架構(gòu)的硬件。
● ASan玲躯、TSan据德、HWASan不能同時開啟,三個只能開啟其中一個跷车。
使能HWASan
方式一
在運行調(diào)試窗口(例如entry --> Edit Configurations)棘利,點擊Diagnostics,勾選Hardware-Assisted Address Sanitizer開啟檢測朽缴。
方式二
修改工程目錄下的AppScope/app.json5文件善玫,添加HWASan配置開關(guān)。
"hwasanEnabled": true
在需要使能HWASan的模塊中密强,通過添加構(gòu)建參數(shù)開啟HWASan檢測插樁裹唆,在對應模塊的模塊級build-profile.json5中添加命令參數(shù):
"arguments": "-DOHOS_ENABLE_HWASAN=ON"
啟用HWASan
運行或調(diào)試當前應用只洒。當程序出現(xiàn)內(nèi)存錯誤時劳坑,彈出HWASan log信息,點擊信息中的鏈接即可跳轉(zhuǎn)至引起內(nèi)存錯誤的代碼處涝开。
關(guān)于HWAan的代碼實例和應用場景基本和ASan一致框仔,此處就不做重復是說明了。
總結(jié)
● 根據(jù)Allocation分析的結(jié)果银舱,可以方便開發(fā)者優(yōu)化應用的服務和組件的內(nèi)存使用跛梗。例如,減少不必要的內(nèi)存分配和釋放核偿,優(yōu)化數(shù)據(jù)結(jié)構(gòu)的使用等。
● 通過ASan或HWASan分析問題時轰绵,精準定位內(nèi)存出錯原因和具體位置尼荆,開發(fā)者應修復這些內(nèi)存管理的問題,如確保所有分配的內(nèi)存都被正確釋放翔悠。
● 開發(fā)過程中野芒,使用合適的內(nèi)存管理和回收機制 :確保應用在不需要內(nèi)存資源時能夠有效地回收和管理這些資源蓄愁,避免過度使用內(nèi)存狞悲。