1 
2 /*
3  * File Allocator.cpp.
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.cpp
21  * Defines allocation procedures for all preprocessor classes.
22  *
23  * @since 02/12/2003, Manchester, replaces the file Memory.hpp
24  * @since 10/01/2008 Manchester, reimplemented
25  */
26 #if VDEBUG
27 
28 #include <sstream>
29 #include "DHMap.hpp"
30 
31 #endif
32 
33 #include <cstring>
34 #include <cstdlib>
35 #include "Lib/System.hpp"
36 #include "Shell/UIHelper.hpp"
37 
38 #define SAFE_OUT_OF_MEM_SOLUTION 1
39 
40 #ifndef USE_SYSTEM_ALLOCATION
41 /** If the following is set to true the Vampire will use the
42  *  C++ new and delete for (de)allocating all data structures.
43  */
44 #define USE_SYSTEM_ALLOCATION 0
45 #endif
46 
47 #if USE_SYSTEM_ALLOCATION
48 # if __APPLE__
49 #  include <malloc/malloc.h>
50 # else
51 #  include <malloc.h>
52 # endif
53 #endif
54 
55 /** set this to 1 to print all allocations/deallocations to stdout -- only with VDEBUG */
56 #define TRACE_ALLOCATIONS 0
57 
58 /** Size of a page prefix (part of the page used for bookkeeping the
59  * page itself) */
60 #define PAGE_PREFIX_SIZE (sizeof(Page)-sizeof(void*))
61 
62 // To watch an address define the following values:
63 //   WATCH_FIRST     - the first watch point
64 //   WATCH_LAST      - the last watch point
65 //   WATCH_ADDRESS   - address to watch, 0 if no watch
66 // If WATCH_ADDRESS is non-null then any changes made to WATCH_ADDRESS will
67 // be output, provided they occur at control points between WATCH_FIRST
68 // and WATCH_LAST
69 #define WATCH_FIRST 0
70 #define WATCH_LAST UINT_MAX
71 // #define WATCH_ADDRESS 0x38001a0
72 #define WATCH_ADDRESS 0
73 
74 #if WATCH_ADDRESS
75 /** becomes true when a page containing the watch point has been allocated */
76 bool watchPage = false;
77 /** last value stored in the watched address */
78 unsigned watchAddressLastValue = 0;
79 #endif
80 
81 #include "Debug/Assertion.hpp"
82 #include "Debug/Tracer.hpp"
83 
84 #include "Shell/Statistics.hpp"
85 
86 #include "Exception.hpp"
87 #include "Environment.hpp"
88 #include "Allocator.hpp"
89 
90 #if CHECK_LEAKS
91 #include "MemoryLeak.hpp"
92 #endif
93 
94 using namespace Lib;
95 using namespace Shell;
96 
97 int Allocator::_initialised = 0;
98 int Allocator::_total = 0;
99 size_t Allocator::_memoryLimit;
100 size_t Allocator::_tolerated;
101 Allocator* Allocator::current;
102 Allocator::Page* Allocator::_pages[MAX_PAGES];
103 size_t Allocator::_usedMemory = 0;
104 Allocator* Allocator::_all[MAX_ALLOCATORS];
105 
106 #if VDEBUG
107 unsigned Allocator::Descriptor::globalTimestamp;
108 size_t Allocator::Descriptor::noOfEntries;
109 size_t Allocator::Descriptor::maxEntries;
110 size_t Allocator::Descriptor::capacity;
111 Allocator::Descriptor* Allocator::Descriptor::map;
112 Allocator::Descriptor* Allocator::Descriptor::afterLast;
113 unsigned Allocator::_tolerantZone = 1; // starts > 0; we are not checking by default, until we say so with START_CHECKING_FOR_BYPASSES
114 #endif
115 
116 #if VDEBUG && USE_PRECISE_CLASS_NAMES && defined(__GNUC__)
117 
118 /**
119  * Function used by a variant of macro CLASS_NAME to obtain a pretty class name
120  * from the content of the __PRETTY_FUNCTION__ variable
121  */
___prettyFunToClassName(std::string str)122 string Lib::___prettyFunToClassName(std::string str)
123 {
124   CALLC("___prettyFunToClassName",MAKE_CALLS);
125 
126   string noPref = str.substr(19);
127   size_t fnNamePos = noPref.find("::className()");
128   string className = noPref.substr(0,fnNamePos);
129   //either empty, or contains one space followed by values of
130   //template params
131   string templateInfo = noPref.substr(fnNamePos+13);
132   return className+templateInfo;
133 }
134 
135 #endif
136 
operator new(size_t s)137 void* Allocator::operator new(size_t s) {
138   return malloc(s);
139 }
140 
operator delete(void * obj)141 void Allocator::operator delete(void* obj) {
142   free(obj);
143 }
144 
145 /**
146  * Create a new allocator.
147  * @since 10/01/2008 Manchester
148  */
Allocator()149 Allocator::Allocator()
150 {
151   CALLC("Allocator::Allocator",MAKE_CALLS);
152 
153 #if ! USE_SYSTEM_ALLOCATION
154   for (int i = REQUIRES_PAGE/4-1;i >= 0;i--) {
155     _freeList[i] = 0;
156   }
157   _reserveBytesAvailable = 0;
158   _nextAvailableReserve = 0;
159   _myPages = 0;
160 #endif
161 } // Allocator::Allocator
162 
163 /**
164  * Returns all pages to the global manager.
165  *
166  * @since 18/03/2008 Torrevieja
167  */
~Allocator()168 Lib::Allocator::~Allocator ()
169 {
170   CALLC("Allocator::~Allocator",MAKE_CALLS);
171 
172   while (_myPages) {
173     deallocatePages(_myPages);
174   }
175 } // Allocator::~allocator
176 
177 /**
178  * Initialise all static structures and create a default allocator.
179  * @since 10/01/2008 Manchester
180  */
initialise()181 void Allocator::initialise()
182 {
183   CALLC("Allocator::initialise",MAKE_CALLS)
184 
185 #if VDEBUG
186   Descriptor::map = 0;
187   Descriptor::afterLast = 0;
188 #endif
189 
190   _memoryLimit = 300000000u;
191   _tolerated = 330000000u;
192 
193 #if ! USE_SYSTEM_ALLOCATION
194   current = newAllocator();
195 
196   for (int i = MAX_PAGES-1;i >= 0;i--) {
197     _pages[i] = 0;
198   }
199 #endif
200 } // Allocator::initialise
201 
202 #if VDEBUG
203 /**
204  * Write information about a memory address to cout.
205  * @since 30/03/2008 flight Murcia-Manchester
206  */
addressStatus(const void * address)207 void Allocator::addressStatus(const void* address)
208 {
209   CALLC("Allocator::addressStatus",MAKE_CALLS);
210 
211   Descriptor* pg = 0; // page descriptor
212   cout << "Status of address " << address << '\n';
213 
214   const char* a = static_cast<const char*>(address);
215   for (int i = Descriptor::capacity-1;i >= 0;i--) {
216     Descriptor& d = Descriptor::map[i];
217     const char* addr = static_cast<const char*>(d.address);
218     if (addr < a) {
219       continue;
220     }
221     unsigned diff = a - addr;
222     if (diff >= d.size) {
223       continue;
224     }
225     if (d.page) {
226       pg = &d;
227       continue;
228     }
229     // found
230     cout << "Descriptor: " << d << '\n'
231 	 << "Offset: " << diff << '\n'
232 	 << "End of status\n";
233     return;
234   }
235   if (pg) {
236     cout << "Not found but belongs to allocated page: " << *pg << '\n';
237   }
238   else {
239     cout << "Does not belong to an allocated page\n";
240   }
241   cout << "End of status\n";
242 } // Allocator::addressStatus
243 
reportUsageByClasses()244 void Allocator::reportUsageByClasses()
245 {
246   Lib::DHMap<const char*, size_t> summary;
247   Lib::DHMap<const char*, size_t> cntSummary;
248   for (int i = Descriptor::capacity-1;i >= 0;i--) {
249     Descriptor& d = Descriptor::map[i];
250     if (!d.address || !d.size || !d.allocated) {
251       continue;
252     }
253     size_t occupied;
254     if(summary.find(d.cls, occupied)) {
255       summary.set(d.cls, occupied+d.size);
256       size_t cnt;
257       ALWAYS(cntSummary.find(d.cls, cnt));
258       cntSummary.set(d.cls, cnt+1);
259     } else {
260       summary.set(d.cls, d.size);
261       cntSummary.set(d.cls, 1);
262     }
263   }
264 
265   Lib::DHMap<const char*, size_t>::Iterator sit(summary);
266   cout<<"class\tcount\tsize"<<endl;
267   while(sit.hasNext()) {
268     const char* cls;
269     size_t occupied, cnt;
270     sit.next(cls,occupied);
271     ALWAYS(cntSummary.find(cls,cnt));
272     cout<<cls<<":\t"<<cnt<<"\t"<<occupied<<endl;
273   }
274 }
275 
276 #endif
277 
278 /**
279  * Cleanup: do whatever needed after the last use of class Allocator.
280  * @since 10/01/2008 Manchester
281  */
cleanup()282 void Allocator::cleanup()
283 {
284   CALLC("Allocator::cleanup",MAKE_CALLS);
285   BYPASSING_ALLOCATOR;
286 
287   // delete all allocators
288   for (int i = _total-1;i >= 0;i--) {
289     delete _all[i];
290   }
291 
292 #if CHECK_LEAKS
293   if (MemoryLeak::report()) {
294     int leaks = 0;
295     for (int i = Descriptor::capacity-1;i >= 0;i--) {
296       Descriptor& d = Descriptor::map[i];
297       if (d.allocated) {
298 	if (! leaks) {
299 	  cout << "Memory leaks found!\n";
300 	}
301 	cout << ++leaks << ": " << d.cls << " (" << d.address << "), timestamp: "
302 	     << d.timestamp << "\n";
303       }
304     }
305     if (leaks) {
306       cout << "End of memory leak report\n";
307     }
308   }
309 #endif
310 
311   // release all the pages
312   for (int i = MAX_PAGES-1;i >= 0;i--) {
313 #if VDEBUG && TRACE_ALLOCATIONS
314     int cnt = 0;
315 #endif
316     while (_pages[i]) {
317       Page* pg = _pages[i];
318       _pages[i] = pg->next;
319 
320       char* mem = reinterpret_cast<char*>(pg);
321       free(mem);
322 #if VDEBUG && TRACE_ALLOCATIONS
323       cnt++;
324 #endif
325     }
326 #if VDEBUG && TRACE_ALLOCATIONS
327       if (cnt) {
328         cout << "deleted " << cnt << " global page(s) of size " << VPAGE_SIZE*(i+1) << endl;
329       }
330 #endif
331   }
332 
333 #if VDEBUG
334   delete[] Descriptor::map;
335 #endif
336 } // Allocator::initialise
337 
338 
339 /**
340  * Deallocate an object whose size is known. It works as follows:
341  * if the object size is less then REQUIRES_PAGE, it is put in the
342  * corresponding free list. Otherwise, it is deallocated as a large
343  * object.
344  * @since 10/01/2008 Manchester
345  */
346 #if VDEBUG
deallocateKnown(void * obj,size_t size,const char * className)347 void Allocator::deallocateKnown(void* obj,size_t size,const char* className)
348 #else
349 void Allocator::deallocateKnown(void* obj,size_t size)
350 #endif
351 {
352   CALLC("Allocator::deallocateKnown",MAKE_CALLS);
353   ASS(obj);
354 
355 #if VDEBUG
356   Descriptor* desc = Descriptor::find(obj);
357   desc->timestamp = ++Descriptor::globalTimestamp;
358 #if TRACE_ALLOCATIONS
359   cout << *desc << ": DK\n" << flush;
360 #endif
361   ASS_EQ(desc->address, obj);
362   ASS_STR_EQ(desc->cls,className);
363   ASS_EQ(desc->size, size);
364   ASS(desc->allocated);
365   ASS(desc->known);
366   ASS(! desc->page);
367 #endif
368 
369 #if USE_SYSTEM_ALLOCATION
370 #if VDEBUG
371   desc->allocated = 0;
372 #endif
373   free(obj);
374   return;
375 #else   // ! USE_SYSTEM_ALLOCATION
376   if (size >= REQUIRES_PAGE) {
377     char* mem = reinterpret_cast<char*>(obj)-PAGE_PREFIX_SIZE;
378     deallocatePages(reinterpret_cast<Page*>(mem));
379   }
380   else {
381     int index = (size-1)/sizeof(Known);
382     Known* mem = reinterpret_cast<Known*>(obj);
383     mem->next = _freeList[index];
384     _freeList[index] = mem;
385   }
386 
387 #if VDEBUG
388   desc->allocated = 0;
389 #endif
390 
391 #if WATCH_ADDRESS
392   unsigned addr = (unsigned)obj;
393   unsigned cp = Debug::Tracer::passedControlPoints();
394   if (addr <= WATCH_ADDRESS &&
395       WATCH_ADDRESS < addr+size &&
396       cp >= WATCH_FIRST &&
397       cp <= WATCH_LAST) {
398     unsigned currentValue = *((unsigned*)WATCH_ADDRESS);
399     cout << "Watch! Known-size piece deallocated!\n"
400 	 << "  Timestamp: " << cp << '\n'
401 	 << "  Piece addresses: " << (void*)addr << '-'
402 	 << (void*)(addr+size-1) << '\n';
403     if (currentValue != watchAddressLastValue) {
404       watchAddressLastValue = currentValue;
405       cout << "  Value: " << (void*)watchAddressLastValue << '\n';
406     }
407     cout << "  " << *desc << '\n'
408 	 << "Watch! end\n";
409   }
410 #endif
411 #endif
412 } // Allocator::deallocateKnown
413 
414 /**
415  * Deallocate an object whose size is unknown. It works similar to
416  * deallocateKnow except that an unknown contains an extra word
417  * storing the size of the object.
418  * @since 13/01/2008 Manchester
419  */
420 #if VDEBUG
deallocateUnknown(void * obj,const char * className)421 void Allocator::deallocateUnknown(void* obj,const char* className)
422 #else
423 void Allocator::deallocateUnknown(void* obj)
424 #endif
425 {
426   CALLC("Allocator::deallocateUnknown",MAKE_CALLS);
427 
428 #if VDEBUG
429   Descriptor* desc = Descriptor::find(obj);
430   desc->timestamp = ++Descriptor::globalTimestamp;
431 #if TRACE_ALLOCATIONS
432   cout << *desc << ": DU\n" << flush;
433 #endif
434   ASS_EQ(desc->address, obj);
435   ASS_STR_EQ(desc->cls,className);
436   ASS(desc->allocated);
437   ASS(! desc->known);
438   ASS(! desc->page);
439   desc->allocated = 0;
440 #endif
441 
442 #if USE_SYSTEM_ALLOCATION
443   char* memObj = reinterpret_cast<char*>(obj) - sizeof(Known);
444   free(memObj);
445   return;
446 #endif
447 
448   char* mem = reinterpret_cast<char*>(obj) - sizeof(Known);
449   Unknown* unknown = reinterpret_cast<Unknown*>(mem);
450   size_t size = unknown->size;
451   ASS_EQ(desc->size, size);
452 
453   if (size >= REQUIRES_PAGE) {
454     mem = mem-PAGE_PREFIX_SIZE;
455     deallocatePages(reinterpret_cast<Page*>(mem));
456   }
457   else {
458     Known* known = reinterpret_cast<Known*>(mem);
459     int index = (size-1)/sizeof(Known);
460     known->next = _freeList[index];
461     _freeList[index] = known;
462   }
463 
464 #if WATCH_ADDRESS
465   unsigned addr = (unsigned)(void*)mem;
466   unsigned cp = Debug::Tracer::passedControlPoints();
467   if (addr <= WATCH_ADDRESS &&
468       WATCH_ADDRESS < addr+size &&
469       cp >= WATCH_FIRST &&
470       cp <= WATCH_LAST) {
471     unsigned currentValue = *((unsigned*)WATCH_ADDRESS);
472     cout << "Watch! Unknown-size piece deallocated!\n"
473 	 << "  Timestamp: " << cp << '\n'
474 	 << "  Piece addresses: " << (void*)addr << '-'
475 	 << (void*)(addr+size-1) << '\n';
476     if (currentValue != watchAddressLastValue) {
477       watchAddressLastValue = currentValue;
478       cout << "  Value: " << (void*)watchAddressLastValue << '\n';
479     }
480     cout << "  " << *desc << '\n'
481 	 << "Watch! end\n";
482   }
483 #endif
484 } // Allocator::deallocateUnknown
485 
486 /**
487  * Reallocate an object whose size is unknown.
488  * The functionality should correspond to the realloc function for the standard library.
489  * This means it can be called with (obj == NULL) in which case it simply allocates.
490  * If obj points to allocated memory, this memory must have been obtained by calling
491  * reallocateUnknown or allocateUnknown.
492  * Also, the part of obj which fits into newsize will get copied over.
493  *
494  * The corresponding "free" function is deallocateUnknown.
495  */
496 #if VDEBUG
reallocateUnknown(void * obj,size_t newsize,const char * className)497 void* Allocator::reallocateUnknown(void* obj, size_t newsize, const char* className)
498 #else
499 void* Allocator::reallocateUnknown(void* obj, size_t newsize)
500 #endif
501 {
502   CALLC("Allocator::reallocateUnknown",MAKE_CALLS);
503 
504   // cout << "reallocateUnknown " << obj << " newsize " << newsize << endl;
505 
506 #if VDEBUG
507   void* newobj = allocateUnknown(newsize,className);
508 #else
509   void* newobj = allocateUnknown(newsize);
510 #endif
511 
512   if (obj == NULL) {
513     return newobj;
514   }
515 
516   size_t size = unknownsSize(obj);
517 
518   ASS_NEQ(size,newsize); // it all works when violated, but a code which wants to reallocate for the same size is suspicious
519 
520   if (newsize < size) {
521     size = newsize;
522   }
523 
524   std::memcpy(newobj,obj,size);
525 
526 #if VDEBUG
527   deallocateUnknown(obj,className);
528 #else
529   deallocateUnknown(obj);
530 #endif
531 
532   return newobj;
533 } // Allocator::reallocateUnknown
534 
535 /**
536  * Create a new allocator.
537  * @since 10/01/2008 Manchester
538  */
newAllocator()539 Allocator* Allocator::newAllocator()
540 {
541   CALLC("Allocator::newAllocator",MAKE_CALLS);
542   BYPASSING_ALLOCATOR;
543 
544 #if VDEBUG && USE_SYSTEM_ALLOCATION
545   ASSERTION_VIOLATION;
546 #else
547   Allocator* result = new Allocator();
548 
549   if (_total >= MAX_ALLOCATORS) {
550     throw Exception("The maximal number of allocators exceeded.");
551   }
552   _all[_total++] = result;
553   return result;
554 #endif
555 } // Allocator::newAllocator
556 
557 /**
558  * Allocate a (multi)page able to store a structure of size @b size
559  * @since 12/01/2008 Manchester
560  */
allocatePages(size_t size)561 Allocator::Page* Allocator::allocatePages(size_t size)
562 {
563   CALLC("Allocator::allocatePages",MAKE_CALLS);
564   ASS(size >= 0);
565 
566 #if VDEBUG && USE_SYSTEM_ALLOCATION
567   ASSERTION_VIOLATION;
568 #else
569   size += PAGE_PREFIX_SIZE;
570 
571   Page* result;
572   size_t index = (size-1)/VPAGE_SIZE;
573   size_t realSize = VPAGE_SIZE*(index+1);
574 
575   // check if the allocation isn't too big
576   if(index>=MAX_PAGES) {
577 #if SAFE_OUT_OF_MEM_SOLUTION
578     env.beginOutput();
579     reportSpiderStatus('m');
580     env.out() << "Unsupported amount of allocated memory: "<<realSize<<"!\n";
581     if(env.statistics) {
582       env.statistics->print(env.out());
583     }
584 #if VDEBUG
585     Debug::Tracer::printStack(env.out());
586 #endif
587     env.endOutput();
588     System::terminateImmediately(1);
589 #else
590     throw Lib::MemoryLimitExceededException();
591 #endif
592   }
593   // check if there is a page in the list available
594   if (_pages[index]) {
595     result = _pages[index];
596     _pages[index] = result->next;
597   }
598   else {
599     size_t newSize = _usedMemory+realSize;
600     if (_tolerated && newSize > _tolerated) {
601       env.statistics->terminationReason = Shell::Statistics::MEMORY_LIMIT;
602       //increase the limit, so that the exception can be handled properly.
603       _tolerated=newSize+1000000;
604 
605 #if SAFE_OUT_OF_MEM_SOLUTION
606       env.beginOutput();
607       reportSpiderStatus('m');
608       env.out() << "Memory limit exceeded!\n";
609 # if VDEBUG
610 	Allocator::reportUsageByClasses();
611 # endif
612       if(env.statistics) {
613 	env.statistics->print(env.out());
614       }
615       env.endOutput();
616       System::terminateImmediately(1);
617 #else
618       throw Lib::MemoryLimitExceededException();
619 #endif
620     }
621     _usedMemory = newSize;
622 
623     char* mem = static_cast<char*>(malloc(realSize));
624     if (!mem) {
625       env.beginOutput();
626       reportSpiderStatus('m');
627       env.out() << "Memory limit exceeded!\n";
628       if(env.statistics) {
629         // statistics should be fine when out of memory, but not RuntimeStatistics, which allocate a Stack
630         // (i.e. potential crazy exception recursion may happen in DEBUG mode)
631         env.statistics->print(env.out());
632       }
633       env.endOutput();
634       System::terminateImmediately(1);
635 
636       // CANNOT throw vampire exception when out of memory - it contains allocations because of the message string
637       // throw Lib::MemoryLimitExceededException(true);
638     }
639     result = reinterpret_cast<Page*>(mem);
640   }
641   result->size = realSize;
642 
643 #if VDEBUG
644   Descriptor* desc = Descriptor::find(result);
645   ASS(! desc->allocated);
646 
647   desc->address = result;
648   desc->cls = "Allocator::Page";
649   desc->timestamp = ++Descriptor::globalTimestamp;
650   desc->size = realSize;
651   desc->allocated = 1;
652   desc->known = 0;
653   desc->page = 1;
654 
655 #if TRACE_ALLOCATIONS
656   cout << *desc << ": AP\n" << flush;
657 #endif // TRACE_ALLOCATIONS
658 #endif // VDEBUG
659 
660   result->next = _myPages;
661   result->previous = 0;
662   if (_myPages) {
663     _myPages->previous = result;
664   }
665   _myPages = result;
666 
667 #if WATCH_ADDRESS
668   unsigned addr = (unsigned)(void*)result;
669   unsigned cp = Debug::Tracer::passedControlPoints();
670   if (addr <= WATCH_ADDRESS &&
671       WATCH_ADDRESS < addr+realSize) {
672     watchPage = true;
673     Debug::Tracer::canWatch = true;
674     if (cp >= WATCH_FIRST &&
675 	cp <= WATCH_LAST) {
676       watchAddressLastValue = *((unsigned*)WATCH_ADDRESS);
677       cout << "Watch! Page allocated!\n"
678 	   << "  Timestamp: " << cp << '\n'
679 	   << "  Page addresses: " << (void*)addr << '-'
680 	   << (void*)(addr+realSize-1) << '\n'
681 	   << "  Value: " << (void*)watchAddressLastValue << '\n'
682 	   << "Watch! end\n";
683     }
684   }
685 #endif
686 
687   return result;
688 #endif // USE_SYSTEM_ALLOCATION
689 } // Allocator::allocatePages
690 
691 /**
692  * Deallocate a (multi)page, that is, add it to the free list of
693  * pages.
694  * @since 11/01/2008 Manchester
695  */
deallocatePages(Page * page)696 void Allocator::deallocatePages(Page* page)
697 {
698   ASS(page);
699 
700 #if VDEBUG && USE_SYSTEM_ALLOCATION
701   ASSERTION_VIOLATION;
702 #else
703   CALLC("Allocator::deallocatePages",MAKE_CALLS);
704 
705 #if VDEBUG
706   Descriptor* desc = Descriptor::find(page);
707   desc->timestamp = ++Descriptor::globalTimestamp;
708 #if TRACE_ALLOCATIONS
709   cout << *desc << ": DP\n" << flush;
710 #endif
711   ASS(desc->address == page);
712   ASS_STR_EQ(desc->cls,"Allocator::Page");
713   ASS(desc->size == page->size);
714   ASS(desc->allocated);
715   ASS(! desc->known);
716   ASS(desc->page);
717   desc->allocated = 0;
718 #endif
719 
720   size_t size = page->size;
721   int index = (size-1)/VPAGE_SIZE;
722 
723   Page* next = page->next;
724   if (next) {
725     next->previous = page->previous;
726   }
727   if (page->previous) {
728     page->previous->next = next;
729   }
730 
731   if (page == _myPages) {
732     _myPages = next;
733   }
734 
735   page->next = _pages[index];
736   _pages[index] = page;
737 
738 #if WATCH_ADDRESS
739   unsigned addr = (unsigned)(void*)page;
740   unsigned cp = Debug::Tracer::passedControlPoints();
741   if (addr <= WATCH_ADDRESS &&
742       WATCH_ADDRESS < addr+size &&
743       cp >= WATCH_FIRST &&
744       cp <= WATCH_LAST) {
745     unsigned currentValue = *((unsigned*)WATCH_ADDRESS);
746     cout << "Watch! Page deallocated!\n"
747 	 << "  Timestamp: " << cp << '\n'
748 	 << "  Page addresses: " << (void*)addr << '-'
749 	 << (void*)(addr+size-1) << '\n';
750     if (currentValue != watchAddressLastValue) {
751       watchAddressLastValue = currentValue;
752       cout << "  Value: " << (void*)watchAddressLastValue << '\n';
753     }
754     cout << "Watch! end\n";
755   }
756 #endif
757 
758 #endif // ! USE_SYSTEM_ALLOCATION
759 } // Allocator::deallocatePages(Page*)
760 
761 
762 /**
763  * Allocate object of size @b size.
764  * @since 12/01/2008 Manchester
765  */
766 #if VDEBUG
allocateKnown(size_t size,const char * className)767 void* Allocator::allocateKnown(size_t size,const char* className)
768 #else
769 void* Allocator::allocateKnown(size_t size)
770 #endif
771 {
772   CALLC("Allocator::allocateKnown",MAKE_CALLS);
773   ASS(size > 0);
774 
775   char* result = allocatePiece(size);
776 
777 #if VDEBUG
778   Descriptor* desc = Descriptor::find(result);
779   ASS_REP(! desc->allocated, size);
780 
781   desc->address = result;
782   desc->cls = className;
783   desc->timestamp = ++Descriptor::globalTimestamp;
784   desc->size = size;
785   desc->allocated = 1;
786   desc->known = 1;
787   desc->page = 0;
788 #if TRACE_ALLOCATIONS
789   cout << *desc << ": AK\n" << flush;
790 #endif
791 #endif
792 
793 #if WATCH_ADDRESS
794   unsigned addr = (unsigned)(void*)result;
795   unsigned cp = Debug::Tracer::passedControlPoints();
796   if (addr <= WATCH_ADDRESS &&
797       WATCH_ADDRESS < addr+size &&
798       cp >= WATCH_FIRST &&
799       cp <= WATCH_LAST) {
800     unsigned currentValue = *((unsigned*)WATCH_ADDRESS);
801     cout << "Watch! Known-size piece allocated!\n"
802 	 << "  Timestamp: " << cp << '\n'
803 	 << "  Piece addresses: " << (void*)addr << '-'
804 	 << (void*)(addr+size-1) << '\n';
805     if (currentValue != watchAddressLastValue) {
806       watchAddressLastValue = currentValue;
807       cout << "  Value: " << (void*)watchAddressLastValue << '\n';
808     }
809     cout << "  " << *desc << '\n'
810 	 << "Watch! end\n";
811   }
812 #endif
813   return result;
814 } // Allocator::allocateKnown
815 
816 
817 /**
818  * Allocate a piece of memory of a given size.
819  * If @b size is REQUIRES_PAGE or more it is
820  * allocated on a separate page.
821  * @since 15/01/2008 Manchester
822  */
allocatePiece(size_t size)823 char* Allocator::allocatePiece(size_t size)
824 {
825   CALLC("Allocator::allocatePiece",MAKE_CALLS);
826 
827   char* result;
828 #if USE_SYSTEM_ALLOCATION
829 //  result = new char[size];
830   result = static_cast<char*>(malloc(size));
831 #else // USE_SYSTEM_ALLOCATION
832   if (size >= REQUIRES_PAGE) {
833     Page* page = allocatePages(size);
834     result = reinterpret_cast<char*>(page) + PAGE_PREFIX_SIZE;
835   }
836   else { // try to find it in the free list
837     int index = (size-1)/sizeof(Known);
838     // Align on the pointer basis
839     size = (index+1) * sizeof(Known);
840     Known* mem = _freeList[index];
841     if (mem) {
842       _freeList[index] = mem->next;
843       result = reinterpret_cast<char*>(mem);
844     } // There is no available piece in the free list
845     else if (_reserveBytesAvailable >= size) { // reserve has enough memory
846     use_reserve:
847       result = _nextAvailableReserve;
848       _nextAvailableReserve += size;
849       _reserveBytesAvailable -= size;
850     }
851     else {
852       // No bytes in the reserve, new reserve page must be allocated
853       // First, save any remaining memory in the free list
854       if (_reserveBytesAvailable) {
855 	index = (_reserveBytesAvailable-1)/sizeof(Known);
856 	Known* save = reinterpret_cast<Known*>(_nextAvailableReserve);
857 #if VDEBUG
858 	Descriptor* desc = Descriptor::find(save);
859 	ASS(! desc->allocated);
860 	desc->size = _reserveBytesAvailable;
861 	desc->timestamp = ++Descriptor::globalTimestamp;
862 #if TRACE_ALLOCATIONS
863 	cout << *desc << ": RR\n" << flush;
864 #endif
865 #endif
866 	save->next = _freeList[index];
867 	_freeList[index] = save;
868       }
869       Page* page = allocatePages(0);
870       _reserveBytesAvailable = VPAGE_SIZE-PAGE_PREFIX_SIZE;
871       _nextAvailableReserve = reinterpret_cast<char*>(&page->content);
872       goto use_reserve;
873     }
874   }
875 #endif // USE_SYSTEM_ALLOCATION
876   return result;
877 } // Allocator::allocatePiece
878 
879 
880 /**
881  * Works similar to allocateKnown but saves the size of the
882  * object in an extra word. More precisely, it saves the size
883  * of the memory needed to store the object, that is, the size
884  * of the object plus the size of a word.
885  * @since 13/01/2008 Manchester
886  */
887 #if VDEBUG
allocateUnknown(size_t size,const char * className)888 void* Allocator::allocateUnknown(size_t size,const char* className)
889 #else
890 void* Allocator::allocateUnknown(size_t size)
891 #endif
892 {
893   CALLC("Allocator::allocateUnknown",MAKE_CALLS);
894   ASS(size>0);
895 
896   size += sizeof(Known);
897   char* result = allocatePiece(size);
898   Unknown* unknown = reinterpret_cast<Unknown*>(result);
899   unknown->size = size;
900   result += sizeof(Known);
901 
902 #if VDEBUG
903   Descriptor* desc = Descriptor::find(result);
904   ASS(! desc->allocated);
905 
906   desc->address = result;
907   desc->cls = className;
908   desc->timestamp = ++Descriptor::globalTimestamp;
909   desc->size = size;
910   desc->allocated = 1;
911   desc->known = 0;
912   desc->page = 0;
913 
914 #if TRACE_ALLOCATIONS
915   cout << *desc << ": AU\n" << flush;
916 #endif
917 #endif
918 
919 #if WATCH_ADDRESS
920   unsigned addr = (unsigned)(void*)(result-sizeof(Known));
921   unsigned cp = Debug::Tracer::passedControlPoints();
922   if (addr <= WATCH_ADDRESS &&
923       WATCH_ADDRESS < addr+size &&
924       cp >= WATCH_FIRST &&
925       cp <= WATCH_LAST) {
926     unsigned currentValue = *((unsigned*)WATCH_ADDRESS);
927     cout << "Watch! Unknown-size piece allocated!\n"
928 	 << "  Timestamp: " << cp << '\n'
929 	 << "  Piece addresses: " << (void*)addr << '-'
930 	 << (void*)(addr+size-1) << '\n';
931     if (currentValue != watchAddressLastValue) {
932       watchAddressLastValue = currentValue;
933       cout << "  Value: " << (void*)watchAddressLastValue << '\n';
934     }
935     cout << "  " << *desc << '\n'
936 	 << "Watch! end\n";
937   }
938 #endif
939   return result;
940 } // Allocator::allocateUnknown
941 
942 
943 #if VDEBUG
944 /**
945  * Find a descriptor in the map, and if it is not there, add it.
946  * @since 14/12/2005 Bellevue
947  */
find(const void * addr)948 Allocator::Descriptor* Allocator::Descriptor::find (const void* addr)
949 {
950   CALLC("Allocator::Descriptor::find",MAKE_CALLS);
951   BYPASSING_ALLOCATOR;
952 
953   if (noOfEntries >= maxEntries) { // too many entries
954     // expand the hash table first
955 //     capacity = capacity ? 2*capacity : 8188;
956     capacity = capacity ? 2*capacity : 2000000;
957 
958 #if TRACE_ALLOCATIONS
959     cout << "Allocator map expansion to capacity " << capacity << "\n" << flush;
960 #endif
961 
962     Descriptor* oldMap = map;
963     try {
964       map = new Descriptor [capacity];
965     } catch(bad_alloc&) {
966       env.beginOutput();
967       reportSpiderStatus('m');
968       env.out() << "Memory limit exceeded!\n";
969       env.endOutput();
970       System::terminateImmediately(1);
971 
972       // CANNOT throw vampire exception when out of memory - it contains allocations because of the message string
973       // throw Lib::MemoryLimitExceededException(true);
974     }
975     Descriptor* oldAfterLast = afterLast;
976     afterLast = map + capacity;
977     maxEntries = (int)(capacity * 0.7);
978     noOfEntries = 0;
979 
980     for (Descriptor* current = oldMap;current != oldAfterLast;current++) {
981       if (! current->address) {
982 	continue;
983       }
984       // now current is occupied
985       Descriptor* d = find(current->address);
986       *d = *current;
987     }
988     delete [] oldMap;
989   }
990   Descriptor* desc = map + (hash(addr) % capacity);
991   while (desc->address) {
992     if (desc->address == addr) {
993       return desc;
994     }
995 
996     desc++;
997     // check if the entry is a valid one
998     if (desc == afterLast) {
999       desc = map;
1000     }
1001   }
1002 
1003   desc->address = addr;
1004   noOfEntries++;
1005 
1006   return desc;
1007 } // Allocator::Descriptor::find
1008 
1009 /**
1010  * Output the string description of the descriptor to an ostream.
1011  * @author Martin Suda
1012  * @since 12/08/2014 Manchester
1013  */
operator <<(ostream & out,const Allocator::Descriptor & d)1014 ostream& Lib::operator<<(ostream& out, const Allocator::Descriptor& d) {
1015   CALLC("operator<<(ostream,Allocator::Descriptor)",MAKE_CALLS);
1016 
1017   out << (size_t)(&d)
1018       << " [address:" << d.address
1019       << ",timestamp:" << d.timestamp
1020       << ",class:" << d.cls
1021       << ",size:" << d.size
1022       << ",allocated:" << (d.allocated ? "yes" : "no")
1023       << ",known:" << (d.known ? "yes" : "no")
1024       << ",page:" << (d.page ? "yes" : "no") << ']';
1025 
1026   return out;
1027 }
1028 
operator new[](size_t s)1029 void* Allocator::Descriptor::operator new[](size_t s) {
1030   return malloc(s);
1031 }
1032 
operator delete[](void * obj)1033 void Allocator::Descriptor::operator delete[](void* obj) {
1034   free(obj);
1035 }
1036 
1037 /**
1038  * Initialise a descriptor.
1039  * @since 17/12/2005 Vancouver
1040  */
Descriptor()1041 Allocator::Descriptor::Descriptor ()
1042   : address(0),
1043     cls("???"),
1044     timestamp(0),
1045     size(0),
1046     allocated(0),
1047     known(0),
1048     page(0)
1049 {
1050 //   CALL("Allocator::Descriptor::Descriptor");
1051 } // Allocator::Descriptor::Descriptor
1052 
1053 /**
1054  * The FNV-hashing.
1055  * @since 31/03/2006 Bellevue
1056  */
hash(const void * addr)1057 unsigned Allocator::Descriptor::hash (const void* addr)
1058 {
1059   CALLC("Allocator::Descriptor::hash",MAKE_CALLS);
1060 
1061   char* val = reinterpret_cast<char*>(&addr);
1062   unsigned hash = 2166136261u;
1063   for (int i = sizeof(void*)-1;i >= 0;i--) {
1064     hash = (hash ^ val[i]) * 16777619u;
1065   }
1066   return hash;
1067 } // Allocator::Descriptor::hash(const char* str)
1068 
1069 #endif
1070 
1071 /**
1072  * In debug mode we replace the global new and delete (also the array versions)
1073  * and terminate in cases when they are used "unwillingly".
1074  * Where "unwillingly" means people didn't mark the code explicitly with BYPASSING_ALLOCATOR
1075  * and yet they attempt to call global new (and not the class specific versions
1076  * built on top of Allocator).
1077  *
1078  * Update: In release, we newly use global new/delete as well,
1079  * but just silently redirect the allocations to our Allocator.
1080  *
1081  * This is a link about some requirements on new/delete:
1082  * http://stackoverflow.com/questions/7194127/how-should-i-write-iso-c-standard-conformant-custom-new-and-delete-operators/
1083  * (Note that we ignore the globalHandler issue here.)
1084  **/
1085 
operator new(size_t sz)1086 void* operator new(size_t sz) {
1087   ASS_REP(Allocator::_tolerantZone > 0,"Attempted to use global new operator, thus bypassing Allocator!");
1088   // Please read: https://github.com/easychair/vampire/wiki/Attempted-to-use-global-new-operator,-thus-bypassing-Allocator!
1089 
1090   static Allocator::Initialiser i; // to initialize Allocator even for other libraries
1091 
1092   if (sz == 0)
1093     sz = 1;
1094 
1095   void* res = ALLOC_UNKNOWN(sz,"global new");
1096 
1097   if (!res)
1098     throw bad_alloc();
1099 
1100   return res;
1101 }
1102 
operator new[](size_t sz)1103 void* operator new[](size_t sz) {
1104   ASS_REP(Allocator::_tolerantZone > 0,"Attempted to use global new[] operator, thus bypassing Allocator!");
1105   // Please read: https://github.com/easychair/vampire/wiki/Attempted-to-use-global-new-operator,-thus-bypassing-Allocator!
1106 
1107   static Allocator::Initialiser i; // to initialize Allocator even for other libraries
1108 
1109   if (sz == 0)
1110     sz = 1;
1111 
1112   void* res = ALLOC_UNKNOWN(sz,"global new[]");
1113 
1114   if (!res)
1115     throw bad_alloc();
1116 
1117   return res;
1118 }
1119 
operator delete(void * obj)1120 void operator delete(void* obj) throw() {
1121   ASS_REP(Allocator::_tolerantZone > 0,"Custom operator new matched by global delete!");
1122   // Please read: https://github.com/easychair/vampire/wiki/Attempted-to-use-global-new-operator,-thus-bypassing-Allocator!
1123 
1124   static Allocator::Initialiser i; // to initialize Allocator even for other libraries
1125 
1126   if (obj != nullptr) {
1127     DEALLOC_UNKNOWN(obj,"global new");
1128   }
1129 }
1130 
operator delete[](void * obj)1131 void operator delete[](void* obj) throw() {
1132   ASS_REP(Allocator::_tolerantZone > 0,"Custom operator new[] matched by global delete[]!");
1133   // Please read: https://github.com/easychair/vampire/wiki/Attempted-to-use-global-new-operator,-thus-bypassing-Allocator!
1134 
1135   static Allocator::Initialiser i; // to initialize Allocator even for other libraries
1136 
1137   if (obj != nullptr) {
1138     DEALLOC_UNKNOWN(obj,"global new[]");
1139   }
1140 }
1141 
1142 #if VTEST
1143 
1144 #include "Random.hpp"
1145 using namespace Lib;
1146 
1147 struct Mem
1148 {
1149   void* address; // 0 if deallocated
1150   int size;      // 0 is deallocated
1151   bool known;
1152   const char* className;
MemMem1153   Mem() : address(0) {}
1154 };
1155 
1156 /**
1157  * Allocate many objects of known small sizes and
1158  * then deallocate them all.
1159  * @since 17/03/2008 Torrevieja
1160  */
testAllocator()1161 void testAllocator()
1162 {
1163   CALLC("testAllocator",MAKE_CALLS);
1164 //   Random::setSeed(1);
1165   cout << "Testing the Allocator class...\n";
1166 
1167   Allocator* a = Allocator::current;
1168 
1169   int tries = 1000000000;  // number of tries
1170   int pieces = 1000;  // max number of allocated pieces
1171   int maxsize = 1000000;    // maximal memory size
1172   const char* classes[10] = {"a","b","c","d","e","f","g","h","i","j"};
1173   int frequency = 1000; // frequency of outputting a dot to cout
1174   int out = frequency;   // output when 0
1175 
1176    Mem* mems = new Mem[pieces]; // memory pieces
1177   int total = 0;
1178   for (int i = 0; i < tries;i++) {
1179     out--;
1180     if (! out) {
1181       out = frequency;
1182       cout << '.' << flush;
1183 //       cout << total << '\n' << flush;
1184     }
1185     int rand = Random::getInteger(pieces);
1186 
1187     Mem& m = mems[rand];
1188     if (m.address) { // allocated
1189       if (m.known) {
1190 	a->deallocateKnown(m.address,m.size,m.className);
1191       }
1192       else {
1193 	a->deallocateUnknown(m.address,m.className);
1194       }
1195       m.address = 0;
1196     }
1197     else { // not allocated
1198       int size = Random::getInteger(maxsize)+1;
1199       m.size = size;
1200       total += size;
1201       const char* className = classes[Random::getInteger(10)];
1202       m.className = className;
1203       if (Random::getBit()) {
1204 	m.known = false;
1205 	m.address = a->allocateUnknown(size,className);
1206       }
1207       else {
1208 	m.known = true;
1209 	m.address = a->allocateKnown(size,className);
1210       }
1211     }
1212   }
1213 
1214   cout << "\nTest completed!\n";
1215 } // testAllocator
1216 
1217 #endif // VTEST
1218 
1219 
1220