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