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 |