| // *********************************************************************** |
| // @@@ START COPYRIGHT @@@ |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| // |
| // @@@ END COPYRIGHT @@@ |
| // *********************************************************************** |
| // -*-C++-*- |
| // *********************************************************************** |
| // |
| // File: NAMemory.cpp |
| // Description: The implementation of NAMemory classes |
| // |
| // |
| // Created: 12/13/96 |
| // Language: C++ |
| // |
| // |
| // *********************************************************************** |
| // |
| |
| // ----------------------------------------------------------------------- |
| // In July, 2006 replaced the allocation routines in this file |
| // with routines from the public domain malloc from Doug Lea. The Doug |
| // Lea allocator is a better performer than the previous allocator and |
| // prevents some of the bad memory fragmentation problems that existed |
| // in the previous allocator. For detailed information about the Doug |
| // Lea allocator, do an Internet search on "Doug Lea malloc". |
| // |
| // Most of the structures and function names in Doug Lea's allocator |
| // are renamed to fit into the existing SQL/MX memory allocation |
| // framework. Most of the algorithms are the same, but are converted |
| // from C functions and macros to C++ member functions that are often |
| // inlined. |
| // ----------------------------------------------------------------------- |
| |
| #include <fstream> |
| |
| #include <errno.h> |
| #include "seabed/fs.h" |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <iostream> |
| |
| #include "str.h" |
| #include "ComSpace.h" |
| |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/mman.h> // For mmap support |
| #include <seabed/ms.h> |
| |
| #ifdef TRACE_DP2_MALLOCS // Only works for NT. |
| #include <fstream> |
| #endif |
| |
| #include "NAError.h" |
| #include "HeapLog.h" |
| #include "Platform.h" |
| #include "NAAssert.h" |
| |
| #include "ComRtUtils.h" |
| #include "StmtCompilationMode.h" |
| |
| #ifdef _DEBUG |
| //-------------------------------------------------------------------------// |
| // Using env variable TRACE_ALLOC_SIZE to record the call stacks when a |
| // given size of memory block was allocated but not de-allocated later. |
| // The call stacks can be dumped to a file with name given by env variable |
| // TRACE_ALLOC_SIZE appended by the process id when the heap is destructed. |
| // |
| // The tracing/logging is done via backtrace() and backtrace_symbols() |
| // calls. The idea is when a specific size (specified by TRACE_ALLOC_SIZE) |
| // is intercepted at allocation, use backtrace() and backtrace_symbols() |
| // in saveTrafStack() to get the call stack at the time. The stack info is |
| // saved to a list in the heap. When such block is freed, the stack info of |
| // that block will be removed from the list. At the time when the heap is |
| // to be destroyed, it dumps whatever left in the list via dumpTrafStack() |
| // to a file or terminal. |
| // |
| // This can be combined with a memory debug method that detects memory |
| // overflow (set the env MEMDEBUG to 2, see checkForOverFlow()). Once |
| // memory overflow is found, use this tracing method to find where the |
| // allocation was done and check if the allocation is adequate. |
| // |
| // Note that env MEMDEBUG should not be set to 2 when tracing the |
| // allocation of specified size because the user specified size would |
| // be different comparing to the allocated size due to extra bytes needed |
| // for block overflow checking. |
| // |
| // As we may delete the entire heap without deallocating each individual |
| // block we ever allocated for some heaps, the trace file may contains |
| // blocks that are ok not to be explicitly deleted. One needs to separate |
| // these from the real issues. |
| // |
| // Limitation: we don't track allocations for blocks greater than the |
| // INT_MAX |
| // |
| // All related code are in common/NAMemory.h, common/NAMemory.cpp (this file), |
| // common/ComRtUtils.h, and common/ComRtUtils.cpp |
| // |
| // Todos: |
| // 1) Re-enabling Setting memory block to 0xfa after allocates and to 0xfc |
| // after de-allocates when env MEMDEBUG is set to 2 |
| // |
| // 2) Ensure existing memory debug methods specified by env MEMDEBUG |
| // still work in multi-threaded environment |
| //-------------------------------------------------------------------------// |
| // static THREAD_P UInt32 TraceAllocSize = 0; ** defined in common/ComRtUtil.h |
| #endif // _DEBUG |
| |
| const UInt32 deallocTraceEntries = 4096; |
| struct DeallocTraceEntry |
| { |
| void * begin_; |
| void * end_; |
| }; |
| UInt32 THREAD_P deallocTraceIndex = deallocTraceEntries - 1, deallocCount = 0; |
| THREAD_P DeallocTraceEntry (*deallocTraceArray)[deallocTraceEntries] = 0; |
| |
| |
| #include "NAMemory.h" |
| #ifdef _DEBUG |
| #include "Collections.h" |
| #endif // _DEBUG |
| |
| class NAMutex |
| { |
| bool threadSafe_; |
| pthread_mutex_t *mutex_; |
| public: |
| // Do not implement a default constructor. This will generate a link |
| // error if anyone tries to call it. |
| NAMutex(); |
| |
| NAMutex(bool threadSafe, pthread_mutex_t *mutex) |
| : threadSafe_(threadSafe), |
| mutex_(mutex) |
| { |
| if (threadSafe_) |
| { |
| int rc = pthread_mutex_lock(mutex_); |
| assert(rc == 0); |
| } |
| } |
| ~NAMutex() |
| { |
| if (threadSafe_) |
| { |
| int rc = pthread_mutex_unlock(mutex_); |
| assert(rc == 0); |
| } |
| } |
| }; |
| |
| |
| #include "logmxevent.h" |
| |
| #define NO_MERGE 0x0 |
| #define BACKWARD_MERGE 0x1 |
| #define FORWARD_MERGE 0x2 |
| |
| #include "logmxevent.h" |
| |
| extern short getRTSSemaphore(); // Functions implemented in SqlStats.cpp |
| extern void releaseRTSSemaphore(); |
| extern NABoolean checkIfRTSSemaphoreLocked(); |
| extern void updateMemStats(); |
| extern SB_Phandle_Type *getMySsmpPhandle(); |
| |
| //CollHeap *CTXTHEAP__ = NULL; // a global, set by CmpContext ctor and used by |
| // Collections.cpp allocate method |
| const size_t DEFAULT_NT_HEAP_INIT_SIZE = 524288; |
| const size_t DEFAULT_NT_HEAP_MAX_SIZE = (size_t)-1; // by default, heaps have no max size |
| const size_t DEFAULT_NT_HEAP_INCR_SIZE = 524288; |
| |
| // MIN_MMAP_ALLOC_SIZE defines the minimum user size that will be used to |
| // allocate a NABlock using mmap(). |
| const size_t MIN_MMAP_ALLOC_SIZE = 524288; |
| |
| // Ignore all HEAPLOG stuff if MUSE |
| #if defined(MUSE) |
| #undef HEAPLOG_ON |
| #undef HEAPLOG_OFF |
| #undef HEAPLOG_REINITIALIZE |
| #undef HEAPLOG_ADD_ENTRY |
| #undef HEAPLOG_DELETE_ENTRY |
| #define HEAPLOG_ON() |
| #define HEAPLOG_OFF() |
| #define HEAPLOG_REINITIALIZE(a) |
| #define HEAPLOG_ADD_ENTRY(a, b, c, d) |
| #define HEAPLOG_DELETE_ENTRY(a, b) |
| #endif |
| |
| // **************************************************************************** |
| // Numeric constants that are local to this file |
| // **************************************************************************** |
| const short NA_FIRST_NONPRIV_SEG_ID = 1600; |
| const short NA_FIRST_IPC_SEG_ID = 2150; |
| const size_t MEGABYTE = 1024 * 1024; |
| const size_t MAXFLATSEGSIZE = 128 * 1024 * 1024; |
| const size_t DEFAULT_THRESHOLD_BLOCK_COUNT = 10; |
| const size_t DEFAULT_MAX_INCREMENT = 4194304; |
| |
| #if defined (NA_YOS) || defined (NA_YOS_SIMULATION) |
| #define FRAG_BYTE_ALIGN 16 |
| #else |
| #define FRAG_BYTE_ALIGN 8 |
| #endif |
| #define FRAG_ALIGN_MASK (FRAG_BYTE_ALIGN - 1) |
| |
| #define HEAPFRAGMENT_SIZE sizeof(NAHeapFragment) |
| #define FRAGMENT_OVERHEAD sizeof(size_t) |
| #define MIN_FRAGMENT_SIZE ((HEAPFRAGMENT_SIZE + FRAG_ALIGN_MASK) & ~FRAG_ALIGN_MASK) |
| |
| // PAD_REQUEST is used to calculated the needed fragment size based on |
| // a requested user size. The "sizeof(size_t)" is added to the requested |
| // size to account for the "head_" field in the NAHeapSegment, and the |
| // sum is rounded up to the next byte aligned boundary (of either 8 or 16). |
| #define PAD_REQUEST(req) (((req) + sizeof(size_t) + FRAG_ALIGN_MASK) \ |
| & ~FRAG_ALIGN_MASK) |
| |
| // ALIGN_OFFSET is used to determine the alignment of the first fragment |
| // given the memory address A. The memory address A is passed from the caller |
| // as the first location that memory could start. If necessary, ALIGN_OFFSET |
| // will return a value that indicates how many additional bytes are needed |
| // to cause the memory to be aligned properly. |
| #define ALIGN_OFFSET(A) \ |
| ((((size_t)(A) & FRAG_ALIGN_MASK) == 0)? 0 :\ |
| ((FRAG_BYTE_ALIGN - ((size_t)(A) & FRAG_ALIGN_MASK)) & FRAG_ALIGN_MASK)) |
| |
| #define BLOCK_OVERHEAD (sizeof(NABlock) + 2 * sizeof(size_t)) |
| |
| #define MAX_REQUEST (0x7fffffffffffff - FRAG_ALIGN_MASK - FRAGMENT_OVERHEAD) |
| #define MIN_REQUEST (MIN_FRAGMENT_SIZE - FRAGMENT_OVERHEAD - 1) |
| #define SMALLBIN_SHIFT 3 |
| #define TREEBIN_SHIFT 8 |
| |
| #define MIN_LARGE_SIZE (1 << TREEBIN_SHIFT) |
| #define MAX_SMALL_SIZE (MIN_LARGE_SIZE - 1) |
| #define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - FRAG_ALIGN_MASK - FRAGMENT_OVERHEAD) |
| #define MAX_SIZE_T (~(size_t)0) |
| #define SIZE_T_BITSIZE (sizeof(size_t) << 3) |
| |
| #define CMFAIL ((char*)(MAX_SIZE_T)) |
| |
| // **************************************************************************** |
| // Some helper macros from Doug Lea's malloc. |
| |
| #define MIN_SMALL_INDEX (small_index(MIN_FRAGMENT_SIZE)) |
| |
| // addressing by index. See above about smallbin repositioning |
| |
| #if ( defined(_DEBUG) || defined(NSK_MEMDEBUG) ) |
| #define RTCHECK(e) (e) |
| #define CORRUPTION_ERROR_ACTION assert(0) |
| #define USAGE_ERROR_ACTION assert(0) |
| #define allocDebugProcess(P, S) HEAPLOG_ADD_ENTRY(P->getMemory(), \ |
| (Lng32)userSize, heapID_.heapNum, \ |
| getName()); \ |
| if (debugLevel_) doAllocDebugProcess(P,S); |
| #define deallocDebugProcess(P) if (debugLevel_) doDeallocDebugProcess(P) |
| #define checkFreeFragment(P) doCheckFreeFragment(P) |
| #define checkInuseFragment(P) doCheckInuseFragment(P) |
| #define checkTopFragment(P) doCheckTopFragment(P) |
| #define checkMallocedFragment(P,N) doCheckMallocedFragment(P,N) |
| #define checkMallocState() doCheckMallocState() |
| #else |
| // If debugging is not turned on, then don't do any verification |
| #define RTCHECK(e) (1) |
| #define CORRUPTION_ERROR_ACTION |
| #define USAGE_ERROR_ACTION |
| #define allocDebugProcess(P, S) |
| #define deallocDebugProcess(P) |
| #define checkFreeFragment(P) |
| #define checkInuseFragment(P) |
| #define checkTopFragment(P) |
| #define checkMallocedFragment(P,N) |
| #define checkMallocState() |
| #endif // !( defined(_DEBUG) || defined(NSK_MEMDEBUG) ) |
| // **************************************************************************** |
| |
| #ifdef NA_YOS_SIMULATION |
| // On Windows NT, use malloc16() and free16() to aligned memory like it |
| // is on NSK. |
| void * malloc16(Int32 size) |
| { |
| Lng32 startAddr, retAddr, *saveStartAddr; |
| startAddr = (Lng32)malloc(size + 20); |
| if (startAddr == 0) |
| return (void *) NULL; |
| retAddr = startAddr + 4; |
| retAddr = (((retAddr - 1) / 16 + 1) * 16); |
| saveStartAddr = (Lng32 *)(retAddr - 4); |
| *saveStartAddr = startAddr; |
| return (void *)retAddr; |
| } |
| |
| void free16(void *retAddr) |
| { |
| void ** startAddr = (void **)((Lng32)retAddr - 4); |
| free(*startAddr); |
| } |
| |
| // Macros to use malloc16() and free16() |
| #define malloc(x) malloc16(x) |
| #define free(x) free16(x) |
| |
| #endif // NA_YOS_SIMULATION |
| |
| #include "muse.cpp" |
| |
| |
| NABlock::NABlock() : |
| segmentId_(0), |
| size_(0), |
| next_(0), |
| sflags_(0) |
| {} |
| |
| // Does this NABlock contain the address? |
| inline NABoolean |
| NABlock::blockHolds(void *addr) |
| { |
| return ((addr >= (void*)this) && ((char*)addr < (char*)this + size_)); |
| } |
| |
| // Find the NABlock that contains an address. |
| inline NABlock* |
| NABlock::blockHolding(NABlock *firstBlock, void *addr) |
| { |
| while (firstBlock && !firstBlock->blockHolds(addr)) { |
| firstBlock = firstBlock->next_; |
| } |
| return firstBlock; |
| } |
| |
| // Return whether this NABlock allocated externally. This only occurs |
| // for EXECUTOR_MEMORY when a segment is allocated externally to the |
| // NAMemory code and passed to the NAMemory within the NAHeap constructor. |
| // An externally allocated segment will never be freed. |
| inline NABoolean |
| NABlock::isExternSegment() |
| { |
| return (sflags_ & EXTERN_BIT) != 0; |
| } |
| |
| // Return whether this NABlock was allocated using mmap(). |
| inline NABoolean |
| NABlock::isMMapped() |
| { |
| return (sflags_ & MMAP_BIT) != 0; |
| } |
| |
| // Return the offset of the first fragment in the block |
| inline size_t |
| NABlock::firstFragmentOffset() |
| { |
| NAHeapFragment *p = (NAHeapFragment*)((char*)this + sizeof(NABlock)); |
| |
| // Align the user memory |
| return sizeof(NABlock) + ALIGN_OFFSET(p->getMemory()); |
| } |
| |
| // Return a pointer to the first fragment for this NABlock. |
| inline NAHeapFragment* |
| NABlock::alignAsFragment() |
| { |
| return (NAHeapFragment*)((char*)this + firstFragmentOffset()); |
| } |
| |
| // Return the usable memory used for this NAHeapFragment. |
| inline void* |
| NAHeapFragment::getMemory(NABoolean cleanUpPrevNext) |
| { |
| // Add 2 size_t sizes to account for prevFoot_ and head_ at |
| // the beginning of the NAHeapFragment. |
| Long *memPtr = (Long*)((char*)this + (sizeof(size_t) * 2)); |
| if (cleanUpPrevNext) |
| { |
| // clean up prev_ and next_ pointers before handing this |
| // memory to the user. |
| memPtr[0] = 0; |
| memPtr[1] = 0; |
| } |
| return (void *)memPtr; |
| } |
| |
| // Convert usable memory pointer to an NAHeapFragment pointer. |
| inline NAHeapFragment* |
| NAHeapFragment::memToFragment(void *mem) |
| { |
| // Subtract 2 size_t sizes to account for prevFoot_ and head_ at |
| // the beginning of the NAHeapFragment. |
| return (NAHeapFragment*)((char*)mem - (sizeof(size_t) * 2)); |
| } |
| |
| // Return whether the CINUSE_BIT (current fragment) is set. |
| inline NABoolean |
| NAHeapFragment::cinuse() |
| { |
| return (head_ & CINUSE_BIT) != 0; |
| } |
| |
| // Return whether the previous fragment is in use. |
| inline NABoolean |
| NAHeapFragment::pinuse() |
| { |
| return (head_ & PINUSE_BIT) != 0; |
| } |
| |
| // Return whether both PINUSE_BIT and CINUSE_BIT bits are set. |
| inline NABoolean |
| NAHeapFragment::cpinuse() |
| { |
| return (head_ & INUSE_BITS) == INUSE_BITS; |
| } |
| |
| // Return the size of the NAHeapFragment |
| inline size_t |
| NAHeapFragment::fragmentSize() |
| { |
| return head_ & ~(INUSE_BITS|MMAPPED_BIT|FIRST_FRAGMENT_BIT); |
| } |
| |
| // Clear CINUSE_BIT. |
| inline void |
| NAHeapFragment::clearCinuse() |
| { |
| head_ &= ~CINUSE_BIT; |
| } |
| |
| // Clear PINUSE_BIT. |
| inline void |
| NAHeapFragment::clearPinuse() |
| { |
| head_ &= ~PINUSE_BIT; |
| } |
| |
| // Set the FIRST_FRAGMENT_BIT and prevFoot_ for the first fragment |
| // in an NABlock. The FIRST_FRAGMENT_BIT is used to determine quickly |
| // whether this fragment occupies an entire NABlock during deallocation. |
| inline void |
| NAHeapFragment::initializeFirstFragment(size_t s) |
| { |
| head_ |= FIRST_FRAGMENT_BIT; |
| prevFoot_ = s; |
| } |
| |
| // Return the status of FIRST_FRAGMENT_BIT. |
| inline size_t |
| NAHeapFragment::getFirstFragmentBit() |
| { |
| return head_ & FIRST_FRAGMENT_BIT; |
| } |
| |
| // Set the head bits of a fragment by ORing them. |
| inline void |
| NAHeapFragment::setHeadBits(size_t bits) |
| { |
| head_ |= bits; |
| } |
| |
| // Does this fragment occupy the whole NABlock? |
| inline NABoolean |
| NAHeapFragment::occupiesCompleteNABlock() |
| { |
| return ((head_ & FIRST_FRAGMENT_BIT) != 0 |
| && prevFoot_ == fragmentSize()); |
| } |
| |
| // Return the address of the header. |
| inline const size_t* |
| NAHeapFragment::getHeadAddr() |
| { |
| return &head_; |
| } |
| |
| // Set the "next_" pointer in the NAHeapFragment |
| inline void |
| NAHeapFragment::setNext(NAHeapFragment *next) |
| { |
| next_ = next; |
| } |
| |
| // Set the "prev_" pointer in the NAHeapFragment |
| inline void |
| NAHeapFragment::setPrev(NAHeapFragment *prev) |
| { |
| prev_ = prev; |
| } |
| |
| // Return the "next_" pointer. |
| inline NAHeapFragment* |
| NAHeapFragment::getNext() |
| { |
| return next_; |
| } |
| |
| // Return the "prev_" pointer. |
| inline NAHeapFragment* |
| NAHeapFragment::getPrev() |
| { |
| return prev_; |
| } |
| |
| // Return the size of the previous fragment (only valid if free). |
| inline size_t |
| NAHeapFragment::getPrevFoot() |
| { |
| return prevFoot_; |
| } |
| |
| // Adjust the size of the previous footer size. For the first fragment |
| // in an NABlock, the prevFoot_ is used to indicate the size of the block. |
| // This function is only used to adjust the size of the prevFoot_ when the |
| // block is resized. |
| inline void |
| NAHeapFragment::adjustBlockSize(Lng32 s) |
| { |
| prevFoot_ += s; |
| } |
| |
| // Return pointer offset as a greater NAHeapFragment pointer |
| inline NAHeapFragment* |
| NAHeapFragment::fragmentPlusOffset(size_t s) |
| { |
| return (NAHeapFragment*)((char*)this + s); |
| } |
| |
| // Return pointer offset as a smaller NAHeapFragment pointer |
| inline NAHeapFragment* |
| NAHeapFragment::fragmentMinusOffset(size_t s) |
| { |
| return (NAHeapFragment*)((char*)this - s); |
| } |
| |
| // Return a pointer to the next fragment |
| inline NAHeapFragment* |
| NAHeapFragment::nextFragment() |
| { |
| return (NAHeapFragment*)((char*)this |
| + (head_ & ~(INUSE_BITS|MMAPPED_BIT|FIRST_FRAGMENT_BIT))); |
| } |
| |
| // Return a pointer to the previous fragment. This code assumes |
| // that the caller has checked whether the previous fragment is |
| // not in use. |
| inline NAHeapFragment* |
| NAHeapFragment::prevFragment() |
| { |
| return (NAHeapFragment*)((char*)this - prevFoot_); |
| } |
| |
| // Return whether the next fragment has PINUSE_BIT set. |
| inline NABoolean |
| NAHeapFragment::nextPinuse() |
| { |
| return nextFragment()->pinuse(); |
| } |
| |
| // Set the size at the footer. |
| inline void |
| NAHeapFragment::setFoot(size_t s) |
| { |
| size_t size = s & ~FIRST_FRAGMENT_BIT; |
| ((NAHeapFragment*)((char*)this + size))->prevFoot_ = s; |
| } |
| |
| // Set the size, pinuse bit, and the size at the footer |
| inline void |
| NAHeapFragment::setSizeAndPinuseOfFreeFragment(size_t s) |
| { |
| head_ = (s|PINUSE_BIT); |
| setFoot(s); |
| } |
| |
| // Set size, pinuse bit, foot, and clear next pinuse |
| inline void |
| NAHeapFragment::setFreeWithPinuse(size_t s, NAHeapFragment *next) |
| { |
| next->clearPinuse(); |
| setSizeAndPinuseOfFreeFragment(s); |
| } |
| |
| // Set size, inuse and pinuse of this fragment and pinuse of next |
| // fragment. |
| inline void |
| NAHeapFragment::setInuseAndPinuse(size_t s) |
| { |
| head_ = (s|PINUSE_BIT|CINUSE_BIT); |
| size_t size = (s & ~FIRST_FRAGMENT_BIT); |
| ((NAHeapFragment*)((char*)this + size))->head_ |= PINUSE_BIT; |
| } |
| |
| // Set head to the size passed in. No inuse bits will be set. |
| inline void |
| NAHeapFragment::setSize(size_t s) |
| { |
| head_ = s; |
| } |
| |
| // Set the size and pinuse bits |
| inline void |
| NAHeapFragment::setSizeAndPinuse(size_t s) |
| { |
| head_ = (s|PINUSE_BIT); |
| } |
| |
| // Set size, cinuse and pinuse bit of this fragment. |
| inline void |
| NAHeapFragment::setSizeAndPinuseOfInuseFragment(size_t s) |
| { |
| head_ = (s|PINUSE_BIT|CINUSE_BIT); |
| } |
| |
| // Return whether the head is a fencepost |
| inline NABoolean |
| NAHeapFragment::isFencePost() |
| { |
| return (head_ == FENCEPOST_HEAD || |
| (head_ == (sizeof(size_t) | CINUSE_BIT) && |
| (fragmentPlusOffset(sizeof(size_t)))->head_ == FENCEPOST_HEAD)); |
| } |
| |
| // Set fenceposts after the top fragment |
| inline void |
| NAHeapFragment::setFencePosts() |
| { |
| head_ = sizeof(size_t) | CINUSE_BIT; |
| NAHeapFragment *next = fragmentPlusOffset(sizeof(size_t)); |
| next->head_ = FENCEPOST_HEAD; |
| } |
| |
| // Return whether the MMAPPED_BIT is set. This bit is only set when a |
| // NABlock contains a single large fragment and the NABlock was allocated |
| // using mmap(). |
| inline NABoolean |
| NAHeapFragment::isMMapped() |
| { |
| return (head_ & MMAPPED_BIT) != 0; |
| } |
| |
| // Set the size in the head with the MMAP bit and initialize the trailing |
| // fenceposts. |
| inline void |
| NAHeapFragment::setSizeOfMMapFragment(size_t size) |
| { |
| head_ = (size|PINUSE_BIT|CINUSE_BIT|MMAPPED_BIT); |
| ((NAHeapFragment*)((char*)this + size))->head_ = FENCEPOST_HEAD; |
| ((NAHeapFragment*)((char*)this + size + sizeof(size_t)))->head_ = FENCEPOST_HEAD; |
| } |
| |
| // mark free pages in this fragment as "non-dirty". Benefits are: |
| // 1) pages will become readily stealable if needed by the OS. |
| // 2) When stolen, OS will not have to swap the contents to the disk. |
| inline void |
| NAHeapFragment::cleanFreePages(size_t fragSize) |
| { |
| } |
| |
| // Get either the left or right child of an NATreeFragment |
| inline NATreeFragment* |
| NATreeFragment::getChild(UInt32 childNo) |
| { |
| return child_[childNo]; |
| } |
| |
| // Get address of either the left or right child of an NATreeFragment |
| inline NATreeFragment** |
| NATreeFragment::getChildAddr(UInt32 childNo) |
| { |
| return &child_[childNo]; |
| } |
| |
| // Get the leftmost child of an NATreeFragment |
| inline NATreeFragment* |
| NATreeFragment::leftmostChild() |
| { |
| return (child_[0] != 0 ? child_[0] : child_[1]); |
| } |
| |
| // Set either the left or right child of an NATreeFragment |
| inline void |
| NATreeFragment::setChild(Int32 childNo, NATreeFragment *p) |
| { |
| child_[childNo] = p; |
| } |
| |
| // Get the parent of an NATreeFragment |
| inline NATreeFragment* |
| NATreeFragment::getParent() |
| { |
| return parent_; |
| } |
| |
| // Set the parent of an NATreeFragment |
| inline void |
| NATreeFragment::setParent(NATreeFragment *p) |
| { |
| parent_ = p; |
| } |
| |
| // Return the index of an NATreeFragment. This is the index into |
| // NAHeap.treebins_[]. |
| inline NAHeap::bindex_t |
| NATreeFragment::getIndex() |
| { |
| return index_; |
| } |
| |
| // Set the index of the NATreeFragment. |
| inline void |
| NATreeFragment::setIndex(NAHeap::bindex_t idx) |
| { |
| index_ = idx; |
| } |
| |
| // Get/Set the freedNSKMemory_ flag |
| |
| inline short |
| NATreeFragment::getFreedNSKMemory() |
| { |
| return freedNSKMemory_; |
| } |
| |
| inline void |
| NATreeFragment::setFreedNSKMemory(short value) |
| { |
| freedNSKMemory_ = value; |
| } |
| |
| Lng32 NAMemory::getVmSize() |
| { |
| pid_t myPid; |
| char fileName[32], buffer[1024], *currPtr; |
| size_t bytesRead; |
| Int32 success; |
| ULng32 memSize; // VMSize in KB |
| if (procStatusFile_ == 0) |
| { |
| myPid = getpid(); |
| sprintf(fileName, "/proc/%d/status", myPid); |
| procStatusFile_ = fopen(fileName, "r"); |
| char *envVar = getenv("EXECUTOR_VM_RESERVE_SIZE"); |
| if (envVar) |
| executorVmReserveSize_ = atol(envVar); |
| else |
| executorVmReserveSize_ = 256; |
| } |
| else |
| success = fseek(procStatusFile_, 0, SEEK_SET); |
| bytesRead = fread(buffer, 1, 1024, procStatusFile_); |
| currPtr = strstr(buffer, "VmSize"); |
| sscanf(currPtr, "%*s %u kB", &memSize); |
| lastVmSize_ = memSize; |
| if (memSize > maxVmSize_) |
| maxVmSize_ = memSize; |
| return memSize; |
| } |
| |
| void NAMemory::allocationIncrement(Lng32 increment) |
| { |
| allocationDelta_ += increment; |
| if (allocationDelta_ >= 128 * 1024 * 1024) |
| { |
| allocationDelta_ = 0ll; |
| Lng32 vmSize = getVmSize(); |
| if (vmSize >= (4 * 1024 * 1024) - ((128 + executorVmReserveSize_) * 1024)) |
| crowdedTotalSize_ = (Int64)vmSize * 1024; |
| } |
| } |
| |
| void NAMemory::allocationDecrement(Lng32 decrement) |
| { |
| allocationDelta_ -= decrement; |
| if (allocationDelta_ <= -128 * 1024 * 1024) |
| { |
| allocationDelta_ = 0ll; |
| Lng32 vmSize = getVmSize(); |
| if (vmSize < (4 * 1024 * 1024) - ((128 + executorVmReserveSize_) * 1024)) |
| crowdedTotalSize_ = 0ll; |
| } |
| } |
| |
| NABlock* |
| NAMemory::allocateMMapBlock(size_t s) |
| { |
| NABlock *blk; |
| blk = (NABlock*)mmap(0, s, (PROT_READ|PROT_WRITE), |
| (MAP_PRIVATE|MAP_ANONYMOUS), -1, 0); |
| if (blk == (NABlock*)-1) |
| { |
| mmapErrno_ = errno; |
| return NULL; |
| } |
| else |
| allocationIncrement(s); |
| |
| // one more block allocated |
| blockCnt_++; |
| totalSize_+= s; |
| |
| blk->segmentId_ = 0; |
| blk->size_ = s; |
| blk->sflags_ = NABlock::MMAP_BIT; |
| |
| return blk; |
| } |
| |
| // Free a NABlock back to the operating system using munmap() or the equivalent. |
| // The caller should have already modified the statistics on NAMemory before |
| // calling this function. |
| inline void |
| NAMemory::deallocateMMapBlock(NABlock *blk) |
| { |
| allocationDecrement(blk->size_); |
| Int32 munmapRetVal; |
| munmapRetVal = munmap((void*)blk, blk->size_); |
| if (munmapRetVal == -1) |
| munmapErrno_ = errno; |
| } |
| |
| // Either the correct call to free the memory used by a NABlock. This function |
| // prevents a lot of extra #ifdef code in later sections of code. |
| inline void |
| NAMemory::sysFreeBlock(NABlock *blk) |
| { |
| if (blk->isMMapped()) |
| deallocateMMapBlock(blk); |
| else |
| free((void*)blk); |
| } |
| |
| #ifndef MUSE |
| NAMemory::NAMemory(const char * name) |
| : |
| type_(EXECUTOR_MEMORY), |
| maximumSize_((size_t)-1), // no maximum |
| parent_(NULL), |
| firstBlk_(NULL), |
| allocSize_(0), |
| upperLimit_(0), |
| highWaterMark_(0), |
| intervalWaterMark_(0), |
| allocCnt_(0), |
| totalSize_(0), |
| blockCnt_(0), |
| thBlockCnt_(DEFAULT_THRESHOLD_BLOCK_COUNT), |
| memoryList_(NULL), |
| lastListEntry_(NULL), |
| nextEntry_(NULL), |
| exhaustedMem_(FALSE), |
| errorsMask_(0), |
| crowdedTotalSize_(0ll) |
| , allocationDelta_(0ll) |
| , procStatusFile_(NULL) |
| , mmapErrno_(0) |
| , munmapErrno_(0) |
| , lastVmSize_(0l) |
| , maxVmSize_(0l) |
| , sharedMemory_(FALSE) |
| , heapStartAddr_(NULL) |
| , heapStartOffset_(NULL) |
| { |
| setType(type_, 0); |
| #if ( defined(_DEBUG) || defined(NSK_MEMDEBUG) ) |
| char * debugLevel = getenv("MEMDEBUG"); |
| if (debugLevel) |
| debugLevel_ = (Lng32)atoi(debugLevel); |
| else |
| debugLevel_ = 0; |
| #else // Release build with no debugging |
| debugLevel_ = 0; |
| #endif |
| if (name != NULL) |
| setName(name); |
| else |
| setName("Unnamed memory"); |
| |
| // need to initialize an NAStringRef object "on top" of the array |
| // (don't touch this unless you know what you're doing!) |
| NAStringRef * tmp = |
| new ( (void*) (&nullNAStringRep_[3]) ) |
| NAStringRef (NAStringRef::NULL_CTOR, this) ; |
| } |
| |
| NAMemory::NAMemory(const char * name, NAHeap * parent, size_t blockSize, |
| size_t upperLimit) |
| : |
| type_(DERIVED_MEMORY), |
| maximumSize_((size_t)-1), // no maximum |
| parent_(parent), |
| firstBlk_(NULL), |
| allocSize_(0), |
| upperLimit_(upperLimit), |
| highWaterMark_(0), |
| intervalWaterMark_(0), |
| allocCnt_(0), |
| totalSize_(0), |
| blockCnt_(0), |
| thBlockCnt_(DEFAULT_THRESHOLD_BLOCK_COUNT), |
| memoryList_(NULL), |
| lastListEntry_(NULL), |
| nextEntry_(NULL), |
| exhaustedMem_(FALSE), |
| errorsMask_(0), |
| crowdedTotalSize_(0ll) |
| , allocationDelta_(0ll) |
| , procStatusFile_(NULL) |
| , mmapErrno_(0) |
| , munmapErrno_(0) |
| , lastVmSize_(0l) |
| , maxVmSize_(0l) |
| , sharedMemory_(FALSE) |
| , heapStartAddr_(NULL) |
| , heapStartOffset_(NULL) |
| { |
| if (parent_->getSharedMemory()) |
| setSharedMemory(); |
| // a derived memory has to have a parent from which it is derived |
| assert(parent_); |
| #if ( defined(_DEBUG) || defined(NSK_MEMDEBUG) ) |
| char * debugLevel = getenv("MEMDEBUG"); |
| if (debugLevel) |
| debugLevel_ = (Lng32)atoi(debugLevel); |
| else |
| debugLevel_ = 0; |
| #else // Release build with no debugging |
| debugLevel_ = 0; |
| #endif |
| setName(name); |
| |
| if (blockSize <= 0) |
| // illegal block size. Use default of 0.5 MB (adjusted). |
| blockSize = (Lng32)524288; |
| initialSize_ = incrementSize_ = blockSize; |
| |
| // need to initialize an NAStringRef object "on top" of the array |
| // (don't touch this unless you know what you're doing!) |
| NAStringRef * tmp = |
| new ( (void*) (&nullNAStringRep_[3]) ) |
| NAStringRef (NAStringRef::NULL_CTOR, this) ; |
| } |
| |
| NAMemory::NAMemory(const char * name, NAMemoryType type, size_t blockSize, |
| size_t upperLimit) |
| : |
| type_(type), |
| maximumSize_((size_t)-1), // no maximum |
| parent_(NULL), |
| firstBlk_(NULL), |
| allocSize_(0), |
| upperLimit_(upperLimit), |
| highWaterMark_(0), |
| intervalWaterMark_(0), |
| allocCnt_(0), |
| totalSize_(0), |
| blockCnt_(0), |
| thBlockCnt_(DEFAULT_THRESHOLD_BLOCK_COUNT), |
| memoryList_(NULL), |
| lastListEntry_(NULL), |
| nextEntry_(NULL), |
| exhaustedMem_(FALSE), |
| errorsMask_(0), |
| crowdedTotalSize_(0ll) |
| , allocationDelta_(0ll) |
| , procStatusFile_(NULL) |
| , mmapErrno_(0) |
| , munmapErrno_(0) |
| , lastVmSize_(0l) |
| , maxVmSize_(0l) |
| , sharedMemory_(FALSE) |
| , heapStartAddr_(NULL) |
| , heapStartOffset_(NULL) |
| { |
| // call setType to initialize the values of all the sizes |
| setType(type_, blockSize); |
| #if ( defined(_DEBUG) || defined(NSK_MEMDEBUG) ) |
| char * debugLevel = getenv("MEMDEBUG"); |
| if (debugLevel) |
| debugLevel_ = (Lng32)atoi(debugLevel); |
| else |
| debugLevel_ = 0; |
| #else // Release build with no debugging |
| debugLevel_ = 0; |
| #endif |
| setName(name); |
| |
| // need to initialize an NAStringRef object "on top" of the array |
| // (don't touch this unless you know what you're doing!) |
| NAStringRef * tmp = |
| new ( (void*) (&nullNAStringRep_[3]) ) |
| NAStringRef (NAStringRef::NULL_CTOR, this) ; |
| } |
| |
| NAMemory::NAMemory(const char * name, |
| SEG_ID segmentId, |
| void * baseAddr, |
| off_t heapStartOffset, |
| size_t maxSize) |
| : |
| type_(EXECUTOR_MEMORY), |
| parent_(NULL), |
| firstBlk_(NULL), |
| allocSize_(0), |
| upperLimit_(0), |
| highWaterMark_(0), |
| intervalWaterMark_(0), |
| allocCnt_(0), |
| totalSize_(0), |
| blockCnt_(0), |
| thBlockCnt_(DEFAULT_THRESHOLD_BLOCK_COUNT), |
| memoryList_(NULL), |
| lastListEntry_(NULL), |
| nextEntry_(NULL), |
| exhaustedMem_(FALSE), |
| errorsMask_(0), |
| crowdedTotalSize_(0ll) |
| , allocationDelta_(0ll) |
| , procStatusFile_(NULL) |
| , mmapErrno_(0) |
| , munmapErrno_(0) |
| , lastVmSize_(0l) |
| , maxVmSize_(0l) |
| , sharedMemory_(FALSE) |
| , heapStartOffset_(heapStartOffset) |
| , heapStartAddr_(baseAddr) |
| { |
| // call setType to initialize the values of all the sizes |
| setType(type_, 0); |
| |
| #if ( defined(_DEBUG) || defined(NSK_MEMDEBUG) ) |
| char * debugLevel = getenv("MEMDEBUG"); |
| if (debugLevel) |
| debugLevel_ = (Lng32)atoi(debugLevel); |
| else |
| debugLevel_ = 0; |
| #else // Release build with no debugging |
| debugLevel_ = 0; |
| #endif |
| |
| setName(name); |
| |
| // If a first segment was passed in and there is anough usable |
| // space in the segment, then initialize the firstBlk_ within |
| // the passed in memory. The NAHeap constructor will initialize |
| // the top NAHeapFragment. |
| if (heapStartAddr_ != NULL) { |
| blockCnt_ = 1; |
| size_t tsize = maxSize - heapStartOffset_ - BLOCK_OVERHEAD; |
| if (tsize > (8 * sizeof(size_t))) { |
| firstBlk_ = (NABlock*)((char*)heapStartAddr_ + heapStartOffset_); |
| firstBlk_->size_ = maxSize - heapStartOffset_; |
| firstBlk_->sflags_ = NABlock::EXTERN_BIT; |
| firstBlk_->next_ = NULL; |
| firstBlk_->segmentId_ = segmentId; |
| totalSize_ = initialSize_ = maximumSize_ = firstBlk_->size_; |
| } |
| } |
| upperLimit_ = maxSize; |
| // need to initialize an NAStringRef object "on top" of the array |
| // (don't touch this unless you know what you're doing!) |
| NAStringRef * tmp = |
| new ( (void*) (&nullNAStringRep_[3]) ) |
| NAStringRef (NAStringRef::NULL_CTOR, this) ; |
| } |
| |
| |
| void NAMemory::reInitialize() |
| { |
| // delete all blocks allocated for this heap and re-set the heap |
| // to the initial values |
| |
| HEAPLOG_REINITIALIZE(heapID_.heapNum) |
| |
| NABlock *externSegment = NULL; |
| NABlock *p = firstBlk_; |
| |
| if (p != NULL) { |
| |
| switch (type_) { |
| case EXECUTOR_MEMORY: |
| case SYSTEM_MEMORY: |
| case IPC_MEMORY: |
| { |
| while (p) { |
| assert(!p->isExternSegment()); |
| NABlock *q = p->next_; |
| sysFreeBlock(p); |
| p = q; |
| } |
| } |
| break; |
| case DERIVED_FROM_SYS_HEAP: |
| { |
| while (p) { |
| NABlock *q = p->next_; |
| sysFreeBlock(p); |
| p = q; |
| } |
| } |
| break; |
| |
| case DERIVED_MEMORY: |
| { |
| // make sure that we have a parent |
| assert(parent_); |
| HEAPLOG_OFF() // no recursive logging. (eric) |
| |
| // This code provides mutual exclusion for the runtime stats shared |
| // memory segment. |
| short semRetcode = 0; |
| if (parent_->getType() == EXECUTOR_MEMORY && getSharedMemory()) { |
| semRetcode = getRTSSemaphore(); |
| } |
| while (p) { |
| NABlock *q = p->next_; |
| parent_->deallocateHeapMemory((void*)p); |
| p = q; |
| } |
| if (semRetcode == 1) |
| releaseRTSSemaphore(); |
| if (parent_->type_ == NAMemory::EXECUTOR_MEMORY && |
| parent_->parent_ == NULL) |
| { |
| updateMemStats(); |
| } |
| HEAPLOG_ON() |
| } |
| break; |
| default: |
| assert(0); |
| } // switch(_type) |
| } // if (p != NULL) |
| |
| // reinitialize all data members. |
| allocSize_ = highWaterMark_ = intervalWaterMark_ = allocCnt_ = |
| totalSize_ = blockCnt_ = 0; |
| crowdedTotalSize_ = 0ll; |
| thBlockCnt_ = DEFAULT_THRESHOLD_BLOCK_COUNT; |
| incrementSize_ = initialSize_; |
| |
| if (externSegment == NULL) { |
| firstBlk_ = NULL; |
| } else { |
| // This code is not executed by the current code, but exists so |
| // it would be possible to call reInitialize() on the EXECUTOR_MEMORY |
| // or any other type of memory that is allocated externally. The code |
| // in NAHeap::reInitializeHeap() will make the necessary calls to |
| // reinitialize the top fragment. |
| firstBlk_ = externSegment; |
| firstBlk_->next_ = NULL; |
| blockCnt_ = 1; |
| totalSize_ = firstBlk_->size_ - heapStartOffset_; |
| } |
| |
| // If this is an NAHeap, then call reInitializeHeap() to reinitialize |
| // the NAHeap fields. This must be called after reInitializing the |
| // NAMemory fields because code in reInitializeHeap() depends on setting |
| // firstBlk_ correctly. |
| if (derivedClass_ == NAHEAP_CLASS) |
| ((NAHeap*)this)->reInitializeHeap(); |
| } |
| |
| NAMemory::~NAMemory() |
| { |
| |
| } |
| |
| void NAMemory::setType(NAMemoryType type, Lng32 blockSize) |
| { |
| // upperLimit_ must be zero for EXECUTOR_MEMORY, IPC_MEMORY, SYSTEM_MEMORY, |
| |
| type_ = type; |
| |
| parent_ = NULL; |
| |
| // whenever we make changes to the following initial, increment, and |
| // maximum sizes, we should make sure, that all values are divisible |
| // by FRAG_BYTE_ALIGN. NAMemory::NAMemory also guarantees this. The |
| // only exeption from this rule is EXECUTOR_MEMORY on NSK described |
| // below. Also, if we have a maximumSize_, make sure that |
| // initialSize_ + n * incrementSize_ = maximumSize_ |
| |
| switch(type_) { |
| case EXECUTOR_MEMORY: |
| initialSize_ = DEFAULT_NT_HEAP_INIT_SIZE ; |
| maximumSize_ = DEFAULT_NT_HEAP_MAX_SIZE ; // no maximum |
| incrementSize_ = DEFAULT_NT_HEAP_INCR_SIZE ; |
| break; |
| |
| case SYSTEM_MEMORY: |
| case IPC_MEMORY: |
| // just regular memory in any other environment |
| initialSize_ = DEFAULT_NT_HEAP_INIT_SIZE ; |
| maximumSize_ = DEFAULT_NT_HEAP_MAX_SIZE ; // no maximum |
| incrementSize_ = DEFAULT_NT_HEAP_INCR_SIZE ; |
| break; |
| |
| case DERIVED_MEMORY: { |
| assert(parent_); |
| if (blockSize == 0) |
| blockSize = (Lng32)32768; |
| initialSize_ = incrementSize_ = blockSize; |
| maximumSize_ = (size_t)-1; // no maximum |
| } |
| break; |
| |
| case DERIVED_FROM_SYS_HEAP: { |
| // just regular memory in all environments. Even on NSK. Use 32K |
| // if no blockSize is given |
| if (blockSize == 0) |
| blockSize = (Lng32)32768; |
| initialSize_ = incrementSize_ = blockSize; |
| maximumSize_ = (size_t)-1; // no maximum |
| } |
| break; |
| default: |
| assert(0); |
| } |
| } |
| |
| // allocateMemory() calls the appropriate function for allocating memory based |
| // on the derivedClass. This function provides similar functionality to virtual |
| // functions, which don't work correctly with runtime stats when the NAMemory |
| // objects exist in shared memory. |
| void * NAMemory::allocateMemory(size_t size, NABoolean failureIsFatal) |
| { |
| switch (derivedClass_) |
| { |
| case NAHEAP_CLASS: |
| return ((NAHeap *)this)->allocateHeapMemory(size, failureIsFatal); |
| case COMSPACE_CLASS: |
| return ((ComSpace *)this)->allocateSpaceMemory(size, failureIsFatal); |
| case DEFAULTIPCHEAP_CLASS: |
| return ((DefaultIpcHeap *)this)->allocateIpcHeapMemory(size, failureIsFatal); |
| default: |
| assert(0); |
| } |
| return NULL; |
| } |
| |
| // deallocateMemory() frees memory based on the type of memory class this is. |
| // Virtual functions are avoided due to problems with runtime stats placing |
| // NAHeap objects within a shared memory segment. |
| void NAMemory::deallocateMemory(void* addr) |
| { |
| switch (derivedClass_) |
| { |
| case NAHEAP_CLASS: |
| ((NAHeap *)this)->deallocateHeapMemory(addr); |
| break; |
| case COMSPACE_CLASS: |
| ((ComSpace *)this)->deallocateSpaceMemory(addr); |
| break; |
| case DEFAULTIPCHEAP_CLASS: |
| ((DefaultIpcHeap *)this)->deallocateIpcHeapMemory(addr); |
| break; |
| default: |
| assert(0); |
| } |
| } |
| |
| // dump() writes memory statistics to an output file that the caller must open. |
| // It is used as an aid for debugging memory problems and for saving memory |
| // statistics during certain tests. |
| #if (defined(_DEBUG) || defined(NSK_MEMDEBUG)) |
| void NAMemory::dump(ostream* outstream, Lng32 indent) |
| { |
| switch (derivedClass_) |
| { |
| case NAHEAP_CLASS: |
| ((NAHeap *)this)->dumpHeapInfo(outstream, indent); |
| break; |
| case COMSPACE_CLASS: |
| // ((ComSpace *)this)->dumpSpaceInfo(outstream, indent); |
| break; |
| case DEFAULTIPCHEAP_CLASS: |
| ((DefaultIpcHeap *)this)->dumpIpcHeapInfo(outstream, indent); |
| break; |
| default: |
| assert(0); |
| } |
| } |
| #endif |
| |
| // incrementStats() is called when memory is allocated and a current |
| // NAHeapFragment becomes in use. |
| inline void |
| NAMemory::incrementStats(size_t size) |
| { |
| allocSize_ += size; |
| allocCnt_++; |
| if (highWaterMark_ < allocSize_) |
| highWaterMark_ = allocSize_; |
| if (intervalWaterMark_ < allocSize_) |
| intervalWaterMark_ = allocSize_; |
| } |
| |
| // decrementStats() is called when memory is deallocated |
| inline void |
| NAMemory::decrementStats(size_t size) |
| { |
| allocSize_ -= size; |
| allocCnt_--; |
| } |
| #endif // !MUSE |
| |
| // True if address has acceptable alignment |
| inline NABoolean |
| NAHeap::isAligned(void *addr) |
| { |
| return ((size_t)addr & (FRAG_ALIGN_MASK)) == 0; |
| } |
| |
| // Round a size up to the next byte alignment size. |
| inline size_t |
| NAHeap::granularityAlign(size_t size) |
| { |
| return (size + FRAG_BYTE_ALIGN) & ~(FRAG_BYTE_ALIGN - 1); |
| } |
| |
| // Check if address is at least the minimum. |
| inline NABoolean |
| NAHeap::okAddress(void *addr) |
| { |
| return ((char*)addr >= least_addr_); |
| } |
| |
| // Check if address of next fragment n is higher than base fragment p |
| inline NABoolean |
| NAHeap::okNext(NAHeapFragment *p, NAHeapFragment *n) |
| { |
| return ((char*)p < (char*)n); |
| } |
| |
| // Check if p has its cinuse bit on |
| inline NABoolean |
| NAHeap::okCinuse(NAHeapFragment *p) |
| { |
| return p->cinuse(); |
| } |
| |
| // Check if p has its pinuse bit on |
| inline NABoolean |
| NAHeap::okPinuse(NAHeapFragment *p) |
| { |
| return p->pinuse(); |
| } |
| |
| // bit corresponding to given index |
| inline NAHeap::binmap_t |
| NAHeap::idx2bit(bindex_t idx) |
| { |
| return (binmap_t)1 << idx; |
| } |
| |
| // Return index corresponding to given bit. This function assumes |
| // that the caller has isolated a single bit and that exactly one |
| // bit is set. |
| inline NAHeap::bindex_t |
| NAHeap::bit2idx(binmap_t x) |
| { |
| #if defined(i386) |
| UInt32 ret; |
| __asm__("bsfl %1,%0\n\t" : "=r" (ret) : "rm" (x)); |
| return (NAHeap::bindex_t)ret; |
| #else |
| // Set all bits right of the set bit. |
| x--; |
| |
| // Quickly count the number of set bits using a well known |
| // population count algorithm. |
| x -= ((x >> 1) & 0x55555555); |
| x = (((x >> 2) & 0x33333333) + (x & 0x33333333)); |
| x = (((x >> 4) + x) & 0x0f0f0f0f); |
| x += (x >> 8); |
| x += (x >> 16); |
| |
| return x & 0x3f; |
| #endif |
| } |
| |
| // Return whether this size should be treated as a small fragment |
| inline NABoolean |
| NAHeap::isSmall(size_t size) |
| { |
| return (size >> SMALLBIN_SHIFT) < NSMALLBINS; |
| } |
| |
| // Return the small bin index for this size |
| inline NAHeap::bindex_t |
| NAHeap::smallIndex(size_t size) |
| { |
| return (size >> SMALLBIN_SHIFT); |
| } |
| |
| // Return the maximum size for this small bin index |
| inline size_t |
| NAHeap::smallIndex2Size(bindex_t idx) |
| { |
| return (idx << SMALLBIN_SHIFT); |
| } |
| |
| // Set a bit in the small map to indicate this bin contains free fragments |
| inline void |
| NAHeap::markSmallmap(bindex_t idx) |
| { |
| smallmap_ |= idx2bit(idx); |
| } |
| |
| // Clear a bit in the small map to indicate this bin does not contain |
| // free fragments |
| inline void |
| NAHeap::clearSmallmap(bindex_t idx) |
| { |
| smallmap_ &= ~idx2bit(idx); |
| } |
| |
| // Return whether the bit for this index is set in the small map. |
| inline NABoolean |
| NAHeap::smallmapIsMarked(bindex_t idx) |
| { |
| return (smallmap_ & idx2bit(idx)) != 0; |
| } |
| |
| // Mark a bit in the tree map to indicate the tree contains free fragments |
| inline void |
| NAHeap::markTreemap(bindex_t idx) |
| { |
| treemap_ |= idx2bit(idx); |
| } |
| |
| // Clear a bit in the tree map to indicate the tree does not contain |
| // free fragments |
| inline void |
| NAHeap::clearTreemap(bindex_t idx) |
| { |
| treemap_ &= ~idx2bit(idx); |
| } |
| |
| // Return whether a bit for this index is set in the tree map |
| inline NABoolean |
| NAHeap::treemapIsMarked(bindex_t idx) |
| { |
| return (treemap_ & idx2bit(idx)) != 0; |
| } |
| |
| // Compute the tree index for a given size |
| inline NAHeap::bindex_t |
| NAHeap::computeTreeIndex(size_t size) |
| { |
| size_t x = size >> TREEBIN_SHIFT; |
| if (x == 0) |
| return 0; |
| else if (x > 0xFFFF) |
| return NTREEBINS - 1; |
| else { |
| UInt32 k; |
| #if defined(i386) |
| __asm__("bsrl %1,%0\n\t" : "=r" (k) : "rm" (x)); |
| #else |
| UInt32 y = (UInt32)x; |
| UInt32 n = ((y - 0x100) >> 16) & 8; |
| k = (((y <<= n) - 0x1000) >> 16) & 4; |
| n += k; |
| n += k = (((y <<= k) - 0x4000) >> 16) & 2; |
| k = 14 - n + ((y <<= k) >> 15); |
| #endif |
| return (k << 1) + ((size >> (k + (TREEBIN_SHIFT-1)) & 1)); |
| } |
| } |
| |
| // Isolate the least set bit of a bitmap |
| inline NAHeap::binmap_t |
| NAHeap::leastBit(binmap_t bits) |
| { |
| return bits & -(bits); |
| } |
| |
| // Mask with all bits to left of least bit of "bits" on |
| inline NAHeap::binmap_t |
| NAHeap::leftBits(binmap_t bits) |
| { |
| return (bits << 1) | -(bits<<1); |
| } |
| |
| // Shift placing maximum resolved bit in a treebin at idx as sign bit |
| // This function is used during traversing the trees. The right child of |
| // a node (child[1]) will have the next significant bit set, while the left |
| // child of a node will have the next signficant bit unset. This function |
| // does the proper shifting to allow the first signficant bit to be set for |
| // the range of sizes used by the tree bin "idx". |
| inline UInt32 |
| NAHeap::leftshiftForTreeIndex(bindex_t idx) |
| { |
| if (idx == NTREEBINS-1) |
| return 0; |
| else |
| return (SIZE_T_BITSIZE - 1) - ((idx >> 1) + TREEBIN_SHIFT - 2); |
| } |
| |
| // The size of the smallest fragment held in bin with index idx |
| inline size_t |
| NAHeap::minsizeForTreeIndex(bindex_t idx) |
| { |
| return ((1 << ((idx >> 1) + TREEBIN_SHIFT)) | |
| (((size_t)(idx & 1)) << ((idx >> 1) + TREEBIN_SHIFT - 1))); |
| } |
| |
| // Return the address of a small bin for a particular index |
| inline NAHeapFragment* |
| NAHeap::smallbinAt(bindex_t idx) |
| { |
| return (NAHeapFragment*)&smallbins_[idx<<1]; |
| } |
| |
| // Return the head of a tree bin for a particular index |
| inline NATreeFragment** |
| NAHeap::treebinAt(bindex_t idx) |
| { |
| return &treebins_[idx]; |
| } |
| |
| #ifndef MUSE |
| // Initialize bins |
| void |
| NAHeap::initBins() |
| { |
| // Establish circular links for smallbins |
| bindex_t i; |
| for (i = 0; i < NSMALLBINS; ++i) { |
| NAHeapFragment *bin = smallbinAt(i); |
| bin->setNext(bin); |
| bin->setPrev(bin); |
| } |
| for (i = 0; i < NTREEBINS; ++i) |
| *treebinAt(i) = 0; |
| } |
| |
| // Insert a small fragment into the small bins |
| inline void |
| NAHeap::insertSmallFragment(NAHeapFragment *p, size_t size) |
| { |
| bindex_t idx = smallIndex(size); |
| NAHeapFragment *binptr = smallbinAt(idx); |
| NAHeapFragment *nextptr = binptr; |
| assert(size >= MIN_FRAGMENT_SIZE); |
| |
| // If the smallmap is not marked, then mark it. If it is not marked, |
| // then set the next pointer to the first fragment on the list. |
| // NOTE: For release mode builds, RTCHECK is defined as "1" so the |
| // okAddress() check is not made, and nextptr is always set to the |
| // first fragment in the list when the smallmap wasn't marked. |
| if (!smallmapIsMarked(idx)) |
| markSmallmap(idx); |
| else if (RTCHECK(okAddress(binptr->getNext()))) |
| nextptr = binptr->getNext(); |
| else { |
| CORRUPTION_ERROR_ACTION; |
| } |
| binptr->setNext(p); |
| nextptr->setPrev(p); |
| p->setNext(nextptr); |
| p->setPrev(binptr); |
| } |
| |
| // Unlink a small fragment from the small bins |
| inline void |
| NAHeap::unlinkSmallFragment(NAHeapFragment *p, size_t size) |
| { |
| NAHeapFragment *nextFrag = p->getNext(); |
| NAHeapFragment *prevFrag = p->getPrev(); |
| bindex_t idx = smallIndex(size); |
| |
| // Verify a few things that may catch programming errors or data corruption |
| assert(p != prevFrag); |
| assert(p != nextFrag); |
| assert(p->fragmentSize() == smallIndex2Size(idx)); |
| |
| // If the next fragment pointer is equal to the previous fragment pointer, |
| // then this indicates the fragment being unlinked is the only fragment in |
| // the list. The smallmap can be cleared without dealing with additional |
| // pointers because it is initialized properly in insertSmallFragment(). |
| // If this is not the last fragment in the list, then the previous fragment |
| // and next fragments pointers are modified to point to each other. |
| // NOTE: RTCHECK is defined as "1" in release mode, so the checks within |
| // the macro are not made. |
| if (nextFrag == prevFrag) { |
| clearSmallmap(idx); |
| } else if (RTCHECK((nextFrag == smallbinAt(idx) || okAddress(nextFrag)) && |
| (prevFrag == smallbinAt(idx) || okAddress(prevFrag)))) { |
| nextFrag->setPrev(prevFrag); |
| prevFrag->setNext(nextFrag); |
| } else { |
| CORRUPTION_ERROR_ACTION; |
| } |
| } |
| |
| // Unlink the first small fragment from a small bin |
| inline void |
| NAHeap::unlinkFirstSmallFragment(NAHeapFragment *binPtr, |
| NAHeapFragment *fragPtr, |
| bindex_t idx) |
| { |
| NAHeapFragment *next = fragPtr->getNext(); |
| assert(fragPtr != binPtr); |
| assert(fragPtr != next); |
| assert(fragPtr->fragmentSize() == smallIndex2Size(idx)); |
| if (binPtr == next) { |
| clearSmallmap(idx); |
| } else if (RTCHECK(okAddress(next))) { |
| binPtr->setNext(next); |
| next->setPrev(binPtr); |
| } else { |
| CORRUPTION_ERROR_ACTION; |
| } |
| } |
| |
| // Insert fragment into tree |
| inline void |
| NAHeap::insertLargeFragment(NATreeFragment *treeFrag, size_t size) |
| { |
| bindex_t idx = computeTreeIndex(size); // Index for this tree |
| NATreeFragment **treebinPtr = treebinAt(idx); // Treebin pointer |
| |
| // Initialize part of the tree fragment that is being inserted into |
| // the tree. |
| treeFrag->setIndex(idx); |
| treeFrag->setChild(0, NULL); |
| treeFrag->setChild(1, NULL); |
| treeFrag->setFreedNSKMemory(0); // memory not yet released to NSK |
| |
| // If this is the first fragment inserted into the tree, then mark |
| // the treemap, set the treebin pointer to point to this fragment, and |
| // finish initializing the tree fragment. |
| if (!treemapIsMarked(idx)) { |
| markTreemap(idx); |
| *treebinPtr = treeFrag; |
| treeFrag->setParent((NATreeFragment*)treebinPtr); |
| treeFrag->setNext(treeFrag); |
| treeFrag->setPrev(treeFrag); |
| } |
| else { |
| // If the code reaches here, then this is not the first fragment |
| // inserted into this tree bin. The code below looks at the bits |
| // of the size for a tree fragment range. For instance, there |
| // could be a tree bin that deals with sizes of 769 bytes to 1024 |
| // bytes. In this case, the most signficant bit would be the bit |
| // set by 1024, or (1 << 10). The leftshiftForTreeindex determines |
| // the number of bits needed to shift the size so the most signficant |
| // bit of the range is in the 32nd bit. Then the code below looks |
| // at the most signficant bit in "sizebits" during each time through |
| // the loop. Each time through the "sizebits" is shifted left by one |
| // so that the next significant bit can be examined. This technique |
| // allows for a theoretically balanced tree, and allows for quick |
| // traversing of tree tree. |
| NATreeFragment *treePtr = *treebinPtr; // Pointer for tree traversal |
| size_t sizebits = size << leftshiftForTreeIndex(idx); |
| for (;;) { |
| if (treePtr->fragmentSize() != size) { |
| |
| NATreeFragment **childPtr = treePtr->getChildAddr( |
| (sizebits >> (SIZE_T_BITSIZE - 1)) & 1); |
| sizebits <<= 1; |
| if (*childPtr != 0) |
| treePtr = *childPtr; |
| else if (RTCHECK(okAddress(childPtr))) { |
| *childPtr = treeFrag; |
| treeFrag->setParent(treePtr); |
| treeFrag->setNext(treeFrag); |
| treeFrag->setPrev(treeFrag); |
| break; // Break from for loop |
| } else { |
| CORRUPTION_ERROR_ACTION; |
| break; // Break from for loop |
| } |
| } else { |
| NATreeFragment *next = (NATreeFragment*)treePtr->getNext(); |
| if (RTCHECK(okAddress(treePtr) && okAddress(next))) { |
| treePtr->setNext(treeFrag); |
| next->setPrev(treeFrag); |
| treeFrag->setNext(next); |
| treeFrag->setPrev(treePtr); |
| treeFrag->setParent(NULL); |
| break; // Break from for loop |
| } else { |
| CORRUPTION_ERROR_ACTION; |
| break; // Break from for loop |
| } |
| } |
| } |
| } |
| } |
| |
| // Unlink a large fragment from the tree |
| // Steps: |
| // 1. If "treeFrag" is a chained node, unlink it from its same-sized |
| // next/prev links and choose its prev node as its replacement. |
| // 2. If treeFrag was the last node of its size, but not a leaf node, |
| // it must be replaced with a leaf node (not merely one with an |
| // open left or right), to make sure that lefts and rights of |
| // descendents correspond properly to bit masks. We use the |
| // rightmost descendent of treeFrag. We could use any other leaf, |
| // but this is easy to locate and tends to counteract removal of |
| // leftmosts elsewhere, and so keeps paths shorter than minimally |
| // guaranteed. This doesn't loop much because on average a node |
| // in a tree is near the bottom. |
| // 3. If x is the base of a chain (i.e., has parent links) relink |
| // x's parent and children to x's replacement (or null if none). |
| inline void |
| NAHeap::unlinkLargeFragment(NATreeFragment *treeFrag) |
| { |
| NATreeFragment *parent = treeFrag->getParent(); |
| NATreeFragment *prev; |
| if (treeFrag->getPrev() != treeFrag) { |
| NATreeFragment *next = (NATreeFragment*)treeFrag->getNext(); |
| prev = (NATreeFragment*)treeFrag->getPrev(); |
| if (RTCHECK(okAddress(next))) { |
| next->setPrev(prev); |
| prev->setNext(next); |
| } else { |
| CORRUPTION_ERROR_ACTION; |
| } |
| } else { |
| // If the current node has any children, then the next section of |
| // code searches for the rightmost descendent and sets "prev" to |
| // the parent of this descendent, and "rightmostPtr" to the |
| // descendent. |
| NATreeFragment **rightmostPtr; |
| if (((prev = *(rightmostPtr = treeFrag->getChildAddr(1))) != 0) || |
| ((prev = *(rightmostPtr = treeFrag->getChildAddr(0))) != 0)) { |
| NATreeFragment **childPtr; |
| while ((*(childPtr = prev->getChildAddr(1)) != 0) || |
| (*(childPtr = prev->getChildAddr(0)) != 0)) { |
| prev = *(rightmostPtr = childPtr); |
| } |
| if (RTCHECK(okAddress(rightmostPtr))) { |
| *rightmostPtr = 0; |
| } else { |
| CORRUPTION_ERROR_ACTION; |
| } |
| } |
| } |
| |
| if (parent != 0) { |
| NATreeFragment **treebinPtr = treebinAt(treeFrag->getIndex()); |
| if (treeFrag == *treebinPtr) { |
| if ((*treebinPtr = prev) == 0) |
| clearTreemap(treeFrag->getIndex()); |
| } else if (RTCHECK(okAddress(parent))) { |
| if (parent->getChild(0) == treeFrag) |
| parent->setChild(0, prev); |
| else |
| parent->setChild(1, prev); |
| } else { |
| CORRUPTION_ERROR_ACTION; |
| } |
| |
| if (prev != 0) { |
| if (RTCHECK(okAddress(prev))) { |
| NATreeFragment *leftChild, *rightChild; |
| prev->setParent(parent); |
| if ((leftChild = treeFrag->getChild(0)) != 0) { |
| if (RTCHECK(okAddress(leftChild))) { |
| prev->setChild(0, leftChild); |
| leftChild->setParent(prev); |
| } else { |
| CORRUPTION_ERROR_ACTION; |
| } |
| } |
| if ((rightChild = treeFrag->getChild(1)) != 0) { |
| if (RTCHECK(okAddress(rightChild))) { |
| prev->setChild(1, rightChild); |
| rightChild->setParent(prev); |
| } else { |
| CORRUPTION_ERROR_ACTION; |
| } |
| } |
| } else { |
| CORRUPTION_ERROR_ACTION; |
| } |
| } |
| } |
| } |
| |
| // Insert this fragment into either a large or small bin |
| inline void |
| NAHeap::insertFragment(NAHeapFragment *p, size_t size) |
| { |
| if (isSmall(size)) { |
| insertSmallFragment(p, size); |
| } else { |
| NATreeFragment *tp = (NATreeFragment*)p; |
| insertLargeFragment(tp, size); |
| } |
| } |
| |
| // Unlink either a large or small fragment from the small bins or |
| // large trees |
| inline void |
| NAHeap::unlinkFragment(NAHeapFragment *p, size_t size) |
| { |
| if (isSmall(size)) { |
| unlinkSmallFragment(p, size); |
| } else { |
| NATreeFragment *tp = (NATreeFragment*)p; |
| unlinkLargeFragment(tp); |
| } |
| } |
| |
| // Replace the designated victim with a different fragment |
| inline void |
| NAHeap::replaceDV(NAHeapFragment *p, size_t size) |
| { |
| if (dvsize_ != 0) |
| insertSmallFragment(dv_, dvsize_); |
| |
| dvsize_ = size; |
| dv_ = p; |
| } |
| |
| // Resize the top fragment when a segment changes size. |
| inline void |
| NAHeap::resizeTop(size_t newsize) |
| { |
| topsize_ = newsize; |
| |
| size_t firstFragBit = top_->getFirstFragmentBit(); |
| top_->setSizeAndPinuse(newsize | firstFragBit); |
| |
| NAHeapFragment *next = top_->nextFragment(); |
| next->setFencePosts(); |
| } |
| |
| // Initialize the top fragment |
| inline void |
| NAHeap::initTop(NABlock *block) |
| { |
| size_t offset = block->firstFragmentOffset(); |
| |
| // Calculate topsize_ with rounding down to the FRAG_ALIGN boundary. |
| topsize_ = (block->size_ - offset - MIN_FRAGMENT_SIZE) & ~FRAG_ALIGN_MASK; |
| top_ = (NAHeapFragment*)((char*)block + offset); |
| |
| top_->setSizeAndPinuse(topsize_); |
| |
| // Set trailing fenceposts. The assert insures that there is enough |
| // space for the trailing fenceposts at the end of the block. |
| NAHeapFragment *r = top_->nextFragment(); |
| assert((char*)r + (3 * sizeof(size_t)) <= (char*)block + block->size_); |
| r->setFencePosts(); |
| } |
| |
| // initialize the fields in the NAHeapFragment structures when a new |
| // noncontiguous NABlock is allocated. |
| void |
| NAHeap::addBlock(NABlock *newBlock) |
| { |
| // Insert the old top into a bin as an ordinary free fragment. |
| if (topsize_ != 0) { |
| size_t firstFragBit = top_->getFirstFragmentBit(); |
| NAHeapFragment *next = top_->fragmentPlusOffset(topsize_); |
| |
| top_->setFreeWithPinuse(topsize_, next); |
| insertFragment(top_, topsize_); |
| top_->setHeadBits(firstFragBit); // Set first fragment bit if it was set |
| } |
| |
| // reset top to new space |
| initTop(newBlock); |
| top_->initializeFirstFragment(topsize_); |
| checkTopFragment(top_); |
| } |
| |
| // Free an NABlock if all of the memory becomes free. This releases |
| // it to the operating system or frees it in the parent memory. |
| NABoolean |
| NAHeap::deallocateFreeBlock(NAHeapFragment *p) |
| { |
| NABlock *prev = NULL; |
| NABlock *curr = firstBlk_; |
| |
| // Find the NABlock containing this fragment |
| while (curr != NULL && !curr->blockHolds(p)) { |
| prev = curr; |
| curr = curr->next_; |
| } |
| |
| assert(curr != NULL); // A block had better contain this fragment |
| |
| // If this block was allocated externally, then it should not be |
| // deallocated here. This shouldn't really happen. For a future |
| // project it might make sense to resize this segment to something |
| // smaller to save on memory. |
| if (curr->isExternSegment()) |
| return FALSE; |
| |
| // Decrement statistics |
| totalSize_ -= curr->size_; |
| blockCnt_--; |
| |
| // Reset top_ if it is in this NABlock |
| if (curr->blockHolds(top_)) { |
| top_ = NULL; |
| topsize_ = 0; |
| } |
| |
| // Reset designated victim if it is within this NABlock |
| if (curr->blockHolds(dv_)) { |
| dv_ = NULL; |
| dvsize_ = 0; |
| } |
| |
| // Remove this NABlock from the NABlock list |
| if (prev == NULL) |
| firstBlk_ = curr->next_; |
| else |
| prev->next_ = curr->next_; |
| |
| // Now release the memory to the operating system. |
| switch (type_) |
| { |
| case EXECUTOR_MEMORY: |
| case SYSTEM_MEMORY: |
| case IPC_MEMORY: |
| sysFreeBlock(curr); |
| break; |
| case DERIVED_FROM_SYS_HEAP: |
| sysFreeBlock(curr); |
| break; |
| case DERIVED_MEMORY: |
| assert(parent_); |
| HEAPLOG_OFF() // no recursive logging. |
| // This code provides mutual exclusion for the runtime stats shared |
| // memory segment. |
| if (parent_->getType() == EXECUTOR_MEMORY && getSharedMemory()) { |
| short retcode = getRTSSemaphore(); |
| parent_->deallocateHeapMemory((void*)curr); |
| if (retcode == 1) |
| releaseRTSSemaphore(); |
| } else { |
| parent_->deallocateHeapMemory((void*)curr); |
| } |
| HEAPLOG_ON() |
| break; |
| default: |
| assert(0); // Shouldn't happen |
| break; |
| } |
| |
| return TRUE; |
| } |
| |
| // Allocate a large request from the best fitting fragment in a treebin |
| // and return a pointer to the NAHeapFragment that best fits the request. |
| // If the allocation cannot be satisfied or if the designated victim is |
| // a better fit than the best fitting fragment in any of the trees, then |
| // a NULL pointer is returned. |
| NAHeapFragment* |
| NAHeap::tmallocLarge(size_t nb) |
| { |
| NATreeFragment *v = NULL; |
| size_t rsize = -nb; // Unsigned negation. |
| NATreeFragment *t; |
| bindex_t idx = computeTreeIndex(nb); |
| |
| if ((t = *treebinAt(idx)) != 0) { |
| // Traverse tree for this bin looking for node with size == nb |
| size_t sizebits = nb << leftshiftForTreeIndex(idx); |
| NATreeFragment *rst = NULL; // The deepest untaken right subtree |
| for (;;) { |
| size_t trem = t->fragmentSize() - nb; // Can cause underflow |
| if (trem < rsize) { |
| v = t; |
| if ((rsize = trem) == 0) |
| break; |
| } |
| NATreeFragment *rt = t->getChild(1); |
| t = t->getChild((sizebits >> (SIZE_T_BITSIZE - 1)) & 1); |
| if (rt != 0 && rt != t) |
| rst = rt; |
| if (t == 0) { |
| t = rst; // set t to least subtree holding sizes > nb |
| break; |
| } |
| sizebits <<= 1; |
| } |
| } |
| |
| if (t == 0 && v == 0) { // set t to root of next non-empty treebin |
| binmap_t leftbits = leftBits(idx2bit(idx)) & treemap_; |
| if (leftbits != 0) { |
| binmap_t leastbit = leastBit(leftbits); |
| bindex_t i = bit2idx(leastbit); |
| t = *treebinAt(i); |
| } |
| } |
| |
| while (t != 0) { // find smallest of tree or subtree |
| size_t trem = t->fragmentSize() - nb; |
| if (trem < rsize) { |
| rsize = trem; |
| v = t; |
| } |
| t = t->leftmostChild(); |
| } |
| |
| // If dv is a not a better fit, then use the tree fragment that was |
| // found. If necessary, split the tree fragment into two fragments, |
| // and reinsert the remainder into a small bin or another tree. |
| if (v != 0 && rsize < (dvsize_ - nb)) { |
| if (RTCHECK(okAddress(v))) { // split |
| NAHeapFragment *r = v->fragmentPlusOffset(nb); |
| assert(v->fragmentSize() == rsize + nb); |
| size_t firstFragBit = v->getFirstFragmentBit(); |
| if (RTCHECK(okNext(v, r))) { |
| unlinkLargeFragment(v); |
| if (rsize < MIN_FRAGMENT_SIZE) { |
| v->setInuseAndPinuse((rsize + nb) | firstFragBit); |
| } else { |
| v->setSizeAndPinuseOfInuseFragment(nb | firstFragBit); |
| r->setSizeAndPinuseOfFreeFragment(rsize); |
| insertFragment(r, rsize); |
| } |
| return v; |
| } |
| } |
| CORRUPTION_ERROR_ACTION; |
| } |
| // Return NULL because the designated victim is larger than the treebin |
| // or there wasn't a tree fragment that satisfied the request. |
| return NULL; |
| } |
| |
| // allocate a small request from the best fitting fragment in a treebin |
| NAHeapFragment* |
| NAHeap::tmallocSmall(size_t nb) |
| { |
| binmap_t leastbit = leastBit(treemap_); |
| bindex_t idx = bit2idx(leastbit); |
| |
| NATreeFragment *t, *v; |
| v = t = *treebinAt(idx); |
| size_t rsize = t->fragmentSize() - nb; |
| |
| while ((t = t->leftmostChild()) != 0) { |
| size_t trem = t->fragmentSize() - nb; |
| if (trem < rsize) { |
| rsize = trem; |
| v = t; |
| } |
| } |
| |
| if (RTCHECK(okAddress(v))) { |
| NAHeapFragment *r = v->fragmentPlusOffset(nb); |
| assert(v->fragmentSize() == rsize + nb); |
| size_t firstFragBit = v->getFirstFragmentBit(); |
| |
| if (RTCHECK(okNext(v, r))) { |
| unlinkLargeFragment(v); |
| if (rsize < MIN_FRAGMENT_SIZE) { |
| v->setInuseAndPinuse((rsize + nb) | firstFragBit); |
| } else { |
| v->setSizeAndPinuseOfInuseFragment(nb | firstFragBit); |
| r->setSizeAndPinuseOfFreeFragment(rsize); |
| replaceDV(r, rsize); |
| } |
| return v; |
| } |
| } |
| |
| CORRUPTION_ERROR_ACTION; |
| return NULL; |
| } |
| |
| Lng32 NAMemory::getAllocatedSpaceSize() |
| { |
| assert(type_ != NO_MEMORY_TYPE); |
| return allocSize_; |
| } |
| |
| void NAMemory::setName(const char * name) |
| { |
| Lng32 copyLen = str_len(name); |
| if (copyLen > 20) |
| copyLen = 20; |
| memcpy(name_, name, copyLen); |
| name_[copyLen] = 0; |
| } |
| |
| void NAMemory::registerMemory(NAMemory * child) |
| { |
| // put child at the end of the memoryList_ |
| if (lastListEntry_) |
| lastListEntry_->nextEntry_ = child; |
| else |
| memoryList_ = child; |
| |
| lastListEntry_ = child; |
| } |
| |
| void NAMemory::unRegisterMemory(NAMemory * child) |
| { |
| NAMemory * p = memoryList_; |
| NAMemory * q = NULL; |
| while (p && p != child) { |
| q = p; |
| p = p->nextEntry_; |
| } |
| assert(p); |
| |
| if (q == NULL) |
| // we remove the first entry |
| memoryList_ = p->nextEntry_; |
| else |
| q->nextEntry_ = p->nextEntry_; |
| |
| // adjust lastListEntry_ |
| if (child == lastListEntry_) |
| lastListEntry_ = q; |
| } |
| |
| // Return the resize segment size including the first seg offset |
| // (totalSize_, maximumSize_, etc does not include the first seg offset) |
| |
| |
| NABlock* |
| NAHeap::allocateBlock(size_t size, NABoolean failureIsFatal) |
| { |
| assert(type_ != NO_MEMORY_TYPE); |
| |
| // if a limit is specified for this heap, make sure we don't pass it |
| if ((IdentifyMyself::GetMyName() == I_AM_EMBEDDED_SQL_COMPILER) && |
| (upperLimit_ != 0) && |
| ((allocSize_ + size) > upperLimit_)) |
| { |
| // reset the upper limit for now so testing can continue |
| upperLimit_ = 0; |
| handleExhaustedMemory(); |
| } |
| |
| |
| void * addr = 0; |
| short segmentId = (firstBlk_ == NULL ? 0 : firstBlk_->segmentId_); |
| |
| // for derived memories, double the incrementSize_ if we have |
| // allocated 10 (20, 30, ...) blocks |
| if ((type_ == DERIVED_MEMORY || |
| type_ == DERIVED_FROM_SYS_HEAP) && |
| thBlockCnt_ == (blockCnt_ + 1) && |
| incrementSize_ < DEFAULT_MAX_INCREMENT) { |
| |
| // next adjustment when we get 10 more blocks. |
| incrementSize_ *= 2; |
| if (incrementSize_ > DEFAULT_MAX_INCREMENT) |
| incrementSize_ = DEFAULT_MAX_INCREMENT; |
| thBlockCnt_ += DEFAULT_THRESHOLD_BLOCK_COUNT; |
| } |
| |
| // allocate always at least blockSize bytes |
| size_t blockSize = (firstBlk_ == NULL) ? initialSize_ : incrementSize_; |
| |
| |
| |
| // Ensure that the blockSize is at least large enough to hold the |
| // requested size. |
| if (blockSize < size) // Compare as unsigned integer |
| blockSize = size; |
| |
| // make sure that the block size is a multiple of FRAG_BYTE_ALIGN |
| blockSize = (blockSize + FRAG_ALIGN_MASK) & ~FRAG_ALIGN_MASK; |
| |
| NABlock *p = NULL; // Pointer to the returned block. |
| |
| switch (type_) { |
| case EXECUTOR_MEMORY: { |
| |
| // This could be either Global Executor Memory or Stats Globals |
| // Don't add a block if Stats Globals! |
| if (getSharedMemory()) |
| return NULL; |
| |
| // Try to allocate the NABlock using mmap(). If it succeeds return the |
| // NABlock. Otherwise, fall through and try to allocate the normal way. |
| if (blockSize >= MIN_MMAP_ALLOC_SIZE && |
| (p = allocateMMapBlock(blockSize)) != NULL) |
| return p; |
| |
| // allocate a block from the OS memory |
| p = (NABlock*)malloc(blockSize); |
| if (p) |
| allocationIncrement(blockSize); |
| } |
| break; |
| |
| case SYSTEM_MEMORY: |
| case IPC_MEMORY: |
| { |
| // Try to allocate the NABlock using mmap(). If it succeeds return the |
| // NABlock. Otherwise, fall through and try to allocate the normal way. |
| if (blockSize >= MIN_MMAP_ALLOC_SIZE && |
| (p = allocateMMapBlock(blockSize)) != NULL) |
| return p; |
| |
| // allocate a block from the OS memory |
| p = (NABlock*)malloc(blockSize); |
| if (p) |
| allocationIncrement(blockSize); |
| } |
| break; |
| |
| case DERIVED_FROM_SYS_HEAP: { |
| // allocate a block from the OS memory |
| // Try to allocate the NABlock using mmap(). If it succeeds return the |
| // NABlock. Otherwise, fall through and try to allocate the normal way. |
| if (blockSize >= MIN_MMAP_ALLOC_SIZE && |
| (p = allocateMMapBlock(blockSize)) != NULL) |
| return p; |
| |
| p = (NABlock*)malloc(blockSize); |
| if (p) |
| allocationIncrement(blockSize); |
| } |
| break; |
| |
| case DERIVED_MEMORY: { |
| // make sure that we have a parent |
| assert(parent_); |
| // allocate a block from the parent |
| HEAPLOG_OFF() // no recursive logging. (eric) |
| |
| // This code provides mutual exclusion for the runtime stats shared |
| // memory segment. |
| // The IF condition below is not needed since we are checking if |
| // semaphore is obtained in allocateHeapMemory or deallocateHeapMemory |
| // for both global and process stats heap. But leaving it now |
| // since it won't hurt other than extra cpu cycles |
| if (getSharedMemory()) { |
| short retcode = getRTSSemaphore(); |
| p = (NABlock*)parent_->allocateHeapMemory(blockSize, FALSE); |
| |
| if (p == NULL) |
| // Unit tested this code with the test case in QC 1387 |
| // - 3/22/2012. |
| { |
| // Exhausted shared segment. Prevent cascade of core-files by |
| // making one core-file of myself while holding semaphore. |
| // Then bring down this node. |
| genLinuxCorefile("Shared Segment might be full"); |
| Int32 nid = 0; |
| Int32 pid = 0; |
| if (XZFIL_ERR_OK == msg_mon_get_my_info(&nid, &pid, NULL, |
| 0, NULL, NULL, NULL, NULL)) |
| { |
| // The SSMP is responsible for preventing leaks. So get a |
| // corefile of it. |
| char ssmpName[MS_MON_MAX_PROCESS_NAME]; |
| memset(ssmpName, 0, MS_MON_MAX_PROCESS_NAME); |
| if (XZFIL_ERR_OK == XPROCESSHANDLE_DECOMPOSE_( |
| getMySsmpPhandle() |
| , NULL // cpu |
| , NULL // pin |
| , NULL // nodenumber |
| , NULL // nodename |
| , 0 // nodename_maxlen |
| , NULL // nodename_length |
| , ssmpName |
| , sizeof(ssmpName))) |
| { |
| char coreFile[1024]; |
| msg_mon_dump_process_name(NULL, ssmpName, coreFile); |
| } |
| Int32 ndRetcode = msg_mon_node_down2(nid, |
| "RMS shared segment is exhausted."); |
| sleep(30); |
| NAExit(0); // already made a core. |
| } |
| else |
| assert(p != NULL); |
| } |
| |
| if (retcode == 1) |
| releaseRTSSemaphore(); |
| } else { |
| p = (NABlock*)parent_->allocateHeapMemory(blockSize, failureIsFatal); |
| if (parent_->type_ == NAMemory::EXECUTOR_MEMORY && |
| parent_->parent_ == NULL) |
| { |
| updateMemStats(); |
| } |
| } |
| segmentId = NABlock::DERIVED_SEGMENT_ID; // Add another check for muse |
| HEAPLOG_ON() |
| } |
| break; |
| } // switch(type_) |
| |
| // if the allocation was not sucessfull, we return a NULL |
| if (p == NULL) |
| return NULL; |
| |
| // one more block allocated |
| blockCnt_++; |
| totalSize_+= blockSize; |
| |
| // Initialize the returned NABlock information |
| p->segmentId_ = segmentId; |
| p->size_ = blockSize; |
| p->next_ = NULL; // The caller is responsible for linking this |
| p->sflags_ = 0; |
| |
| return p; |
| } // NAHeap::allocateBlock(size_t size, NABoolean failureIsFatal) |
| #endif // !MUSE |
| |
| void NAMemory::showStats(ULng32 level) |
| { |
| char indent[100]; |
| Int32 i = 0; |
| for (; i < (2 + 2 * (Int32) level); i++) |
| indent[i] = ' '; |
| indent[i] = '\0'; |
| |
| cerr << indent << "NAMemory: " << this << " is a "; |
| switch(type_) { |
| case NO_MEMORY_TYPE: |
| cerr << "NO_MEMORY_TYPE"; |
| break; |
| case EXECUTOR_MEMORY: |
| cerr << "EXECUTOR_MEMORY"; |
| break; |
| case SYSTEM_MEMORY: |
| cerr << "SYSTEM_MEMORY"; |
| break; |
| case IPC_MEMORY: |
| cerr << "IPC_MEMORY"; |
| break; |
| case DERIVED_MEMORY: |
| cerr << "DERIVED_MEMORY"; |
| break; |
| case DERIVED_FROM_SYS_HEAP: |
| cerr << "DERIVED_FROM_SYS_HEAP"; |
| break; |
| } |
| cerr << endl |
| << indent << "name of memory: " << name_ << endl |
| << indent << "parent memory: " << parent_ << endl |
| << indent << "initial size: " << initialSize_ << endl |
| << indent << "maximum size: " << maximumSize_ << endl |
| << indent << "increment size: " << incrementSize_ << endl |
| << indent << "total size: " << totalSize_ << endl |
| << indent << "#of blocks: " << blockCnt_ << endl |
| << indent << "allocated size: " << allocSize_ << endl |
| << indent << "high water mark: " << highWaterMark_ << endl |
| << indent << "interval water mark: " << intervalWaterMark_ << endl |
| << indent << "allocated fragments: " << allocCnt_ << endl |
| << indent << "-------------------------------------------------" << endl; |
| |
| for (NAMemory * p = memoryList_; p != NULL; p = p->nextEntry_) |
| p->showStats(level + 1); |
| } |
| |
| // this method used to belong to CollHeap |
| #ifndef MUSE |
| void |
| NAMemory::handleExhaustedMemory() |
| { |
| exhaustedMem_ = TRUE; |
| } |
| #endif // MUSE |
| |
| |
| NABoolean NAMemory::getUsage(size_t * lastBlockSize, size_t * freeSize, size_t * totalSize) |
| { |
| *freeSize = 0; |
| NAMemory * memory = this; |
| NABoolean crowded = FALSE; |
| |
| do |
| { |
| *freeSize += memory->totalSize_ - memory->allocSize_; |
| *totalSize = memory->totalSize_; |
| if (memory->type_ == EXECUTOR_MEMORY) |
| { |
| //if (memory->firstBlk_->next_ == NULL) |
| *lastBlockSize = 0; |
| //*lastBlockSize = memory->firstBlk_->size_; |
| //else |
| //*lastBlockSize = memory->firstBlk_->next_->size_; |
| if (memory->crowdedTotalSize_ > 0ll && |
| *freeSize < *totalSize / 2) // Free size is < half total alloc'd |
| { |
| // Simulate size on NSK when unable to allocate 128 MB flat segment |
| *lastBlockSize = (Lng32)((4ll * 1024ll * 1024ll * 1024ll - memory->crowdedTotalSize_) / 2); |
| crowded = TRUE; |
| } |
| } |
| memory = memory->parent_; |
| } while (memory != NULL); |
| return crowded; |
| } |
| |
| NABoolean NAMemory::checkSize(size_t size, NABoolean failureIsFatal) |
| { |
| if (size > MAX_MEMORY_SIZE_IN_AN_ALLOC) { |
| if (failureIsFatal) |
| abort(); |
| else |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| // --------------------------------------------------------------------------- |
| // NAHeap methods |
| // --------------------------------------------------------------------------- |
| #ifndef MUSE |
| NAHeap::NAHeap() |
| : NAMemory("Unknown Memory Type"), |
| smallmap_(0), |
| treemap_(0), |
| dvsize_(0), |
| topsize_(0), |
| least_addr_(0), |
| dv_(NULL), |
| top_(NULL), |
| errCallback_(NULL) |
| { |
| initBins(); |
| derivedClass_ = NAHEAP_CLASS; |
| |
| threadSafe_ = false; |
| memset(&mutex_, '\0', sizeof(mutex_)); |
| |
| #ifdef _DEBUG |
| setAllocTrace(); |
| #endif // _DEBUG |
| } |
| |
| NAHeap::NAHeap(const char * name, |
| NAHeap * parent, |
| Lng32 blockSize, |
| size_t upperLimit) |
| : NAMemory(name, parent, blockSize, upperLimit), |
| smallmap_(0), |
| treemap_(0), |
| dvsize_(0), |
| topsize_(0), |
| least_addr_(0), |
| dv_(NULL), |
| top_(NULL), |
| errCallback_(NULL) |
| { |
| initBins(); |
| derivedClass_ = NAHEAP_CLASS; |
| |
| threadSafe_ = false; |
| memset(&mutex_, '\0', sizeof(mutex_)); |
| if (parent != NULL) |
| { |
| NAMutex mutex(parent->threadSafe_, &parent->mutex_); |
| parent_->registerMemory(this); |
| } |
| |
| #ifdef _DEBUG |
| setAllocTrace(); |
| #endif // _DEBUG |
| } |
| |
| NAHeap::NAHeap(const char * name, |
| NAMemoryType type, |
| Lng32 blockSize, |
| size_t upperLimit) |
| : NAMemory(name, type, blockSize, upperLimit), |
| smallmap_(0), |
| treemap_(0), |
| dvsize_(0), |
| topsize_(0), |
| least_addr_(0), |
| dv_(NULL), |
| top_(NULL), |
| errCallback_(NULL) |
| { |
| initBins(); |
| derivedClass_ = NAHEAP_CLASS; |
| |
| threadSafe_ = false; |
| memset(&mutex_, '\0', sizeof(mutex_)); |
| |
| #ifdef _DEBUG |
| setAllocTrace(); |
| #endif // _DEBUG |
| } |
| |
| NAHeap::NAHeap(const char * name) |
| : NAMemory(name), |
| smallmap_(0), |
| treemap_(0), |
| dvsize_(0), |
| topsize_(0), |
| least_addr_(0), |
| dv_(NULL), |
| top_(NULL), |
| errCallback_(NULL) |
| { |
| initBins(); |
| derivedClass_ = NAHEAP_CLASS; |
| |
| if (firstBlk_) { |
| initTop(firstBlk_); |
| least_addr_ = (char*)firstBlk_; |
| } |
| |
| if (deallocTraceArray == 0) |
| { |
| char *deallocTraceEnvvar = getenv("EXE_DEALLOC_MEM_TRACE"); |
| if (deallocTraceEnvvar != NULL) |
| { |
| deallocTraceArray = |
| (DeallocTraceEntry (*) [deallocTraceEntries])malloc(sizeof(DeallocTraceEntry) * deallocTraceEntries); |
| memset((void *)deallocTraceArray, '\0', sizeof(DeallocTraceEntry) * deallocTraceEntries); |
| } |
| } |
| |
| threadSafe_ = false; |
| memset(&mutex_, '\0', sizeof(mutex_)); |
| |
| #ifdef _DEBUG |
| setAllocTrace(); |
| #endif // _DEBUG |
| } |
| |
| // Constructor that imposes the NAHeap struture on already allocated memory |
| NAHeap::NAHeap(const char * name, |
| SEG_ID segmentId, |
| void * baseAddr, |
| off_t heapStartOffset, |
| size_t maxSize) |
| : NAMemory(name, segmentId, baseAddr, heapStartOffset, maxSize), |
| smallmap_(0), |
| treemap_(0), |
| dvsize_(0), |
| topsize_(0), |
| least_addr_(0), |
| dv_(NULL), |
| top_(NULL), |
| errCallback_(NULL) |
| { |
| initBins(); |
| derivedClass_ = NAHEAP_CLASS; |
| |
| if (firstBlk_) { |
| initTop(firstBlk_); |
| least_addr_ = (char*)firstBlk_; |
| } |
| |
| if (deallocTraceArray == 0) |
| { |
| char *deallocTraceEnvvar = getenv("EXE_DEALLOC_MEM_TRACE"); |
| if (deallocTraceEnvvar != NULL) |
| { |
| deallocTraceArray = |
| (DeallocTraceEntry (*) [deallocTraceEntries])malloc(sizeof(DeallocTraceEntry) * deallocTraceEntries); |
| memset((void *)deallocTraceArray, '\0', sizeof(DeallocTraceEntry) * deallocTraceEntries); |
| } |
| } |
| |
| threadSafe_ = false; |
| memset(&mutex_, '\0', sizeof(mutex_)); |
| |
| #ifdef _DEBUG |
| setAllocTrace(); |
| #endif // _DEBUG |
| } |
| |
| NAHeap::~NAHeap() |
| { |
| destroy(); |
| if (threadSafe_) |
| pthread_mutex_destroy(&mutex_); |
| } |
| |
| void NAHeap::setThreadSafe() |
| { |
| int rc; |
| pthread_mutexattr_t attr; |
| rc = pthread_mutexattr_init(&attr); |
| assert(rc == 0); |
| rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); |
| assert(rc == 0); |
| NAHeap *heap = this; |
| // set this heap and heaps it is derived from |
| while (heap) |
| { |
| if (heap->threadSafe_ == false) |
| { |
| rc = pthread_mutex_init(&(heap->mutex_), &attr); |
| assert(rc == 0); |
| heap->threadSafe_ = true; |
| } |
| heap = heap->parent_; |
| } |
| } |
| |
| // destroy() is called by the NAMemory destructor to perform the |
| // derived class destructor. It is part of the mechanism that |
| // prevents virtual functions, which cause problems when used with |
| // runtime statistics. |
| void NAHeap::destroy() |
| { |
| if (parent_ != NULL) |
| NAMutex mutex(parent_->threadSafe_, &parent_->mutex_); |
| |
| #ifdef _DEBUG |
| if (la_) |
| { |
| char header[120]; |
| sprintf(header, "Stacks when block of %d bytes was asked from heap (%s)", |
| TraceAllocSize, getName()); |
| dumpTrafStack(getSAL(), header, true); |
| delete la_; |
| } |
| #endif // _DEBUG |
| reInitialize(); |
| |
| // remove this memory from the parents memoryList_ |
| if (parent_) |
| parent_->unRegisterMemory(this); |
| } |
| |
| // reInitializeHeap reinitializes the NAHeap structures. It is called from the |
| // NAMemory reInitialize(), which may either be called directly or from a destructor. |
| void NAHeap::reInitializeHeap() |
| { |
| NAMutex mutex(threadSafe_, &mutex_); |
| |
| #if (defined(_DEBUG) || defined(NSK_MEMDEBUG)) |
| // Before freeing the NABlocks in reInitialize(), check to see whether any |
| // of the memory fragments in the blocks have overflowed |
| if (debugLevel_ == 2) |
| checkForOverflow(); |
| #endif |
| |
| // Reinitialize the fields that are specific to the NAHeap class |
| smallmap_ = treemap_ = dvsize_ = topsize_ = 0; |
| dv_ = NULL; |
| top_ = NULL; |
| least_addr_ = NULL; |
| |
| initBins(); |
| |
| // If a segment is still active, then reinitialize the top fragment |
| // so that it can be used. This code is not currently called, but |
| // is here to allow the EXECUTOR_MEMORY or any other memory that |
| // will be allocated by an external means to be reinitialized. |
| // The first part of reinitialization occurs in NAMemory::reInitialize(). |
| // That code frees the NABlocks and will reinitialize the firstBlk_ |
| // if it was allocated externally. |
| if (firstBlk_ != NULL) { |
| assert((char*)firstBlk_ == ((char*)heapStartAddr_ - heapStartOffset_)); |
| least_addr_ = (char*)firstBlk_; |
| initTop(firstBlk_); |
| } |
| } |
| |
| // NAHeap::setErrorCallback() sets a pointer to a function that |
| // will be called if the heap cannot allocate an NABlock. The |
| // callback function will be called with a NAHeap pointer argument |
| // and an argument that indicates the amount of memory that was |
| // being allocated. |
| void NAHeap::setErrorCallback(void (*errCallback)(NAHeap*,size_t)) |
| { |
| errCallback_ = errCallback; |
| } |
| |
| void * NAHeap::allocateAlignedHeapMemory(size_t userSize, size_t alignment, NABoolean failureIsFatal) |
| { |
| if (alignment & (sizeof(void *) - 1)) // Is alignment a multiple of sizeof(void *)? |
| return NULL; |
| if (alignment & (alignment - 1)) // Is alignment a power of 2? |
| return NULL; |
| size_t retAddr, alignedAddr; |
| retAddr = (size_t)allocateHeapMemory(userSize + alignment, failureIsFatal); |
| if (retAddr == (size_t) NULL) |
| return NULL; |
| alignedAddr = retAddr & ~(alignment - 1); |
| if (alignedAddr != retAddr) |
| { |
| alignedAddr += alignment; |
| if ((alignedAddr - retAddr) < (sizeof(size_t) * 2)) |
| { // need to double the alignment to avoid override header info |
| deallocateHeapMemory((void *)retAddr); |
| retAddr = (size_t)allocateHeapMemory(userSize + 2 * alignment, failureIsFatal); |
| if (retAddr == (size_t) NULL) |
| return NULL; |
| alignedAddr = retAddr & ~(alignment - 1); |
| if (alignedAddr == retAddr) |
| return (void *)alignedAddr; |
| alignedAddr += alignment; // move to aligned addr inside the fragment |
| if ((alignedAddr - retAddr) < (sizeof(size_t) * 2)) |
| alignedAddr += alignment; // move to next alignment |
| } |
| NAHeapFragment *p = NAHeapFragment::memToFragment((void *)alignedAddr); |
| p->initializeFirstFragment(alignedAddr - retAddr); |
| p->setSizeAndPinuseOfInuseFragment(0); // Set inuse bits on and size = 0 |
| } |
| return (void *)alignedAddr; |
| } |
| |
| // NAHeap::allocateHeapMemory() |
| // This algorithm is taken from Doug Lea's public domain malloc. |
| // Basic algorithm: |
| // If a small request (< 256 bytes minus per-fragment overhead): |
| // 1. If one exists, use a remainderless fragment in associated smallbin. |
| // (Remainderless means that there are too few excess bytes to |
| // represent as a fragment.) |
| // 2. If it is big enough, use the designated victim fragment. The |
| // designated fragment is used for quickly servicing small requests. |
| // 3. If one exists, split the smallest available fragment in a bin, |
| // saving remainder in dv. |
| // 4. If the top fragment is not large enough to handle the request, then |
| // call NAHeap::allocateBlock() to allocate another segment from the |
| // OS or to increase the size of the last segment. Set the size of or |
| // resize the top fragment. |
| // 5. Allocate memory from the top fragment. |
| // Otherwise, for a large request: |
| // 1. Find the smallest available binned fragment that fits, and use it |
| // if it is better fitting than dv fragment, splitting if necessary. |
| // 2. If better fitting than any binned fragment, use the dv fragment. |
| // 3. If the top fragment is not large enough to handle the request, then |
| // call NAHeap::allocateBlock() to allocate another segment from the |
| // OS or to increase the size of the last segment. Set the size of or |
| // resize the top fragment. |
| // 4. Allocate memory from the top fragment. |
| // |
| void * NAHeap::allocateHeapMemory(size_t userSize, NABoolean failureIsFatal) |
| { |
| |
| NAMutex mutex(threadSafe_, &mutex_); |
| |
| assert(type_ != NO_MEMORY_TYPE); |
| |
| // allocation of size 0. We return a NULL. Alternative would be to |
| // allocate 0 bytes. But this would waste memory to maintain a |
| // heap fragment of size 0. |
| if (userSize == 0) |
| return NULL; |
| |
| if (! checkSize(userSize, failureIsFatal)) |
| return NULL; |
| |
| // getSharedMemory() check alone is enough since it will return for both |
| // global and process stats heap. Leaving the rest of the condition here |
| // |
| if (getSharedMemory()) |
| { |
| // Check if you are within semaphore |
| if (! checkIfRTSSemaphoreLocked()) |
| NAExit(1); |
| } |
| |
| // Some additional bytes are added in debug mode, but the original userSize |
| // must be retained. |
| size_t additionalUserSize = userSize; |
| |
| #if (defined(_DEBUG) || defined(NSK_MEMDEBUG)) |
| if (debugLevel_) |
| dynamicAllocs.addEntry(userSize); |
| |
| // Allocate some additional memory to help with memory debugging. At |
| // debug level 2 on Windows, there are 40 bytes allocated to store |
| // 10 program counters of 4 bytes each. There are 4 bytes to store |
| // the original user size. Finally, there are between 8 and 11 |
| // additional bytes that are set to a pattern to allow buffer overflow |
| // detection. If the user asked for a number of bytes that is not |
| // a multiple of 4, then the rest of the word will be used for |
| // buffer flow detection (along with the next 8 bytes). NSK is similar |
| // to Windows, but doesn't store 40 extra bytes for program counters. |
| // A lookup table is the quickest way to determine how many extra bytes |
| // should be used. |
| if (debugLevel_ == 2) { |
| const size_t additionalBytes[4] = { 12, 15, 14, 13 }; |
| additionalUserSize += additionalBytes[userSize & 0x3]; |
| } |
| #endif // (defined(_DEBUG) || defined(NSK_MEMDEBUG)) |
| |
| size_t nb; |
| NAHeapFragment *p; |
| // Clean up prev_ and next_ pointers only when debugLevel_ is 0. |
| // When debugLevel_ is either 1 or 2, user memory gets reset to some |
| // eye catching pattern and it must not be overwritten for overflow |
| // checking logic to work. |
| NABoolean cleanUpPrevNext = debugLevel_== 0 ? TRUE : FALSE; |
| |
| // If the user is allocating a large piece of memory from a non-derived |
| // heap that is also not the runtime statistics shared memory segment, |
| // then allocate a NABlock using mmap(). This prevents any changes |
| // to the "top_" fragment and allows the memory used by the request to |
| // be returned to the operating system when the user frees it. |
| if (additionalUserSize >= MIN_MMAP_ALLOC_SIZE && parent_ == NULL && (! getSharedMemory())) |
| { |
| |
| nb = PAD_REQUEST(additionalUserSize); |
| size_t reqSize = NAHeap::granularityAlign(nb+(2*sizeof(size_t))+BLOCK_OVERHEAD+1); |
| NABlock *mmappedBlock = allocateMMapBlock(reqSize); |
| if (mmappedBlock != NULL) { |
| // Insert this NABlock after firstBlk_ so it doesn't interfere with |
| // logic that assumes firstBlk_ contains the top fragment |
| if (firstBlk_ != NULL) { |
| mmappedBlock->next_ = firstBlk_->next_; |
| firstBlk_->next_ = mmappedBlock; |
| } else { |
| mmappedBlock->next_ = NULL; |
| firstBlk_ = mmappedBlock; |
| } |
| |
| // Adjust the least addr |
| if (least_addr_ == NULL || (char*)mmappedBlock < least_addr_) |
| least_addr_ = (char*)mmappedBlock; |
| |
| // Set up the NAHeapFragment, adjust more statistics and return the memory. |
| p = mmappedBlock->alignAsFragment(); |
| p->setSizeOfMMapFragment(nb); |
| incrementStats(nb); |
| allocDebugProcess(p, userSize); |
| return p->getMemory(); |
| } |
| // Fall through and try to allocate the regular way. |
| } |
| |
| if (additionalUserSize <= MAX_SMALL_REQUEST) { |
| |
| nb = (additionalUserSize < MIN_REQUEST) ? |
| MIN_FRAGMENT_SIZE : PAD_REQUEST(additionalUserSize); |
| bindex_t idx = smallIndex(nb); |
| binmap_t smallbits = smallmap_ >> idx; |
| |
| if ((smallbits & 0x3) != 0) { // Remainderless fit to a smallbin. |
| idx += ~smallbits & 1; // Uses next bin if idx empty |
| NAHeapFragment *b = smallbinAt(idx); |
| p = b->getNext(); |
| size_t firstFragBit = p->getFirstFragmentBit(); |
| size_t fragSize = smallIndex2Size(idx); |
| if (!(p->fragmentSize() == fragSize)) |
| assert(p->fragmentSize() == fragSize); |
| unlinkFirstSmallFragment(b, p, idx); |
| p->setInuseAndPinuse(fragSize | firstFragBit); |
| #ifdef _DEBUG |
| if (p->fragmentSize() == TraceAllocSize) |
| saveTrafStack(getSAL(), p); |
| #endif // _DEBUG |
| incrementStats(fragSize); |
| allocDebugProcess(p, userSize); |
| checkMallocedFragment(p, nb); |
| return p->getMemory(cleanUpPrevNext); |
| } else if (nb > dvsize_) { |
| |
| if (smallbits != 0) { // Use fragment in next nonempty smallbin |
| binmap_t leftbits = (smallbits << idx) & leftBits(idx2bit(idx)); |
| binmap_t leastbit = leastBit(leftbits); |
| bindex_t i = bit2idx(leastbit); |
| NAHeapFragment *b = smallbinAt(i); |
| NAHeapFragment *p = b->getNext(); |
| size_t firstFragBit = p->getFirstFragmentBit(); |
| size_t fragSize = smallIndex2Size(i); |
| |
| assert(p->fragmentSize() == fragSize); |
| unlinkFirstSmallFragment(b, p, i); |
| size_t rsize = fragSize - nb; |
| // Fit here cannot be remainderless if 4byte sizes |
| if (sizeof(size_t) != 4 && rsize < MIN_FRAGMENT_SIZE) { |
| p->setInuseAndPinuse(fragSize | firstFragBit); |
| incrementStats(fragSize); |
| } else { |
| p->setSizeAndPinuseOfInuseFragment(nb | firstFragBit); |
| NAHeapFragment *r = p->fragmentPlusOffset(nb); |
| r->setSizeAndPinuseOfFreeFragment(rsize); |
| replaceDV(r, rsize); |
| incrementStats(nb); |
| } |
| allocDebugProcess(p, userSize); |
| checkMallocedFragment(p, nb); |
| #ifdef _DEBUG |
| if (p->fragmentSize() == TraceAllocSize) |
| saveTrafStack(getSAL(), p); |
| #endif // _DEBUG |
| return p->getMemory(cleanUpPrevNext); |
| } |
| |
| // Try to allocate from the smallest tree fragment |
| if (treemap_ != 0 && (p = tmallocSmall(nb)) != 0) { |
| incrementStats(p->fragmentSize()); |
| allocDebugProcess(p, userSize); |
| checkMallocedFragment(p, nb); |
| #ifdef _DEBUG |
| if (p->fragmentSize() == TraceAllocSize) |
| saveTrafStack(getSAL(), p); |
| #endif // _DEBUG |
| return p->getMemory(cleanUpPrevNext); |
| } |
| } |
| } else if (additionalUserSize >= MAX_REQUEST) { |
| nb = MAX_REQUEST + 100L; // Too big to allocate. Force failure in allocateBlock. |
| } else { |
| nb = PAD_REQUEST(additionalUserSize); |
| |
| if (treemap_ != 0 && (p = tmallocLarge(nb)) != 0) { |
| allocDebugProcess(p, userSize); |
| checkMallocedFragment(p, nb); |
| incrementStats(p->fragmentSize()); |
| #ifdef _DEBUG |
| if (p->fragmentSize() == TraceAllocSize) |
| saveTrafStack(getSAL(), p); |
| #endif // _DEBUG |
| return p->getMemory(cleanUpPrevNext); |
| } |
| } |
| |
| // The following code allocates memory from the designated victim if |
| // possible. |
| if (nb <= dvsize_) { |
| size_t rsize = dvsize_ - nb; |
| p = dv_; |
| size_t firstFragBit = p->getFirstFragmentBit(); |
| |
| if (rsize >= MIN_FRAGMENT_SIZE) { // split dv |
| NAHeapFragment *r = dv_ = p->fragmentPlusOffset(nb); |
| dvsize_ = rsize; |
| r->setSizeAndPinuseOfFreeFragment(rsize); |
| p->setSizeAndPinuseOfInuseFragment(nb | firstFragBit); |
| incrementStats(nb); |
| } else { // exhaust dv |
| size_t dvs = dvsize_; |
| dvsize_ = 0; |
| dv_ = 0; |
| p->setInuseAndPinuse(dvs | firstFragBit); |
| incrementStats(dvs); |
| } |
| allocDebugProcess(p, userSize); |
| checkMallocedFragment(p, nb); |
| #ifdef _DEBUG |
| if (p->fragmentSize() == TraceAllocSize) |
| saveTrafStack(getSAL(), p); |
| #endif // _DEBUG |
| return p->getMemory(cleanUpPrevNext); |
| } |
| |
| // If the top fragment is not large enough to hold the allocation, |
| // then call allocateBlock to allocate memory from either the |
| // operating system or the parent memory. |
| if (nb > topsize_) { |
| |
| // Save the current size of the first block because it will be lost |
| // if the first block is resized. The size is needed later in this |
| // function. |
| size_t prevFirstBlockSize = firstBlk_ ? firstBlk_->size_ : 0; |
| |
| // reqSize is the maximum size that may be needed to satisfy |
| // this request. NOTE: "+1" may not be nececessary but was part of |
| // the Doug Lea malloc(). |
| size_t reqSize = NAHeap::granularityAlign(nb+(2*sizeof(size_t))+BLOCK_OVERHEAD+1); |
| |
| // allocateBlock allocates memory of at least reqSize. |
| NABlock *newBlock = allocateBlock(reqSize, failureIsFatal); |
| if (newBlock == NULL) { |
| |
| // If the caller set up an error callback, then call it here. |
| if (errCallback_ != NULL) |
| (*errCallback_)(this, userSize); |
| |
| if (failureIsFatal) { |
| handleExhaustedMemory(); |
| abort(); |
| } |
| |
| // Caller will handle the error so just return null. |
| return NULL; |
| } |
| |
| if (least_addr_ == NULL || (char*)newBlock < least_addr_) |
| least_addr_ = (char*)newBlock; |
| |
| // If the current block was resized, then adjust the top size and |
| // adjust the size of prevFoot_ in the first fragment. |
| if (newBlock == firstBlk_) { |
| assert(newBlock->size_ > prevFirstBlockSize); |
| Lng32 increasedSize = (Lng32)(newBlock->size_ - prevFirstBlockSize); |
| resizeTop(topsize_ + increasedSize); |
| |
| // Adjust prevFoot_ in first fragment. |
| NAHeapFragment *firstFrag = newBlock->alignAsFragment(); |
| firstFrag->adjustBlockSize(increasedSize); |
| } else { |
| // Chain the new block on the linked-list of NABlocks. |
| newBlock->next_ = firstBlk_; |
| firstBlk_ = newBlock; |
| |
| // If the top fragment has not been initialized or has been |
| // exhausted in a previous allocation, then call initTop() to |
| // initialized a new top fragment. In other cases, the top |
| // fragment will contain useful memory that can be used |
| // by later allocations. The code calls addBlock() to add |
| // the top fragment to the freelist and to create a new top |
| // fragment in the newly allocated NABlock. |
| if (topsize_ == 0) { |
| initTop(newBlock); |
| top_->initializeFirstFragment(topsize_); |
| } else { |
| addBlock(newBlock); |
| } |
| } |
| } |
| |
| assert(topsize_ >= nb); |
| |
| // If execution reaches here, then the request can be satisfied from |
| // the top fragment. If the top fragment is large enough to have useful |
| // memory after satisfying the request, the top fragment will be split |
| // into two fragments. The first fragment will be returned to the caller, |
| // and a pointer to the second fragment will be assigned to the top |
| // fragment pointer. |
| size_t rsize = topsize_ - nb; |
| p = top_; |
| |
| size_t firstFragmentBit = p->getFirstFragmentBit(); |
| |
| NAHeapFragment *r; |
| |
| if (rsize >= MIN_FRAGMENT_SIZE) { |
| topsize_ = rsize; |
| r = top_ = p->fragmentPlusOffset(nb); |
| r->setSizeAndPinuse(rsize); |
| p->setSizeAndPinuseOfInuseFragment(nb|firstFragmentBit); |
| } else { |
| // The top fragment has been exhausted. Set top_ to point to |
| // the next fragment in case a later allocation resizes the |
| // current NABlock. |
| nb = topsize_; |
| topsize_ = 0; |
| top_ = p->fragmentPlusOffset(nb); |
| p->setInuseAndPinuse(nb|firstFragmentBit); |
| } |
| checkTopFragment(top_); |
| incrementStats(nb); |
| allocDebugProcess(p, userSize); |
| checkMallocedFragment(p, nb); |
| #ifdef _DEBUG |
| if (p->fragmentSize() == TraceAllocSize) |
| saveTrafStack(getSAL(), p); |
| #endif // _DEBUG |
| return p->getMemory(cleanUpPrevNext); |
| } |
| |
| // NAHeap::deallocateHeapMemory() |
| // Memory is deallocated in this function. The memory fragments |
| // that may exist before and after this freed fragment are coalesced |
| // with the freed memory if possible. If the previous memory or |
| // next memory is the designated victim, then the designated victim |
| // will be adjusted to include this memory. If the next memory is |
| // the top fragment, then the top fragment will now include the |
| // coalesced memory. If all memory in an NABlock is freed, then |
| // NAMemory::deallocateFreeBlock() will be called to either free |
| // the block to the OS or to the parent memory. If the memory |
| // is not all freed and is not the designated victim or top fragment, |
| // the fragment will be linked into a small bin or tree. |
| void NAHeap::deallocateHeapMemory(void* addr) |
| { |
| // If addr is NULL, we are done. |
| // This is apparently standard behavior. I don't like it, but some of |
| // our code relies on it. |
| if (addr == NULL) |
| return; |
| |
| |
| assert(type_ != NO_MEMORY_TYPE); |
| |
| NAMutex mutex(threadSafe_, &mutex_); |
| |
| if (getSharedMemory()) |
| { |
| // Check if you are within semaphore |
| if (! checkIfRTSSemaphoreLocked()) |
| NAExit(1); |
| } |
| |
| HEAPLOG_DELETE_ENTRY(addr, heapID_.heapNum) |
| |
| // Get a pointer to the NAHeapFragement for this memory address |
| NAHeapFragment *p = NAHeapFragment::memToFragment(addr); |
| |
| if (p->fragmentSize() == 0 && okCinuse(p) && okPinuse(p)) // allocateAlignedHeapMemory fragment? |
| { |
| p = (NAHeapFragment *)((size_t)(p) - p->getPrevFoot()); |
| } |
| |
| #ifdef _DEBUG |
| if (TraceAllocSize > 0 && p->fragmentSize() == TraceAllocSize) |
| delTrafStack(getSAL(), p); |
| #endif // _DEBUG |
| |
| checkInuseFragment(p); |
| if (!(RTCHECK(okAddress(p) && okCinuse(p)))) { |
| USAGE_ERROR_ACTION; |
| return; |
| } |
| |
| // deallocDebugProcess() is only called in debug builds. If |
| // MEMDEBUG=1, then the freed memory is set to a 0xfdfdfdfd |
| // pattern. If MEMDEBUG=2, then a check is made to see if |
| // there was a buffer overflow. |
| deallocDebugProcess(p); |
| |
| // get the size of the current fragment |
| size_t psize = p->fragmentSize(); |
| |
| // update allocation statistics |
| decrementStats(psize); |
| |
| if (type_ == EXECUTOR_MEMORY && |
| deallocTraceArray != NULL && |
| (memcmp(name_, "Global Executor Memo", 20) == 0)) |
| { |
| deallocCount += 1; |
| unsigned short i = deallocTraceIndex == deallocTraceEntries - 1 ? 0 : deallocTraceIndex + 1; |
| (*deallocTraceArray)[i].begin_ = (void *)p; |
| (*deallocTraceArray)[i].end_ = (void *)((char *)p + psize); |
| deallocTraceIndex = i; |
| } |
| |
| if (p->isMMapped()) { |
| deallocateFreeBlock(p); |
| return; |
| } |
| |
| // Get a pointer to the next fragment |
| NAHeapFragment *next = p->fragmentPlusOffset(psize); |
| // Pointer to the previous fragment |
| NAHeapFragment *prev = NULL; |
| |
| size_t firstFragmentBit; // Is this the first fragment of a NABlock? |
| |
| // Save the pointer to this fragment and the size |
| NAHeapFragment *q = p; |
| size_t qsize = psize; |
| |
| Int32 mergeFlags = NO_MERGE; |
| |
| if (p->pinuse()) { |
| firstFragmentBit = p->getFirstFragmentBit(); |
| } else { |
| mergeFlags |= BACKWARD_MERGE; |
| size_t prevsize = p->getPrevFoot(); |
| prev = p->fragmentMinusOffset(prevsize); |
| firstFragmentBit = prev->getFirstFragmentBit(); |
| psize += prevsize; |
| p = prev; |
| if (RTCHECK(okAddress(prev))) { // consolidate backward |
| if (p != dv_) { |
| unlinkFragment(p, prevsize); |
| } else if (next->cpinuse()) { // CINUSE_BIT and PINUSE_BIT set. |
| // No further consolidation can take place. The previous |
| // fragment was the designated victim so update the dvsize. |
| dvsize_ = psize; |
| p->setFreeWithPinuse(psize, next); |
| p->setHeadBits(firstFragmentBit); |
| if (p->occupiesCompleteNABlock()) |
| deallocateFreeBlock(p); |
| else |
| q->releaseFreePages(prev, next, mergeFlags); |
| return; |
| } |
| } else { |
| USAGE_ERROR_ACTION; |
| return; |
| } |
| } |
| |
| if (!(RTCHECK(okNext(p, next) && okPinuse(next)))) { |
| USAGE_ERROR_ACTION; |
| return; |
| } |
| |
| if ((!next->cinuse()) || |
| ((next == top_) && (next->isFencePost()) && (topsize_ == 0))) |
| { // consolidate forward |
| mergeFlags |= FORWARD_MERGE; |
| if (next == top_) { |
| if ((top_->isFencePost()) && (topsize_ == 0)) |
| top_->clearPinuse(); // If top was set to fencepost because top was |
| // exhausted, reset the pinuse bit of the |
| // fencepost, because the prev of the fencepost |
| // is now top_, which should not be in use. |
| size_t tsize = topsize_ += psize; |
| top_ = p; |
| p->setSizeAndPinuse(tsize); |
| p->setHeadBits(firstFragmentBit); |
| if (p == dv_) { |
| dv_ = NULL; |
| dvsize_ = 0; |
| } |
| if (p->occupiesCompleteNABlock()) |
| deallocateFreeBlock(p); |
| else |
| q->releaseFreePages(prev, next, mergeFlags); |
| return; |
| } else if (next == dv_) { |
| size_t dsize = dvsize_ += psize; |
| dv_ = p; |
| p->setSizeAndPinuseOfFreeFragment(dsize); |
| p->setHeadBits(firstFragmentBit); |
| if (p->occupiesCompleteNABlock()) |
| deallocateFreeBlock(p); |
| else |
| q->releaseFreePages(prev, next, mergeFlags); |
| return; |
| } else { |
| size_t nsize = next->fragmentSize(); |
| psize += nsize; |
| unlinkFragment(next, nsize); |
| p->setSizeAndPinuseOfFreeFragment(psize); |
| p->setHeadBits(firstFragmentBit); |
| if (p == dv_) { |
| dvsize_ = psize; |
| if (p->occupiesCompleteNABlock()) |
| deallocateFreeBlock(p); |
| else |
| q->releaseFreePages(prev, next, mergeFlags); |
| return; |
| } |
| } |
| } else { // This fragment could not be consolidated forward |
| p->setFreeWithPinuse(psize, next); |
| p->setHeadBits(firstFragmentBit); |
| } |
| |
| // If this fragment uses the whole NABlock, then deallocate it. |
| if (p->occupiesCompleteNABlock()) { |
| // Only return if the deallocation really occurred. |
| if (deallocateFreeBlock(p)) |
| return; |
| } |
| |
| q->releaseFreePages(prev, next, mergeFlags); |
| |
| // Insert the free fragment into either a small bin or tree |
| insertFragment(p, psize); |
| checkFreeFragment(p); |
| |
| return; |
| } |
| |
| #endif // !MUSE |
| |
| #if (defined(_DEBUG) || defined(NSK_MEMDEBUG)) |
| void NAHeap::dumpHeapInfo(ostream* outstream, Lng32 indent) |
| { |
| NAMutex mutex(threadSafe_, &mutex_); |
| |
| char ind[100]; |
| |
| Lng32 indIdx = 0; |
| for (; indIdx < indent; indIdx++) |
| ind[indIdx] = ' '; |
| ind[indIdx] = '\0'; |
| |
| // Verify the malloc state. This can be fairly heavyweight depending |
| // on the number of allocations. |
| checkMallocState(); |
| |
| if (!outstream) |
| outstream = &cerr; |
| *outstream << ind << "Dump of Heap " << this << ':' << endl |
| << ind << "Name: " << name_ << endl |
| << ind << "Parent: " |
| << ind << (parent_ ? parent_->name_ : " ") << endl |
| << ind << "Initial Size (Bytes): " << initialSize_ << endl |
| << ind << "Maximum Size (Bytes): " << maximumSize_ << endl |
| << ind << "Increment Size (Bytes): " << incrementSize_ << endl |
| << ind << "Allocated Size (Bytes): " << allocSize_ << endl |
| << ind << "High Water Mark (Bytes): " << highWaterMark_ << endl |
| << ind << "Interval Water Mark (Bytes): " << intervalWaterMark_ << endl |
| << ind << "Allocated Units: " << allocCnt_ << endl |
| << ind << "Total Size (Bytes): " << totalSize_ << endl |
| << ind << "Block Count: " << blockCnt_ << endl; |
| |
| MemoryStats freeStats; |
| MemoryStats allocStats; |
| NABlock * p = firstBlk_; |
| while (p) { |
| p->dump(outstream, debugLevel_, freeStats, allocStats, top_, indent); |
| p = p->next_; |
| } |
| |
| if (debugLevel_ == 0) { |
| *outstream << "dynamicAllocs not kept because debugLevel_ == 0" << endl; |
| } else { |
| dynamicAllocs.dump(outstream, "all allocated", indent); |
| } |
| allocStats.dump(outstream, "leftover allocated", indent); |
| freeStats.dump(outstream, "free", indent); |
| *outstream << ind << "---------------------------------------------" << endl; |
| |
| // we are done with this heap. Go thru all the derived heaps |
| // and dump their info too |
| NAMemory *derivedHeap = memoryList_; |
| while (derivedHeap) { |
| derivedHeap->dump(outstream, indent + 2); |
| derivedHeap = derivedHeap->nextEntry_; |
| } |
| } |
| #endif // (defined(_DEBUG) || defined(NSK_MEMDEBUG)) ... |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| // --------------------------------------------------------------------------- |
| // NABlock methods |
| // --------------------------------------------------------------------------- |
| |
| #if (defined(_DEBUG) || defined(NSK_MEMDEBUG)) |
| void NABlock::dump(ostream* outstream, |
| Lng32 debugLevel, |
| MemoryStats& freeStats, |
| MemoryStats& allocStats, |
| NAHeapFragment *top, |
| Lng32 indent) |
| { |
| char ind[100]; |
| Lng32 indIdx = 0; |
| for (; indIdx < indent; indIdx++) |
| ind[indIdx] = ' '; |
| ind[indIdx] = '\0'; |
| if (!outstream) |
| outstream = &cerr; |
| |
| // go thru all allocated and free fragments and for Windows dump the |
| // stack trace for allocated fragments |
| NAHeapFragment *q = alignAsFragment(); |
| |
| while (blockHolds(q) && q != top && !q->isFencePost()) { |
| |
| if (q->cinuse()) { |
| // this is an allocated fragment. |
| allocStats.addEntry(q->fragmentSize()); |
| |
| |
| } else { |
| // this is a free fragment |
| freeStats.addEntry(q->fragmentSize()); |
| } |
| |
| // Go to next fragment |
| q = q->nextFragment(); |
| } |
| } |
| #endif // (defined(_DEBUG) || defined(NSK_MEMDEBUG)) ... |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| MemoryStats::MemoryStats() |
| : count_(0), |
| sum_(0.0), |
| sum2_(0.0) |
| { |
| for (Lng32 i = 0; i < 18; i++) |
| histBuckets_[i] = 0; |
| } |
| |
| void MemoryStats::addEntry(size_t value) |
| { |
| count_++; // one more entry |
| double fvalue = (double)value; |
| sum_ += fvalue; |
| sum2_ += (fvalue * fvalue); |
| // determine the histogram bucket |
| value = value >> 5; // everything <= 32 ends up in the first bucket |
| Lng32 i = 0; |
| while(value) { |
| value = value >> 1; |
| i++; |
| } |
| // everything bigger than 2 MB ends up in the last bucket |
| i = (i < 18) ? i : 17; |
| histBuckets_[i]++; |
| } |
| |
| #if (defined(_DEBUG) || defined(NSK_MEMDEBUG)) |
| void MemoryStats::dump(ostream* outstream, const char * name, Lng32 indent) |
| { |
| char ind[100]; |
| Lng32 indIdx = 0; |
| for (; indIdx < indent; indIdx++) |
| ind[indIdx] = ' '; |
| ind[indIdx] = '\0'; |
| if (!outstream) |
| outstream = &cerr; |
| *outstream << ind << name << " units:" << endl |
| << ind << "# of entries: " << count_ << endl |
| << ind << "average size: " << (count_ ? (sum_/count_) : 0.0) |
| << endl |
| << ind << "variance: " << ((count_ > 1) ? |
| ((1.0/(double)(count_ - 1)) * |
| (sum2_ - (1.0/(double)count_) * sum_ * sum_)) : 0.0) << endl; |
| |
| Lng32 size = 16; |
| char unit = 'B'; |
| for (Lng32 i = 0; i < 17; i++) { |
| size *= 2; |
| if (i == 5) { |
| unit = 'K'; |
| size /= 1024; |
| } |
| else if (i == 15) { |
| size = size/1024; |
| unit = 'M'; |
| } |
| *outstream << ind << "# entries <= " << size << unit |
| << " : " << histBuckets_[i] << endl; |
| } |
| *outstream << ind << "# entries > " << size << unit |
| << " : " << histBuckets_[17] << endl; |
| |
| } |
| #endif // (defined(_DEBUG) || defined(NSK_MEMDEBUG)) |
| |
| |
| // -------------------- Debugging Support --------------------------- |
| #if ( defined(_DEBUG) || defined(NSK_MEMDEBUG) ) |
| |
| // The below values are used for buffer overflow detection. Lookup |
| // tables are used so that the logic for checking the remaining |
| // bytes in a word can be very quick. |
| const UInt32 buf_overflow_val = 0xF2F2F2F2; |
| #ifdef NA_LITTLE_ENDIAN |
| const UInt32 overflow_mask[4] = { 0, 0xFFFFFF00, 0xFFFF0000, 0xFF000000 }; |
| const UInt32 overflow_val[4] = { 0, 0xF2F2F200, 0xF2F20000, 0xF2000000 }; |
| #else // NA_BIG_ENDIAN |
| const UInt32 overflow_mask[4] = { 0, 0xFFFFFF, 0xFFFF, 0xFF }; |
| const UInt32 overflow_val[4] = { 0, 0xF2F2F2, 0xF2F2, 0xF2 }; |
| #endif |
| |
| // doAllocDebugProcess() is called with debugLevel_ is set to at |
| // least 1. It resets newly allocated memory to a 0xfafafafa. On |
| // Windows when debugLevel is set to 2, this function also adds |
| // information to indicate whether the end of the buffer has been |
| // overwritten and saves the last 10 levels of the stack frame. |
| |
| void |
| NAHeap::doAllocDebugProcess(NAHeapFragment *p, size_t userSize) |
| { |
| // Reset the user requested memory to a 0xfafafafa pattern. |
| // Disabled for now, see Todo 1) |
| // memset(p->getMemory(), 0xfa, userSize); |
| |
| if (debugLevel_ == 2 && p->fragmentSize() < INT_MAX) { |
| |
| // Determine location that the user requested size is stored |
| // in the memory. |
| size_t userSizeOffset = p->fragmentSize() - (2 * sizeof(size_t)); |
| UInt32 * userSizePtr = (UInt32 *)((char*)p->getMemory() + userSizeOffset); |
| |
| // Set the integer pointer to the last word in the user |
| // requested memory |
| UInt32 *lastWord = (UInt32*)((char*)p->getMemory() + |
| ((userSize - 1) & ~(0x3))); |
| |
| // Set the bits in the first word. If the user memory uses the |
| // whole word, then the user memory will not be modified. |
| UInt32 remainder = userSize & 0x3; |
| lastWord[0] = (lastWord[0] & ~overflow_mask[remainder]) |
| | (buf_overflow_val & overflow_mask[remainder]); |
| // Set the bits in the next 2 words. |
| lastWord[1] = buf_overflow_val; |
| lastWord[2] = buf_overflow_val; |
| // Store the userSize in the buffer. |
| // Note that the userSizePtr may point to the same location of lastWord[2] |
| *userSizePtr = (UInt32) userSize; |
| |
| } |
| } |
| |
| // doDeallocDebugProcess() resets the deallocated memory to |
| // a 0xfdfdfdfd. This function is only called when debugLevel_ is |
| // at set to at least 1. |
| |
| void |
| NAHeap::doDeallocDebugProcess(NAHeapFragment *p) |
| { |
| if (debugLevel_ == 2 && p->fragmentSize() < INT_MAX) { |
| // Check for buffer overflows |
| p->checkBufferOverflow(); |
| } |
| |
| // Reset the user memory to a 0xfdfdfdfd pattern. |
| // size_t userSize = p->fragmentSize() - sizeof(size_t); |
| // Disabled for now, see Todo 1) |
| // memset(p->getMemory(), 0xfd, userSize); |
| } |
| |
| // Check properties of any fragment, whether free, inuse, etc. |
| void |
| NAHeap::doCheckAnyFragment(NAHeapFragment *p) |
| { |
| assert((isAligned(p->getMemory())) || (p->isFencePost())); |
| assert(okAddress(p)); |
| } |
| |
| // Check properties of top fragment |
| void |
| NAHeap::doCheckTopFragment(NAHeapFragment *p) |
| { |
| NABlock* sp = NABlock::blockHolding(firstBlk_,p); |
| size_t sz = p->fragmentSize(); |
| assert(sp != 0); |
| assert(okAddress(p)); |
| assert(p->pinuse()); |
| if (topsize_ == 0) { |
| assert(p->isFencePost()); |
| } else { |
| assert(!p->isFencePost()); |
| assert(isAligned(p->getMemory())); |
| assert(sz == topsize_); |
| assert(sz > 0); |
| assert(sz == (size_t)sp + sp->size_ |
| - (size_t)p - sp->firstFragmentOffset()); |
| assert(!p->nextPinuse()); |
| } |
| } |
| |
| // Check properties of inuse fragments |
| void |
| NAHeap::doCheckInuseFragment(NAHeapFragment *p) |
| { |
| doCheckAnyFragment(p); |
| assert(p->cinuse()); |
| assert(p->nextPinuse()); |
| // If not pinuse, previous fragment has OK offset |
| assert(p->pinuse() || p->prevFragment()->nextFragment() == p); |
| } |
| |
| // Check properties of free fragments |
| void |
| NAHeap::doCheckFreeFragment(NAHeapFragment *p) |
| { |
| size_t sz = p->fragmentSize(); |
| NAHeapFragment *next = p->fragmentPlusOffset(sz); |
| doCheckAnyFragment(p); |
| assert(!p->cinuse()); |
| assert(!p->nextPinuse()); |
| if (p != dv_ && p != top_) { |
| if (sz >= MIN_FRAGMENT_SIZE) { |
| assert((sz & FRAG_ALIGN_MASK) == 0); |
| assert(isAligned(p->getMemory())); |
| assert(next->getPrevFoot() == sz); |
| assert(p->pinuse()); |
| assert (next == top_ || next->cinuse()); |
| assert(p->getNext()->getPrev() == p); |
| assert(p->getPrev()->getNext() == p); |
| } |
| else // markers are always of size size_t |
| assert(sz == sizeof(size_t)); |
| } |
| } |
| |
| // Check properties of malloced fragments at the point they are malloced |
| void |
| NAHeap::doCheckMallocedFragment(NAHeapFragment *p, size_t s) |
| { |
| if (p != 0) { |
| size_t sz = p->fragmentSize(); |
| doCheckInuseFragment(p); |
| assert((sz & FRAG_ALIGN_MASK) == 0); |
| assert(sz >= MIN_FRAGMENT_SIZE); |
| assert(sz >= s); |
| |
| // size is less than MIN_FRAGMENT_SIZE more than request |
| assert(sz < (s + MIN_FRAGMENT_SIZE)); |
| } |
| } |
| |
| // Check a tree and its subtrees |
| void |
| NAHeap::doCheckTree(NATreeFragment *t) |
| { |
| NATreeFragment *head = NULL; |
| NATreeFragment *u = t; |
| bindex_t tindex = t->getIndex(); |
| size_t tsize = t->fragmentSize(); |
| bindex_t idx = computeTreeIndex(tsize); |
| |
| assert(tindex == idx); |
| assert(tsize >= MIN_LARGE_SIZE); |
| assert(tsize >= minsizeForTreeIndex(idx)); |
| assert((idx == NTREEBINS-1) || (tsize < minsizeForTreeIndex((idx+1)))); |
| |
| do { // traverse through chain of same-sized nodes |
| doCheckAnyFragment((NAHeapFragment*)u); |
| assert(u->getIndex() == tindex); |
| assert(u->fragmentSize() == tsize); |
| assert(!u->cinuse()); |
| assert(!u->nextPinuse()); |
| assert(u->getNext()->getPrev() == u); |
| assert(u->getPrev()->getNext() == u); |
| if (u->getParent() == NULL) { |
| assert(u->getChild(0) == NULL); |
| assert(u->getChild(1) == NULL); |
| } |
| else { |
| assert(head == 0); // only one node on chain has parent |
| head = u; |
| assert(u->getParent() != u); |
| assert (u->getParent()->getChild(0) == u || |
| u->getParent()->getChild(1) == u || |
| *((NATreeFragment**)u->getParent()) == u); |
| if (u->getChild(0) != NULL) { |
| assert(u->getChild(0)->getParent() == u); |
| assert(u->getChild(0) != u); |
| doCheckTree(u->getChild(0)); |
| } |
| if (u->getChild(1) != NULL) { |
| assert(u->getChild(1)->getParent() == u); |
| assert(u->getChild(1) != u); |
| doCheckTree(u->getChild(1)); |
| } |
| if (u->getChild(0) != NULL && u->getChild(1) != NULL) { |
| assert(u->getChild(0)->fragmentSize() < |
| u->getChild(1)->fragmentSize()); |
| } |
| } |
| u = (NATreeFragment*)u->getNext(); |
| } while (u != t); |
| assert(head != NULL); |
| } |
| |
| // Check all the fragments in a treebin |
| void |
| NAHeap::doCheckTreebin(bindex_t i) |
| { |
| NATreeFragment **tb = treebinAt(i); |
| NATreeFragment *t = *tb; |
| NABoolean empty = (treemap_ & (1U << i)) == 0; |
| if (t == 0) |
| assert(empty); |
| if (!empty) |
| doCheckTree(t); |
| } |
| |
| // Check all the fragments in a smallbin |
| void |
| NAHeap::doCheckSmallbin(bindex_t i) |
| { |
| NAHeapFragment *b = smallbinAt(i); |
| NAHeapFragment *p = b->getPrev(); |
| NABoolean empty = (smallmap_ & (1U << i)) == 0; |
| if (p == b) |
| assert(empty); |
| if (!empty) { |
| for (; p != b; p = p->getPrev()) { |
| size_t size = p->fragmentSize(); |
| NAHeapFragment *q; |
| // each fragment claims to be free |
| doCheckFreeFragment(p); |
| // fragment belongs in bin |
| assert(smallIndex(size) == i); |
| assert(p->getPrev() == b || |
| p->getPrev()->fragmentSize() == p->fragmentSize()); |
| // fragment is followed by an inuse fragment |
| q = p->nextFragment(); |
| if (!q->isFencePost()) |
| doCheckInuseFragment(q); |
| } |
| } |
| } |
| |
| // Check all of the properties in NAMemory related to Doug Lea's malloc() |
| void |
| NAHeap::doCheckMallocState() |
| { |
| bindex_t i; |
| size_t total; |
| |
| // check bins |
| for (i = 0; i < NSMALLBINS; ++i) |
| doCheckSmallbin(i); |
| for (i = 0; i < NTREEBINS; ++i) |
| doCheckTreebin(i); |
| |
| if (dvsize_ != 0) { // check dv fragment |
| doCheckAnyFragment(dv_); |
| assert(dvsize_ == dv_->fragmentSize()); |
| assert(dvsize_ >= MIN_FRAGMENT_SIZE); |
| assert(binFind(dv_) == 0); |
| } |
| |
| if (top_ != NULL) { // check top fragment |
| doCheckTopFragment(top_); |
| if (topsize_ == 0) { |
| assert(top_->isFencePost()); |
| } else { |
| assert(topsize_ == top_->fragmentSize()); |
| } |
| assert(binFind(top_) == 0); |
| } |
| |
| total = traverseAndCheck(); |
| assert(total <= totalSize_); |
| assert(allocSize_ <= highWaterMark_); |
| assert(allocSize_ <= intervalWaterMark_); |
| |
| // also check for overflow |
| if (debugLevel_ == 2) |
| checkForOverflow(); |
| |
| } |
| |
| // Find x in a bin. Used in other check functions. |
| NABoolean |
| NAHeap::binFind(NAHeapFragment *x) |
| { |
| size_t size = x->fragmentSize(); |
| if (isSmall(size)) { |
| bindex_t sidx = smallIndex(size); |
| NAHeapFragment *b = smallbinAt(sidx); |
| if (smallmapIsMarked(sidx)) { |
| NAHeapFragment *p = b; |
| do { |
| if (p == x) |
| return TRUE; |
| } while ((p = p->getNext()) != b); |
| } |
| } else { |
| bindex_t tidx = computeTreeIndex(size); |
| if (treemapIsMarked(tidx)) { |
| NATreeFragment *t = *treebinAt(tidx); |
| size_t sizebits = size << leftshiftForTreeIndex(tidx); |
| while (t != 0 && t->fragmentSize() != size) { |
| t = t->getChild((sizebits >> (SIZE_T_BITSIZE - 1)) & 1); |
| sizebits <<= 1; |
| } |
| if (t != 0) { |
| NATreeFragment *u = t; |
| do { |
| if (u == (NATreeFragment*)x) |
| return TRUE; |
| } while ((u = (NATreeFragment*)u->getNext()) != t); |
| } |
| } |
| } |
| return FALSE; |
| } |
| |
| // Traverse each fragment and check it; return total |
| size_t |
| NAHeap::traverseAndCheck() |
| { |
| size_t sum = 0; |
| size_t blockCnt = 0; |
| NABlock *s = firstBlk_; |
| |
| if (s != NULL) { |
| sum += topsize_ + BLOCK_OVERHEAD; |
| |
| while (s != NULL) { |
| ++blockCnt; |
| NAHeapFragment *q = s->alignAsFragment(); |
| |
| // The first fragment of an externally allocated segment should |
| // not have the first fragment bit set. If the NABlock was allocated |
| // using mmap, then either the first fragment should have the first |
| // fragment bit set or should have the MMAPPED_BIT set. If mmap was |
| // not used to allocate the NABlock, then the first fragment should |
| // have the first fragment bit set. All other fragments should not |
| // have either bit set. |
| if (s->isExternSegment()) { |
| assert(!q->getFirstFragmentBit()); |
| } else { |
| if (s->isMMapped()) { |
| assert(q->getFirstFragmentBit() || q->isMMapped()); |
| } else { |
| assert(q->getFirstFragmentBit()); |
| } |
| } |
| |
| NAHeapFragment *lastq = NULL; |
| assert(q->pinuse()); |
| while (s->blockHolds(q) && q != top_) { |
| sum += q->fragmentSize(); |
| if (q->cinuse()) { |
| assert(!binFind(q)); |
| doCheckInuseFragment(q); |
| } else { |
| assert(q == dv_ || binFind(q)); |
| assert(lastq == NULL || lastq->cinuse()); // Not 2 consecutive free |
| doCheckFreeFragment(q); |
| } |
| lastq = q; |
| q = q->nextFragment(); |
| |
| // Quit if this is a fencepost. This is put here because a fencepost |
| // may have the same bit set as a mmap'ed fragment. |
| if (q->isFencePost()) |
| break; |
| assert(!q->getFirstFragmentBit()); |
| assert(!q->isMMapped()); |
| } |
| s = (NABlock*)s->next_; |
| } |
| } |
| assert (blockCnt == blockCnt_); |
| return sum; |
| } |
| |
| void |
| NAHeapFragment::checkBufferOverflow() |
| { |
| size_t userSizeOffset = fragmentSize() - (2 * sizeof(size_t)); |
| UInt32 *userSizePtr = (UInt32*)((char*)getMemory() + userSizeOffset); |
| UInt32 userSize = *userSizePtr; |
| |
| // Set pointer to the last word of the user allocated memory. |
| UInt32 *lastWord = (UInt32*)((char*)getMemory() |
| + ((userSize - 1) & ~(0x3))); |
| |
| // Check for buffer overflow; |
| UInt32 remainder = userSize & 0x3; |
| if ((lastWord[0] & overflow_mask[remainder]) != overflow_val[remainder] || |
| lastWord[1] != buf_overflow_val) { |
| // char buf[128]; |
| // sprintf(buf, "Buffer overflow of memory allocated at %p (Size=" PFSZ ")\n", |
| // getMemory(), userSize); |
| // cerr << buf; |
| assert(0); // overflow detected |
| } |
| if (userSizePtr != &lastWord[2] && lastWord[2] != buf_overflow_val) |
| assert(0); // overflow detected |
| } |
| |
| // Traverse over all of the blocks for this NAHeap to see if any of them contain |
| // buffers that have overflowed. |
| void |
| NAHeap::checkForOverflow() |
| { |
| NABlock *bp = firstBlk_; |
| while (bp != NULL) { |
| NAHeapFragment *fp = bp->alignAsFragment(); |
| while (!fp->isFencePost()) { |
| if (fp->cinuse()) { |
| fp->checkBufferOverflow(); |
| } |
| fp = fp->nextFragment(); |
| } |
| bp = bp->next_; |
| } |
| } |
| |
| #endif // ( defined(_DEBUG) || defined(NSK_MEMDEBUG) ) |
| |
| #ifdef _DEBUG |
| void |
| NAHeap::setAllocTrace() |
| { |
| static THREAD_P bool traceEnvChecked = 0; |
| |
| if (!traceEnvChecked) |
| { |
| char *traceEnv = getenv("TRACE_ALLOC_SIZE"); |
| if (traceEnv != NULL && atol(traceEnv) > 0) |
| TraceAllocSize = atol(traceEnv); |
| else |
| TraceAllocSize = 0; |
| traceEnvChecked = true; |
| } |
| if (TraceAllocSize != 0 && memcmp(name_, "Process Stats Heap", 18)) |
| la_ = new LIST(TrafAddrStack *)(NULL); // on C++ heap |
| else |
| la_ = NULL; |
| } |
| #endif // _DEBUG |
| |
| |
| // ----------------------------------------------------------------------- |
| // methods for class DefaultIpcHeap |
| // ----------------------------------------------------------------------- |
| |
| // This object is allocated on either global or a passed-in heap. |
| // Its dstr (defined here) is never explicitly called. |
| DefaultIpcHeap::~DefaultIpcHeap() |
| { |
| destroy(); |
| } |
| void DefaultIpcHeap::destroy() |
| { |
| // This class can't delete anything because it doesn't keep track of |
| // any of its allocations. |
| } |
| |
| void * DefaultIpcHeap::allocateIpcHeapMemory(size_t size, NABoolean failureIsFatal) |
| { |
| if (! checkSize(size, failureIsFatal)) |
| return NULL; |
| |
| void * rc = ::operator new(size); |
| HEAPLOG_ADD_ENTRY(rc, size, heapID_.heapNum, getName()) |
| if (rc) return rc; |
| if (failureIsFatal) |
| { |
| handleExhaustedMemory(); |
| abort(); |
| } |
| return rc; |
| } |
| |
| void DefaultIpcHeap::deallocateIpcHeapMemory(void * buffer) |
| { |
| HEAPLOG_DELETE_ENTRY(buffer, heapID_.heapNum) |
| ::operator delete(buffer); |
| } |
| |
| #if (defined(_DEBUG) || defined(NSK_MEMDEBUG)) |
| void DefaultIpcHeap::dumpIpcHeapInfo(ostream* outstream, Lng32 indent) |
| { |
| // This class doesn't keep track of anything so it can't dump anything |
| // useful. |
| } |
| #endif |
| |
| // Release unused memory in the heap back to kernel. This will help |
| // reduce the memory pressure and increase the free page count. |
| void NAHeapFragment::releaseFreePages(NAHeapFragment *prev, |
| NAHeapFragment *next, |
| Int32 mergeFlags) |
| { |
| } |
| |
| SEG_ID gStatsSegmentId_ = -1; |
| #define RMS_SEGMENT_ID_OFFSET 0x10000000 |
| |
| SEG_ID getStatsSegmentId() |
| |
| { |
| Int32 segid; |
| Int32 error; |
| if (gStatsSegmentId_ == -1) |
| { |
| error = msg_mon_get_my_segid(&segid); |
| assert(error == 0);//XZFIL_ERR_OK); |
| gStatsSegmentId_ = segid + RMS_SEGMENT_ID_OFFSET; |
| } |
| return gStatsSegmentId_; |
| } |
| |