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
67 private:
69 void* DoAllocate(Layout layout) override;
70
72 void DoDeallocate(void* ptr) override;
73
75 void DoDeallocate(void* ptr, Layout) override { DoDeallocate(ptr); }
76
78 bool DoResize(void* ptr, size_t new_size) override;
79
81 void* DoReallocate(void* ptr, Layout new_layout) override;
82
84 Result<Layout> DoGetInfo(InfoType info_type, const void* ptr) const override {
85 return GetInfo(allocator_, info_type, ptr);
86 }
87
88 Allocator& allocator_;
90};
91
92// Template method implementation.
93
94template <typename MetricsType>
96 Layout requested = layout;
97 void* new_ptr = allocator_.Allocate(requested);
98 if (new_ptr == nullptr) {
99 metrics_.RecordFailure(requested.size());
100 return nullptr;
101 }
102 Layout allocated = Layout::Unwrap(GetAllocatedLayout(new_ptr));
103 metrics_.IncrementAllocations();
104 metrics_.ModifyRequested(requested.size(), 0);
105 metrics_.ModifyAllocated(allocated.size(), 0);
106 return new_ptr;
107}
108
109template <typename MetricsType>
110void TrackingAllocator<MetricsType>::DoDeallocate(void* ptr) {
111 Layout requested = Layout::Unwrap(GetRequestedLayout(ptr));
112 Layout allocated = Layout::Unwrap(GetAllocatedLayout(ptr));
113 allocator_.Deallocate(ptr);
114 metrics_.IncrementDeallocations();
115 metrics_.ModifyRequested(0, requested.size());
116 metrics_.ModifyAllocated(0, allocated.size());
117}
118
119template <typename MetricsType>
120bool TrackingAllocator<MetricsType>::DoResize(void* ptr, size_t new_size) {
121 Layout requested = Layout::Unwrap(GetRequestedLayout(ptr));
122 Layout allocated = Layout::Unwrap(GetAllocatedLayout(ptr));
123 Layout new_requested(new_size, requested.alignment());
124 if (!allocator_.Resize(ptr, new_requested.size())) {
125 metrics_.RecordFailure(new_size);
126 return false;
127 }
128 Layout new_allocated = Layout::Unwrap(GetAllocatedLayout(ptr));
129 metrics_.IncrementResizes();
130 metrics_.ModifyRequested(new_requested.size(), requested.size());
131 metrics_.ModifyAllocated(new_allocated.size(), allocated.size());
132 return true;
133}
134
135template <typename MetricsType>
136void* TrackingAllocator<MetricsType>::DoReallocate(void* ptr,
137 Layout new_layout) {
138 Layout requested = Layout::Unwrap(GetRequestedLayout(ptr));
139 Layout allocated = Layout::Unwrap(GetAllocatedLayout(ptr));
140 Layout new_requested(new_layout.size(), requested.alignment());
141 void* new_ptr = allocator_.Reallocate(ptr, new_requested);
142 if (new_ptr == nullptr) {
143 metrics_.RecordFailure(new_requested.size());
144 return nullptr;
145 }
146 metrics_.IncrementReallocations();
147 metrics_.ModifyRequested(new_requested.size(), requested.size());
148 Layout new_allocated = Layout::Unwrap(GetAllocatedLayout(new_ptr));
149 if (ptr != new_ptr) {
150 // Reallocate performed "alloc, copy, free". Increment and decrement
151 // seperately in order to ensure "peak" metrics are correct.
152 metrics_.ModifyAllocated(new_allocated.size(), 0);
153 metrics_.ModifyAllocated(0, allocated.size());
154 } else {
155 // Reallocate performed "resize" without additional overhead.
156 metrics_.ModifyAllocated(new_allocated.size(), allocated.size());
157 }
158 return new_ptr;
159}
160
161// TODO(b/326509341): This is an interim alias to facilitate refactoring
162// downstream consumers of `TrackingAllocator` to add a template parameter.
163//
164// The following migration steps are complete:
165// 1. Downstream consumers will be updated to use `TrackingAllocatorImpl<...>`.
166// 2. The iterim `TrackingAllocator` class will be removed.
167// 3. `TrackingAllocatorImpl<...>` will be renamed to `TrackingAllocator<...>`,
168// with a `TrackingAllocatorImpl<...>` alias pointing to it.
169//
170// The following migration steps remain:
171// 4. Downstream consumers will be updated to use `TrackingAllocator<...>`.
172// 5. The `TrackingAllocatorImpl<...>` alias will be removed.
173template <typename MetricsType>
174using TrackingAllocatorImpl = TrackingAllocator<MetricsType>;
175
176} // namespace pw::allocator
Definition: allocator.h:32
constexpr Allocator()=default
TODO(b/326509341): Remove when downstream consumers migrate.
Definition: layout.h:39
Definition: tracking_allocator.h:47
Definition: metrics.h:124
Definition: tracking_allocator.h:35