diff -r 6aeb7a756187 -r 3c88a81ff781 utilities/standaloneallocator/newallocator.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/utilities/standaloneallocator/newallocator.cpp Fri Oct 15 17:30:59 2010 -0400 @@ -0,0 +1,3311 @@ +/* + * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). + * + * This file is part of Qt Web Runtime. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +/**************************************************************************** + * + * This file is part of the Symbian application wrapper of the Qt Toolkit. + * + * The memory allocator is backported from Symbian OS, and can eventually + * be removed from Qt once it is built in to all supported OS versions. + * The allocator is a composite of three allocators: + * - A page allocator, for large allocations + * - A slab allocator, for small allocations + * - Doug Lea's allocator, for medium size allocations + * + ***************************************************************************/ + + +#include +#include +#include +#include + +#ifndef QT_SYMBIAN_HAVE_U32STD_H +struct SThreadCreateInfo + { + TAny* iHandle; + TInt iType; + TThreadFunction iFunction; + TAny* iPtr; + TAny* iSupervisorStack; + TInt iSupervisorStackSize; + TAny* iUserStack; + TInt iUserStackSize; + TInt iInitialThreadPriority; + TPtrC iName; + TInt iTotalSize; // Size including any extras (must be a multiple of 8 bytes) + }; + +struct SStdEpocThreadCreateInfo : public SThreadCreateInfo + { + RAllocator* iAllocator; + TInt iHeapInitialSize; + TInt iHeapMaxSize; + TInt iPadding; // Make structure size a multiple of 8 bytes + }; +#else +#include +#endif +#include + +//Named local chunks require support from the kernel, which depends on Symbian^3 +#define NO_NAMED_LOCAL_CHUNKS +//Reserving a minimum heap size is not supported, because the implementation does not know what type of +//memory to use. DLA memory grows upwards, slab and page allocators grow downwards. +//This would need kernel support to do properly. +#define NO_RESERVE_MEMORY + +//The BTRACE debug framework requires Symbian OS 9.4 or higher. +//Required header files are not included in S60 5.0 SDKs, but +//they are available for open source versions of Symbian OS. +//Note that although Symbian OS 9.3 supports BTRACE, the usage in this file +//depends on 9.4 header files. + +//This debug flag uses BTRACE to emit debug traces to identify the heaps. +//Note that it uses the ETest1 trace category which is not reserved +//#define TRACING_HEAPS +//This debug flag uses BTRACE to emit debug traces to aid with debugging +//allocs, frees & reallocs. It should be used together with the KUSERHEAPTRACE +//kernel trace flag to enable heap tracing. +//#define TRACING_ALLOCS +//This debug flag turns on tracing of the call stack for each alloc trace. +//It is dependent on TRACING_ALLOCS. +//#define TRACING_CALLSTACKS + +#if defined(TRACING_ALLOCS) || defined(TRACING_HEAPS) +#include +#endif + +// Memory logging routines inherited from webkit allocator 9.2TB. +// #define OOM_LOGGING +// This debug flag logs error conditions when memory is unmapped/mapped from the system. +// Also, exports routines to dump the internal state and memory usage of the DL allocator. +// #define DL_CHUNK_MEM_DEBUG +// Exports debug rouintes to assert/trace chunked memory access. +#if defined(OOM_LOGGING) || defined(DL_CHUNK_MEM_DEBUG) +#include "MemoryLogger.h" +#endif + + +#ifndef __WINS__ +#pragma push +#pragma arm +#endif + +#include "dla_p.h" +#include "newallocator_p.h" + +// if non zero this causes the slabs to be configured only when the chunk size exceeds this level +#define DELAYED_SLAB_THRESHOLD (64*1024) // 64KB seems about right based on trace data +#define SLAB_CONFIG (0xabe) + +_LIT(KDLHeapPanicCategory, "DL Heap"); +#define GET_PAGE_SIZE(x) HAL::Get(HALData::EMemoryPageSize, x) +#define __CHECK_CELL(p) +#define __POWER_OF_2(x) ((TUint32)((x)^((x)-1))>=(TUint32)(x)) +#define HEAP_PANIC(r) Panic(r) + +LOCAL_C void Panic(TCdtPanic aPanic) +// Panic the process with USER as the category. + { + User::Panic(_L("USER"),aPanic); + } + + /* Purpose: Map chunk memory pages from system RAM + * Arguments: tp - tchunkptr in which memmory should be mapped + * psize - incoming tchunk size + * Return: KErrNone if successful, else KErrNoMemory + * Note: + */ +TInt RNewAllocator::map_chunk_pages(tchunkptr tp, size_t psize) +{ + if (page_not_in_memory(tp, psize)) { + char *a_addr = tchunk_page_align(tp); + size_t npages = tp->npages; + +#ifdef OOM_LOGGING + // check that npages matches the psize + size_t offset = address_offset(a_addr,tp); + if (offset < psize && (psize - offset) >= mparams.page_size ) + { + size_t tpages = ( psize - offset) >> pageshift; + if (tpages != tp->npages) //assert condition + MEM_LOG("CHUNK_PAGE_ERROR:map_chunk_pages, error in npages"); + } + else + MEM_LOG("CHUNK_PAGE_ERROR::map_chunk_pages: - Incorrect page-in-memmory flag"); +#endif + + if (map(a_addr, npages*mparams.page_size)) { + TRACE_DL_CHUNK_MAP(tp, psize, a_addr, npages*mparams.page_size); + ASSERT_RCHUNK_SIZE(); + TRACE_UNMAPPED_CHUNK(-1*npages*mparams.page_size); + return KErrNone; + } + else { +#ifdef OOM_LOGGING + + MEM_LOGF(_L8("CHUNK_PAGE_ERROR:: map_chunk_pages - Failed to Commit RAM, page_addr=%x, npages=%d, chunk_size=%d"), a_addr, npages, psize); + MEM_DUMP_OOM_LOGS(psize, "RSymbianDLHeap::map_chunk_pages - Failed to Commit RAM"); +#endif + return KErrNoMemory; + } + } + return KErrNone; +} + +/* Purpose: Map partial chunk memory pages from system RAM + * Arguments: tp - tchunkptr in which memmory should be mapped + * psize - incoming tchunk size + * r - remainder chunk pointer + * rsize - remainder chunk size + * Return: Number of unmapped pages from remainder chunk if successful (0 or more), else KErrNoMemory + * Note: Remainder chunk should be large enough to be mapped out (checked before invoking this function) + * pageout headers will be set from insert_large_chunk(), not here. + */ +TInt RNewAllocator::map_chunk_pages_partial(tchunkptr tp, size_t psize, tchunkptr r, size_t rsize) +{ + if (page_not_in_memory(tp, psize)) { + size_t npages = tp->npages; // total no of pages unmapped in this chunk + char *page_addr_map = tchunk_page_align(tp); // address to begin page map + char *page_addr_rem = tchunk_page_align(r); // address in remainder chunk to remain unmapped + assert(address_offset(page_addr_rem, r) < rsize); + size_t npages_map = address_offset(page_addr_rem, page_addr_map) >> pageshift; // no of pages to be mapped + if (npages_map > 0) { + if (map(page_addr_map, npages_map*mparams.page_size)) { +#ifdef DL_CHUNK_MEM_DEBUG + TRACE_DL_CHUNK_MAP(tp, psize, page_addr_map, npages_map*mparams.page_size); + ASSERT_RCHUNK_SIZE(); + TRACE_UNMAPPED_CHUNK(-1*npages_map*mparams.page_size); +#endif + return (npages - npages_map); + } + else { +#ifdef OOM_LOGGING + MEM_LOGF(_L8("CHUNK_PAGE_ERROR:: map_chunk_pages_partial - Failed to Commit RAM, page_addr=%x, npages=%d, chunk_size=%d"), page_addr_map, npages_map, psize); + MEM_DUMP_OOM_LOGS(psize, "RSymbianDLHeap::map_chunk_pages_partial - Failed to Commit RAM"); +#endif + return KErrNoMemory; + } + } + else { + // map not needed, first page is already mapped + return npages; + } + } + + return 0; +} + + +/* Purpose: Release (unmap) chunk memory pages to system RAM + * Arguments: tp - tchunkptr from which memmory may be released + * psize - incoming tchunk size + * prev_npages - number of pages that has been already unmapped from this chunk + * Return: total number of pages that has been unmapped from this chunk (new unmapped pages + prev_npages) + * Note: pageout headers will be set from insert_large_chunk(), not here. + */ +TInt RNewAllocator::unmap_chunk_pages(tchunkptr tp, size_t psize, size_t prev_npages) +{ + size_t npages = 0; + char *a_addr = tchunk_page_align(tp); + size_t offset = address_offset(a_addr,tp); + if (offset < psize && (psize - offset) >= mparams.page_size) + { /* check for new pages to decommit */ + npages = ( psize - offset) >> pageshift; + if (npages > prev_npages) { + unmap(a_addr, npages*mparams.page_size); // assuming kernel takes care of already unmapped pages + TRACE_DL_CHUNK_UNMAP(tp, psize, a_addr, npages*mparams.page_size); + iChunkSize += prev_npages*mparams.page_size; //adjust actual chunk size + ASSERT_RCHUNK_SIZE(); + TRACE_UNMAPPED_CHUNK((npages-prev_npages)*mparams.page_size); + assert((a_addr + npages*mparams.page_size - 1) < (char*)next_chunk(tp)); + } + } + +#ifdef OOM_LOGGING + if (npages && (npages < prev_npages)) + MEM_LOG("CHUNK_PAGE_ERROR:unmap_chunk_pages, error in npages"); + if (npages > prev_npages) { + /* check that end of decommited address lie within this chunk */ + if ((a_addr + npages*mparams.page_size - 1) >= (char*)next_chunk(tp)) + MEM_LOG("CHUNK_PAGE_ERROR:unmap_chunk_pages, error chunk boundary"); + } +#endif +#ifdef DL_CHUNK_MEM_DEBUG + mchunkptr next = next_chunk(tp); + do_check_any_chunk_access(next, chunksize(next)); + if (!npages) do_check_any_chunk_access((mchunkptr)tp, psize); +#endif + + return (npages); +} + +/* Purpose: Unmap all pages between previously unmapped and end of top chunk + and reset top to beginning of prev chunk + * Arguments: fm - global malloc state + * prev - previous chunk which has unmapped pages + * psize - size of previous chunk + * prev_npages - number of unmapped pages from previous chunk + * Return: nonzero if sucessful, else 0 + * Note: + */ +TInt RNewAllocator::sys_trim_partial(mstate m, mchunkptr prev, size_t psize, size_t prev_npages) +{ + size_t released = 0; + size_t extra = 0; + if (is_initialized(m)) { + psize += m->topsize; + char *a_addr = tchunk_page_align(prev); // includes space for TOP footer + size_t addr_offset = address_offset(a_addr, prev); + assert(addr_offset > TOP_FOOT_SIZE); //always assert? + assert((char*)iTop >= a_addr); //always assert? + if ((char*)iTop > a_addr) + extra = address_offset(iTop, a_addr); + +#ifdef OOM_LOGGING + if ((char*)iTop < a_addr) + MEM_LOGF(_L8("RSymbianDLHeap::sys_trim_partial - incorrect iTop value, top=%x, iTop=%x"), m->top, iTop); +#endif + msegmentptr sp = segment_holding(m, (TUint8*)prev); + if (!is_extern_segment(sp)) { + if (is_mmapped_segment(sp)) { + if (HAVE_MMAP && sp->size >= extra && !has_segment_link(m, sp)) { /* can't shrink if pinned */ + // size_t newsize = sp->size - extra; + /* Prefer mremap, fall back to munmap */ + if ((CALL_MREMAP(sp->base, sp->size, sp->size - extra, 0) != MFAIL) || + (CALL_MUNMAP(sp->base + sp->size - extra, extra) == 0)) { + released = extra; + } + } + } + else if (HAVE_MORECORE) { + if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ + extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - mparams.granularity; + ACQUIRE_MORECORE_LOCK(m); + { + /* Make sure end of memory is where we last set it. */ + TUint8* old_br = (TUint8*)(CALL_MORECORE(0)); + if (old_br == sp->base + sp->size) { + TUint8* rel_br = (TUint8*)(CALL_MORECORE(-extra)); + TUint8* new_br = (TUint8*)(CALL_MORECORE(0)); + if (rel_br != CMFAIL && new_br < old_br) + released = old_br - new_br; + } + } + RELEASE_MORECORE_LOCK(m); + } + } + + if (released != 0) { + TRACE_DL_CHUNK_UNMAP(prev, psize, a_addr, released); + iChunkSize += prev_npages*mparams.page_size; // prev_unmapped was already unmapped + TRACE_UNMAPPED_CHUNK(-1*prev_npages*mparams.page_size); + ASSERT_RCHUNK_SIZE(); + sp->size -= released; + m->footprint -= released; + } + + /* reset top to prev chunk */ + init_top(m, prev, addr_offset - TOP_FOOT_SIZE); + check_top_chunk(m, m->top); + } + + // DL region not initalized, do not reset top here + return (released != 0)? 1 : 0; +} + + +#define STACKSIZE 32 +inline void RNewAllocator::TraceCallStack() +{ +#ifdef TRACING_CALLSTACKS + TUint32 filteredStack[STACKSIZE]; + TThreadStackInfo info; + TUint32 *sp = (TUint32*)&sp; + RThread().StackInfo(info); + Lock(); + TInt i; + for (i=0;i=info.iBase) break; + while ((TLinAddr)sp < info.iBase) { + TUint32 cur = *sp++; + TUint32 range = cur & 0xF0000000; + if (range == 0x80000000 || range == 0x70000000) { + filteredStack[i] = cur; + break; + } + } + } + Unlock(); + BTraceContextBig(BTrace::EHeap, BTrace::EHeapCallStack, (TUint32)this, filteredStack, i * 4); +#endif +} + +size_t getpagesize() +{ + TInt size; + TInt err = GET_PAGE_SIZE(size); + if (err != KErrNone) + return (size_t)0x1000; + return (size_t)size; +} + +#define gm (&iGlobalMallocState) + +RNewAllocator::RNewAllocator(TInt aMaxLength, TInt aAlign, TBool aSingleThread) +// constructor for a fixed heap. Just use DL allocator + :iMinLength(aMaxLength), iMaxLength(aMaxLength), iOffset(0), iGrowBy(0), iChunkHandle(0), + iNestingLevel(0), iAllocCount(0), iFailType(ENone), iTestData(NULL), iChunkSize(aMaxLength) + { + + if ((TUint32)aAlign>=sizeof(TAny*) && __POWER_OF_2(iAlign)) + { + iAlign = aAlign; + } + else + { + iAlign = 4; + } + iPageSize = 0; + iFlags = aSingleThread ? (ESingleThreaded|EFixedSize) : EFixedSize; + + Init(0, 0, 0); + } + +RNewAllocator::RNewAllocator(TInt aChunkHandle, TInt aOffset, TInt aMinLength, TInt aMaxLength, TInt aGrowBy, + TInt aAlign, TBool aSingleThread) + : iMinLength(aMinLength), iMaxLength(aMaxLength), iOffset(aOffset), iChunkHandle(aChunkHandle), iAlign(aAlign), iNestingLevel(0), iAllocCount(0), + iFailType(ENone), iTestData(NULL), iChunkSize(aMinLength),iHighWaterMark(aMinLength) + { + iPageSize = malloc_getpagesize; + __ASSERT_ALWAYS(aOffset >=0, User::Panic(KDLHeapPanicCategory, ETHeapNewBadOffset)); + iGrowBy = _ALIGN_UP(aGrowBy, iPageSize); + iFlags = aSingleThread ? ESingleThreaded : 0; + + // Initialise + // if the heap is created with aMinLength==aMaxLength then it cannot allocate slab or page memory + // so these sub-allocators should be disabled. Otherwise initialise with default values + if (aMinLength == aMaxLength) + Init(0, 0, 0); + else + Init(0x3fff, 15, 0x10000); // all slabs, page {32KB}, trim {64KB} // Andrew: Adopting Webkit config? + //Init(0xabe, 16, iPageSize*4); // slabs {48, 40, 32, 24, 20, 16, 12, 8}, page {64KB}, trim {16KB} +#ifdef TRACING_HEAPS + RChunk chunk; + chunk.SetHandle(iChunkHandle); + TKName chunk_name; + chunk.FullName(chunk_name); + BTraceContextBig(BTrace::ETest1, 2, 22, chunk_name.Ptr(), chunk_name.Size()); + + TUint32 traceData[4]; + traceData[0] = iChunkHandle; + traceData[1] = iMinLength; + traceData[2] = iMaxLength; + traceData[3] = iAlign; + BTraceContextN(BTrace::ETest1, 1, (TUint32)this, 11, traceData, sizeof(traceData)); +#endif + + } + +TAny* RNewAllocator::operator new(TUint aSize, TAny* aBase) __NO_THROW + { + __ASSERT_ALWAYS(aSize>=sizeof(RNewAllocator), HEAP_PANIC(ETHeapNewBadSize)); + RNewAllocator* h = (RNewAllocator*)aBase; + h->iAlign = 0x80000000; // garbage value + h->iBase = ((TUint8*)aBase) + aSize; + return aBase; + } + +void RNewAllocator::Init(TInt aBitmapSlab, TInt aPagePower, size_t aTrimThreshold) + { + __ASSERT_ALWAYS((TUint32)iAlign>=sizeof(TAny*) && __POWER_OF_2(iAlign), HEAP_PANIC(ETHeapNewBadAlignment)); + + /*Moved code which does initialization */ + iTop = (TUint8*)this + iMinLength; + spare_page = 0; + iAllocCount = 0; // FIXME -- not used anywhere - already initialized to 0 in constructor anyway + memset(&mparams,0,sizeof(mparams)); + + Init_Dlmalloc(iTop - iBase, 0, aTrimThreshold); + + slab_init(aBitmapSlab); + + /*10-1K,11-2K,12-4k,13-8K,14-16K,15-32K,16-64K*/ + paged_init(aPagePower); + +#ifdef TRACING_ALLOCS + TUint32 traceData[3]; + traceData[0] = aBitmapSlab; + traceData[1] = aPagePower; + traceData[2] = aTrimThreshold; + BTraceContextN(BTrace::ETest1, BTrace::EHeapAlloc, (TUint32)this, 0, traceData, sizeof(traceData)); +#endif + + } + +RNewAllocator::SCell* RNewAllocator::GetAddress(const TAny* aCell) const +// +// As much as possible, check a cell address and backspace it +// to point at the cell header. +// + { + + TLinAddr m = TLinAddr(iAlign - 1); + __ASSERT_ALWAYS(!(TLinAddr(aCell)&m), HEAP_PANIC(ETHeapBadCellAddress)); + + SCell* pC = (SCell*)(((TUint8*)aCell)-EAllocCellSize); + __CHECK_CELL(pC); + + return pC; + } + +TInt RNewAllocator::AllocLen(const TAny* aCell) const +{ + if (ptrdiff(aCell, this) >= 0) + { + mchunkptr m = mem2chunk(aCell); + return chunksize(m) - CHUNK_OVERHEAD; // Andrew: Picking up webkit change. + } + if (lowbits(aCell, pagesize) > cellalign) + return header_size(slab::slabfor(aCell)->header); + if (lowbits(aCell, pagesize) == cellalign) + return *(unsigned*)(offset(aCell,-int(cellalign)))-cellalign; + return paged_descriptor(aCell)->size; +} + +TAny* RNewAllocator::Alloc(TInt aSize) +{ + __ASSERT_ALWAYS((TUint)aSize<(KMaxTInt/2),HEAP_PANIC(ETHeapBadAllocatedCellSize)); + + TAny* addr; + +#ifdef TRACING_ALLOCS + TInt aCnt=0; +#endif + Lock(); + if (aSize < slab_threshold) + { + TInt ix = sizemap[(aSize+3)>>2]; + ASSERT(ix != 0xff); + addr = slab_allocate(slaballoc[ix]); + if (addr) iTotalAllocSize += slaballoc[ix].size; + }else if ((aSize >> page_threshold)==0) + { +#ifdef TRACING_ALLOCS + aCnt=1; +#endif + addr = dlmalloc(aSize); + } + else + { +#ifdef TRACING_ALLOCS + aCnt=2; +#endif + addr = paged_allocate(aSize); + //attempt dlmalloc() if paged_allocate() fails. This can improve allocation chances if fragmentation is high in the heap. + if (!addr) { // paged_allocator failed, try in dlmalloc + addr = dlmalloc(aSize); + } + } + + if (addr) { + iCellCount++; + // Increment iTotalAllocSize in memory segment specific code for more accuracy + //iTotalAllocSize += aSize; + } + Unlock(); + +#ifdef TRACING_ALLOCS + if (iFlags & ETraceAllocs) + { + TUint32 traceData[3]; + traceData[0] = AllocLen(addr); + traceData[1] = aSize; + traceData[2] = aCnt; + BTraceContextN(BTrace::EHeap, BTrace::EHeapAlloc, (TUint32)this, (TUint32)addr, traceData, sizeof(traceData)); + TraceCallStack(); + } +#endif + + return addr; +} + +TInt RNewAllocator::Compress() + { + if (iFlags & EFixedSize) + return 0; + + Lock(); + dlmalloc_trim(0); + if (spare_page) + { + unmap(spare_page,pagesize); + spare_page = 0; + } + Unlock(); + return 0; + } + +void RNewAllocator::Free(TAny* aPtr) +{ + +#ifdef TRACING_ALLOCS + TInt aCnt=0; +#endif +#ifdef ENABLE_DEBUG_TRACE + RThread me; + TBuf<100> thName; + me.FullName(thName); +#endif + //if (!aPtr) return; //return in case of NULL pointer + + Lock(); + + if (!aPtr) + ; + else if (ptrdiff(aPtr, this) >= 0) + { +#ifdef TRACING_ALLOCS + aCnt = 1; +#endif + dlfree( aPtr); + } + else if (lowbits(aPtr, pagesize) <= cellalign) + { +#ifdef TRACING_ALLOCS + aCnt = 2; +#endif + paged_free(aPtr); + } + else + { +#ifdef TRACING_ALLOCS + aCnt = 0; +#endif + slab_free(aPtr); + } + iCellCount--; + Unlock(); + +#ifdef TRACING_ALLOCS + if (iFlags & ETraceAllocs) + { + TUint32 traceData; + traceData = aCnt; + BTraceContextN(BTrace::EHeap, BTrace::EHeapFree, (TUint32)this, (TUint32)aPtr, &traceData, sizeof(traceData)); + TraceCallStack(); + } +#endif +} + + +void RNewAllocator::Reset() + { + // TODO free everything + User::Panic(_L("RNewAllocator"), 1); //this should never be called + } + +#ifdef TRACING_ALLOCS +inline void RNewAllocator::TraceReAlloc(TAny* aPtr, TInt aSize, TAny* aNewPtr, TInt aZone) +{ + if (aNewPtr && (iFlags & ETraceAllocs)) { + TUint32 traceData[3]; + traceData[0] = AllocLen(aNewPtr); + traceData[1] = aSize; + traceData[2] = (TUint32) aPtr; + BTraceContextN(BTrace::EHeap, BTrace::EHeapReAlloc, (TUint32) this, (TUint32) aNewPtr, + traceData, sizeof(traceData)); + TraceCallStack(); + //workaround for SAW not handling reallocs properly + if (aZone >= 0 && aPtr != aNewPtr) { + BTraceContextN(BTrace::EHeap, BTrace::EHeapFree, (TUint32) this, (TUint32) aPtr, + &aZone, sizeof(aZone)); + TraceCallStack(); + } + } +} +#else +//Q_UNUSED generates code that prevents the compiler optimising out the empty inline function +inline void RNewAllocator::TraceReAlloc(TAny* , TInt , TAny* , TInt ) +{} +#endif + +TAny* RNewAllocator::ReAlloc(TAny* aPtr, TInt aSize, TInt /*aMode = 0*/) + { + if (ptrdiff(aPtr,this)>=0) + { + // original cell is in DL zone + if ((aSize>>page_threshold)==0 || aSize <= chunksize(mem2chunk(aPtr)) - CHUNK_OVERHEAD) + { + // new one is below page limit or smaller than old one (so can't be moved) + Lock(); + TAny* addr = dlrealloc(aPtr,aSize); + Unlock(); + TraceReAlloc(aPtr, aSize, addr, 2); + return addr; + } + } + else if (lowbits(aPtr,pagesize)<=cellalign) + { + // original cell is either NULL or in paged zone + if (!aPtr) + return Alloc(aSize); + + // either the new size is larger (in which case it will still be in paged zone) + // or it is smaller, but we will never move a shrinking cell so in paged zone + // must handle [rare] case that aSize == 0, as paged_[re]allocate() will panic + if (aSize == 0) + aSize = 1; + Lock(); + TAny* addr = paged_reallocate(aPtr,aSize); + Unlock(); + TraceReAlloc(aPtr, aSize, addr, 2); + return addr; + } + else + { + // original cell is in slab zone + // return original if new one smaller + if (aSize <= header_size(slab::slabfor(aPtr)->header)) + return aPtr; + } + // can't do better than allocate/copy/free + TAny* newp = Alloc(aSize); + if (newp) + { + TInt oldsize = AllocLen(aPtr); + memcpy(newp,aPtr,oldsize (this); + mallinfo info = self->dlmallinfo(); + aBiggestBlock = info.largestBlock; + return info.fordblks; +} +TInt RNewAllocator::AllocSize(TInt& aTotalAllocSize) const +{ + aTotalAllocSize = iTotalAllocSize; + return iCellCount; +} + +TInt RNewAllocator::DebugFunction(TInt aFunc, TAny* a1, TAny* /*a2*/) + { + TInt r = KErrNotSupported; + TInt* a1int = reinterpret_cast(a1); + switch (aFunc) { + case RAllocator::ECount: + { + struct mallinfo mi = dlmallinfo(); + *a1int = mi.fordblks; + r = mi.uordblks; + } + break; + case RAllocator::EMarkStart: + case RAllocator::EMarkEnd: + case RAllocator::ESetFail: + case RAllocator::ECheck: + r = KErrNone; + break; + } + return r; + } + +TInt RNewAllocator::Extension_(TUint /* aExtensionId */, TAny*& /* a0 */, TAny* /* a1 */) + { + return KErrNotSupported; + } + +/////////////////////////////////////////////////////////////////////////////// +// imported from dla.cpp +/////////////////////////////////////////////////////////////////////////////// + +//#include +//#define DEBUG_REALLOC +#ifdef DEBUG_REALLOC +#include +#endif +int RNewAllocator::init_mparams(size_t aTrimThreshold /*= DEFAULT_TRIM_THRESHOLD*/) +{ + if (mparams.page_size == 0) + { + size_t s; + mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD; + mparams.trim_threshold = aTrimThreshold; + #if MORECORE_CONTIGUOUS + mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT; + #else /* MORECORE_CONTIGUOUS */ + mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT; + #endif /* MORECORE_CONTIGUOUS */ + + s = (size_t)0x58585858U; + ACQUIRE_MAGIC_INIT_LOCK(&mparams); + if (mparams.magic == 0) { + mparams.magic = s; + /* Set up lock for main malloc area */ + INITIAL_LOCK(&gm->mutex); + gm->mflags = mparams.default_mflags; + } + RELEASE_MAGIC_INIT_LOCK(&mparams); + + mparams.page_size = malloc_getpagesize; + + mparams.granularity = ((DEFAULT_GRANULARITY != 0)? + DEFAULT_GRANULARITY : mparams.page_size); + + /* Sanity-check configuration: + size_t must be unsigned and as wide as pointer type. + ints must be at least 4 bytes. + alignment must be at least 8. + Alignment, min chunk size, and page size must all be powers of 2. + */ + + if ((sizeof(size_t) != sizeof(TUint8*)) || + (MAX_SIZE_T < MIN_CHUNK_SIZE) || + (sizeof(int) < 4) || + (MALLOC_ALIGNMENT < (size_t)8U) || + ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || + ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || + ((mparams.granularity & (mparams.granularity-SIZE_T_ONE)) != 0) || + ((mparams.page_size & (mparams.page_size-SIZE_T_ONE)) != 0)) + ABORT; + } + return 0; +} + +void RNewAllocator::init_bins(mstate m) { + /* Establish circular links for smallbins */ + bindex_t i; + for (i = 0; i < NSMALLBINS; ++i) { + sbinptr bin = smallbin_at(m,i); + bin->fd = bin->bk = bin; + } +} +/* ---------------------------- malloc support --------------------------- */ + +/* allocate a large request from the best fitting chunk in a treebin */ +void* RNewAllocator::tmalloc_large(mstate m, size_t nb) { + tchunkptr v = 0; + size_t rsize = -nb; /* Unsigned negation */ + tchunkptr t; + bindex_t idx; + compute_tree_index(nb, idx); + + if ((t = *treebin_at(m, idx)) != 0) { + /* Traverse tree for this bin looking for node with size == nb */ + size_t sizebits = + nb << + leftshift_for_tree_index(idx); + tchunkptr rst = 0; /* The deepest untaken right subtree */ + for (;;) { + tchunkptr rt; + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + v = t; + if ((rsize = trem) == 0) + break; + } + rt = t->child[1]; + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 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 = left_bits(idx2bit(idx)) & m->treemap; + if (leftbits != 0) { + bindex_t i; + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + t = *treebin_at(m, i); + } + } + while (t != 0) { /* find smallest of tree or subtree */ + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + t = leftmost_child(t); + } + /* If dv is a better fit, return 0 so malloc will use it */ + if (v != 0) { + if (RTCHECK(ok_address(m, v))) { /* split */ + mchunkptr r = chunk_plus_offset(v, nb); + assert(chunksize(v) == rsize + nb); + + /* check for chunk memory page-in */ + size_t npages_out = 0; + if (page_not_in_memory(v, chunksize(v))) { + if (!is_small(rsize) && rsize>=CHUNK_PAGEOUT_THESHOLD) { + // partial chunk page mapping + TInt result = map_chunk_pages_partial(v, chunksize(v), (tchunkptr)r, rsize); + if (result < 0) return 0; // Failed to Commit RAM + else npages_out = (size_t)result; + } + else { + // full chunk page map needed + TInt err = map_chunk_pages(v, chunksize(v)); + if (err != KErrNone) return 0; // Failed to Commit RAM + } + } + + if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); + if (rsize < free_chunk_threshold) // exaust if less than slab threshold + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + insert_chunk(m, r, rsize, npages_out); + } + return chunk2mem(v); + } + } +#if !INSECURE // conditional statement to keep compiler happy. code is reachable if RTCHECK evaluates to False + CORRUPTION_ERROR_ACTION(m); +#endif + } + return 0; +} + +/* allocate a small request from the best fitting chunk in a treebin */ +void* RNewAllocator::tmalloc_small(mstate m, size_t nb) { + tchunkptr t, v; + size_t rsize; + bindex_t i; + binmap_t leastbit = least_bit(m->treemap); + compute_bit2idx(leastbit, i); + + v = t = *treebin_at(m, i); + rsize = chunksize(t) - nb; + + while ((t = leftmost_child(t)) != 0) { + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + } + + if (RTCHECK(ok_address(m, v))) { + mchunkptr r = chunk_plus_offset(v, nb); + assert(chunksize(v) == rsize + nb); + + /* check for chunk memory page-in */ + if (page_not_in_memory(v, chunksize(v))) { + TInt err = map_chunk_pages(v, chunksize(v)); + if (err != KErrNone) return 0; // Failed to Commit RAM + } + + if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); + if (rsize < free_chunk_threshold) // exaust if less than slab threshold + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + insert_chunk(m, r, rsize, 0); + } + return chunk2mem(v); + } + } +#if !INSECURE // conditional statement to keep compiler happy. code is reachable if RTCHECK evaluates to False + CORRUPTION_ERROR_ACTION(m); + return 0; +#endif +} + +void RNewAllocator::init_top(mstate m, mchunkptr p, size_t psize) +{ + /* Ensure alignment */ + size_t offset = align_offset(chunk2mem(p)); + p = (mchunkptr)((TUint8*)p + offset); + psize -= offset; + m->top = p; + m->topsize = psize; + p->head = psize | PINUSE_BIT; + /* set size of fake trailing chunk holding overhead space only once */ + mchunkptr chunkPlusOff = chunk_plus_offset(p, psize); + chunkPlusOff->head = TOP_FOOT_SIZE; + m->trim_check = mparams.trim_threshold; /* reset on each update */ +} + +void* RNewAllocator::internal_realloc(mstate m, void* oldmem, size_t bytes) +{ + if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + return 0; + } + if (!PREACTION(m)) { + mchunkptr oldp = mem2chunk(oldmem); + size_t oldsize = chunksize(oldp); + mchunkptr next = chunk_plus_offset(oldp, oldsize); + mchunkptr newp = 0; + void* extra = 0; + + /* Try to either shrink or extend into top. Else malloc-copy-free */ + + if (RTCHECK(ok_address(m, oldp) && ok_cinuse(oldp) && + ok_next(oldp, next) && ok_pinuse(next))) { + size_t nb = request2size(bytes); + if (is_mmapped(oldp)) + newp = mmap_resize(m, oldp, nb); + else + if (oldsize >= nb) { /* already big enough */ + size_t rsize = oldsize - nb; + newp = oldp; + if (rsize >= free_chunk_threshold) { + mchunkptr remainder = chunk_plus_offset(newp, nb); + set_inuse(m, newp, nb); + set_inuse(m, remainder, rsize); + extra = chunk2mem(remainder); + iTotalAllocSize -= rsize; + } + } + /*AMOD: Modified to optimized*/ + else if (next == m->top && oldsize + m->topsize > nb) + { + /* Expand into top */ + if (oldsize + m->topsize > nb) + { + size_t newsize = oldsize + m->topsize; + size_t newtopsize = newsize - nb; + mchunkptr newtop = chunk_plus_offset(oldp, nb); + set_inuse(m, oldp, nb); + newtop->head = newtopsize |PINUSE_BIT; + m->top = newtop; + m->topsize = newtopsize; + iTotalAllocSize += nb - oldsize; + newp = oldp; + } + } + } + else { + USAGE_ERROR_ACTION(m, oldmem); + POSTACTION(m); + return 0; + } + + POSTACTION(m); + + if (newp != 0) { + if (extra != 0) { + internal_free(m, extra); + } + check_inuse_chunk(m, newp); + return chunk2mem(newp); + } + else { + void* newmem = internal_malloc(m, bytes); + if (newmem != 0) { + size_t oc = oldsize - overhead_for(oldp); + memcpy(newmem, oldmem, (oc < bytes)? oc : bytes); + internal_free(m, oldmem); + } + return newmem; + } + } +#if USE_LOCKS // keep the compiler happy + return 0; +#endif +} +/* ----------------------------- statistics ------------------------------ */ +mallinfo RNewAllocator::internal_mallinfo(mstate m) { + struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + TInt chunkCnt = 0; + if (!PREACTION(m)) { + check_malloc_state(m); + if (is_initialized(m)) { + size_t nfree = SIZE_T_ONE; /* top always free */ + size_t mfree = m->topsize + TOP_FOOT_SIZE; + size_t sum = mfree; + msegmentptr s = &m->seg; + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + chunkCnt++; + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + size_t sz = chunksize(q); + sum += sz; + if (!cinuse(q)) { + if (sz > nm.largestBlock) + nm.largestBlock = sz; + mfree += sz; + ++nfree; + } + q = next_chunk(q); + } + s = s->next; + } + nm.arena = sum; + nm.ordblks = nfree; + nm.hblkhd = m->footprint - sum; + nm.usmblks = m->max_footprint; + nm.uordblks = m->footprint - mfree; + nm.fordblks = mfree; + nm.keepcost = m->topsize; + nm.cellCount= chunkCnt;/*number of chunks allocated*/ + } + POSTACTION(m); + } + return nm; +} + +void RNewAllocator::internal_malloc_stats(mstate m) { +if (!PREACTION(m)) { + size_t fp = 0; + size_t used = 0; + check_malloc_state(m); + if (is_initialized(m)) { + msegmentptr s = &m->seg; + //size_t maxfp = m->max_footprint; + fp = m->footprint; + used = fp - (m->topsize + TOP_FOOT_SIZE); + + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + if (!cinuse(q)) + used -= chunksize(q); + q = next_chunk(q); + } + s = s->next; + } + } + POSTACTION(m); +} +} +/* support for mallopt */ +int RNewAllocator::change_mparam(int param_number, int value) { + size_t val = (size_t)value; + init_mparams(DEFAULT_TRIM_THRESHOLD); + switch (param_number) { + case M_TRIM_THRESHOLD: + mparams.trim_threshold = val; + return 1; + case M_GRANULARITY: + if (val >= mparams.page_size && ((val & (val-1)) == 0)) { + mparams.granularity = val; + return 1; + } + else + return 0; + case M_MMAP_THRESHOLD: + mparams.mmap_threshold = val; + return 1; + default: + return 0; + } +} +/* Get memory from system using MORECORE or MMAP */ +void* RNewAllocator::sys_alloc(mstate m, size_t nb) +{ + TUint8* tbase = CMFAIL; + size_t tsize = 0; + flag_t mmap_flag = 0; + //init_mparams();/*No need to do init_params here*/ + /* Directly map large chunks */ + if (use_mmap(m) && nb >= mparams.mmap_threshold) + { + void* mem = mmap_alloc(m, nb); + if (mem != 0) + return mem; + } + /* + Try getting memory in any of three ways (in most-preferred to + least-preferred order): + 1. A call to MORECORE that can normally contiguously extend memory. + (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or + or main space is mmapped or a previous contiguous call failed) + 2. A call to MMAP new space (disabled if not HAVE_MMAP). + Note that under the default settings, if MORECORE is unable to + fulfill a request, and HAVE_MMAP is true, then mmap is + used as a noncontiguous system allocator. This is a useful backup + strategy for systems with holes in address spaces -- in this case + sbrk cannot contiguously expand the heap, but mmap may be able to + find space. + 3. A call to MORECORE that cannot usually contiguously extend memory. + (disabled if not HAVE_MORECORE) + */ + /*Trying to allocate the memory*/ + if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) + { + TUint8* br = CMFAIL; + msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (TUint8*)m->top); + size_t asize = 0; + ACQUIRE_MORECORE_LOCK(m); + if (ss == 0) + { /* First time through or recovery */ + TUint8* base = (TUint8*)CALL_MORECORE(0); + if (base != CMFAIL) + { + asize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE); + /* Adjust to end on a page boundary */ + if (!is_page_aligned(base)) + asize += (page_align((size_t)base) - (size_t)base); + /* Can't call MORECORE if size is negative when treated as signed */ + if (asize < HALF_MAX_SIZE_T &&(br = (TUint8*)(CALL_MORECORE(asize))) == base) + { + tbase = base; + tsize = asize; + } + } + } + else + { + /* Subtract out existing available top space from MORECORE request. */ + asize = granularity_align(nb - m->topsize + TOP_FOOT_SIZE + SIZE_T_ONE); + /* Use mem here only if it did continuously extend old space */ + if (asize < HALF_MAX_SIZE_T && + (br = (TUint8*)(CALL_MORECORE(asize))) == ss->base+ss->size) { + tbase = br; + tsize = asize; + } + } + if (tbase == CMFAIL) { /* Cope with partial failure */ + if (br != CMFAIL) { /* Try to use/extend the space we did get */ + if (asize < HALF_MAX_SIZE_T && + asize < nb + TOP_FOOT_SIZE + SIZE_T_ONE) { + size_t esize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE - asize); + if (esize < HALF_MAX_SIZE_T) { + TUint8* end = (TUint8*)CALL_MORECORE(esize); + if (end != CMFAIL) + asize += esize; + else { /* Can't use; try to release */ + CALL_MORECORE(-asize); + br = CMFAIL; + } + } + } + } + if (br != CMFAIL) { /* Use the space we did get */ + tbase = br; + tsize = asize; + } + else + disable_contiguous(m); /* Don't try contiguous path in the future */ + } + RELEASE_MORECORE_LOCK(m); + } + if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */ + size_t req = nb + TOP_FOOT_SIZE + SIZE_T_ONE; + size_t rsize = granularity_align(req); + if (rsize > nb) { /* Fail if wraps around zero */ + TUint8* mp = (TUint8*)(CALL_MMAP(rsize)); + if (mp != CMFAIL) { + tbase = mp; + tsize = rsize; + mmap_flag = IS_MMAPPED_BIT; + } + } + } + if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */ + size_t asize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE); + if (asize < HALF_MAX_SIZE_T) { + TUint8* br = CMFAIL; + TUint8* end = CMFAIL; + ACQUIRE_MORECORE_LOCK(m); + br = (TUint8*)(CALL_MORECORE(asize)); + end = (TUint8*)(CALL_MORECORE(0)); + RELEASE_MORECORE_LOCK(m); + if (br != CMFAIL && end != CMFAIL && br < end) { + size_t ssize = end - br; + if (ssize > nb + TOP_FOOT_SIZE) { + tbase = br; + tsize = ssize; + } + } + } + } + if (tbase != CMFAIL) { + if ((m->footprint += tsize) > m->max_footprint) + m->max_footprint = m->footprint; + if (!is_initialized(m)) { /* first-time initialization */ + m->seg.base = m->least_addr = tbase; + m->seg.size = tsize; + m->seg.sflags = mmap_flag; + m->magic = mparams.magic; + init_bins(m); + if (is_global(m)) + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + else { + /* Offset top by embedded malloc_state */ + mchunkptr mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (TUint8*)mn) -TOP_FOOT_SIZE); + } + }else { + /* Try to merge with an existing segment */ + msegmentptr sp = &m->seg; + while (sp != 0 && tbase != sp->base + sp->size) + sp = sp->next; + if (sp != 0 && !is_extern_segment(sp) && + (sp->sflags & IS_MMAPPED_BIT) == mmap_flag && + segment_holds(sp, m->top)) + { /* append */ + sp->size += tsize; + init_top(m, m->top, m->topsize + tsize); + } + else { + if (tbase < m->least_addr) + m->least_addr = tbase; + sp = &m->seg; + while (sp != 0 && sp->base != tbase + tsize) + sp = sp->next; + if (sp != 0 && + !is_extern_segment(sp) && + (sp->sflags & IS_MMAPPED_BIT) == mmap_flag) { + TUint8* oldbase = sp->base; + sp->base = tbase; + sp->size += tsize; + return prepend_alloc(m, tbase, oldbase, nb); + } + else + add_segment(m, tbase, tsize, mmap_flag); + } + } + if (nb < m->topsize) { /* Allocate from new or extended top space */ + size_t rsize = m->topsize -= nb; + mchunkptr p = m->top; + mchunkptr r = m->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + check_top_chunk(m, m->top); + check_malloced_chunk(m, chunk2mem(p), nb); + return chunk2mem(p); + } + } + /*need to check this*/ + MEM_DUMP_OOM_LOGS(nb, "sys_alloc:: FAILED to get more memory"); + + //errno = -1; + return 0; +} +msegmentptr RNewAllocator::segment_holding(mstate m, TUint8* addr) { + msegmentptr sp = &m->seg; + for (;;) { + if (addr >= sp->base && addr < sp->base + sp->size) + return sp; + if ((sp = sp->next) == 0) + return 0; + } +} +/* Unlink the first chunk from a smallbin */ +inline void RNewAllocator::unlink_first_small_chunk(mstate M,mchunkptr B,mchunkptr P,bindex_t& I) +{ + mchunkptr F = P->fd; + assert(P != B); + assert(P != F); + assert(chunksize(P) == small_index2size(I)); + if (B == F) + clear_smallmap(M, I); + else if (RTCHECK(ok_address(M, F))) { + B->fd = F; + F->bk = B; + } + else { + CORRUPTION_ERROR_ACTION(M); + } +} +/* Link a free chunk into a smallbin */ +inline void RNewAllocator::insert_small_chunk(mstate M,mchunkptr P, size_t S) +{ + bindex_t I = small_index(S); + mchunkptr B = smallbin_at(M, I); + mchunkptr F = B; + assert(S >= MIN_CHUNK_SIZE); + if (!smallmap_is_marked(M, I)) + mark_smallmap(M, I); + else if (RTCHECK(ok_address(M, B->fd))) + F = B->fd; + else { + CORRUPTION_ERROR_ACTION(M); + } + B->fd = P; + F->bk = P; + P->fd = F; + P->bk = B; +} + + +inline void RNewAllocator::insert_chunk(mstate M,mchunkptr P,size_t S,size_t NPAGES) +{ + if (is_small(S)) + insert_small_chunk(M, P, S); + else{ + tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S, NPAGES); + } +} + +inline void RNewAllocator::unlink_large_chunk(mstate M,tchunkptr X) +{ + tchunkptr XP = X->parent; + tchunkptr R; + reset_tchunk_mem_pageout(X); // clear chunk pageout flag + if (X->bk != X) { + tchunkptr F = X->fd; + R = X->bk; + if (RTCHECK(ok_address(M, F))) { + F->bk = R; + R->fd = F; + } + else { + CORRUPTION_ERROR_ACTION(M); + } + } + else { + tchunkptr* RP; + if (((R = *(RP = &(X->child[1]))) != 0) || + ((R = *(RP = &(X->child[0]))) != 0)) { + tchunkptr* CP; + while ((*(CP = &(R->child[1])) != 0) || + (*(CP = &(R->child[0])) != 0)) { + R = *(RP = CP); + } + if (RTCHECK(ok_address(M, RP))) + *RP = 0; + else { + CORRUPTION_ERROR_ACTION(M); + } + } + } + if (XP != 0) { + tbinptr* H = treebin_at(M, X->index); + if (X == *H) { + if ((*H = R) == 0) + clear_treemap(M, X->index); + } + else if (RTCHECK(ok_address(M, XP))) { + if (XP->child[0] == X) + XP->child[0] = R; + else + XP->child[1] = R; + } + else + CORRUPTION_ERROR_ACTION(M); + if (R != 0) { + if (RTCHECK(ok_address(M, R))) { + tchunkptr C0, C1; + R->parent = XP; + if ((C0 = X->child[0]) != 0) { + if (RTCHECK(ok_address(M, C0))) { + R->child[0] = C0; + C0->parent = R; + } + else + CORRUPTION_ERROR_ACTION(M); + } + if ((C1 = X->child[1]) != 0) { + if (RTCHECK(ok_address(M, C1))) { + R->child[1] = C1; + C1->parent = R; + } + else + CORRUPTION_ERROR_ACTION(M); + } + } + else + CORRUPTION_ERROR_ACTION(M); + } + } +} + +/* Unlink a chunk from a smallbin */ +inline void RNewAllocator::unlink_small_chunk(mstate M, mchunkptr P,size_t S) +{ + mchunkptr F = P->fd; + mchunkptr B = P->bk; + bindex_t I = small_index(S); + assert(P != B); + assert(P != F); + assert(chunksize(P) == small_index2size(I)); + if (F == B) + clear_smallmap(M, I); + else if (RTCHECK((F == smallbin_at(M,I) || ok_address(M, F)) && + (B == smallbin_at(M,I) || ok_address(M, B)))) { + F->bk = B; + B->fd = F; + } + else { + CORRUPTION_ERROR_ACTION(M); + } +} + +inline void RNewAllocator::unlink_chunk(mstate M, mchunkptr P, size_t S) +{ + if (is_small(S)) + unlink_small_chunk(M, P, S); + else + { + tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); + } +} + +inline void RNewAllocator::compute_tree_index(size_t S, bindex_t& I) +{ + size_t X = S >> TREEBIN_SHIFT; + if (X == 0) + I = 0; + else if (X > 0xFFFF) + I = NTREEBINS-1; + else { + unsigned int Y = (unsigned int)X; + unsigned int N = ((Y - 0x100) >> 16) & 8; + unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4; + N += K; + N += K = (((Y <<= K) - 0x4000) >> 16) & 2; + K = 14 - N + ((Y <<= K) >> 15); + I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)); + } +} + +/* ------------------------- Operations on trees ------------------------- */ + +/* Insert chunk into tree */ +inline void RNewAllocator::insert_large_chunk(mstate M,tchunkptr X,size_t S,size_t NPAGES) +{ + tbinptr* H; + bindex_t I; + compute_tree_index(S, I); + H = treebin_at(M, I); + X->index = I; + X->child[0] = X->child[1] = 0; + + if (NPAGES) { set_tchunk_mem_pageout(X, NPAGES) } + else { reset_tchunk_mem_pageout(X) } + + if (!treemap_is_marked(M, I)) { + mark_treemap(M, I); + *H = X; + X->parent = (tchunkptr)H; + X->fd = X->bk = X; + } + else { + tchunkptr T = *H; + size_t K = S << leftshift_for_tree_index(I); + for (;;) { + if (chunksize(T) != S) { + tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]); + K <<= 1; + if (*C != 0) + T = *C; + else if (RTCHECK(ok_address(M, C))) { + *C = X; + X->parent = T; + X->fd = X->bk = X; + break; + } + else { + CORRUPTION_ERROR_ACTION(M); + break; + } + } + else { + tchunkptr F = T->fd; + if (RTCHECK(ok_address(M, T) && ok_address(M, F))) { + T->fd = F->bk = X; + X->fd = F; + X->bk = T; + X->parent = 0; + break; + } + else { + CORRUPTION_ERROR_ACTION(M); + break; + } + } + } + } +} + +/* + Unlink steps: + + 1. If x is a chained node, unlink it from its same-sized fd/bk links + and choose its bk node as its replacement. + 2. If x 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 x. 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). +*/ + +/* Replace dv node, binning the old one */ +/* Used only when dvsize known to be small */ +inline void RNewAllocator::replace_dv(mstate M, mchunkptr P, size_t S) +{ + size_t DVS = M->dvsize; + if (DVS != 0) { + mchunkptr DV = M->dv; + assert(is_small(DVS)); + insert_small_chunk(M, DV, DVS); + } + M->dvsize = S; + M->dv = P; +} + +inline void RNewAllocator::compute_bit2idx(binmap_t X,bindex_t& I) +{ + unsigned int Y = X - 1; + unsigned int K = Y >> (16-4) & 16; + unsigned int N = K; Y >>= K; + N += K = Y >> (8-3) & 8; Y >>= K; + N += K = Y >> (4-2) & 4; Y >>= K; + N += K = Y >> (2-1) & 2; Y >>= K; + N += K = Y >> (1-0) & 1; Y >>= K; + I = (bindex_t)(N + Y); +} + +void RNewAllocator::add_segment(mstate m, TUint8* tbase, size_t tsize, flag_t mmapped) { + /* Determine locations and sizes of segment, fenceposts, old top */ + TUint8* old_top = (TUint8*)m->top; + msegmentptr oldsp = segment_holding(m, old_top); + TUint8* old_end = oldsp->base + oldsp->size; + size_t ssize = pad_request(sizeof(struct malloc_segment)); + TUint8* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + size_t offset = align_offset(chunk2mem(rawsp)); + TUint8* asp = rawsp + offset; + TUint8* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp; + mchunkptr sp = (mchunkptr)csp; + msegmentptr ss = (msegmentptr)(chunk2mem(sp)); + mchunkptr tnext = chunk_plus_offset(sp, ssize); + mchunkptr p = tnext; + int nfences = 0; + + /* reset top to new space */ + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + + /* Set up segment record */ + assert(is_aligned(ss)); + set_size_and_pinuse_of_inuse_chunk(m, sp, ssize); + *ss = m->seg; /* Push current record */ + m->seg.base = tbase; + m->seg.size = tsize; + m->seg.sflags = mmapped; + m->seg.next = ss; + + /* Insert trailing fenceposts */ + for (;;) { + mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE); + p->head = FENCEPOST_HEAD; + ++nfences; + if ((TUint8*)(&(nextp->head)) < old_end) + p = nextp; + else + break; + } + assert(nfences >= 2); + + /* Insert the rest of old top into a bin as an ordinary free chunk */ + if (csp != old_top) { + mchunkptr q = (mchunkptr)old_top; + size_t psize = csp - old_top; + mchunkptr tn = chunk_plus_offset(q, psize); + set_free_with_pinuse(q, psize, tn); + insert_chunk(m, q, psize, 0); + } + + check_top_chunk(m, m->top); +} + + +void* RNewAllocator::prepend_alloc(mstate m, TUint8* newbase, TUint8* oldbase, + size_t nb) { + mchunkptr p = align_as_chunk(newbase); + mchunkptr oldfirst = align_as_chunk(oldbase); + size_t psize = (TUint8*)oldfirst - (TUint8*)p; + mchunkptr q = chunk_plus_offset(p, nb); + size_t qsize = psize - nb; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + + assert((TUint8*)oldfirst > (TUint8*)q); + assert(pinuse(oldfirst)); + assert(qsize >= MIN_CHUNK_SIZE); + + /* consolidate remainder with first chunk of old base */ + if (oldfirst == m->top) { + size_t tsize = m->topsize += qsize; + m->top = q; + q->head = tsize | PINUSE_BIT; + check_top_chunk(m, q); + } + else { + if (!cinuse(oldfirst)) { + size_t nsize = chunksize(oldfirst); + + /* check for chunk memory page-in */ + if (page_not_in_memory(oldfirst, nsize)) + map_chunk_pages((tchunkptr)oldfirst, nsize); //Err Ignored, branch not reachable. + + unlink_chunk(m, oldfirst, nsize); + oldfirst = chunk_plus_offset(oldfirst, nsize); + qsize += nsize; + } + set_free_with_pinuse(q, qsize, oldfirst); + insert_chunk(m, q, qsize, 0); + check_free_chunk(m, q); + } + + check_malloced_chunk(m, chunk2mem(p), nb); + return chunk2mem(p); +} + +void* RNewAllocator::mmap_alloc(mstate m, size_t nb) { + size_t mmsize = granularity_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + if (mmsize > nb) { /* Check for wrap around 0 */ + TUint8* mm = (TUint8*)(DIRECT_MMAP(mmsize)); + if (mm != CMFAIL) { + size_t offset = align_offset(chunk2mem(mm)); + size_t psize = mmsize - offset - MMAP_FOOT_PAD; + mchunkptr p = (mchunkptr)(mm + offset); + p->prev_foot = offset | IS_MMAPPED_BIT; + (p)->head = (psize|CINUSE_BIT); + mark_inuse_foot(m, p, psize); + chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0; + + if (mm < m->least_addr) + m->least_addr = mm; + if ((m->footprint += mmsize) > m->max_footprint) + m->max_footprint = m->footprint; + assert(is_aligned(chunk2mem(p))); + check_mmapped_chunk(m, p); + return chunk2mem(p); + } + } + return 0; +} + + int RNewAllocator::sys_trim(mstate m, size_t pad) + { + size_t released = 0; + if (pad < MAX_REQUEST && is_initialized(m)) { + pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ + + if (m->topsize > pad) { + /* Shrink top space in granularity-size units, keeping at least one */ + size_t unit = mparams.granularity; + size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - SIZE_T_ONE) * unit; + msegmentptr sp = segment_holding(m, (TUint8*)m->top); + + if (!is_extern_segment(sp)) { + if (is_mmapped_segment(sp)) { + if (HAVE_MMAP && + sp->size >= extra && + !has_segment_link(m, sp)) { /* can't shrink if pinned */ + /*size_t newsize = sp->size - extra; */ + /* Prefer mremap, fall back to munmap */ + if ((CALL_MREMAP(sp->base, sp->size, sp->size - extra, 0) != MFAIL) || + (CALL_MUNMAP(sp->base + sp->size - extra, extra) == 0)) { + released = extra; + } + } + } + else if (HAVE_MORECORE) { + if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ + extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit; + ACQUIRE_MORECORE_LOCK(m); + { + /* Make sure end of memory is where we last set it. */ + TUint8* old_br = (TUint8*)(CALL_MORECORE(0)); + if (old_br == sp->base + sp->size) { + TUint8* rel_br = (TUint8*)(CALL_MORECORE(-extra)); + TUint8* new_br = (TUint8*)(CALL_MORECORE(0)); + if (rel_br != CMFAIL && new_br < old_br) + released = old_br - new_br; + } + } + RELEASE_MORECORE_LOCK(m); + } + } + + if (released != 0) { + sp->size -= released; + m->footprint -= released; + init_top(m, m->top, m->topsize - released); + check_top_chunk(m, m->top); + } + } + + /* Unmap any unused mmapped segments */ + if (HAVE_MMAP) + released += release_unused_segments(m); + + /* On failure, disable autotrim to avoid repeated failed future calls */ + if (released == 0) + m->trim_check = MAX_SIZE_T; + } + + return (released != 0)? 1 : 0; + } + + inline int RNewAllocator::has_segment_link(mstate m, msegmentptr ss) + { + msegmentptr sp = &m->seg; + for (;;) { + if ((TUint8*)sp >= ss->base && (TUint8*)sp < ss->base + ss->size) + return 1; + if ((sp = sp->next) == 0) + return 0; + } + } + + /* Unmap and unlink any mmapped segments that don't contain used chunks */ + size_t RNewAllocator::release_unused_segments(mstate m) + { + size_t released = 0; + msegmentptr pred = &m->seg; + msegmentptr sp = pred->next; + while (sp != 0) { + TUint8* base = sp->base; + size_t size = sp->size; + msegmentptr next = sp->next; + if (is_mmapped_segment(sp) && !is_extern_segment(sp)) { + mchunkptr p = align_as_chunk(base); + size_t psize = chunksize(p); + /* Can unmap if first chunk holds entire segment and not pinned */ + if (!cinuse(p) && (TUint8*)p + psize >= base + size - TOP_FOOT_SIZE) { + tchunkptr tp = (tchunkptr)p; + size_t npages_out = tp->npages; + assert(segment_holds(sp, (TUint8*)sp)); + unlink_large_chunk(m, tp); + if (CALL_MUNMAP(base, size) == 0) { + released += size; + m->footprint -= size; + /* unlink obsoleted record */ + sp = pred; + sp->next = next; + } + else { /* back out if cannot unmap */ + insert_large_chunk(m, tp, psize, npages_out); + } + } + } + pred = sp; + sp = next; + }/*End of while*/ + return released; + } + /* Realloc using mmap */ + inline mchunkptr RNewAllocator::mmap_resize(mstate m, mchunkptr oldp, size_t nb) + { + size_t oldsize = chunksize(oldp); + if (is_small(nb)) /* Can't shrink mmap regions below small size */ + return 0; + /* Keep old chunk if big enough but not too big */ + if (oldsize >= nb + SIZE_T_SIZE && + (oldsize - nb) <= (mparams.granularity << 1)) + return oldp; + else { + size_t offset = oldp->prev_foot & ~IS_MMAPPED_BIT; + size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD; + size_t newmmsize = granularity_align(nb + SIX_SIZE_T_SIZES + + CHUNK_ALIGN_MASK); + TUint8* cp = (TUint8*)CALL_MREMAP((char*)oldp - offset, + oldmmsize, newmmsize, 1); + if (cp != CMFAIL) { + mchunkptr newp = (mchunkptr)(cp + offset); + size_t psize = newmmsize - offset - MMAP_FOOT_PAD; + newp->head = (psize|CINUSE_BIT); + mark_inuse_foot(m, newp, psize); + chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; + + if (cp < m->least_addr) + m->least_addr = cp; + if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint) + m->max_footprint = m->footprint; + check_mmapped_chunk(m, newp); + return newp; + } + } + return 0; + } + + +void RNewAllocator::Init_Dlmalloc(size_t capacity, int locked, size_t aTrimThreshold) + { + memset(gm,0,sizeof(malloc_state)); + init_mparams(aTrimThreshold); /* Ensure pagesize etc initialized */ + // The maximum amount that can be allocated can be calculated as:- + // 2^sizeof(size_t) - sizeof(malloc_state) - TOP_FOOT_SIZE - page size (all accordingly padded) + // If the capacity exceeds this, no allocation will be done. + gm->seg.base = gm->least_addr = iBase; + gm->seg.size = capacity; + gm->seg.sflags = !IS_MMAPPED_BIT; + set_lock(gm, locked); + gm->magic = mparams.magic; + init_bins(gm); + init_top(gm, (mchunkptr)iBase, capacity - TOP_FOOT_SIZE); + } + +void* RNewAllocator::dlmalloc(size_t bytes) { + /* + Basic algorithm: + If a small request (< 256 bytes minus per-chunk overhead): + 1. If one exists, use a remainderless chunk in associated smallbin. + (Remainderless means that there are too few excess bytes to represent as a chunk.) + 2. If one exists, split the smallest available chunk in a bin, saving remainder in bin. + 4. If it is big enough, use the top chunk. + 5. If available, get memory from system and use it + Otherwise, for a large request: + 1. Find the smallest available binned chunk that fits, splitting if necessary. + 3. If it is big enough, use the top chunk. + 4. If request size >= mmap threshold, try to directly mmap this chunk. + 5. If available, get memory from system and use it + + The ugly goto's here ensure that postaction occurs along all paths. + */ + if (!PREACTION(gm)) { + void* mem; + size_t nb; + if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + idx = small_index(nb); + smallbits = gm->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(gm, idx); + p = b->fd; + assert(chunksize(p) == small_index2size(idx)); + unlink_first_small_chunk(gm, b, p, idx); + set_inuse_and_pinuse(gm, p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } else { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + b = smallbin_at(gm, i); + p = b->fd; + assert(chunksize(p) == small_index2size(i)); + unlink_first_small_chunk(gm, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (rsize < free_chunk_threshold) + set_inuse_and_pinuse(gm, p, small_index2size(i)); + else { + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + insert_chunk(gm, r, rsize, 0); + } + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + } + } /* else - large alloc request */ + else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ + else { + nb = pad_request(bytes); + if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + } + + if (nb < gm->topsize) { /* Split top */ + size_t rsize = gm->topsize -= nb; + mchunkptr p = gm->top; + mchunkptr r = gm->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + mem = chunk2mem(p); + check_top_chunk(gm, gm->top); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + mem = sys_alloc(gm, nb); + + postaction: + POSTACTION(gm); +#ifdef DL_CHUNK_MEM_DEBUG + if (mem) { + mchunkptr pp = mem2chunk(mem); + do_check_any_chunk_access(pp, chunksize(pp)); + } +#endif + + if (mem) { + mchunkptr pp = mem2chunk(mem); + iTotalAllocSize += chunksize(pp); + } + + return mem; + } +#if USE_LOCKS // keep the compiler happy + return 0; +#endif +} + +void RNewAllocator::dlfree(void* mem) { + /* + Consolidate freed chunks with preceeding or succeeding bordering + free chunks, if they exist, and then place in a bin. Intermixed + with special cases for top, dv, mmapped chunks, and usage errors. + */ + + if (mem != 0) + { + size_t unmapped_pages = 0; + int prev_chunk_unmapped = 0; + mchunkptr p = mem2chunk(mem); +#if FOOTERS + mstate fm = get_mstate_for(p); + if (!ok_magic(fm)) + { + USAGE_ERROR_ACTION(fm, p); + return; + } +#else /* FOOTERS */ +#define fm gm +#endif /* FOOTERS */ + + if (!PREACTION(fm)) + { + check_inuse_chunk(fm, p); + if (RTCHECK(ok_address(fm, p) && ok_cinuse(p))) + { + size_t psize = chunksize(p); + iTotalAllocSize -= psize; + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) + { + size_t prevsize = p->prev_foot; + if ((prevsize & IS_MMAPPED_BIT) != 0) + { + prevsize &= ~IS_MMAPPED_BIT; + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + fm->footprint -= psize; + goto postaction; + } + else + { + mchunkptr prev = chunk_minus_offset(p, prevsize); + if (page_not_in_memory(prev, prevsize)) { + prev_chunk_unmapped = 1; + unmapped_pages = ((tchunkptr)prev)->npages; + } + + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(fm, prev))) + { /* consolidate backward */ + unlink_chunk(fm, p, prevsize); + } + else + goto erroraction; + } + } + + if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) + { + if (!cinuse(next)) + { /* consolidate forward */ + if (next == fm->top) + { + if (prev_chunk_unmapped) { // previous chunk is unmapped + /* unmap all pages between previously unmapped and end of top chunk + and reset top to beginning of prev chunk - done in sys_trim_partial() */ + sys_trim_partial(fm, p, psize, unmapped_pages); + do_check_any_chunk_access(fm->top, fm->topsize); + goto postaction; + } + else { // forward merge to top + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (should_trim(fm, tsize)) + sys_trim(fm, 0); + do_check_any_chunk_access(fm->top, fm->topsize); + goto postaction; + } + } + else + { + size_t nsize = chunksize(next); + //int next_chunk_unmapped = 0; + if ( page_not_in_memory(next, nsize) ) { + //next_chunk_unmapped = 1; + unmapped_pages += ((tchunkptr)next)->npages; + } + + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + } + } + else + set_free_with_pinuse(p, psize, next); + + /* check if chunk memmory can be released */ + size_t npages_out = 0; + if (!is_small(psize) && psize>=CHUNK_PAGEOUT_THESHOLD) + npages_out = unmap_chunk_pages((tchunkptr)p, psize, unmapped_pages); + + insert_chunk(fm, p, psize, npages_out); + check_free_chunk(fm, p); + do_chunk_page_release_check(p, psize, fm, npages_out); + goto postaction; + } + } +erroraction: + USAGE_ERROR_ACTION(fm, p); +postaction: + POSTACTION(fm); + } + } +#if !FOOTERS +#undef fm +#endif /* FOOTERS */ +} + +void* RNewAllocator::dlrealloc(void* oldmem, size_t bytes) { + if (oldmem == 0) + return dlmalloc(bytes); +#ifdef REALLOC_ZERO_BYTES_FREES + if (bytes == 0) { + dlfree(oldmem); + return 0; + } +#endif /* REALLOC_ZERO_BYTES_FREES */ + else { +#if ! FOOTERS + mstate m = gm; +#else /* FOOTERS */ + mstate m = get_mstate_for(mem2chunk(oldmem)); + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + return internal_realloc(m, oldmem, bytes); + } +} + + +int RNewAllocator::dlmalloc_trim(size_t pad) { + int result = 0; + if (!PREACTION(gm)) { + result = sys_trim(gm, pad); + POSTACTION(gm); + } + return result; +} + +size_t RNewAllocator::dlmalloc_footprint(void) { + return gm->footprint; +} + +size_t RNewAllocator::dlmalloc_max_footprint(void) { + return gm->max_footprint; +} + +#if !NO_MALLINFO +struct mallinfo RNewAllocator::dlmallinfo(void) { + return internal_mallinfo(gm); +} +#endif /* NO_MALLINFO */ + +void RNewAllocator::dlmalloc_stats() { + internal_malloc_stats(gm); +} + +int RNewAllocator::dlmallopt(int param_number, int value) { + return change_mparam(param_number, value); +} + +//inline slab* slab::slabfor(void* p) +slab* slab::slabfor( const void* p) +{ +return (slab*)(floor(p, slabsize)); +} + + +void RNewAllocator::tree_remove(slab* s) +{ + slab** r = s->parent; + slab* c1 = s->child1; + slab* c2 = s->child2; + for (;;) + { + if (!c2) + { + *r = c1; + if (c1) + c1->parent = r; + return; + } + if (!c1) + { + *r = c2; + c2->parent = r; + return; + } + if (c1 > c2) + { + slab* c3 = c1; + c1 = c2; + c2 = c3; + } + slab* newc2 = c1->child2; + *r = c1; + c1->parent = r; + c1->child2 = c2; + c2->parent = &c1->child2; + s = c1; + c1 = s->child1; + c2 = newc2; + r = &s->child1; + } +} +void RNewAllocator::tree_insert(slab* s,slab** r) + { + slab* n = *r; + for (;;) + { + if (!n) + { // tree empty + *r = s; + s->parent = r; + s->child1 = s->child2 = 0; + break; + } + if (s < n) + { // insert between parent and n + *r = s; + s->parent = r; + s->child1 = n; + s->child2 = 0; + n->parent = &s->child1; + break; + } + slab* c1 = n->child1; + slab* c2 = n->child2; + if ((c1 - 1) > (c2 - 1)) + { + r = &n->child1; + n = c1; + } + else + { + r = &n->child2; + n = c2; + } + } + } +void* RNewAllocator::allocnewslab(slabset& allocator) +// +// Acquire and initialise a new slab, returning a cell from the slab +// The strategy is: +// 1. Use the lowest address free slab, if available. This is done by using the lowest slab +// in the page at the root of the partial_page heap (which is address ordered). If the +// is now fully used, remove it from the partial_page heap. +// 2. Allocate a new page for slabs if no empty slabs are available +// +{ + page* p = page::pagefor(partial_page); + if (!p) + return allocnewpage(allocator); + + unsigned h = p->slabs[0].header; + unsigned pagemap = header_pagemap(h); + ASSERT(&p->slabs[hibit(pagemap)] == partial_page); + + unsigned slabix = lowbit(pagemap); + p->slabs[0].header = h &~ (0x100<slabs[slabix]); +} + +/**Defination of this functionis not there in proto code***/ +#if 0 +void RNewAllocator::partial_insert(slab* s) + { + // slab has had first cell freed and needs to be linked back into partial tree + slabset& ss = slaballoc[sizemap[s->clz]]; + + ASSERT(s->used == slabfull); + s->used = ss.fulluse - s->clz; // full-1 loading + tree_insert(s,&ss.partial); + checktree(ss.partial); + } +/**Defination of this functionis not there in proto code***/ +#endif + +void* RNewAllocator::allocnewpage(slabset& allocator) +// +// Acquire and initialise a new page, returning a cell from a new slab +// The partial_page tree is empty (otherwise we'd have used a slab from there) +// The partial_page link is put in the highest addressed slab in the page, and the +// lowest addressed slab is used to fulfill the allocation request +// +{ + page* p = spare_page; + if (p) + spare_page = 0; + else + { + p = static_cast(map(0,pagesize)); + if (!p) + return 0; + } + ASSERT(p == floor(p,pagesize)); + p->slabs[0].header = ((1<<3) + (1<<2) + (1<<1))<<8; // set pagemap + p->slabs[3].parent = &partial_page; + p->slabs[3].child1 = p->slabs[3].child2 = 0; + partial_page = &p->slabs[3]; + return allocator.initslab(&p->slabs[0]); +} + +void RNewAllocator::freepage(page* p) +// +// Release an unused page to the OS +// A single page is cached for reuse to reduce thrashing +// the OS allocator. +// +{ + ASSERT(ceiling(p,pagesize) == p); + if (!spare_page) + { + spare_page = p; + return; + } + unmap(p,pagesize); +} + +void RNewAllocator::freeslab(slab* s) +// +// Release an empty slab to the slab manager +// The strategy is: +// 1. The page containing the slab is checked to see the state of the other slabs in the page by +// inspecting the pagemap field in the header of the first slab in the page. +// 2. The pagemap is updated to indicate the new unused slab +// 3. If this is the only unused slab in the page then the slab header is used to add the page to +// the partial_page tree/heap +// 4. If all the slabs in the page are now unused the page is release back to the OS +// 5. If this slab has a higher address than the one currently used to track this page in +// the partial_page heap, the linkage is moved to the new unused slab +// +{ + tree_remove(s); + checktree(*s->parent); + ASSERT(header_usedm4(s->header) == header_size(s->header)-4); + CHECK(s->header |= 0xFF00000); // illegal value for debug purposes + page* p = page::pagefor(s); + unsigned h = p->slabs[0].header; + int slabix = s - &p->slabs[0]; + unsigned pagemap = header_pagemap(h); + p->slabs[0].header = h | (0x100<slabs[hibit(pagemap)]; + pagemap ^= (1< sl) + { // replace current link with new one. Address-order tree so position stays the same + slab** r = sl->parent; + slab* c1 = sl->child1; + slab* c2 = sl->child2; + s->parent = r; + s->child1 = c1; + s->child2 = c2; + *r = s; + if (c1) + c1->parent = &s->child1; + if (c2) + c2->parent = &s->child2; + } + CHECK(if (s < sl) s=sl); + } + ASSERT(header_pagemap(p->slabs[0].header) != 0); + ASSERT(hibit(header_pagemap(p->slabs[0].header)) == unsigned(s - &p->slabs[0])); +} + +void RNewAllocator::slab_init(unsigned slabbitmap) +{ + ASSERT((slabbitmap & ~okbits) == 0); + ASSERT(maxslabsize <= 60); + + slab_threshold=0; + partial_page = 0; + unsigned char ix = 0xff; + unsigned bit = 1<<((maxslabsize>>2)-1); + for (int sz = maxslabsize; sz >= 0; sz -= 4, bit >>= 1) + { + if (slabbitmap & bit) + { + if (++ix == 0) + slab_threshold=sz+1; + slabset& c = slaballoc[ix]; + c.size = sz; + c.partial = 0; + } + sizemap[sz>>2] = ix; + } + + free_chunk_threshold = pad_request(slab_threshold); +} + +void* RNewAllocator::slab_allocate(slabset& ss) +// +// Allocate a cell from the given slabset +// Strategy: +// 1. Take the partially full slab at the top of the heap (lowest address). +// 2. If there is no such slab, allocate from a new slab +// 3. If the slab has a non-empty freelist, pop the cell from the front of the list and update the slab +// 4. Otherwise, if the slab is not full, return the cell at the end of the currently used region of +// the slab, updating the slab +// 5. Otherwise, release the slab from the partial tree/heap, marking it as 'floating' and go back to +// step 1 +// +{ + for (;;) + { + slab *s = ss.partial; + if (!s) + break; + unsigned h = s->header; + unsigned free = h & 0xff; // extract free cell positiong + if (free) + { + ASSERT(((free<<2)-sizeof(slabhdr))%header_size(h) == 0); + void* p = offset(s,free<<2); + free = *(unsigned char*)p; // get next pos in free list + h += (h&0x3C000)<<6; // update usedm4 + h &= ~0xff; + h |= free; // update freelist + s->header = h; + ASSERT(header_free(h) == 0 || ((header_free(h)<<2)-sizeof(slabhdr))%header_size(h) == 0); + ASSERT(header_usedm4(h) <= 0x3F8u); + ASSERT((header_usedm4(h)+4)%header_size(h) == 0); + return p; + } + unsigned h2 = h + ((h&0x3C000)<<6); + if (h2 < 0xfc00000) + { + ASSERT((header_usedm4(h2)+4)%header_size(h2) == 0); + s->header = h2; + return offset(s,(h>>18) + sizeof(unsigned) + sizeof(slabhdr)); + } + h |= 0x80000000; // mark the slab as full-floating + s->header = h; + tree_remove(s); + checktree(ss.partial); + // go back and try the next slab... + } + // no partial slabs found, so allocate from a new slab + return allocnewslab(ss); +} + +void RNewAllocator::slab_free(void* p) +// +// Free a cell from the slab allocator +// Strategy: +// 1. Find the containing slab (round down to nearest 1KB boundary) +// 2. Push the cell into the slab's freelist, and update the slab usage count +// 3. If this is the last allocated cell, free the slab to the main slab manager +// 4. If the slab was full-floating then insert the slab in it's respective partial tree +// +{ + ASSERT(lowbits(p,3)==0); + slab* s = slab::slabfor(p); + + unsigned pos = lowbits(p, slabsize); + unsigned h = s->header; + ASSERT(header_usedm4(h) != 0x3fC); // slab is empty already + ASSERT((pos-sizeof(slabhdr))%header_size(h) == 0); + *(unsigned char*)p = (unsigned char)h; + h &= ~0xFF; + h |= (pos>>2); + unsigned size = h & 0x3C000; + unsigned allocSize = (h & 0x3F000) >> 12; // size is stored in bits 12...17 in slabhdr + iTotalAllocSize -= allocSize; + if (int(h) >= 0) + { + h -= size<<6; + if (int(h)>=0) + { + s->header = h; + return; + } + freeslab(s); + return; + } + h -= size<<6; + h &= ~0x80000000; + s->header = h; + slabset& ss = slaballoc[sizemap[size>>14]]; + tree_insert(s,&ss.partial); + checktree(ss.partial); +} + +void* slabset::initslab(slab* s) +// +// initialise an empty slab for this allocator and return the fist cell +// pre-condition: the slabset has no partial slabs for allocation +// +{ + ASSERT(partial==0); + unsigned h = s->header & 0xF00; // preserve pagemap only + h |= (size<<12); // set size + h |= (size-4)<<18; // set usedminus4 to one object minus 4 + s->header = h; + partial = s; + s->parent = &partial; + s->child1 = s->child2 = 0; + return offset(s,sizeof(slabhdr)); +} + +TAny* RNewAllocator::SetBrk(TInt32 aDelta) +{ + if (iFlags & EFixedSize) + return MFAIL; + + if (aDelta < 0) + { + unmap(offset(iTop, aDelta), -aDelta); + } + else if (aDelta > 0) + { + if (!map(iTop, aDelta)) + return MFAIL; + } + void * p =iTop; + iTop = offset(iTop, aDelta); + return p; +} + +void* RNewAllocator::map(void* p,unsigned sz) +// +// allocate pages in the chunk +// if p is NULL, find and allocate the required number of pages (which must lie in the lower half) +// otherwise commit the pages specified +// +{ +ASSERT(p == floor(p, pagesize)); +ASSERT(sz == ceiling(sz, pagesize)); +ASSERT(sz > 0); + + if (iChunkSize + sz > iMaxLength) + return 0; + + RChunk chunk; + chunk.SetHandle(iChunkHandle); + if (p) + { + TInt r = chunk.Commit(iOffset + ptrdiff(p, this),sz); + if (r < 0) + return 0; + iChunkSize += sz; + return p; + } + + TInt r = chunk.Allocate(sz); + if (r < 0) + return 0; + if (r > iOffset) + { + // can't allow page allocations in DL zone + chunk.Decommit(r, sz); + return 0; + } + iChunkSize += sz; +#ifdef TRACING_HEAPS + if (iChunkSize > iHighWaterMark) + { + iHighWaterMark = ceiling(iChunkSize,16*pagesize); + + + RChunk chunk; + chunk.SetHandle(iChunkHandle); + TKName chunk_name; + chunk.FullName(chunk_name); + BTraceContextBig(BTrace::ETest1, 4, 44, chunk_name.Ptr(), chunk_name.Size()); + + TUint32 traceData[6]; + traceData[0] = iChunkHandle; + traceData[1] = iMinLength; + traceData[2] = iMaxLength; + traceData[3] = sz; + traceData[4] = iChunkSize; + traceData[5] = iHighWaterMark; + BTraceContextN(BTrace::ETest1, 3, (TUint32)this, 33, traceData, sizeof(traceData)); + } +#endif + + return offset(this, r - iOffset); + // code below does delayed initialisation of the slabs. + /* + if (iChunkSize >= slab_init_threshold) + { // set up slab system now that heap is large enough + slab_config(slab_config_bits); + slab_init_threshold = KMaxTUint; + } + return p; + */ +} + +void* RNewAllocator::remap(void* p,unsigned oldsz,unsigned sz) +{ + if (oldsz > sz) + { // shrink + unmap(offset(p,sz), oldsz-sz); + } + else if (oldsz < sz) + { // grow, try and do this in place first + if (!map(offset(p, oldsz), sz-oldsz)) + { + // need to allocate-copy-free + void* newp = map(0, sz); + if (newp) { + memcpy(newp, p, oldsz); + unmap(p,oldsz); + } + return newp; + } + } + return p; +} + +void RNewAllocator::unmap(void* p,unsigned sz) +{ + ASSERT(p == floor(p, pagesize)); + ASSERT(sz == ceiling(sz, pagesize)); + ASSERT(sz > 0); + + RChunk chunk; + chunk.SetHandle(iChunkHandle); + TInt r = chunk.Decommit(ptrdiff(p, offset(this,-iOffset)), sz); + //TInt offset = (TUint8*)p-(TUint8*)chunk.Base(); + //TInt r = chunk.Decommit(offset,sz); + + ASSERT(r >= 0); + iChunkSize -= sz; +#ifdef TRACING_HEAPS + if (iChunkSize > iHighWaterMark) + { + iHighWaterMark = ceiling(iChunkSize,16*pagesize); + + + RChunk chunk; + chunk.SetHandle(iChunkHandle); + TKName chunk_name; + chunk.FullName(chunk_name); + BTraceContextBig(BTrace::ETest1, 4, 44, chunk_name.Ptr(), chunk_name.Size()); + + TUint32 traceData[6]; + traceData[0] = iChunkHandle; + traceData[1] = iMinLength; + traceData[2] = iMaxLength; + traceData[3] = sz; + traceData[4] = iChunkSize; + traceData[5] = iHighWaterMark; + BTraceContextN(BTrace::ETest1, 3, (TUint32)this, 33, traceData, sizeof(traceData)); + } +#endif +} + +void RNewAllocator::paged_init(unsigned pagepower) + { + if (pagepower == 0) + pagepower = 31; + else if (pagepower < minpagepower) + pagepower = minpagepower; + page_threshold = pagepower; + for (int i=0;ipage == 0) + { + void* p = map(0, nbytes); + if (!p) + return 0; + c->page = p; + c->size = nbytes; + iTotalAllocSize += nbytes; + return p; + } + } + // use a cell header + nbytes = ceiling(size + cellalign, pagesize); + void* p = map(0, nbytes); + if (!p) + return 0; + *static_cast(p) = nbytes; + iTotalAllocSize += nbytes; + return offset(p, cellalign); +} + +void* RNewAllocator::paged_reallocate(void* p, unsigned size) +{ + if (lowbits(p, pagesize) == 0) + { // continue using descriptor + pagecell* c = paged_descriptor(p); + unsigned nbytes = ceiling(size, pagesize); + void* newp = remap(p, c->size, nbytes); + if (!newp) + return 0; + c->page = newp; + c->size = nbytes; + iTotalAllocSize += nbytes-c->size; + return newp; + } + else + { // use a cell header + ASSERT(lowbits(p,pagesize) == cellalign); + p = offset(p,-int(cellalign)); + unsigned nbytes = ceiling(size + cellalign, pagesize); + unsigned obytes = *static_cast(p); + void* newp = remap(p, obytes, nbytes); + if (!newp) + return 0; + *static_cast(newp) = nbytes; + iTotalAllocSize += nbytes-obytes; + return offset(newp, cellalign); + } +} + +void RNewAllocator::paged_free(void* p) +{ + if (lowbits(p,pagesize) == 0) + { // check pagelist + pagecell* c = paged_descriptor(p); + + iTotalAllocSize -= c->size; + + unmap(p, c->size); + c->page = 0; + c->size = 0; + } + else + { // check page header + unsigned* page = static_cast(offset(p,-int(cellalign))); + unsigned size = *page; + + iTotalAllocSize -= size; + + unmap(page,size); + } +} + +pagecell* RNewAllocator::paged_descriptor(const void* p) const +{ + ASSERT(lowbits(p,pagesize) == 0); + // Double casting to keep the compiler happy. Seems to think we can trying to + // change a non-const member (pagelist) in a const function + pagecell* c = (pagecell*)((void*)pagelist); +#ifdef _DEBUG + pagecell* e = c + npagecells; +#endif + for (;;) + { + ASSERT(c!=e); + if (c->page == p) + return c; + ++c; + } +} + +RNewAllocator* RNewAllocator::FixedHeap(TAny* aBase, TInt aMaxLength, TInt aAlign, TBool aSingleThread) +/** +Creates a fixed length heap at a specified location. + +On successful return from this function, aMaxLength bytes are committed by the chunk. +The heap cannot be extended. + +@param aBase A pointer to the location where the heap is to be constructed. +@param aMaxLength The length of the heap. If the supplied value is less + than KMinHeapSize, it is discarded and the value KMinHeapSize + is used instead. +@param aAlign The alignment of heap cells. +@param aSingleThread Indicates whether single threaded or not. + +@return A pointer to the new heap, or NULL if the heap could not be created. + +@panic USER 56 if aMaxLength is negative. +*/ +// +// Force construction of the fixed memory. +// + { + + __ASSERT_ALWAYS(aMaxLength>=0, ::Panic(ETHeapMaxLengthNegative)); + if (aMaxLengthiLock.CreateLocal(); + if (r!=KErrNone) + return NULL; + h->iHandles = (TInt*)&h->iLock; + h->iHandleCount = 1; + } + return h; + } + +RNewAllocator* RNewAllocator::ChunkHeap(const TDesC* aName, TInt aMinLength, TInt aMaxLength, TInt aGrowBy, TInt aAlign, TBool aSingleThread) +/** +Creates a heap in a local or global chunk. + +The chunk hosting the heap can be local or global. + +A local chunk is one which is private to the process creating it and is not +intended for access by other user processes. +A global chunk is one which is visible to all processes. + +The hosting chunk is local, if the pointer aName is NULL, otherwise +the hosting chunk is global and the descriptor *aName is assumed to contain +the name to be assigned to it. + +Ownership of the host chunk is vested in the current process. + +A minimum and a maximum size for the heap can be specified. On successful +return from this function, the size of the heap is at least aMinLength. +If subsequent requests for allocation of memory from the heap cannot be +satisfied by compressing the heap, the size of the heap is extended in +increments of aGrowBy until the request can be satisfied. Attempts to extend +the heap causes the size of the host chunk to be adjusted. + +Note that the size of the heap cannot be adjusted by more than aMaxLength. + +@param aName If NULL, the function constructs a local chunk to host + the heap. + If not NULL, a pointer to a descriptor containing the name + to be assigned to the global chunk hosting the heap. +@param aMinLength The minimum length of the heap. +@param aMaxLength The maximum length to which the heap can grow. + If the supplied value is less than KMinHeapSize, then it + is discarded and the value KMinHeapSize used instead. +@param aGrowBy The increments to the size of the host chunk. If a value is + not explicitly specified, the value KMinHeapGrowBy is taken + by default +@param aAlign The alignment of heap cells. +@param aSingleThread Indicates whether single threaded or not. + +@return A pointer to the new heap or NULL if the heap could not be created. + +@panic USER 41 if aMinLength is greater than the supplied value of aMaxLength. +@panic USER 55 if aMinLength is negative. +@panic USER 56 if aMaxLength is negative. +*/ +// +// Allocate a Chunk of the requested size and force construction. +// + { + + __ASSERT_ALWAYS(aMinLength>=0, ::Panic(ETHeapMinLengthNegative)); + __ASSERT_ALWAYS(aMaxLength>=aMinLength, ::Panic(ETHeapCreateMaxLessThanMin)); + if (aMaxLength=0, ::Panic(ETHeapMinLengthNegative)); + __ASSERT_ALWAYS(maxLength>=aMinLength, ::Panic(ETHeapCreateMaxLessThanMin)); + aMinLength = _ALIGN_UP(Max(aMinLength, (TInt)sizeof(RNewAllocator) + min_cell) + aOffset, round_up); + + // the new allocator uses a disconnected chunk so must commit the initial allocation + // with Commit() instead of Adjust() + // TInt r=aChunk.Adjust(aMinLength); + //TInt r = aChunk.Commit(aOffset, aMinLength); + + aOffset = maxLength; + //TInt MORE_CORE_OFFSET = maxLength/2; + //TInt r = aChunk.Commit(MORE_CORE_OFFSET, aMinLength); + TInt r = aChunk.Commit(aOffset, aMinLength); + + if (r!=KErrNone) + return NULL; + + RNewAllocator* h = new (aChunk.Base() + aOffset) RNewAllocator(aChunk.Handle(), aOffset, aMinLength, maxLength, aGrowBy, aAlign, aSingleThread); + //RNewAllocator* h = new (aChunk.Base() + MORE_CORE_OFFSET) RNewAllocator(aChunk.Handle(), aOffset, aMinLength, maxLength, aGrowBy, aAlign, aSingleThread); + + TBool duplicateLock = EFalse; + if (!aSingleThread) + { + duplicateLock = aMode & UserHeap::EChunkHeapSwitchTo; + if (h->iLock.CreateLocal(duplicateLock ? EOwnerThread : EOwnerProcess)!=KErrNone) + { + h->iChunkHandle = 0; + return NULL; + } + } + + if (aMode & UserHeap::EChunkHeapSwitchTo) + User::SwitchHeap(h); + + h->iHandles = &h->iChunkHandle; + if (!aSingleThread) + { + // now change the thread-relative chunk/semaphore handles into process-relative handles + h->iHandleCount = 2; + if (duplicateLock) + { + RHandleBase s = h->iLock; + r = h->iLock.Duplicate(RThread()); + s.Close(); + } + if (r==KErrNone && (aMode & UserHeap::EChunkHeapDuplicate)) + { + r = ((RChunk*)&h->iChunkHandle)->Duplicate(RThread()); + if (r!=KErrNone) + h->iLock.Close(), h->iChunkHandle=0; + } + } + else + { + h->iHandleCount = 1; + if (aMode & UserHeap::EChunkHeapDuplicate) + r = ((RChunk*)&h->iChunkHandle)->Duplicate(RThread(), EOwnerThread); + } + + // return the heap address + return (r==KErrNone) ? h : NULL; + } + + /* Only for debugging purpose - start*/ +#ifdef DL_CHUNK_MEM_DEBUG +void RNewAllocator::debug_check_small_chunk_access(mchunkptr p, size_t psize) +{ + size_t sz = chunksize(p); + char ch = *((char*)chunk_plus_offset(p, psize-1)); +} + +void RNewAllocator::debug_check_any_chunk_access(mchunkptr p, size_t psize) +{ + if (p==0 || psize==0) return; + + mchunkptr next = chunk_plus_offset(p, psize); + char* t = (char*)chunk_plus_offset(p, mparams.page_size); + char ch = *((char*)p); + while ((size_t)t<(size_t)next) + { + ch = *t; + t = (char*)chunk_plus_offset(t, mparams.page_size); + }; +} + +void RNewAllocator::debug_check_large_chunk_access(tchunkptr p, size_t psize) +{ + mchunkptr next = chunk_plus_offset(p, psize); + char* t = (char*)chunk_plus_offset(p, mparams.page_size); + char ch = *((char*)p); + while ((size_t)t<(size_t)next) + { + ch = *t; + t = (char*)chunk_plus_offset(t, mparams.page_size); + }; +} + +void RNewAllocator::debug_chunk_page_release_check(mchunkptr p, size_t psize, mstate fm, int mem_released) +{ + if (mem_released) + { + if (!page_not_in_memory(p, psize) ) + MEM_LOG("CHUNK_PAGE_ERROR::dlfree, error - page_in_mem flag is corrupt"); + if (chunk_plus_offset(p, psize) > fm->top) + MEM_LOG("CHUNK_PAGE_ERROR: error Top chunk address invalid"); + if (fm->dv >= p && fm->dv < chunk_plus_offset(p, psize)) + MEM_LOG("CHUNK_PAGE_ERROR: error DV chunk address invalid"); + } +} +#endif + +#ifdef OOM_LOGGING +#include +void RNewAllocator::dump_large_chunk(mstate m, tchunkptr t) { + tchunkptr u = t; + bindex_t tindex = t->index; + size_t tsize = chunksize(t); + bindex_t idx; + compute_tree_index(tsize, idx); + + size_t free = 0; + int nfree = 0; + do + { /* traverse through chain of same-sized nodes */ + if (u->child[0] != 0) + { + dump_large_chunk(m, u->child[0]); + } + + if (u->child[1] != 0) + { + dump_large_chunk(m, u->child[1]); + } + + free += chunksize(u); + nfree++; + u = u->fd; + } + while (u != t); + C_LOGF(_L8("LARGE_BIN,%d,%d,%d"), tsize, free, nfree); +} + +void RNewAllocator::dump_dl_free_chunks() +{ + C_LOG(""); + C_LOG("------------ dump_dl_free_chunks start -------------"); + C_LOG("BinType,BinSize,FreeSize,FreeCount"); + + // dump small bins + for (int i = 0; i < NSMALLBINS; ++i) + { + sbinptr b = smallbin_at(gm, i); + unsigned int empty = (gm->smallmap & (1 << i)) == 0; + int nfree = 0; + if (!empty) + { + int nfree = 0; + size_t free = 0; + mchunkptr p = b->bk; + size_t size = chunksize(p); + for (; p != b; p = p->bk) + { + free += chunksize(p); + nfree++; + } + + C_LOGF(_L8("SMALL_BIN,%d,%d,%d"), size, free, nfree); + } + } + + // dump large bins + for (int i = 0; i < NTREEBINS; ++i) + { + tbinptr* tb = treebin_at(gm, i); + tchunkptr t = *tb; + int empty = (gm->treemap & (1 << i)) == 0; + if (!empty) + dump_large_chunk(gm, t); + } + + C_LOG("------------ dump_dl_free_chunks end -------------"); + C_LOG(""); + } + +void RNewAllocator::dump_heap_logs(size_t fail_size) +{ + MEM_LOG(""); + if (fail_size) { + MEM_LOG("MEMDEBUG::RSymbianDLHeap OOM Log dump *************** start"); + MEM_LOGF(_L8("Failing to alloc size: %d"), fail_size); + } + else + MEM_LOG("MEMDEBUG::RSymbianDLHeap Log dump *************** start"); + + TInt dl_chunk_size = ptrdiff(iTop,iBase); + TInt slabp_chunk_size = iChunkSize + iUnmappedChunkSize - dl_chunk_size; + TInt freeMem = 0; + HAL::Get(HALData::EMemoryRAMFree, freeMem); + MEM_LOGF(_L8("System Free RAM Size: %d"), freeMem); + MEM_LOGF(_L8("Allocator Commited Chunk Size: %d"), iChunkSize); + MEM_LOGF(_L8("DLHeap Arena Size=%d"), dl_chunk_size); + MEM_LOGF(_L8("DLHeap unmapped chunk size: %d"), iUnmappedChunkSize); + MEM_LOGF(_L8("Slab-Page Allocator Chunk Size=%d"), slabp_chunk_size); + + mallinfo info = dlmallinfo(); + TUint heapAlloc = info.uordblks; + TUint heapFree = info.fordblks; + MEM_LOGF(_L8("DLHeap allocated size: %d"), heapAlloc); + MEM_LOGF(_L8("DLHeap free size: %d"), heapFree); + + if (fail_size) { + MEM_LOG("MEMDEBUG::RSymbianDLHeap OOM Log dump *************** end"); + }else { + MEM_LOG("MEMDEBUG::RSymbianDLHeap Log dump *************** end"); + } + MEM_LOG(""); +} + +#endif +/* Only for debugging purpose - end*/ + + +#define UserTestDebugMaskBit(bit) (TBool)(UserSvr::DebugMask(bit>>5) & (1<<(bit&31))) + +#ifndef NO_NAMED_LOCAL_CHUNKS +//this class requires Symbian^3 for ElocalNamed + +// Hack to get access to TChunkCreateInfo internals outside of the kernel +class TFakeChunkCreateInfo: public TChunkCreateInfo + { +public: + void SetThreadNewAllocator(TInt aInitialSize, TInt aMaxSize, const TDesC& aName) + { + iType = TChunkCreate::ENormal | TChunkCreate::EDisconnected | TChunkCreate::EData; + iMaxSize = aMaxSize * 2; + + iInitialBottom = 0; + iInitialTop = aInitialSize; + iAttributes = TChunkCreate::ELocalNamed; + iName = &aName; + iOwnerType = EOwnerThread; + } + }; +#endif + +#ifndef NO_NAMED_LOCAL_CHUNKS +_LIT(KLitDollarHeap,"$HEAP"); +#endif +TInt RNewAllocator::CreateThreadHeap(SStdEpocThreadCreateInfo& aInfo, RNewAllocator*& aHeap, TInt aAlign, TBool aSingleThread) +/** +@internalComponent +*/ +// +// Create a user-side heap +// + { + TInt page_size = malloc_getpagesize; + TInt minLength = _ALIGN_UP(aInfo.iHeapInitialSize, page_size); + TInt maxLength = Max(aInfo.iHeapMaxSize, minLength); +#ifdef TRACING_ALLOCS + if (UserTestDebugMaskBit(96)) // 96 == KUSERHEAPTRACE in nk_trace.h + aInfo.iFlags |= ETraceHeapAllocs; +#endif + // Create the thread's heap chunk. + RChunk c; +#ifndef NO_NAMED_LOCAL_CHUNKS + TFakeChunkCreateInfo createInfo; + createInfo.SetThreadNewAllocator(0, maxLength, KLitDollarHeap()); // Initialise with no memory committed. + TInt r = c.Create(createInfo); +#else + TInt r = c.CreateDisconnectedLocal(0, 0, maxLength * 2); +#endif + if (r!=KErrNone) + return r; + aHeap = ChunkHeap(c, minLength, page_size, maxLength, aAlign, aSingleThread, UserHeap::EChunkHeapSwitchTo|UserHeap::EChunkHeapDuplicate); + c.Close(); + if (!aHeap) + return KErrNoMemory; +#ifdef TRACING_ALLOCS + if (aInfo.iFlags & ETraceHeapAllocs) + { + aHeap->iFlags |= RAllocator::ETraceAllocs; + BTraceContext8(BTrace::EHeap, BTrace::EHeapCreate,(TUint32)aHeap, RNewAllocator::EAllocCellSize); + TInt handle = aHeap->ChunkHandle(); + TInt chunkId = ((RHandleBase&)handle).BTraceId(); + BTraceContext8(BTrace::EHeap, BTrace::EHeapChunkCreate, (TUint32)aHeap, chunkId); + } +#endif + return KErrNone; + } + +/* + * \internal + * Called from the qtmain.lib application wrapper. + * Create a new heap as requested, but use the new allocator + */ +TInt _symbian_SetupThreadHeap(TBool /*aNotFirst*/, SStdEpocThreadCreateInfo& aInfo) + { + TInt r = KErrNone; + if (!aInfo.iAllocator && aInfo.iHeapInitialSize>0) + { + // new heap required + RNewAllocator* pH = NULL; + r = RNewAllocator::CreateThreadHeap(aInfo, pH); + } + else if (aInfo.iAllocator) + { + // sharing a heap + RAllocator* pA = aInfo.iAllocator; + r = pA->Open(); + if (r == KErrNone) + { + User::SwitchAllocator(pA); + } + } + return r; + } + +#ifndef __WINS__ +#pragma pop +#endif