• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1.. _module-pw_allocator-guides:
2
3======
4Guides
5======
6.. pigweed-module-subpage::
7   :name: pw_allocator
8
9.. _module-pw_allocator-get-started:
10
11-----------
12Get started
13-----------
14.. tab-set::
15
16   .. tab-item:: Bazel
17
18      Add ``@pigweed//pw_allocator`` to the ``deps`` list in your Bazel target:
19
20      .. code-block::
21
22         cc_library("...") {
23           # ...
24           deps = [
25             # ...
26             "@pigweed//pw_allocator",
27             # ...
28           ]
29         }
30
31      For a narrower dependency, depend on subtargets, e.g.
32      ``@pigweed//pw_allocator:tracking_allocator``.
33
34      This assumes ``@pigweed`` is the name you pulled Pigweed into your Bazel
35      ``WORKSPACE`` as.
36
37      This assumes that your Bazel ``WORKSPACE`` has a `repository`_ named
38      ``@pigweed`` that points to the upstream Pigweed repository.
39
40   .. tab-item:: GN
41
42      Add ``dir_pw_allocator`` to the ``deps`` list in your build target:
43
44      .. code-block::
45
46         pw_executable("...") {
47           # ...
48           deps = [
49             # ...
50             dir_pw_allocator,
51             # ...
52           ]
53         }
54
55      For a narrower dependency, depend on subtargets, e.g.
56      ``"$dir_pw_allocator:tracking_allocator"``.
57
58      If the build target is a dependency of other targets and includes
59      allocators in its public interface, e.g. its header file, use
60      ``public_deps`` instead of ``deps``.
61
62   .. tab-item:: CMake
63
64      Add ``pw_allocator`` to your ``pw_add_library`` or similar CMake target:
65
66      .. code-block::
67
68         pw_add_library(my_library STATIC
69           HEADERS
70             ...
71           PRIVATE_DEPS
72             # ...
73             pw_allocator
74             # ...
75         )
76
77      For a narrower dependency, depend on subtargets, e.g.
78      ``pw_allocator.tracking_allocator``, etc.
79
80      If the build target is a dependency of other targets and includes
81      allocators in its public interface, e.g. its header file, use
82      ``PUBLIC_DEPS`` instead of ``PRIVATE_DEPS``.
83
84.. _module-pw_allocator-module-configuration:
85
86--------------------
87Module configuration
88--------------------
89This module has configuration options that globally affect the behavior of
90``pw_allocator`` via compile-time configuration of this module, see the
91:ref:`module documentation <module-structure-compile-time-configuration>` for
92more details.
93
94Module configuration options include:
95
96- :ref:`module-pw_allocator-config-block_poison_interval` determines how
97  frequently blocks that implemented the
98  :ref:`module-pw_allocator-api-poisonable_block` mix-in should apply the poison
99  pattern on deallocation.
100- :ref:`module-pw_allocator-config-hardening` allows you to set how many
101  validation checks are enabled. Additional checks can detect more errors at the
102  cost of performance and code size.
103- :ref:`module-pw_allocator-config-suppress_deprecated_warnings` allows you to
104  silence warnings about deprecated interfaces. This is a temporary measure. It
105  is strongly advised to migrate away from deprecated interfaces as soon as
106  possible as they will eventually be removed.
107
108-----------------
109Inject allocators
110-----------------
111Routines that need to allocate memory dynamically should do so using the generic
112:ref:`module-pw_allocator-api-allocator` interface. By using dependency
113injection, memory users can be kept decoupled from the details of how memory is
114provided. This yields the most flexibility for modifying or replacing the
115specific allocators.
116
117Use the ``Allocator`` interface
118===============================
119Write or refactor your memory-using routines to take
120a pointer or reference to such an object and use the ``Allocate``,
121``Deallocate``, ``Reallocate``, and ``Resize`` methods to manage memory. For
122example:
123
124.. literalinclude:: examples/basic.cc
125   :language: cpp
126   :linenos:
127   :start-after: [pw_allocator-examples-basic-allocate]
128   :end-before: [pw_allocator-examples-basic-allocate]
129
130.. literalinclude:: examples/basic.cc
131   :language: cpp
132   :linenos:
133   :start-after: [pw_allocator-examples-basic-deallocate]
134   :end-before: [pw_allocator-examples-basic-deallocate]
135
136To invoke methods or objects that inject allocators now requires an allocator to
137have been constructed. The exact location where allocators should be
138instantiated will vary from project to project, but it's likely to be early in a
139program's lifecycle and in a device-specific location of the source tree.
140
141For initial testing on :ref:`target-host`, a simple allocator such as
142:ref:`module-pw_allocator-api-libc_allocator` can be used. This allocator is
143trivally constructed and simply wraps ``malloc`` and ``free``.
144
145Use New and Delete
146==================
147In addition to managing raw memory, an ``Allocator`` can also be used to create
148and destroy objects:
149
150.. literalinclude:: examples/basic.cc
151   :language: cpp
152   :linenos:
153   :start-after: [pw_allocator-examples-basic-new_delete]
154   :end-before: [pw_allocator-examples-basic-new_delete]
155
156Use UniquePtr<T>
157================
158Where possible, using `RAII`_ is a recommended approach for making memory
159management easier and less error-prone.
160:ref:`module-pw_allocator-api-unique_ptr` is a smart pointer that makes
161allocating and deallocating memory more transparent:
162
163.. literalinclude:: examples/basic.cc
164   :language: cpp
165   :linenos:
166   :start-after: [pw_allocator-examples-basic-make_unique]
167   :end-before: [pw_allocator-examples-basic-make_unique]
168
169Determine an allocation's Layout
170================================
171Several of the :ref:`module-pw_allocator-api-allocator` methods take a parameter
172of the :ref:`module-pw_allocator-api-layout` type. This type combines the size
173and alignment requirements of an allocation. It can be constructed directly, or
174if allocating memory for a specific type, by using a templated static method:
175
176.. literalinclude:: examples/block_allocator.cc
177   :language: cpp
178   :linenos:
179   :start-after: [pw_allocator-examples-block_allocator-layout_of]
180   :end-before: [pw_allocator-examples-block_allocator-layout_of]
181
182As stated above, you should generally try to keep allocator implementation
183details abstracted behind the :ref:`module-pw_allocator-api-allocator`
184interface. One exception to this guidance is when integrating allocators into
185existing code that assumes ``malloc`` and ``free`` semantics. Notably, ``free``
186does not take any parameters beyond a pointer describing the memory to be freed.
187
188.. literalinclude:: examples/block_allocator.cc
189   :language: cpp
190   :linenos:
191   :start-after: [pw_allocator-examples-block_allocator-malloc_free]
192   :end-before: [pw_allocator-examples-block_allocator-malloc_free]
193
194.. _module-pw_allocator-use-standard-library-containers:
195
196Use standard library containers
197===============================
198All of C++'s standard library containers are `AllocatorAwareContainers`_, with
199the exception of ``std::array``. These types include a template parameter used
200to specify an allocator type, and a constructor which takes a reference to an
201object of this type.
202
203While there are
204:ref:`module-pw_allocator-design-differences-with-polymorphic-allocators`, an
205:ref:`module-pw_allocator-api-allocator` can be used with these containers by
206wrapping them with a PMR adapter type,
207:ref:`module-pw_allocator-api-pmr_allocator`:
208
209.. literalinclude:: examples/pmr.cc
210   :language: cpp
211   :linenos:
212   :start-after: [pw_allocator-examples-pmr]
213   :end-before: [pw_allocator-examples-pmr]
214
215.. Warning::
216   Some of the standard library containers may add a significant amount of
217   additional code size and/or memory overhead. In particular, implementations
218   of ``std::deque`` are known to preallocate significant memory in order to
219   meet its complexity requirements, e.g. O(1) insertion at the front of the
220   deque.
221
222.. Warning::
223   The standard library containers expect their allocators to throw an exception
224   on allocation failure, and do not check for failure themselves. If
225   exceptions are disabled, :ref:`module-pw_allocator-api-pmr_allocator`
226   instead **asserts** that allocation succeeded. Care must be taken in this
227   case to ensure that memory is not exhausted.
228
229--------------------------
230Choose the right allocator
231--------------------------
232Once one or more routines are using allocators, you can consider how best to
233implement memory allocation for each particular scenario.
234
235.. TODO(b/378549332): Add a decision tree for selecting an allocator.
236
237Concrete allocator implementations
238==================================
239This module provides several allocator implementations. The following is an
240overview. Consult the :ref:`module-pw_allocator-api` for additional details.
241
242- :ref:`module-pw_allocator-api-libc_allocator`: Uses ``malloc``, ``realloc``,
243  and ``free``. This should only be used if the ``libc`` in use provides those
244  functions. This allocator is a stateless singleton that may be referenced
245  using ``GetLibCAllocator()``.
246- :ref:`module-pw_allocator-api-null_allocator`: Always fails. This may be
247  useful if allocations should be disallowed under specific circumstances.
248  This allocator is a stateless singleton that may be referenced using
249  ``GetNullAllocator()``.
250- :ref:`module-pw_allocator-api-bump_allocator`: Allocates objects out of a
251  region of memory and only frees them all at once when the allocator is
252  destroyed.
253- :ref:`module-pw_allocator-api-buddy_allocator`: Allocates objects out of a
254  blocks with sizes that are powers of two. Blocks are split evenly for smaller
255  allocations and merged on free.
256- :ref:`module-pw_allocator-api-block_allocator`: Tracks memory using
257  :ref:`module-pw_allocator-api-block`. Derived types use specific strategies
258  for how to choose a block to use to satisfy a request. See also
259  :ref:`module-pw_allocator-design-blocks`. Derived types include:
260
261  - :ref:`module-pw_allocator-api-first_fit_allocator`: Chooses the first
262    block that's large enough to satisfy a request. This strategy is very fast,
263    but may increase fragmentation.
264  - :ref:`module-pw_allocator-api-best_fit_allocator`: Chooses the
265    smallest block that's large enough to satisfy a request. This strategy
266    maximizes the avilable space for large allocations, but may increase
267    fragmentation and is slower.
268  - :ref:`module-pw_allocator-api-worst_fit_allocator`: Chooses the
269    largest block if it's large enough to satisfy a request. This strategy
270    minimizes the amount of memory in unusably small blocks, but is slower.
271  - :ref:`module-pw_allocator-api-bucket_block_allocator`: Sorts and stores
272    each free blocks in a :ref:`module-pw_allocator-api-bucket` with a given
273    maximum block inner size.
274
275- :ref:`module-pw_allocator-api-typed_pool`: Efficiently creates and
276  destroys objects of a single given type.
277
278Forwarding allocator implementations
279====================================
280This module provides several "forwarding" allocators, as described in
281:ref:`module-pw_allocator-design-forwarding`. The following is an overview.
282Consult the :ref:`module-pw_allocator-api` for additional details.
283
284- :ref:`module-pw_allocator-api-fallback_allocator`: Dispatches first to a
285  primary allocator, and, if that fails, to a secondary allocator.
286- :ref:`module-pw_allocator-api-pmr_allocator`: Adapts an allocator to be a
287  ``std::pmr::polymorphic_allocator``, which can be used with standard library
288  containers that `use allocators`_, such as ``std::pmr::vector<T>``.
289- :ref:`module-pw_allocator-api-synchronized_allocator`: Synchronizes access to
290  another allocator, allowing it to be used by multiple threads.
291- :ref:`module-pw_allocator-api-tracking_allocator`: Wraps another allocator and
292  records its usage.
293
294.. _module-pw_allocator-guide-custom_allocator:
295
296Custom allocator implementations
297================================
298If none of the allocator implementations provided by this module meet your
299needs, you can implement your allocator and pass it into any routine that uses
300the generic interface.
301
302:ref:`module-pw_allocator-api-allocator` uses an `NVI`_ pattern. To add a custom
303allocator implementation, you must at a miniumum implement the ``DoAllocate``
304and ``DoDeallocate`` methods.
305
306For example, the following is a forwarding allocator that simply writes to the
307log whenever a threshold is exceeded:
308
309.. literalinclude:: examples/public/examples/custom_allocator.h
310   :language: cpp
311   :linenos:
312   :start-after: [pw_allocator-examples-custom_allocator]
313
314.. literalinclude:: examples/custom_allocator.cc
315   :language: cpp
316   :linenos:
317   :start-after: [pw_allocator-examples-custom_allocator]
318
319There are also several optional methods you can provide:
320
321- If an implementation of ``DoResize`` isn't provided, then ``Resize`` will
322  always return false.
323- If an implementation of ``DoReallocate`` isn't provided, then ``Reallocate``
324  will try to ``Resize``, and, if unsuccessful, try to ``Allocate``, copy, and
325  ``Deallocate``.
326- If an implementation of ``DoGetInfo`` isn't provided, then ``GetInfo``
327  will always return ``pw::Status::Unimplmented``.
328
329Custom allocators can indicate which optional methods they implement and what
330optional behaviors they want from the base class by specifying
331:ref:`module-pw_allocator-api-capabilities` when invoking the base class
332constructor.
333
334.. TODO: b/328076428 - Make Deallocate optional once traits supporting
335   MonotonicAllocator are added.
336
337--------------------
338Measure memory usage
339--------------------
340You can observe how much memory is being used for a particular use case using a
341:ref:`module-pw_allocator-api-tracking_allocator`.
342
343.. literalinclude:: examples/metrics.cc
344   :language: cpp
345   :linenos:
346   :start-after: [pw_allocator-examples-metrics-custom_metrics1]
347   :end-before: [pw_allocator-examples-metrics-custom_metrics1]
348
349.. literalinclude:: examples/metrics.cc
350   :language: cpp
351   :linenos:
352   :start-after: [pw_allocator-examples-metrics-custom_metrics2]
353   :end-before: [pw_allocator-examples-metrics-custom_metrics2]
354
355Metric data can be retrieved according to the steps described in
356:ref:`module-pw_metric-exporting`, or by using the ``Dump`` method of
357:ref:`module-pw_metric-group`:
358
359.. literalinclude:: examples/metrics.cc
360   :language: cpp
361   :linenos:
362   :start-after: [pw_allocator-examples-metrics-dump]
363   :end-before: [pw_allocator-examples-metrics-dump]
364
365
366The ``CustomMetrics`` type used in the example above is a struct provided by the
367developer. You can create your own metrics structs that enable zero or more of
368the following metrics:
369
370- **requested_bytes**: The number of bytes currently requested from this
371  allocator.
372- **peak_requested_bytes**: The most bytes requested from this allocator at any
373  one time.
374- **cumulative_requested_bytes**: The total number of bytes that have been
375  requested from this allocator across its lifetime.
376- **allocated_bytes**: The number of bytes currently allocated by this
377  allocator.
378- **peak_allocated_bytes**: The most bytes allocated by this allocator at any
379  one time.
380- **cumulative_allocated_bytes**: The total number of bytes that have been
381  allocated by this allocator across its lifetime.
382- **num_allocations**: The number of allocation requests this allocator has
383  successfully completed.
384- **num_deallocations**: The number of deallocation requests this allocator has
385  successfully completed.
386- **num_resizes**: The number of resize requests this allocator has successfully
387  completed.
388- **num_reallocations**: The number of reallocation requests this allocator has
389  successfully completed.
390- **num_failures**: The number of requests this allocator has failed to
391  complete.
392
393If you only want a subset of these metrics, you can implement your own metrics
394struct. For example:
395
396.. literalinclude:: examples/metrics.cc
397   :language: cpp
398   :linenos:
399   :start-after: [pw_allocator-examples-metrics-custom_metrics1]
400   :end-before: [pw_allocator-examples-metrics-custom_metrics1]
401
402If you have multiple routines that share an underlying allocator that you want
403to track separately, you can create multiple tracking allocators that forward to
404the same underlying allocator:
405
406.. literalinclude:: examples/metrics.cc
407   :language: cpp
408   :linenos:
409   :start-after: [pw_allocator-examples-metrics-multiple_trackers]
410   :end-before: [pw_allocator-examples-metrics-multiple_trackers]
411
412Measure fragmentation
413=====================
414
415If you are using a :ref:`module-pw_allocator-api-block_allocator`, you can use
416the ``MeasureFragmentation`` method to examine how fragmented the heap is. This
417method returns a :ref:`module-pw_allocator-api-fragmentation` struct, which
418includes the "sum of squares" and the sum of the inner sizes of the current free
419blocks. On a platform or host with floating point support, you can divide the
420square root of the sum of squares by the sum to obtain a number that ranges from
4210 to 1 to indicate maximal and minimal fragmenation, respectively. Subtracting
422this number from 1 can give a more intuitive "fragmenation score".
423
424For example, consider a heap consisting of the following blocks:
425
426- 100 bytes in use.
427- 200 bytes free.
428- 50 bytes in use.
429- 10 bytes free.
430- 200 bytes in use.
431- 300 bytes free.
432
433For such a heap, ``MeasureFragmentation`` will return 130100 and 510. The above
434calculation gives a fragmentation score of ``1 - sqrt(130100) / 510``, which is
435approximately ``0.29``.
436
437.. TODO: b/328648868 - Add guide for heap-viewer and link to cli.rst.
438
439------------------------
440Detect memory corruption
441------------------------
442The :ref:`module-pw_allocator-design-blocks` provide a few different mechanisms
443to help detect memory corruptions when they happen. On every deallocation they
444will check the integrity of the block header and assert if it has been modified.
445
446Additionally, you can enable poisoning to detect additional memory corruptions
447such as use-after-frees. The :ref:`module-pw_allocator-module-configuration` for
448``pw_allocator`` includes the ``PW_ALLOCATOR_BLOCK_POISON_INTERVAL`` option. If
449a block derives from :ref:`module-pw_allocator-api-poisonable_block`, the
450allocator will "poison" every N-th block it frees. Allocators "poison" blocks by
451writing a set pattern to the usable memory, and later check on allocation that
452the pattern is intact. If it is not, something has illegally modified
453unallocated memory.
454
455----------------------
456Test custom allocators
457----------------------
458If you create your own allocator implementation, it's strongly recommended that
459you test it as well. If you're creating a forwarding allocator, you can use
460:ref:`module-pw_allocator-api-allocator_for_test`. This simple allocator
461provides its own backing storage and automatically frees any outstanding
462allocations when it goes out of scope. It also tracks the most recent values
463provided as parameters to the interface methods.
464
465For example, the following tests the custom allocator from
466:ref:`module-pw_allocator-guide-custom_allocator`:
467
468.. literalinclude:: examples/custom_allocator_test.cc
469   :language: cpp
470   :linenos:
471   :start-after: [pw_allocator-examples-custom_allocator-unit_test]
472   :end-before: [pw_allocator-examples-custom_allocator-unit_test]
473
474You can also extend the :ref:`module-pw_allocator-api-test_harness` to perform
475pseudorandom sequences of allocations and deallocations, e.g. as part of a
476performance test:
477
478.. literalinclude:: examples/public/examples/custom_allocator_test_harness.h
479   :language: cpp
480   :linenos:
481   :start-after: [pw_allocator-examples-custom_allocator-test_harness]
482
483.. literalinclude:: examples/custom_allocator_perf_test.cc
484   :language: cpp
485   :linenos:
486   :start-after: [pw_allocator-examples-custom_allocator-perf_test]
487
488Even better, you can easily add fuzz tests for your allocator. This module
489uses the :ref:`module-pw_allocator-api-test_harness` to integrate with
490:ref:`module-pw_fuzzer` and provide
491:ref:`module-pw_allocator-api-fuzzing_support`.
492
493.. literalinclude:: examples/custom_allocator_test.cc
494   :language: cpp
495   :linenos:
496   :start-after: [pw_allocator-examples-custom_allocator-fuzz_test]
497   :end-before: [pw_allocator-examples-custom_allocator-fuzz_test]
498
499-----------------------------
500Measure custom allocator size
501-----------------------------
502If you create your own allocator implementation, you may wish to measure its
503code size, similar to measurements in the module's own
504:ref:`module-pw_allocator-size-reports`. You can use ``pw_bloat`` and the
505:ref:`module-pw_allocator-api-size_reports` to create size reports as described
506in :ref:`bloat-howto`.
507
508For example, the C++ code for a size report binary might look like:
509
510.. literalinclude:: examples/size_report.cc
511   :language: cpp
512   :linenos:
513   :start-after: [pw_allocator-examples-size_report]
514
515The resulting binary could be compared with the binary produced from
516pw_allocator/size_report/first_fit.cc to identify just the code added in this
517case by ``CustomAllocator``.
518
519For example, the GN build rule to generate a size report might look liek:
520
521.. code-block::
522
523   pw_size_diff("size_report") {
524     title = "Example size report"
525     binaries = [
526       {
527         target = ":size_report"
528         base = "$dir_pw_allocator/size_report:first_fit"
529         label = "CustomAllocator"
530       },
531     ]
532   }
533
534The size report produced by this rule would render as:
535
536.. TODO: b/388905812 - Re-enable the size report.
537.. .. include:: examples/custom_allocator_size_report
538.. include:: ../size_report_notice
539
540.. _AllocatorAwareContainers: https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer
541.. _NVI: https://en.wikipedia.org/wiki/Non-virtual_interface_pattern
542.. _RAII: https://en.cppreference.com/w/cpp/language/raii
543.. _repository: https://bazel.build/concepts/build-ref#repositories
544.. _use allocators: https://en.cppreference.com/w/cpp/memory/uses_allocator
545