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