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