日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

HeapSnap工具原理及其应用

發布時間:2023/12/10 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HeapSnap工具原理及其应用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

HeapSnap工具原理及其應用

  • HeapSnap工具原理及其應用
      • 簡介
      • HeapSnap工具演示
      • HeapSnap工具的實現原理

簡介

HeapSnap工具,其名稱源于Heap Snapshot,意即堆內存快照。其實現方式是:在不同的時間點上保存堆內存的快照,然后對比這些不同時間點的快照,找出導致內存增長的泄露點。

HeapSnap工具專門用于處理Android系統中native進程的heap內存泄露問題。它的實現是基于Android已有的libc debug機制,對目標進程的影響小,易于使用,且解決問題的概率高。

源碼下載: https://github.com/albuer/heapsnap

HeapSnap工具演示

演示環境: Android5.1
  • 使能malloc leak調試開關
    setprop libc.debug.malloc 1
  • 啟動測試程序,該程序每3秒鐘泄露4KB大小的內存,測試代碼如下:
 1 #include <stdio.h>2 #include <stdlib.h>3 #include <android/log.h>4 5 void* foo(void)6 {7 void* p = malloc(4096);8 memset(p, 0x5A, 4096);9 return p;10 }11 12 int main(void)13 {14 int count=0;15 void * p = NULL;16 while(1) {17 p = foo();18 ++count;19 printf("%d: %p\n", count, p);20 sleep(3);21 }22 return 0;23 }
  • 向目標進程注入代碼,從而讓目標進程擁有保存heap快照的功能
    heapsnap -p <進程ID> -l libheapsnap.so
  • 保存heap快照
    kill -21 <進程ID>

    從logcat信息中可以看到heap快照被保存在/data/local/tmp/heap_snap/proc_7104_0000.heap,其內容如下:

Heap Snapshot v0.2Total memory: 103424 Allocation records: 2size 4096, dup 25, 0xb6ec576e, 0xb6f3d282, 0xb6fa74ba, 0xb6fa73ac, 0xb6f3d39c, 0xb6fa7428#00 pc 0000676e /system/lib/libc_malloc_debug_leak.so (leak_malloc+101)#01 pc 00012282 /system/lib/libc.so (malloc+9)#02 pc 000004ba /system/bin/leak_test#03 pc 000003ac /system/bin/leak_test#04 pc 0001239c /system/lib/libc.so (__libc_init+43)#05 pc 00000428 /system/bin/leak_test size 1024, dup 1, 0xb6ec576e, 0xb6f3d282, 0xb6f60efa, 0xb6f65eb2, 0xb6f61bac, 0xb6f63172, 0xb6f61128, 0xb6fa73b8, 0xb6f3d39c, 0xb6fa7428#00 pc 0000676e /system/lib/libc_malloc_debug_leak.so (leak_malloc+101)#01 pc 00012282 /system/lib/libc.so (malloc+9)#02 pc 00035efa /system/lib/libc.so (__smakebuf+21)#03 pc 0003aeb2 /system/lib/libc.so (__swsetup+105)#04 pc 00036bac /system/lib/libc.so#05 pc 00038172 /system/lib/libc.so (vfprintf+17)#06 pc 00036128 /system/lib/libc.so (printf+23)#07 pc 000003b8 /system/bin/leak_test#08 pc 0001239c /system/lib/libc.so (__libc_init+43)#09 pc 00000428 /system/bin/leak_test******** MAPS ******** 其中:
  • Total memory: 103424,指的是進程申請的堆內存大小,此處是103424 Bytes
  • Allocation records: 2,總共有2條內存分配路徑
  • size 4096, dup 25, 0xb6ec576e, … ,size指的是每塊內存的大小,此處為4096Bytes,dup指重復的次數,此處為25,表示這個內存分配路徑被調用了25次,后面的地址就是backtrace,以及對backtrace解析后的信息。

這個demo程序中只有兩條heap分配記錄,第一條的size為4096,dup為25,表明這個heap分配路徑被重復執行了25次,且分配的內存都沒有釋放掉,因些我們能夠很明顯看出這個內存分配路徑就是泄露點了。

從backtrace我們可以看到調用malloc函數的位置是

#02 pc 000004ba /system/bin/leak_test

我們調用addr2line查詢地址”0x000004ba”所指向的代碼行:

arm-linux-gnueabi-addr2line -ife out/target/product/rk3288/symbols/system/bin/leak_test 0x4ba
test
/home/cmy/WORK/android-src/android5.1-vr/external/heapsnap/leak_test.c:7

通過addr2line命令反查到”0x4ba”這個地址指向leak_test.c文件的第7行,foo()函數。

在實際的程序中,heap快照中的分配記錄通常都是非常多的,不容易找出哪個內存分配路徑是泄露點,此時可以通過對比不同時間點上,相同內存分配路徑下的dup次數來做出判斷,如果某個分配路徑的dup次數處于持續增長的狀態,那么它就很可能是一個泄露點了。

HeapSnap工具的實現原理

  • 獲取進程的heap信息
    我們首先來看下在Android中,進程的heap分配的backtrace信息是如何被保存以及如何讀取的。

    方法很簡單,只需要先打開malloc調試開關之后,再執行目標進程,那么在這個新運行的進程內發生的所有heap分配的backtrace信息就會被記錄下來,之后在該進程內調用get_malloc_leak_info函數即可獲取到所有前面保存的heap’s backtrace記錄。

  • malloc調試開關

    • 調用’setprop libc.debug.malloc 1’設置好屬性后,malloc開關就使能了且被設置為leak模式(注:在Android7以及之后的版本,設置屬性”setprop libc.debug.malloc.options backtrace”)。

    • 接著,我們開始執行目標進程,進行啟動過程上中會初始化一個加載器,在Android中是/system/bin/linker

    • 通過加載器linker,把所有需要用到的so文件都加載進來。

    • 在linker加載so文件過程中,會自動執行so文件內的.init/.init_array section代碼

    • 在libc.so中,定義了一個函數”attribute((constructor)) static void libc_preinit()”,修飾符“__attribute((constructor))”告知編譯器把__libc_preinit函數指針放到.init_array section,于是函數__libc_preinit在so被linker加載時被自動執行了

    • 在__libc_preinit()函數中,會去讀取libc.debug.malloc屬性值,并根據所獲得的值設置malloc/free…等函數指針指向不同的函數實現,此處libc.debug.malloc為1,則函數指針指向leak_malloc/leak_free….等leak_xxxx形式的函數實現。

    于是,屬性libc.debug.malloc決定了所使用到的mallc/free的實現函數。

  • heap’s backtrace信息的保存與讀取

    • 當malloc調試開關設置為leak模式后,在進程內執行malloc/calloc等函數時候,實際調用的是leak_malloc/leak_calloc等函數,這些函數會在分配的內存前面附加一個頭部信息,在該頭部信息里面保存了此次內存分配的backtrace信息。
    • libc提供了一個函數get_malloc_leak_info,通過該函數我們就能獲得當前進程所有未釋放的heap的backtrace信息了。最終我們獲取到的backtrace信息如前面所示。
  • 進程注入
    前面說到調用get_malloc_leak_info可以獲得當前進程的heap信息,但要怎么才能讓目標進程去調用get_malloc_leak_info函數,一種是修改代碼目標代碼,加入獲取進程heap快照的相關代碼后重新編譯;還有另外一種方式就是使用進程注入,它能把一段代碼注入到目標進程中并執行。

    進程注入流程圖如下所示:

    大致流程描述如下:

  • 先調用ptrace(PTRACE_ATTACH, …)把當前程序附著在目標進程之上,即成為目標進程的父進程。

  • 在目標進程中開辟一塊內存空間用于傳遞參數信息到目標進程

  • 把要注入的程序庫文件名信息保存在前面開辟出來的內存空間,并調用dlopen把該程序庫加載到目標進程中。

  • 在程序庫中定義有一個“extern “C” void attribute((constructor)) prepare()”函數,這個函數會在so被加載到目標進程后被自動執行,它會為信號SIGTTIN注冊一個處理函數,當該信號被觸發后,其注冊的處理函數會調用get_malloc_leak_info獲得當前進程的heap快照,并保存成文件。

  • 至此,注入程序已經把指定代碼注入到目標進程中,并且在目標進程中注冊了一個函數用于處理信號SIGTTIN,于是我們可以用kill命令向目標進程發送SIGTTIN信號,然后目標進程的當前heap快照就會被保存下來了。

  • 最后,我們需要把先前在目標進程中開辟的那塊內存空間釋放掉,還原寄存器,調用ptrace(PTRACE_DETACH, pid, …)結束對該進程的追蹤,目標進程就能沿著原來的流程繼續執行下去了,只是在它內部多了我們注入的代碼。

總結

以上是生活随笔為你收集整理的HeapSnap工具原理及其应用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。