# 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/+/master/base/trace_event/memory_dump_provider.h
[mdm]: https://chromium.googlesource.com/chromium/src/+/master/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/+/master/cc/resources/resource_pool.h
[mem-alloc-dump]: https://chromium.googlesource.com/chromium/src/+/master/base/trace_event/memory_allocator_dump.h
[pmd]:            https://chromium.googlesource.com/chromium/src/+/master/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/+/master/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/+/master/gpu/command_buffer/service/texture_manager.cc
