<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          自己搞一個 MemoryGraph 工具行不行?

          共 3577字,需瀏覽 8分鐘

           ·

          2021-11-28 09:40

          大家好,我是腦洞TF,目前就職于阿里巴巴。從事 iOS 開發(fā)多年。這是我剛開的公眾號。未來我會持續(xù)不定期更新,歡迎大家關(guān)注我。一起學(xué)習(xí),一起探討,一起成長。
          老鐵,關(guān)注我不迷路,咱也雙擊一個 666~

          本篇文字有點多,大家耐心看,有些東西大家自己去實驗一下,收獲的會更多。
          話不多說,下面進入正題~~~(由于內(nèi)容需要脫敏,導(dǎo)致有些東西不能說的太透,原諒我~有興趣的同學(xué)可以私信我)



          一、前言:

          在 Xcode 里關(guān)于 malloc stack logging 這個工具可以看到 2 個選項,如下圖。

          一個是只查看當(dāng)前存活的對象,一個是記錄內(nèi)存 alloc 和 free 的行為(下面簡稱內(nèi)存行為日志)。
          這 2 個均可以幫助我們分析內(nèi)存問題。選擇 Live Allocations Only 然后導(dǎo)出 memGraph 文件,還可以用 malloc_history、vmmap 等命令對其進行解析,根據(jù)每個內(nèi)存節(jié)點的棧信息再結(jié)合 linkmap,以類、動態(tài)庫、.a 的維度對內(nèi)存數(shù)據(jù)進行聚合。
          但是!最大的缺陷就是無法自動化,且強依賴 Xcode 工具,Xcode 聯(lián)調(diào)導(dǎo)出 memGraph 文件非常不穩(wěn)定。經(jīng)常出現(xiàn)斷聯(lián)、卡死、內(nèi)存數(shù)據(jù)異常、導(dǎo)出 memGraph 文件失敗等情況。

          二、業(yè)內(nèi)方案:


          方案

          流程

          優(yōu)點

          缺點

          hook malloc方法

          hook 內(nèi)存分配釋放的方法

          ->?;厮?/p>

          ->記錄內(nèi)存行為日志

          ->分配釋放成對數(shù)據(jù)過濾

          ->符號化

          ->數(shù)據(jù)挖掘

          難度較高、成本較高

          性能較差,且覆蓋case不全。

          實現(xiàn)系統(tǒng)的malloc logger的勾子

          實現(xiàn)malloc_logger方法

          ->?;厮?/p>

          ->記錄內(nèi)存行為日志

          ->分配釋放成對數(shù)據(jù)過濾

          ->符號化

          ->數(shù)據(jù)挖掘

          難度較高、覆蓋case

          性能較差

          遍歷內(nèi)存所有live的節(jié)點

          遍歷所有節(jié)點

          ->符號化

          難度較低

          性能高、無法抓堆棧。

          以上方案思路均沒有什么問題,但是實際實現(xiàn)中有2個問題:

          1、性能問題,卡頓。

          2、需要自定義 mallocZone,否則會出現(xiàn)自身邏輯開辟的內(nèi)存也會被統(tǒng)計上,影響真實數(shù)據(jù)。


          三、那么 Malloc Stack Logging 是如何實現(xiàn)的?

          那有沒有什么辦法能夠利用系統(tǒng)的一些能力,然后達到我們拿到內(nèi)存行為日志的呢?最開始的思路就是看看系統(tǒng)的 Malloc Stack Logging 是如何實現(xiàn)的。


          1、我始終堅信 Malloc Stack Logging 工具是有寫文件的,猜測一定是把內(nèi)存行為日志以及堆棧記錄下來了(雖然后面發(fā)現(xiàn)不是完全正確哈)。

          就著這個思路,通過逆向系統(tǒng)的工具和一些動態(tài)庫,也是幾經(jīng)周折終于找到了日志存放的路徑。雖然找到文件的路徑,但是總不能所有打的包都打開 Malloc Stack Logging 這個配置項吧,而且日志文件非常大。所以還是要想想辦法看看有沒有代碼的方式控制是否輸出日志。(日志存放在 tmp 目錄下)


          2、如何用代碼控制日志開關(guān)?

          通過看 libmalloc 的源碼找到2個比較可疑的方法:
          extern boolean_t turn_on_stack_logging(stack_logging_mode_type mode);extern void turn_off_stack_logging(void);

          一嘗試果真是控制內(nèi)存行為日志的開關(guān)。一打開日志發(fā)現(xiàn)里面存的都是 2 進制。如何解析呢?


          3、如何解析日志?

          先看看二進制文件的規(guī)律吧,如下圖:


          從上圖可以很規(guī)律的看出,每 64 位表示一個數(shù),感覺每 4 個 64 位像是一組數(shù)。

          改了下解析二進制的腳本將數(shù)據(jù)解析成四元素數(shù)據(jù)結(jié)構(gòu),這次感覺應(yīng)該沒啥問題了。數(shù)據(jù)如下:

          alloc size:296 stackid:0x00000000035944 add:0x08829C30 free size:0 stackid:0x0000000001B793 add:0x08821270 free size:0 stackid:0x00000000035D92 add:0x08821270 alloc size:296 stackid:0x000000000157A2 add:0x08821270 alloc size:24 stackid:0x00000000030A4C add:0x06DF6F88 alloc size:296 stackid:0x00000000035944 add:0x08829080 free size:0 stackid:0x0000000001B793 add:0x08821270 free size:0 stackid:0x00000000035D92 add:0x08821270 alloc size:28 stackid:0x000000000277EE add:0x08820AF0


          但是通過數(shù)據(jù)結(jié)構(gòu)發(fā)現(xiàn)并沒有棧信息,有的只是一個 stackid。有這玩意沒有用啊,那如何能獲取到 frames。

          4、如何將 stackid 轉(zhuǎn)為 frames:

          結(jié)合系統(tǒng)源碼,找到了一個可以將 stackid 轉(zhuǎn)為 frames 的方式。
          到了這一步感覺好像這條路可以走通了。
          到目前為止:有內(nèi)存行為日志 > 日志可以解析 > 獲取到棧信息。有了以上數(shù)據(jù),核心鏈路就通了。


          方案工程化驗證:

          日志體量:1 小時 6G 左右

          解析耗時:6G 日志 stackid 轉(zhuǎn)成 frames 耗時也大概1小時

          解析后日志體量:大約 30G 左右

          結(jié)論:將 stackid 轉(zhuǎn)成 frames 存在手機上,磁盤會爆炸。這個方式不行,必須想辦法進行離線日志解析。



          5、如何離線解析日志和獲取棧信息

          感覺一定是有一個 map 存儲這個 stackid 和 frames 的映射關(guān)系。只要把 map 持久化到本地,就可以做離線解析了。

          看了下相關(guān)源碼,發(fā)現(xiàn)原來系統(tǒng)并不是僅僅對 stackid 和 frames 做了一個 map,而是用一個樹來存儲棧。并且是放在內(nèi)存中的。這樣一來是大大地減少了內(nèi)存的占用,二來是通過 stackid 和樹之間建立一種映射關(guān)系,查找速度也非??欤『喼苯^了!

          然后對這棵樹進行序列化和反序列化,驗證了下,真是一點毛病沒有??!如此離線解析日志就做到了。而且解析 6G 數(shù)據(jù)耗時大概 7min 左右。


          6、數(shù)據(jù)驗證:

          同一份數(shù)據(jù),通過上面提到過的運行時解析離線解析兩種解析方式進行數(shù)據(jù)對比,數(shù)據(jù)結(jié)果是一致的。(在這個過程中 type_flags 內(nèi)存分配類型這里也有點小問題,就不展開了)

          如此,基本的路已經(jīng)通了,那么自動化分析內(nèi)存工具是可行的。剩下的工作就是根據(jù)地址將 alloc 和 free 成對的數(shù)據(jù)過濾掉。然后符號化再做數(shù)據(jù)挖掘了。

          其實在方案工程化的過程中,會遇到各種各樣的問題,每走一步都比較難,這一篇就不展開介紹了。


          四、方案優(yōu)缺點

          優(yōu)點:

          • 實現(xiàn)成本低

          • 直接使用系統(tǒng)原始日志,自定義工具空間大,想怎么處理聚類都可以。

          • 性能特別好,雖然打印了很多的日志,但是還是特別絲滑,毫無卡頓。

          • 無需像 MemGraph 那樣連接 Mac 設(shè)備,任何時間,任何環(huán)境都可以打開開關(guān)記錄內(nèi)存行為。

          缺點:

          • 長時間記錄內(nèi)存行為日志,占用磁盤空間(不過我覺得這不叫事,這些外力都可以解決~)


          五、整體總結(jié)

          調(diào)研過程比上述過程要坎坷,不過大概了解了 memgraph 的原理和 Instrument 的原理。在逆向很多系統(tǒng)工具的時候,發(fā)現(xiàn)如果設(shè)備和 Xcode 或者 Instrument 工具建立的socket 通道,數(shù)據(jù)會通過通道發(fā)送到 Mac 上。
          memgraph 的 Live Allocations Only 不是拿的內(nèi)存行為日志,應(yīng)該是遍歷的所有的內(nèi)存節(jié)點,再通過 stack tree 獲取到的每個內(nèi)存節(jié)點的棧信息。

          Instrument 工具應(yīng)該是對內(nèi)存行為日志做了包裝,二者的核心都是類似的。

          該方案的應(yīng)用場景和想象空間還是非常大的,想象力往往是前進的源頭和動力。而且方案的靈活度也比較高,畢竟拿到的都是原始的日志和數(shù)據(jù)啥的,想怎么搞怎么搞,還不是任由咱們擺布,哈哈哈哈哈~


          這就是本片的全部內(nèi)容,如果有什么不對的地方,歡迎各位大佬私信我。

          六、寫在最后

          最后回答一下標(biāo)題的問題,能不能自己做一個 MemoryGraph 工具?

          答案是能!

          那還有沒有性能更好的方案?

          答案是有。


          關(guān)注我,后面我會繼續(xù)分享~

          (下一篇打算分享一點逆向相關(guān)的內(nèi)容。先把下一篇的方向定好,然后寫在文章里,這樣能倒逼自己,一定要堅持~~)



          瀏覽 66
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩视频高清无码 | 国产玖玖| 国产男女网站 | 国产成人自拍在线观看 | 欧美极品视频 |