• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
21 #include "pw_allocator/allocator.h"
22 #include "pw_allocator/block/small_block.h"
23 #include "pw_allocator/block_allocator.h"
24 #include "pw_bloat/bloat_this_binary.h"
25 #include "pw_bytes/span.h"
26 
27 namespace pw::allocator::size_report {
28 
29 /// Default block type to use for tests.
30 using BlockType = SmallBlock;
31 
32 /// Type used for exercising an allocator.
33 struct Foo final {
34   std::array<std::byte, 16> buffer;
35 };
36 
37 /// Type used for exercising an allocator.
38 struct Bar {
39   Foo foo;
40   size_t number;
41 
BarBar42   Bar(size_t number_) : number(number_) {
43     std::memset(foo.buffer.data(), 0, foo.buffer.size());
44   }
45 };
46 
47 /// Type used for exercising an allocator.
48 struct Baz {
49   Foo foo;
50   uint16_t id;
51 };
52 
53 /// Returns a view of a statically allocated array of bytes.
54 ByteSpan GetBuffer();
55 
56 /// Measures the size of common functions and data without any allocators.
57 ///
58 /// @param[in]  mask        A bitmap that can be passed to `PW_BLOAT_COND` and
59 ///                         `PW_BLOAT_EXPR`. See those macros for details.
60 int SetBaseline(uint32_t mask);
61 
62 /// Exercises a block implementation as part of a size report.
63 ///
64 /// @tparam     BlockType   The type of block to create and exercise.
65 /// @param[in]  mask        A bitmap that can be passed to `PW_BLOAT_COND` and
66 ///                         `PW_BLOAT_EXPR`. See those macros for details.
67 template <typename BlockType>
68 int MeasureBlock(uint32_t mask);
69 
70 /// Exercises a bucket as part of a size report.
71 ///
72 /// @tparam     BucketType  The type of bucket to create and exercise.
73 /// @param[in]  mask        A bitmap that can be passed to `PW_BLOAT_COND` and
74 ///                         `PW_BLOAT_EXPR`. See those macros for details.
75 template <typename BucketType>
76 int MeasureBucket(BucketType& bucket, uint32_t mask);
77 
78 /// Exercises an allocator as part of a size report.
79 ///
80 /// @param[in]  allocator   The allocator to exercise.
81 /// @param[in]  mask        A bitmap that can be passed to `PW_BLOAT_COND` and
82 ///                         `PW_BLOAT_EXPR`. See those macros for details.
83 int MeasureAllocator(Allocator& allocator, uint32_t mask);
84 
85 /// Exercises a block allocator as part of a size report.
86 ///
87 /// @param[in]  allocator   The block allocator to exercise.
88 /// @param[in]  mask        A bitmap that can be passed to `PW_BLOAT_COND` and
89 ///                         `PW_BLOAT_EXPR`. See those macros for details.
90 int MeasureBlockAllocator(BlockAllocator<BlockType>& allocator, uint32_t mask);
91 
92 // Template method implementations.
93 
94 template <typename BlockType>
MeasureBlock(uint32_t mask)95 int MeasureBlock(uint32_t mask) {
96   if (SetBaseline(mask) != 0) {
97     return 1;
98   }
99 
100   // Measure `Init`.
101   auto result = BlockType::Init(GetBuffer());
102   BlockType* block = *result;
103 
104   // Measure `UsableSpace`.
105   std::byte* bytes = block->UsableSpace();
106 
107   // Measure `FromUsableSpace`.
108   block = BlockType::FromUsableSpace(bytes);
109 
110   if constexpr (is_allocatable_v<BlockType>) {
111     // Measure `AllocFirst`.
112     Layout foo = Layout::Of<Foo>();
113     auto block_result = BlockType::AllocFirst(std::move(block), foo);
114     if (!block_result.ok()) {
115       return 1;
116     }
117 
118     BlockType* first_block = block_result.block();
119     block = first_block->Next();
120 
121     // Measure `AllocLast`.
122     if constexpr (is_alignable_v<BlockType>) {
123       constexpr Layout kOverlyAligned(128, 64);
124       block_result = BlockType::AllocLast(std::move(block), kOverlyAligned);
125     } else {
126       Layout baz = Layout::Of<Baz>();
127       block_result = BlockType::AllocLast(std::move(block), baz);
128     }
129     if (!block_result.ok()) {
130       return 1;
131     }
132 
133     BlockType* last_block = block_result.block();
134     block = last_block->Prev();
135 
136     // Measure `Resize`.
137     block_result = block->Resize(sizeof(Bar));
138     if (!block_result.ok()) {
139       return 1;
140     }
141 
142     // Measure `Free`.
143     block_result = BlockType::Free(std::move(first_block));
144     return block_result.ok() ? 0 : 1;
145   }
146 }
147 
148 template <typename BucketType>
MeasureBucket(BucketType & bucket,uint32_t mask)149 int MeasureBucket(BucketType& bucket, uint32_t mask) {
150   if (int rc = SetBaseline(mask); rc != 0) {
151     return rc;
152   }
153   if (int rc = MeasureBlock<BlockType>(mask); rc != 0) {
154     return rc;
155   }
156 
157   auto result = BlockType::Init(GetBuffer());
158   BlockType* unallocated = *result;
159 
160   // Exercise `Add`.
161   std::array<BlockType*, 4> blocks;
162   for (size_t i = 0; i < blocks.size(); ++i) {
163     Layout layout(16 * (i + 1), 1);
164     auto block_result = BlockType::AllocFirst(std::move(unallocated), layout);
165     blocks[i] = block_result.block();
166     unallocated = blocks[i]->Next();
167     PW_BLOAT_COND(bucket.Add(*blocks[i]), mask);
168   }
169 
170   // Exercise `Remove`.
171   PW_BLOAT_COND(bucket.Remove(*blocks[0]), mask);
172 
173   // Exercise `RemoveCompatible`.
174   BlockType* compatible = bucket.RemoveCompatible(Layout(32, 1));
175   PW_BLOAT_COND(compatible != nullptr, mask);
176 
177   // Exercise `RemoveAny`.
178   BlockType* any_block = bucket.RemoveAny();
179   PW_BLOAT_COND(any_block != nullptr, mask);
180 
181   // Exercise `empty` and `Clear`.
182   PW_BLOAT_COND(!bucket.empty(), mask);
183   PW_BLOAT_EXPR(bucket.Clear(), mask);
184   return bucket.empty() ? 0 : 1;
185 }
186 
187 }  // namespace pw::allocator::size_report
188