chrome/chromium 上的內存管理模塊-allocator介紹

本文介紹chromium在不同平臺上 malloc/new 是如何封裝調用的。

從代碼中很容易發現,chromium的基礎代碼並不是僅僅使用“malloc”來分配內存
例如:
        renderer(Blink)大部分都是用chromium單獨設計的PartitionAlloc和BlinkGC(Oilpan)
        像javascript引擎V8這樣比較獨立的子系統使用自己的內存管理機制
        還有部分模塊會使用抽象化的內存管理模塊如 ShareMemory或者DiscardMemory,和上面的內存管理器類似,他們也有自己的頁面級內存管理器

背景:
allocator分配器的目標是在程序編譯階段實現定義malloc/new 在特定的平臺上實現哪些任務。與此相關的編譯選項包括 “use_allocator”和“win_use_allocator_shim”.

默認的實現方式是:

**windows平臺**
use_allocator        :winheap, 即windows系統默認的heap;
另外,static_libary(即 外部庫)通過設置win_use_allocator_shim會對malloc/new編譯出一個封裝層,該層對外提供安全的API調用,例如防止第三方模塊分配過大的內存而導致出現各種bug。

**linux平臺/CrOS**
use_allocator        :tcmalloc, 這個是基於third_party/tcmalloc/chromium下的tcmalloc拉出來的。設置use_allocator爲none,默認就會編譯系統(glibc)的符號表。

**Android平臺**
use_allocator        :none, 使用的allocator符號就是Android的libc(Bionic)。Bionic作爲系統的一部分,對於small devices或者內存緊張的設備來說就是最優的選擇。當前Bionic的malloc 符號取決於board的配置並且是可以
修改的(一般情況下Nexus使用dlmalloc或jemalloc)

**Mac/iOS**
use_allocator        :none,通常使用系統的分配器實現。

另外,當編譯爲asan/msan/syzyasan/valgrind 等內存調試模式的時候,allocator shim等是禁用的。

架構模型
allocator的設計目標是既能夠爲tcmalloc(使用該管理器的平臺)提供源文件,又可以爲windows shim layer提供linker flags。base是唯一一個依賴於allocator的模塊。除了少數可執行程序或動態鏈接庫,沒有其他模塊再依賴
於allocator模塊,這些可執行程序或動態鏈接庫直接或間接使用base模塊。
重要的一點,base模塊之外,不應該再有使用特殊的allocator分配的地方(如直接使用 third_party/tcmalloc),如果需要使用分配器功能的話,應使用base模塊裏面的抽象類來完成。(參見/base/allocator/allocator_exrension.hhe /base/memory/)

**爲什麼base要依賴於allocator**
這是因爲他要依賴於具體的allocator來提供服務。以前 base 被當作allocator是不可知的而被其他層用來引入分配屬性。這導致了使用上的混淆。
詳細參見[allocator cleanuo doc][url-allocator-cleanup]

鏈接單元(可執行程序或共享庫)通過某種方式依賴於base(大部分在代碼裏面)來自動獲取linker flag,以此來引入tcmalloc或者windows shim layer

**source code**
這個目錄下僅僅包含了allocator layer(即 shim),該層實現了在不同具體內存分配器之間轉換的功能。

tcmalloc的庫來自於chromium之外且放置在../../third_party/tcmalloc(目前,實際的路徑定義在allocator.gyp 文件)。third party 的源碼通過提供vendor-branch SCM的方式來跟蹤
chromium的特殊修改,以此來與上游部分的修改獨立出來。這樣做的目的是促使上游模塊來做適配修改這樣我們就不需要隨證時間的推移而重新拉出新的分支了。

**統一的allocator shim層**
在很多平臺上,chrome都會重寫 malloc /new 等方法,(包括相應的 free/delete 和其他一些方法)這是爲了強制的安全檢查以及最近啓用的新功能[memory-infra heap profiler][rul-memory-heap-profiler]

以前,不同的平臺上對於allocator  在代碼處理的地方方法都有自己不同的實現邏輯。統一的allocator shim模塊的設計目標就是在中間層定義一個標準的allocator 實現邏輯。

-- 文檔:[Allocator shim design doc][url-allocator-shim]
-- 進展: Linux CrOS和android上目前可用
-- bug跟蹤:[https://crbug.com/550886][crbug.com/550886].
-- 編譯標籤: ues_experimental_allocator_shim

**allocator shim模塊概覽**

allocator shim 模塊包含了三部分:

malloc & friends  symbols definition 

shimlayer implementation

Routing to allocator

-- libc symbols (malloc,calloc, free, ...) 

-- C++ symbols (operator new, delete, ...) 

-- glibc weak symbols  (__libc_malloc, ...)

-- Security checks    

-- Chain of dispatchers

    that can intercept

    and override 

    allocations

-- tcmalloc 

-- glibc   

-- Android  bionic   

-- WinHeap 

 


**1. malloc 的定義**
這部分主要重寫 malloc、 free 、operator new 、operator delete及friends並在allocator shim 內部實現這些方法的調用方式(next point)
這些在頭文件allocator_shim_override_* 中描述

*Linux和CrOS上*:這些內存操作方法在allocator_shim_override_libc_symbos.h(包括malloc free friends)和allocator_shim_override_cpp_symbos.h(包括operator new、operator delete、friends)中被聲明爲外部全局方法
這樣可以使主可執行程序所使用的malloc方法與任何第三方內存管理庫都可以適當的匹配。Linux上的符號解析是從跟鏈接單元開始的廣度優先搜索,這個是可執行程序(參見 EXECUTABLE AND LINKABLE FORMAT(ELF)- 便攜式格式規範))
另外,當tcmalloc作爲默認內存分配器的時候,一些其他的glibc 方法在allocator_shim_override_glibc_weak_symbos.h也會有定義。詳細的原因參見:The Linux/CrOS shim was introduced by
[crrev.com/1675143004](https://crrev.com/1675143004).

**android上**:(不同於linux和CrOS上的實現)在加載階段插入實現方法是不可能的,因爲android的進程都是android aygote fork出來的,zygote進程會預加載libc.so,而後面本地代碼的加載
使用過dlopen完成的(分配的方法是來自於dlopen所加載的庫實現的,加載在另外的空間)
這種情況下,該方案通過--Wl,-wrap,malloc 等鏈接flag代替鏈接時(即編譯時)包裝符號表的解決方案。

使用wrap flag會導致:
chrome中所有引用allocator symbos的代碼都要重寫爲引用__wrap_malloc和friends 。__wrap_malloc定義在文件allocator_shim_override_linker_wraped_symbos.h中,實現在allocator shim層中。
最原始的malloc(系統庫libc.so中定義的)引用可以通過特定的__real_malloc和friends來實現(它將在加載的時候被重置來對應 malloc)

總之,這種方法對動態加載是透明的,動態加載看到的仍然是未定義的malloc引用。這些方法將會對應libc.so的方法。
詳見: [crrev.com/1719433002](https://crrev.com/1719433002).

**2.shim layer層實現**
這部分主要是shim layer層的實現部分。包含了:
--一個獨立的鏈接調度表(指向malloc的函數指針結構體)。調度表可以在運行的時候動態插入(通過API:InsertAllocatorDispatch)。
它們可以攔截和重寫allocator方法。
--安全檢查(同過std::new_handler在malloc ~ failure過程中 釋放等等)

這些都在allocator_shim.cc內實現。

**3.最終的allocator 實現**
上面提到的調度結構鏈的最終實現單元是在編譯的時候定義死的,並最終將allocator指引到最終的實際分配器allocator(如上面背景部分描述的)。這個在allocator_shim_default_dispatch_to_*文件中有引述。

附錄:
略......


附:
linux下C的程序可以用wrap的方式替換系統malloc
     編譯加上選項:gcc -Wl,-wrap,malloc
     可以做到對malloc這個函數,linker會調用__wrap_malloc代替之, 若要調用原來的malloc函數__real_malloc;
     
例如,想把所有的malloc函數都作修改,以便讓malloc出的內存都是32字節對齊的。
我們可以給ld傳選項“­­wrap=malloc”, 告訴ld,我們將替換名稱爲malloc的函數。
接着再如下定義一個新的malloc函數:

void * __wrap_malloc( size_t size) {
  void* result;
  result=memalign(32, size);
  return result;
}
可以看到,程序中有個類似庫函數名稱的__wrap_malloc。
ld在遇到__wrap選項後,會使用__wrap_malloc函數名替換所有對malloc的調用。
並以此實現替換的作用。

那麼,如果還向調用原函數怎麼辦呢?
可以使用__real_malloc,這個函數名稱就對應了原來的malloc。
每次調用malloc時都打印生成的指針地址。

void * __wrap_malloc( size_t size) {
  void* result;
  result= __real_malloc( size);
  printf("Malloc: allocate %d byte mem, start at %p", size, result);

  return result;
}


在chromium的allocator上:
malloc->__wrap_malloc_ShimMalloc->alloc_function->RealMalloc->__real_malloc(android bionic)
libc上:
__real_malloc->je_malloc

/bionic/libc/bionic/malloc_common.cpp
 
#define Malloc(function)  je_ ## function
 
extern "C" void* malloc(size_t bytes) {
  auto _malloc = __libc_globals->malloc_dispatch.malloc;
  if (__predict_false(_malloc != nullptr)) {
    return _malloc(bytes);
  }
  return Malloc(malloc)(bytes); //=je_malloc()
}
 
Jemalloc的路徑:external/jemalloc/

#0  je_bitmap_set (bitmap=<optimized out>, binfo=<optimized out>, bit=<optimized out>) at external/jemalloc/include/jemalloc/internal/bitmap.h:176
176    external/jemalloc/include/jemalloc/internal/bitmap.h: No such file or directory.
(gdb) bt
#0  je_bitmap_set (bitmap=<optimized out>, binfo=<optimized out>, bit=<optimized out>) at external/jemalloc/include/jemalloc/internal/bitmap.h:176
#1  je_bitmap_sfu (bitmap=<error reading variable: DWARF-2 expression error: DW_OP_reg operations must be used either alone or in conjunction with DW_OP_piece or DW_OP_bit_piece.>, binfo=<optimized out>)
    at external/jemalloc/include/jemalloc/internal/bitmap.h:228
#2  arena_run_reg_alloc (run=0x90f82654, bin_info=<optimized out>) at external/jemalloc/src/arena.c:290
#3  arena_malloc_small (tsdn=<optimized out>, binind=11, arena=<optimized out>, zero=<optimized out>) at external/jemalloc/src/arena.c:2578
#4  je_arena_malloc_hard (tsdn=0x90f6d688, arena=<optimized out>, size=<optimized out>, ind=11, zero=<error reading variable: access outside bounds of object referenced via synthetic pointer>)
    at external/jemalloc/src/arena.c:2694
#5  0xadbcf302 in je_arena_malloc (arena=<optimized out>, size=<optimized out>, zero=<error reading variable: access outside bounds of object referenced via synthetic pointer>, 
    slow_path=<error reading variable: access outside bounds of object referenced via synthetic pointer>, tsdn=<optimized out>, ind=<optimized out>, tcache=<optimized out>)
    at external/jemalloc/include/jemalloc/internal/arena.h:1365
#6  je_iallocztm (size=<optimized out>, zero=<error reading variable: access outside bounds of object referenced via synthetic pointer>, 
    is_metadata=<error reading variable: access outside bounds of object referenced via synthetic pointer>, 
    slow_path=<error reading variable: access outside bounds of object referenced via synthetic pointer>, tsdn=<optimized out>, ind=<optimized out>, tcache=<optimized out>, arena=<optimized out>)
    at external/jemalloc/include/jemalloc/internal/jemalloc_internal.h:1061
#7  je_ialloc (tsd=<optimized out>, size=<optimized out>, zero=<error reading variable: access outside bounds of object referenced via synthetic pointer>, 
    slow_path=<error reading variable: access outside bounds of object referenced via synthetic pointer>, ind=<optimized out>) at external/jemalloc/include/jemalloc/internal/jemalloc_internal.h:1073
#8  ialloc_body (slow_path=<error reading variable: access outside bounds of object referenced via synthetic pointer>, size=<optimized out>, zero=<optimized out>, tsdn=<optimized out>, 
    usize=<optimized out>) at external/jemalloc/src/jemalloc.c:1611
#9  je_malloc (size=<optimized out>) at external/jemalloc/src/jemalloc.c:1650
#10 0x97836480 in ShimCppNew () at ../../base/allocator/allocator_shim.cc:171
#11 operator new () at ../../base/allocator/allocator_shim_override_cpp_symbols.h:19
#12 0x98a28576 in re2::Compiler::AllocInst () at ../../third_party/re2/src/re2/compile.cc:288
#13 0x98a287ec in re2::Compiler::ByteRange () at ../../third_party/re2/src/re2/compile.cc:403
#14 0x98a28a92 in re2::Compiler::Literal () at ../../third_party/re2/src/re2/compile.cc:846
#15 0x98a29970 in re2::Compiler::PostVisit () at ../../third_party/re2/src/re2/compile.cc:919
#16 0x98a29b24 in re2::Compiler::PostVisit () at ../../third_party/re2/src/re2/compile.cc:864
#17 0x98a29d34 in re2::Regexp::Walker<re2::Frag>::WalkInternal () at ../../third_party/re2/src/re2/walker-inl.h:210
#18 0x98a29e3c in WalkExponential () at ../../third_party/re2/src/re2/walker-inl.h:243
#19 re2::Compiler::Compile () at ../../third_party/re2/src/re2/compile.cc:1157
#20 0x98a32a58 in re2::RE2::Init () at ../../third_party/re2/src/re2/re2.cc:214
#21 0x98a32bba in re2::RE2::RE2 () at ../../third_party/re2/src/re2/re2.cc:112
#22 0x97bed83c in StringMismatch () at ../../gpu/config/gpu_control_list.cc:104
#23 0x97bef30e in gpu::GpuControlList::GpuControlListEntry::Contains () at ../../gpu/config/gpu_control_list.cc:1308
#24 0x97bef6a8 in gpu::GpuControlList::GpuControlListEntry::Contains () at ../../gpu/config/gpu_control_list.cc:1354
#25 0x97bf1a10 in gpu::GpuControlList::MakeDecision () at ../../gpu/config/gpu_control_list.cc:1523
#26 0x97fcfee2 in content::GpuDataManagerImplPrivate::UpdateGpuInfoHelper () at ../../content/browser/gpu/gpu_data_manager_impl_private.cc:639
#27 0x97fcd972 in content::GpuDataManagerImpl::UpdateGpuInfo () at ../../content/browser/gpu/gpu_data_manager_impl.cc:154
#28 0x97fd3ab8 in content::GpuProcessHost::OnInitialized () at ../../content/browser/gpu/gpu_process_host.cc:815
#29 0x97fd3002 in DispatchToMethodImpl<content::GpuProcessHost*, void (content::GpuProcessHost::*)(bool, gpu::GPUInfo const&, gpu::GpuFeatureInfo const&), std::__ndk1::tuple<bool, gpu::GPUInfo, gpu::GpuFeatureInfo> const&, 0u, 1u, 2u> () at ../../base/tuple.h:91
#30 DispatchToMethod<content::GpuProcessHost*, void (content::GpuProcessHost::*)(bool, gpu::GPUInfo const&, gpu::GpuFeatureInfo const&), std::__ndk1::tuple<bool, gpu::GPUInfo, gpu::GpuFeatureInfo> const&> () at ../../base/tuple.h:98
#31 DispatchToMethod<content::GpuProcessHost, void (content::GpuProcessHost::*)(bool, gpu::GPUInfo const&, gpu::GpuFeatureInfo const&), void, std::__ndk1::tuple<bool, gpu::GPUInfo, gpu::GpuFeatureInfo> >
    () at ../../ipc/ipc_message_templates.h:26
#32 IPC::MessageT<GpuHostMsg_Initialized_Meta, std::__ndk1::tuple<bool, gpu::GPUInfo, gpu::GpuFeatureInfo>, void>::Dispatch<content::GpuProcessHost, content::GpuProcessHost, void, void (content::GpuProcessHost::*)(bool, gpu::GPUInfo const&, gpu::GpuFeatureInfo const&)> () at ../../ipc/ipc_message_templates.h:121
#33 0x97fd30b2 in content::GpuProcessHost::OnMessageReceived () at ../../content/browser/gpu/gpu_process_host.cc:702
#34 0x9813795c in content::ChildProcessHostImpl::OnMessageReceived () at ../../content/common/child_process_host_impl.cc:245
#35 0x97c9215a in IPC::ChannelMojo::OnMessageReceived () at ../../ipc/ipc_channel_mojo.cc:414
#36 0x97c9540e in IPC::internal::MessagePipeReader::Receive () at ../../ipc/ipc_message_pipe_reader.cc:110
#37 0x97c9af82 in IPC::mojom::ChannelStubDispatch::Accept () at gen/ipc/ipc.mojom.cc:268
#38 0x97c9e218 in mojo::InterfaceEndpointClient::HandleValidatedMessage () at ../../mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:411
#39 0x97c97d08 in Accept () at ../../ipc/ipc_mojo_bootstrap.cc:755
#40 0x97c9cf64 in mojo::Connector::ReadSingleMessage () at ../../mojo/public/cpp/bindings/lib/connector.cc:284
#41 0x97c9d0a6 in mojo::Connector::ReadAllAvailableMessages () at ../../mojo/public/cpp/bindings/lib/connector.cc:311
#42 0x97b949e0 in Run () at ../../base/callback.h:80
#43 mojo::SimpleWatcher::OnHandleReady () at ../../mojo/public/cpp/system/simple_watcher.cc:262
#44 0x977dcbde in Run () at ../../base/callback.h:91
#45 base::debug::TaskAnnotator::RunTask () at ../../base/debug/task_annotator.cc:59
#46 0x977ed280 in base::MessageLoop::RunTask () at ../../base/message_loop/message_loop.cc:423
#47 0x977ed790 in base::MessageLoop::DeferOrRunPendingTask () at ../../base/message_loop/message_loop.cc:434
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章