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