1
2 /*
3 * File Allocator.hpp.
4 *
5 * This file is part of the source code of the software program
6 * Vampire. It is protected by applicable
7 * copyright laws.
8 *
9 * This source code is distributed under the licence found here
10 * https://vprover.github.io/license.html
11 * and in the source directory
12 *
13 * In summary, you are allowed to use Vampire for non-commercial
14 * purposes but not allowed to distribute, modify, copy, create derivatives,
15 * or use in competitions.
16 * For other uses of Vampire please contact developers for a different
17 * licence, which we will make an effort to provide.
18 */
19 /**
20 * @file Allocator.hpp
21 * Defines the class Allocator plus the global allocator for Vampire.
22 *
23 * @since 13/12/2005 Redmond, made from the Shell Allocator.
24 * @since 10/01/2008 Manchester, reimplemented
25 */
26
27
28 #ifndef __Allocator__
29 #define __Allocator__
30
31 #include <cstddef>
32
33 #include "Debug/Assertion.hpp"
34 #include "Debug/Tracer.hpp"
35
36 #include "Portability.hpp"
37
38 #if VDEBUG
39 #include <string>
40 #endif
41
42 #define MAKE_CALLS 0
43
44 #define USE_PRECISE_CLASS_NAMES 0
45
46 /** Page size in bytes */
47 #define VPAGE_SIZE 131000
48 /** maximal size of allocated multi-page (in pages) */
49 //#define MAX_PAGES 4096
50 //#define MAX_PAGES 8192
51 #define MAX_PAGES 40000
52 /** Any memory piece of this or larger size will be allocated as a page
53 * or contiguous sequence of pages */
54 #define REQUIRES_PAGE (VPAGE_SIZE/2)
55 /** Maximal allowed number of allocators */
56 #define MAX_ALLOCATORS 256
57
58 /** The largest piece of memory that can be allocated at once */
59 #define MAXIMAL_ALLOCATION (static_cast<unsigned long long>(VPAGE_SIZE)*MAX_PAGES)
60
61 //this macro is undefined at the end of the file
62 // alloc_size follows C notation, for a C++ method, argument 1 is the pointer to this, so
63 // the actual allocation size lies in argument 2
64 #if defined(__GNUC__) && !defined(__ICC) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ > 2)
65 # define ALLOC_SIZE_ATTR __attribute__((malloc, alloc_size(2)))
66 #else
67 # define ALLOC_SIZE_ATTR
68 #endif
69
70 namespace Lib {
71
72 class Allocator {
73 public:
74 // Allocator is the only class which we don't allocate using Allocator ;)
75 void* operator new (size_t s);
76 void operator delete (void* obj);
77
78 Allocator();
79 ~Allocator();
80
81 /** Return the amount of used memory */
getUsedMemory()82 static size_t getUsedMemory()
83 {
84 CALLC("Allocator::getUsedMemory",MAKE_CALLS);
85 return _usedMemory;
86 }
87 /** Return the global memory limit (in bytes) */
getMemoryLimit()88 static size_t getMemoryLimit()
89 {
90 CALLC("Allocator::getMemoryLimit",MAKE_CALLS);
91 return _memoryLimit;
92 }
93 /** Set the global memory limit (in bytes) */
setMemoryLimit(size_t size)94 static void setMemoryLimit(size_t size)
95 {
96 CALLC("Allocator::setMemoryLimit",MAKE_CALLS);
97 _memoryLimit = size;
98 _tolerated = size + (size/10);
99 }
100 /** The current allocator
101 * - through which allocations by the here defined macros are channelled */
102 static Allocator* current;
103
104 #if VDEBUG
105 void* allocateKnown(size_t size,const char* className) ALLOC_SIZE_ATTR;
106 void deallocateKnown(void* obj,size_t size,const char* className);
107 void* allocateUnknown(size_t size,const char* className) ALLOC_SIZE_ATTR;
108 void* reallocateUnknown(void* obj, size_t newsize,const char* className);
109 void deallocateUnknown(void* obj,const char* className);
110 static void addressStatus(const void* address);
111 static void reportUsageByClasses();
112 #else
113 void* allocateKnown(size_t size) ALLOC_SIZE_ATTR;
114 void deallocateKnown(void* obj,size_t size);
115 void* allocateUnknown(size_t size) ALLOC_SIZE_ATTR;
116 void* reallocateUnknown(void* obj, size_t newsize);
117 void deallocateUnknown(void* obj);
118 #endif
119
120 class Initialiser {
121 public:
122 /** Initialise the static allocator's methods */
Initialiser()123 Initialiser()
124 {
125 CALLC("Allocator::Initialiser::Initialiser",MAKE_CALLS);
126
127 if (Allocator::_initialised++ == 0) {
128 Allocator::initialise();
129 }
130 } // Initialiser::Initialiser
131
~Initialiser()132 ~Initialiser()
133 {
134 CALLC("Allocator::Initialiser::~Initialiser",MAKE_CALLS);
135 if (--Allocator::_initialised == 0) {
136 Allocator::cleanup();
137 }
138 }
139 }; // class Allocator::Initialiser
140
141 static Allocator* newAllocator();
142
143 private:
144 char* allocatePiece(size_t size);
145 static void initialise();
146 static void cleanup();
147 /** Array of Allocators. It is assumed that a small number of Allocators is
148 * available at any given time and Allocator deletions are rare since
149 * a deletion involves a linear lookup in the array and the
150 * size of the array is small (currently, no during runtime deletion supported).
151 */
152 static Allocator* _all[MAX_ALLOCATORS];
153 /** Total number of allocators currently available */
154 static int _total;
155 /** > 0 if the global page manager has been initialised */
156 static int _initialised;
157
158 /**
159 * A piece of memory whose size is known by procedures de-allocating
160 * this piece. Since the size is known in advance, no bookkeeping of
161 * the size is required.
162 *
163 * Note: the code of Allocator uses sizeof(Known) as a canonical
164 * way of describing the size of a general pointer on the current
165 * architecture.
166 *
167 * This pieces of memory are kept in a free list.
168 * @since 10/01/2007 Manchester
169 */
170 struct Known {
171 /** Pointer to the next available piece. Used when kept in a free list. */
172 Known* next;
173 }; // class Known
174
175 /**
176 * A piece of memory whose size is unknown by procedures de-allocating
177 * this piece. Since the size is unknown, storing the size is required.
178 *
179 * This pieces of memory are kept in a list
180 * @since 10/01/2007 Manchester
181 */
182 struct Unknown {
183 /** Size, used both when the piece is allocated and when it is kept
184 * in the free list. It is the size of the piece, since the size of
185 * the datastructure using the piece may actually be smaller */
186 size_t size;
187 /** Pointer to the next available piece. Used when kept in
188 * the list. */
189 Unknown* next;
190 }; // class Unknown
191
unknownsSize(void * obj)192 static size_t unknownsSize(void* obj) {
193 ASS_LE(sizeof(size_t), sizeof(Known)); // because the code all around jumps back by sizeof(Known), but then reads/writes into size_t
194
195 char* mem = reinterpret_cast<char*>(obj) - sizeof(Known);
196 Unknown* unknown = reinterpret_cast<Unknown*>(mem);
197 return unknown->size - sizeof(Known);
198 }
199
200 #if VDEBUG
201 public:
202 /** A helper struct used for implementing the BYPASSING_ALLOCATOR macro. */
203 struct EnableBypassChecking {
204 unsigned _save;
EnableBypassCheckingLib::Allocator::EnableBypassChecking205 EnableBypassChecking() { _save = _tolerantZone; _tolerantZone = 0; }
~EnableBypassCheckingLib::Allocator::EnableBypassChecking206 ~EnableBypassChecking() { _tolerantZone = _save; }
207 };
208
209 /** A helper struct used for implementing the BYPASSING_ALLOCATOR macro. */
210 struct DisableBypassChecking {
DisableBypassCheckingLib::Allocator::DisableBypassChecking211 DisableBypassChecking() { _tolerantZone = 1; }
212 };
213
214 /** A helper struct used for implementing the BYPASSING_ALLOCATOR macro. */
215 struct AllowBypassing {
AllowBypassingLib::Allocator::AllowBypassing216 AllowBypassing() { _tolerantZone++; }
~AllowBypassingLib::Allocator::AllowBypassing217 ~AllowBypassing() { _tolerantZone--; }
218 };
219
220 /** Descriptor stores information about allocated pieces of memory */
221 struct Descriptor
222 {
223 // Allocator (and its Descriptor) are the only classes which we don't allocate using Allocator
224 void* operator new[] (size_t s);
225 void operator delete[] (void* obj);
226
227 /** address of a piece of memory */
228 const void* address;
229 /** class to which it belongs */
230 const char* cls;
231 /** time when it allocated/deallocated */
232 unsigned timestamp;
233 /** size in bytes, 0 is unused */
234 unsigned size;
235 /** true if allocated */
236 unsigned allocated : 1;
237 /** true if allocated as a known-size object */
238 unsigned known : 1;
239 /** true if it is a page allocated to store other objects */
240 unsigned page : 1;
241 Descriptor();
242
243 friend std::ostream& operator<<(std::ostream& out, const Descriptor& d);
244
245 static unsigned hash (const void* addr);
246 static Descriptor* find(const void* addr);
247 /** map from addresses to memory descriptors */
248 static Descriptor* map;
249 /** timestamp for (de)allocations */
250 static unsigned globalTimestamp;
251 /** number of entries in the map of memory descriptors */
252 static size_t noOfEntries;
253 /** number of entries in the map of memory descriptors */
254 static size_t maxEntries;
255 /** pointer to after the last descriptor in the table */
256 static Descriptor* afterLast;
257 /** capacity of the map */
258 static size_t capacity;
259 };
260 #endif
261
262 private:
263 /**
264 * A piece of memory whose size is multiple of VPAGE_SIZE. Each page
265 * is used in one of the following ways:
266 * <ol>
267 * <li>to store a collection of Knowns</li>
268 * <li>to store a collection of Uknowns</li>
269 * <li>to store a single object of size greater than or equal to
270 * REQUIRES_PAGE</li>
271 * </ol>
272 * @warning @b size should go just before @b content since Vampire
273 * must be able to know the size of both Page and Unknown
274 * before knowing the type (that is, Page or Unknown)
275 * @since 10/01/2007 Manchester
276 */
277 struct Page {
278 /** The next page, if any */
279 Page* next;
280 /** The previous page, if any */
281 Page* previous;
282 /** Size of this page, multiple of VPAGE_SIZE */
283 size_t size;
284 /** The page content starts here */
285 void* content[1];
286 }; // class Page
287
288 Page* allocatePages(size_t size);
289 void deallocatePages(Page* page);
290
291 /** The global memory limit */
292 static size_t _memoryLimit;
293 /** 10% over the memory limit. When reached, memory de-fragmentation
294 * should occur */
295 static size_t _tolerated;
296
297 // structures used inside the allocator start here
298 /** The free list.
299 * At index @b i there are a linked list of Knowns of size (i+1)*sizeof(Known)
300 * Note that, essentially, sizeof(Known) = sizeof(void*).
301 */
302 Known* _freeList[REQUIRES_PAGE/4];
303 /** All pages allocated by this allocator and not returned to
304 * the global manager via deallocatePages (doubly linked). */
305 Page* _myPages;
306 #if ! USE_SYSTEM_ALLOCATION
307 /** Number of bytes available on the reserve page */
308 size_t _reserveBytesAvailable;
309 /** next available known */
310 char* _nextAvailableReserve;
311 #endif // ! USE_SYSTEM_ALLOCATION
312
313 /** Total memory allocated by pages */
314 static size_t _usedMemory;
315 /** Page allocator array, a.k.a. "the global manager".
316 * Each entry is a (singly linked) list */
317 static Page* _pages[MAX_PAGES];
318
319 friend class Initialiser;
320
321 #if VDEBUG
322 /*
323 * A tool for marking pieces of code which are allowed to bypass Allocator.
324 * See also Allocator::AllowBypassing and the BYPASSING_ALLOCATOR macro.
325 */
326 static unsigned _tolerantZone;
327 friend void* ::operator new(size_t);
328 friend void* ::operator new[](size_t);
329 friend void ::operator delete(void*) noexcept;
330 friend void ::operator delete[](void*) noexcept;
331 #endif
332 }; // class Allocator
333
334 /** An initialiser to be included in every compilation unit to ensure
335 * that the memory has been initialised properly.
336 */
337 static Allocator::Initialiser _____;
338
339 /**
340 * Initialise an array of objects of type @b T that has length @b length
341 * starting at @b placement, and return a pointer to its first element
342 * (whose value is equal to @b placement). This function is required when
343 * we use an allocated piece of memory an array of elements of type @b T -
344 * in this case we also have to initialise every array cell by applying
345 * the constructor of T.
346 * @author Krystof Hoder
347 * @author Andrei Voronkov (documentation)
348 */
349 template<typename T>
array_new(void * placement,size_t length)350 T* array_new(void* placement, size_t length)
351 {
352 CALLC("array_new",MAKE_CALLS);
353 ASS_NEQ(placement,0);
354 ASS_G(length,0);
355
356 T* res=static_cast<T*>(placement);
357 T* p=res;
358 while(length--) {
359 ::new(static_cast<void*>(p++)) T();
360 }
361 return res;
362 } // array_new
363
364 /**
365 * Apply the destructor of T to each element of the array @b array of objects
366 * of type @b T and that has length @b length.
367 * @see array_new() for more information.
368 * @author Krystof Hoder
369 * @author Andrei Voronkov (documentation)
370 */
371 template<typename T>
array_delete(T * array,size_t length)372 void array_delete(T* array, size_t length)
373 {
374 CALLC("array_delete",MAKE_CALLS);
375 ASS_NEQ(array,0);
376 ASS_G(length,0);
377
378 array+=length;
379 while(length--) {
380 (--array)->~T();
381 }
382 }
383
384 #define DECLARE_PLACEMENT_NEW \
385 void* operator new (size_t, void* buffer) { return buffer; } \
386 void operator delete (void*, void*) {}
387
388
389 #if VDEBUG
390
391 std::ostream& operator<<(std::ostream& out, const Allocator::Descriptor& d);
392
393 #define USE_ALLOCATOR_UNK \
394 void* operator new (size_t sz) \
395 { return Lib::Allocator::current->allocateUnknown(sz,className()); } \
396 void operator delete (void* obj) \
397 { if (obj) Lib::Allocator::current->deallocateUnknown(obj,className()); }
398 #define USE_ALLOCATOR(C) \
399 void* operator new (size_t sz) \
400 { ASS_EQ(sz,sizeof(C)); return Lib::Allocator::current->allocateKnown(sizeof(C),className()); } \
401 void operator delete (void* obj) \
402 { if (obj) Lib::Allocator::current->deallocateKnown(obj,sizeof(C),className()); }
403 #define USE_ALLOCATOR_ARRAY \
404 void* operator new[] (size_t sz) \
405 { return Lib::Allocator::current->allocateUnknown(sz,className()); } \
406 void operator delete[] (void* obj) \
407 { if (obj) Lib::Allocator::current->deallocateUnknown(obj,className()); }
408
409
410 #if USE_PRECISE_CLASS_NAMES
411 # if defined(__GNUC__)
412
413 std::string ___prettyFunToClassName(std::string str);
414
415 # define CLASS_NAME(C) \
416 static const char* className () { \
417 static std::string res = ___prettyFunToClassName(std::string(__PRETTY_FUNCTION__)); return res.c_str(); }
418 # else
419 # define CLASS_NAME(C) \
420 static const char* className () { return typeid(C).name(); }
421 # endif
422 #else
423 # define CLASS_NAME(C) \
424 static const char* className () { return #C; }
425 #endif
426
427 #define ALLOC_KNOWN(size,className) \
428 (Lib::Allocator::current->allocateKnown(size,className))
429 #define ALLOC_UNKNOWN(size,className) \
430 (Lib::Allocator::current->allocateUnknown(size,className))
431 #define DEALLOC_KNOWN(obj,size,className) \
432 (Lib::Allocator::current->deallocateKnown(obj,size,className))
433 #define REALLOC_UNKNOWN(obj,newsize,className) \
434 (Lib::Allocator::current->reallocateUnknown(obj,newsize,className))
435 #define DEALLOC_UNKNOWN(obj,className) \
436 (Lib::Allocator::current->deallocateUnknown(obj,className))
437
438 #define BYPASSING_ALLOCATOR_(SEED) Allocator::AllowBypassing _tmpBypass_##SEED;
439 #define BYPASSING_ALLOCATOR BYPASSING_ALLOCATOR_(__LINE__)
440
441 #define START_CHECKING_FOR_BYPASSES(SEED) Allocator::EnableBypassChecking _tmpBypass_##SEED;
442 #define START_CHECKING_FOR_ALLOCATOR_BYPASSES START_CHECKING_FOR_BYPASSES(__LINE__)
443
444 #define STOP_CHECKING_FOR_BYPASSES(SEED) Allocator::DisableBypassChecking _tmpBypass_##SEED;
445 #define STOP_CHECKING_FOR_ALLOCATOR_BYPASSES STOP_CHECKING_FOR_BYPASSES(__LINE__)
446
447 #else
448
449 #define CLASS_NAME(name)
450 #define ALLOC_KNOWN(size,className) \
451 (Lib::Allocator::current->allocateKnown(size))
452 #define DEALLOC_KNOWN(obj,size,className) \
453 (Lib::Allocator::current->deallocateKnown(obj,size))
454 #define USE_ALLOCATOR_UNK \
455 inline void* operator new (size_t sz) \
456 { return Lib::Allocator::current->allocateUnknown(sz); } \
457 inline void operator delete (void* obj) \
458 { if (obj) Lib::Allocator::current->deallocateUnknown(obj); }
459 #define USE_ALLOCATOR(C) \
460 inline void* operator new (size_t) \
461 { return Lib::Allocator::current->allocateKnown(sizeof(C)); }\
462 inline void operator delete (void* obj) \
463 { if (obj) Lib::Allocator::current->deallocateKnown(obj,sizeof(C)); }
464 #define USE_ALLOCATOR_ARRAY \
465 inline void* operator new[] (size_t sz) \
466 { return Lib::Allocator::current->allocateUnknown(sz); } \
467 inline void operator delete[] (void* obj) \
468 { if (obj) Lib::Allocator::current->deallocateUnknown(obj); }
469 #define ALLOC_UNKNOWN(size,className) \
470 (Lib::Allocator::current->allocateUnknown(size))
471 #define REALLOC_UNKNOWN(obj,newsize,className) \
472 (Lib::Allocator::current->reallocateUnknown(obj,newsize))
473 #define DEALLOC_UNKNOWN(obj,className) \
474 (Lib::Allocator::current->deallocateUnknown(obj))
475
476 #define START_CHECKING_FOR_ALLOCATOR_BYPASSES
477 #define STOP_CHECKING_FOR_ALLOCATOR_BYPASSES
478 #define BYPASSING_ALLOCATOR
479
480 #endif
481
482 } // namespace Lib
483
484 #undef ALLOC_SIZE_ATTR
485
486 #endif // __Allocator__
487