日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
基于Rust的Android Native內(nèi)存分析方案

背景:高德地圖車機版運行的車載系統(tǒng)環(huán)境絕大部分都是基于安卓的定制系統(tǒng),且高德車機版底層代碼均為C/C++ Native代碼。因此,在安卓上需要有一種通用的Native內(nèi)存性能分析方案。內(nèi)存塔(MemTower)是一個基于開源項目memory-profiler并移植安卓且優(yōu)化改進后的方案,解決了之前方案存在的痛點問題,滿足了通用Native內(nèi)存性能分析需求。該項目采用Rust語言編寫,并利用了Rust的一些特性來完成對Native內(nèi)存訪問的Hook.

創(chuàng)新互聯(lián)建站網(wǎng)站建設(shè)公司一直秉承“誠信做人,踏實做事”的原則,不欺瞞客戶,是我們最起碼的底線! 以服務(wù)為基礎(chǔ),以質(zhì)量求生存,以技術(shù)求發(fā)展,成交一個客戶多一個朋友!專注中小微企業(yè)官網(wǎng)定制,做網(wǎng)站、成都網(wǎng)站設(shè)計,塑造企業(yè)網(wǎng)絡(luò)形象打造互聯(lián)網(wǎng)企業(yè)效應(yīng)。

1. Android Native內(nèi)存分析痛點與訴求

這一節(jié)主要介紹我們?yōu)槭裁匆鲞@件事以及對于這件事我們期望達到什么樣的目標。

1.1 現(xiàn)有工具缺陷

Android在Java層面有很完善的性能分析工具,但是在Native層面沒有完整的解決方案。主要表現(xiàn)在:

  • 不支持Android 4.x,線上統(tǒng)計數(shù)據(jù)顯示4.x版本的車機仍占有較大比重,因此這點成為了無法忽視的問題。
  • 安卓自帶的malloc_debug功能在不同的版本上行為不同,而且車機安卓系統(tǒng)大多經(jīng)過了系統(tǒng)廠商的定制,不能保證這些功能可用。

因此, 無法基于Android系統(tǒng)自有的功能做到Native內(nèi)存性能分析。

我們團隊之前也在這方面做出了一些成果,但還是存在下面幾個問題:

  • 通過修改編譯參數(shù)對Native代碼函數(shù)入口/結(jié)束位置插樁來進行Hook,導致了性能嚴重下降;
  • 由于是侵入式分析,對內(nèi)存問題分析需要單獨編譯出包分析,解決效率大幅降低,一個內(nèi)存泄漏問題的排查成本按天計算。
  • 缺少精準內(nèi)存使用數(shù)據(jù)。

1.2 打造一套完整的Native內(nèi)存性能分析方案

結(jié)合上門的問題痛點,我們希望能夠有一套完整的Native內(nèi)存性能分析方案。具體訴求表現(xiàn)在下面幾點:

  • 支持安卓4.x在內(nèi)的絕大多數(shù)安卓系統(tǒng)。
  • 無侵入式分析,內(nèi)存問題的發(fā)現(xiàn)與精準定位同時完成。
  • 性能優(yōu)異,overhead低。
  • 支持長時間內(nèi)存泄漏壓測。包括車廠客戶在內(nèi)的研發(fā)團隊都會對導航進行壓測,需要能夠支持長時間的壓測并定位內(nèi)存泄漏問題。
  • 函數(shù)級內(nèi)存使用數(shù)據(jù)。原先的方案重點在于解決內(nèi)存泄漏的問題,獲取的內(nèi)存使用數(shù)據(jù)不夠精確。而我們希望新的方案能夠獲得詳細的內(nèi)存使用數(shù)據(jù),用來支持內(nèi)存性能優(yōu)化。

2. 內(nèi)存塔(MemTower)方案

本節(jié)主要介紹memory-profiler項目的實現(xiàn)和內(nèi)存塔(MemTower)方案在移植該項目至Android平臺上的過程和對原方案的改進。闡述我們是如何實現(xiàn)并滿足上述的訴求。

2.1 選擇Rust & Memory-profiler

針對上門的訴求,期望能夠找到一種新的解決方案。當時正好在研究Rust,因此在GitHub上結(jié)合關(guān)鍵字搜索便發(fā)現(xiàn)了memory-profiler(以下簡稱mp)項目,作者koute是前Nokia工程師。接著才有了后面的內(nèi)存塔。本節(jié)主要闡述mp如何結(jié)合Rust實現(xiàn)內(nèi)存Profile的相關(guān)原理和功能。

2.1.1 Hook實現(xiàn)

通常對Native內(nèi)存性能分析使用的方案是Hook malloc 和 free 等內(nèi)存調(diào)用請求。mp的原理也是如此,利用LD_PRELOAD 預(yù)加載自定義庫實現(xiàn)對內(nèi)存操作函數(shù)的Hook。這種方案最大的問題是容易引發(fā)循環(huán)malloc調(diào)用。如下圖,Hook了程序內(nèi)存請求后,Hook業(yè)務(wù)自身的內(nèi)存請求也會觸發(fā)內(nèi)存請求,從而造成了malloc循環(huán)調(diào)用,引發(fā)棧崩潰。

mp的做法利用了Rust的可自定義內(nèi)存分配器(Allocator)的特性,將曾經(jīng)的Rust默認內(nèi)存分配器jemalloc作為自定義分配器,并在jemalloc-sys的c代碼中將最終的內(nèi)存申請mmap替換成自定義的函數(shù)入口(從而也區(qū)分應(yīng)用和自身的mmap調(diào)用),最終調(diào)用mmap系統(tǒng)調(diào)用。

將Rust內(nèi)存請求轉(zhuǎn)發(fā)給系統(tǒng)調(diào)用后,還需要將應(yīng)用的內(nèi)存請求繼續(xù)傳遞給系統(tǒng)libc. mp的做法是通過Rust的feature開關(guān),可以自行選擇兩種方式處理應(yīng)用內(nèi)存請求,這兩種方式都是通過在Rust中指定link_name 屬性實現(xiàn):

  • 直接通過__libc_malloc的link_name將應(yīng)用內(nèi)存請求轉(zhuǎn)發(fā)給libc
  • 通過指定成jemallocator的函數(shù)入口 _rjem_malloc,使應(yīng)用和Rust共用jemalloc.
  • 最終可以使Hook業(yè)務(wù)使用完整的Rust語言功能而不用擔心Rust自身代碼引起的循環(huán)調(diào)用崩潰。

2.1.2 高性能堆棧反解

除了利用Rust系統(tǒng)編程語言特性避開內(nèi)存循環(huán)調(diào)用之外,作者還利用Rust的高性能特點實現(xiàn)了幾種高性能堆棧反解。

利用ELF的.eh_frame 節(jié)(C++異常處理機制)提供的棧回溯信息。

基于.ARM.exidx + .ARM.extab的?;厮?,這個是ARM提供的unwind table.

具體實現(xiàn)可以看作者的這個Crate not-perf。這里選擇第二種做說明,如圖下,對每個線程的堆棧都用線程局部存儲維護了一套棧幀緩存,這個緩存來自于ELF文件中的unwind table信息,當堆棧的幀在緩存未命中時會把對應(yīng)二進制的unwind表被加載到內(nèi)存,而命中的時候,就不需要去讀取文件。通常二進制被加載后它的地址空間就不會發(fā)生變化,所以緩存的效率很高。缺點是每個線程都有一套完整的緩存。從系統(tǒng)層面看占用的內(nèi)存overhead很大。

2.1.3 強大的數(shù)據(jù)分析功能

從mp的頁面可以看到它除了內(nèi)存Profile外,還有一個對應(yīng)的數(shù)據(jù)分析Server端,采用actix-web框架,且具備一個非常強大的分析功能。主要特性有下面幾點:

  • 內(nèi)存使用量和泄漏兩種視角的時序曲線非常直觀。
  • 搭配了一個非常強大的過濾器,可以實現(xiàn)針對內(nèi)存生命周期、函數(shù)、時間等多維度做過濾查詢及其對應(yīng)的內(nèi)存火焰圖功能。
  • 所有功能具備RESTful API接口,可以非常容易的實現(xiàn)定制。

詳細的使用說明這里不做過多的介紹。

2.2 移植

了解完mp的基本原理后,本節(jié)我們主要闡述在移植安卓平臺過程中遇到的各種問題(坑)。

2.2.1 自定義Allocator

mp的Hook方案在Android平臺上存在較多問題,主要體現(xiàn)在下面幾點:

  • Jemalloc本身也才是Android 5.0開始引入安卓,mp自帶的jemalloc-sys會導致一個應(yīng)用里存在兩個jemalloc,最終表現(xiàn)為在不同的版本上有著各種各樣的異常崩潰,問題排查成了阻礙。
  • __libc_malloc是glibc提供的malloc函數(shù)入口別名,但在Android平臺沒有對應(yīng)這類實現(xiàn)。

因此,我們采用最原始的dlsym 方法獲取內(nèi)存相關(guān)函數(shù)入口,再將其封裝成Rust Allocator. 應(yīng)用的內(nèi)存請求也使用這些函數(shù)地址。如下圖,最終所有內(nèi)存請求都傳給libc,這樣Rust的業(yè)務(wù)代碼對libc來說是透明的。

2.2.2 棧回溯

?;厮葸@塊同樣有一些移植修改。上面說到作者提供了基于C++異常處理機制的?;厮莘椒?,但是這個方案要求依賴C++庫。而C在Android 8.0之后才會成為默認依賴。這要求在8.0之前的版本運行時應(yīng)用必須也依賴C++庫。因此我們移除了這個?;厮莘桨?,舍去了這個依賴。

2.2.3 地址空間重載

在程序啟動或調(diào)用dlopen/dlclose時鏈接器會加載(或卸載)ELF文件,相應(yīng)的,程序的地址空間會發(fā)生變化,這時候?;厮菥彺胬锏牡刂房臻g就可能會失效,需要重新加載(reload),reload操作掃描整個地址空間的變更,這個成本很高。與此同時還需要一種低成本獲取地址空間變化的方式. mp的實現(xiàn)主要有兩種方式:

libc提供的接口dl_iterate_phdr. Android API_LEVEL低于21(即5.0之前)沒有,5.0之后這個函數(shù)的結(jié)構(gòu)體和在高版本Android的實現(xiàn)不同。所以Rust定義的單一C結(jié)構(gòu)體格式會導致讀取到臟數(shù)據(jù)作為reload依據(jù),導致非常高頻繁地reload.;

Perf的 PERF_RECORD_MMAP2 事件,這個要求內(nèi)核版本大于3.16。因此這在Android 4.x上也不具備。

實際運行過程中程序在加載完所有依賴ELF后,地址空間幾乎很少再變。因此,我們修改為只有在新的ELF被加載時才進行地址空間重載。火焰圖結(jié)果顯示可以大幅降低Hook時的計算成本。

2.3 改進

到目前為止, 內(nèi)存塔已經(jīng)可以在支持 LD_PRELOAD 的Android版本上正確運行了(含4.x)。但是上面訴求中還有一點無法滿足:長時間內(nèi)存泄漏壓測。而且在數(shù)據(jù)分析過程中,我們希望有更多維度的信息。因此,本小節(jié)主要介紹我們對內(nèi)存塔的改進。

2.3.1 內(nèi)存泄漏壓測

mp原先的定位正如它的名稱表述,是一款內(nèi)存性能分析工具,它記錄的是全量內(nèi)存信息。這點決定了它的數(shù)據(jù)量規(guī)模。在長時間壓測一小時的多個業(yè)務(wù)場景中,根據(jù)內(nèi)存使用量不同,生成的采樣數(shù)據(jù)文件有1GB~7GB之多。這樣的數(shù)據(jù)量無法滿足業(yè)務(wù)的需要。

因此,我們增加了內(nèi)存泄漏檢測模式(ONLY_LEAKED),這個模式的原理如下:

  • 將記錄到內(nèi)存開辟的每一層棧幀記錄到一個字典樹(Trie Tree)中,同時記錄開辟的內(nèi)存大小。
  • 內(nèi)存釋放時更新字典樹對應(yīng)的節(jié)點信息。當前泄漏是否達到某個閾值(如100MB), 是則停止采樣。
  • 在結(jié)束采樣時把整個字典樹存儲的未釋放內(nèi)存記錄寫入文件。

這種模式的優(yōu)點是最終的數(shù)據(jù)量非常的小,實際壓測一小時數(shù)據(jù)文件大小在100~200MB之間。再進過mp自帶的postprocess 子命令壓縮后,大小不足100MB。不足之處是內(nèi)存塔需要在內(nèi)存中緩存一個全量的堆棧歷史數(shù)據(jù),當沒有新的棧幀記錄出現(xiàn)后這個內(nèi)存增長才會趨于穩(wěn)定。

2.3.2 增強分析過濾器

導航的業(yè)務(wù)模塊劃分和線程很多,因此增加了按線程和庫正則篩選過濾器選項。

2.3.3 內(nèi)存火焰圖完善

mp原方案的內(nèi)存火焰圖是以內(nèi)存大小(allocated)作為火焰圖維度,在分析內(nèi)存性能時內(nèi)存開辟次數(shù)(allocations)也是一個很重要的指標,因此加入內(nèi)存開辟次數(shù)火焰圖。這是當初最早改進的功能,而且火焰圖的形狀類似塔狀,就把該項目重命名為:內(nèi)存塔(MemTower)。

最后一點是原方案的火焰圖信息沒有以線程為單位劃分,我們把堆棧信息按線程區(qū)分后會更加直觀。

分配次數(shù)火焰圖

分配大小火焰圖

3. 內(nèi)存塔的能力及更多可能

最后一節(jié)介紹下內(nèi)存塔提供了什么樣的能力、收益以及還有哪些可能。

3.1 能力

內(nèi)存塔(MemTower)在Android 8.0以下依賴setprop wrap.com.xxx.xxx 和 root權(quán)限的能力,8.0以上版本如果沒有root權(quán)限還可以通過配置Android項目wrap.sh來加載內(nèi)存塔庫。另外,由于mp原生支持Linux的原因,我們也成功適配了奔馳戴姆勒這類嵌入式Linux項目車機。

  • 支持平臺:Android 4.x、5.1.1和7或更高以上版本(5.0和6系統(tǒng)存在Bug, 無法設(shè)置setprop ). Linux x86_64, AArch64, Arm.
  • 采樣方式: 非侵入式. 非Root設(shè)備可選侵入式方式。
  • 采樣模式: 常規(guī)性能分析模式和內(nèi)存泄漏壓測模式。
  • 特點: 高性能堆棧反解、完善的內(nèi)存分析Insight體驗(多維度過濾器分析、內(nèi)存火焰圖等)。

原先發(fā)現(xiàn)內(nèi)存泄漏問題重新出包二次壓測分析,再推斷可能泄漏點的流程耗費時間按天計算。利用內(nèi)存塔(MemTower)做一遍測試后幾分鐘即可解析出精細化數(shù)據(jù),大幅降低了內(nèi)存性能問題分析成本。mp提供的這套Hook思路和高性能堆棧反解其實可以不僅僅局限在內(nèi)存方面的分析,還可以針對IO性能分析或其它問題上。


文章題目:基于Rust的Android Native內(nèi)存分析方案
URL標題:http://www.5511xx.com/article/coescsd.html