| OLD | NEW |
| 1 # Adding MemoryInfra Tracing to a Component | 1 This document has moved to [//docs/memory-infra/adding_memory_infra_tracing.md](
/docs/memory-infra/adding_memory_infra_tracing.md). |
| 2 | 2 |
| 3 If you have a component that manages memory allocations, you should be | |
| 4 registering and tracking those allocations with Chrome's MemoryInfra system. | |
| 5 This 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 | |
| 15 Some 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 | |
| 25 In order to hook into Chrome's MemoryInfra system, your component needs to do | |
| 26 two 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 | |
| 37 You can implement a [`MemoryDumpProvider`][mdp] as a stand-alone class, or as an | |
| 38 additional interface on an existing class. For example, this interface is | |
| 39 frequently implemented on classes which manage a pool of allocations (see | |
| 40 [`cc::ResourcePool`][resource-pool] for an example). | |
| 41 | |
| 42 A `MemoryDumpProvider` has one basic job, to implement `OnMemoryDump`. This | |
| 43 function is responsible for iterating over the resources allocated or tracked by | |
| 44 your component, and creating a [`MemoryAllocatorDump`][mem-alloc-dump] for each | |
| 45 using [`ProcessMemoryDump::CreateAllocatorDump`][pmd]. A simple example: | |
| 46 | |
| 47 ```cpp | |
| 48 bool 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 | |
| 67 For 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/res
ources/resource_pool.h | |
| 72 [mem-alloc-dump]: https://chromium.googlesource.com/chromium/src/+/master/base/t
race_event/memory_allocator_dump.h | |
| 73 [pmd]: https://chromium.googlesource.com/chromium/src/+/master/base/t
race_event/process_memory_dump.h | |
| 74 | |
| 75 ## Registering a Memory Dump Provider | |
| 76 | |
| 77 Once you have created a [`MemoryDumpProvider`][mdp], you need to register it | |
| 78 with the [`MemoryDumpManager`][mdm] before the system can start polling it for | |
| 79 memory information. Registration is generally straightforward, and involves | |
| 80 calling `MemoryDumpManager::RegisterDumpProvider`: | |
| 81 | |
| 82 ```cpp | |
| 83 // Each process uses a singleton |MemoryDumpManager|. | |
| 84 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( | |
| 85 my_memory_dump_provider_, my_single_thread_task_runner_); | |
| 86 ``` | |
| 87 | |
| 88 In the above code, `my_memory_dump_provider_` is the `MemoryDumpProvider` | |
| 89 outlined in the previous section. `my_single_thread_task_runner_` is more | |
| 90 complex 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/ba
se/thread_task_runner_handle.h | |
| 99 | |
| 100 ## Unregistration | |
| 101 | |
| 102 Unregistration must happen on the thread belonging to the | |
| 103 `SingleThreadTaskRunner` provided at registration time. Unregistering on another | |
| 104 thread can lead to race conditions if tracing is active when the provider is | |
| 105 unregistered. | |
| 106 | |
| 107 ```cpp | |
| 108 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( | |
| 109 my_memory_dump_provider_); | |
| 110 ``` | |
| 111 | |
| 112 ## Handling Shared Memory Allocations | |
| 113 | |
| 114 When an allocation is shared between two components, it may be useful to dump | |
| 115 the allocation in both components, but you also want to avoid double-counting | |
| 116 the allocation. This can be achieved using the concept of _ownership edges_. | |
| 117 An ownership edge represents that the _source_ memory allocator dump owns a | |
| 118 _target_ memory allocator dump. If multiple source dumps own a single target, | |
| 119 then the cost of that target allocation will be split between the sources. | |
| 120 Additionally, importance can be added to a specific ownership edge, allowing | |
| 121 the highest importance source of that edge to claim the entire cost of the | |
| 122 target. | |
| 123 | |
| 124 In the typical case, you will use [`ProcessMemoryDump`][pmd] to create a shared | |
| 125 global allocator dump. This dump will act as the target of all | |
| 126 component-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. | |
| 131 MyAllocationType* alloc_; | |
| 132 base::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. | |
| 141 base::trace_event::MemoryAllocatorDumpGUID guid(alloc_->GetGUIDString()); | |
| 142 | |
| 143 // From this global ID we can generate the parent allocator dump. | |
| 144 base::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. | |
| 153 const int kImportance = 1; | |
| 154 process_memory_dump->AddOwnershipEdge( | |
| 155 source_mad->guid(), target_mad->guid(), kImportance); | |
| 156 ``` | |
| 157 | |
| 158 If an allocation is being shared across process boundaries, it may be useful to | |
| 159 generate a global ID which incorporates the ID of the local process, preventing | |
| 160 two processes from generating colliding IDs. As it is not recommended to pass a | |
| 161 process ID between processes for security reasons, a function | |
| 162 `MemoryDumpManager::GetTracingProcessId` is provided which generates a unique ID | |
| 163 per process that can be passed with the resource without security concerns. | |
| 164 Frequently this ID is used to generate a global ID that is based on the | |
| 165 allocated resource's ID combined with the allocating process' tracing ID. | |
| 166 | |
| 167 ## Suballocations | |
| 168 | |
| 169 Another advanced use case involves tracking sub-allocations of a larger | |
| 170 allocation. For instance, this is used in | |
| 171 [`gpu::gles2::TextureManager`][texture-manager] to dump both the suballocations | |
| 172 which 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 | |
| 176 allocation as the first parameter. | |
| 177 | |
| 178 [texture-manager]: https://chromium.googlesource.com/chromium/src/+/master/gpu/c
ommand_buffer/service/texture_manager.cc | |
| OLD | NEW |