Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(27)

Side by Side Diff: base/metrics/persistent_memory_allocator.h

Issue 1410213004: Create "persistent memory allocator" for persisting and sharing objects. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: addressed review comments by Chris Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/base.gypi ('k') | base/metrics/persistent_memory_allocator.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef BASE_METRICS_PERSISTENT_MEMORY_ALLOCATOR_H_
6 #define BASE_METRICS_PERSISTENT_MEMORY_ALLOCATOR_H_
7
8 #include <stdint.h>
9 #include <atomic>
10 #include <string>
11
12 #include "base/atomicops.h"
13 #include "base/base_export.h"
14 #include "base/gtest_prod_util.h"
15 #include "base/macros.h"
16 #include "base/memory/scoped_ptr.h"
17
18 namespace base {
19
20 class HistogramBase;
21 class MemoryMappedFile;
22
23 // Simple allocator for pieces of a memory block that may be persistent
24 // to some storage or shared across multiple processes. This class resides
25 // under base/metrics because it was written for that purpose. It is,
26 // however, fully general-purpose and can be freely moved to base/memory
27 // if other uses are found.
28 //
29 // This class provides for thread-secure (i.e. safe against other threads
30 // or processes that may be compromised and thus have malicious intent)
31 // allocation of memory within a designated block and also a mechanism by
32 // which other threads can learn of these allocations.
33 //
34 // There is (currently) no way to release an allocated block of data because
35 // doing so would risk invalidating pointers held by other processes and
36 // greatly complicate the allocation algorithm.
37 //
38 // Construction of this object can accept new, clean (i.e. zeroed) memory
39 // or previously initialized memory. In the first case, construction must
40 // be allowed to complete before letting other allocators attach to the same
41 // segment. In other words, don't share the segment until at least one
42 // allocator has been attached to it.
43 //
44 // Note that memory not in active use is not accessed so it is possible to
45 // use virtual memory, including memory-mapped files, as backing storage with
46 // the OS "pinning" new (zeroed) physical RAM pages only as they are needed.
47 class BASE_EXPORT PersistentMemoryAllocator {
48 public:
49 typedef uint32_t Reference;
50
51 // Internal state information when iterating over memory allocations.
52 class Iterator {
53 public:
54 Iterator() : last(0) {}
55
56 bool operator==(const Iterator& rhs) const { return last == rhs.last; }
57 bool operator!=(const Iterator& rhs) const { return last != rhs.last; }
58
59 void clear() { last = 0; }
60 bool is_clear() const { return last == 0; }
61
62 private:
63 friend class PersistentMemoryAllocator;
64
65 Reference last;
66 uint32_t niter;
67 };
68
69 // Returned information about the internal state of the heap.
70 struct MemoryInfo {
71 size_t total;
72 size_t free;
73 };
74
75 enum : uint32_t {
76 kTypeIdAny = 0 // Match any type-id inside GetAsObject().
77 };
78
79 // The allocator operates on any arbitrary block of memory. Creation and
80 // persisting or sharing of that block with another process is the
81 // responsibility of the caller. The allocator needs to know only the
82 // block's |base| address, the total |size| of the block, and any internal
83 // |page| size (zero if not paged) across which allocations should not span.
84 // The |id| is an arbitrary value the caller can use to identify a
85 // particular memory segment. It will only be loaded during the initial
86 // creation of the segment and can be checked by the caller for consistency.
87 // The |name|, if provided, is used to distinguish histograms for this
88 // allocator. Only the primary owner of the segment should define this value;
89 // other processes can learn it from the shared state. If the underlying
90 // memory is |readonly| then no changes will be made to it. The resulting
91 // object should be stored as a "const" pointer.
92 //
93 // PersistentMemoryAllocator does NOT take ownership of the memory block.
94 // The caller must manage it and ensure it stays available throughout the
95 // lifetime of this object.
96 //
97 // Memory segments for sharing must have had an allocator attached to them
98 // before actually being shared. If the memory segment was just created, it
99 // should be zeroed before being passed here. If it was an existing segment,
100 // the values here will be compared to copies stored in the shared segment
101 // as a guard against corruption.
102 //
103 // Make sure that the memory segment is acceptable (see IsMemoryAcceptable()
104 // method below) before construction if the definition of the segment can
105 // vary in any way at run-time. Invalid memory segments will cause a crash.
106 PersistentMemoryAllocator(void* base, size_t size, size_t page_size,
107 uint64_t id, const std::string& name,
108 bool readonly);
109 virtual ~PersistentMemoryAllocator();
110
111 // Check if memory segment is acceptable for creation of an Allocator. This
112 // doesn't do any analysis of the data and so doesn't guarantee that the
113 // contents are valid, just that the paramaters won't cause the program to
114 // abort. The IsCorrupt() method will report detection of data problems
115 // found during construction and general operation.
116 static bool IsMemoryAcceptable(const void* data, size_t size,
117 size_t page_size, bool readonly);
118
119 // Get the internal identifier for this persistent memory segment.
120 uint64_t Id() const;
121
122 // Get the internal name of this allocator (possibly an empty string).
123 const char* Name() const;
124
125 // Is this segment open only for read?
126 bool IsReadonly() { return readonly_; }
127
128 // Create internal histograms for tracking memory use and allocation sizes
129 // for allocator of |name| (which can simply be the result of Name()). This
130 // is done seperately from construction for situations such as when the
131 // histograms will be backed by memory provided by this very allocator.
132 void CreateTrackingHistograms(const std::string& name);
133
134 // Direct access to underlying memory segment. If the segment is shared
135 // across threads or processes, reading data through these values does
136 // not guarantee consistency. Use with care. Do not write.
137 const void* data() const { return const_cast<const char*>(mem_base_); }
138 size_t length() const { return mem_size_; }
139 size_t used() const;
140
141 // Get an object referenced by a |ref|. For safety reasons, the |type_id|
142 // code and size-of(|T|) are compared to ensure the reference is valid
143 // and cannot return an object outside of the memory segment. A |type_id| of
144 // kTypeIdAny (zero) will match any though the size is still checked. NULL is
145 // returned if any problem is detected, such as corrupted storage or incorrect
146 // parameters. Callers MUST check that the returned value is not-null EVERY
147 // TIME before accessing it or risk crashing! Once dereferenced, the pointer
148 // is safe to reuse forever.
149 //
150 // NOTE: Though this method will guarantee that an object of the specified
151 // type can be accessed without going outside the bounds of the memory
152 // segment, it makes no guarantees of the validity of the data within the
153 // object itself. If it is expected that the contents of the segment could
154 // be compromised with malicious intent, the object must be hardened as well.
155 //
156 // Though the persistent data may be "volatile" if it is shared with
157 // other processes, such is not necessarily the case. The internal
158 // "volatile" designation is discarded so as to not propagate the viral
159 // nature of that keyword to the caller. It can add it back, if necessary,
160 // based on knowledge of how the allocator is being used.
161 template <typename T>
162 T* GetAsObject(Reference ref, uint32_t type_id) {
163 static_assert(!std::is_polymorphic<T>::value, "no polymorphic objects");
164 return const_cast<T*>(
165 reinterpret_cast<volatile T*>(GetBlockData(ref, type_id, sizeof(T))));
166 }
167 template <typename T>
168 const T* GetAsObject(Reference ref, uint32_t type_id) const {
169 static_assert(!std::is_polymorphic<T>::value, "no polymorphic objects");
170 return const_cast<const T*>(
171 reinterpret_cast<const volatile T*>(GetBlockData(
172 ref, type_id, sizeof(T))));
173 }
174
175 // Get the number of bytes allocated to a block. This is useful when storing
176 // arrays in order to validate the ending boundary. The returned value will
177 // include any padding added to achieve the required alignment and so could
178 // be larger than given in the original Allocate() request.
179 size_t GetAllocSize(Reference ref) const;
180
181 // Access the internal "type" of an object. This generally isn't necessary
182 // but can be used to "clear" the type and so effectively mark it as deleted
183 // even though the memory stays valid and allocated.
184 uint32_t GetType(Reference ref) const;
185 void SetType(Reference ref, uint32_t type_id);
186
187 // Reserve space in the memory segment of the desired |size| and |type_id|.
188 // A return value of zero indicates the allocation failed, otherwise the
189 // returned reference can be used by any process to get a real pointer via
190 // the GetAsObject() call.
191 Reference Allocate(size_t size, uint32_t type_id);
192
193 // Allocated objects can be added to an internal list that can then be
194 // iterated over by other processes. If an allocated object can be found
195 // another way, such as by having its reference within a different object
196 // that will be made iterable, then this call is not necessary. This always
197 // succeeds unless corruption is detected; check IsCorrupted() to find out.
198 // Once an object is made iterable, its position in iteration can never
199 // change; new iterable objects will always be added after it in the series.
200 void MakeIterable(Reference ref);
201
202 // Get the information about the amount of free space in the allocator. The
203 // amount of free space should be treated as approximate due to extras from
204 // alignment and metadata. Concurrent allocations from other threads will
205 // also make the true amount less than what is reported.
206 void GetMemoryInfo(MemoryInfo* meminfo) const;
207
208 // Iterating uses a |state| structure (initialized by CreateIterator) and
209 // returns both the reference to the object as well as the |type_id| of
210 // that object. A zero return value indicates there are currently no more
211 // objects to be found but future attempts can be made without having to
212 // reset the iterator to "first". Creating an iterator |starting_after|
213 // a known iterable object allows "resume" from that point with the next
214 // call to GetNextIterable returning the object after it.
215 void CreateIterator(Iterator* state) const { CreateIterator(state, 0); };
216 void CreateIterator(Iterator* state, Reference starting_after) const;
217 Reference GetNextIterable(Iterator* state, uint32_t* type_id) const;
218
219 // If there is some indication that the memory has become corrupted,
220 // calling this will attempt to prevent further damage by indicating to
221 // all processes that something is not as expected.
222 void SetCorrupt() const;
223
224 // This can be called to determine if corruption has been detected in the
225 // segment, possibly my a malicious actor. Once detected, future allocations
226 // will fail and iteration may not locate all objects.
227 bool IsCorrupt() const;
228
229 // Flag set if an allocation has failed because the memory segment was full.
230 bool IsFull() const;
231
232 // Update those "tracking" histograms which do not get updates during regular
233 // operation, such as how much memory is currently used. This should be
234 // called before such information is to be displayed or uploaded.
235 void UpdateTrackingHistograms();
236
237 protected:
238 volatile char* const mem_base_; // Memory base. (char so sizeof guaranteed 1)
239 const uint32_t mem_size_; // Size of entire memory segment.
240 const uint32_t mem_page_; // Page size allocations shouldn't cross.
241
242 private:
243 struct SharedMetadata;
244 struct BlockHeader;
245 static const uint32_t kAllocAlignment;
246 static const Reference kReferenceQueue;
247 static const Reference kReferenceNull;
248
249 // The shared metadata is always located at the top of the memory segment.
250 // These convenience functions eliminate constant casting of the base
251 // pointer within the code.
252 const volatile SharedMetadata* shared_meta() const {
253 return reinterpret_cast<const volatile SharedMetadata*>(mem_base_);
254 }
255 volatile SharedMetadata* shared_meta() {
256 return reinterpret_cast<volatile SharedMetadata*>(mem_base_);
257 }
258
259 // Actual method for doing the allocation.
260 Reference AllocateImpl(size_t size, uint32_t type_id);
261
262 // Get the block header associated with a specific reference.
263 const volatile BlockHeader* GetBlock(Reference ref, uint32_t type_id,
264 uint32_t size, bool queue_ok,
265 bool free_ok) const;
266 volatile BlockHeader* GetBlock(Reference ref, uint32_t type_id, uint32_t size,
267 bool queue_ok, bool free_ok) {
268 return const_cast<volatile BlockHeader*>(
269 const_cast<const PersistentMemoryAllocator*>(this)->GetBlock(
270 ref, type_id, size, queue_ok, free_ok));
271 }
272
273 // Get the actual data within a block associated with a specific reference.
274 const volatile void* GetBlockData(Reference ref, uint32_t type_id,
275 uint32_t size) const;
276 volatile void* GetBlockData(Reference ref, uint32_t type_id,
277 uint32_t size) {
278 return const_cast<volatile void*>(
279 const_cast<const PersistentMemoryAllocator*>(this)->GetBlockData(
280 ref, type_id, size));
281 }
282
283 const bool readonly_; // Indicates access to read-only memory.
284 std::atomic<bool> corrupt_; // Local version of "corrupted" flag.
285
286 HistogramBase* allocs_histogram_; // Histogram recording allocs.
287 HistogramBase* used_histogram_; // Histogram recording used space.
288
289 friend class PersistentMemoryAllocatorTest;
290 FRIEND_TEST_ALL_PREFIXES(PersistentMemoryAllocatorTest, AllocateAndIterate);
291 DISALLOW_COPY_AND_ASSIGN(PersistentMemoryAllocator);
292 };
293
294
295 // This allocator uses a local memory block it allocates from the general
296 // heap. It is generally used when some kind of "death rattle" handler will
297 // save the contents to persistent storage during process shutdown. It is
298 // also useful for testing.
299 class BASE_EXPORT LocalPersistentMemoryAllocator
300 : public PersistentMemoryAllocator {
301 public:
302 LocalPersistentMemoryAllocator(size_t size, uint64_t id,
303 const std::string& name);
304 ~LocalPersistentMemoryAllocator() override;
305
306 private:
307 DISALLOW_COPY_AND_ASSIGN(LocalPersistentMemoryAllocator);
308 };
309
310
311 // This allocator takes a memory-mapped file object and performs allocation
312 // from it. The allocator takes ownership of the file object. Only read access
313 // is provided due to limitions of the MemoryMappedFile class.
314 class BASE_EXPORT FilePersistentMemoryAllocator
315 : public PersistentMemoryAllocator {
316 public:
317 FilePersistentMemoryAllocator(MemoryMappedFile* file, uint64_t id,
318 const std::string& name);
319 ~FilePersistentMemoryAllocator() override;
320
321 // Ensure that the file isn't so invalid that it won't crash when passing it
322 // to the allocator. This doesn't guarantee the file is valid, just that it
323 // won't cause program to abort. The existing IsCorrupt() call will handle
324 // the rest.
325 static bool IsFileAcceptable(const MemoryMappedFile& file);
326
327 private:
328 scoped_ptr<MemoryMappedFile> mapped_file_;
329 };
330
331 } // namespace base
332
333 #endif // BASE_METRICS_PERSISTENT_MEMORY_ALLOCATOR_H_
OLDNEW
« no previous file with comments | « base/base.gypi ('k') | base/metrics/persistent_memory_allocator.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698