
See this doc: go/chromium-coil-change for more info BUG=1210385 Change-Id: I6cd8548301e360bae8e71384dbd8ef11aa6c985f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2902904 Reviewed-by: My Nguyen <myy@chromium.org> Commit-Queue: My Nguyen <myy@chromium.org> Auto-Submit: John Palmer <jopalmer@chromium.org> Cr-Commit-Position: refs/heads/master@{#885851}
179 lines
8.1 KiB
Markdown
179 lines
8.1 KiB
Markdown
# Adding MemoryInfra Tracing to a Component
|
|
|
|
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:
|
|
|
|
* See an overview of your allocations, giving insight into total size and
|
|
breakdown.
|
|
* Understand how your allocations change over time and how they are impacted by
|
|
other parts of Chrome.
|
|
* Catch regressions in your component's allocations size by setting up
|
|
telemetry tests which monitor your allocation sizes under certain
|
|
circumstances.
|
|
|
|
Some existing components that use MemoryInfra:
|
|
|
|
* **Discardable Memory**: Tracks usage of discardable memory throughout Chrome.
|
|
* **GPU**: Tracks OpenGL and other GPU object allocations.
|
|
* **V8**: Tracks the heap size for JS.
|
|
|
|
[TOC]
|
|
|
|
## Overview
|
|
|
|
In order to hook into Chrome's MemoryInfra system, your component needs to do
|
|
two things:
|
|
|
|
1. Create a [`MemoryDumpProvider`][mdp] for your component.
|
|
2. Register and unregister you dump provider with the
|
|
[`MemoryDumpManager`][mdm].
|
|
|
|
[mdp]: https://chromium.googlesource.com/chromium/src/+/main/base/trace_event/memory_dump_provider.h
|
|
[mdm]: https://chromium.googlesource.com/chromium/src/+/main/base/trace_event/memory_dump_manager.h
|
|
|
|
## Creating a Memory Dump Provider
|
|
|
|
You can implement a [`MemoryDumpProvider`][mdp] 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`][resource-pool] 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`][mem-alloc-dump] for each
|
|
using [`ProcessMemoryDump::CreateAllocatorDump`][pmd]. A simple example:
|
|
|
|
```cpp
|
|
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](#Handling-Shared-Memory-Allocations) and
|
|
[Suballocations](#Suballocations) for information on more complex use cases.
|
|
|
|
[resource-pool]: https://chromium.googlesource.com/chromium/src/+/main/cc/resources/resource_pool.h
|
|
[mem-alloc-dump]: https://chromium.googlesource.com/chromium/src/+/main/base/trace_event/memory_allocator_dump.h
|
|
[pmd]: https://chromium.googlesource.com/chromium/src/+/main/base/trace_event/process_memory_dump.h
|
|
|
|
## Registering a Memory Dump Provider
|
|
|
|
Once you have created a [`MemoryDumpProvider`][mdp], you need to register it
|
|
with the [`MemoryDumpManager`][mdm] before the system can start polling it for
|
|
memory information. Registration is generally straightforward, and involves
|
|
calling `MemoryDumpManager::RegisterDumpProvider`:
|
|
|
|
```cpp
|
|
// 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:
|
|
|
|
* Most commonly, if your component is always used from the main message loop,
|
|
`my_single_thread_task_runner_` may just be
|
|
[`base::ThreadTaskRunnerHandle::Get()`][task-runner-handle].
|
|
* If your component already uses a custom `base::SingleThreadTaskRunner` for
|
|
executing tasks on a specific thread, you should likely use this runner.
|
|
|
|
[task-runner-handle]: https://chromium.googlesource.com/chromium/src/+/main/base/thread_task_runner_handle.h
|
|
|
|
## Unregistration
|
|
|
|
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.
|
|
|
|
```cpp
|
|
base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
|
|
my_memory_dump_provider_);
|
|
```
|
|
|
|
## Handling Shared Memory Allocations
|
|
|
|
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`][pmd] to create a shared
|
|
global allocator dump. This dump will act as the target of all
|
|
component-specific dumps of a specific resource:
|
|
|
|
```cpp
|
|
// 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.
|
|
|
|
## Suballocations
|
|
|
|
Another advanced use case involves tracking sub-allocations of a larger
|
|
allocation. For instance, this is used in
|
|
[`gpu::gles2::TextureManager`][texture-manager] to dump both the suballocations
|
|
which make up a texture. To create a suballocation, instead of calling
|
|
[`ProcessMemoryDump::CreateAllocatorDump`][pmd] to create a
|
|
[`MemoryAllocatorDump`][mem-alloc-dump], you call
|
|
[`ProcessMemoryDump::AddSubAllocation`][pmd], providing the ID of the parent
|
|
allocation as the first parameter.
|
|
|
|
[texture-manager]: https://chromium.googlesource.com/chromium/src/+/main/gpu/command_buffer/service/texture_manager.cc
|