C/C++ API Reference
Loading...
Searching...
No Matches
tracking_allocator.h
1// Copyright 2023 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 <cstddef>
17#include <cstdint>
18#include <cstring>
19
20#include "pw_allocator/allocator.h"
21#include "pw_allocator/capability.h"
22#include "pw_allocator/metrics.h"
23#include "pw_assert/assert.h"
24#include "pw_metric/metric.h"
25#include "pw_preprocessor/compiler.h"
26#include "pw_result/result.h"
27#include "pw_status/status.h"
28#include "pw_status/status_with_size.h"
29
30namespace pw::allocator {
31
33
37static constexpr struct AddTrackingAllocatorAsChild {
38} kAddTrackingAllocatorAsChild = {};
39
48template <typename MetricsType>
50 public:
51 TrackingAllocator(metric::Token token, Allocator& allocator)
52 : Allocator(allocator.capabilities() | kImplementsGetRequestedLayout),
53 allocator_(allocator),
54 metrics_(token) {}
55
56 template <typename OtherMetrics>
57 TrackingAllocator(metric::Token token,
60 : TrackingAllocator(token, parent) {
61 parent.metric_group().Add(metric_group());
62 }
63
64 const metric::Group& metric_group() const { return metrics_.group(); }
65 metric::Group& metric_group() { return metrics_.group(); }
66
67 const MetricsType& metrics() const { return metrics_.metrics(); }
68
72 void UpdateDeferred() const { metrics_.UpdateDeferred(allocator_); }
73
74 private:
76 void* DoAllocate(Layout layout) override;
77
79 void DoDeallocate(void* ptr) override;
80
82 void DoDeallocate(void* ptr, Layout) override { DoDeallocate(ptr); }
83
85 bool DoResize(void* ptr, size_t new_size) override;
86
88 void* DoReallocate(void* ptr, Layout new_layout) override;
89
91 size_t DoGetAllocated() const override { return allocator_.GetAllocated(); }
92
94 Result<Layout> DoGetInfo(InfoType info_type, const void* ptr) const override {
95 return GetInfo(allocator_, info_type, ptr);
96 }
97
98 Allocator& allocator_;
99 mutable internal::Metrics<MetricsType> metrics_;
100};
101
102// Template method implementation.
103
104template <typename MetricsType>
106 if constexpr (internal::AnyEnabled<MetricsType>()) {
107 Layout requested = layout;
108 size_t allocated = allocator_.GetAllocated();
109 void* new_ptr = allocator_.Allocate(requested);
110 if (new_ptr == nullptr) {
111 metrics_.RecordFailure(requested.size());
112 return nullptr;
113 }
114 metrics_.IncrementAllocations();
115 metrics_.ModifyRequested(requested.size(), 0);
116 metrics_.ModifyAllocated(allocator_.GetAllocated(), allocated);
117 return new_ptr;
118 } else {
119 return allocator_.Allocate(layout);
120 }
121}
122
123template <typename MetricsType>
125 if constexpr (internal::AnyEnabled<MetricsType>()) {
126 Layout requested = Layout::Unwrap(GetRequestedLayout(ptr));
127 size_t allocated = allocator_.GetAllocated();
128 allocator_.Deallocate(ptr);
129 metrics_.IncrementDeallocations();
130 metrics_.ModifyRequested(0, requested.size());
131 metrics_.ModifyAllocated(allocator_.GetAllocated(), allocated);
132 } else {
133 allocator_.Deallocate(ptr);
134 }
135}
136
137template <typename MetricsType>
138bool TrackingAllocator<MetricsType>::DoResize(void* ptr, size_t new_size) {
139 if constexpr (internal::AnyEnabled<MetricsType>()) {
140 Layout requested = Layout::Unwrap(GetRequestedLayout(ptr));
141 size_t allocated = allocator_.GetAllocated();
142 if (!allocator_.Resize(ptr, new_size)) {
143 metrics_.RecordFailure(new_size);
144 return false;
145 }
146 metrics_.IncrementResizes();
147 metrics_.ModifyRequested(new_size, requested.size());
148 metrics_.ModifyAllocated(allocator_.GetAllocated(), allocated);
149 return true;
150 } else {
151 return allocator_.Resize(ptr, new_size);
152 }
153}
154
155template <typename MetricsType>
157 Layout new_layout) {
158 if constexpr (internal::AnyEnabled<MetricsType>()) {
159 // Check if possible to resize in place with no additional overhead.
160 Layout requested = Layout::Unwrap(GetRequestedLayout(ptr));
161 size_t allocated = allocator_.GetAllocated();
162 size_t new_size = new_layout.size();
163 if (allocator_.Resize(ptr, new_size)) {
164 metrics_.IncrementReallocations();
165 metrics_.ModifyRequested(new_size, requested.size());
166 metrics_.ModifyAllocated(allocator_.GetAllocated(), allocated);
167 return ptr;
168 }
169
170 // Need to move data to a brand new allocation.
171 // In order to properly record the peak allocation, this method needs to
172 // perform the steps of allocating, copying, and deallocating memory, and
173 // recording metrics in the interim steps.
174 Result<Layout> old_layout = GetUsableLayout(ptr);
175 if (!old_layout.ok()) {
176 metrics_.RecordFailure(new_size);
177 return nullptr;
178 }
179 void* new_ptr = allocator_.Allocate(new_layout);
180 if (new_ptr == nullptr) {
181 metrics_.RecordFailure(new_size);
182 return nullptr;
183 }
184 // Update with transient allocation to ensure peak metrics are correct.
185 size_t transient_allocated = allocator_.GetAllocated();
186 metrics_.ModifyAllocated(transient_allocated, allocated);
187 if (ptr != nullptr) {
188 std::memcpy(new_ptr, ptr, std::min(new_size, old_layout->size()));
189 allocator_.Deallocate(ptr);
190 }
191 metrics_.IncrementReallocations();
192 metrics_.ModifyRequested(new_size, requested.size());
193 metrics_.ModifyAllocated(allocator_.GetAllocated(), transient_allocated);
194 return new_ptr;
195 } else {
196 return allocator_.Reallocate(ptr, new_layout);
197 }
198}
199
200// TODO(b/326509341): This is an interim alias to facilitate refactoring
201// downstream consumers of `TrackingAllocator` to add a template parameter.
202//
203// The following migration steps are complete:
204// 1. Downstream consumers will be updated to use `TrackingAllocatorImpl<...>`.
205// 2. The iterim `TrackingAllocator` class will be removed.
206// 3. `TrackingAllocatorImpl<...>` will be renamed to `TrackingAllocator<...>`,
207// with a `TrackingAllocatorImpl<...>` alias pointing to it.
208//
209// The following migration steps remain:
210// 4. Downstream consumers will be updated to use `TrackingAllocator<...>`.
211// 5. The `TrackingAllocatorImpl<...>` alias will be removed.
212template <typename MetricsType>
214
216
217} // namespace pw::allocator
Definition: allocator.h:36
constexpr Allocator()=default
TODO(b/326509341): Remove when downstream consumers migrate.
size_t GetAllocated() const
Definition: allocator.h:346
Definition: poll.h:25
Definition: layout.h:58
Definition: tracking_allocator.h:49
void UpdateDeferred() const
Definition: tracking_allocator.h:72
size_t DoGetAllocated() const override
Definition: tracking_allocator.h:91
Result< Layout > DoGetInfo(InfoType info_type, const void *ptr) const override
Definition: tracking_allocator.h:94
void DoDeallocate(void *ptr, Layout) override
Definition: tracking_allocator.h:82
Definition: metrics.h:195
bool DoResize(void *ptr, size_t new_size) override
Definition: tracking_allocator.h:138
void DoDeallocate(void *ptr) override
Definition: tracking_allocator.h:124
void * DoReallocate(void *ptr, Layout new_layout) override
Definition: tracking_allocator.h:156
void * DoAllocate(Layout layout) override
Definition: tracking_allocator.h:105
Definition: tracking_allocator.h:37