1# Adding MemoryInfra Tracing to a Component
2
3If you have a component that manages memory allocations, you should be
4registering and tracking those allocations with Chrome's MemoryInfra system.
5This lets you:
6
7 * See an overview of your allocations, giving insight into total size and
8   breakdown.
9 * Understand how your allocations change over time and how they are impacted by
10   other parts of Chrome.
11 * Catch regressions in your component's allocations size by setting up
12   telemetry tests which monitor your allocation sizes under certain
13   circumstances.
14
15Some existing components that use MemoryInfra:
16
17 * **Discardable Memory**: Tracks usage of discardable memory throughout Chrome.
18 * **GPU**: Tracks OpenGL and other GPU object allocations.
19 * **V8**: Tracks the heap size for JS.
20
21[TOC]
22
23## Overview
24
25In order to hook into Chrome's MemoryInfra system, your component needs to do
26two things:
27
28 1. Create a [`MemoryDumpProvider`][mdp] for your component.
29 2. Register and unregister you dump provider with the
30    [`MemoryDumpManager`][mdm].
31
32[mdp]: https://chromium.googlesource.com/chromium/src/+/master/base/trace_event/memory_dump_provider.h
33[mdm]: https://chromium.googlesource.com/chromium/src/+/master/base/trace_event/memory_dump_manager.h
34
35## Creating a Memory Dump Provider
36
37You can implement a [`MemoryDumpProvider`][mdp] as a stand-alone class, or as an
38additional interface on an existing class. For example, this interface is
39frequently implemented on classes which manage a pool of allocations (see
40[`cc::ResourcePool`][resource-pool] for an example).
41
42A `MemoryDumpProvider` has one basic job, to implement `OnMemoryDump`. This
43function is responsible for iterating over the resources allocated or tracked by
44your component, and creating a [`MemoryAllocatorDump`][mem-alloc-dump] for each
45using [`ProcessMemoryDump::CreateAllocatorDump`][pmd]. A simple example:
46
47```cpp
48bool MyComponent::OnMemoryDump(const MemoryDumpArgs& args,
49                               ProcessMemoryDump* process_memory_dump) {
50  for (const auto& allocation : my_allocations_) {
51    auto* dump = process_memory_dump->CreateAllocatorDump(
52        "path/to/my/component/allocation_" + allocation.id().ToString());
53    dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
54                    base::trace_event::MemoryAllocatorDump::kUnitsBytes,
55                    allocation.size_bytes());
56
57    // While you will typically have a kNameSize entry, you can add additional
58    // entries to your dump with free-form names. In this example we also dump
59    // an object's "free_size", assuming the object may not be entirely in use.
60    dump->AddScalar("free_size",
61                    base::trace_event::MemoryAllocatorDump::kUnitsBytes,
62                    allocation.free_size_bytes());
63  }
64}
65```
66
67For many components, this may be all that is needed. See
68[Handling Shared Memory Allocations](#Handling-Shared-Memory-Allocations) and
69[Suballocations](#Suballocations) for information on more complex use cases.
70
71[resource-pool]:  https://chromium.googlesource.com/chromium/src/+/master/cc/resources/resource_pool.h
72[mem-alloc-dump]: https://chromium.googlesource.com/chromium/src/+/master/base/trace_event/memory_allocator_dump.h
73[pmd]:            https://chromium.googlesource.com/chromium/src/+/master/base/trace_event/process_memory_dump.h
74
75## Registering a Memory Dump Provider
76
77Once you have created a [`MemoryDumpProvider`][mdp], you need to register it
78with the [`MemoryDumpManager`][mdm] before the system can start polling it for
79memory information. Registration is generally straightforward, and involves
80calling `MemoryDumpManager::RegisterDumpProvider`:
81
82```cpp
83// Each process uses a singleton |MemoryDumpManager|.
84base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
85    my_memory_dump_provider_, my_single_thread_task_runner_);
86```
87
88In the above code, `my_memory_dump_provider_` is the `MemoryDumpProvider`
89outlined in the previous section. `my_single_thread_task_runner_` is more
90complex and may be a number of things:
91
92 * Most commonly, if your component is always used from the main message loop,
93   `my_single_thread_task_runner_` may just be
94   [`base::ThreadTaskRunnerHandle::Get()`][task-runner-handle].
95 * If your component already uses a custom `base::SingleThreadTaskRunner` for
96   executing tasks on a specific thread, you should likely use this runner.
97
98[task-runner-handle]: https://chromium.googlesource.com/chromium/src/+/master/base/thread_task_runner_handle.h
99
100## Unregistration
101
102Unregistration must happen on the thread belonging to the
103`SingleThreadTaskRunner` provided at registration time. Unregistering on another
104thread can lead to race conditions if tracing is active when the provider is
105unregistered.
106
107```cpp
108base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
109      my_memory_dump_provider_);
110```
111
112## Handling Shared Memory Allocations
113
114When an allocation is shared between two components, it may be useful to dump
115the allocation in both components, but you also want to avoid double-counting
116the allocation. This can be achieved using the concept of _ownership edges_.
117An ownership edge represents that the _source_ memory allocator dump owns a
118_target_ memory allocator dump. If multiple source dumps own a single target,
119then the cost of that target allocation will be split between the sources.
120Additionally, importance can be added to a specific ownership edge, allowing
121the highest importance source of that edge to claim the entire cost of the
122target.
123
124In the typical case, you will use [`ProcessMemoryDump`][pmd] to create a shared
125global allocator dump. This dump will act as the target of all
126component-specific dumps of a specific resource:
127
128```cpp
129// Component 1 is going to create a dump, source_mad, for an allocation,
130// alloc_, which may be shared with other components / processes.
131MyAllocationType* alloc_;
132base::trace_event::MemoryAllocatorDump* source_mad;
133
134// Component 1 creates and populates source_mad;
135...
136
137// In addition to creating a source dump, we must create a global shared
138// target dump. This dump should be created with a unique global ID which can be
139// generated any place the allocation is used. I recommend adding a global ID
140// generation function to the allocation type.
141base::trace_event::MemoryAllocatorDumpGUID guid(alloc_->GetGUIDString());
142
143// From this global ID we can generate the parent allocator dump.
144base::trace_event::MemoryAllocatorDump* target_mad =
145    process_memory_dump->CreateSharedGlobalAllocatorDump(guid);
146
147// We now create an ownership edge from the source dump to the target dump.
148// When creating an edge, you can assign an importance to this edge. If all
149// edges have the same importance, the size of the allocation will be split
150// between all sources which create a dump for the allocation. If one
151// edge has higher importance than the others, its source will be assigned the
152// full size of the allocation.
153const int kImportance = 1;
154process_memory_dump->AddOwnershipEdge(
155    source_mad->guid(), target_mad->guid(), kImportance);
156```
157
158If an allocation is being shared across process boundaries, it may be useful to
159generate a global ID which incorporates the ID of the local process, preventing
160two processes from generating colliding IDs. As it is not recommended to pass a
161process ID between processes for security reasons, a function
162`MemoryDumpManager::GetTracingProcessId` is provided which generates a unique ID
163per process that can be passed with the resource without security concerns.
164Frequently this ID is used to generate a global ID that is based on the
165allocated resource's ID combined with the allocating process' tracing ID.
166
167## Suballocations
168
169Another advanced use case involves tracking sub-allocations of a larger
170allocation. For instance, this is used in
171[`gpu::gles2::TextureManager`][texture-manager] to dump both the suballocations
172which make up a texture. To create a suballocation, instead of calling
173[`ProcessMemoryDump::CreateAllocatorDump`][pmd] to create a
174[`MemoryAllocatorDump`][mem-alloc-dump], you call
175[`ProcessMemoryDump::AddSubAllocation`][pmd], providing the ID of the parent
176allocation as the first parameter.
177
178[texture-manager]: https://chromium.googlesource.com/chromium/src/+/master/gpu/command_buffer/service/texture_manager.cc
179