--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/common/heap.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1704 @@
+// Copyright (c) 1994-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// e32\common\heap.cpp
+//
+//
+
+#include "common.h"
+#ifdef __KERNEL_MODE__
+#include <kernel/kern_priv.h>
+#endif
+
+#ifdef _DEBUG
+#define __SIMULATE_ALLOC_FAIL(s) if (CheckForSimulatedAllocFail()) {s}
+#define __CHECK_CELL(p) CheckCell(p)
+#define __ZAP_CELL(p) memset( ((TUint8*)p) + RHeap::EAllocCellSize, 0xde, p->len - RHeap::EAllocCellSize)
+#define __DEBUG_SAVE(p) TInt dbgNestLevel = ((SDebugCell*)p)->nestingLevel
+#define __DEBUG_RESTORE(p) ((SDebugCell*)(((TUint8*)p)-EAllocCellSize))->nestingLevel = dbgNestLevel
+#else
+#define __SIMULATE_ALLOC_FAIL(s)
+#define __CHECK_CELL(p)
+#define __ZAP_CELL(p)
+#define __DEBUG_SAVE(p)
+#define __DEBUG_RESTORE(p)
+#endif
+
+#define __NEXT_CELL(p) ((SCell*)(((TUint8*)p)+p->len))
+
+#define __POWER_OF_2(x) ((TUint32)((x)^((x)-1))>=(TUint32)(x))
+
+#define __MEMORY_MONITOR_CHECK_CELL(p) \
+ { \
+ TLinAddr m = TLinAddr(iAlign-1); \
+ SCell* c = (SCell*)(((TUint8*)p)-EAllocCellSize); \
+ if((c->len & m) || (c->len<iMinCell) || ((TUint8*)c<iBase) || ((TUint8*)__NEXT_CELL(c)>iTop)) \
+ BTraceContext12(BTrace::EHeap, BTrace::EHeapCorruption, (TUint32)this, (TUint32)p, (TUint32)c->len-EAllocCellSize); \
+ }
+
+/**
+@SYMPatchable
+@publishedPartner
+@released
+
+Defines the minimum cell size of a heap.
+
+The constant can be changed at ROM build time using patchdata OBY keyword.
+*/
+#ifdef __X86GCC__ // For X86GCC we dont use the proper data import attribute
+#undef IMPORT_D // since the constant is not really imported. GCC doesn't
+#define IMPORT_D // allow imports from self.
+#endif
+IMPORT_D extern const TInt KHeapMinCellSize;
+
+/**
+@SYMPatchable
+@publishedPartner
+@released
+
+This constant defines the ratio that determines the amount of hysteresis between heap growing and heap
+shrinking.
+It is a 32-bit fixed point number where the radix point is defined to be
+between bits 7 and 8 (where the LSB is bit 0) i.e. using standard notation, a Q8 or a fx24.8
+fixed point number. For example, for a ratio of 2.0, set KHeapShrinkHysRatio=0x200.
+
+The heap shrinking hysteresis value is calculated to be:
+@code
+KHeapShrinkHysRatio*(iGrowBy>>8)
+@endcode
+where iGrowBy is a page aligned value set by the argument, aGrowBy, to the RHeap constructor.
+The default hysteresis value is iGrowBy bytes i.e. KHeapShrinkHysRatio=2.0.
+
+Memory usage may be improved by reducing the heap shrinking hysteresis
+by setting 1.0 < KHeapShrinkHysRatio < 2.0. Heap shrinking hysteresis is disabled/removed
+when KHeapShrinkHysRatio <= 1.0.
+
+The constant can be changed at ROM build time using patchdata OBY keyword.
+*/
+IMPORT_D extern const TInt KHeapShrinkHysRatio;
+
+#pragma warning( disable : 4705 ) // statement has no effect
+UEXPORT_C RHeap::RHeap(TInt aMaxLength, TInt aAlign, TBool aSingleThread)
+/**
+@internalComponent
+*/
+//
+// Constructor for fixed size heap
+//
+ : iMinLength(aMaxLength), iMaxLength(aMaxLength), iOffset(0), iGrowBy(0), iChunkHandle(0),
+ iNestingLevel(0), iAllocCount(0), iFailType(ENone), iTestData(NULL)
+ {
+ iAlign = aAlign ? aAlign : ECellAlignment;
+ iPageSize = 0;
+ iFlags = aSingleThread ? (ESingleThreaded|EFixedSize) : EFixedSize;
+ Initialise();
+ }
+#pragma warning( default : 4705 )
+
+
+
+
+UEXPORT_C RHeap::RHeap(TInt aChunkHandle, TInt aOffset, TInt aMinLength, TInt aMaxLength, TInt aGrowBy, TInt aAlign, TBool aSingleThread)
+/**
+@internalComponent
+*/
+//
+// Constructor for chunk heaps.
+//
+ : iOffset(aOffset), iChunkHandle(aChunkHandle),
+ iNestingLevel(0), iAllocCount(0), iFailType(ENone), iTestData(NULL)
+ {
+ TInt sz = iBase - ((TUint8*)this - iOffset);
+ GET_PAGE_SIZE(iPageSize);
+ __ASSERT_ALWAYS(iOffset>=0, HEAP_PANIC(ETHeapNewBadOffset));
+ iMinLength = Max(aMinLength, sz + EAllocCellSize);
+ iMinLength = _ALIGN_UP(iMinLength, iPageSize);
+ iMaxLength = Max(aMaxLength, iMinLength);
+ iMaxLength = _ALIGN_UP(iMaxLength, iPageSize);
+ iGrowBy = _ALIGN_UP(aGrowBy, iPageSize);
+ iFlags = aSingleThread ? ESingleThreaded : 0;
+ iAlign = aAlign ? aAlign : ECellAlignment;
+ Initialise();
+ }
+
+
+
+
+UEXPORT_C TAny* RHeap::operator new(TUint aSize, TAny* aBase) __NO_THROW
+/**
+@internalComponent
+*/
+ {
+ __ASSERT_ALWAYS(aSize>=sizeof(RHeap), HEAP_PANIC(ETHeapNewBadSize));
+ RHeap* h = (RHeap*)aBase;
+ h->iAlign = 0x80000000; // garbage value
+ h->iBase = ((TUint8*)aBase) + aSize;
+ return aBase;
+ }
+
+void RHeap::Initialise()
+//
+// Initialise the heap.
+//
+ {
+
+ __ASSERT_ALWAYS((TUint32)iAlign>=sizeof(TAny*) && __POWER_OF_2(iAlign), HEAP_PANIC(ETHeapNewBadAlignment));
+ iCellCount = 0;
+ iTotalAllocSize = 0;
+ iBase = (TUint8*)Align(iBase + EAllocCellSize);
+ iBase -= EAllocCellSize;
+ TInt b = iBase - ((TUint8*)this - iOffset);
+ TInt len = _ALIGN_DOWN(iMinLength - b, iAlign);
+ iTop = iBase + len;
+ iMinLength = iTop - ((TUint8*)this - iOffset);
+ iMinCell = Align(KHeapMinCellSize + Max((TInt)EAllocCellSize, (TInt)EFreeCellSize));
+#ifdef _DEBUG
+ memset(iBase, 0xa5, len);
+#endif
+ SCell* pM=(SCell*)iBase; // First free cell
+ iFree.next=pM; // Free list points to first free cell
+ iFree.len=0; // Stop free from joining this with a free block
+ pM->next=NULL; // Terminate the free list
+ pM->len=len; // Set the size of the free cell
+ }
+
+#ifdef _DEBUG
+void RHeap::CheckCell(const SCell* aCell) const
+ {
+ TLinAddr m = TLinAddr(iAlign - 1);
+
+ __ASSERT_DEBUG(!(aCell->len & m), HEAP_PANIC(ETHeapBadCellAddress));
+ __ASSERT_DEBUG(aCell->len >= iMinCell, HEAP_PANIC(ETHeapBadCellAddress));
+ __ASSERT_DEBUG((TUint8*)aCell>=iBase, HEAP_PANIC(ETHeapBadCellAddress));
+ __ASSERT_DEBUG((TUint8*)__NEXT_CELL(aCell)<=iTop, HEAP_PANIC(ETHeapBadCellAddress));
+ }
+#endif
+
+UEXPORT_C RHeap::SCell* RHeap::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;
+ }
+
+
+
+
+UEXPORT_C TInt RHeap::AllocLen(const TAny* aCell) const
+/**
+Gets the length of the available space in the specified allocated cell.
+
+@param aCell A pointer to the allocated cell.
+
+@return The length of the available space in the allocated cell.
+
+@panic USER 42 if aCell does not point to a valid cell.
+*/
+ {
+
+ SCell* pC = GetAddress(aCell);
+ return pC->len - EAllocCellSize;
+ }
+
+
+
+
+
+#if !defined(__HEAP_MACHINE_CODED__) || defined(_DEBUG)
+RHeap::SCell* RHeap::DoAlloc(TInt aSize, SCell*& aLastFree)
+//
+// Allocate without growing. aSize includes cell header and alignment.
+// Lock already held.
+//
+ {
+ SCell* pP = &iFree;
+ SCell* pC = pP->next;
+ for (; pC; pP=pC, pC=pC->next) // Scan the free list
+ {
+ __CHECK_CELL(pC);
+ SCell* pE;
+ if (pC->len >= aSize) // Block size bigger than request
+ {
+ if (pC->len - aSize < iMinCell) // Leftover must be large enough to hold an SCell
+ {
+ aSize = pC->len; // It isn't, so take it all
+ pE = pC->next; // Set the next field
+ }
+ else
+ {
+ pE = (SCell*)(((TUint8*)pC)+aSize); // Take amount required
+ pE->len = pC->len - aSize; // Initialize new free cell
+ pE->next = pC->next;
+ }
+ pP->next = pE; // Update previous pointer
+ pC->len = aSize; // Set control size word
+#if defined(_DEBUG)
+ ((SDebugCell*)pC)->nestingLevel = iNestingLevel;
+ ((SDebugCell*)pC)->allocCount = ++iAllocCount;
+#endif
+ return pC;
+ }
+ }
+ aLastFree = pP;
+ return NULL;
+ }
+#endif
+
+
+
+
+UEXPORT_C TAny* RHeap::Alloc(TInt aSize)
+/**
+Allocates a cell of the specified size from the heap.
+
+If there is insufficient memory available on the heap from which to allocate
+a cell of the required size, the function returns NULL.
+
+The cell is aligned according to the alignment value specified at construction,
+or the default alignment value, if an explict value was not specified.
+
+The resulting size of the allocated cell may be rounded up to a
+value greater than aSize, but is guaranteed to be not less than aSize.
+
+@param aSize The
+size of the cell to be allocated from the heap
+
+@return A pointer to the allocated cell. NULL if there is insufficient memory
+ available.
+
+@panic USER 47 if the maximum unsigned value of aSize is greater than or equal
+ to the value of KMaxTInt/2; for example, calling Alloc(-1) raises
+ this panic.
+
+@see KMaxTInt
+*/
+ {
+
+ __CHECK_THREAD_STATE;
+ __ASSERT_ALWAYS((TUint)aSize<(KMaxTInt/2),HEAP_PANIC(ETHeapBadAllocatedCellSize));
+ __SIMULATE_ALLOC_FAIL(return NULL;)
+
+ TInt origSize = aSize;
+ aSize = Max(Align(aSize + EAllocCellSize), iMinCell);
+ SCell* pL = NULL;
+ Lock();
+ SCell* pC = (SCell*)DoAlloc(aSize, pL);
+ if (!pC && !(iFlags & EFixedSize))
+ {
+ // try to grow chunk heap
+ TInt r = TryToGrowHeap(aSize, pL);
+ if (r==KErrNone)
+ pC = DoAlloc(aSize, pL);
+ }
+ if (pC)
+ ++iCellCount, iTotalAllocSize += (pC->len - EAllocCellSize);
+ Unlock();
+ if (pC)
+ {
+ TAny* result=((TUint8*)pC) + EAllocCellSize;
+ if (iFlags & ETraceAllocs)
+ {
+ TUint32 traceData[2];
+ traceData[0] = AllocLen(result);
+ traceData[1] = origSize;
+ BTraceContextN(BTrace::EHeap, BTrace::EHeapAlloc, (TUint32)this, (TUint32)result, traceData, sizeof(traceData));
+ }
+#ifdef __KERNEL_MODE__
+ memclr(result, pC->len - EAllocCellSize);
+#endif
+ return result;
+ }
+ if (iFlags & ETraceAllocs)
+ BTraceContext8(BTrace::EHeap, BTrace::EHeapAllocFail, (TUint32)this, (TUint32)origSize);
+ return NULL;
+ }
+
+
+
+
+TInt RHeap::TryToGrowHeap(TInt aSize, SCell* aLastFree)
+ {
+ TBool at_end = IsLastCell(aLastFree);
+ TInt extra = at_end ? aSize - aLastFree->len : aSize;
+ extra = (extra + iGrowBy - 1) / iGrowBy;
+ extra *= iGrowBy;
+ TInt cur_len = _ALIGN_UP(iTop - ((TUint8*)this - iOffset), iPageSize);
+ TInt new_len = cur_len + extra;
+ TInt r = KErrNoMemory;
+ if (new_len <= iMaxLength)
+ {
+ r = SetBrk(new_len);
+ if (r == KErrNone)
+ {
+ if (at_end)
+ aLastFree->len += extra;
+ else
+ {
+ SCell* pC = (SCell*)iTop;
+ pC->len = extra;
+ pC->next = NULL;
+ aLastFree->next = pC;
+ }
+ iTop += extra;
+ }
+ }
+ return r;
+ }
+
+
+
+
+#ifndef __KERNEL_MODE__
+EXPORT_C TInt RHeap::Compress()
+/**
+Compresses the heap.
+
+The function frees excess committed space from the top
+of the heap. The size of the heap is never reduced below the minimum size
+specified during creation of the heap.
+
+@return The space reclaimed. If no space can be reclaimed, then this value
+ is zero.
+*/
+ {
+
+ if (iFlags & EFixedSize)
+ return 0;
+ TInt r = 0;
+ Lock();
+ SCell* pC = &iFree;
+ for (; pC->next; pC=pC->next) {}
+ if (pC!=&iFree)
+ {
+ __CHECK_CELL(pC);
+ if (IsLastCell(pC))
+ r = Reduce(pC);
+ }
+ Unlock();
+ return r;
+ }
+#endif
+
+
+
+
+#if !defined(__HEAP_MACHINE_CODED__) || defined(_DEBUG)
+void RHeap::DoFree(SCell* pC)
+ {
+ __ZAP_CELL(pC);
+
+ SCell* pP = &iFree;
+ SCell* pE = pP->next;
+ for (; pE && pE<pC; pP=pE, pE=pE->next) {}
+ if (pE) // Is there a following free cell?
+ {
+ SCell* pN = __NEXT_CELL(pC);
+ __ASSERT_ALWAYS(pN<=pE, HEAP_PANIC(ETHeapFreeBadNextCell)); // Following cell overlaps
+ if (pN==pE) // Is it adjacent
+ {
+ pC->len += pE->len; // Yes - coalesce adjacent free cells
+ pC->next = pE->next;
+ }
+ else // pN<pE, non-adjacent free cells
+ pC->next = pE; // Otherwise just point to it
+ }
+ else
+ pC->next = NULL; // No following free cell
+ SCell* pN = __NEXT_CELL(pP); // pN=pP=&iFree if no preceding free cell
+ __ASSERT_ALWAYS(pN<=pC, HEAP_PANIC(ETHeapFreeBadPrevCell)); // Previous cell overlaps
+ if (pN==pC) // Is it adjacent
+ {
+ pP->len += pC->len; // Yes - coalesce adjacent free cells
+ pP->next = pC->next;
+ pC = pP; // for size reduction check
+ }
+ else // pN<pC, non-adjacent free cells
+ pP->next = pC; // point previous cell to the one being freed
+ pN = __NEXT_CELL(pC); // End of amalgamated free cell
+ if ((TUint8*)pN==iTop && !(iFlags & EFixedSize) &&
+ pC->len >= KHeapShrinkHysRatio*(iGrowBy>>8))
+ Reduce(pC);
+ }
+#endif
+
+
+
+
+UEXPORT_C void RHeap::Free(TAny* aCell)
+/**
+Frees the specified cell and returns it to the heap.
+
+@param aCell A pointer to a valid cell; this pointer can also be NULL,
+ in which case the function does nothing and just returns.
+
+@panic USER 42 if aCell points to an invalid cell.
+*/
+ {
+ __CHECK_THREAD_STATE;
+ if (!aCell)
+ return;
+ Lock();
+ if (iFlags & EMonitorMemory)
+ __MEMORY_MONITOR_CHECK_CELL(aCell);
+ SCell* pC = GetAddress(aCell);
+ --iCellCount;
+ iTotalAllocSize -= (pC->len - EAllocCellSize);
+ DoFree(pC);
+ if (iFlags & ETraceAllocs)
+ BTraceContext8(BTrace::EHeap, BTrace::EHeapFree, (TUint32)this, (TUint32)aCell);
+ Unlock();
+ }
+
+
+
+
+TInt RHeap::Reduce(SCell* aCell)
+ {
+ TInt reduce=0;
+ TInt offset=((TUint8*)aCell)-((TUint8*)this - iOffset);
+ if (offset>=iMinLength)
+ reduce = aCell->len; // length of entire free cell
+ else
+ reduce = offset + aCell->len - iMinLength; // length of free cell past minimum heap size
+ reduce = _ALIGN_DOWN(reduce, iPageSize); // round down to page multiple
+ if (reduce<=0)
+ return 0; // can't reduce this heap
+ TInt new_cell_len = aCell->len - reduce; // length of last free cell after reduction
+ if (new_cell_len == 0)
+ {
+ // the free cell can be entirely eliminated
+ SCell* pP = &iFree;
+ for (; pP->next!=aCell; pP=pP->next) {}
+ pP->next = NULL;
+ }
+ else
+ {
+ if (new_cell_len < iMinCell)
+ {
+ // max reduction would leave a cell too small
+ reduce -= iPageSize;
+ new_cell_len += iPageSize;
+ }
+ aCell->len = new_cell_len; // reduce the cell length
+ }
+ iTop -= reduce;
+ TInt new_len = _ALIGN_UP(iTop - ((TUint8*)this - iOffset), iPageSize);
+ TInt r = SetBrk(new_len);
+ __ASSERT_ALWAYS(r==KErrNone, HEAP_PANIC(ETHeapReduceFailed));
+ return reduce;
+ }
+
+
+
+
+#ifndef __KERNEL_MODE__
+EXPORT_C void RHeap::Reset()
+/**
+Frees all allocated cells on this heap.
+*/
+ {
+
+ Lock();
+ if (!(iFlags & EFixedSize))
+ {
+ TInt r = SetBrk(iMinLength);
+ __ASSERT_ALWAYS(r==KErrNone, HEAP_PANIC(ETHeapResetFailed));
+ }
+ Initialise();
+ Unlock();
+ }
+#endif
+
+
+
+
+inline void RHeap::FindFollowingFreeCell(SCell* aCell, SCell*& aPrev, SCell*& aNext)
+//
+// Find the free cell that immediately follows aCell, if one exists
+// If found, aNext is set to point to it, else it is set to NULL.
+// aPrev is set to the free cell before aCell or the dummy free cell where there are no free cells before aCell.
+// Called with lock enabled.
+//
+ {
+ aPrev = &iFree;
+ aNext = aPrev->next;
+ for (; aNext && aNext<aCell; aPrev=aNext, aNext=aNext->next) {}
+
+ if (aNext) // If there is a following free cell, check its directly after aCell.
+ {
+ SCell* pNextCell = __NEXT_CELL(aCell); // end of this cell
+ __ASSERT_ALWAYS(pNextCell<=aNext, (Unlock(), HEAP_PANIC(ETHeapReAllocBadNextCell))); // Following free cell overlaps
+ if (pNextCell!=aNext)
+ aNext=NULL;
+ }
+ }
+
+
+
+
+TInt RHeap::TryToGrowCell(SCell* aCell,SCell* aPrev, SCell* aNext, TInt aSize)
+//
+// Try to grow the heap cell 'aCell' in place, to size 'aSize'.
+// Requires the free cell immediately after aCell (aNext), and the free cell prior to
+// that (aPrev), to be provided. (As found by FindFollowingFreeCell)
+//
+
+ {
+ TInt extra = aSize - aCell->len;
+ if (aNext && (aNext->len>=extra)) // Is there a following free cell big enough?
+ {
+ if (aNext->len - extra >= iMinCell) // take part of free cell ?
+ {
+ SCell* pX = (SCell*)((TUint8*)aNext + extra); // remainder of free cell
+ pX->next = aNext->next; // remainder->next = original free cell->next
+ pX->len = aNext->len - extra; // remainder length = original free cell length - extra
+ aPrev->next = pX; // put remainder into free chain
+ }
+ else
+ {
+ extra = aNext->len; // Take whole free cell
+ aPrev->next = aNext->next; // remove from free chain
+ }
+#ifdef __KERNEL_MODE__
+ memclr(((TUint8*)aCell) + aCell->len, extra);
+#endif
+ aCell->len += extra; // update reallocated cell length
+ iTotalAllocSize += extra;
+ return KErrNone;
+ }
+ return KErrGeneral; // No space to grow cell
+ }
+
+
+
+
+// UEXPORT_C TAny* RHeap::ReAlloc(TAny* aCell, TInt aSize, TInt aMode)
+/**
+Increases or decreases the size of an existing cell in the heap.
+
+If the cell is being decreased in size, then it is guaranteed not to move,
+and the function returns the pointer originally passed in aCell. Note that the
+length of the cell will be the same if the difference between the old size
+and the new size is smaller than the minimum cell size.
+
+If the cell is being increased in size, i.e. aSize is bigger than its
+current size, then the function tries to grow the cell in place.
+If successful, then the function returns the pointer originally
+passed in aCell. If unsuccessful, then:
+
+1. if the cell cannot be moved, i.e. aMode has the ENeverMove bit set, then
+ the function returns NULL.
+2. if the cell can be moved, i.e. aMode does not have the ENeverMove bit set,
+ then the function tries to allocate a new replacement cell, and, if
+ successful, returns a pointer to the new cell; if unsuccessful, it
+ returns NULL.
+
+Note that in debug mode, the function returns NULL if the cell cannot be grown
+in place, regardless of whether the ENeverMove bit is set.
+
+If the reallocated cell is at a different location from the original cell, then
+the content of the original cell is copied to the reallocated cell.
+
+If the supplied pointer, aCell is NULL, then the function attempts to allocate
+a new cell, but only if the cell can be moved, i.e. aMode does not have
+the ENeverMove bit set.
+
+Note the following general points:
+
+1. If reallocation fails, the content of the original cell is preserved.
+
+2. The resulting size of the re-allocated cell may be rounded up to a value
+ greater than aSize, but is guaranteed to be not less than aSize.
+
+@param aCell A pointer to the cell to be reallocated. This may be NULL.
+
+@param aSize The new size of the cell. This may be bigger or smaller than the
+ size of the original cell.
+
+@param aMode Flags controlling the reallocation. The only bit which has any
+ effect on this function is that defined by the enumeration
+ ENeverMove of the enum RAllocator::TReAllocMode.
+ If this is set, then any successful reallocation guarantees not
+ to have changed the start address of the cell.
+ By default, this parameter is zero.
+
+@return A pointer to the reallocated cell. This may be the same as the original
+ pointer supplied through aCell. NULL if there is insufficient memory to
+ reallocate the cell, or to grow it in place.
+
+@panic USER 42, if aCell is not NULL, and does not point to a valid cell.
+@panic USER 47, if the maximum unsigned value of aSize is greater
+ than or equal to KMaxTInt/2. For example,
+ calling ReAlloc(someptr,-1) raises this panic.
+
+@see RAllocator::TReAllocMode
+*/
+UEXPORT_C TAny* RHeap::ReAlloc(TAny* aCell, TInt aSize, TInt aMode)
+ {
+ if (aCell && iFlags&EMonitorMemory)
+ __MEMORY_MONITOR_CHECK_CELL(aCell);
+ TAny* retval = ReAllocImpl(aCell, aSize, aMode);
+ if (iFlags & ETraceAllocs)
+ {
+ if (retval)
+ {
+ TUint32 traceData[3];
+ traceData[0] = AllocLen(retval);
+ traceData[1] = aSize;
+ traceData[2] = (TUint32)aCell;
+ BTraceContextN(BTrace::EHeap, BTrace::EHeapReAlloc,(TUint32)this, (TUint32)retval,traceData, sizeof(traceData));
+ }
+ else
+ BTraceContext12(BTrace::EHeap, BTrace::EHeapReAllocFail, (TUint32)this, (TUint32)aCell, (TUint32)aSize);
+ }
+ return retval;
+ }
+inline TAny* RHeap::ReAllocImpl(TAny* aCell, TInt aSize, TInt aMode)
+ {
+ __CHECK_THREAD_STATE;
+ if (!aCell)
+ return (aMode & ENeverMove) ? NULL : Alloc(aSize);
+ __ASSERT_ALWAYS((TUint)aSize<(KMaxTInt/2),HEAP_PANIC(ETHeapBadAllocatedCellSize));
+ Lock();
+ SCell* pC = GetAddress(aCell);
+ TInt old_len = pC->len;
+ __DEBUG_SAVE(pC);
+ aSize = Max(Align(aSize + EAllocCellSize), iMinCell);
+ if (aSize > old_len) // Trying to grow cell
+ {
+ __SIMULATE_ALLOC_FAIL({ Unlock(); return NULL;})
+
+ // Try to grow cell in place, without reallocation
+ SCell* pPrev;
+ SCell* pNext;
+ FindFollowingFreeCell(pC,pPrev, pNext);
+ TInt r = TryToGrowCell(pC, pPrev, pNext, aSize);
+
+ if (r==KErrNone)
+ {
+ Unlock();
+ return aCell;
+ }
+
+ if (!(aMode & ENeverMove))
+ // If moving allowed, try re-alloc.
+ // If we need to extend heap,and cell is at the end, try and grow in place
+ {
+ SCell* pLastFree;
+ SCell* pNewCell = (SCell*)DoAlloc(aSize, pLastFree);
+ if (!pNewCell && !(iFlags & EFixedSize))
+ // if we need to extend the heap to alloc
+ {
+ if (IsLastCell(pC) || (pNext && IsLastCell(pNext)))
+ // if last used Cell, try and extend heap and then cell
+ {
+ TInt r = TryToGrowHeap(aSize - old_len, pLastFree);
+ if (r==KErrNone)
+ {
+ r = TryToGrowCell(pC, pPrev, pPrev->next, aSize);
+ Unlock();
+ __ASSERT_DEBUG(r == KErrNone, HEAP_PANIC(ETHeapCellDidntGrow));
+ return aCell;
+ }
+ }
+ else
+ // try to grow chunk heap and Alloc on it
+ {
+ TInt r = TryToGrowHeap(aSize, pLastFree);
+ if (r==KErrNone)
+ pNewCell = DoAlloc(aSize, pLastFree);
+ }
+ }
+
+ if (pNewCell)
+ // if we created a new cell, adjust tellies, copy the contents and delete old cell.
+ {
+ iCellCount++;
+ iTotalAllocSize += (pNewCell->len - EAllocCellSize);
+
+ Unlock();
+ TUint8* raw = ((TUint8*) pNewCell);
+
+ memcpy(raw + EAllocCellSize, aCell, old_len - EAllocCellSize);
+#ifdef __KERNEL_MODE__
+ memclr(raw + old_len, pNewCell->len - old_len);
+#endif
+ Free(aCell);
+ __DEBUG_RESTORE(raw + EAllocCellSize);
+ return raw + EAllocCellSize;
+ }
+ }
+ else
+ // No moving, but still posible to extend the heap (if heap extendable)
+ {
+ if (!(iFlags & EFixedSize) && (IsLastCell(pC) || (pNext && IsLastCell(pNext))))
+ {
+ SCell* pLastFree = pNext ? pNext : pPrev;
+ TInt r = TryToGrowHeap(aSize - old_len, pLastFree);
+ if (r==KErrNone)
+ {
+ r = TryToGrowCell(pC, pPrev, pPrev->next, aSize);
+ Unlock();
+ __ASSERT_DEBUG(r==KErrNone, HEAP_PANIC(ETHeapCellDidntGrow));
+ return aCell;
+ }
+ }
+ }
+ Unlock();
+ return NULL;
+ }
+ if (old_len - aSize >= iMinCell)
+ {
+ // cell shrinking, remainder big enough to form a new free cell
+ SCell* pX = (SCell*)((TUint8*)pC + aSize); // pointer to new free cell
+ pC->len = aSize; // update cell size
+ pX->len = old_len - aSize; // size of remainder
+ iTotalAllocSize -= pX->len;
+ DoFree(pX); // link new free cell into chain, shrink heap if necessary
+ }
+ Unlock();
+ return aCell;
+ }
+
+
+
+
+#ifndef __KERNEL_MODE__
+
+EXPORT_C TInt RHeap::Available(TInt& aBiggestBlock) const
+/**
+Gets the total free space currently available on the heap and the space
+available in the largest free block.
+
+The space available represents the total space which can be allocated.
+
+Note that compressing the heap may reduce the total free space available and
+the space available in the largest free block.
+
+@param aBiggestBlock On return, contains the space available
+ in the largest free block on the heap.
+
+@return The total free space currently available on the heap.
+*/
+ {
+
+ TInt total = 0;
+ TInt max = 0;
+ Lock();
+ SCell* pC = iFree.next;
+ for (; pC; pC=pC->next)
+ {
+ TInt l = pC->len - EAllocCellSize;
+ if (l > max)
+ max = l;
+ total += l;
+ }
+ Unlock();
+ aBiggestBlock = max;
+ return total;
+ }
+
+
+
+
+EXPORT_C TInt RHeap::AllocSize(TInt& aTotalAllocSize) const
+/**
+Gets the number of cells allocated on this heap, and the total space
+allocated to them.
+
+@param aTotalAllocSize On return, contains the total space allocated
+ to the cells.
+
+@return The number of cells allocated on this heap.
+*/
+ {
+ Lock();
+ TInt c = iCellCount;
+ aTotalAllocSize = iTotalAllocSize;
+ Unlock();
+ return c;
+ }
+
+
+
+
+EXPORT_C RHeap* UserHeap::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.
+@panic USER 172 if aAlign is not a power of 2 or is less than the size of a TAny*.
+*/
+//
+// Force construction of the fixed memory.
+//
+ {
+
+ __ASSERT_ALWAYS(aMaxLength>=0, ::Panic(ETHeapMaxLengthNegative));
+ if (aMaxLength<KMinHeapSize)
+ aMaxLength=KMinHeapSize;
+ RHeap* h = new(aBase) RHeap(aMaxLength, aAlign, aSingleThread);
+ if (!aSingleThread)
+ {
+ TInt r = h->iLock.CreateLocal();
+ if (r!=KErrNone)
+ return NULL;
+ h->iHandles = (TInt*)&h->iLock;
+ h->iHandleCount = 1;
+ }
+ return h;
+ }
+
+
+/**
+Constructor where minimum and maximum length of the heap can be defined.
+It defaults the chunk heap to be created to have use a new local chunk,
+to have a grow by value of KMinHeapGrowBy, to be unaligned, not to be
+single threaded and not to have any mode flags set.
+
+@param aMinLength The minimum length of the heap to be created.
+@param aMaxLength The maximum length to which the heap to be created can grow.
+ If the supplied value is less than KMinHeapSize, then it
+ is discarded and the value KMinHeapSize used instead.
+*/
+EXPORT_C TChunkHeapCreateInfo::TChunkHeapCreateInfo(TInt aMinLength, TInt aMaxLength) :
+ iVersionNumber(EVersion0), iMinLength(aMinLength), iMaxLength(aMaxLength),
+ iAlign(0), iGrowBy(1), iSingleThread(EFalse),
+ iOffset(0), iPaging(EUnspecified), iMode(0), iName(NULL)
+ {
+ }
+
+
+/**
+Sets the chunk heap to create a new chunk with the specified name.
+
+This overriddes any previous call to TChunkHeapCreateInfo::SetNewChunkHeap() or
+TChunkHeapCreateInfo::SetExistingChunkHeap() for this TChunkHeapCreateInfo object.
+
+@param aName The name to be given to the chunk heap to be created
+ 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.
+*/
+EXPORT_C void TChunkHeapCreateInfo::SetCreateChunk(const TDesC* aName)
+ {
+ iName = (TDesC*)aName;
+ iChunk.SetHandle(KNullHandle);
+ }
+
+
+/**
+Sets the chunk heap to be created to use the chunk specified.
+
+This overriddes any previous call to TChunkHeapCreateInfo::SetNewChunkHeap() or
+TChunkHeapCreateInfo::SetExistingChunkHeap() for this TChunkHeapCreateInfo object.
+
+@param aChunk A handle to the chunk to use for the heap.
+*/
+EXPORT_C void TChunkHeapCreateInfo::SetUseChunk(const RChunk aChunk)
+ {
+ iName = NULL;
+ iChunk = aChunk;
+ }
+
+
+/**
+Creates a chunk heap of the type specified by the parameter aCreateInfo.
+
+@param aCreateInfo A reference to a TChunkHeapCreateInfo object specifying the
+ type of chunk heap to create.
+
+@return A pointer to the new heap or NULL if the heap could not be created.
+
+@panic USER 41 if the heap's specified minimum length is greater than the specified maximum length.
+@panic USER 55 if the heap's specified minimum length is negative.
+@panic USER 172 if the heap's specified alignment is not a power of 2 or is less than the size of a TAny*.
+*/
+EXPORT_C RHeap* UserHeap::ChunkHeap(const TChunkHeapCreateInfo& aCreateInfo)
+ {
+ // aCreateInfo must have been configured to use a new chunk or an exiting chunk.
+ __ASSERT_ALWAYS(!(aCreateInfo.iMode & (TUint32)~EChunkHeapMask), ::Panic(EHeapCreateInvalidMode));
+ RHeap* h = NULL;
+
+ if (aCreateInfo.iChunk.Handle() == KNullHandle)
+ {// A new chunk is to be created for this heap.
+ __ASSERT_ALWAYS(aCreateInfo.iMinLength >= 0, ::Panic(ETHeapMinLengthNegative));
+ __ASSERT_ALWAYS(aCreateInfo.iMaxLength >= aCreateInfo.iMinLength, ::Panic(ETHeapCreateMaxLessThanMin));
+
+ TInt maxLength = aCreateInfo.iMaxLength;
+ if (maxLength < KMinHeapSize)
+ maxLength = KMinHeapSize;
+
+ TChunkCreateInfo chunkInfo;
+ chunkInfo.SetNormal(0, maxLength);
+ chunkInfo.SetOwner((aCreateInfo.iSingleThread)? EOwnerThread : EOwnerProcess);
+ if (aCreateInfo.iName)
+ chunkInfo.SetGlobal(*aCreateInfo.iName);
+ // Set the paging attributes of the chunk.
+ if (aCreateInfo.iPaging == TChunkHeapCreateInfo::EPaged)
+ chunkInfo.SetPaging(TChunkCreateInfo::EPaged);
+ if (aCreateInfo.iPaging == TChunkHeapCreateInfo::EUnpaged)
+ chunkInfo.SetPaging(TChunkCreateInfo::EUnpaged);
+ // Create the chunk.
+ RChunk chunk;
+ if (chunk.Create(chunkInfo) != KErrNone)
+ return NULL;
+ // Create the heap using the new chunk.
+ TUint mode = aCreateInfo.iMode | EChunkHeapDuplicate; // Must duplicate the handle.
+ h = OffsetChunkHeap(chunk, aCreateInfo.iMinLength, aCreateInfo.iOffset,
+ aCreateInfo.iGrowBy, maxLength, aCreateInfo.iAlign,
+ aCreateInfo.iSingleThread, mode);
+ chunk.Close();
+ }
+ else
+ {
+ h = OffsetChunkHeap(aCreateInfo.iChunk, aCreateInfo.iMinLength, aCreateInfo.iOffset,
+ aCreateInfo.iGrowBy, aCreateInfo.iMaxLength, aCreateInfo.iAlign,
+ aCreateInfo.iSingleThread, aCreateInfo.iMode);
+ }
+ return h;
+ }
+
+
+EXPORT_C RHeap* UserHeap::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 172 if aAlign is not a power of 2 or is less than the size of a TAny*.
+*/
+//
+// Allocate a Chunk of the requested size and force construction.
+//
+ {
+ TChunkHeapCreateInfo createInfo(aMinLength, aMaxLength);
+ createInfo.SetCreateChunk(aName);
+ createInfo.SetGrowBy(aGrowBy);
+ createInfo.SetAlignment(aAlign);
+ createInfo.SetSingleThread(aSingleThread);
+ return ChunkHeap(createInfo);
+ }
+
+
+
+
+EXPORT_C RHeap* UserHeap::ChunkHeap(RChunk aChunk, TInt aMinLength, TInt aGrowBy, TInt aMaxLength, TInt aAlign, TBool aSingleThread, TUint32 aMode)
+/**
+Creates a heap in an existing chunk.
+
+This function is intended to be used to create a heap in a user writable code
+chunk as created by a call to RChunk::CreateLocalCode().
+This type of heap can be used to hold code fragments from a JIT compiler.
+
+The maximum length to which the heap can grow is the same as
+the maximum size of the chunk.
+
+@param aChunk The chunk that will host the heap.
+@param aMinLength The minimum length of the heap.
+@param aGrowBy The increments to the size of the host chunk.
+@param aMaxLength The maximum length to which the heap can grow.
+@param aAlign The alignment of heap cells.
+@param aSingleThread Indicates whether single threaded or not.
+@param aMode Flags controlling the heap creation. This should be set
+ from one or more of the values in TChunkHeapCreateMode.
+
+@return A pointer to the new heap or NULL if the heap could not be created.
+
+@panic USER 172 if aAlign is not a power of 2 or is less than the size of a TAny*.
+*/
+//
+// Construct a heap in an already existing chunk
+//
+ {
+
+ return OffsetChunkHeap(aChunk, aMinLength, 0, aGrowBy, aMaxLength, aAlign, aSingleThread, aMode);
+ }
+
+
+
+
+EXPORT_C RHeap* UserHeap::OffsetChunkHeap(RChunk aChunk, TInt aMinLength, TInt aOffset, TInt aGrowBy, TInt aMaxLength, TInt aAlign, TBool aSingleThread, TUint32 aMode)
+/**
+Creates a heap in an existing chunk, offset from the beginning of the chunk.
+
+This function is intended to be used to create a heap where a fixed amount of
+additional data must be stored at a known location. The additional data can be
+placed at the base address of the chunk, allowing it to be located without
+depending on the internals of the heap structure.
+
+The maximum length to which the heap can grow is the maximum size of the chunk,
+minus the offset.
+
+@param aChunk The chunk that will host the heap.
+@param aMinLength The minimum length of the heap.
+@param aOffset The offset from the start of the chunk, to the start of the heap.
+@param aGrowBy The increments to the size of the host chunk.
+@param aMaxLength The maximum length to which the heap can grow.
+@param aAlign The alignment of heap cells.
+@param aSingleThread Indicates whether single threaded or not.
+@param aMode Flags controlling the heap creation. This should be set
+ from one or more of the values in TChunkHeapCreateMode.
+
+@return A pointer to the new heap or NULL if the heap could not be created.
+
+@panic USER 172 if aAlign is not a power of 2 or is less than the size of a TAny*.
+*/
+//
+// Construct a heap in an already existing chunk
+//
+ {
+
+ TInt page_size;
+ UserHal::PageSizeInBytes(page_size);
+ if (!aAlign)
+ aAlign = RHeap::ECellAlignment;
+ TInt maxLength = aChunk.MaxSize();
+ TInt round_up = Max(aAlign, page_size);
+ TInt min_cell = _ALIGN_UP(Max((TInt)RHeap::EAllocCellSize, (TInt)RHeap::EFreeCellSize), aAlign);
+ aOffset = _ALIGN_UP(aOffset, 8);
+ if (aMaxLength && aMaxLength+aOffset<maxLength)
+ maxLength = _ALIGN_UP(aMaxLength+aOffset, round_up);
+ __ASSERT_ALWAYS(aMinLength>=0, ::Panic(ETHeapMinLengthNegative));
+ __ASSERT_ALWAYS(maxLength>=aMinLength, ::Panic(ETHeapCreateMaxLessThanMin));
+ aMinLength = _ALIGN_UP(Max(aMinLength, (TInt)sizeof(RHeap) + min_cell) + aOffset, round_up);
+ TInt r=aChunk.Adjust(aMinLength);
+ if (r!=KErrNone)
+ return NULL;
+
+ RHeap* h = new (aChunk.Base() + aOffset) RHeap(aChunk.Handle(), aOffset, aMinLength, maxLength, aGrowBy, aAlign, aSingleThread);
+
+ TBool duplicateLock = EFalse;
+ if (!aSingleThread)
+ {
+ duplicateLock = aMode & EChunkHeapSwitchTo;
+ if(h->iLock.CreateLocal(duplicateLock ? EOwnerThread : EOwnerProcess)!=KErrNone)
+ {
+ h->iChunkHandle = 0;
+ return NULL;
+ }
+ }
+
+ if (aMode & 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 & EChunkHeapDuplicate))
+ {
+ r = ((RChunk*)&h->iChunkHandle)->Duplicate(RThread());
+ if (r!=KErrNone)
+ h->iLock.Close(), h->iChunkHandle=0;
+ }
+ }
+ else
+ {
+ h->iHandleCount = 1;
+ if (aMode & EChunkHeapDuplicate)
+ r = ((RChunk*)&h->iChunkHandle)->Duplicate(RThread(), EOwnerThread);
+ }
+
+ // return the heap address
+ return (r==KErrNone) ? h : NULL;
+ }
+
+
+
+#define UserTestDebugMaskBit(bit) (TBool)(UserSvr::DebugMask(bit>>5) & (1<<(bit&31)))
+
+_LIT(KLitDollarHeap,"$HEAP");
+EXPORT_C TInt UserHeap::CreateThreadHeap(SStdEpocThreadCreateInfo& aInfo, RHeap*& aHeap, TInt aAlign, TBool aSingleThread)
+/**
+@internalComponent
+*/
+//
+// Create a user-side heap
+//
+ {
+ TInt page_size;
+ UserHal::PageSizeInBytes(page_size);
+ TInt minLength = _ALIGN_UP(aInfo.iHeapInitialSize, page_size);
+ TInt maxLength = Max(aInfo.iHeapMaxSize, minLength);
+ if (UserTestDebugMaskBit(96)) // 96 == KUSERHEAPTRACE in nk_trace.h
+ aInfo.iFlags |= ETraceHeapAllocs;
+
+ // Create the thread's heap chunk.
+ RChunk c;
+ TChunkCreateInfo createInfo;
+ createInfo.SetThreadHeap(0, maxLength, KLitDollarHeap()); // Initialise with no memory committed.
+
+ // Set the heap chunk to be paged or unpaged based on the thread's settings.
+ // aInfo.iFlags will have already been set by the kernel based on the paging policies.
+ TBool paged = (aInfo.iFlags & EThreadCreateFlagPagingMask) == EThreadCreateFlagPaged;
+ TChunkCreateInfo::TChunkPagingAtt paging =
+ (paged)? TChunkCreateInfo::EPaged : TChunkCreateInfo::EUnpaged;
+ createInfo.SetPaging(paging);
+
+ TInt r = c.Create(createInfo);
+ if (r!=KErrNone)
+ return r;
+
+ aHeap = ChunkHeap(c, minLength, page_size, maxLength, aAlign, aSingleThread, EChunkHeapSwitchTo|EChunkHeapDuplicate);
+ c.Close();
+ if (!aHeap)
+ return KErrNoMemory;
+ if (aInfo.iFlags & ETraceHeapAllocs)
+ {
+ aHeap->iFlags |= RHeap::ETraceAllocs;
+ BTraceContext8(BTrace::EHeap, BTrace::EHeapCreate,(TUint32)aHeap, RHeap::EAllocCellSize);
+ TInt handle = aHeap->ChunkHandle();
+ TInt chunkId = ((RHandleBase&)handle).BTraceId();
+ BTraceContext8(BTrace::EHeap, BTrace::EHeapChunkCreate, (TUint32)aHeap, chunkId);
+ }
+ if (aInfo.iFlags & EMonitorHeapMemory)
+ aHeap->iFlags |= RHeap::EMonitorMemory;
+ return KErrNone;
+ }
+
+#endif // __KERNEL_MODE__
+
+void RHeap::WalkCheckCell(TAny* aPtr, TCellType aType, TAny* aCell, TInt aLen)
+ {
+ (void)aCell;
+ SHeapCellInfo& info = *(SHeapCellInfo*)aPtr;
+ switch(aType)
+ {
+ case EGoodAllocatedCell:
+ {
+ ++info.iTotalAlloc;
+ info.iTotalAllocSize += (aLen-EAllocCellSize);
+#if defined(_DEBUG)
+ RHeap& h = *info.iHeap;
+ if ( ((SDebugCell*)aCell)->nestingLevel == h.iNestingLevel )
+ {
+ if (++info.iLevelAlloc==1)
+ info.iStranded = (SDebugCell*)aCell;
+#ifdef __KERNEL_MODE__
+ if (KDebugNum(KSERVER) || KDebugNum(KTESTFAST))
+ {
+// __KTRACE_OPT(KSERVER,Kern::Printf("LEAKED KERNEL HEAP CELL @ %08x : len=%d", aCell, aLen));
+ Kern::Printf("LEAKED KERNEL HEAP CELL @ %08x : len=%d", aCell, aLen);
+ TLinAddr base = ((TLinAddr)aCell)&~0x0f;
+ TLinAddr end = ((TLinAddr)aCell)+(TLinAddr)aLen;
+ while(base<end)
+ {
+ const TUint32* p = (const TUint32*)base;
+ Kern::Printf("%08x: %08x %08x %08x %08x", p, p[0], p[1], p[2], p[3]);
+ base += 16;
+ }
+ }
+#endif
+ }
+#endif
+ break;
+ }
+ case EGoodFreeCell:
+ ++info.iTotalFree;
+ break;
+ case EBadAllocatedCellSize:
+ HEAP_PANIC(ETHeapBadAllocatedCellSize);
+ case EBadAllocatedCellAddress:
+ HEAP_PANIC(ETHeapBadAllocatedCellAddress);
+ case EBadFreeCellAddress:
+ HEAP_PANIC(ETHeapBadFreeCellAddress);
+ case EBadFreeCellSize:
+ HEAP_PANIC(ETHeapBadFreeCellSize);
+ default:
+ HEAP_PANIC(ETHeapWalkBadCellType);
+ }
+ }
+
+TInt RHeap::DoCountAllocFree(TInt& aFree)
+ {
+ SHeapCellInfo info;
+ memclr(&info, sizeof(info));
+ info.iHeap = this;
+ Walk(&WalkCheckCell, &info);
+ aFree = info.iTotalFree;
+ return info.iTotalAlloc;
+ }
+
+
+UEXPORT_C TInt RHeap::DebugFunction(TInt aFunc, TAny* a1, TAny* a2)
+/**
+@internalComponent
+*/
+ {
+ TInt r = KErrNone;
+ switch(aFunc)
+ {
+ case RAllocator::ECount:
+ r = DoCountAllocFree(*(TInt*)a1);
+ break;
+ case RAllocator::EMarkStart:
+ __DEBUG_ONLY(DoMarkStart());
+ break;
+ case RAllocator::EMarkEnd:
+ __DEBUG_ONLY( r = DoMarkEnd((TInt)a1) );
+ break;
+ case RAllocator::ECheck:
+ r = DoCheckHeap((SCheckInfo*)a1);
+ break;
+ case RAllocator::ESetFail:
+ __DEBUG_ONLY(DoSetAllocFail((TAllocFail)(TInt)a1, (TInt)a2));
+ break;
+ case RAllocator::ESetBurstFail:
+#if _DEBUG
+ {
+ SRAllocatorBurstFail* fail = (SRAllocatorBurstFail*) a2;
+ DoSetAllocFail((TAllocFail)(TInt)a1, fail->iRate, fail->iBurst);
+ }
+#endif
+ break;
+
+ case RAllocator::ECheckFailure:
+ // iRand will be incremented for each EFailNext, EBurstFailNext,
+ // EDeterministic and EBurstDeterministic failure.
+ r = iRand;
+ break;
+
+ case RAllocator::ECopyDebugInfo:
+ {
+ TInt nestingLevel = ((SDebugCell*)a1)[-1].nestingLevel;
+ ((SDebugCell*)a2)[-1].nestingLevel = nestingLevel;
+ break;
+ }
+ case RHeap::EWalk:
+ Walk((TWalkFunc)a1, a2);
+ break;
+ default:
+ return KErrNotSupported;
+ }
+ return r;
+ }
+
+
+
+
+void RHeap::Walk(TWalkFunc aFunc, TAny* aPtr)
+//
+// Walk the heap calling the info function.
+//
+ {
+
+ Lock();
+ SCell* pC = (SCell*)iBase; // allocated cells
+ SCell* pF = &iFree; // free cells
+ FOREVER
+ {
+ pF = pF->next; // next free cell
+ if (!pF)
+ pF = (SCell*)iTop; // to make size checking work
+ else if ( (TUint8*)pF>=iTop || (pF->next && pF->next<=pF) )
+ {
+ if (iFlags & ETraceAllocs)
+ BTraceContext12(BTrace::EHeap, BTrace::EHeapCorruption, (TUint32)this, (TUint32)pF+EFreeCellSize, 0);
+ // free cell pointer off the end or going backwards
+ Unlock();
+ (*aFunc)(aPtr, EBadFreeCellAddress, pF, 0);
+ return;
+ }
+ else
+ {
+ TInt l = pF->len;
+ if (l<iMinCell || (l & (iAlign-1)))
+ {
+ if (iFlags & ETraceAllocs)
+ BTraceContext12(BTrace::EHeap, BTrace::EHeapCorruption, (TUint32)this, (TUint32)pF+EFreeCellSize, l-EFreeCellSize);
+ // free cell length invalid
+ Unlock();
+ (*aFunc)(aPtr, EBadFreeCellSize, pF, l);
+ return;
+ }
+ }
+ while (pC!=pF) // walk allocated cells up to next free cell
+ {
+ TInt l = pC->len;
+ if (l<iMinCell || (l & (iAlign-1)))
+ {
+ if (iFlags & ETraceAllocs)
+ BTraceContext12(BTrace::EHeap, BTrace::EHeapCorruption, (TUint32)this, (TUint32)pC+EAllocCellSize, l-EAllocCellSize);
+ // allocated cell length invalid
+ Unlock();
+ (*aFunc)(aPtr, EBadAllocatedCellSize, pC, l);
+ return;
+ }
+ (*aFunc)(aPtr, EGoodAllocatedCell, pC, l);
+ SCell* pN = __NEXT_CELL(pC);
+ if (pN > pF)
+ {
+ if (iFlags & ETraceAllocs)
+ BTraceContext12(BTrace::EHeap, BTrace::EHeapCorruption, (TUint32)this, (TUint32)pC+EAllocCellSize, l-EAllocCellSize);
+ // cell overlaps next free cell
+ Unlock();
+ (*aFunc)(aPtr, EBadAllocatedCellAddress, pC, l);
+ return;
+ }
+ pC = pN;
+ }
+ if ((TUint8*)pF == iTop)
+ break; // reached end of heap
+ pC = __NEXT_CELL(pF); // step to next allocated cell
+ (*aFunc)(aPtr, EGoodFreeCell, pF, pF->len);
+ }
+ Unlock();
+ }
+
+TInt RHeap::DoCheckHeap(SCheckInfo* aInfo)
+ {
+ (void)aInfo;
+ SHeapCellInfo info;
+ memclr(&info, sizeof(info));
+ info.iHeap = this;
+ Walk(&WalkCheckCell, &info);
+#if defined(_DEBUG)
+ if (!aInfo)
+ return KErrNone;
+ TInt expected = aInfo->iCount;
+ TInt actual = aInfo->iAll ? info.iTotalAlloc : info.iLevelAlloc;
+ if (actual!=expected && !iTestData)
+ {
+#ifdef __KERNEL_MODE__
+ Kern::Fault("KERN-ALLOC COUNT", (expected<<16)|actual );
+#else
+ User::Panic(_L("ALLOC COUNT"), (expected<<16)|actual );
+#endif
+ }
+#endif
+ return KErrNone;
+ }
+
+#ifdef _DEBUG
+void RHeap::DoMarkStart()
+ {
+ if (iNestingLevel==0)
+ iAllocCount=0;
+ iNestingLevel++;
+ }
+
+TUint32 RHeap::DoMarkEnd(TInt aExpected)
+ {
+ if (iNestingLevel==0)
+ return 0;
+ SHeapCellInfo info;
+ SHeapCellInfo* p = iTestData ? (SHeapCellInfo*)iTestData : &info;
+ memclr(p, sizeof(info));
+ p->iHeap = this;
+ Walk(&WalkCheckCell, p);
+ if (p->iLevelAlloc != aExpected && !iTestData)
+ return (TUint32)(p->iStranded + 1);
+ if (--iNestingLevel == 0)
+ iAllocCount = 0;
+ return 0;
+ }
+
+void ResetAllocCellLevels(TAny* aPtr, RHeap::TCellType aType, TAny* aCell, TInt aLen)
+ {
+ (void)aPtr;
+ (void)aLen;
+ RHeap::SDebugCell* cell = (RHeap::SDebugCell*)aCell;
+ if (aType == RHeap::EGoodAllocatedCell)
+ {
+ cell->nestingLevel = 0;
+ }
+ }
+
+void RHeap::DoSetAllocFail(TAllocFail aType, TInt aRate)
+ {// Default to a burst mode of 1, as aType may be a burst type.
+ DoSetAllocFail(aType, aRate, 1);
+ }
+
+// Don't change as the ETHeapBadDebugFailParameter check below and the API
+// documentation rely on this being 16 for RHeap.
+LOCAL_D const TInt KBurstFailRateShift = 16;
+LOCAL_D const TInt KBurstFailRateMask = (1 << KBurstFailRateShift) - 1;
+
+void RHeap::DoSetAllocFail(TAllocFail aType, TInt aRate, TUint aBurst)
+ {
+ if (aType==EReset)
+ {
+ // reset levels of all allocated cells to 0
+ // this should prevent subsequent tests failing unnecessarily
+ iFailed = EFalse; // Reset for ECheckFailure relies on this.
+ Walk(&ResetAllocCellLevels, NULL);
+ // reset heap allocation mark as well
+ iNestingLevel=0;
+ iAllocCount=0;
+ aType=ENone;
+ }
+
+ switch (aType)
+ {
+ case EBurstRandom:
+ case EBurstTrueRandom:
+ case EBurstDeterministic:
+ case EBurstFailNext:
+ // If the fail type is a burst type then iFailRate is split in 2:
+ // the 16 lsbs are the fail rate and the 16 msbs are the burst length.
+ if (TUint(aRate) > (TUint)KMaxTUint16 || aBurst > KMaxTUint16)
+ HEAP_PANIC(ETHeapBadDebugFailParameter);
+
+ iFailed = EFalse;
+ iFailType = aType;
+ iFailRate = (aRate == 0) ? 1 : aRate;
+ iFailAllocCount = -iFailRate;
+ iFailRate = iFailRate | (aBurst << KBurstFailRateShift);
+ break;
+
+ default:
+ iFailed = EFalse;
+ iFailType = aType;
+ iFailRate = (aRate == 0) ? 1 : aRate; // A rate of <1 is meaningless
+ iFailAllocCount = 0;
+ break;
+ }
+
+ // Set up iRand for either:
+ // - random seed value, or
+ // - a count of the number of failures so far.
+ iRand = 0;
+#ifndef __KERNEL_MODE__
+ switch (iFailType)
+ {
+ case ETrueRandom:
+ case EBurstTrueRandom:
+ {
+ TTime time;
+ time.HomeTime();
+ TInt64 seed = time.Int64();
+ iRand = Math::Rand(seed);
+ break;
+ }
+ case ERandom:
+ case EBurstRandom:
+ {
+ TInt64 seed = 12345;
+ iRand = Math::Rand(seed);
+ break;
+ }
+ default:
+ break;
+ }
+#endif
+ }
+
+TBool RHeap::CheckForSimulatedAllocFail()
+//
+// Check to see if the user has requested simulated alloc failure, and if so possibly
+// Return ETrue indicating a failure.
+//
+ {
+ // For burst mode failures iFailRate is shared
+ TUint16 rate = (TUint16)(iFailRate & KBurstFailRateMask);
+ TUint16 burst = (TUint16)(iFailRate >> KBurstFailRateShift);
+ TBool r = EFalse;
+ switch (iFailType)
+ {
+#ifndef __KERNEL_MODE__
+ case ERandom:
+ case ETrueRandom:
+ if (++iFailAllocCount>=iFailRate)
+ {
+ iFailAllocCount=0;
+ if (!iFailed) // haven't failed yet after iFailRate allocations so fail now
+ return(ETrue);
+ iFailed=EFalse;
+ }
+ else
+ {
+ if (!iFailed)
+ {
+ TInt64 seed=iRand;
+ iRand=Math::Rand(seed);
+ if (iRand%iFailRate==0)
+ {
+ iFailed=ETrue;
+ return(ETrue);
+ }
+ }
+ }
+ break;
+
+ case EBurstRandom:
+ case EBurstTrueRandom:
+ if (++iFailAllocCount < 0)
+ {
+ // We haven't started failing yet so should we now?
+ TInt64 seed = iRand;
+ iRand = Math::Rand(seed);
+ if (iRand % rate == 0)
+ {// Fail now. Reset iFailAllocCount so we fail burst times
+ iFailAllocCount = 0;
+ r = ETrue;
+ }
+ }
+ else
+ {
+ if (iFailAllocCount < burst)
+ {// Keep failing for burst times
+ r = ETrue;
+ }
+ else
+ {// We've now failed burst times so start again.
+ iFailAllocCount = -(rate - 1);
+ }
+ }
+ break;
+#endif
+ case EDeterministic:
+ if (++iFailAllocCount%iFailRate==0)
+ {
+ r=ETrue;
+ iRand++; // Keep count of how many times we have failed
+ }
+ break;
+
+ case EBurstDeterministic:
+ // This will fail burst number of times, every rate attempts.
+ if (++iFailAllocCount >= 0)
+ {
+ if (iFailAllocCount == burst - 1)
+ {// This is the burst time we have failed so make it the last by
+ // reseting counts so we next fail after rate attempts.
+ iFailAllocCount = -rate;
+ }
+ r = ETrue;
+ iRand++; // Keep count of how many times we have failed
+ }
+ break;
+
+ case EFailNext:
+ if ((++iFailAllocCount%iFailRate)==0)
+ {
+ iFailType=ENone;
+ r=ETrue;
+ iRand++; // Keep count of how many times we have failed
+ }
+ break;
+
+ case EBurstFailNext:
+ if (++iFailAllocCount >= 0)
+ {
+ if (iFailAllocCount == burst - 1)
+ {// This is the burst time we have failed so make it the last.
+ iFailType = ENone;
+ }
+ r = ETrue;
+ iRand++; // Keep count of how many times we have failed
+ }
+ break;
+ default:
+ break;
+ }
+ return r;
+ }
+#endif // ifdef _DEBUG
+
+UEXPORT_C TInt RHeap::Extension_(TUint aExtensionId, TAny*& a0, TAny* a1)
+ {
+ return RAllocator::Extension_(aExtensionId, a0, a1);
+ }
+
+#if defined(__HEAP_MACHINE_CODED__) && !defined(_DEBUG)
+GLDEF_C void RHeap_PanicBadAllocatedCellSize()
+ {
+ HEAP_PANIC(ETHeapBadAllocatedCellSize);
+ }
+
+GLDEF_C void RHeap_PanicBadNextCell()
+ {
+ HEAP_PANIC(ETHeapFreeBadNextCell);
+ }
+
+GLDEF_C void RHeap_PanicBadPrevCell()
+ {
+ HEAP_PANIC(ETHeapFreeBadPrevCell);
+ }
+
+GLDEF_C void RHeap_PanicBadCellAddress()
+ {
+ HEAP_PANIC(ETHeapBadCellAddress);
+ }
+#endif
+
+
+
+
+