If you have a component that manages memory allocations, you should be registering and tracking those allocations with Chrome's MemoryInfra system. This lets you:
Some existing components that use MemoryInfra:
In order to hook into Chrome's MemoryInfra system, your component needs to do two things:
MemoryDumpProvider
for your component.MemoryDumpManager
.You can implement a MemoryDumpProvider
as a stand-alone class, or as an additional interface on an existing class. For example, this interface is frequently implemented on classes which manage a pool of allocations (see cc::ResourcePool
for an example).
A MemoryDumpProvider
has one basic job, to implement OnMemoryDump
. This function is responsible for iterating over the resources allocated or tracked by your component, and creating a MemoryAllocatorDump
for each using ProcessMemoryDump::CreateAllocatorDump
. A simple example:
bool MyComponent::OnMemoryDump(const MemoryDumpArgs& args, ProcessMemoryDump* process_memory_dump) { for (const auto& allocation : my_allocations_) { auto* dump = process_memory_dump->CreateAllocatorDump( "path/to/my/component/allocation_" + allocation.id().ToString()); dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, base::trace_event::MemoryAllocatorDump::kUnitsBytes, allocation.size_bytes()); // While you will typically have a kNameSize entry, you can add additional // entries to your dump with free-form names. In this example we also dump // an object's "free_size", assuming the object may not be entirely in use. dump->AddScalar("free_size", base::trace_event::MemoryAllocatorDump::kUnitsBytes, allocation.free_size_bytes()); } }
For many components, this may be all that is needed. See Handling Shared Memory Allocations and Suballocations for information on more complex use cases.
Once you have created a MemoryDumpProvider
, you need to register it with the MemoryDumpManager
before the system can start polling it for memory information. Registration is generally straightforward, and involves calling MemoryDumpManager::RegisterDumpProvider
:
// Each process uses a singleton |MemoryDumpManager|. base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( my_memory_dump_provider_, my_single_thread_task_runner_);
In the above code, my_memory_dump_provider_
is the MemoryDumpProvider
outlined in the previous section. my_single_thread_task_runner_
is more complex and may be a number of things:
my_single_thread_task_runner_
may just be [base::SingleThreadTaskRunner::GetCurrentDefault()
][task-runner-handle].base::SingleThreadTaskRunner
for executing tasks on a specific thread, you should likely use this runner.Unregistration must happen on the thread belonging to the SingleThreadTaskRunner
provided at registration time. Unregistering on another thread can lead to race conditions if tracing is active when the provider is unregistered.
base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( my_memory_dump_provider_);
When an allocation is shared between two components, it may be useful to dump the allocation in both components, but you also want to avoid double-counting the allocation. This can be achieved using the concept of ownership edges. An ownership edge represents that the source memory allocator dump owns a target memory allocator dump. If multiple source dumps own a single target, then the cost of that target allocation will be split between the sources. Additionally, importance can be added to a specific ownership edge, allowing the highest importance source of that edge to claim the entire cost of the target.
In the typical case, you will use ProcessMemoryDump
to create a shared global allocator dump. This dump will act as the target of all component-specific dumps of a specific resource:
// Component 1 is going to create a dump, source_mad, for an allocation, // alloc_, which may be shared with other components / processes. MyAllocationType* alloc_; base::trace_event::MemoryAllocatorDump* source_mad; // Component 1 creates and populates source_mad; ... // In addition to creating a source dump, we must create a global shared // target dump. This dump should be created with a unique global ID which can be // generated any place the allocation is used. I recommend adding a global ID // generation function to the allocation type. base::trace_event::MemoryAllocatorDumpGUID guid(alloc_->GetGUIDString()); // From this global ID we can generate the parent allocator dump. base::trace_event::MemoryAllocatorDump* target_mad = process_memory_dump->CreateSharedGlobalAllocatorDump(guid); // We now create an ownership edge from the source dump to the target dump. // When creating an edge, you can assign an importance to this edge. If all // edges have the same importance, the size of the allocation will be split // between all sources which create a dump for the allocation. If one // edge has higher importance than the others, its source will be assigned the // full size of the allocation. const int kImportance = 1; process_memory_dump->AddOwnershipEdge( source_mad->guid(), target_mad->guid(), kImportance);
If an allocation is being shared across process boundaries, it may be useful to generate a global ID which incorporates the ID of the local process, preventing two processes from generating colliding IDs. As it is not recommended to pass a process ID between processes for security reasons, a function MemoryDumpManager::GetTracingProcessId
is provided which generates a unique ID per process that can be passed with the resource without security concerns. Frequently this ID is used to generate a global ID that is based on the allocated resource‘s ID combined with the allocating process’ tracing ID.
Another advanced use case involves tracking sub-allocations of a larger allocation. For instance, this is used in gpu::gles2::TextureManager
to dump both the suballocations which make up a texture. To create a suballocation, instead of calling ProcessMemoryDump::CreateAllocatorDump
to create a MemoryAllocatorDump
, you call ProcessMemoryDump::AddSubAllocation
, providing the ID of the parent allocation as the first parameter.