1 // Copyright 2024 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <array> 17 #include <cstddef> 18 #include <cstdint> 19 #include <cstring> 20 #include <type_traits> 21 22 #include "pw_assert/assert.h" 23 #include "pw_bloat/bloat_this_binary.h" 24 #include "pw_bytes/span.h" 25 26 #ifndef PW_ALLOCATOR_SIZE_REPORTER_BASE 27 #include "pw_allocator/allocator.h" 28 #include "pw_allocator/null_allocator.h" 29 #endif // PW_ALLOCATOR_SIZE_REPORTER_BASE 30 31 namespace pw::allocator { 32 33 /// Utility class for generating allocator size reports. 34 /// 35 /// The `pw_bloat` module can be used to compare the size of binaries. This 36 /// class facilitates creating binaries with and without a given allocator type. 37 /// 38 /// To create a size report: 39 /// 1. Make a copy of //pw_allocator/size_report/base.cc 40 /// 2. Instantiate your allocator and pass it to `MeasureAllocator`. 41 /// 3. Create build target(s) for your binary, and a `pw_size_diff` target 42 /// that compares it to "$dir_pw_allocator/size_report:base". 43 class SizeReporter final { 44 public: 45 /// Nested type used for exercising an allocator. 46 struct Foo final { 47 std::array<std::byte, 16> buffer; 48 }; 49 50 /// Nested type used for exercising an allocator. 51 struct Bar { 52 Foo foo; 53 size_t number; 54 BarBar55 Bar(size_t number_) : number(number_) { 56 std::memset(foo.buffer.data(), 0, foo.buffer.size()); 57 } 58 }; 59 60 /// Nested type used for exercising an allocator. 61 struct Baz { 62 Foo foo; 63 uint16_t id; 64 }; 65 SetBaseline()66 void SetBaseline() { 67 pw::bloat::BloatThisBinary(); 68 ByteSpan bytes = buffer(); 69 Bar* bar = new (bytes.data()) Bar(0); 70 PW_ASSERT(bar != nullptr); 71 std::destroy_at(bar); 72 #ifndef PW_ALLOCATOR_SIZE_REPORTER_BASE 73 // Include the NullAllocator to bake in the costs of the base `Allocator`. 74 Allocator& allocator = GetNullAllocator(); 75 PW_ASSERT(allocator.Allocate(Layout::Of<Foo>()) == nullptr); 76 #endif // PW_ALLOCATOR_SIZE_REPORTER_BASE 77 } 78 buffer()79 ByteSpan buffer() { return buffer_; } 80 81 #ifndef PW_ALLOCATOR_SIZE_REPORTER_BASE 82 /// Exercises an allocator as part of a size report. 83 /// 84 /// @param[in] allocator The allocator to exercise. Will be ignored if not 85 /// derived from `Allocator`. Measure(Allocator & allocator)86 void Measure(Allocator& allocator) { 87 // Measure `Layout::Of`. 88 Layout layout = Layout::Of<Foo>(); 89 90 // Measure `Allocate`. 91 void* ptr = allocator.Allocate(layout); 92 93 // Measure `Reallocate`. 94 allocator.Resize(ptr, sizeof(Bar)); 95 96 // Measure `Reallocate`. 97 ptr = allocator.Reallocate(ptr, Layout::Of<Baz>()); 98 99 // Measure `Deallocate`. 100 allocator.Deallocate(ptr); 101 102 // Measure `New`. 103 Foo* foo = allocator.template New<Foo>(); 104 105 // Measure `Delete`. 106 allocator.Delete(foo); 107 108 // Measure `MakeUnique`. 109 UniquePtr<Foo> unique_foo = allocator.template MakeUnique<Foo>(); 110 PW_ASSERT(ptr == nullptr || unique_foo != nullptr); 111 } 112 #endif // PW_ALLOCATOR_SIZE_REPORTER_BASE 113 114 private: 115 std::array<std::byte, 0x400> buffer_; 116 }; 117 118 } // namespace pw::allocator 119