Changes in libcfa/src/heap.cfa [116a2ea:5a076837]
- File:
-
- 1 edited
-
libcfa/src/heap.cfa (modified) (55 diffs)
Legend:
- Unmodified
- Added
- Removed
-
libcfa/src/heap.cfa
r116a2ea r5a076837 10 10 // Created On : Tue Dec 19 21:58:35 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Oct 11 15:08:33 202213 // Update Count : 1 52512 // Last Modified On : Fri Apr 29 19:05:03 2022 13 // Update Count : 1167 14 14 // 15 15 16 #include <stdio.h>17 16 #include <string.h> // memset, memcpy 18 17 #include <limits.h> // ULONG_MAX … … 22 21 #include <malloc.h> // memalign, malloc_usable_size 23 22 #include <sys/mman.h> // mmap, munmap 24 extern "C" {25 23 #include <sys/sysinfo.h> // get_nprocs 26 } // extern "C"27 24 28 25 #include "bits/align.hfa" // libAlign 29 26 #include "bits/defs.hfa" // likely, unlikely 30 27 #include "bits/locks.hfa" // __spinlock_t 31 #include "concurrency/kernel/fwd.hfa" // __POLL_PREEMPTION32 28 #include "startup.hfa" // STARTUP_PRIORITY_MEMORY 33 #include "math.hfa" // ceiling,min29 #include "math.hfa" // min 34 30 #include "bitmanip.hfa" // is_pow2, ceiling2 35 31 36 // supported mallopt options 37 #ifndef M_MMAP_THRESHOLD 38 #define M_MMAP_THRESHOLD (-1) 39 #endif // M_MMAP_THRESHOLD 40 41 #ifndef M_TOP_PAD 42 #define M_TOP_PAD (-2) 43 #endif // M_TOP_PAD 44 45 #define FASTLOOKUP // use O(1) table lookup from allocation size to bucket size 46 #define RETURNSPIN // toggle spinlock / lockfree stack 47 #define OWNERSHIP // return freed memory to owner thread 48 49 #define CACHE_ALIGN 64 50 #define CALIGN __attribute__(( aligned(CACHE_ALIGN) )) 51 52 #define TLSMODEL __attribute__(( tls_model("initial-exec") )) 53 54 //#define __STATISTICS__ 55 56 enum { 57 // The default extension heap amount in units of bytes. When the current heap reaches the brk address, the brk 58 // address is extended by the extension amount. 59 __CFA_DEFAULT_HEAP_EXPANSION__ = 10 * 1024 * 1024, 60 61 // The mmap crossover point during allocation. Allocations less than this amount are allocated from buckets; values 62 // greater than or equal to this value are mmap from the operating system. 63 __CFA_DEFAULT_MMAP_START__ = 512 * 1024 + 1, 64 65 // The default unfreed storage amount in units of bytes. When the uC++ program ends it subtracts this amount from 66 // the malloc/free counter to adjust for storage the program does not free. 67 __CFA_DEFAULT_HEAP_UNFREED__ = 0 68 }; // enum 69 70 71 //####################### Heap Trace/Print #################### 32 #define FASTLOOKUP 33 #define __STATISTICS__ 72 34 73 35 … … 93 55 static bool prtFree = false; 94 56 95 bool prtFree() {57 static bool prtFree() { 96 58 return prtFree; 97 59 } // prtFree 98 60 99 bool prtFreeOn() {61 static bool prtFreeOn() { 100 62 bool temp = prtFree; 101 63 prtFree = true; … … 103 65 } // prtFreeOn 104 66 105 bool prtFreeOff() {67 static bool prtFreeOff() { 106 68 bool temp = prtFree; 107 69 prtFree = false; … … 110 72 111 73 112 //######################### Spin Lock ######################### 113 114 115 // pause to prevent excess processor bus usage 116 #if defined( __i386 ) || defined( __x86_64 ) 117 #define Pause() __asm__ __volatile__ ( "pause" : : : ) 118 #elif defined(__ARM_ARCH) 119 #define Pause() __asm__ __volatile__ ( "YIELD" : : : ) 120 #else 121 #error unsupported architecture 122 #endif 123 124 typedef volatile uintptr_t SpinLock_t CALIGN; // aligned addressable word-size 125 126 static inline __attribute__((always_inline)) void lock( volatile SpinLock_t & slock ) { 127 enum { SPIN_START = 4, SPIN_END = 64 * 1024, }; 128 unsigned int spin = SPIN_START; 129 130 for ( unsigned int i = 1;; i += 1 ) { 131 if ( slock == 0 && __atomic_test_and_set( &slock, __ATOMIC_SEQ_CST ) == 0 ) break; // Fence 132 for ( volatile unsigned int s = 0; s < spin; s += 1 ) Pause(); // exponential spin 133 spin += spin; // powers of 2 134 //if ( i % 64 == 0 ) spin += spin; // slowly increase by powers of 2 135 if ( spin > SPIN_END ) spin = SPIN_END; // cap spinning 136 } // for 137 } // spin_lock 138 139 static inline __attribute__((always_inline)) void unlock( volatile SpinLock_t & slock ) { 140 __atomic_clear( &slock, __ATOMIC_SEQ_CST ); // Fence 141 } // spin_unlock 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 142 87 143 88 … … 175 120 unsigned int free_calls, free_null_calls; 176 121 unsigned long long int free_storage_request, free_storage_alloc; 177 unsigned int return_pulls, return_pushes;178 unsigned long long int return_storage_request, return_storage_alloc;122 unsigned int away_pulls, away_pushes; 123 unsigned long long int away_storage_request, away_storage_alloc; 179 124 unsigned int mmap_calls, mmap_0_calls; // no zero calls 180 125 unsigned long long int mmap_storage_request, mmap_storage_alloc; … … 186 131 187 132 static_assert( sizeof(HeapStatistics) == CntTriples * sizeof(StatsOverlay), 188 "Heap statistics counter-triplets does not match with array size" );133 "Heap statistics counter-triplets does not match with array size" ); 189 134 190 135 static void HeapStatisticsCtor( HeapStatistics & stats ) { … … 258 203 static_assert( libAlign() >= sizeof( Storage ), "minimum alignment < sizeof( Storage )" ); 259 204 260 struct __attribute__(( aligned (8) ))FreeHeader {261 size_t blockSize __attribute__(( aligned (8) )); // size of allocations on this list205 struct FreeHeader { 206 size_t blockSize __attribute__(( aligned (8) )); // size of allocations on this list 262 207 #if BUCKETLOCK == SPINLOCK 263 #ifdef OWNERSHIP 264 #ifdef RETURNSPIN 265 SpinLock_t returnLock; 266 #endif // RETURNSPIN 267 Storage * returnList; // other thread return list 268 #endif // OWNERSHIP 269 Storage * freeList; // thread free list 208 __spinlock_t lock; 209 Storage * freeList; 270 210 #else 271 211 StackLF(Storage) freeList; 272 212 #endif // BUCKETLOCK 273 Heap * homeManager; // heap owner (free storage to bucket, from bucket to heap) 274 }; // FreeHeader 213 } __attribute__(( aligned (8) )); // FreeHeader 275 214 276 215 FreeHeader freeLists[NoBucketSizes]; // buckets for different allocation sizes 277 void * heapBuffer; // start of free storage in buffer 278 size_t heapReserve; // amount of remaining free storage in buffer 279 280 #if defined( __STATISTICS__ ) || defined( __CFA_DEBUG__ ) 281 Heap * nextHeapManager; // intrusive link of existing heaps; traversed to collect statistics or check unfreed storage 282 #endif // __STATISTICS__ || __CFA_DEBUG__ 283 Heap * nextFreeHeapManager; // intrusive link of free heaps from terminated threads; reused by new threads 284 285 #ifdef __CFA_DEBUG__ 286 int64_t allocUnfreed; // running total of allocations minus frees; can be negative 287 #endif // __CFA_DEBUG__ 288 289 #ifdef __STATISTICS__ 290 HeapStatistics stats; // local statistic table for this heap 291 #endif // __STATISTICS__ 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 292 221 }; // Heap 293 222 294 223 #if BUCKETLOCK == LOCKFREE 295 inline __attribute__((always_inline)) 296 static { 224 static inline { 297 225 Link(Heap.Storage) * ?`next( Heap.Storage * this ) { return &this->header.kind.real.next; } 298 226 void ?{}( Heap.FreeHeader & ) {} … … 301 229 #endif // LOCKFREE 302 230 303 304 struct HeapMaster { 305 SpinLock_t extLock; // protects allocation-buffer extension 306 SpinLock_t mgrLock; // protects freeHeapManagersList, heapManagersList, heapManagersStorage, heapManagersStorageEnd 307 308 void * heapBegin; // start of heap 309 void * heapEnd; // logical end of heap 310 size_t heapRemaining; // amount of storage not allocated in the current chunk 311 size_t pageSize; // architecture pagesize 312 size_t heapExpand; // sbrk advance 313 size_t mmapStart; // cross over point for mmap 314 unsigned int maxBucketsUsed; // maximum number of buckets in use 315 316 Heap * heapManagersList; // heap-list head 317 Heap * freeHeapManagersList; // free-list head 318 319 // Heap superblocks are not linked; heaps in superblocks are linked via intrusive links. 320 Heap * heapManagersStorage; // next heap to use in heap superblock 321 Heap * heapManagersStorageEnd; // logical heap outside of superblock's end 322 323 #ifdef __STATISTICS__ 324 HeapStatistics stats; // global stats for thread-local heaps to add there counters when exiting 325 unsigned long int threads_started, threads_exited; // counts threads that have started and exited 326 unsigned long int reused_heap, new_heap; // counts reusability of heaps 327 unsigned int sbrk_calls; 328 unsigned long long int sbrk_storage; 329 int stats_fd; 330 #endif // __STATISTICS__ 331 }; // HeapMaster 231 static inline size_t getKey( const Heap.FreeHeader & freeheader ) { return freeheader.blockSize; } 332 232 333 233 334 234 #ifdef FASTLOOKUP 335 enum { LookupSizes = 65_536 + sizeof(Heap.Storage) }; // number of fast lookup sizes235 enum { LookupSizes = 65_536 + sizeof(Heap.Storage) }; // number of fast lookup sizes 336 236 static unsigned char lookup[LookupSizes]; // O(1) lookup for small sizes 337 237 #endif // FASTLOOKUP 338 238 339 static volatile bool heapMasterBootFlag = false; // trigger for first heap 340 static HeapMaster heapMaster @= {}; // program global 341 342 static void heapMasterCtor(); 343 static void heapMasterDtor(); 344 static Heap * getHeap(); 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__ 345 243 346 244 … … 370 268 static_assert( NoBucketSizes == sizeof(bucketSizes) / sizeof(bucketSizes[0] ), "size of bucket array wrong" ); 371 269 372 373 // extern visibility, used by runtime kernel 374 libcfa_public size_t __page_size; // architecture pagesize 375 libcfa_public int __map_prot; // common mmap/mprotect protection 376 377 378 // Thread-local storage is allocated lazily when the storage is accessed. 379 static __thread size_t PAD1 CALIGN TLSMODEL __attribute__(( unused )); // protect false sharing 380 static __thread Heap * volatile heapManager CALIGN TLSMODEL; 381 static __thread size_t PAD2 CALIGN TLSMODEL __attribute__(( unused )); // protect further false sharing 382 383 384 // declare helper functions for HeapMaster 385 void noMemory(); // forward, called by "builtin_new" when malloc returns 0 386 387 388 // generic Bsearchl does not inline, so substitute with hand-coded binary-search. 389 inline __attribute__((always_inline)) 390 static size_t Bsearchl( unsigned int key, const unsigned int vals[], size_t dim ) { 391 size_t l = 0, m, h = dim; 392 while ( l < h ) { 393 m = (l + h) / 2; 394 if ( (unsigned int &)(vals[m]) < key ) { // cast away const 395 l = m + 1; 396 } else { 397 h = m; 398 } // if 399 } // while 400 return l; 401 } // Bsearchl 402 403 404 void heapMasterCtor() with( heapMaster ) { 405 // Singleton pattern to initialize heap master 406 407 verify( bucketSizes[0] == (16 + sizeof(Heap.Storage)) ); 408 409 __page_size = sysconf( _SC_PAGESIZE ); 410 __map_prot = PROT_READ | PROT_WRITE | PROT_EXEC; 411 412 ?{}( extLock ); 413 ?{}( mgrLock ); 414 415 char * end = (char *)sbrk( 0 ); 416 heapBegin = heapEnd = sbrk( (char *)ceiling2( (long unsigned int)end, libAlign() ) - end ); // move start of heap to multiple of alignment 417 heapRemaining = 0; 418 heapExpand = malloc_expansion(); 419 mmapStart = malloc_mmap_start(); 420 421 // find the closest bucket size less than or equal to the mmapStart size 422 maxBucketsUsed = Bsearchl( mmapStart, bucketSizes, NoBucketSizes ); // binary search 423 424 verify( (mmapStart >= pageSize) && (bucketSizes[NoBucketSizes - 1] >= mmapStart) ); 425 verify( maxBucketsUsed < NoBucketSizes ); // subscript failure ? 426 verify( mmapStart <= bucketSizes[maxBucketsUsed] ); // search failure ? 427 428 heapManagersList = 0p; 429 freeHeapManagersList = 0p; 430 431 heapManagersStorage = 0p; 432 heapManagersStorageEnd = 0p; 433 434 #ifdef __STATISTICS__ 435 HeapStatisticsCtor( stats ); // clear statistic counters 436 threads_started = threads_exited = 0; 437 reused_heap = new_heap = 0; 438 sbrk_calls = sbrk_storage = 0; 439 stats_fd = STDERR_FILENO; 440 #endif // __STATISTICS__ 441 442 #ifdef FASTLOOKUP 443 for ( unsigned int i = 0, idx = 0; i < LookupSizes; i += 1 ) { 444 if ( i > bucketSizes[idx] ) idx += 1; 445 lookup[i] = idx; 446 verify( i <= bucketSizes[idx] ); 447 verify( (i <= 32 && idx == 0) || (i > bucketSizes[idx - 1]) ); 448 } // for 449 #endif // FASTLOOKUP 450 451 heapMasterBootFlag = true; 452 } // heapMasterCtor 453 454 455 #define NO_MEMORY_MSG "insufficient heap memory available to allocate %zd new bytes." 456 457 Heap * getHeap() with( heapMaster ) { 458 Heap * heap; 459 if ( freeHeapManagersList ) { // free heap for reused ? 460 heap = freeHeapManagersList; 461 freeHeapManagersList = heap->nextFreeHeapManager; 462 463 #ifdef __STATISTICS__ 464 reused_heap += 1; 465 #endif // __STATISTICS__ 466 } else { // free heap not found, create new 467 // Heap size is about 12K, FreeHeader (128 bytes because of cache alignment) * NoBucketSizes (91) => 128 heaps * 468 // 12K ~= 120K byte superblock. Where 128-heap superblock handles a medium sized multi-processor server. 469 size_t remaining = heapManagersStorageEnd - heapManagersStorage; // remaining free heaps in superblock 470 if ( ! heapManagersStorage || remaining != 0 ) { 471 // Each block of heaps is a multiple of the number of cores on the computer. 472 int HeapDim = get_nprocs(); // get_nprocs_conf does not work 473 size_t size = HeapDim * sizeof( Heap ); 474 475 heapManagersStorage = (Heap *)mmap( 0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 ); 476 if ( unlikely( heapManagersStorage == (Heap *)MAP_FAILED ) ) { // failed ? 477 if ( errno == ENOMEM ) abort( NO_MEMORY_MSG, size ); // no memory 478 // Do not call strerror( errno ) as it may call malloc. 479 abort( "attempt to allocate block of heaps of size %zu bytes and mmap failed with errno %d.", size, errno ); 480 } // if 481 heapManagersStorageEnd = &heapManagersStorage[HeapDim]; // outside array 482 } // if 483 484 heap = heapManagersStorage; 485 heapManagersStorage = heapManagersStorage + 1; // bump next heap 486 487 #if defined( __STATISTICS__ ) || defined( __CFA_DEBUG__ ) 488 heap->nextHeapManager = heapManagersList; 489 #endif // __STATISTICS__ || __CFA_DEBUG__ 490 heapManagersList = heap; 491 492 #ifdef __STATISTICS__ 493 new_heap += 1; 494 #endif // __STATISTICS__ 495 496 with( *heap ) { 497 for ( unsigned int j = 0; j < NoBucketSizes; j += 1 ) { // initialize free lists 498 #ifdef OWNERSHIP 499 #ifdef RETURNSPIN 500 ?{}( freeLists[j].returnLock ); 501 #endif // RETURNSPIN 502 freeLists[j].returnList = 0p; 503 #endif // OWNERSHIP 504 freeLists[j].freeList = 0p; 505 freeLists[j].homeManager = heap; 506 freeLists[j].blockSize = bucketSizes[j]; 507 } // for 508 509 heapBuffer = 0p; 510 heapReserve = 0; 511 nextFreeHeapManager = 0p; 512 #ifdef __CFA_DEBUG__ 513 allocUnfreed = 0; 514 #endif // __CFA_DEBUG__ 515 } // with 516 } // if 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 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 554 272 555 273 556 274 //####################### Memory Allocation Routines Helpers #################### 557 275 276 277 #ifdef __CFA_DEBUG__ 278 static size_t allocUnfreed; // running total of allocations minus frees 279 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 pid 288 } // if 289 } // prtUnfreed 558 290 559 291 extern int cfa_main_returned; // from interpose.cfa 560 292 extern "C" { 561 void memory_startup( void ) {562 if ( ! heapMasterBootFlag ) heapManagerCtor(); // sanity check563 } // memory_startup564 565 void memory_shutdown( void ) {566 heapManagerDtor();567 } // memory_shutdown568 569 293 void heapAppStart() { // called by __cfaabi_appready_startup 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__ 294 allocUnfreed = 0; 578 295 } // heapAppStart 579 296 580 297 void heapAppStop() { // called by __cfaabi_appready_startdown 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 long long int 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 %llu(0x%llx) 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__ 298 fclose( stdin ); fclose( stdout ); 299 if ( cfa_main_returned ) prtUnfreed(); // do not check unfreed storage if exit called 602 300 } // heapAppStop 603 301 } // extern "C" 302 #endif // __CFA_DEBUG__ 604 303 605 304 606 305 #ifdef __STATISTICS__ 607 306 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 stderr 608 311 609 312 #define prtFmt \ … … 618 321 " realloc >0 calls %'u; 0 calls %'u; storage %'llu / %'llu bytes\n" \ 619 322 " free !null calls %'u; null 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" 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" \ 626 326 627 327 // Use "write" because streams may be shutdown when calls are made. 628 static int printStats( HeapStatistics & stats ) with( heapMaster, stats ) {// see malloc_stats328 static int printStats() { // see malloc_stats 629 329 char helpText[sizeof(prtFmt) + 1024]; // space for message and values 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, 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, 641 340 sbrk_calls, sbrk_storage, 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 341 stats.mmap_calls, stats.mmap_storage_request, stats.mmap_storage_alloc, 342 stats.munmap_calls, stats.munmap_storage_request, stats.munmap_storage_alloc 646 343 ); 647 344 } // printStats … … 661 358 "<total type=\"realloc\" >0 count=\"%'u;\" 0 count=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \ 662 359 "<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" \664 360 "<total type=\"sbrk\" count=\"%'u;\" size=\"%'llu\"/> bytes\n" \ 665 361 "<total type=\"mmap\" count=\"%'u;\" size=\"%'llu / %'llu\" / > bytes\n" \ 666 362 "<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" \669 363 "</malloc>" 670 364 671 static int printStatsXML( HeapStatistics & stats, FILE * stream ) with( heapMaster, stats ) {// see malloc_info365 static int printStatsXML( FILE * stream ) { // see malloc_info 672 366 char helpText[sizeof(prtFmtXML) + 1024]; // space for message and values 673 367 return __cfaabi_bits_print_buffer( fileno( stream ), helpText, sizeof(helpText), prtFmtXML, 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, 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, 684 377 sbrk_calls, sbrk_storage, 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 378 stats.mmap_calls, stats.mmap_storage_request, stats.mmap_storage_alloc, 379 stats.munmap_calls, stats.munmap_storage_request, stats.munmap_storage_alloc 689 380 ); 690 381 } // 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 } // for699 700 unlock( mgrLock );701 return stats;702 } // collectStats703 382 #endif // __STATISTICS__ 704 383 705 384 706 static bool setMmapStart( size_t value ) with( heapMaster ) { // true => mmapped, false => sbrk 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 707 411 if ( value < __page_size || bucketSizes[NoBucketSizes - 1] < value ) return false; 708 412 mmapStart = value; // set global 709 413 710 414 // find the closest bucket size less than or equal to the mmapStart size 711 maxBucketsUsed = Bsearchl( mmapStart, bucketSizes, NoBucketSizes ); // binary search712 verify( maxBucketsUsed < NoBucketSizes ); // subscript failure ?713 verify( mmapStart <= bucketSizes[maxBucketsUsed] ); // search failure ?415 maxBucketsUsed = Bsearchl( (unsigned int)mmapStart, bucketSizes, NoBucketSizes ); // binary search 416 assert( maxBucketsUsed < NoBucketSizes ); // subscript failure ? 417 assert( mmapStart <= bucketSizes[maxBucketsUsed] ); // search failure ? 714 418 return true; 715 419 } // setMmapStart … … 734 438 735 439 736 inline __attribute__((always_inline)) 737 static void checkAlign( size_t alignment ) { 440 static inline void checkAlign( size_t alignment ) { 738 441 if ( unlikely( alignment < libAlign() || ! is_pow2( alignment ) ) ) { 739 442 abort( "**** Error **** alignment %zu for memory allocation is less than %d and/or not a power of 2.", alignment, libAlign() ); … … 742 445 743 446 744 inline __attribute__((always_inline)) 745 static void checkHeader( bool check, const char name[], void * addr ) { 447 static inline void checkHeader( bool check, const char name[], void * addr ) { 746 448 if ( unlikely( check ) ) { // bad address ? 747 449 abort( "**** Error **** attempt to %s storage %p with address outside the heap.\n" … … 768 470 769 471 770 inline __attribute__((always_inline)) 771 static void fakeHeader( Heap.Storage.Header *& header, size_t & alignment ) { 472 static inline void fakeHeader( Heap.Storage.Header *& header, size_t & alignment ) { 772 473 if ( unlikely( AlignmentBit( header ) ) ) { // fake header ? 773 474 alignment = ClearAlignmentBit( header ); // clear flag from value … … 782 483 783 484 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 ) { 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 ) { 787 487 header = HeaderAddr( addr ); 788 488 … … 809 509 checkHeader( header < (Heap.Storage.Header *)heapBegin || (Heap.Storage.Header *)heapEnd < header, name, addr ); // bad address ? (offset could be + or -) 810 510 811 Heap * homeManager;812 511 if ( unlikely( freeHead == 0p || // freed and only free-list node => null link 813 512 // freed and link points at another free block not to a bucket in the bucket array. 814 (homeManager = freeHead->homeManager, freeHead < &homeManager->freeLists[0] || 815 &homeManager->freeLists[NoBucketSizes] <= freeHead ) ) ) { 513 freeHead < &freeLists[0] || &freeLists[NoBucketSizes] <= freeHead ) ) { 816 514 abort( "**** Error **** attempt to %s storage %p with corrupted header.\n" 817 515 "Possible cause is duplicate free on same block or overwriting of header information.", … … 823 521 } // headers 824 522 825 826 static void * master_extend( size_t size ) with( heapMaster ) { 827 lock( extLock ); 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 ); 828 546 829 547 ptrdiff_t rem = heapRemaining - size; … … 831 549 // If the size requested is bigger than the current remaining storage, increase the size of the heap. 832 550 833 size_t increase = ceiling2( size > heapExpand ? size : heapExpand, libAlign());551 size_t increase = ceiling2( size > heapExpand ? size : heapExpand, __page_size ); 834 552 // Do not call abort or strerror( errno ) as they may call malloc. 835 if ( unlikely( sbrk( increase ) == (void *)-1 ) ) {// failed, no memory ?836 unlock( ext Lock );553 if ( sbrk( increase ) == (void *)-1 ) { // failed, no memory ? 554 unlock( extlock ); 837 555 __cfaabi_bits_print_nolock( STDERR_FILENO, NO_MEMORY_MSG, size ); 838 556 _exit( EXIT_FAILURE ); // give up 839 557 } // if 840 rem = heapRemaining + increase - size; 558 559 // Make storage executable for thunks. 560 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 841 565 842 566 #ifdef __STATISTICS__ … … 844 568 sbrk_storage += increase; 845 569 #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; 846 578 } // if 847 579 … … 849 581 heapRemaining = rem; 850 582 heapEnd = (char *)heapEnd + size; 851 852 unlock( extLock ); 583 unlock( extlock ); 853 584 return block; 854 } // master_extend 855 856 857 __attribute__(( noinline )) 858 static void * manager_extend( size_t size ) with( *heapManager ) { 859 ptrdiff_t rem = heapReserve - size; 860 861 if ( unlikely( rem < 0 ) ) { // negative 862 // If the size requested is bigger than the current remaining reserve, use the current reserve to populate 863 // smaller freeLists, and increase the reserve. 864 865 rem = heapReserve; // positive 866 867 if ( rem >= bucketSizes[0] ) { // minimal size ? otherwise ignore 868 size_t bucket; 869 #ifdef FASTLOOKUP 870 if ( likely( rem < LookupSizes ) ) bucket = lookup[rem]; 871 #endif // FASTLOOKUP 872 bucket = Bsearchl( rem, bucketSizes, heapMaster.maxBucketsUsed ); 873 verify( 0 <= bucket && bucket <= heapMaster.maxBucketsUsed ); 874 Heap.FreeHeader * freeHead = &(freeLists[bucket]); 875 876 // The remaining storage many not be bucket size, whereas all other allocations are. Round down to previous 877 // bucket size in this case. 878 if ( unlikely( freeHead->blockSize > (size_t)rem ) ) freeHead -= 1; 879 Heap.Storage * block = (Heap.Storage *)heapBuffer; 880 881 block->header.kind.real.next = freeHead->freeList; // push on stack 882 freeHead->freeList = block; 883 } // if 884 885 size_t increase = ceiling( size > ( heapMaster.heapExpand / 10 ) ? size : ( heapMaster.heapExpand / 10 ), libAlign() ); 886 heapBuffer = master_extend( increase ); 887 rem = increase - size; 888 } // if 889 890 Heap.Storage * block = (Heap.Storage *)heapBuffer; 891 heapReserve = rem; 892 heapBuffer = (char *)heapBuffer + size; 893 894 return block; 895 } // manager_extend 896 897 898 #define BOOT_HEAP_MANAGER \ 899 if ( unlikely( ! heapMasterBootFlag ) ) { \ 900 heapManagerCtor(); /* trigger for first heap */ \ 901 } /* if */ 902 903 #ifdef __STATISTICS__ 904 #define STAT_NAME __counter 905 #define STAT_PARM , unsigned int STAT_NAME 906 #define STAT_ARG( name ) , name 907 #define STAT_0_CNT( counter ) stats.counters[counter].calls_0 += 1 908 #else 909 #define STAT_NAME 910 #define STAT_PARM 911 #define STAT_ARG( name ) 912 #define STAT_0_CNT( counter ) 913 #endif // __STATISTICS__ 914 915 #define PROLOG( counter, ... ) \ 916 BOOT_HEAP_MANAGER; \ 917 if ( unlikely( size == 0 ) || /* 0 BYTE ALLOCATION RETURNS NULL POINTER */ \ 918 unlikely( size > ULONG_MAX - sizeof(Heap.Storage) ) ) { /* error check */ \ 919 STAT_0_CNT( counter ); \ 920 __VA_ARGS__; \ 921 return 0p; \ 922 } /* if */ 923 924 925 #define SCRUB_SIZE 1024lu 926 // Do not use '\xfe' for scrubbing because dereferencing an address composed of it causes a SIGSEGV *without* a valid IP 927 // pointer in the interrupt frame. 928 #define SCRUB '\xff' 929 930 static void * doMalloc( size_t size STAT_PARM ) libcfa_nopreempt with( *heapManager ) { 931 PROLOG( STAT_NAME ); 932 933 verify( heapManager ); 934 Heap.Storage * block; // pointer to new block of storage 585 } // extend 586 587 588 static inline void * doMalloc( size_t size ) with( heapManager ) { 589 Heap.Storage * block; // pointer to new block of storage 935 590 936 591 // Look up size in the size list. Make sure the user request includes space for the header that must be allocated 937 592 // along with the block and is a multiple of the alignment size. 593 938 594 size_t tsize = size + sizeof(Heap.Storage); 939 595 940 #ifdef __STATISTICS__ 941 stats.counters[STAT_NAME].calls += 1; 942 stats.counters[STAT_NAME].request += size; 943 #endif // __STATISTICS__ 944 945 #ifdef __CFA_DEBUG__ 946 allocUnfreed += size; 947 #endif // __CFA_DEBUG__ 948 949 if ( likely( tsize < heapMaster.mmapStart ) ) { // small size => sbrk 950 size_t bucket; 596 if ( likely( tsize < mmapStart ) ) { // small size => sbrk 597 size_t posn; 951 598 #ifdef FASTLOOKUP 952 if ( likely( tsize < LookupSizes ) ) bucket= lookup[tsize];599 if ( tsize < LookupSizes ) posn = lookup[tsize]; 953 600 else 954 601 #endif // FASTLOOKUP 955 bucket = Bsearchl( tsize, bucketSizes, heapMaster.maxBucketsUsed ); 956 verify( 0 <= bucket && bucket <= heapMaster.maxBucketsUsed ); 957 Heap.FreeHeader * freeHead = &freeLists[bucket]; 958 959 verify( freeHead <= &freeLists[heapMaster.maxBucketsUsed] ); // subscripting error ? 960 verify( tsize <= freeHead->blockSize ); // search failure ? 961 962 tsize = freeHead->blockSize; // total space needed for request 963 #ifdef __STATISTICS__ 964 stats.counters[STAT_NAME].alloc += tsize; 965 #endif // __STATISTICS__ 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 966 607 967 608 // Spin until the lock is acquired for this particular size of block. 968 609 969 610 #if BUCKETLOCK == SPINLOCK 970 block = freeHead->freeList; // remove node from stack 611 lock( freeElem->lock __cfaabi_dbg_ctx2 ); 612 block = freeElem->freeList; // remove node from stack 971 613 #else 972 block = pop( free Head->freeList );614 block = pop( freeElem->freeList ); 973 615 #endif // BUCKETLOCK 974 616 if ( unlikely( block == 0p ) ) { // no free block ? 975 #ifdef OWNERSHIP 976 // Freelist for that size is empty, so carve it out of the heap, if there is enough left, or get some more 617 #if BUCKETLOCK == SPINLOCK 618 unlock( freeElem->lock ); 619 #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 977 622 // and then carve it off. 978 #ifdef RETURNSPIN 979 #if BUCKETLOCK == SPINLOCK 980 lock( freeHead->returnLock ); 981 block = freeHead->returnList; 982 freeHead->returnList = 0p; 983 unlock( freeHead->returnLock ); 984 #else 985 block = __atomic_exchange_n( &freeHead->returnList, nullptr, __ATOMIC_SEQ_CST ); 986 #endif // RETURNSPIN 987 988 if ( likely( block == 0p ) ) { // return list also empty? 989 #endif // OWNERSHIP 990 // Do not leave kernel thread as manager_extend accesses heapManager. 991 disable_interrupts(); 992 block = (Heap.Storage *)manager_extend( tsize ); // mutual exclusion on call 993 enable_interrupts( false ); 994 995 // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED. 996 997 #ifdef __CFA_DEBUG__ 998 // Scrub new memory so subsequent uninitialized usages might fail. Only scrub the first 1024 bytes. 999 memset( block->data, SCRUB, min( SCRUB_SIZE, tsize - sizeof(Heap.Storage) ) ); 1000 #endif // __CFA_DEBUG__ 1001 #endif // BUCKETLOCK 1002 #ifdef OWNERSHIP 1003 } else { // merge returnList into freeHead 1004 #ifdef __STATISTICS__ 1005 stats.return_pulls += 1; 1006 #endif // __STATISTICS__ 1007 1008 // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED. 1009 1010 freeHead->freeList = block->header.kind.real.next; 1011 } // if 1012 #endif // OWNERSHIP 623 624 block = (Heap.Storage *)extend( tsize ); // mutual exclusion on call 625 #if BUCKETLOCK == SPINLOCK 1013 626 } else { 1014 // Memory is scrubbed in doFree. 1015 freeHead->freeList = block->header.kind.real.next; 1016 } // if 1017 1018 block->header.kind.real.home = freeHead; // pointer back to free list of apropriate size 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 1019 633 } else { // large size => mmap 1020 634 if ( unlikely( size > ULONG_MAX - __page_size ) ) return 0p; 1021 635 tsize = ceiling2( tsize, __page_size ); // must be multiple of page size 1022 636 #ifdef __STATISTICS__ 1023 stats.counters[STAT_NAME].alloc += tsize; 1024 stats.mmap_calls += 1; 1025 stats.mmap_storage_request += size; 1026 stats.mmap_storage_alloc += tsize; 1027 #endif // __STATISTICS__ 1028 1029 disable_interrupts(); 1030 block = (Heap.Storage *)mmap( 0, tsize, __map_prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 ); 1031 enable_interrupts( false ); 1032 1033 // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED. 1034 1035 if ( unlikely( block == (Heap.Storage *)MAP_FAILED ) ) { // failed ? 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 ); 640 #endif // __STATISTICS__ 641 642 block = (Heap.Storage *)mmap( 0, tsize, __map_prot, MAP_PRIVATE | MAP_ANONYMOUS, mmapFd, 0 ); 643 if ( block == (Heap.Storage *)MAP_FAILED ) { // failed ? 1036 644 if ( errno == ENOMEM ) abort( NO_MEMORY_MSG, tsize ); // no memory 1037 645 // Do not call strerror( errno ) as it may call malloc. 1038 abort( "attempt to allocate large object (> %zu) of size %zu bytes and mmap failed with errno %d.", size, heapMaster.mmapStart, errno ); 1039 } // if 646 abort( "(Heap &)0x%p.doMalloc() : internal error, mmap failure, size:%zu errno:%d.", &heapManager, tsize, errno ); 647 } //if 648 #ifdef __CFA_DEBUG__ 649 // Set new memory to garbage so subsequent uninitialized usages might fail. 650 memset( block, '\xde', tsize ); 651 //Memset( block, tsize ); 652 #endif // __CFA_DEBUG__ 1040 653 block->header.kind.real.blockSize = MarkMmappedBit( tsize ); // storage size for munmap 1041 1042 #ifdef __CFA_DEBUG__1043 // Scrub new memory so subsequent uninitialized usages might fail. Only scrub the first 1024 bytes. The rest of1044 // the storage set to 0 by mmap.1045 memset( block->data, SCRUB, min( SCRUB_SIZE, tsize - sizeof(Heap.Storage) ) );1046 #endif // __CFA_DEBUG__1047 654 } // if 1048 655 … … 1052 659 1053 660 #ifdef __CFA_DEBUG__ 661 __atomic_add_fetch( &allocUnfreed, tsize, __ATOMIC_SEQ_CST ); 1054 662 if ( traceHeap() ) { 1055 663 char helpText[64]; … … 1059 667 #endif // __CFA_DEBUG__ 1060 668 1061 // poll_interrupts(); // call rollforward1062 1063 669 return addr; 1064 670 } // doMalloc 1065 671 1066 672 1067 static void doFree( void * addr ) libcfa_nopreempt with( *heapManager ) { 1068 verify( addr ); 1069 1070 // detect free after thread-local storage destruction and use global stats in that case 673 static inline void doFree( void * addr ) with( heapManager ) { 674 #ifdef __CFA_DEBUG__ 675 if ( unlikely( heapManager.heapBegin == 0p ) ) { 676 abort( "doFree( %p ) : internal error, called before heap is initialized.", addr ); 677 } // if 678 #endif // __CFA_DEBUG__ 1071 679 1072 680 Heap.Storage.Header * header; 1073 Heap.FreeHeader * freeHead; 1074 size_t size, alignment; 1075 1076 bool mapped = headers( "free", addr, header, freeHead, size, alignment ); 1077 #if defined( __STATISTICS__ ) || defined( __CFA_DEBUG__ ) 1078 size_t rsize = header->kind.real.size; // optimization 1079 #endif // __STATISTICS__ || __CFA_DEBUG__ 1080 1081 #ifdef __STATISTICS__ 1082 stats.free_storage_request += rsize; 1083 stats.free_storage_alloc += size; 1084 #endif // __STATISTICS__ 1085 1086 #ifdef __CFA_DEBUG__ 1087 allocUnfreed -= rsize; 1088 #endif // __CFA_DEBUG__ 1089 1090 if ( unlikely( mapped ) ) { // mmapped ? 1091 #ifdef __STATISTICS__ 1092 stats.munmap_calls += 1; 1093 stats.munmap_storage_request += rsize; 1094 stats.munmap_storage_alloc += size; 1095 #endif // __STATISTICS__ 1096 1097 // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED. 1098 1099 // Does not matter where this storage is freed. 1100 if ( unlikely( munmap( header, size ) == -1 ) ) { 1101 // Do not call strerror( errno ) as it may call malloc. 1102 abort( "attempt to deallocate large object %p and munmap failed with errno %d.\n" 1103 "Possible cause is invalid delete pointer: either not allocated or with corrupt header.", 1104 addr, errno ); 681 Heap.FreeHeader * freeElem; 682 size_t size, alignment; // not used (see realloc) 683 684 if ( headers( "free", addr, header, freeElem, size, alignment ) ) { // mmapped ? 685 #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 ); 689 #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 ); 1105 694 } // if 1106 695 } else { 1107 696 #ifdef __CFA_DEBUG__ 1108 // memset is NOT always inlined! 1109 disable_interrupts(); 1110 // Scrub old memory so subsequent usages might fail. Only scrub the first/last SCRUB_SIZE bytes. 1111 char * data = ((Heap.Storage *)header)->data; // data address 1112 size_t dsize = size - sizeof(Heap.Storage); // data size 1113 if ( dsize <= SCRUB_SIZE * 2 ) { 1114 memset( data, SCRUB, dsize ); // scrub all 1115 } else { 1116 memset( data, SCRUB, SCRUB_SIZE ); // scrub front 1117 memset( data + dsize - SCRUB_SIZE, SCRUB, SCRUB_SIZE ); // scrub back 1118 } // if 1119 enable_interrupts( false ); 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 ) ); 1120 700 #endif // __CFA_DEBUG__ 1121 701 1122 if ( likely( heapManager == freeHead->homeManager ) ) { // belongs to this thread 1123 header->kind.real.next = freeHead->freeList; // push on stack 1124 freeHead->freeList = (Heap.Storage *)header; 1125 } else { // return to thread owner 1126 verify( heapManager ); 1127 1128 #ifdef OWNERSHIP 1129 #ifdef RETURNSPIN 1130 lock( freeHead->returnLock ); 1131 header->kind.real.next = freeHead->returnList; // push to bucket return list 1132 freeHead->returnList = (Heap.Storage *)header; 1133 unlock( freeHead->returnLock ); 1134 #else // lock free 1135 header->kind.real.next = freeHead->returnList; // link new node to top node 1136 // CAS resets header->kind.real.next = freeHead->returnList on failure 1137 while ( ! __atomic_compare_exchange_n( &freeHead->returnList, &header->kind.real.next, header, 1138 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) ); 1139 #endif // RETURNSPIN 1140 1141 #else // no OWNERSHIP 1142 1143 freeHead = &heap->freeLists[ClearStickyBits( header->kind.real.home ) - &freeHead->homeManager->freeLists[0]]; 1144 header->kind.real.next = freeHead->freeList; // push on stack 1145 freeHead->freeList = (Heap.Storage *)header; 1146 #endif // ! OWNERSHIP 1147 1148 #ifdef __U_STATISTICS__ 1149 stats.return_pushes += 1; 1150 stats.return_storage_request += rsize; 1151 stats.return_storage_alloc += size; 1152 #endif // __U_STATISTICS__ 1153 1154 // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED. 1155 } // if 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 1156 716 } // if 1157 717 1158 718 #ifdef __CFA_DEBUG__ 719 __atomic_add_fetch( &allocUnfreed, -size, __ATOMIC_SEQ_CST ); 1159 720 if ( traceHeap() ) { 1160 721 char helpText[64]; … … 1163 724 } // if 1164 725 #endif // __CFA_DEBUG__ 1165 1166 // poll_interrupts(); // call rollforward1167 726 } // doFree 1168 727 1169 728 1170 s ize_t prtFree( Heap & manager ) with( manager ) {729 static size_t prtFree( Heap & manager ) with( manager ) { 1171 730 size_t total = 0; 1172 731 #ifdef __STATISTICS__ … … 1174 733 __cfaabi_bits_print_nolock( STDERR_FILENO, "\nBin lists (bin size : free blocks on list)\n" ); 1175 734 #endif // __STATISTICS__ 1176 for ( unsigned int i = 0; i < heapMaster.maxBucketsUsed; i += 1 ) {735 for ( unsigned int i = 0; i < maxBucketsUsed; i += 1 ) { 1177 736 size_t size = freeLists[i].blockSize; 1178 737 #ifdef __STATISTICS__ … … 1205 764 __cfaabi_bits_release(); 1206 765 #endif // __STATISTICS__ 1207 return (char *)heap Master.heapEnd - (char *)heapMaster.heapBegin - total;766 return (char *)heapEnd - (char *)heapBegin - total; 1208 767 } // prtFree 1209 768 1210 769 1211 #ifdef __STATISTICS__ 1212 static void incCalls( long int statName ) libcfa_nopreempt { 1213 heapManager->stats.counters[statName].calls += 1; 1214 } // incCalls 1215 1216 static void incZeroCalls( long int statName ) libcfa_nopreempt { 1217 heapManager->stats.counters[statName].calls_0 += 1; 1218 } // incZeroCalls 1219 #endif // __STATISTICS__ 1220 1221 #ifdef __CFA_DEBUG__ 1222 static void incUnfreed( size_t offset ) libcfa_nopreempt { 1223 heapManager->allocUnfreed += offset; 1224 } // incUnfreed 1225 #endif // __CFA_DEBUG__ 1226 1227 1228 static void * memalignNoStats( size_t alignment, size_t size STAT_PARM ) { 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__ 1229 841 checkAlign( alignment ); // check alignment 1230 1231 // if alignment <= default alignment or size == 0, do normal malloc as two headers are unnecessary 1232 if ( unlikely( alignment <= libAlign() || size == 0 ) ) return doMalloc( size STAT_ARG( STAT_NAME ) ); 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 ); 1233 846 1234 847 // Allocate enough storage to guarantee an address on the alignment boundary, and sufficient space before it for … … 1241 854 // subtract libAlign() because it is already the minimum alignment 1242 855 // add sizeof(Storage) for fake header 1243 size_t offset = alignment - libAlign() + sizeof(Heap.Storage); 1244 char * addr = (char *)doMalloc( size + offset STAT_ARG( STAT_NAME ) ); 856 char * addr = (char *)mallocNoStats( size + alignment - libAlign() + sizeof(Heap.Storage) ); 1245 857 1246 858 // address in the block of the "next" alignment address … … 1248 860 1249 861 // address of header from malloc 1250 Heap.Storage.Header * realHeader = HeaderAddr( addr ); 1251 realHeader->kind.real.size = size; // correct size to eliminate above alignment offset 1252 #ifdef __CFA_DEBUG__ 1253 incUnfreed( -offset ); // adjustment off the offset from call to doMalloc 1254 #endif // __CFA_DEBUG__ 1255 1256 // address of fake header *before* the alignment location 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 1257 865 Heap.Storage.Header * fakeHeader = HeaderAddr( user ); 1258 1259 866 // SKULLDUGGERY: insert the offset to the start of the actual storage block and remember alignment 1260 fakeHeader->kind.fake.offset = (char *)fakeHeader - (char *) realHeader;867 fakeHeader->kind.fake.offset = (char *)fakeHeader - (char *)RealHeader; 1261 868 // SKULLDUGGERY: odd alignment implies fake header 1262 869 fakeHeader->kind.fake.alignment = MarkAlignmentBit( alignment ); … … 1273 880 // then malloc() returns a unique pointer value that can later be successfully passed to free(). 1274 881 void * malloc( size_t size ) libcfa_public { 1275 return doMalloc( size STAT_ARG( MALLOC ) ); 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 ); 1276 892 } // malloc 1277 893 … … 1279 895 // Same as malloc() except size bytes is an array of dim elements each of elemSize bytes. 1280 896 void * aalloc( size_t dim, size_t elemSize ) libcfa_public { 1281 return doMalloc( dim * elemSize STAT_ARG( AALLOC ) ); 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 ); 1282 908 } // aalloc 1283 909 … … 1286 912 void * calloc( size_t dim, size_t elemSize ) libcfa_public { 1287 913 size_t size = dim * elemSize; 1288 char * addr = (char *)doMalloc( size STAT_ARG( CALLOC ) ); 1289 1290 if ( unlikely( addr == NULL ) ) return NULL; // stop further processing if 0p is returned 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 ); 1291 926 1292 927 Heap.Storage.Header * header; 1293 Heap.FreeHeader * free Head;928 Heap.FreeHeader * freeElem; 1294 929 size_t bsize, alignment; 1295 930 … … 1297 932 bool mapped = 1298 933 #endif // __CFA_DEBUG__ 1299 headers( "calloc", addr, header, free Head, bsize, alignment );934 headers( "calloc", addr, header, freeElem, bsize, alignment ); 1300 935 1301 936 #ifndef __CFA_DEBUG__ 1302 937 // Mapped storage is zero filled, but in debug mode mapped memory is scrubbed in doMalloc, so it has to be reset to zero. 1303 if ( likely( ! mapped ))938 if ( ! mapped ) 1304 939 #endif // __CFA_DEBUG__ 1305 940 // <-------0000000000000000000000000000UUUUUUUUUUUUUUUUUUUUUUUUU> bsize (bucket size) U => undefined … … 1317 952 // call to malloc(), alloc(), calloc() or realloc(). If the area pointed to was moved, a free(oaddr) is done. 1318 953 void * resize( void * oaddr, size_t size ) libcfa_public { 1319 if ( unlikely( oaddr == 0p ) ) { // => malloc( size ) 1320 return doMalloc( size STAT_ARG( RESIZE ) ); 1321 } // if 1322 1323 PROLOG( RESIZE, doFree( oaddr ) ); // => free( oaddr ) 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 1324 972 1325 973 Heap.Storage.Header * header; 1326 Heap.FreeHeader * free Head;974 Heap.FreeHeader * freeElem; 1327 975 size_t bsize, oalign; 1328 headers( "resize", oaddr, header, free Head, bsize, oalign );976 headers( "resize", oaddr, header, freeElem, bsize, oalign ); 1329 977 1330 978 size_t odsize = DataStorage( bsize, oaddr, header ); // data storage available in bucket … … 1332 980 if ( oalign == libAlign() && size <= odsize && odsize <= size * 2 ) { // allow 50% wasted storage for smaller size 1333 981 ClearZeroFillBit( header ); // no alignment and turn off 0 fill 1334 #ifdef __CFA_DEBUG__1335 incUnfreed( size - header->kind.real.size ); // adjustment off the size difference1336 #endif // __CFA_DEBUG__1337 982 header->kind.real.size = size; // reset allocation size 1338 #ifdef __STATISTICS__1339 incCalls( RESIZE );1340 #endif // __STATISTICS__1341 983 return oaddr; 1342 984 } // if 1343 985 986 #ifdef __STATISTICS__ 987 __atomic_add_fetch( &stats.resize_storage_request, size, __ATOMIC_SEQ_CST ); 988 #endif // __STATISTICS__ 989 1344 990 // change size, DO NOT preserve STICKY PROPERTIES. 1345 doFree( oaddr ); // free previous storage 1346 1347 return doMalloc( size STAT_ARG( RESIZE ) ); // create new area 991 free( oaddr ); 992 return mallocNoStats( size ); // create new area 1348 993 } // resize 1349 994 … … 1352 997 // the old and new sizes. 1353 998 void * realloc( void * oaddr, size_t size ) libcfa_public { 1354 if ( unlikely( oaddr == 0p ) ) { // => malloc( size ) 1355 return doMalloc( size STAT_ARG( REALLOC ) ); 1356 } // if 1357 1358 PROLOG( REALLOC, doFree( oaddr ) ); // => free( oaddr ) 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 1359 1017 1360 1018 Heap.Storage.Header * header; 1361 Heap.FreeHeader * free Head;1019 Heap.FreeHeader * freeElem; 1362 1020 size_t bsize, oalign; 1363 headers( "realloc", oaddr, header, free Head, bsize, oalign );1021 headers( "realloc", oaddr, header, freeElem, bsize, oalign ); 1364 1022 1365 1023 size_t odsize = DataStorage( bsize, oaddr, header ); // data storage available in bucket … … 1367 1025 bool ozfill = ZeroFillBit( header ); // old allocation zero filled 1368 1026 if ( unlikely( size <= odsize ) && odsize <= size * 2 ) { // allow up to 50% wasted storage 1369 #ifdef __CFA_DEBUG__ 1370 incUnfreed( size - header->kind.real.size ); // adjustment off the size difference 1371 #endif // __CFA_DEBUG__ 1372 header->kind.real.size = size; // reset allocation size 1027 header->kind.real.size = size; // reset allocation size 1373 1028 if ( unlikely( ozfill ) && size > osize ) { // previous request zero fill and larger ? 1374 1029 memset( (char *)oaddr + osize, '\0', size - osize ); // initialize added storage 1375 1030 } // if 1376 #ifdef __STATISTICS__1377 incCalls( REALLOC );1378 #endif // __STATISTICS__1379 1031 return oaddr; 1380 1032 } // if 1381 1033 1034 #ifdef __STATISTICS__ 1035 __atomic_add_fetch( &stats.realloc_storage_request, size, __ATOMIC_SEQ_CST ); 1036 #endif // __STATISTICS__ 1037 1382 1038 // change size and copy old content to new storage 1383 1039 1384 1040 void * naddr; 1385 if ( likely( oalign <= libAlign() ) ) { // previous request not aligned ?1386 naddr = doMalloc( size STAT_ARG( REALLOC ) );// create new area1041 if ( likely( oalign == libAlign() ) ) { // previous request not aligned ? 1042 naddr = mallocNoStats( size ); // create new area 1387 1043 } else { 1388 naddr = memalignNoStats( oalign, size STAT_ARG( REALLOC ) ); // create new aligned area 1389 } // if 1390 1391 headers( "realloc", naddr, header, freeHead, bsize, oalign ); 1392 // To preserve prior fill, the entire bucket must be copied versus the size. 1044 naddr = memalignNoStats( oalign, size ); // create new aligned area 1045 } // if 1046 1047 headers( "realloc", naddr, header, freeElem, bsize, oalign ); 1393 1048 memcpy( naddr, oaddr, min( osize, size ) ); // copy bytes 1394 doFree( oaddr ); // free previous storage1049 free( oaddr ); 1395 1050 1396 1051 if ( unlikely( ozfill ) ) { // previous request zero fill ? … … 1412 1067 // Same as malloc() except the memory address is a multiple of alignment, which must be a power of two. (obsolete) 1413 1068 void * memalign( size_t alignment, size_t size ) libcfa_public { 1414 return memalignNoStats( alignment, size STAT_ARG( MEMALIGN ) ); 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 ); 1415 1079 } // memalign 1416 1080 … … 1418 1082 // Same as aalloc() with memory alignment. 1419 1083 void * amemalign( size_t alignment, size_t dim, size_t elemSize ) libcfa_public { 1420 return memalignNoStats( alignment, dim * elemSize STAT_ARG( AMEMALIGN ) ); 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 ); 1421 1095 } // amemalign 1422 1096 … … 1425 1099 void * cmemalign( size_t alignment, size_t dim, size_t elemSize ) libcfa_public { 1426 1100 size_t size = dim * elemSize; 1427 char * addr = (char *)memalignNoStats( alignment, size STAT_ARG( CMEMALIGN ) ); 1428 1429 if ( unlikely( addr == NULL ) ) return NULL; // stop further processing if 0p is returned 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 ); 1430 1113 1431 1114 Heap.Storage.Header * header; 1432 Heap.FreeHeader * free Head;1115 Heap.FreeHeader * freeElem; 1433 1116 size_t bsize; 1434 1117 … … 1436 1119 bool mapped = 1437 1120 #endif // __CFA_DEBUG__ 1438 headers( "cmemalign", addr, header, free Head, bsize, alignment );1121 headers( "cmemalign", addr, header, freeElem, bsize, alignment ); 1439 1122 1440 1123 // Mapped storage is zero filled, but in debug mode mapped memory is scrubbed in doMalloc, so it has to be reset to zero. … … 1486 1169 // 0p, no operation is performed. 1487 1170 void free( void * addr ) libcfa_public { 1488 // verify( heapManager );1489 1490 1171 if ( unlikely( addr == 0p ) ) { // special case 1491 1172 #ifdef __STATISTICS__ 1492 if ( heapManager ) 1493 incZeroCalls( FREE ); 1173 __atomic_add_fetch( &stats.free_null_calls, 1, __ATOMIC_SEQ_CST ); 1494 1174 #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 // } // if 1182 // #endif // __CFA_DEBUG__ 1495 1183 return; 1496 } // if 1497 1498 #ifdef __STATISTICS__ 1499 incCalls( FREE ); 1500 #endif // __STATISTICS__ 1501 1502 doFree( addr ); // handles heapManager == nullptr 1184 } // exit 1185 1186 doFree( addr ); 1503 1187 } // free 1504 1188 … … 1543 1227 if ( unlikely( addr == 0p ) ) return 0; // null allocation has 0 size 1544 1228 Heap.Storage.Header * header; 1545 Heap.FreeHeader * free Head;1229 Heap.FreeHeader * freeElem; 1546 1230 size_t bsize, alignment; 1547 1231 1548 headers( "malloc_usable_size", addr, header, free Head, bsize, alignment );1232 headers( "malloc_usable_size", addr, header, freeElem, bsize, alignment ); 1549 1233 return DataStorage( bsize, addr, header ); // data storage in bucket 1550 1234 } // malloc_usable_size … … 1554 1238 void malloc_stats( void ) libcfa_public { 1555 1239 #ifdef __STATISTICS__ 1556 HeapStatistics stats; 1557 HeapStatisticsCtor( stats ); 1558 if ( printStats( collectStats( stats ) ) == -1 ) { 1559 #else 1560 #define MALLOC_STATS_MSG "malloc_stats statistics disabled.\n" 1561 if ( write( STDERR_FILENO, MALLOC_STATS_MSG, sizeof( MALLOC_STATS_MSG ) - 1 /* size includes '\0' */ ) == -1 ) { 1562 #endif // __STATISTICS__ 1563 abort( "write failed in malloc_stats" ); 1564 } // if 1240 printStats(); 1241 if ( prtFree() ) prtFree( heapManager ); 1242 #endif // __STATISTICS__ 1565 1243 } // malloc_stats 1566 1244 … … 1569 1247 int malloc_stats_fd( int fd __attribute__(( unused )) ) libcfa_public { 1570 1248 #ifdef __STATISTICS__ 1571 int temp = heapMaster.stats_fd;1572 heapMaster.stats_fd = fd;1249 int temp = stats_fd; 1250 stats_fd = fd; 1573 1251 return temp; 1574 1252 #else … … 1584 1262 if ( options != 0 ) { errno = EINVAL; return -1; } 1585 1263 #ifdef __STATISTICS__ 1586 HeapStatistics stats; 1587 HeapStatisticsCtor( stats ); 1588 return printStatsXML( collectStats( stats ), stream ); // returns bytes written or -1 1264 return printStatsXML( stream ); 1589 1265 #else 1590 1266 return 0; // unsupported … … 1599 1275 choose( option ) { 1600 1276 case M_TOP_PAD: 1601 heap Master.heapExpand = ceiling2( value, __page_size );1277 heapExpand = ceiling2( value, __page_size ); 1602 1278 return 1; 1603 1279 case M_MMAP_THRESHOLD: … … 1643 1319 // Must have CFA linkage to overload with C linkage realloc. 1644 1320 void * resize( void * oaddr, size_t nalign, size_t size ) libcfa_public { 1645 if ( unlikely( oaddr == 0p ) ) { // => malloc( size ) 1646 return memalignNoStats( nalign, size STAT_ARG( RESIZE ) ); 1647 } // if 1648 1649 PROLOG( RESIZE, doFree( oaddr ) ); // => free( oaddr ) 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; 1328 } // if 1329 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 1650 1342 1651 1343 // Attempt to reuse existing alignment. … … 1655 1347 1656 1348 if ( unlikely( isFakeHeader ) ) { 1657 checkAlign( nalign ); // check alignment1658 1349 oalign = ClearAlignmentBit( header ); // old alignment 1659 1350 if ( unlikely( (uintptr_t)oaddr % nalign == 0 // lucky match ? … … 1662 1353 ) ) { 1663 1354 HeaderAddr( oaddr )->kind.fake.alignment = MarkAlignmentBit( nalign ); // update alignment (could be the same) 1664 Heap.FreeHeader * free Head;1355 Heap.FreeHeader * freeElem; 1665 1356 size_t bsize, oalign; 1666 headers( "resize", oaddr, header, free Head, bsize, oalign );1357 headers( "resize", oaddr, header, freeElem, bsize, oalign ); 1667 1358 size_t odsize = DataStorage( bsize, oaddr, header ); // data storage available in bucket 1668 1359 … … 1670 1361 HeaderAddr( oaddr )->kind.fake.alignment = MarkAlignmentBit( nalign ); // update alignment (could be the same) 1671 1362 ClearZeroFillBit( header ); // turn off 0 fill 1672 #ifdef __CFA_DEBUG__1673 incUnfreed( size - header->kind.real.size ); // adjustment off the size difference1674 #endif // __CFA_DEBUG__1675 1363 header->kind.real.size = size; // reset allocation size 1676 #ifdef __STATISTICS__1677 incCalls( RESIZE );1678 #endif // __STATISTICS__1679 1364 return oaddr; 1680 1365 } // if … … 1685 1370 } // if 1686 1371 1372 #ifdef __STATISTICS__ 1373 __atomic_add_fetch( &stats.resize_storage_request, size, __ATOMIC_SEQ_CST ); 1374 #endif // __STATISTICS__ 1375 1687 1376 // change size, DO NOT preserve STICKY PROPERTIES. 1688 doFree( oaddr ); // free previous storage1689 return memalignNoStats( nalign, size STAT_ARG( RESIZE ) );// create new aligned area1377 free( oaddr ); 1378 return memalignNoStats( nalign, size ); // create new aligned area 1690 1379 } // resize 1691 1380 1692 1381 1693 1382 void * realloc( void * oaddr, size_t nalign, size_t size ) libcfa_public { 1694 if ( unlikely( oaddr == 0p ) ) { // => malloc( size ) 1695 return memalignNoStats( nalign, size STAT_ARG( REALLOC ) ); 1696 } // if 1697 1698 PROLOG( REALLOC, doFree( oaddr ) ); // => free( oaddr ) 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; 1390 } // if 1391 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 1699 1404 1700 1405 // Attempt to reuse existing alignment. … … 1703 1408 size_t oalign; 1704 1409 if ( unlikely( isFakeHeader ) ) { 1705 checkAlign( nalign ); // check alignment1706 1410 oalign = ClearAlignmentBit( header ); // old alignment 1707 1411 if ( unlikely( (uintptr_t)oaddr % nalign == 0 // lucky match ? … … 1717 1421 } // if 1718 1422 1719 Heap.FreeHeader * freeHead; 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; 1720 1429 size_t bsize; 1721 headers( "realloc", oaddr, header, free Head, bsize, oalign );1430 headers( "realloc", oaddr, header, freeElem, bsize, oalign ); 1722 1431 1723 1432 // change size and copy old content to new storage … … 1726 1435 bool ozfill = ZeroFillBit( header ); // old allocation zero filled 1727 1436 1728 void * naddr = memalignNoStats( nalign, size STAT_ARG( REALLOC ) );// create new aligned area1729 1730 headers( "realloc", naddr, header, free Head, bsize, oalign );1437 void * naddr = memalignNoStats( nalign, size ); // create new aligned area 1438 1439 headers( "realloc", naddr, header, freeElem, bsize, oalign ); 1731 1440 memcpy( naddr, oaddr, min( osize, size ) ); // copy bytes 1732 doFree( oaddr ); // free previous storage1441 free( oaddr ); 1733 1442 1734 1443 if ( unlikely( ozfill ) ) { // previous request zero fill ? … … 1742 1451 1743 1452 1744 void * reallocarray( void * oaddr, size_t nalign, size_t dim, size_t elemSize ) __THROW {1745 return realloc( oaddr, nalign, dim * elemSize );1746 } // reallocarray1747 1748 1749 1453 // Local Variables: // 1750 1454 // tab-width: 4 //
Note:
See TracChangeset
for help on using the changeset viewer.