跨平臺c開發庫tbox:內存庫使用詳解

TBOX是一個用c語言實現的跨平臺開發庫。
針對各個平臺,封裝了統一的接口,簡化了各種開發過程當中經常使用操做,使你在開發過程當中,更加關注實際應用的開發,而不是把時間浪費在瑣碎的接口兼容性上面,而且充分利用了各個平臺獨有的一些特性進行優化。
這個項目的目的,是爲了使C開發更加的簡單高效。linux

內存總體架構

TBOX的內存管理模型,參考了linux kernel的內存管理機制,並在其基礎上作了一些改進和優化。git

內存池架構

大塊內存池:large_pool

整個內存分配的最底層,都是基於large_pool的大塊內存分配池,相似於linux的基於page的分配管理,不過有所不一樣的是,large_pool並無像linux那樣使用buddy算法進行(2^N)*page進行分配,這樣若是須要2.1m的內存,須要分配4m的內存塊,這樣力度太大,很是浪費。github

所以large_pool內部採用N*page的基於page_size爲最小粒度進行分配,所以每次分配頂多浪費不到一頁的空間。算法

並且若是須要的內存不到整頁,剩下的內存也會一併返回給上層,若是上層須要(好比small_pool),能夠充分利用這多餘的部份內存空間,使得內存利用率達到最優化。segmentfault

並且根據tb_init實際傳入的參數需求,large_pool有兩種模式:緩存

  1. 直接使用系統內存分配接口將進行大塊內存的分配,並用雙鏈維護,這種比較簡單,就很少說了。
  2. 在一大塊連續內存上進行統一管理,實現內存分配。

具體使用哪一種方式,根據應用需求,通常的應用只須要使用方式1就好了,這個時候tb_init傳tb_null就好了,若是是嵌入式應用,須要管理有限的一塊內存空間,這個時候可使用方式2, tb_init傳入指定內存空間地址和大小。bash

這裏就主要看下方式2的large_pool的內存結構(假設頁大小是4KB):cookie

--------------------------------------------------------------------------
    |                                     data                                 |
     --------------------------------------------------------------------------
                                         |
     --------------------------------------------------------------------------
    | head | 4KB | 16KB | 8KB | 128KB | ... | 32KB |       ...       |  4KB*N  |
     --------------------------------------------------------------------------

因爲large_pool主要用於大塊分配,而超小塊的分配在上層small_pool中已經被分流掉了,因此這個應用中,large_pool不會太過頻繁的分配,因此碎片量不會太大,爲了進一步減小碎片的產生,在free時候都會對下一個鄰近的空閒塊進行合併。而malloc在分配當前空閒塊空間不夠的狀況下,也會嘗試對下一個鄰近空閒塊進行合併。數據結構

因爲每一個內存塊都是鄰近挨着的,也沒用雙鏈維護,沒有內存塊,都有個塊頭,合併過程僅僅只是改動內存塊頭部的size字段,這樣的合併不會影響效率。架構

因爲沒像buddy算法那樣,用雙鏈維護空閒內存,雖然節省了鏈表維護的空間和時間,可是每次分配內存都要順序遍歷全部塊,來查找空閒的內存,這樣的效率實在過低了,爲了解決這個問題,large_pool內部針對不一樣級別的塊,進行了預測,每次free或者malloc的時候,若是都會把當前和鄰近的空閒快,緩存到對應級別的預測池裏面去,具體的分級以下:

--------------------------------------
    | >0KB :      4KB       | > 0*page     | 
    |-----------------------|--------------
    | >4KB :      8KB       | > 1*page     | 
    |-----------------------|--------------
    | >8KB :    12-16KB     | > 2*page     | 
    |-----------------------|--------------
    | >16KB :   20-32KB     | > 4*page     | 
    |-----------------------|--------------
    | >32KB :   36-64KB     | > 8*page     | 
    |-----------------------|--------------
    | >64KB :   68-128KB    | > 16*page    | 
    |-----------------------|--------------
    | >128KB :  132-256KB   | > 32*page    | 
    |-----------------------|--------------
    | >256KB :  260-512KB   | > 64*page    | 
    |-----------------------|--------------
    | >512KB :  516-1024KB  | > 128*page   | 
    |-----------------------|--------------
    | >1024KB : 1028-...KB  | > 256*page   | 
     --------------------------------------

因爲一般不會分配太大塊的內存,所以只要可以預測1m內存,就足夠,而對於>1m的內存,這裏也單獨加了一個預測,來應對偶爾的超大塊分配,而且使得總體分配流程更加的統一。

若是當前級別的預測塊不存在,則會到下一級別的預測塊中查找,若是都找不到,纔回去遍歷整個內存池。

實際測試下,每一個塊的預測成功基本都在95%以上,也就說大部分狀況下,分配效率都是維持在O(1)級別的。

小塊內存池:small_pool

小塊內存分配池

在上層每次調用malloc進行內存分配的時候,回去判斷須要多大的內存,若是這個內存超過或者等於一頁,則會直接從large_pool進行分配,若是小於一頁,則會優先經過small_pool進行分配,small_pool針對小塊的內存進行了高速緩存,並優化了空間管理和分配效率。

因爲程序大部分狀況下,都在使用小塊內存,所以small_pool對內存的分配作了很大的分流,使得large_pool承受的壓力減少,碎片量減小不少,而small_pool內部因爲都是由fixed_pool來對固定大小的內存進行管理,是不會存在外部碎片的。而小塊內存的粒度自己就很小,因此內部碎片量也至關少。

small_pool中的fixed_pool,就像是linux kernel中的slub,在small_pool中總共有12級別的fixed_pool,每一個級別分別管理一種固定大小的內存塊,具體級別以下:

--------------------------------------
    |    fixed pool: 16B    |  1-16B       | 
    |--------------------------------------|
    |    fixed pool: 32B    |  17-32B      |  
    |--------------------------------------|
    |    fixed pool: 64B    |  33-64B      | 
    |--------------------------------------|
    |    fixed pool: 96B*   |  65-96B*     | 
    |--------------------------------------|
    |    fixed pool: 128B   |  97-128B     |  
    |--------------------------------------|
    |    fixed pool: 192B*  |  129-192B*   |  
    |--------------------------------------|
    |    fixed pool: 256B   |  193-256B    |  
    |--------------------------------------|
    |    fixed pool: 384B*  |  257-384B*   |  
    |--------------------------------------|
    |    fixed pool: 512B   |  385-512B    |  
    |--------------------------------------|
    |    fixed pool: 1024B  |  513-1024B   |  
    |--------------------------------------|
    |    fixed pool: 2048B  |  1025-2048B  |  
    |--------------------------------------|
    |    fixed pool: 3072B* |  2049-3072B* |  
     --------------------------------------

其中 96B, 192B,384B,3072B並非按2的整數冪大小,這麼作主要是爲了更加有效的利用小塊內存的空間減小內部碎片。

固定塊內存池:fixed_pool

顧名思義,fixed_pool就是用來管理固定大小的內存分配的,至關於linux中slub,而fixed_pool中又由多個slot組成,每一個slot負責一塊連續的內存空間,管理部份內存塊的管理,相似linux中的slab, 每一個slot由雙鏈維護,而且參考linux的管理機制,分爲三種slot管理方式:

  1. 當前正在分配的slot
  2. 部分空閒slots鏈表
  3. 徹底full的slots鏈表

具體結構以下:

current:
         --------------
        |              |
     --------------    |
    |     slot     |<--
    |--------------|
    ||||||||||||||||  
    |--------------| 
    |              | 
    |--------------| 
    |              | 
    |--------------| 
    ||||||||||||||||  
    |--------------| 
    |||||||||||||||| 
    |--------------| 
    |              | 
     --------------  

    partial:

     --------------       --------------               --------------
    |     slot     | <=> |     slot     | <=> ... <=> |     slot     |
    |--------------|     |--------------|             |--------------|
    ||||||||||||||||     |              |             |              |
    |--------------|     |--------------|             |--------------|
    |              |     ||||||||||||||||             |              |
    |--------------|     |--------------|             |--------------|
    |              |     ||||||||||||||||             ||||||||||||||||
    |--------------|     |--------------|             |--------------|
    ||||||||||||||||     ||||||||||||||||             |              |
    |--------------|     |--------------|             |--------------|
    ||||||||||||||||     |              |             |              |
    |--------------|     |--------------|             |--------------|
    |              |     |              |             ||||||||||||||||
    --------------       --------------               --------------

    full:

     --------------       --------------               --------------
    |     slot     | <=> |     slot     | <=> ... <=> |     slot     |
    |--------------|     |--------------|             |--------------|
    ||||||||||||||||     ||||||||||||||||             ||||||||||||||||
    |--------------|     |--------------|             |--------------|
    ||||||||||||||||     ||||||||||||||||             ||||||||||||||||
    |--------------|     |--------------|             |--------------|
    ||||||||||||||||     ||||||||||||||||             ||||||||||||||||
    |--------------|     |--------------|             |--------------|
    ||||||||||||||||     ||||||||||||||||             ||||||||||||||||
    |--------------|     |--------------|             |--------------|
    ||||||||||||||||     ||||||||||||||||             ||||||||||||||||
    |--------------|     |--------------|             |--------------|
    ||||||||||||||||     ||||||||||||||||             ||||||||||||||||
     --------------       --------------               --------------

具體的分配算法

  1. 若是當前slot中還有空閒的塊,優先從當前slot進行分配
  2. 若是當前slot中沒有空閒塊,則把這個slot放到full鏈表中去
  3. 從部分空閒slot鏈表中,挑一個空閒的slot進行分配,並把它設爲當前分配狀態。

具體的釋放算法

  1. 釋放後若是這個slot徹底空閒了,而且不是正在分配的slot,則把整個slot釋放掉,這樣既能夠保證有一個能夠分配的slot以外,還極大的下降了內存使用,也避免某些狀況下頻繁的釋放分配slot。
  2. 若是釋放的slot屬於full鏈表而且變爲了部分空閒,則把這個slot移到部分空閒slot鏈表中去。

額外要提一下的是

large_pool每次分配一塊空間給一個slot的時候,殘留下來的部分剩餘空間(<1*page), 也能直接返回給slot,讓slot充分利用這部分數據,這樣能夠能夠切分出更多地內存塊。

例如:

fixed_pool每次增加一個包含256個32B內存塊的slot(須要8192B大小+16B內部數據維護大小),其實在用large_pool分配的時候,須要8208B的大小,因爲須要按頁對齊(4KB),實際分配確佔用了8192+4096: 12288B的大小的空間。

可是large_pool支持把全部空間數據一併返回給上層,這樣slot其實獲取到了一個12288B大小的內存,而且也知道其實際大小爲:12288B,所以實際切分了(12288-(32B的slot內部維護數據))/32也就是383個內存塊。

多維護了127個內存塊,充分把large_pool的內部碎片也利用上了,進一步增長了內存利用率。

fixed_pool中的slot

雖然類比與linux中的slab,可是其數據結構確跟slab不太同樣,它並無像slab那樣,對每一個空閒小塊都用鏈表維護,而是直接用位段來維護是否空閒的信息,這樣更加節省內存,並且經過優化算法,其分配效率和slab幾乎同樣。

在fixed_pool的slot的頭部,專門有一小塊獨立的數據,用於維護每一個小塊的空閒信息,每一個塊只暫用一比特位的信息,來判斷這個塊是否空閒,因爲沒有內存塊都是固定大小的,因此比特位的位置定位,徹底能夠經過索引計算獲得。

並且每次釋放和分配,都會去緩存一個雙字大小的位信息端,來預測下一次的分配,因爲是雙字大小,總共有32個比特位,因此每次緩存,最多能夠預測鄰近32個內存塊。所以大部分狀況下,預測成功率一直都是>98%的,分配效率都維持在O(1),比起large_pool的預測率還高不少,因此small_pool對large_pool的分流,還在必定程度上,進一步提升了內存分配效率。

而就算很倒黴,沒預測成功,slot的順序遍從來查找空閒快的算法,也至關高效,徹底是高度優化的,下面就詳細描述下。

slot的順序遍歷分配算法優化

咱們這裏主要用到了gcc的幾個內置函數:

  1. __builtin_clz:計算32位整數前導0的個數
  2. __builtin_ctz:計算32位整數後置0的個數
  3. __builtin_clzll:計算64位整數前導0的個數
  4. __builtin_ctzll:計算64位整數後置0的個數

其實這四個相似,咱們這裏就拿第一說明好了,爲何要使用__builtin_clz呢?其實就是爲了在一個32位端裏面,快速查找某個空閒位的索引,這樣就能快速定位某個空閒塊的位置了。

好比有一個32位的位段信息整數:x,計算對應空閒位0的索引,主須要:__builtin_clz(~x)

簡單吧,因爲__builtin_clz這些內置函數,gcc用匯編針對不一樣平臺高度優化過的,計算起來至關的快,那若是不是gcc的編譯器怎麼辦呢?

不要緊,咱們能夠本身用c實現個優化版本的,固然徹底能夠彙編繼續優化,這裏就先給個c的實現:

static __tb_inline__ tb_size_t tb_bits_cl0_u32_be_inline(tb_uint32_t x)
    {
        // check
        tb_check_return_val(x, 32);

        // done
        tb_size_t n = 31;
        if (x & 0xffff0000) { n -= 16;  x >>= 16;   }
        if (x & 0xff00)     { n -= 8;   x >>= 8;    }
        if (x & 0xf0)       { n -= 4;   x >>= 4;    }
        if (x & 0xc)        { n -= 2;   x >>= 2;    }
        if (x & 0x2)        { n--;                  }
        return n;
    }

說白了,就是每次對半開,來減小判斷次數,比起每次一位一位的枚舉遍歷,這種已是至關高效了,更況且還有__builtin_clz呢。

接下來就看下具體的遍歷過程:

  1. 按4/8字節對齊位段的起始地址
  2. 每次按4/8字節遍歷位段數據,遍歷過程利用cpu cache的大小,針對性的作循環展開,來優化性能。
  3. 經過判斷 !(x + 1) 來快速過濾 0xffffffff 這些已經滿了的位段,進一步提升遍歷效率。
  4. 若是某個位段不是0xffffffff,則經過__builtin_clz(~x)計算實際的空閒塊索引,並進行實際的分配。
  5. 最後若是這個的32位的位段沒有被分配滿,能夠把它進行緩存,來爲下次分配作預測。

字符串內存池:string_pool

講到這,TBOX的內存池管理模型,基本算是大概講完了,這裏就簡單提下string_pool,即:字符串池

string_pool主要針對上層應用而言的,針對某些頻繁使用小型字符串,而且重複率很高的模塊,就能夠經過string_pool進行優化,進一步減小內存使用,string_pool內部經過引用計數+哈希表維護,針對相同的字符串只保存一份。

例如能夠用於cookies中字符串維護、http中header部分的字符串維護等等。。

切換全局的內存分配器

tbox的默認內存分配,是徹底基於本身的內存池架構,支持內存的快速分配,和對碎片的優化,而且支持各類內存泄露、溢出檢測。

若是不想用tbox內置的默認內存分配管理,也能夠靈活切換到其餘分配模式,由於tbox如今已經徹底支持allocator架構,
只要在init階段傳入不一樣的分配器模型,就能快速切換分配模式,例如:

默認內存分配器

tbox默認的初始化採用了默認的tbox內存管理,他會默認啓用內存池維護、碎片優化、內存泄露溢出檢測等全部特性。

tb_init(tb_null, tb_null);

上面的初始化等價於:

tb_init(tb_null, tb_default_allocator(tb_null, 0));

默認的分配器,一般狀況下都會直接調用系統malloc使用系統原生native內存,只是在此基礎上多了層內存管理和內存檢測支持,若是想要在一塊連續內存上徹底託管,可使用下面的方式:

tb_init(tb_null, tb_default_allocator((tb_byte_t*)malloc(300 * 1024 * 1024), 300 * 1024 * 1024));

靜態內存分配器

咱們也能夠直接採用一整塊靜態buffer上進行維護,啓用內存泄露溢出檢測等全部特性,這個跟tb_default_allocator的區別就是,
這個分配器比較輕量,內部的數據結構簡單,佔用內存少,適合低資源環境,好比在一些嵌入式環境,用這個分配器資源利用率更高些。

!> 可是這個allocator不支持碎片優化,容易產生碎片。

tb_init(tb_null, tb_static_allocator((tb_byte_t*)malloc(300 * 1024 * 1024), 300 * 1024 * 1024));

原生內存分配器

徹底使用系統native內存分配,內部不作任何處理和數據維護,全部特性依賴系統環境,因此內存池和內存檢測等特性也是不支持的,至關於直接透傳給了malloc等系統分配接口。

用戶能夠根據本身的須要,若是不想使用tbox內置的內存池維護,就可使用此分配器。

tb_init(tb_null, tb_native_allocator());

虛擬內存分配器

v1.6.4版本以後,tbox新提供了一種分配器類型:虛擬內存分配器,主要用來分配一些超大塊的內存。

一般,用戶並不須要它,由於tbox默認的內存分配器內部自動會對超大塊的內存塊,切換到虛擬內存池去分配,不過若是用戶想要強制切換到虛擬內存分配,也能夠經過下面的方式切換使用:

tb_init(tb_null, tb_virtual_allocator());

自定義內存分配器

若是以爲這些分配器仍是不夠用,能夠自定義本身的內存分配器,讓tbox去使用,自定義的方式也很簡單,這裏拿tb_native_allocator的實現代碼爲例:

static tb_pointer_t tb_native_allocator_malloc(tb_allocator_ref_t allocator, tb_size_t size __tb_debug_decl__)
{
    // trace
    tb_trace_d("malloc(%lu) at %s(): %lu, %s", size, func_, line_, file_);

    // malloc it
    return malloc(size);
}
static tb_pointer_t tb_native_allocator_ralloc(tb_allocator_ref_t allocator, tb_pointer_t data, tb_size_t size __tb_debug_decl__)
{
    // trace
    tb_trace_d("realloc(%p, %lu) at %s(): %lu, %s", data, size, func_, line_, file_);

    // realloc it
    return realloc(data, size);
}
static tb_bool_t tb_native_allocator_free(tb_allocator_ref_t allocator, tb_pointer_t data __tb_debug_decl__)
{
    // trace    
    tb_trace_d("free(%p) at %s(): %lu, %s", data, func_, line_, file_);

    // free it
    return free(data);
}

而後咱們初始化下我們本身實現的native分配器:

tb_allocator_t myallocator    = {0};
myallocator.type              = TB_ALLOCATOR_NATIVE;
myallocator.malloc            = tb_native_allocator_malloc;
myallocator.ralloc            = tb_native_allocator_ralloc;
myallocator.free              = tb_native_allocator_free;

是否是很簡單,須要注意的是,上面的__tb_debug_decl__宏裏面聲明瞭一些debug信息,例如_file, _func, _line等內存分配時候記錄的信息,
你能夠在debug的時候打印出來,作調試,也能夠利用這些信息本身去處理一些高級的內存檢測操做,可是這些在release下,是不可獲取的

因此處理的時候,須要使用__tb_debug__宏,來分別處理。。

將myallocator傳入tb_init接口後,以後 tb_malloc/tb_ralloc/tb_free/... 等全部tbox內存分配接口都會切到新的allocator上進行分配。。

tb_init(tb_null, &myallocator);

固然若是想直接從一個特定的allocator上進行分配,還能夠直接調用allocator的分配接口來實現:

tb_allocator_malloc(&myallocator, 10);
tb_allocator_ralloc(&myallocator, data, 100);
tb_allocator_free(&myallocator, data);

內存分配接口

數據分配接口

此類接口能夠直接分配內存數據,不過返回的是tb_pointer_t類型數據,經過用戶須要本身作類型強轉才能訪問。

!> 其中malloc0這種後綴帶0字樣的接口,分配的內存會自動作內存清0操做。

tb_free(data)                               
tb_malloc(size)                             
tb_malloc0(size)                            
tb_nalloc(item, size)                       
tb_nalloc0(item, size)                      
tb_ralloc(data, size)

字符串分配接口

tbox也提供了字符串類型的便捷分配,操做的數據類型直接就是tb_char_t*,省去了額外的強轉過程。

tb_malloc_cstr(size)                        
tb_malloc0_cstr(size)                       
tb_nalloc_cstr(item, size)                  
tb_nalloc0_cstr(item, size)                 
tb_ralloc_cstr(data, size)

字節數據分配接口

這個也是數據分配接口,惟一的區別就是,默認作了tb_byte_t*類型的強轉處理,訪問數據讀寫訪問。

tb_malloc_bytes(size)                       
tb_malloc0_bytes(size)                      
tb_nalloc_bytes(item, size)                 
tb_nalloc0_bytes(item, size)                
tb_ralloc_bytes(data, size)

struct結構數據分配接口

若是要分配一些struct數據,那麼此類接口自帶了struct類型強轉處理。

tb_malloc_type(type)                        
tb_malloc0_type(type)                       
tb_nalloc_type(item, type)                  
tb_nalloc0_type(item, type)                 
tb_ralloc_type(data, item, type)

使用方式以下:

typedef struct __xxx_t
{
    tb_int_t dummy;

}xxx_t;

xxx_t* data = tb_malloc0_type(xxx_t);
if (data)
{
    data->dummy = 0;
    tb_free(data);
}

能夠看到,咱們省去了類型轉換過程,因此這是個提供必定便利性的輔助接口。

地址對齊數據分配接口

若是咱們有時候要求分配出來的內存數據地址,必須是按照指定大小對齊過的,就可使用此類接口:

tb_align_free(data)                         
tb_align_malloc(size, align)                
tb_align_malloc0(size, align)               
tb_align_nalloc(item, size, align)          
tb_align_nalloc0(item, size, align)         
tb_align_ralloc(data, size, align)

例如:

tb_pointer_t data = tb_align_malloc(1234, 16);

實際分配出來的data數據地址是16字節對齊的。

若是是按8字節對齊的內存數據分配,也能夠經過下面的接口來分配,此類接口對64bits系統上作了優化,並沒作什麼特殊處理:

#if TB_CPU_BIT64
#   define tb_align8_free(data)                     tb_free((tb_pointer_t)data)
#   define tb_align8_malloc(size)                   tb_malloc(size)
#   define tb_align8_malloc0(size)                  tb_malloc0(size)
#   define tb_align8_nalloc(item, size)             tb_nalloc(item, size)
#   define tb_align8_nalloc0(item, size)            tb_nalloc0(item, size)
#   define tb_align8_ralloc(data, size)             tb_ralloc((tb_pointer_t)data, size)
#else
#   define tb_align8_free(data)                     tb_align_free((tb_pointer_t)data)
#   define tb_align8_malloc(size)                   tb_align_malloc(size, 8)
#   define tb_align8_malloc0(size)                  tb_align_malloc0(size, 8)
#   define tb_align8_nalloc(item, size)             tb_align_nalloc(item, size, 8)
#   define tb_align8_nalloc0(item, size)            tb_align_nalloc0(item, size, 8)
#   define tb_align8_ralloc(data, size)             tb_align_ralloc((tb_pointer_t)data, size, 8)
#endif

內存檢測

TBOX的內存分配在調試模式下,能夠檢測支持內存泄露和越界,並且還能精肯定位到出問題的那塊內存具體分配位置,和函數調用堆棧。

要使用tbox的內存檢測功能,只須要切換到debug模式編譯:

$ xmake f -m debug
$ xmake

內存泄露檢測

!> 泄露檢測,必須在程序完整退出,確保調用了tb_exit()接口後才能觸發檢測。

內存泄露的檢測必須在程序退出的前一刻,調用tb_exit()的時候,纔會執行,若是有泄露,會有詳細輸出到終端上。

tb_void_t tb_demo_leak()
    {
        tb_pointer_t data = tb_malloc0(10);
    }

輸出:

[tbox]: [error]: leak: 0x7f9d5b058908 at tb_static_fixed_pool_dump(): 735, memory/impl/static_fixed_pool.c
    [tbox]: [error]: data: from: tb_demo_leak(): 43, memory/check.c
    [tbox]: [error]:     [0x000001050e742a]: 0   demo.b                              0x00000001050e742a tb_fixed_pool_malloc0_ + 186
    [tbox]: [error]:     [0x000001050f972b]: 1   demo.b                              0x00000001050f972b tb_small_pool_malloc0_ + 507
    [tbox]: [error]:     [0x000001050f593c]: 2   demo.b                              0x00000001050f593c tb_pool_malloc0_ + 540
    [tbox]: [error]:     [0x00000105063cd7]: 3   demo.b                              0x0000000105063cd7 tb_demo_leak + 55
    [tbox]: [error]:     [0x00000105063e44]: 4   demo.b                              0x0000000105063e44 tb_demo_memory_check_main + 20
    [tbox]: [error]:     [0x0000010505b08e]: 5   demo.b                              0x000000010505b08e main + 878
    [tbox]: [error]:     [0x007fff8c95a5fd]: 6   libdyld.dylib                       0x00007fff8c95a5fd start + 1
    [tbox]: [error]:     [0x00000000000002]: 7   ???                                 0x0000000000000002 0x0 + 2
    [tbox]: [error]: data: 0x7f9d5b058908, size: 10, patch: cc

內存越界檢測

越界溢出的檢測,是實時完成的,並且對libc也作了插樁,因此對經常使用strcpy,memset等的使用,都回去檢測

tb_void_t tb_demo_overflow()
    {
        tb_pointer_t data = tb_malloc0(10);
        if (data)
        {
            tb_memset(data, 0, 11);
            tb_free(data);
        }
    }

輸出:

[tbox]: [memset]: [overflow]: [0x0 x 11] => [0x7f950b044508, 10]
    [tbox]: [memset]: [overflow]: [0x0000010991a1c7]: 0   demo.b                              0x000000010991a1c7 tb_memset + 151
    [tbox]: [memset]: [overflow]: [0x000001098a2d01]: 1   demo.b                              0x00000001098a2d01 tb_demo_overflow + 97
    [tbox]: [memset]: [overflow]: [0x000001098a3044]: 2   demo.b                              0x00000001098a3044 tb_demo_memory_check_main + 20
    [tbox]: [memset]: [overflow]: [0x0000010989a28e]: 3   demo.b                              0x000000010989a28e main + 878
    [tbox]: [memset]: [overflow]: [0x007fff8c95a5fd]: 4   libdyld.dylib                       0x00007fff8c95a5fd start + 1
    [tbox]: [memset]: [overflow]: [0x00000000000002]: 5   ???                                 0x0000000000000002 0x0 + 2
    [tbox]:     [malloc]: [from]: data: from: tb_demo_overflow(): 12, memory/check.c
    [tbox]:     [malloc]: [from]:     [0x0000010992662a]: 0   demo.b                              0x000000010992662a tb_fixed_pool_malloc0_ + 186
    [tbox]:     [malloc]: [from]:     [0x0000010993892b]: 1   demo.b                              0x000000010993892b tb_small_pool_malloc0_ + 507
    [tbox]:     [malloc]: [from]:     [0x00000109934b3c]: 2   demo.b                              0x0000000109934b3c tb_pool_malloc0_ + 540
    [tbox]:     [malloc]: [from]:     [0x000001098a2cd7]: 3   demo.b                              0x00000001098a2cd7 tb_demo_overflow + 55
    [tbox]:     [malloc]: [from]:     [0x000001098a3044]: 4   demo.b                              0x00000001098a3044 tb_demo_memory_check_main + 20
    [tbox]:     [malloc]: [from]:     [0x0000010989a28e]: 5   demo.b                              0x000000010989a28e main + 878
    [tbox]:     [malloc]: [from]:     [0x007fff8c95a5fd]: 6   libdyld.dylib                       0x00007fff8c95a5fd start + 1
    [tbox]:     [malloc]: [from]:     [0x00000000000002]: 7   ???                                 0x0000000000000002 0x0 + 2
    [tbox]:     [malloc]: [from]: data: 0x7f950b044508, size: 10, patch: cc
    [tbox]:     [malloc]: [from]: data: first 10-bytes:
    [tbox]: ===================================================================================================================================================
    [tbox]: 00000000   00 00 00 00  00 00 00 00  00 00                                                                         ..........
    [tbox]: [error]: abort at tb_memset(): 255, libc/string/memset.c

內存重疊覆蓋檢測

若是兩塊內存的copy發生了重疊,有可能會覆蓋掉部分數據,致使bug,所以TBOX對此也作了些檢測。

tb_void_t tb_demo_overlap()
    {
        tb_pointer_t data = tb_malloc(10);
        if (data)
        {
            tb_memcpy(data, (tb_byte_t const*)data + 1, 5);
            tb_free(data);
        }
    }

輸出

[tbox]: [memcpy]: [overlap]: [0x7fe9b5042509, 5] => [0x7fe9b5042508, 5]
    [tbox]: [memcpy]: [overlap]: [0x000001094403b8]: 0   demo.b                              0x00000001094403b8 tb_memcpy + 632
    [tbox]: [memcpy]: [overlap]: [0x000001093c99f9]: 1   demo.b                              0x00000001093c99f9 tb_demo_overlap + 105
    [tbox]: [memcpy]: [overlap]: [0x000001093c9a44]: 2   demo.b                              0x00000001093c9a44 tb_demo_memory_check_main + 20
    [tbox]: [memcpy]: [overlap]: [0x000001093c0c8e]: 3   demo.b                              0x00000001093c0c8e main + 878
    [tbox]: [memcpy]: [overlap]: [0x007fff8c95a5fd]: 4   libdyld.dylib                       0x00007fff8c95a5fd start + 1
    [tbox]: [memcpy]: [overlap]: [0x00000000000002]: 5   ???                                 0x0000000000000002 0x0 + 2
    [tbox]:     [malloc]: [from]: data: from: tb_demo_overlap(): 58, memory/check.c
    [tbox]:     [malloc]: [from]:     [0x0000010945eadb]: 0   demo.b                              0x000000010945eadb tb_small_pool_malloc_ + 507
    [tbox]:     [malloc]: [from]:     [0x0000010945b23c]: 1   demo.b                              0x000000010945b23c tb_pool_malloc_ + 540
    [tbox]:     [malloc]: [from]:     [0x000001093c99c7]: 2   demo.b                              0x00000001093c99c7 tb_demo_overlap + 55
    [tbox]:     [malloc]: [from]:     [0x000001093c9a44]: 3   demo.b                              0x00000001093c9a44 tb_demo_memory_check_main + 20
    [tbox]:     [malloc]: [from]:     [0x000001093c0c8e]: 4   demo.b                              0x00000001093c0c8e main + 878
    [tbox]:     [malloc]: [from]:     [0x007fff8c95a5fd]: 5   libdyld.dylib                       0x00007fff8c95a5fd start + 1
    [tbox]:     [malloc]: [from]:     [0x00000000000002]: 6   ???                                 0x0000000000000002 0x0 + 2
    [tbox]:     [malloc]: [from]: data: 0x7fe9b5042508, size: 10, patch: cc
    [tbox]:     [malloc]: [from]: data: first 10-bytes:
    [tbox]: ===================================================================================================================================================
    [tbox]: 00000000   CC CC CC CC  CC CC CC CC  CC CC                                                                         ..........
    [tbox]: [error]: abort at tb_memcpy(): 125, libc/string/memcpy.c

內存雙重釋放檢測

tb_void_t tb_demo_free2()
    {
        tb_pointer_t data = tb_malloc0(10);
        if (data)
        {
            tb_free(data);
            tb_free(data);
        }
    }

輸出

[tbox]: [assert]: expr[((impl->used_info)[(index) >> 3] & (0x1 << ((index) & 7)))]: double free data: 0x7fd93386c708 at tb_static_fixed_pool_free(): 612, memory/impl/static_fixed_pool.c
    [tbox]:     [0x0000010c9f553c]: 0   demo.b                              0x000000010c9f553c tb_static_fixed_pool_free + 972
    [tbox]:     [0x0000010c9ee7a9]: 1   demo.b                              0x000000010c9ee7a9 tb_fixed_pool_free_ + 713
    [tbox]:     [0x0000010ca01ff5]: 2   demo.b                              0x000000010ca01ff5 tb_small_pool_free_ + 885
    [tbox]:     [0x0000010c9fdb4f]: 3   demo.b                              0x000000010c9fdb4f tb_pool_free_ + 751
    [tbox]:     [0x0000010c96ac8e]: 4   demo.b                              0x000000010c96ac8e tb_demo_free2 + 158
    [tbox]:     [0x0000010c96ae44]: 5   demo.b                              0x000000010c96ae44 tb_demo_memory_check_main + 20
    [tbox]:     [0x0000010c96208e]: 6   demo.b                              0x000000010c96208e main + 878
    [tbox]:     [0x007fff8c95a5fd]: 7   libdyld.dylib                       0x00007fff8c95a5fd start + 1
    [tbox]:     [0x00000000000002]: 8   ???                                 0x0000000000000002 0x0 + 2
    [tbox]: [error]: free(0x7fd93386c708) failed! at tb_demo_free2(): 37, memory/check.c at tb_static_fixed_pool_free(): 649, memory/impl/static_fixed_pool.c
    [tbox]: [error]: data: from: tb_demo_free2(): 33, memory/check.c
    [tbox]: [error]:     [0x0000010c9ee42a]: 0   demo.b                              0x000000010c9ee42a tb_fixed_pool_malloc0_ + 186
    [tbox]: [error]:     [0x0000010ca0072b]: 1   demo.b                              0x000000010ca0072b tb_small_pool_malloc0_ + 507
    [tbox]: [error]:     [0x0000010c9fc93c]: 2   demo.b                              0x000000010c9fc93c tb_pool_malloc0_ + 540
    [tbox]: [error]:     [0x0000010c96ac27]: 3   demo.b                              0x000000010c96ac27 tb_demo_free2 + 55
    [tbox]: [error]:     [0x0000010c96ae44]: 4   demo.b                              0x000000010c96ae44 tb_demo_memory_check_main + 20
    [tbox]: [error]:     [0x0000010c96208e]: 5   demo.b                              0x000000010c96208e main + 878
    [tbox]: [error]:     [0x007fff8c95a5fd]: 6   libdyld.dylib                       0x00007fff8c95a5fd start + 1
    [tbox]: [error]:     [0x00000000000002]: 7   ???                                 0x0000000000000002 0x0 + 2
    [tbox]: [error]: data: 0x7fd93386c708, size: 10, patch: cc
    [tbox]: [error]: data: first 10-bytes:
    [tbox]: ===================================================================================================================================================
    [tbox]: 00000000   00 00 00 00  00 00 00 00  00 00                                                                         ..........
    [tbox]: [error]: abort at tb_static_fixed_pool_free(): 655, memory/impl/static_fixed_pool.c
我的主頁
我的項目
相關文章
相關標籤/搜索