Changeset c2b3243 for libcfa/src/heap.cfa
- Timestamp:
- Oct 18, 2022, 9:13:33 PM (3 years ago)
- Branches:
- ADT, ast-experimental, master
- Children:
- 9511841
- Parents:
- 5408b59 (diff), ce7d197 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)links above to see all the changes relative to each parent. - File:
-
- 1 edited
-
libcfa/src/heap.cfa (modified) (55 diffs)
Legend:
- Unmodified
- Added
- Removed
-
libcfa/src/heap.cfa
r5408b59 rc2b3243 10 10 // Created On : Tue Dec 19 21:58:35 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Apr 29 19:05:03202213 // Update Count : 1 16712 // Last Modified On : Thu Oct 13 22:21:52 2022 13 // Update Count : 1557 14 14 // 15 15 16 #include <stdio.h> 16 17 #include <string.h> // memset, memcpy 17 18 #include <limits.h> // ULONG_MAX … … 21 22 #include <malloc.h> // memalign, malloc_usable_size 22 23 #include <sys/mman.h> // mmap, munmap 24 extern "C" { 23 25 #include <sys/sysinfo.h> // get_nprocs 26 } // extern "C" 24 27 25 28 #include "bits/align.hfa" // libAlign 26 29 #include "bits/defs.hfa" // likely, unlikely 27 #include " bits/locks.hfa" // __spinlock_t30 #include "concurrency/kernel/fwd.hfa" // __POLL_PREEMPTION 28 31 #include "startup.hfa" // STARTUP_PRIORITY_MEMORY 29 #include "math.hfa" // min32 #include "math.hfa" // ceiling, min 30 33 #include "bitmanip.hfa" // is_pow2, ceiling2 31 34 32 #define FASTLOOKUP 33 #define __STATISTICS__ 35 // supported mallopt options 36 #ifndef M_MMAP_THRESHOLD 37 #define M_MMAP_THRESHOLD (-1) 38 #endif // M_MMAP_THRESHOLD 39 40 #ifndef M_TOP_PAD 41 #define M_TOP_PAD (-2) 42 #endif // M_TOP_PAD 43 44 #define FASTLOOKUP // use O(1) table lookup from allocation size to bucket size 45 #define RETURNSPIN // toggle spinlock / lockfree stack 46 #define OWNERSHIP // return freed memory to owner thread 47 48 #define CACHE_ALIGN 64 49 #define CALIGN __attribute__(( aligned(CACHE_ALIGN) )) 50 51 #define TLSMODEL __attribute__(( tls_model("initial-exec") )) 52 53 //#define __STATISTICS__ 54 55 enum { 56 // The default extension heap amount in units of bytes. When the current heap reaches the brk address, the brk 57 // address is extended by the extension amount. 58 __CFA_DEFAULT_HEAP_EXPANSION__ = 10 * 1024 * 1024, 59 60 // The mmap crossover point during allocation. Allocations less than this amount are allocated from buckets; values 61 // greater than or equal to this value are mmap from the operating system. 62 __CFA_DEFAULT_MMAP_START__ = 512 * 1024 + 1, 63 64 // The default unfreed storage amount in units of bytes. When the uC++ program ends it subtracts this amount from 65 // the malloc/free counter to adjust for storage the program does not free. 66 __CFA_DEFAULT_HEAP_UNFREED__ = 0 67 }; // enum 68 69 70 //####################### Heap Trace/Print #################### 34 71 35 72 … … 55 92 static bool prtFree = false; 56 93 57 staticbool prtFree() {94 bool prtFree() { 58 95 return prtFree; 59 96 } // prtFree 60 97 61 staticbool prtFreeOn() {98 bool prtFreeOn() { 62 99 bool temp = prtFree; 63 100 prtFree = true; … … 65 102 } // prtFreeOn 66 103 67 staticbool prtFreeOff() {104 bool prtFreeOff() { 68 105 bool temp = prtFree; 69 106 prtFree = false; … … 72 109 73 110 74 enum { 75 // The default extension heap amount in units of bytes. When the current heap reaches the brk address, the brk 76 // address is extended by the extension amount. 77 __CFA_DEFAULT_HEAP_EXPANSION__ = 10 * 1024 * 1024, 78 79 // The mmap crossover point during allocation. Allocations less than this amount are allocated from buckets; values 80 // greater than or equal to this value are mmap from the operating system. 81 __CFA_DEFAULT_MMAP_START__ = 512 * 1024 + 1, 82 83 // The default unfreed storage amount in units of bytes. When the uC++ program ends it subtracts this amount from 84 // the malloc/free counter to adjust for storage the program does not free. 85 __CFA_DEFAULT_HEAP_UNFREED__ = 0 86 }; // enum 111 //######################### Spin Lock ######################### 112 113 114 // pause to prevent excess processor bus usage 115 #if defined( __i386 ) || defined( __x86_64 ) 116 #define Pause() __asm__ __volatile__ ( "pause" : : : ) 117 #elif defined(__ARM_ARCH) 118 #define Pause() __asm__ __volatile__ ( "YIELD" : : : ) 119 #else 120 #error unsupported architecture 121 #endif 122 123 typedef volatile uintptr_t SpinLock_t CALIGN; // aligned addressable word-size 124 125 static inline __attribute__((always_inline)) void lock( volatile SpinLock_t & slock ) { 126 enum { SPIN_START = 4, SPIN_END = 64 * 1024, }; 127 unsigned int spin = SPIN_START; 128 129 for ( unsigned int i = 1;; i += 1 ) { 130 if ( slock == 0 && __atomic_test_and_set( &slock, __ATOMIC_SEQ_CST ) == 0 ) break; // Fence 131 for ( volatile unsigned int s = 0; s < spin; s += 1 ) Pause(); // exponential spin 132 spin += spin; // powers of 2 133 //if ( i % 64 == 0 ) spin += spin; // slowly increase by powers of 2 134 if ( spin > SPIN_END ) spin = SPIN_END; // cap spinning 135 } // for 136 } // spin_lock 137 138 static inline __attribute__((always_inline)) void unlock( volatile SpinLock_t & slock ) { 139 __atomic_clear( &slock, __ATOMIC_SEQ_CST ); // Fence 140 } // spin_unlock 87 141 88 142 … … 120 174 unsigned int free_calls, free_null_calls; 121 175 unsigned long long int free_storage_request, free_storage_alloc; 122 unsigned int away_pulls, away_pushes;123 unsigned long long int away_storage_request, away_storage_alloc;176 unsigned int return_pulls, return_pushes; 177 unsigned long long int return_storage_request, return_storage_alloc; 124 178 unsigned int mmap_calls, mmap_0_calls; // no zero calls 125 179 unsigned long long int mmap_storage_request, mmap_storage_alloc; … … 131 185 132 186 static_assert( sizeof(HeapStatistics) == CntTriples * sizeof(StatsOverlay), 133 "Heap statistics counter-triplets does not match with array size" );187 "Heap statistics counter-triplets does not match with array size" ); 134 188 135 189 static void HeapStatisticsCtor( HeapStatistics & stats ) { … … 203 257 static_assert( libAlign() >= sizeof( Storage ), "minimum alignment < sizeof( Storage )" ); 204 258 205 struct FreeHeader {206 size_t blockSize __attribute__(( aligned (8) )); // size of allocations on this list259 struct __attribute__(( aligned (8) )) FreeHeader { 260 size_t blockSize __attribute__(( aligned(8) )); // size of allocations on this list 207 261 #if BUCKETLOCK == SPINLOCK 208 __spinlock_t lock; 209 Storage * freeList; 262 #ifdef OWNERSHIP 263 #ifdef RETURNSPIN 264 SpinLock_t returnLock; 265 #endif // RETURNSPIN 266 Storage * returnList; // other thread return list 267 #endif // OWNERSHIP 268 Storage * freeList; // thread free list 210 269 #else 211 270 StackLF(Storage) freeList; 212 271 #endif // BUCKETLOCK 213 } __attribute__(( aligned (8) )); // FreeHeader 272 Heap * homeManager; // heap owner (free storage to bucket, from bucket to heap) 273 }; // FreeHeader 214 274 215 275 FreeHeader freeLists[NoBucketSizes]; // buckets for different allocation sizes 216 217 __spinlock_t extlock; // protects allocation-buffer extension 218 void * heapBegin; // start of heap 219 void * heapEnd; // logical end of heap 220 size_t heapRemaining; // amount of storage not allocated in the current chunk 276 void * heapBuffer; // start of free storage in buffer 277 size_t heapReserve; // amount of remaining free storage in buffer 278 279 #if defined( __STATISTICS__ ) || defined( __CFA_DEBUG__ ) 280 Heap * nextHeapManager; // intrusive link of existing heaps; traversed to collect statistics or check unfreed storage 281 #endif // __STATISTICS__ || __CFA_DEBUG__ 282 Heap * nextFreeHeapManager; // intrusive link of free heaps from terminated threads; reused by new threads 283 284 #ifdef __CFA_DEBUG__ 285 int64_t allocUnfreed; // running total of allocations minus frees; can be negative 286 #endif // __CFA_DEBUG__ 287 288 #ifdef __STATISTICS__ 289 HeapStatistics stats; // local statistic table for this heap 290 #endif // __STATISTICS__ 221 291 }; // Heap 222 292 223 293 #if BUCKETLOCK == LOCKFREE 224 static inline { 294 inline __attribute__((always_inline)) 295 static { 225 296 Link(Heap.Storage) * ?`next( Heap.Storage * this ) { return &this->header.kind.real.next; } 226 297 void ?{}( Heap.FreeHeader & ) {} … … 229 300 #endif // LOCKFREE 230 301 231 static inline size_t getKey( const Heap.FreeHeader & freeheader ) { return freeheader.blockSize; } 302 303 struct HeapMaster { 304 SpinLock_t extLock; // protects allocation-buffer extension 305 SpinLock_t mgrLock; // protects freeHeapManagersList, heapManagersList, heapManagersStorage, heapManagersStorageEnd 306 307 void * heapBegin; // start of heap 308 void * heapEnd; // logical end of heap 309 size_t heapRemaining; // amount of storage not allocated in the current chunk 310 size_t pageSize; // architecture pagesize 311 size_t heapExpand; // sbrk advance 312 size_t mmapStart; // cross over point for mmap 313 unsigned int maxBucketsUsed; // maximum number of buckets in use 314 315 Heap * heapManagersList; // heap-list head 316 Heap * freeHeapManagersList; // free-list head 317 318 // Heap superblocks are not linked; heaps in superblocks are linked via intrusive links. 319 Heap * heapManagersStorage; // next heap to use in heap superblock 320 Heap * heapManagersStorageEnd; // logical heap outside of superblock's end 321 322 #ifdef __STATISTICS__ 323 HeapStatistics stats; // global stats for thread-local heaps to add there counters when exiting 324 unsigned long int threads_started, threads_exited; // counts threads that have started and exited 325 unsigned long int reused_heap, new_heap; // counts reusability of heaps 326 unsigned int sbrk_calls; 327 unsigned long long int sbrk_storage; 328 int stats_fd; 329 #endif // __STATISTICS__ 330 }; // HeapMaster 232 331 233 332 234 333 #ifdef FASTLOOKUP 235 enum { LookupSizes = 65_536 + sizeof(Heap.Storage) }; // number of fast lookup sizes334 enum { LookupSizes = 65_536 + sizeof(Heap.Storage) }; // number of fast lookup sizes 236 335 static unsigned char lookup[LookupSizes]; // O(1) lookup for small sizes 237 336 #endif // FASTLOOKUP 238 337 239 static const off_t mmapFd = -1; // fake or actual fd for anonymous file 240 #ifdef __CFA_DEBUG__ 241 static bool heapBoot = 0; // detect recursion during boot 242 #endif // __CFA_DEBUG__ 338 static volatile bool heapMasterBootFlag = false; // trigger for first heap 339 static HeapMaster heapMaster @= {}; // program global 340 341 static void heapMasterCtor(); 342 static void heapMasterDtor(); 343 static Heap * getHeap(); 243 344 244 345 … … 268 369 static_assert( NoBucketSizes == sizeof(bucketSizes) / sizeof(bucketSizes[0] ), "size of bucket array wrong" ); 269 370 270 // The constructor for heapManager is called explicitly in memory_startup. 271 static Heap heapManager __attribute__(( aligned (128) )) @= {}; // size of cache line to prevent false sharing 371 372 // extern visibility, used by runtime kernel 373 libcfa_public size_t __page_size; // architecture pagesize 374 libcfa_public int __map_prot; // common mmap/mprotect protection 375 376 377 // Thread-local storage is allocated lazily when the storage is accessed. 378 static __thread size_t PAD1 CALIGN TLSMODEL __attribute__(( unused )); // protect false sharing 379 static __thread Heap * volatile heapManager CALIGN TLSMODEL; 380 static __thread size_t PAD2 CALIGN TLSMODEL __attribute__(( unused )); // protect further false sharing 381 382 383 // declare helper functions for HeapMaster 384 void noMemory(); // forward, called by "builtin_new" when malloc returns 0 385 386 387 // generic Bsearchl does not inline, so substitute with hand-coded binary-search. 388 inline __attribute__((always_inline)) 389 static size_t Bsearchl( unsigned int key, const unsigned int vals[], size_t dim ) { 390 size_t l = 0, m, h = dim; 391 while ( l < h ) { 392 m = (l + h) / 2; 393 if ( (unsigned int &)(vals[m]) < key ) { // cast away const 394 l = m + 1; 395 } else { 396 h = m; 397 } // if 398 } // while 399 return l; 400 } // Bsearchl 401 402 403 void heapMasterCtor() with( heapMaster ) { 404 // Singleton pattern to initialize heap master 405 406 verify( bucketSizes[0] == (16 + sizeof(Heap.Storage)) ); 407 408 __page_size = sysconf( _SC_PAGESIZE ); 409 __map_prot = PROT_READ | PROT_WRITE | PROT_EXEC; 410 411 ?{}( extLock ); 412 ?{}( mgrLock ); 413 414 char * end = (char *)sbrk( 0 ); 415 heapBegin = heapEnd = sbrk( (char *)ceiling2( (long unsigned int)end, libAlign() ) - end ); // move start of heap to multiple of alignment 416 heapRemaining = 0; 417 heapExpand = malloc_expansion(); 418 mmapStart = malloc_mmap_start(); 419 420 // find the closest bucket size less than or equal to the mmapStart size 421 maxBucketsUsed = Bsearchl( mmapStart, bucketSizes, NoBucketSizes ); // binary search 422 423 verify( (mmapStart >= pageSize) && (bucketSizes[NoBucketSizes - 1] >= mmapStart) ); 424 verify( maxBucketsUsed < NoBucketSizes ); // subscript failure ? 425 verify( mmapStart <= bucketSizes[maxBucketsUsed] ); // search failure ? 426 427 heapManagersList = 0p; 428 freeHeapManagersList = 0p; 429 430 heapManagersStorage = 0p; 431 heapManagersStorageEnd = 0p; 432 433 #ifdef __STATISTICS__ 434 HeapStatisticsCtor( stats ); // clear statistic counters 435 threads_started = threads_exited = 0; 436 reused_heap = new_heap = 0; 437 sbrk_calls = sbrk_storage = 0; 438 stats_fd = STDERR_FILENO; 439 #endif // __STATISTICS__ 440 441 #ifdef FASTLOOKUP 442 for ( unsigned int i = 0, idx = 0; i < LookupSizes; i += 1 ) { 443 if ( i > bucketSizes[idx] ) idx += 1; 444 lookup[i] = idx; 445 verify( i <= bucketSizes[idx] ); 446 verify( (i <= 32 && idx == 0) || (i > bucketSizes[idx - 1]) ); 447 } // for 448 #endif // FASTLOOKUP 449 450 heapMasterBootFlag = true; 451 } // heapMasterCtor 452 453 454 #define NO_MEMORY_MSG "**** Error **** insufficient heap memory available to allocate %zd new bytes." 455 456 Heap * getHeap() with( heapMaster ) { 457 Heap * heap; 458 if ( freeHeapManagersList ) { // free heap for reused ? 459 heap = freeHeapManagersList; 460 freeHeapManagersList = heap->nextFreeHeapManager; 461 462 #ifdef __STATISTICS__ 463 reused_heap += 1; 464 #endif // __STATISTICS__ 465 } else { // free heap not found, create new 466 // Heap size is about 12K, FreeHeader (128 bytes because of cache alignment) * NoBucketSizes (91) => 128 heaps * 467 // 12K ~= 120K byte superblock. Where 128-heap superblock handles a medium sized multi-processor server. 468 size_t remaining = heapManagersStorageEnd - heapManagersStorage; // remaining free heaps in superblock 469 if ( ! heapManagersStorage || remaining != 0 ) { 470 // Each block of heaps is a multiple of the number of cores on the computer. 471 int HeapDim = get_nprocs(); // get_nprocs_conf does not work 472 size_t size = HeapDim * sizeof( Heap ); 473 474 heapManagersStorage = (Heap *)mmap( 0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 ); 475 if ( unlikely( heapManagersStorage == (Heap *)MAP_FAILED ) ) { // failed ? 476 if ( errno == ENOMEM ) abort( NO_MEMORY_MSG, size ); // no memory 477 // Do not call strerror( errno ) as it may call malloc. 478 abort( "**** Error **** attempt to allocate block of heaps of size %zu bytes and mmap failed with errno %d.", size, errno ); 479 } // if 480 heapManagersStorageEnd = &heapManagersStorage[HeapDim]; // outside array 481 } // if 482 483 heap = heapManagersStorage; 484 heapManagersStorage = heapManagersStorage + 1; // bump next heap 485 486 #if defined( __STATISTICS__ ) || defined( __CFA_DEBUG__ ) 487 heap->nextHeapManager = heapManagersList; 488 #endif // __STATISTICS__ || __CFA_DEBUG__ 489 heapManagersList = heap; 490 491 #ifdef __STATISTICS__ 492 new_heap += 1; 493 #endif // __STATISTICS__ 494 495 with( *heap ) { 496 for ( unsigned int j = 0; j < NoBucketSizes; j += 1 ) { // initialize free lists 497 #ifdef OWNERSHIP 498 #ifdef RETURNSPIN 499 ?{}( freeLists[j].returnLock ); 500 #endif // RETURNSPIN 501 freeLists[j].returnList = 0p; 502 #endif // OWNERSHIP 503 freeLists[j].freeList = 0p; 504 freeLists[j].homeManager = heap; 505 freeLists[j].blockSize = bucketSizes[j]; 506 } // for 507 508 heapBuffer = 0p; 509 heapReserve = 0; 510 nextFreeHeapManager = 0p; 511 #ifdef __CFA_DEBUG__ 512 allocUnfreed = 0; 513 #endif // __CFA_DEBUG__ 514 } // with 515 } // if 516 517 return heap; 518 } // getHeap 519 520 521 void heapManagerCtor() libcfa_public { 522 if ( unlikely( ! heapMasterBootFlag ) ) heapMasterCtor(); 523 524 lock( heapMaster.mgrLock ); // protect heapMaster counters 525 526 // get storage for heap manager 527 528 heapManager = getHeap(); 529 530 #ifdef __STATISTICS__ 531 HeapStatisticsCtor( heapManager->stats ); // heap local 532 heapMaster.threads_started += 1; 533 #endif // __STATISTICS__ 534 535 unlock( heapMaster.mgrLock ); 536 } // heapManagerCtor 537 538 539 void heapManagerDtor() libcfa_public { 540 lock( heapMaster.mgrLock ); 541 542 // place heap on list of free heaps for reusability 543 heapManager->nextFreeHeapManager = heapMaster.freeHeapManagersList; 544 heapMaster.freeHeapManagersList = heapManager; 545 546 #ifdef __STATISTICS__ 547 heapMaster.threads_exited += 1; 548 #endif // __STATISTICS__ 549 550 // Do not set heapManager to NULL because it is used after Cforall is shutdown but before the program shuts down. 551 552 unlock( heapMaster.mgrLock ); 553 } // heapManagerDtor 272 554 273 555 274 556 //####################### Memory Allocation Routines Helpers #################### 275 557 276 277 #ifdef __CFA_DEBUG__278 static size_t allocUnfreed; // running total of allocations minus frees279 280 static void prtUnfreed() {281 if ( allocUnfreed != 0 ) {282 // DO NOT USE STREAMS AS THEY MAY BE UNAVAILABLE AT THIS POINT.283 char helpText[512];284 __cfaabi_bits_print_buffer( STDERR_FILENO, helpText, sizeof(helpText),285 "CFA warning (UNIX pid:%ld) : program terminating with %zu(0x%zx) bytes of storage allocated but not freed.\n"286 "Possible cause is unfreed storage allocated by the program or system/library routines called from the program.\n",287 (long int)getpid(), allocUnfreed, allocUnfreed ); // always print the UNIX pid288 } // if289 } // prtUnfreed290 558 291 559 extern int cfa_main_returned; // from interpose.cfa 292 560 extern "C" { 561 void memory_startup( void ) { 562 if ( ! heapMasterBootFlag ) heapManagerCtor(); // sanity check 563 } // memory_startup 564 565 void memory_shutdown( void ) { 566 heapManagerDtor(); 567 } // memory_shutdown 568 293 569 void heapAppStart() { // called by __cfaabi_appready_startup 294 allocUnfreed = 0; 570 verify( heapManager ); 571 #ifdef __CFA_DEBUG__ 572 heapManager->allocUnfreed = 0; // clear prior allocation counts 573 #endif // __CFA_DEBUG__ 574 575 #ifdef __STATISTICS__ 576 HeapStatisticsCtor( heapManager->stats ); // clear prior statistic counters 577 #endif // __STATISTICS__ 295 578 } // heapAppStart 296 579 297 580 void heapAppStop() { // called by __cfaabi_appready_startdown 298 fclose( stdin ); fclose( stdout ); 299 if ( cfa_main_returned ) prtUnfreed(); // do not check unfreed storage if exit called 581 fclose( stdin ); fclose( stdout ); // free buffer storage 582 if ( ! cfa_main_returned ) return; // do not check unfreed storage if exit called 583 584 #ifdef __CFA_DEBUG__ 585 // allocUnfreed is set to 0 when a heap is created and it accumulates any unfreed storage during its multiple thread 586 // usages. At the end, add up each heap allocUnfreed value across all heaps to get the total unfreed storage. 587 int64_t allocUnfreed = 0; 588 for ( Heap * heap = heapMaster.heapManagersList; heap; heap = heap->nextHeapManager ) { 589 allocUnfreed += heap->allocUnfreed; 590 } // for 591 592 allocUnfreed -= malloc_unfreed(); // subtract any user specified unfreed storage 593 if ( allocUnfreed > 0 ) { 594 // DO NOT USE STREAMS AS THEY MAY BE UNAVAILABLE AT THIS POINT. 595 char helpText[512]; 596 __cfaabi_bits_print_buffer( STDERR_FILENO, helpText, sizeof(helpText), 597 "CFA warning (UNIX pid:%ld) : program terminating with %ju(0x%jx) bytes of storage allocated but not freed.\n" 598 "Possible cause is unfreed storage allocated by the program or system/library routines called from the program.\n", 599 (long int)getpid(), allocUnfreed, allocUnfreed ); // always print the UNIX pid 600 } // if 601 #endif // __CFA_DEBUG__ 300 602 } // heapAppStop 301 603 } // extern "C" 302 #endif // __CFA_DEBUG__303 604 304 605 305 606 #ifdef __STATISTICS__ 306 607 static HeapStatistics stats; // zero filled 307 static unsigned int sbrk_calls;308 static unsigned long long int sbrk_storage;309 // Statistics file descriptor (changed by malloc_stats_fd).310 static int stats_fd = STDERR_FILENO; // default stderr311 608 312 609 #define prtFmt \ … … 321 618 " realloc >0 calls %'u; 0 calls %'u; storage %'llu / %'llu bytes\n" \ 322 619 " free !null calls %'u; null calls %'u; storage %'llu / %'llu bytes\n" \ 323 " sbrk calls %'u; storage %'llu bytes\n" \ 324 " mmap calls %'u; storage %'llu / %'llu bytes\n" \ 325 " munmap calls %'u; storage %'llu / %'llu bytes\n" \ 620 " return pulls %'u; pushes %'u; storage %'llu / %'llu bytes\n" \ 621 " sbrk calls %'u; storage %'llu bytes\n" \ 622 " mmap calls %'u; storage %'llu / %'llu bytes\n" \ 623 " munmap calls %'u; storage %'llu / %'llu bytes\n" \ 624 " threads started %'lu; exited %'lu\n" \ 625 " heaps new %'lu; reused %'lu\n" 326 626 327 627 // Use "write" because streams may be shutdown when calls are made. 328 static int printStats( ) {// see malloc_stats628 static int printStats( HeapStatistics & stats ) with( heapMaster, stats ) { // see malloc_stats 329 629 char helpText[sizeof(prtFmt) + 1024]; // space for message and values 330 return __cfaabi_bits_print_buffer( STDERR_FILENO, helpText, sizeof(helpText), prtFmt, 331 stats.malloc_calls, stats.malloc_0_calls, stats.malloc_storage_request, stats.malloc_storage_alloc, 332 stats.aalloc_calls, stats.aalloc_0_calls, stats.aalloc_storage_request, stats.aalloc_storage_alloc, 333 stats.calloc_calls, stats.calloc_0_calls, stats.calloc_storage_request, stats.calloc_storage_alloc, 334 stats.memalign_calls, stats.memalign_0_calls, stats.memalign_storage_request, stats.memalign_storage_alloc, 335 stats.amemalign_calls, stats.amemalign_0_calls, stats.amemalign_storage_request, stats.amemalign_storage_alloc, 336 stats.cmemalign_calls, stats.cmemalign_0_calls, stats.cmemalign_storage_request, stats.cmemalign_storage_alloc, 337 stats.resize_calls, stats.resize_0_calls, stats.resize_storage_request, stats.resize_storage_alloc, 338 stats.realloc_calls, stats.realloc_0_calls, stats.realloc_storage_request, stats.realloc_storage_alloc, 339 stats.free_calls, stats.free_null_calls, stats.free_storage_request, stats.free_storage_alloc, 630 return __cfaabi_bits_print_buffer( stats_fd, helpText, sizeof(helpText), prtFmt, 631 malloc_calls, malloc_0_calls, malloc_storage_request, malloc_storage_alloc, 632 aalloc_calls, aalloc_0_calls, aalloc_storage_request, aalloc_storage_alloc, 633 calloc_calls, calloc_0_calls, calloc_storage_request, calloc_storage_alloc, 634 memalign_calls, memalign_0_calls, memalign_storage_request, memalign_storage_alloc, 635 amemalign_calls, amemalign_0_calls, amemalign_storage_request, amemalign_storage_alloc, 636 cmemalign_calls, cmemalign_0_calls, cmemalign_storage_request, cmemalign_storage_alloc, 637 resize_calls, resize_0_calls, resize_storage_request, resize_storage_alloc, 638 realloc_calls, realloc_0_calls, realloc_storage_request, realloc_storage_alloc, 639 free_calls, free_null_calls, free_storage_request, free_storage_alloc, 640 return_pulls, return_pushes, return_storage_request, return_storage_alloc, 340 641 sbrk_calls, sbrk_storage, 341 stats.mmap_calls, stats.mmap_storage_request, stats.mmap_storage_alloc, 342 stats.munmap_calls, stats.munmap_storage_request, stats.munmap_storage_alloc 642 mmap_calls, mmap_storage_request, mmap_storage_alloc, 643 munmap_calls, munmap_storage_request, munmap_storage_alloc, 644 threads_started, threads_exited, 645 new_heap, reused_heap 343 646 ); 344 647 } // printStats … … 358 661 "<total type=\"realloc\" >0 count=\"%'u;\" 0 count=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \ 359 662 "<total type=\"free\" !null=\"%'u;\" 0 null=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \ 663 "<total type=\"return\" pulls=\"%'u;\" 0 pushes=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \ 360 664 "<total type=\"sbrk\" count=\"%'u;\" size=\"%'llu\"/> bytes\n" \ 361 665 "<total type=\"mmap\" count=\"%'u;\" size=\"%'llu / %'llu\" / > bytes\n" \ 362 666 "<total type=\"munmap\" count=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \ 667 "<total type=\"threads\" started=\"%'lu;\" exited=\"%'lu\"/>\n" \ 668 "<total type=\"heaps\" new=\"%'lu;\" reused=\"%'lu\"/>\n" \ 363 669 "</malloc>" 364 670 365 static int printStatsXML( FILE * stream ) {// see malloc_info671 static int printStatsXML( HeapStatistics & stats, FILE * stream ) with( heapMaster, stats ) { // see malloc_info 366 672 char helpText[sizeof(prtFmtXML) + 1024]; // space for message and values 367 673 return __cfaabi_bits_print_buffer( fileno( stream ), helpText, sizeof(helpText), prtFmtXML, 368 stats.malloc_calls, stats.malloc_0_calls, stats.malloc_storage_request, stats.malloc_storage_alloc, 369 stats.aalloc_calls, stats.aalloc_0_calls, stats.aalloc_storage_request, stats.aalloc_storage_alloc, 370 stats.calloc_calls, stats.calloc_0_calls, stats.calloc_storage_request, stats.calloc_storage_alloc, 371 stats.memalign_calls, stats.memalign_0_calls, stats.memalign_storage_request, stats.memalign_storage_alloc, 372 stats.amemalign_calls, stats.amemalign_0_calls, stats.amemalign_storage_request, stats.amemalign_storage_alloc, 373 stats.cmemalign_calls, stats.cmemalign_0_calls, stats.cmemalign_storage_request, stats.cmemalign_storage_alloc, 374 stats.resize_calls, stats.resize_0_calls, stats.resize_storage_request, stats.resize_storage_alloc, 375 stats.realloc_calls, stats.realloc_0_calls, stats.realloc_storage_request, stats.realloc_storage_alloc, 376 stats.free_calls, stats.free_null_calls, stats.free_storage_request, stats.free_storage_alloc, 674 malloc_calls, malloc_0_calls, malloc_storage_request, malloc_storage_alloc, 675 aalloc_calls, aalloc_0_calls, aalloc_storage_request, aalloc_storage_alloc, 676 calloc_calls, calloc_0_calls, calloc_storage_request, calloc_storage_alloc, 677 memalign_calls, memalign_0_calls, memalign_storage_request, memalign_storage_alloc, 678 amemalign_calls, amemalign_0_calls, amemalign_storage_request, amemalign_storage_alloc, 679 cmemalign_calls, cmemalign_0_calls, cmemalign_storage_request, cmemalign_storage_alloc, 680 resize_calls, resize_0_calls, resize_storage_request, resize_storage_alloc, 681 realloc_calls, realloc_0_calls, realloc_storage_request, realloc_storage_alloc, 682 free_calls, free_null_calls, free_storage_request, free_storage_alloc, 683 return_pulls, return_pushes, return_storage_request, return_storage_alloc, 377 684 sbrk_calls, sbrk_storage, 378 stats.mmap_calls, stats.mmap_storage_request, stats.mmap_storage_alloc, 379 stats.munmap_calls, stats.munmap_storage_request, stats.munmap_storage_alloc 685 mmap_calls, mmap_storage_request, mmap_storage_alloc, 686 munmap_calls, munmap_storage_request, munmap_storage_alloc, 687 threads_started, threads_exited, 688 new_heap, reused_heap 380 689 ); 381 690 } // printStatsXML 691 692 static HeapStatistics & collectStats( HeapStatistics & stats ) with( heapMaster ) { 693 lock( mgrLock ); 694 695 stats += heapMaster.stats; 696 for ( Heap * heap = heapManagersList; heap; heap = heap->nextHeapManager ) { 697 stats += heap->stats; 698 } // for 699 700 unlock( mgrLock ); 701 return stats; 702 } // collectStats 382 703 #endif // __STATISTICS__ 383 704 384 705 385 // statically allocated variables => zero filled. 386 static size_t heapExpand; // sbrk advance 387 static size_t mmapStart; // cross over point for mmap 388 static unsigned int maxBucketsUsed; // maximum number of buckets in use 389 // extern visibility, used by runtime kernel 390 // would be cool to remove libcfa_public but it's needed for libcfathread 391 libcfa_public size_t __page_size; // architecture pagesize 392 libcfa_public int __map_prot; // common mmap/mprotect protection 393 394 395 // thunk problem 396 size_t Bsearchl( unsigned int key, const unsigned int * vals, size_t dim ) { 397 size_t l = 0, m, h = dim; 398 while ( l < h ) { 399 m = (l + h) / 2; 400 if ( (unsigned int &)(vals[m]) < key ) { // cast away const 401 l = m + 1; 402 } else { 403 h = m; 404 } // if 405 } // while 406 return l; 407 } // Bsearchl 408 409 410 static inline bool setMmapStart( size_t value ) { // true => mmapped, false => sbrk 706 static bool setMmapStart( size_t value ) with( heapMaster ) { // true => mmapped, false => sbrk 411 707 if ( value < __page_size || bucketSizes[NoBucketSizes - 1] < value ) return false; 412 708 mmapStart = value; // set global 413 709 414 710 // find the closest bucket size less than or equal to the mmapStart size 415 maxBucketsUsed = Bsearchl( (unsigned int)mmapStart, bucketSizes, NoBucketSizes ); // binary search416 assert( maxBucketsUsed < NoBucketSizes ); // subscript failure ?417 assert( mmapStart <= bucketSizes[maxBucketsUsed] ); // search failure ?711 maxBucketsUsed = Bsearchl( mmapStart, bucketSizes, NoBucketSizes ); // binary search 712 verify( maxBucketsUsed < NoBucketSizes ); // subscript failure ? 713 verify( mmapStart <= bucketSizes[maxBucketsUsed] ); // search failure ? 418 714 return true; 419 715 } // setMmapStart … … 438 734 439 735 440 static inline void checkAlign( size_t alignment ) { 736 inline __attribute__((always_inline)) 737 static void checkAlign( size_t alignment ) { 441 738 if ( unlikely( alignment < libAlign() || ! is_pow2( alignment ) ) ) { 442 739 abort( "**** Error **** alignment %zu for memory allocation is less than %d and/or not a power of 2.", alignment, libAlign() ); … … 445 742 446 743 447 static inline void checkHeader( bool check, const char name[], void * addr ) { 744 inline __attribute__((always_inline)) 745 static void checkHeader( bool check, const char name[], void * addr ) { 448 746 if ( unlikely( check ) ) { // bad address ? 449 747 abort( "**** Error **** attempt to %s storage %p with address outside the heap.\n" … … 470 768 471 769 472 static inline void fakeHeader( Heap.Storage.Header *& header, size_t & alignment ) { 770 inline __attribute__((always_inline)) 771 static void fakeHeader( Heap.Storage.Header *& header, size_t & alignment ) { 473 772 if ( unlikely( AlignmentBit( header ) ) ) { // fake header ? 474 773 alignment = ClearAlignmentBit( header ); // clear flag from value … … 483 782 484 783 485 static inline bool headers( const char name[] __attribute__(( unused )), void * addr, Heap.Storage.Header *& header, 486 Heap.FreeHeader *& freeHead, size_t & size, size_t & alignment ) with( heapManager ) { 784 inline __attribute__((always_inline)) 785 static bool headers( const char name[] __attribute__(( unused )), void * addr, Heap.Storage.Header *& header, 786 Heap.FreeHeader *& freeHead, size_t & size, size_t & alignment ) with( heapMaster, *heapManager ) { 487 787 header = HeaderAddr( addr ); 488 788 … … 509 809 checkHeader( header < (Heap.Storage.Header *)heapBegin || (Heap.Storage.Header *)heapEnd < header, name, addr ); // bad address ? (offset could be + or -) 510 810 811 Heap * homeManager; 511 812 if ( unlikely( freeHead == 0p || // freed and only free-list node => null link 512 813 // freed and link points at another free block not to a bucket in the bucket array. 513 freeHead < &freeLists[0] || &freeLists[NoBucketSizes] <= freeHead ) ) { 814 (homeManager = freeHead->homeManager, freeHead < &homeManager->freeLists[0] || 815 &homeManager->freeLists[NoBucketSizes] <= freeHead ) ) ) { 514 816 abort( "**** Error **** attempt to %s storage %p with corrupted header.\n" 515 817 "Possible cause is duplicate free on same block or overwriting of header information.", … … 521 823 } // headers 522 824 523 // #ifdef __CFA_DEBUG__ 524 // #if __SIZEOF_POINTER__ == 4 525 // #define MASK 0xdeadbeef 526 // #else 527 // #define MASK 0xdeadbeefdeadbeef 528 // #endif 529 // #define STRIDE size_t 530 531 // static void * Memset( void * addr, STRIDE size ) { // debug only 532 // if ( size % sizeof(STRIDE) != 0 ) abort( "Memset() : internal error, size %zd not multiple of %zd.", size, sizeof(STRIDE) ); 533 // if ( (STRIDE)addr % sizeof(STRIDE) != 0 ) abort( "Memset() : internal error, addr %p not multiple of %zd.", addr, sizeof(STRIDE) ); 534 535 // STRIDE * end = (STRIDE *)addr + size / sizeof(STRIDE); 536 // for ( STRIDE * p = (STRIDE *)addr; p < end; p += 1 ) *p = MASK; 537 // return addr; 538 // } // Memset 539 // #endif // __CFA_DEBUG__ 540 541 542 #define NO_MEMORY_MSG "insufficient heap memory available for allocating %zd new bytes." 543 544 static inline void * extend( size_t size ) with( heapManager ) { 545 lock( extlock __cfaabi_dbg_ctx2 ); 825 826 static void * master_extend( size_t size ) with( heapMaster ) { 827 lock( extLock ); 546 828 547 829 ptrdiff_t rem = heapRemaining - size; … … 549 831 // If the size requested is bigger than the current remaining storage, increase the size of the heap. 550 832 551 size_t increase = ceiling2( size > heapExpand ? size : heapExpand, __page_size);833 size_t increase = ceiling2( size > heapExpand ? size : heapExpand, libAlign() ); 552 834 // Do not call abort or strerror( errno ) as they may call malloc. 553 if ( sbrk( increase ) == (void *)-1 ) { // failed, no memory ? 554 unlock( extlock ); 555 __cfaabi_bits_print_nolock( STDERR_FILENO, NO_MEMORY_MSG, size ); 556 _exit( EXIT_FAILURE ); // give up 835 if ( unlikely( sbrk( increase ) == (void *)-1 ) ) { // failed, no memory ? 836 unlock( extLock ); 837 abort( NO_MEMORY_MSG, size ); // no memory 557 838 } // if 558 839 559 840 // Make storage executable for thunks. 560 841 if ( mprotect( (char *)heapEnd + heapRemaining, increase, __map_prot ) ) { 561 unlock( extlock ); 562 __cfaabi_bits_print_nolock( STDERR_FILENO, "extend() : internal error, mprotect failure, heapEnd:%p size:%zd, errno:%d.\n", heapEnd, increase, errno ); 563 _exit( EXIT_FAILURE ); 564 } // if 842 unlock( extLock ); 843 abort( "**** Error **** attempt to make heap storage executable for thunks and mprotect failed with errno %d.", errno ); 844 } // if 845 846 rem = heapRemaining + increase - size; 565 847 566 848 #ifdef __STATISTICS__ … … 568 850 sbrk_storage += increase; 569 851 #endif // __STATISTICS__ 570 571 #ifdef __CFA_DEBUG__572 // Set new memory to garbage so subsequent uninitialized usages might fail.573 memset( (char *)heapEnd + heapRemaining, '\xde', increase );574 //Memset( (char *)heapEnd + heapRemaining, increase );575 #endif // __CFA_DEBUG__576 577 rem = heapRemaining + increase - size;578 852 } // if 579 853 … … 581 855 heapRemaining = rem; 582 856 heapEnd = (char *)heapEnd + size; 583 unlock( extlock ); 857 858 unlock( extLock ); 584 859 return block; 585 } // extend 586 587 588 static inline void * doMalloc( size_t size ) with( heapManager ) { 589 Heap.Storage * block; // pointer to new block of storage 860 } // master_extend 861 862 863 __attribute__(( noinline )) 864 static void * manager_extend( size_t size ) with( *heapManager ) { 865 ptrdiff_t rem = heapReserve - size; 866 867 if ( unlikely( rem < 0 ) ) { // negative 868 // If the size requested is bigger than the current remaining reserve, use the current reserve to populate 869 // smaller freeLists, and increase the reserve. 870 871 rem = heapReserve; // positive 872 873 if ( rem >= bucketSizes[0] ) { // minimal size ? otherwise ignore 874 size_t bucket; 875 #ifdef FASTLOOKUP 876 if ( likely( rem < LookupSizes ) ) bucket = lookup[rem]; 877 #endif // FASTLOOKUP 878 bucket = Bsearchl( rem, bucketSizes, heapMaster.maxBucketsUsed ); 879 verify( 0 <= bucket && bucket <= heapMaster.maxBucketsUsed ); 880 Heap.FreeHeader * freeHead = &(freeLists[bucket]); 881 882 // The remaining storage many not be bucket size, whereas all other allocations are. Round down to previous 883 // bucket size in this case. 884 if ( unlikely( freeHead->blockSize > (size_t)rem ) ) freeHead -= 1; 885 Heap.Storage * block = (Heap.Storage *)heapBuffer; 886 887 block->header.kind.real.next = freeHead->freeList; // push on stack 888 freeHead->freeList = block; 889 } // if 890 891 size_t increase = ceiling( size > ( heapMaster.heapExpand / 10 ) ? size : ( heapMaster.heapExpand / 10 ), libAlign() ); 892 heapBuffer = master_extend( increase ); 893 rem = increase - size; 894 } // if 895 896 Heap.Storage * block = (Heap.Storage *)heapBuffer; 897 heapReserve = rem; 898 heapBuffer = (char *)heapBuffer + size; 899 900 return block; 901 } // manager_extend 902 903 904 #define BOOT_HEAP_MANAGER \ 905 if ( unlikely( ! heapMasterBootFlag ) ) { \ 906 heapManagerCtor(); /* trigger for first heap */ \ 907 } /* if */ 908 909 #ifdef __STATISTICS__ 910 #define STAT_NAME __counter 911 #define STAT_PARM , unsigned int STAT_NAME 912 #define STAT_ARG( name ) , name 913 #define STAT_0_CNT( counter ) stats.counters[counter].calls_0 += 1 914 #else 915 #define STAT_NAME 916 #define STAT_PARM 917 #define STAT_ARG( name ) 918 #define STAT_0_CNT( counter ) 919 #endif // __STATISTICS__ 920 921 #define PROLOG( counter, ... ) \ 922 BOOT_HEAP_MANAGER; \ 923 if ( unlikely( size == 0 ) || /* 0 BYTE ALLOCATION RETURNS NULL POINTER */ \ 924 unlikely( size > ULONG_MAX - sizeof(Heap.Storage) ) ) { /* error check */ \ 925 STAT_0_CNT( counter ); \ 926 __VA_ARGS__; \ 927 return 0p; \ 928 } /* if */ 929 930 931 #define SCRUB_SIZE 1024lu 932 // Do not use '\xfe' for scrubbing because dereferencing an address composed of it causes a SIGSEGV *without* a valid IP 933 // pointer in the interrupt frame. 934 #define SCRUB '\xff' 935 936 static void * doMalloc( size_t size STAT_PARM ) libcfa_nopreempt with( *heapManager ) { 937 PROLOG( STAT_NAME ); 938 939 verify( heapManager ); 940 Heap.Storage * block; // pointer to new block of storage 590 941 591 942 // Look up size in the size list. Make sure the user request includes space for the header that must be allocated 592 943 // along with the block and is a multiple of the alignment size. 593 594 944 size_t tsize = size + sizeof(Heap.Storage); 595 945 596 if ( likely( tsize < mmapStart ) ) { // small size => sbrk 597 size_t posn; 946 #ifdef __STATISTICS__ 947 stats.counters[STAT_NAME].calls += 1; 948 stats.counters[STAT_NAME].request += size; 949 #endif // __STATISTICS__ 950 951 #ifdef __CFA_DEBUG__ 952 allocUnfreed += size; 953 #endif // __CFA_DEBUG__ 954 955 if ( likely( tsize < heapMaster.mmapStart ) ) { // small size => sbrk 956 size_t bucket; 598 957 #ifdef FASTLOOKUP 599 if ( tsize < LookupSizes ) posn= lookup[tsize];958 if ( likely( tsize < LookupSizes ) ) bucket = lookup[tsize]; 600 959 else 601 960 #endif // FASTLOOKUP 602 posn = Bsearchl( (unsigned int)tsize, bucketSizes, (size_t)maxBucketsUsed ); 603 Heap.FreeHeader * freeElem = &freeLists[posn]; 604 verify( freeElem <= &freeLists[maxBucketsUsed] ); // subscripting error ? 605 verify( tsize <= freeElem->blockSize ); // search failure ? 606 tsize = freeElem->blockSize; // total space needed for request 961 bucket = Bsearchl( tsize, bucketSizes, heapMaster.maxBucketsUsed ); 962 verify( 0 <= bucket && bucket <= heapMaster.maxBucketsUsed ); 963 Heap.FreeHeader * freeHead = &freeLists[bucket]; 964 965 verify( freeHead <= &freeLists[heapMaster.maxBucketsUsed] ); // subscripting error ? 966 verify( tsize <= freeHead->blockSize ); // search failure ? 967 968 tsize = freeHead->blockSize; // total space needed for request 969 #ifdef __STATISTICS__ 970 stats.counters[STAT_NAME].alloc += tsize; 971 #endif // __STATISTICS__ 607 972 608 973 // Spin until the lock is acquired for this particular size of block. 609 974 610 975 #if BUCKETLOCK == SPINLOCK 611 lock( freeElem->lock __cfaabi_dbg_ctx2 ); 612 block = freeElem->freeList; // remove node from stack 976 block = freeHead->freeList; // remove node from stack 613 977 #else 614 block = pop( free Elem->freeList );978 block = pop( freeHead->freeList ); 615 979 #endif // BUCKETLOCK 616 980 if ( unlikely( block == 0p ) ) { // no free block ? 981 #ifdef OWNERSHIP 982 // Freelist for that size is empty, so carve it out of the heap, if there is enough left, or get some more 983 // and then carve it off. 984 #ifdef RETURNSPIN 617 985 #if BUCKETLOCK == SPINLOCK 618 unlock( freeElem->lock ); 986 lock( freeHead->returnLock ); 987 block = freeHead->returnList; 988 freeHead->returnList = 0p; 989 unlock( freeHead->returnLock ); 990 #else 991 block = __atomic_exchange_n( &freeHead->returnList, nullptr, __ATOMIC_SEQ_CST ); 992 #endif // RETURNSPIN 993 994 if ( likely( block == 0p ) ) { // return list also empty? 995 #endif // OWNERSHIP 996 // Do not leave kernel thread as manager_extend accesses heapManager. 997 disable_interrupts(); 998 block = (Heap.Storage *)manager_extend( tsize ); // mutual exclusion on call 999 enable_interrupts( false ); 1000 1001 // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED. 1002 1003 #ifdef __CFA_DEBUG__ 1004 // Scrub new memory so subsequent uninitialized usages might fail. Only scrub the first 1024 bytes. 1005 memset( block->data, SCRUB, min( SCRUB_SIZE, tsize - sizeof(Heap.Storage) ) ); 1006 #endif // __CFA_DEBUG__ 619 1007 #endif // BUCKETLOCK 620 621 // Freelist for that size was empty, so carve it out of the heap if there's enough left, or get some more 622 // and then carve it off. 623 624 block = (Heap.Storage *)extend( tsize ); // mutual exclusion on call 625 #if BUCKETLOCK == SPINLOCK 1008 #ifdef OWNERSHIP 1009 } else { // merge returnList into freeHead 1010 #ifdef __STATISTICS__ 1011 stats.return_pulls += 1; 1012 #endif // __STATISTICS__ 1013 1014 // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED. 1015 1016 freeHead->freeList = block->header.kind.real.next; 1017 } // if 1018 #endif // OWNERSHIP 626 1019 } else { 627 freeElem->freeList = block->header.kind.real.next; 628 unlock( freeElem->lock ); 629 #endif // BUCKETLOCK 630 } // if 631 632 block->header.kind.real.home = freeElem; // pointer back to free list of apropriate size 1020 // Memory is scrubbed in doFree. 1021 freeHead->freeList = block->header.kind.real.next; 1022 } // if 1023 1024 block->header.kind.real.home = freeHead; // pointer back to free list of apropriate size 633 1025 } else { // large size => mmap 634 1026 if ( unlikely( size > ULONG_MAX - __page_size ) ) return 0p; 635 1027 tsize = ceiling2( tsize, __page_size ); // must be multiple of page size 636 1028 #ifdef __STATISTICS__ 637 __atomic_add_fetch( &stats.mmap_calls, 1, __ATOMIC_SEQ_CST ); 638 __atomic_add_fetch( &stats.mmap_storage_request, size, __ATOMIC_SEQ_CST ); 639 __atomic_add_fetch( &stats.mmap_storage_alloc, tsize, __ATOMIC_SEQ_CST ); 1029 stats.counters[STAT_NAME].alloc += tsize; 1030 stats.mmap_calls += 1; 1031 stats.mmap_storage_request += size; 1032 stats.mmap_storage_alloc += tsize; 640 1033 #endif // __STATISTICS__ 641 1034 642 block = (Heap.Storage *)mmap( 0, tsize, __map_prot, MAP_PRIVATE | MAP_ANONYMOUS, mmapFd, 0 ); 643 if ( block == (Heap.Storage *)MAP_FAILED ) { // failed ? 1035 disable_interrupts(); 1036 block = (Heap.Storage *)mmap( 0, tsize, __map_prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 ); 1037 enable_interrupts( false ); 1038 1039 // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED. 1040 1041 if ( unlikely( block == (Heap.Storage *)MAP_FAILED ) ) { // failed ? 644 1042 if ( errno == ENOMEM ) abort( NO_MEMORY_MSG, tsize ); // no memory 645 1043 // Do not call strerror( errno ) as it may call malloc. 646 abort( "(Heap &)0x%p.doMalloc() : internal error, mmap failure, size:%zu errno:%d.", &heapManager, tsize, errno ); 647 } //if 1044 abort( "**** Error **** attempt to allocate large object (> %zu) of size %zu bytes and mmap failed with errno %d.", size, heapMaster.mmapStart, errno ); 1045 } // if 1046 block->header.kind.real.blockSize = MarkMmappedBit( tsize ); // storage size for munmap 1047 648 1048 #ifdef __CFA_DEBUG__ 649 // S et new memory to garbage so subsequent uninitialized usages might fail.650 memset( block, '\xde', tsize );651 //Memset( block, tsize);1049 // Scrub new memory so subsequent uninitialized usages might fail. Only scrub the first 1024 bytes. The rest of 1050 // the storage set to 0 by mmap. 1051 memset( block->data, SCRUB, min( SCRUB_SIZE, tsize - sizeof(Heap.Storage) ) ); 652 1052 #endif // __CFA_DEBUG__ 653 block->header.kind.real.blockSize = MarkMmappedBit( tsize ); // storage size for munmap654 1053 } // if 655 1054 … … 659 1058 660 1059 #ifdef __CFA_DEBUG__ 661 __atomic_add_fetch( &allocUnfreed, tsize, __ATOMIC_SEQ_CST );662 1060 if ( traceHeap() ) { 663 1061 char helpText[64]; … … 667 1065 #endif // __CFA_DEBUG__ 668 1066 1067 // poll_interrupts(); // call rollforward 1068 669 1069 return addr; 670 1070 } // doMalloc 671 1071 672 1072 673 static inline void doFree( void * addr ) with( heapManager ) { 1073 static void doFree( void * addr ) libcfa_nopreempt with( *heapManager ) { 1074 verify( addr ); 1075 1076 // detect free after thread-local storage destruction and use global stats in that case 1077 1078 Heap.Storage.Header * header; 1079 Heap.FreeHeader * freeHead; 1080 size_t size, alignment; 1081 1082 bool mapped = headers( "free", addr, header, freeHead, size, alignment ); 1083 #if defined( __STATISTICS__ ) || defined( __CFA_DEBUG__ ) 1084 size_t rsize = header->kind.real.size; // optimization 1085 #endif // __STATISTICS__ || __CFA_DEBUG__ 1086 1087 #ifdef __STATISTICS__ 1088 stats.free_storage_request += rsize; 1089 stats.free_storage_alloc += size; 1090 #endif // __STATISTICS__ 1091 674 1092 #ifdef __CFA_DEBUG__ 675 if ( unlikely( heapManager.heapBegin == 0p ) ) { 676 abort( "doFree( %p ) : internal error, called before heap is initialized.", addr ); 677 } // if 1093 allocUnfreed -= rsize; 678 1094 #endif // __CFA_DEBUG__ 679 1095 680 Heap.Storage.Header * header; 681 Heap.FreeHeader * freeElem; 682 size_t size, alignment; // not used (see realloc) 683 684 if ( headers( "free", addr, header, freeElem, size, alignment ) ) { // mmapped ? 1096 if ( unlikely( mapped ) ) { // mmapped ? 685 1097 #ifdef __STATISTICS__ 686 __atomic_add_fetch( &stats.munmap_calls, 1, __ATOMIC_SEQ_CST );687 __atomic_add_fetch( &stats.munmap_storage_request, header->kind.real.size, __ATOMIC_SEQ_CST );688 __atomic_add_fetch( &stats.munmap_storage_alloc, size, __ATOMIC_SEQ_CST );1098 stats.munmap_calls += 1; 1099 stats.munmap_storage_request += rsize; 1100 stats.munmap_storage_alloc += size; 689 1101 #endif // __STATISTICS__ 690 if ( munmap( header, size ) == -1 ) { 691 abort( "Attempt to deallocate storage %p not allocated or with corrupt header.\n" 692 "Possible cause is invalid pointer.", 693 addr ); 1102 1103 // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED. 1104 1105 // Does not matter where this storage is freed. 1106 if ( unlikely( munmap( header, size ) == -1 ) ) { 1107 // Do not call strerror( errno ) as it may call malloc. 1108 abort( "**** Error **** attempt to deallocate large object %p and munmap failed with errno %d.\n" 1109 "Possible cause is invalid delete pointer: either not allocated or with corrupt header.", 1110 addr, errno ); 694 1111 } // if 695 1112 } else { 696 1113 #ifdef __CFA_DEBUG__ 697 // Set free memory to garbage so subsequent usages might fail. 698 memset( ((Heap.Storage *)header)->data, '\xde', freeElem->blockSize - sizeof( Heap.Storage ) ); 699 //Memset( ((Heap.Storage *)header)->data, freeElem->blockSize - sizeof( Heap.Storage ) ); 1114 // memset is NOT always inlined! 1115 disable_interrupts(); 1116 // Scrub old memory so subsequent usages might fail. Only scrub the first/last SCRUB_SIZE bytes. 1117 char * data = ((Heap.Storage *)header)->data; // data address 1118 size_t dsize = size - sizeof(Heap.Storage); // data size 1119 if ( dsize <= SCRUB_SIZE * 2 ) { 1120 memset( data, SCRUB, dsize ); // scrub all 1121 } else { 1122 memset( data, SCRUB, SCRUB_SIZE ); // scrub front 1123 memset( data + dsize - SCRUB_SIZE, SCRUB, SCRUB_SIZE ); // scrub back 1124 } // if 1125 enable_interrupts( false ); 700 1126 #endif // __CFA_DEBUG__ 701 1127 702 #ifdef __STATISTICS__ 703 __atomic_add_fetch( &stats.free_calls, 1, __ATOMIC_SEQ_CST ); 704 __atomic_add_fetch( &stats.free_storage_request, header->kind.real.size, __ATOMIC_SEQ_CST ); 705 __atomic_add_fetch( &stats.free_storage_alloc, size, __ATOMIC_SEQ_CST ); 706 #endif // __STATISTICS__ 707 708 #if BUCKETLOCK == SPINLOCK 709 lock( freeElem->lock __cfaabi_dbg_ctx2 ); // acquire spin lock 710 header->kind.real.next = freeElem->freeList; // push on stack 711 freeElem->freeList = (Heap.Storage *)header; 712 unlock( freeElem->lock ); // release spin lock 713 #else 714 push( freeElem->freeList, *(Heap.Storage *)header ); 715 #endif // BUCKETLOCK 1128 if ( likely( heapManager == freeHead->homeManager ) ) { // belongs to this thread 1129 header->kind.real.next = freeHead->freeList; // push on stack 1130 freeHead->freeList = (Heap.Storage *)header; 1131 } else { // return to thread owner 1132 verify( heapManager ); 1133 1134 #ifdef OWNERSHIP 1135 #ifdef RETURNSPIN 1136 lock( freeHead->returnLock ); 1137 header->kind.real.next = freeHead->returnList; // push to bucket return list 1138 freeHead->returnList = (Heap.Storage *)header; 1139 unlock( freeHead->returnLock ); 1140 #else // lock free 1141 header->kind.real.next = freeHead->returnList; // link new node to top node 1142 // CAS resets header->kind.real.next = freeHead->returnList on failure 1143 while ( ! __atomic_compare_exchange_n( &freeHead->returnList, &header->kind.real.next, header, 1144 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) ); 1145 #endif // RETURNSPIN 1146 1147 #else // no OWNERSHIP 1148 1149 freeHead = &heap->freeLists[ClearStickyBits( header->kind.real.home ) - &freeHead->homeManager->freeLists[0]]; 1150 header->kind.real.next = freeHead->freeList; // push on stack 1151 freeHead->freeList = (Heap.Storage *)header; 1152 #endif // ! OWNERSHIP 1153 1154 #ifdef __U_STATISTICS__ 1155 stats.return_pushes += 1; 1156 stats.return_storage_request += rsize; 1157 stats.return_storage_alloc += size; 1158 #endif // __U_STATISTICS__ 1159 1160 // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED. 1161 } // if 716 1162 } // if 717 1163 718 1164 #ifdef __CFA_DEBUG__ 719 __atomic_add_fetch( &allocUnfreed, -size, __ATOMIC_SEQ_CST );720 1165 if ( traceHeap() ) { 721 1166 char helpText[64]; … … 724 1169 } // if 725 1170 #endif // __CFA_DEBUG__ 1171 1172 // poll_interrupts(); // call rollforward 726 1173 } // doFree 727 1174 728 1175 729 s tatic size_t prtFree( Heap & manager ) with( manager ) {1176 size_t prtFree( Heap & manager ) with( manager ) { 730 1177 size_t total = 0; 731 1178 #ifdef __STATISTICS__ … … 733 1180 __cfaabi_bits_print_nolock( STDERR_FILENO, "\nBin lists (bin size : free blocks on list)\n" ); 734 1181 #endif // __STATISTICS__ 735 for ( unsigned int i = 0; i < maxBucketsUsed; i += 1 ) {1182 for ( unsigned int i = 0; i < heapMaster.maxBucketsUsed; i += 1 ) { 736 1183 size_t size = freeLists[i].blockSize; 737 1184 #ifdef __STATISTICS__ … … 764 1211 __cfaabi_bits_release(); 765 1212 #endif // __STATISTICS__ 766 return (char *)heap End - (char *)heapBegin - total;1213 return (char *)heapMaster.heapEnd - (char *)heapMaster.heapBegin - total; 767 1214 } // prtFree 768 1215 769 1216 770 static void ?{}( Heap & manager ) with( manager ) { 771 __page_size = sysconf( _SC_PAGESIZE ); 772 __map_prot = PROT_READ | PROT_WRITE | PROT_EXEC; 773 774 for ( unsigned int i = 0; i < NoBucketSizes; i += 1 ) { // initialize the free lists 775 freeLists[i].blockSize = bucketSizes[i]; 776 } // for 777 778 #ifdef FASTLOOKUP 779 unsigned int idx = 0; 780 for ( unsigned int i = 0; i < LookupSizes; i += 1 ) { 781 if ( i > bucketSizes[idx] ) idx += 1; 782 lookup[i] = idx; 783 } // for 784 #endif // FASTLOOKUP 785 786 if ( ! setMmapStart( malloc_mmap_start() ) ) { 787 abort( "Heap : internal error, mmap start initialization failure." ); 788 } // if 789 heapExpand = malloc_expansion(); 790 791 char * end = (char *)sbrk( 0 ); 792 heapBegin = heapEnd = sbrk( (char *)ceiling2( (long unsigned int)end, __page_size ) - end ); // move start of heap to multiple of alignment 793 } // Heap 794 795 796 static void ^?{}( Heap & ) { 797 #ifdef __STATISTICS__ 798 if ( traceHeapTerm() ) { 799 printStats(); 800 // prtUnfreed() called in heapAppStop() 801 } // if 802 #endif // __STATISTICS__ 803 } // ~Heap 804 805 806 static void memory_startup( void ) __attribute__(( constructor( STARTUP_PRIORITY_MEMORY ) )); 807 void memory_startup( void ) { 808 #ifdef __CFA_DEBUG__ 809 if ( heapBoot ) { // check for recursion during system boot 810 abort( "boot() : internal error, recursively invoked during system boot." ); 811 } // if 812 heapBoot = true; 813 #endif // __CFA_DEBUG__ 814 815 //verify( heapManager.heapBegin != 0 ); 816 //heapManager{}; 817 if ( heapManager.heapBegin == 0p ) heapManager{}; // sanity check 818 } // memory_startup 819 820 static void memory_shutdown( void ) __attribute__(( destructor( STARTUP_PRIORITY_MEMORY ) )); 821 void memory_shutdown( void ) { 822 ^heapManager{}; 823 } // memory_shutdown 824 825 826 static inline void * mallocNoStats( size_t size ) { // necessary for malloc statistics 827 verify( heapManager.heapBegin != 0p ); // called before memory_startup ? 828 if ( unlikely( size ) == 0 ) return 0p; // 0 BYTE ALLOCATION RETURNS NULL POINTER 829 830 #if __SIZEOF_POINTER__ == 8 831 verify( size < ((typeof(size_t))1 << 48) ); 832 #endif // __SIZEOF_POINTER__ == 8 833 return doMalloc( size ); 834 } // mallocNoStats 835 836 837 static inline void * memalignNoStats( size_t alignment, size_t size ) { 838 if ( unlikely( size ) == 0 ) return 0p; // 0 BYTE ALLOCATION RETURNS NULL POINTER 839 840 #ifdef __CFA_DEBUG__ 1217 #ifdef __STATISTICS__ 1218 static void incCalls( intptr_t statName ) libcfa_nopreempt { 1219 heapManager->stats.counters[statName].calls += 1; 1220 } // incCalls 1221 1222 static void incZeroCalls( intptr_t statName ) libcfa_nopreempt { 1223 heapManager->stats.counters[statName].calls_0 += 1; 1224 } // incZeroCalls 1225 #endif // __STATISTICS__ 1226 1227 #ifdef __CFA_DEBUG__ 1228 static void incUnfreed( intptr_t offset ) libcfa_nopreempt { 1229 heapManager->allocUnfreed += offset; 1230 } // incUnfreed 1231 #endif // __CFA_DEBUG__ 1232 1233 1234 static void * memalignNoStats( size_t alignment, size_t size STAT_PARM ) { 841 1235 checkAlign( alignment ); // check alignment 842 #endif // __CFA_DEBUG__ 843 844 // if alignment <= default alignment, do normal malloc as two headers are unnecessary 845 if ( unlikely( alignment <= libAlign() ) ) return mallocNoStats( size ); 1236 1237 // if alignment <= default alignment or size == 0, do normal malloc as two headers are unnecessary 1238 if ( unlikely( alignment <= libAlign() || size == 0 ) ) return doMalloc( size STAT_ARG( STAT_NAME ) ); 846 1239 847 1240 // Allocate enough storage to guarantee an address on the alignment boundary, and sufficient space before it for … … 854 1247 // subtract libAlign() because it is already the minimum alignment 855 1248 // add sizeof(Storage) for fake header 856 char * addr = (char *)mallocNoStats( size + alignment - libAlign() + sizeof(Heap.Storage) ); 1249 size_t offset = alignment - libAlign() + sizeof(Heap.Storage); 1250 char * addr = (char *)doMalloc( size + offset STAT_ARG( STAT_NAME ) ); 857 1251 858 1252 // address in the block of the "next" alignment address … … 860 1254 861 1255 // address of header from malloc 862 Heap.Storage.Header * RealHeader = HeaderAddr( addr ); 863 RealHeader->kind.real.size = size; // correct size to eliminate above alignment offset 864 // address of fake header * before* the alignment location 1256 Heap.Storage.Header * realHeader = HeaderAddr( addr ); 1257 realHeader->kind.real.size = size; // correct size to eliminate above alignment offset 1258 #ifdef __CFA_DEBUG__ 1259 incUnfreed( -offset ); // adjustment off the offset from call to doMalloc 1260 #endif // __CFA_DEBUG__ 1261 1262 // address of fake header *before* the alignment location 865 1263 Heap.Storage.Header * fakeHeader = HeaderAddr( user ); 1264 866 1265 // SKULLDUGGERY: insert the offset to the start of the actual storage block and remember alignment 867 fakeHeader->kind.fake.offset = (char *)fakeHeader - (char *) RealHeader;1266 fakeHeader->kind.fake.offset = (char *)fakeHeader - (char *)realHeader; 868 1267 // SKULLDUGGERY: odd alignment implies fake header 869 1268 fakeHeader->kind.fake.alignment = MarkAlignmentBit( alignment ); … … 880 1279 // then malloc() returns a unique pointer value that can later be successfully passed to free(). 881 1280 void * malloc( size_t size ) libcfa_public { 882 #ifdef __STATISTICS__ 883 if ( likely( size > 0 ) ) { 884 __atomic_add_fetch( &stats.malloc_calls, 1, __ATOMIC_SEQ_CST ); 885 __atomic_add_fetch( &stats.malloc_storage_request, size, __ATOMIC_SEQ_CST ); 886 } else { 887 __atomic_add_fetch( &stats.malloc_0_calls, 1, __ATOMIC_SEQ_CST ); 888 } // if 889 #endif // __STATISTICS__ 890 891 return mallocNoStats( size ); 1281 return doMalloc( size STAT_ARG( MALLOC ) ); 892 1282 } // malloc 893 1283 … … 895 1285 // Same as malloc() except size bytes is an array of dim elements each of elemSize bytes. 896 1286 void * aalloc( size_t dim, size_t elemSize ) libcfa_public { 897 size_t size = dim * elemSize; 898 #ifdef __STATISTICS__ 899 if ( likely( size > 0 ) ) { 900 __atomic_add_fetch( &stats.aalloc_calls, 1, __ATOMIC_SEQ_CST ); 901 __atomic_add_fetch( &stats.aalloc_storage_request, size, __ATOMIC_SEQ_CST ); 902 } else { 903 __atomic_add_fetch( &stats.aalloc_0_calls, 1, __ATOMIC_SEQ_CST ); 904 } // if 905 #endif // __STATISTICS__ 906 907 return mallocNoStats( size ); 1287 return doMalloc( dim * elemSize STAT_ARG( AALLOC ) ); 908 1288 } // aalloc 909 1289 … … 912 1292 void * calloc( size_t dim, size_t elemSize ) libcfa_public { 913 1293 size_t size = dim * elemSize; 914 if ( unlikely( size ) == 0 ) { // 0 BYTE ALLOCATION RETURNS NULL POINTER 915 #ifdef __STATISTICS__ 916 __atomic_add_fetch( &stats.calloc_0_calls, 1, __ATOMIC_SEQ_CST ); 917 #endif // __STATISTICS__ 918 return 0p; 919 } // if 920 #ifdef __STATISTICS__ 921 __atomic_add_fetch( &stats.calloc_calls, 1, __ATOMIC_SEQ_CST ); 922 __atomic_add_fetch( &stats.calloc_storage_request, dim * elemSize, __ATOMIC_SEQ_CST ); 923 #endif // __STATISTICS__ 924 925 char * addr = (char *)mallocNoStats( size ); 1294 char * addr = (char *)doMalloc( size STAT_ARG( CALLOC ) ); 1295 1296 if ( unlikely( addr == NULL ) ) return NULL; // stop further processing if 0p is returned 926 1297 927 1298 Heap.Storage.Header * header; 928 Heap.FreeHeader * free Elem;1299 Heap.FreeHeader * freeHead; 929 1300 size_t bsize, alignment; 930 1301 … … 932 1303 bool mapped = 933 1304 #endif // __CFA_DEBUG__ 934 headers( "calloc", addr, header, free Elem, bsize, alignment );1305 headers( "calloc", addr, header, freeHead, bsize, alignment ); 935 1306 936 1307 #ifndef __CFA_DEBUG__ 937 1308 // Mapped storage is zero filled, but in debug mode mapped memory is scrubbed in doMalloc, so it has to be reset to zero. 938 if ( ! mapped)1309 if ( likely( ! mapped ) ) 939 1310 #endif // __CFA_DEBUG__ 940 1311 // <-------0000000000000000000000000000UUUUUUUUUUUUUUUUUUUUUUUUU> bsize (bucket size) U => undefined … … 952 1323 // call to malloc(), alloc(), calloc() or realloc(). If the area pointed to was moved, a free(oaddr) is done. 953 1324 void * resize( void * oaddr, size_t size ) libcfa_public { 954 // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned. 955 if ( unlikely( size == 0 ) ) { // special cases 956 #ifdef __STATISTICS__ 957 __atomic_add_fetch( &stats.resize_0_calls, 1, __ATOMIC_SEQ_CST ); 958 #endif // __STATISTICS__ 959 free( oaddr ); 960 return 0p; 961 } // if 962 #ifdef __STATISTICS__ 963 __atomic_add_fetch( &stats.resize_calls, 1, __ATOMIC_SEQ_CST ); 964 #endif // __STATISTICS__ 965 966 if ( unlikely( oaddr == 0p ) ) { 967 #ifdef __STATISTICS__ 968 __atomic_add_fetch( &stats.resize_storage_request, size, __ATOMIC_SEQ_CST ); 969 #endif // __STATISTICS__ 970 return mallocNoStats( size ); 971 } // if 1325 if ( unlikely( oaddr == 0p ) ) { // => malloc( size ) 1326 return doMalloc( size STAT_ARG( RESIZE ) ); 1327 } // if 1328 1329 PROLOG( RESIZE, doFree( oaddr ) ); // => free( oaddr ) 972 1330 973 1331 Heap.Storage.Header * header; 974 Heap.FreeHeader * free Elem;1332 Heap.FreeHeader * freeHead; 975 1333 size_t bsize, oalign; 976 headers( "resize", oaddr, header, free Elem, bsize, oalign );1334 headers( "resize", oaddr, header, freeHead, bsize, oalign ); 977 1335 978 1336 size_t odsize = DataStorage( bsize, oaddr, header ); // data storage available in bucket … … 980 1338 if ( oalign == libAlign() && size <= odsize && odsize <= size * 2 ) { // allow 50% wasted storage for smaller size 981 1339 ClearZeroFillBit( header ); // no alignment and turn off 0 fill 1340 #ifdef __CFA_DEBUG__ 1341 incUnfreed( size - header->kind.real.size ); // adjustment off the size difference 1342 #endif // __CFA_DEBUG__ 982 1343 header->kind.real.size = size; // reset allocation size 1344 #ifdef __STATISTICS__ 1345 incCalls( RESIZE ); 1346 #endif // __STATISTICS__ 983 1347 return oaddr; 984 1348 } // if 985 1349 986 #ifdef __STATISTICS__987 __atomic_add_fetch( &stats.resize_storage_request, size, __ATOMIC_SEQ_CST );988 #endif // __STATISTICS__989 990 1350 // change size, DO NOT preserve STICKY PROPERTIES. 991 free( oaddr ); 992 return mallocNoStats( size ); // create new area 1351 doFree( oaddr ); // free previous storage 1352 1353 return doMalloc( size STAT_ARG( RESIZE ) ); // create new area 993 1354 } // resize 994 1355 … … 997 1358 // the old and new sizes. 998 1359 void * realloc( void * oaddr, size_t size ) libcfa_public { 999 // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned. 1000 if ( unlikely( size == 0 ) ) { // special cases 1001 #ifdef __STATISTICS__ 1002 __atomic_add_fetch( &stats.realloc_0_calls, 1, __ATOMIC_SEQ_CST ); 1003 #endif // __STATISTICS__ 1004 free( oaddr ); 1005 return 0p; 1006 } // if 1007 #ifdef __STATISTICS__ 1008 __atomic_add_fetch( &stats.realloc_calls, 1, __ATOMIC_SEQ_CST ); 1009 #endif // __STATISTICS__ 1010 1011 if ( unlikely( oaddr == 0p ) ) { 1012 #ifdef __STATISTICS__ 1013 __atomic_add_fetch( &stats.realloc_storage_request, size, __ATOMIC_SEQ_CST ); 1014 #endif // __STATISTICS__ 1015 return mallocNoStats( size ); 1016 } // if 1360 if ( unlikely( oaddr == 0p ) ) { // => malloc( size ) 1361 return doMalloc( size STAT_ARG( REALLOC ) ); 1362 } // if 1363 1364 PROLOG( REALLOC, doFree( oaddr ) ); // => free( oaddr ) 1017 1365 1018 1366 Heap.Storage.Header * header; 1019 Heap.FreeHeader * free Elem;1367 Heap.FreeHeader * freeHead; 1020 1368 size_t bsize, oalign; 1021 headers( "realloc", oaddr, header, free Elem, bsize, oalign );1369 headers( "realloc", oaddr, header, freeHead, bsize, oalign ); 1022 1370 1023 1371 size_t odsize = DataStorage( bsize, oaddr, header ); // data storage available in bucket … … 1025 1373 bool ozfill = ZeroFillBit( header ); // old allocation zero filled 1026 1374 if ( unlikely( size <= odsize ) && odsize <= size * 2 ) { // allow up to 50% wasted storage 1027 header->kind.real.size = size; // reset allocation size 1375 #ifdef __CFA_DEBUG__ 1376 incUnfreed( size - header->kind.real.size ); // adjustment off the size difference 1377 #endif // __CFA_DEBUG__ 1378 header->kind.real.size = size; // reset allocation size 1028 1379 if ( unlikely( ozfill ) && size > osize ) { // previous request zero fill and larger ? 1029 1380 memset( (char *)oaddr + osize, '\0', size - osize ); // initialize added storage 1030 1381 } // if 1382 #ifdef __STATISTICS__ 1383 incCalls( REALLOC ); 1384 #endif // __STATISTICS__ 1031 1385 return oaddr; 1032 1386 } // if 1033 1387 1034 #ifdef __STATISTICS__1035 __atomic_add_fetch( &stats.realloc_storage_request, size, __ATOMIC_SEQ_CST );1036 #endif // __STATISTICS__1037 1038 1388 // change size and copy old content to new storage 1039 1389 1040 1390 void * naddr; 1041 if ( likely( oalign == libAlign() ) ) { // previous request not aligned ?1042 naddr = mallocNoStats( size );// create new area1391 if ( likely( oalign <= libAlign() ) ) { // previous request not aligned ? 1392 naddr = doMalloc( size STAT_ARG( REALLOC ) ); // create new area 1043 1393 } else { 1044 naddr = memalignNoStats( oalign, size ); // create new aligned area 1045 } // if 1046 1047 headers( "realloc", naddr, header, freeElem, bsize, oalign ); 1394 naddr = memalignNoStats( oalign, size STAT_ARG( REALLOC ) ); // create new aligned area 1395 } // if 1396 1397 headers( "realloc", naddr, header, freeHead, bsize, oalign ); 1398 // To preserve prior fill, the entire bucket must be copied versus the size. 1048 1399 memcpy( naddr, oaddr, min( osize, size ) ); // copy bytes 1049 free( oaddr );1400 doFree( oaddr ); // free previous storage 1050 1401 1051 1402 if ( unlikely( ozfill ) ) { // previous request zero fill ? … … 1067 1418 // Same as malloc() except the memory address is a multiple of alignment, which must be a power of two. (obsolete) 1068 1419 void * memalign( size_t alignment, size_t size ) libcfa_public { 1069 #ifdef __STATISTICS__ 1070 if ( likely( size > 0 ) ) { 1071 __atomic_add_fetch( &stats.memalign_calls, 1, __ATOMIC_SEQ_CST ); 1072 __atomic_add_fetch( &stats.memalign_storage_request, size, __ATOMIC_SEQ_CST ); 1073 } else { 1074 __atomic_add_fetch( &stats.memalign_0_calls, 1, __ATOMIC_SEQ_CST ); 1075 } // if 1076 #endif // __STATISTICS__ 1077 1078 return memalignNoStats( alignment, size ); 1420 return memalignNoStats( alignment, size STAT_ARG( MEMALIGN ) ); 1079 1421 } // memalign 1080 1422 … … 1082 1424 // Same as aalloc() with memory alignment. 1083 1425 void * amemalign( size_t alignment, size_t dim, size_t elemSize ) libcfa_public { 1084 size_t size = dim * elemSize; 1085 #ifdef __STATISTICS__ 1086 if ( likely( size > 0 ) ) { 1087 __atomic_add_fetch( &stats.cmemalign_calls, 1, __ATOMIC_SEQ_CST ); 1088 __atomic_add_fetch( &stats.cmemalign_storage_request, size, __ATOMIC_SEQ_CST ); 1089 } else { 1090 __atomic_add_fetch( &stats.cmemalign_0_calls, 1, __ATOMIC_SEQ_CST ); 1091 } // if 1092 #endif // __STATISTICS__ 1093 1094 return memalignNoStats( alignment, size ); 1426 return memalignNoStats( alignment, dim * elemSize STAT_ARG( AMEMALIGN ) ); 1095 1427 } // amemalign 1096 1428 … … 1099 1431 void * cmemalign( size_t alignment, size_t dim, size_t elemSize ) libcfa_public { 1100 1432 size_t size = dim * elemSize; 1101 if ( unlikely( size ) == 0 ) { // 0 BYTE ALLOCATION RETURNS NULL POINTER 1102 #ifdef __STATISTICS__ 1103 __atomic_add_fetch( &stats.cmemalign_0_calls, 1, __ATOMIC_SEQ_CST ); 1104 #endif // __STATISTICS__ 1105 return 0p; 1106 } // if 1107 #ifdef __STATISTICS__ 1108 __atomic_add_fetch( &stats.cmemalign_calls, 1, __ATOMIC_SEQ_CST ); 1109 __atomic_add_fetch( &stats.cmemalign_storage_request, dim * elemSize, __ATOMIC_SEQ_CST ); 1110 #endif // __STATISTICS__ 1111 1112 char * addr = (char *)memalignNoStats( alignment, size ); 1433 char * addr = (char *)memalignNoStats( alignment, size STAT_ARG( CMEMALIGN ) ); 1434 1435 if ( unlikely( addr == NULL ) ) return NULL; // stop further processing if 0p is returned 1113 1436 1114 1437 Heap.Storage.Header * header; 1115 Heap.FreeHeader * free Elem;1438 Heap.FreeHeader * freeHead; 1116 1439 size_t bsize; 1117 1440 … … 1119 1442 bool mapped = 1120 1443 #endif // __CFA_DEBUG__ 1121 headers( "cmemalign", addr, header, free Elem, bsize, alignment );1444 headers( "cmemalign", addr, header, freeHead, bsize, alignment ); 1122 1445 1123 1446 // Mapped storage is zero filled, but in debug mode mapped memory is scrubbed in doMalloc, so it has to be reset to zero. … … 1169 1492 // 0p, no operation is performed. 1170 1493 void free( void * addr ) libcfa_public { 1494 // verify( heapManager ); 1495 1171 1496 if ( unlikely( addr == 0p ) ) { // special case 1172 1497 #ifdef __STATISTICS__ 1173 __atomic_add_fetch( &stats.free_null_calls, 1, __ATOMIC_SEQ_CST ); 1498 if ( heapManager ) 1499 incZeroCalls( FREE ); 1174 1500 #endif // __STATISTICS__ 1175 1176 // #ifdef __CFA_DEBUG__1177 // if ( traceHeap() ) {1178 // #define nullmsg "Free( 0x0 ) size:0\n"1179 // // Do not debug print free( 0p ), as it can cause recursive entry from sprintf.1180 // __cfaabi_dbg_write( nullmsg, sizeof(nullmsg) - 1 );1181 // } // if1182 // #endif // __CFA_DEBUG__1183 1501 return; 1184 } // exit 1185 1186 doFree( addr ); 1502 } // if 1503 1504 #ifdef __STATISTICS__ 1505 incCalls( FREE ); 1506 #endif // __STATISTICS__ 1507 1508 doFree( addr ); // handles heapManager == nullptr 1187 1509 } // free 1188 1510 … … 1227 1549 if ( unlikely( addr == 0p ) ) return 0; // null allocation has 0 size 1228 1550 Heap.Storage.Header * header; 1229 Heap.FreeHeader * free Elem;1551 Heap.FreeHeader * freeHead; 1230 1552 size_t bsize, alignment; 1231 1553 1232 headers( "malloc_usable_size", addr, header, free Elem, bsize, alignment );1554 headers( "malloc_usable_size", addr, header, freeHead, bsize, alignment ); 1233 1555 return DataStorage( bsize, addr, header ); // data storage in bucket 1234 1556 } // malloc_usable_size … … 1238 1560 void malloc_stats( void ) libcfa_public { 1239 1561 #ifdef __STATISTICS__ 1240 printStats(); 1241 if ( prtFree() ) prtFree( heapManager ); 1562 HeapStatistics stats; 1563 HeapStatisticsCtor( stats ); 1564 if ( printStats( collectStats( stats ) ) == -1 ) { 1565 #else 1566 #define MALLOC_STATS_MSG "malloc_stats statistics disabled.\n" 1567 if ( write( STDERR_FILENO, MALLOC_STATS_MSG, sizeof( MALLOC_STATS_MSG ) - 1 /* size includes '\0' */ ) == -1 ) { 1242 1568 #endif // __STATISTICS__ 1569 abort( "**** Error **** write failed in malloc_stats" ); 1570 } // if 1243 1571 } // malloc_stats 1244 1572 … … 1247 1575 int malloc_stats_fd( int fd __attribute__(( unused )) ) libcfa_public { 1248 1576 #ifdef __STATISTICS__ 1249 int temp = stats_fd;1250 stats_fd = fd;1577 int temp = heapMaster.stats_fd; 1578 heapMaster.stats_fd = fd; 1251 1579 return temp; 1252 1580 #else … … 1262 1590 if ( options != 0 ) { errno = EINVAL; return -1; } 1263 1591 #ifdef __STATISTICS__ 1264 return printStatsXML( stream ); 1592 HeapStatistics stats; 1593 HeapStatisticsCtor( stats ); 1594 return printStatsXML( collectStats( stats ), stream ); // returns bytes written or -1 1265 1595 #else 1266 1596 return 0; // unsupported … … 1275 1605 choose( option ) { 1276 1606 case M_TOP_PAD: 1277 heap Expand = ceiling2( value, __page_size );1607 heapMaster.heapExpand = ceiling2( value, __page_size ); 1278 1608 return 1; 1279 1609 case M_MMAP_THRESHOLD: … … 1319 1649 // Must have CFA linkage to overload with C linkage realloc. 1320 1650 void * resize( void * oaddr, size_t nalign, size_t size ) libcfa_public { 1321 // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned. 1322 if ( unlikely( size == 0 ) ) { // special cases 1323 #ifdef __STATISTICS__ 1324 __atomic_add_fetch( &stats.resize_0_calls, 1, __ATOMIC_SEQ_CST ); 1325 #endif // __STATISTICS__ 1326 free( oaddr ); 1327 return 0p; 1651 if ( unlikely( oaddr == 0p ) ) { // => malloc( size ) 1652 return memalignNoStats( nalign, size STAT_ARG( RESIZE ) ); 1328 1653 } // if 1329 1654 1330 if ( unlikely( nalign < libAlign() ) ) nalign = libAlign(); // reset alignment to minimum 1331 #ifdef __CFA_DEBUG__ 1332 else checkAlign( nalign ); // check alignment 1333 #endif // __CFA_DEBUG__ 1334 1335 if ( unlikely( oaddr == 0p ) ) { 1336 #ifdef __STATISTICS__ 1337 __atomic_add_fetch( &stats.resize_calls, 1, __ATOMIC_SEQ_CST ); 1338 __atomic_add_fetch( &stats.resize_storage_request, size, __ATOMIC_SEQ_CST ); 1339 #endif // __STATISTICS__ 1340 return memalignNoStats( nalign, size ); 1341 } // if 1655 PROLOG( RESIZE, doFree( oaddr ) ); // => free( oaddr ) 1342 1656 1343 1657 // Attempt to reuse existing alignment. … … 1347 1661 1348 1662 if ( unlikely( isFakeHeader ) ) { 1663 checkAlign( nalign ); // check alignment 1349 1664 oalign = ClearAlignmentBit( header ); // old alignment 1350 1665 if ( unlikely( (uintptr_t)oaddr % nalign == 0 // lucky match ? … … 1353 1668 ) ) { 1354 1669 HeaderAddr( oaddr )->kind.fake.alignment = MarkAlignmentBit( nalign ); // update alignment (could be the same) 1355 Heap.FreeHeader * free Elem;1670 Heap.FreeHeader * freeHead; 1356 1671 size_t bsize, oalign; 1357 headers( "resize", oaddr, header, free Elem, bsize, oalign );1672 headers( "resize", oaddr, header, freeHead, bsize, oalign ); 1358 1673 size_t odsize = DataStorage( bsize, oaddr, header ); // data storage available in bucket 1359 1674 … … 1361 1676 HeaderAddr( oaddr )->kind.fake.alignment = MarkAlignmentBit( nalign ); // update alignment (could be the same) 1362 1677 ClearZeroFillBit( header ); // turn off 0 fill 1678 #ifdef __CFA_DEBUG__ 1679 incUnfreed( size - header->kind.real.size ); // adjustment off the size difference 1680 #endif // __CFA_DEBUG__ 1363 1681 header->kind.real.size = size; // reset allocation size 1682 #ifdef __STATISTICS__ 1683 incCalls( RESIZE ); 1684 #endif // __STATISTICS__ 1364 1685 return oaddr; 1365 1686 } // if … … 1370 1691 } // if 1371 1692 1372 #ifdef __STATISTICS__1373 __atomic_add_fetch( &stats.resize_storage_request, size, __ATOMIC_SEQ_CST );1374 #endif // __STATISTICS__1375 1376 1693 // change size, DO NOT preserve STICKY PROPERTIES. 1377 free( oaddr );1378 return memalignNoStats( nalign, size );// create new aligned area1694 doFree( oaddr ); // free previous storage 1695 return memalignNoStats( nalign, size STAT_ARG( RESIZE ) ); // create new aligned area 1379 1696 } // resize 1380 1697 1381 1698 1382 1699 void * realloc( void * oaddr, size_t nalign, size_t size ) libcfa_public { 1383 // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned. 1384 if ( unlikely( size == 0 ) ) { // special cases 1385 #ifdef __STATISTICS__ 1386 __atomic_add_fetch( &stats.realloc_0_calls, 1, __ATOMIC_SEQ_CST ); 1387 #endif // __STATISTICS__ 1388 free( oaddr ); 1389 return 0p; 1700 if ( unlikely( oaddr == 0p ) ) { // => malloc( size ) 1701 return memalignNoStats( nalign, size STAT_ARG( REALLOC ) ); 1390 1702 } // if 1391 1703 1392 if ( unlikely( nalign < libAlign() ) ) nalign = libAlign(); // reset alignment to minimum 1393 #ifdef __CFA_DEBUG__ 1394 else checkAlign( nalign ); // check alignment 1395 #endif // __CFA_DEBUG__ 1396 1397 if ( unlikely( oaddr == 0p ) ) { 1398 #ifdef __STATISTICS__ 1399 __atomic_add_fetch( &stats.realloc_calls, 1, __ATOMIC_SEQ_CST ); 1400 __atomic_add_fetch( &stats.realloc_storage_request, size, __ATOMIC_SEQ_CST ); 1401 #endif // __STATISTICS__ 1402 return memalignNoStats( nalign, size ); 1403 } // if 1704 PROLOG( REALLOC, doFree( oaddr ) ); // => free( oaddr ) 1404 1705 1405 1706 // Attempt to reuse existing alignment. … … 1408 1709 size_t oalign; 1409 1710 if ( unlikely( isFakeHeader ) ) { 1711 checkAlign( nalign ); // check alignment 1410 1712 oalign = ClearAlignmentBit( header ); // old alignment 1411 1713 if ( unlikely( (uintptr_t)oaddr % nalign == 0 // lucky match ? … … 1421 1723 } // if 1422 1724 1423 #ifdef __STATISTICS__ 1424 __atomic_add_fetch( &stats.realloc_calls, 1, __ATOMIC_SEQ_CST ); 1425 __atomic_add_fetch( &stats.realloc_storage_request, size, __ATOMIC_SEQ_CST ); 1426 #endif // __STATISTICS__ 1427 1428 Heap.FreeHeader * freeElem; 1725 Heap.FreeHeader * freeHead; 1429 1726 size_t bsize; 1430 headers( "realloc", oaddr, header, free Elem, bsize, oalign );1727 headers( "realloc", oaddr, header, freeHead, bsize, oalign ); 1431 1728 1432 1729 // change size and copy old content to new storage … … 1435 1732 bool ozfill = ZeroFillBit( header ); // old allocation zero filled 1436 1733 1437 void * naddr = memalignNoStats( nalign, size );// create new aligned area1438 1439 headers( "realloc", naddr, header, free Elem, bsize, oalign );1734 void * naddr = memalignNoStats( nalign, size STAT_ARG( REALLOC ) ); // create new aligned area 1735 1736 headers( "realloc", naddr, header, freeHead, bsize, oalign ); 1440 1737 memcpy( naddr, oaddr, min( osize, size ) ); // copy bytes 1441 free( oaddr );1738 doFree( oaddr ); // free previous storage 1442 1739 1443 1740 if ( unlikely( ozfill ) ) { // previous request zero fill ? … … 1451 1748 1452 1749 1750 void * reallocarray( void * oaddr, size_t nalign, size_t dim, size_t elemSize ) __THROW { 1751 return realloc( oaddr, nalign, dim * elemSize ); 1752 } // reallocarray 1753 1754 1453 1755 // Local Variables: // 1454 1756 // tab-width: 4 //
Note:
See TracChangeset
for help on using the changeset viewer.