/**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qt_hybridheap_symbian_p.h" #ifdef QT_USE_NEW_SYMBIAN_ALLOCATOR #define GM (&iGlobalMallocState) #define __HEAP_CORRUPTED_TRACE(t,p,l) BTraceContext12(BTrace::EHeap, BTrace::EHeapCorruption, (TUint32)t, (TUint32)p, (TUint32)l); #define __HEAP_CORRUPTED_TEST(c,x, p,l) if (!c) { if (iFlags & (EMonitorMemory+ETraceAllocs) ) __HEAP_CORRUPTED_TRACE(this,p,l) HEAP_PANIC(x); } #define __HEAP_CORRUPTED_TEST_STATIC(c,t,x,p,l) if (!c) { if (t && (t->iFlags & (EMonitorMemory+ETraceAllocs) )) __HEAP_CORRUPTED_TRACE(t,p,l) HEAP_PANIC(x); } TInt RHybridHeap::DebugFunction(TInt aFunc, TAny* a1, TAny* a2) { TInt r = KErrNone; switch(aFunc) { case RAllocator::ECount: struct HeapInfo info; Lock(); GetInfo(&info, NULL); *(unsigned*)a1 = info.iFreeN; r = info.iAllocN; Unlock(); 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 RHybridHeap::EGetFail: __DEBUG_ONLY(r = iFailType); break; case RHybridHeap::ESetBurstFail: #if _DEBUG { SRAllocatorBurstFail* fail = (SRAllocatorBurstFail*) a2; DoSetAllocFail((TAllocFail)(TInt)a1, fail->iRate, fail->iBurst); } #endif break; case RHybridHeap::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 RHybridHeap::EGetSize: { r = iChunkSize - sizeof(RHybridHeap); break; } case RHybridHeap::EGetMaxLength: { r = iMaxLength; break; } case RHybridHeap::EGetBase: { *(TAny**)a1 = iBase; break; } case RHybridHeap::EAlignInteger: { r = _ALIGN_UP((TInt)a1, iAlign); break; } case RHybridHeap::EAlignAddr: { *(TAny**)a2 = (TAny*)_ALIGN_UP((TLinAddr)a1, iAlign); break; } case RHybridHeap::EWalk: struct HeapInfo hinfo; SWalkInfo winfo; Lock(); winfo.iFunction = (TWalkFunc)a1; winfo.iParam = a2; winfo.iHeap = (RHybridHeap*)this; GetInfo(&hinfo, &winfo); Unlock(); break; #ifndef __KERNEL_MODE__ case RHybridHeap::EHybridHeap: { if ( !a1 ) return KErrGeneral; STestCommand* cmd = (STestCommand*)a1; switch ( cmd->iCommand ) { case EGetConfig: cmd->iConfig.iSlabBits = iSlabConfigBits; cmd->iConfig.iDelayedSlabThreshold = iPageThreshold; cmd->iConfig.iPagePower = iPageThreshold; break; case ESetConfig: // // New configuration data for slab and page allocator. // Reset heap to get data into use // #if USE_HYBRID_HEAP iSlabConfigBits = cmd->iConfig.iSlabBits & 0x3fff; iSlabInitThreshold = cmd->iConfig.iDelayedSlabThreshold; iPageThreshold = (cmd->iConfig.iPagePower & 0x1f); Reset(); #endif break; case EHeapMetaData: cmd->iData = this; break; case ETestData: iTestData = cmd->iData; break; default: return KErrNotSupported; } break; } #endif // __KERNEL_MODE default: return KErrNotSupported; } return r; } void RHybridHeap::Walk(SWalkInfo* aInfo, TAny* aBfr, TInt aLth, TCellType aBfrType, TAllocatorType aAllocatorType) { // // This function is always called from RHybridHeap::GetInfo. // Actual walk function is called if SWalkInfo pointer is defined // // if ( aInfo ) { #ifdef __KERNEL_MODE__ (void)aAllocatorType; #if defined(_DEBUG) if ( aBfrType == EGoodAllocatedCell ) aInfo->iFunction(aInfo->iParam, aBfrType, ((TUint8*)aBfr+EDebugHdrSize), (aLth-EDebugHdrSize) ); else aInfo->iFunction(aInfo->iParam, aBfrType, aBfr, aLth ); #else aInfo->iFunction(aInfo->iParam, aBfrType, aBfr, aLth ); #endif #else // __KERNEL_MODE__ if ( aAllocatorType & (EFullSlab + EPartialFullSlab + EEmptySlab + ESlabSpare) ) { if ( aInfo->iHeap ) { TUint32 dummy; TInt npages; aInfo->iHeap->DoCheckSlab((slab*)aBfr, aAllocatorType); __HEAP_CORRUPTED_TEST_STATIC(aInfo->iHeap->CheckBitmap(Floor(aBfr, PAGESIZE), PAGESIZE, dummy, npages), aInfo->iHeap, ETHeapBadCellAddress, aBfr, aLth); } if ( aAllocatorType & EPartialFullSlab ) WalkPartialFullSlab(aInfo, (slab*)aBfr, aBfrType, aLth); else if ( aAllocatorType & EFullSlab ) WalkFullSlab(aInfo, (slab*)aBfr, aBfrType, aLth); } #if defined(_DEBUG) else if ( aBfrType == EGoodAllocatedCell ) aInfo->iFunction(aInfo->iParam, aBfrType, ((TUint8*)aBfr+EDebugHdrSize), (aLth-EDebugHdrSize) ); else aInfo->iFunction(aInfo->iParam, aBfrType, aBfr, aLth ); #else else aInfo->iFunction(aInfo->iParam, aBfrType, aBfr, aLth ); #endif #endif // __KERNEL_MODE } } #ifndef __KERNEL_MODE__ void RHybridHeap::WalkPartialFullSlab(SWalkInfo* aInfo, slab* aSlab, TCellType /*aBfrType*/, TInt /*aLth*/) { if ( aInfo ) { // // Build bitmap of free buffers in the partial full slab // TUint32 bitmap[4]; __HEAP_CORRUPTED_TEST_STATIC( (aInfo->iHeap != NULL), aInfo->iHeap, ETHeapBadCellAddress, 0, aSlab); aInfo->iHeap->BuildPartialSlabBitmap(bitmap, aSlab); // // Find used (allocated) buffers from iPartial full slab // TUint32 h = aSlab->iHeader; TUint32 size = SlabHeaderSize(h); TUint32 count = KMaxSlabPayload / size; // Total buffer count in slab TUint32 i = 0; TUint32 ix = 0; TUint32 bit = 1; while ( i < count ) { if ( bitmap[ix] & bit ) { aInfo->iFunction(aInfo->iParam, EGoodFreeCell, &aSlab->iPayload[i*size], size ); } else { #if defined(_DEBUG) aInfo->iFunction(aInfo->iParam, EGoodAllocatedCell, (&aSlab->iPayload[i*size]+EDebugHdrSize), (size-EDebugHdrSize) ); #else aInfo->iFunction(aInfo->iParam, EGoodAllocatedCell, &aSlab->iPayload[i*size], size ); #endif } bit <<= 1; if ( bit == 0 ) { bit = 1; ix ++; } i ++; } } } void RHybridHeap::WalkFullSlab(SWalkInfo* aInfo, slab* aSlab, TCellType aBfrType, TInt /*aLth*/) { if ( aInfo ) { TUint32 h = aSlab->iHeader; TUint32 size = SlabHeaderSize(h); TUint32 count = (SlabHeaderUsedm4(h) + 4) / size; TUint32 i = 0; while ( i < count ) { #if defined(_DEBUG) if ( aBfrType == EGoodAllocatedCell ) aInfo->iFunction(aInfo->iParam, aBfrType, (&aSlab->iPayload[i*size]+EDebugHdrSize), (size-EDebugHdrSize) ); else aInfo->iFunction(aInfo->iParam, aBfrType, &aSlab->iPayload[i*size], size ); #else aInfo->iFunction(aInfo->iParam, aBfrType, &aSlab->iPayload[i*size], size ); #endif i ++; } } } void RHybridHeap::BuildPartialSlabBitmap(TUint32* aBitmap, slab* aSlab, TAny* aBfr) { // // Build a bitmap of free buffers in a partial full slab // TInt i; TUint32 bit = 0; TUint32 index; TUint32 h = aSlab->iHeader; TUint32 used = SlabHeaderUsedm4(h)+4; TUint32 size = SlabHeaderSize(h); TInt count = (KMaxSlabPayload / size); TInt free_count = count - (used / size); // Total free buffer count in slab aBitmap[0] = 0, aBitmap[1] = 0, aBitmap[2] = 0, aBitmap[3] = 0; TUint32 offs = (h & 0xff) << 2; // // Process first buffer in partial slab free buffer chain // while ( offs ) { unsigned char* p = (unsigned char*)Offset(aSlab, offs); __HEAP_CORRUPTED_TEST( (sizeof(slabhdr) <= offs), ETHeapBadCellAddress, p, aSlab); offs -= sizeof(slabhdr); __HEAP_CORRUPTED_TEST( (offs % size == 0), ETHeapBadCellAddress, p, aSlab); index = (offs / size); // Bit index in bitmap i = 0; while ( i < 4 ) { if ( index < 32 ) { bit = (1 << index); break; } index -= 32; i ++; } __HEAP_CORRUPTED_TEST( ((aBitmap[i] & bit) == 0), ETHeapBadCellAddress, p, aSlab); // Buffer already in chain aBitmap[i] |= bit; free_count --; offs = ((unsigned)*p) << 2; // Next in free chain } __HEAP_CORRUPTED_TEST( (free_count >= 0), ETHeapBadCellAddress, aBfr, aSlab); // free buffer count/size mismatch // // Process next rest of the free buffers which are in the // wilderness (at end of the slab) // index = count - 1; i = index / 32; index = index % 32; while ( free_count && (i >= 0)) { bit = (1 << index); __HEAP_CORRUPTED_TEST( ((aBitmap[i] & bit) == 0), ETHeapBadCellAddress, aBfr, aSlab); // Buffer already in chain aBitmap[i] |= bit; if ( index ) index --; else { index = 31; i --; } free_count --; } if ( aBfr ) // Assure that specified buffer does NOT exist in partial slab free buffer chain { offs = LowBits(aBfr, SLABSIZE); __HEAP_CORRUPTED_TEST( (sizeof(slabhdr) <= offs), ETHeapBadCellAddress, aBfr, aSlab); offs -= sizeof(slabhdr); __HEAP_CORRUPTED_TEST( ((offs % size) == 0), ETHeapBadCellAddress, aBfr, aSlab); index = (offs / size); // Bit index in bitmap i = 0; while ( i < 4 ) { if ( index < 32 ) { bit = (1 << index); break; } index -= 32; i ++; } __HEAP_CORRUPTED_TEST( ((aBitmap[i] & bit) == 0), ETHeapBadCellAddress, aBfr, aSlab); // Buffer already in chain } } #endif // __KERNEL_MODE__ void RHybridHeap::WalkCheckCell(TAny* aPtr, TCellType aType, TAny* aCell, TInt aLen) { (void)aCell; SHeapCellInfo& info = *(SHeapCellInfo*)aPtr; switch(aType) { case EGoodAllocatedCell: { ++info.iTotalAlloc; info.iTotalAllocSize += aLen; #if defined(_DEBUG) RHybridHeap& h = *info.iHeap; SDebugCell* DbgCell = (SDebugCell*)((TUint8*)aCell-EDebugHdrSize); if ( DbgCell->nestingLevel == h.iNestingLevel ) { if (++info.iLevelAlloc==1) info.iStranded = DbgCell; #ifdef __KERNEL_MODE__ if (KDebugNum(KSERVER) || KDebugNum(KTESTFAST)) { Kern::Printf("LEAKED KERNEL HEAP CELL @ %08x : len=%d", aCell, aLen); TLinAddr base = ((TLinAddr)aCell)&~0x0f; TLinAddr end = ((TLinAddr)aCell)+(TLinAddr)aLen; while(baseiCount; 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 RHybridHeap::DoMarkStart() { if (iNestingLevel==0) iAllocCount=0; iNestingLevel++; } TUint32 RHybridHeap::DoMarkEnd(TInt aExpected) { if (iNestingLevel==0) return 0; SHeapCellInfo info; SHeapCellInfo* p = iTestData ? (SHeapCellInfo*)iTestData : &info; memclr(p, sizeof(info)); p->iHeap = this; struct HeapInfo hinfo; SWalkInfo winfo; Lock(); winfo.iFunction = WalkCheckCell; winfo.iParam = p; winfo.iHeap = (RHybridHeap*)this; GetInfo(&hinfo, &winfo); Unlock(); if (p->iLevelAlloc != aExpected && !iTestData) return (TUint32)(p->iStranded + 1); if (--iNestingLevel == 0) iAllocCount = 0; return 0; } void RHybridHeap::DoSetAllocFail(TAllocFail aType, TInt aRate) {// Default to a burst mode of 1, as aType may be a burst type. DoSetAllocFail(aType, aRate, 1); } void ResetAllocCellLevels(TAny* aPtr, RHybridHeap::TCellType aType, TAny* aCell, TInt aLen) { (void)aPtr; (void)aLen; if (aType == RHybridHeap::EGoodAllocatedCell) { RHybridHeap::SDebugCell* DbgCell = (RHybridHeap::SDebugCell*)((TUint8*)aCell-RHybridHeap::EDebugHdrSize); DbgCell->nestingLevel = 0; } } // Don't change as the ETHeapBadDebugFailParameter check below and the API // documentation rely on this being 16 for RHybridHeap. LOCAL_D const TInt KBurstFailRateShift = 16; LOCAL_D const TInt KBurstFailRateMask = (1 << KBurstFailRateShift) - 1; void RHybridHeap::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. struct HeapInfo hinfo; SWalkInfo winfo; Lock(); winfo.iFunction = (TWalkFunc)&ResetAllocCellLevels; winfo.iParam = NULL; winfo.iHeap = (RHybridHeap*)this; GetInfo(&hinfo, &winfo); Unlock(); // 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 RHybridHeap::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 // DEBUG // // Methods for Doug Lea allocator detailed check // void RHybridHeap::DoCheckAnyChunk(mstate m, mchunkptr p) { __HEAP_CORRUPTED_TEST(((IS_ALIGNED(CHUNK2MEM(p))) || (p->iHead == FENCEPOST_HEAD)), ETHeapBadCellAddress, p, 0); (void)m; } /* Check properties of iTop chunk */ void RHybridHeap::DoCheckTopChunk(mstate m, mchunkptr p) { msegmentptr sp = &m->iSeg; size_t sz = CHUNKSIZE(p); __HEAP_CORRUPTED_TEST((sp != 0), ETHeapBadCellAddress, p, 0); __HEAP_CORRUPTED_TEST(((IS_ALIGNED(CHUNK2MEM(p))) || (p->iHead == FENCEPOST_HEAD)), ETHeapBadCellAddress, p,0); __HEAP_CORRUPTED_TEST((sz == m->iTopSize), ETHeapBadCellAddress,p,0); __HEAP_CORRUPTED_TEST((sz > 0), ETHeapBadCellAddress,p,0); __HEAP_CORRUPTED_TEST((sz == ((sp->iBase + sp->iSize) - (TUint8*)p) - TOP_FOOT_SIZE), ETHeapBadCellAddress,p,0); __HEAP_CORRUPTED_TEST((PINUSE(p)), ETHeapBadCellAddress,p,0); __HEAP_CORRUPTED_TEST((!NEXT_PINUSE(p)), ETHeapBadCellAddress,p,0); } /* Check properties of inuse chunks */ void RHybridHeap::DoCheckInuseChunk(mstate m, mchunkptr p) { DoCheckAnyChunk(m, p); __HEAP_CORRUPTED_TEST((CINUSE(p)), ETHeapBadCellAddress,p,0); __HEAP_CORRUPTED_TEST((NEXT_PINUSE(p)), ETHeapBadCellAddress,p,0); /* If not PINUSE and not mmapped, previous chunk has OK offset */ __HEAP_CORRUPTED_TEST((PINUSE(p) || NEXT_CHUNK(PREV_CHUNK(p)) == p), ETHeapBadCellAddress,p,0); } /* Check properties of free chunks */ void RHybridHeap::DoCheckFreeChunk(mstate m, mchunkptr p) { size_t sz = p->iHead & ~(PINUSE_BIT|CINUSE_BIT); mchunkptr next = CHUNK_PLUS_OFFSET(p, sz); DoCheckAnyChunk(m, p); __HEAP_CORRUPTED_TEST((!CINUSE(p)), ETHeapBadCellAddress,p,0); __HEAP_CORRUPTED_TEST((!NEXT_PINUSE(p)), ETHeapBadCellAddress,p,0); if (p != m->iDv && p != m->iTop) { if (sz >= MIN_CHUNK_SIZE) { __HEAP_CORRUPTED_TEST(((sz & CHUNK_ALIGN_MASK) == 0), ETHeapBadCellAddress,p,0); __HEAP_CORRUPTED_TEST((IS_ALIGNED(CHUNK2MEM(p))), ETHeapBadCellAddress,p,0); __HEAP_CORRUPTED_TEST((next->iPrevFoot == sz), ETHeapBadCellAddress,p,0); __HEAP_CORRUPTED_TEST((PINUSE(p)), ETHeapBadCellAddress,p,0); __HEAP_CORRUPTED_TEST( (next == m->iTop || CINUSE(next)), ETHeapBadCellAddress,p,0); __HEAP_CORRUPTED_TEST((p->iFd->iBk == p), ETHeapBadCellAddress,p,0); __HEAP_CORRUPTED_TEST((p->iBk->iFd == p), ETHeapBadCellAddress,p,0); } else /* markers are always of size SIZE_T_SIZE */ __HEAP_CORRUPTED_TEST((sz == SIZE_T_SIZE), ETHeapBadCellAddress,p,0); } } /* Check properties of malloced chunks at the point they are malloced */ void RHybridHeap::DoCheckMallocedChunk(mstate m, void* mem, size_t s) { if (mem != 0) { mchunkptr p = MEM2CHUNK(mem); size_t sz = p->iHead & ~(PINUSE_BIT|CINUSE_BIT); DoCheckInuseChunk(m, p); __HEAP_CORRUPTED_TEST(((sz & CHUNK_ALIGN_MASK) == 0), ETHeapBadCellAddress,p,0); __HEAP_CORRUPTED_TEST((sz >= MIN_CHUNK_SIZE), ETHeapBadCellAddress,p,0); __HEAP_CORRUPTED_TEST((sz >= s), ETHeapBadCellAddress,p,0); /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */ __HEAP_CORRUPTED_TEST((sz < (s + MIN_CHUNK_SIZE)), ETHeapBadCellAddress,p,0); } } /* Check a tree and its subtrees. */ void RHybridHeap::DoCheckTree(mstate m, tchunkptr t) { tchunkptr head = 0; tchunkptr u = t; bindex_t tindex = t->iIndex; size_t tsize = CHUNKSIZE(t); bindex_t idx; DoComputeTreeIndex(tsize, idx); __HEAP_CORRUPTED_TEST((tindex == idx), ETHeapBadCellAddress,u,0); __HEAP_CORRUPTED_TEST((tsize >= MIN_LARGE_SIZE), ETHeapBadCellAddress,u,0); __HEAP_CORRUPTED_TEST((tsize >= MINSIZE_FOR_TREE_INDEX(idx)), ETHeapBadCellAddress,u,0); __HEAP_CORRUPTED_TEST(((idx == NTREEBINS-1) || (tsize < MINSIZE_FOR_TREE_INDEX((idx+1)))), ETHeapBadCellAddress,u,0); do { /* traverse through chain of same-sized nodes */ DoCheckAnyChunk(m, ((mchunkptr)u)); __HEAP_CORRUPTED_TEST((u->iIndex == tindex), ETHeapBadCellAddress,u,0); __HEAP_CORRUPTED_TEST((CHUNKSIZE(u) == tsize), ETHeapBadCellAddress,u,0); __HEAP_CORRUPTED_TEST((!CINUSE(u)), ETHeapBadCellAddress,u,0); __HEAP_CORRUPTED_TEST((!NEXT_PINUSE(u)), ETHeapBadCellAddress,u,0); __HEAP_CORRUPTED_TEST((u->iFd->iBk == u), ETHeapBadCellAddress,u,0); __HEAP_CORRUPTED_TEST((u->iBk->iFd == u), ETHeapBadCellAddress,u,0); if (u->iParent == 0) { __HEAP_CORRUPTED_TEST((u->iChild[0] == 0), ETHeapBadCellAddress,u,0); __HEAP_CORRUPTED_TEST((u->iChild[1] == 0), ETHeapBadCellAddress,u,0); } else { __HEAP_CORRUPTED_TEST((head == 0), ETHeapBadCellAddress,u,0); /* only one node on chain has iParent */ head = u; __HEAP_CORRUPTED_TEST((u->iParent != u), ETHeapBadCellAddress,u,0); __HEAP_CORRUPTED_TEST( (u->iParent->iChild[0] == u || u->iParent->iChild[1] == u || *((tbinptr*)(u->iParent)) == u), ETHeapBadCellAddress,u,0); if (u->iChild[0] != 0) { __HEAP_CORRUPTED_TEST((u->iChild[0]->iParent == u), ETHeapBadCellAddress,u,0); __HEAP_CORRUPTED_TEST((u->iChild[0] != u), ETHeapBadCellAddress,u,0); DoCheckTree(m, u->iChild[0]); } if (u->iChild[1] != 0) { __HEAP_CORRUPTED_TEST((u->iChild[1]->iParent == u), ETHeapBadCellAddress,u,0); __HEAP_CORRUPTED_TEST((u->iChild[1] != u), ETHeapBadCellAddress,u,0); DoCheckTree(m, u->iChild[1]); } if (u->iChild[0] != 0 && u->iChild[1] != 0) { __HEAP_CORRUPTED_TEST((CHUNKSIZE(u->iChild[0]) < CHUNKSIZE(u->iChild[1])), ETHeapBadCellAddress,u,0); } } u = u->iFd; } while (u != t); __HEAP_CORRUPTED_TEST((head != 0), ETHeapBadCellAddress,u,0); } /* Check all the chunks in a treebin. */ void RHybridHeap::DoCheckTreebin(mstate m, bindex_t i) { tbinptr* tb = TREEBIN_AT(m, i); tchunkptr t = *tb; int empty = (m->iTreeMap & (1U << i)) == 0; if (t == 0) __HEAP_CORRUPTED_TEST((empty), ETHeapBadCellAddress,t,0); if (!empty) DoCheckTree(m, t); } /* Check all the chunks in a smallbin. */ void RHybridHeap::DoCheckSmallbin(mstate m, bindex_t i) { sbinptr b = SMALLBIN_AT(m, i); mchunkptr p = b->iBk; unsigned int empty = (m->iSmallMap & (1U << i)) == 0; if (p == b) __HEAP_CORRUPTED_TEST((empty), ETHeapBadCellAddress,p,0); if (!empty) { for (; p != b; p = p->iBk) { size_t size = CHUNKSIZE(p); mchunkptr q; /* each chunk claims to be free */ DoCheckFreeChunk(m, p); /* chunk belongs in bin */ __HEAP_CORRUPTED_TEST((SMALL_INDEX(size) == i), ETHeapBadCellAddress,p,0); __HEAP_CORRUPTED_TEST((p->iBk == b || CHUNKSIZE(p->iBk) == CHUNKSIZE(p)), ETHeapBadCellAddress,p,0); /* chunk is followed by an inuse chunk */ q = NEXT_CHUNK(p); if (q->iHead != FENCEPOST_HEAD) DoCheckInuseChunk(m, q); } } } /* Find x in a bin. Used in other check functions. */ TInt RHybridHeap::BinFind(mstate m, mchunkptr x) { size_t size = CHUNKSIZE(x); if (IS_SMALL(size)) { bindex_t sidx = SMALL_INDEX(size); sbinptr b = SMALLBIN_AT(m, sidx); if (SMALLMAP_IS_MARKED(m, sidx)) { mchunkptr p = b; do { if (p == x) return 1; } while ((p = p->iFd) != b); } } else { bindex_t tidx; DoComputeTreeIndex(size, tidx); if (TREEMAP_IS_MARKED(m, tidx)) { tchunkptr t = *TREEBIN_AT(m, tidx); size_t sizebits = size << LEFTSHIFT_FOR_TREE_INDEX(tidx); while (t != 0 && CHUNKSIZE(t) != size) { t = t->iChild[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; sizebits <<= 1; } if (t != 0) { tchunkptr u = t; do { if (u == (tchunkptr)x) return 1; } while ((u = u->iFd) != t); } } } return 0; } /* Traverse each chunk and check it; return total */ size_t RHybridHeap::TraverseAndCheck(mstate m) { size_t sum = 0; msegmentptr s = &m->iSeg; sum += m->iTopSize + TOP_FOOT_SIZE; mchunkptr q = ALIGN_AS_CHUNK(s->iBase); mchunkptr lastq = 0; __HEAP_CORRUPTED_TEST((PINUSE(q)), ETHeapBadCellAddress,q,0); while (q != m->iTop && q->iHead != FENCEPOST_HEAD) { sum += CHUNKSIZE(q); if (CINUSE(q)) { __HEAP_CORRUPTED_TEST((!BinFind(m, q)), ETHeapBadCellAddress,q,0); DoCheckInuseChunk(m, q); } else { __HEAP_CORRUPTED_TEST((q == m->iDv || BinFind(m, q)), ETHeapBadCellAddress,q,0); __HEAP_CORRUPTED_TEST((lastq == 0 || CINUSE(lastq)), ETHeapBadCellAddress,q,0); /* Not 2 consecutive free */ DoCheckFreeChunk(m, q); } lastq = q; q = NEXT_CHUNK(q); } return sum; } /* Check all properties of malloc_state. */ void RHybridHeap::DoCheckMallocState(mstate m) { bindex_t i; // size_t total; /* check bins */ for (i = 0; i < NSMALLBINS; ++i) DoCheckSmallbin(m, i); for (i = 0; i < NTREEBINS; ++i) DoCheckTreebin(m, i); if (m->iDvSize != 0) { /* check iDv chunk */ DoCheckAnyChunk(m, m->iDv); __HEAP_CORRUPTED_TEST((m->iDvSize == CHUNKSIZE(m->iDv)), ETHeapBadCellAddress,m->iDv,0); __HEAP_CORRUPTED_TEST((m->iDvSize >= MIN_CHUNK_SIZE), ETHeapBadCellAddress,m->iDv,0); __HEAP_CORRUPTED_TEST((BinFind(m, m->iDv) == 0), ETHeapBadCellAddress,m->iDv,0); } if (m->iTop != 0) { /* check iTop chunk */ DoCheckTopChunk(m, m->iTop); __HEAP_CORRUPTED_TEST((m->iTopSize == CHUNKSIZE(m->iTop)), ETHeapBadCellAddress,m->iTop,0); __HEAP_CORRUPTED_TEST((m->iTopSize > 0), ETHeapBadCellAddress,m->iTop,0); __HEAP_CORRUPTED_TEST((BinFind(m, m->iTop) == 0), ETHeapBadCellAddress,m->iTop,0); } // total = TraverseAndCheck(m); } #ifndef __KERNEL_MODE__ // // Methods for Slab allocator detailed check // void RHybridHeap::DoCheckSlabTree(slab** aS, TBool aPartialPage) { slab* s = *aS; if (!s) return; TUint size = SlabHeaderSize(s->iHeader); slab** parent = aS; slab** child2 = &s->iChild2; while ( s ) { __HEAP_CORRUPTED_TEST((s->iParent == parent), ETHeapBadCellAddress,s,SLABSIZE); __HEAP_CORRUPTED_TEST((!s->iChild1 || s < s->iChild1), ETHeapBadCellAddress,s,SLABSIZE); __HEAP_CORRUPTED_TEST((!s->iChild2 || s < s->iChild2), ETHeapBadCellAddress,s,SLABSIZE); if ( aPartialPage ) { if ( s->iChild1 ) size = SlabHeaderSize(s->iChild1->iHeader); } else { __HEAP_CORRUPTED_TEST((SlabHeaderSize(s->iHeader) == size), ETHeapBadCellAddress,s,SLABSIZE); } parent = &s->iChild1; s = s->iChild1; } parent = child2; s = *child2; while ( s ) { __HEAP_CORRUPTED_TEST((s->iParent == parent), ETHeapBadCellAddress,s,SLABSIZE); __HEAP_CORRUPTED_TEST((!s->iChild1 || s < s->iChild1), ETHeapBadCellAddress,s,SLABSIZE); __HEAP_CORRUPTED_TEST((!s->iChild2 || s < s->iChild2), ETHeapBadCellAddress,s,SLABSIZE); if ( aPartialPage ) { if ( s->iChild2 ) size = SlabHeaderSize(s->iChild2->iHeader); } else { __HEAP_CORRUPTED_TEST((SlabHeaderSize(s->iHeader) == size), ETHeapBadCellAddress,s,SLABSIZE); } parent = &s->iChild2; s = s->iChild2; } } void RHybridHeap::DoCheckSlabTrees() { for (TInt i = 0; i < (MAXSLABSIZE>>2); ++i) DoCheckSlabTree(&iSlabAlloc[i].iPartial, EFalse); DoCheckSlabTree(&iPartialPage, ETrue); } void RHybridHeap::DoCheckSlab(slab* aSlab, TAllocatorType aSlabType, TAny* aBfr) { if ( (aSlabType == ESlabSpare) || (aSlabType == EEmptySlab) ) return; unsigned h = aSlab->iHeader; __HEAP_CORRUPTED_TEST((ZEROBITS(h)), ETHeapBadCellAddress,aBfr,aSlab); unsigned used = SlabHeaderUsedm4(h)+4; unsigned size = SlabHeaderSize(h); __HEAP_CORRUPTED_TEST( (used < SLABSIZE),ETHeapBadCellAddress, aBfr, aSlab); __HEAP_CORRUPTED_TEST( ((size > 3 ) && (size <= MAXSLABSIZE)), ETHeapBadCellAddress,aBfr,aSlab); unsigned count = 0; switch ( aSlabType ) { case EFullSlab: count = (KMaxSlabPayload / size ); __HEAP_CORRUPTED_TEST((used == count*size), ETHeapBadCellAddress,aBfr,aSlab); __HEAP_CORRUPTED_TEST((HeaderFloating(h)), ETHeapBadCellAddress,aBfr,aSlab); break; case EPartialFullSlab: __HEAP_CORRUPTED_TEST(((used % size)==0),ETHeapBadCellAddress,aBfr,aSlab); __HEAP_CORRUPTED_TEST(((SlabHeaderFree(h) == 0) || (((SlabHeaderFree(h)<<2)-sizeof(slabhdr)) % SlabHeaderSize(h) == 0)), ETHeapBadCellAddress,aBfr,aSlab); break; default: break; } } // // Check that committed size in heap equals number of pages in bitmap // plus size of Doug Lea region // void RHybridHeap::DoCheckCommittedSize(TInt aNPages, mstate aM) { TInt total_committed = (aNPages * iPageSize) + aM->iSeg.iSize + (iBase - (TUint8*)this); __HEAP_CORRUPTED_TEST((total_committed == iChunkSize), ETHeapBadCellAddress,total_committed,iChunkSize); } #endif // __KERNEL_MODE__ #endif /* QT_USE_NEW_SYMBIAN_ALLOCATOR */