diff -r 07b41fa8d1dd -r ca8a1b6995f6 memspy/Driver/Shared/heaputils.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/memspy/Driver/Shared/heaputils.cpp Tue Aug 31 16:45:49 2010 +0300 @@ -0,0 +1,1678 @@ +// heaputils.cpp +// +// Copyright (c) 2010 Accenture. All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the "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: +// Accenture - Initial contribution +// +#ifdef TEST_HYBRIDHEAP_ASSERTS +#define private public +#include +#include "slab.h" +#include "page_alloc.h" +#include "heap_hybrid.h" +#endif + +#include "heaputils.h" + +#ifdef __KERNEL_MODE__ + +#include +#define MEM Kern +__ASSERT_COMPILE(sizeof(LtkUtils::RKernelSideAllocatorHelper) == 10*4); +#define KERN_ENTER_CS() NKern::ThreadEnterCS() +#define KERN_LEAVE_CS() NKern::ThreadLeaveCS() +#define LOG(args...) +#define HUEXPORT_C +#else + +#include +#define MEM User +#define KERN_ENTER_CS() +#define KERN_LEAVE_CS() +//#include +//#define LOG(args...) RDebug::Printf(args) +#define LOG(args...) + +#ifdef STANDALONE_ALLOCHELPER +#define HUEXPORT_C +#else +#define HUEXPORT_C EXPORT_C +#endif + +#endif // __KERNEL_MODE__ + +using LtkUtils::RAllocatorHelper; +const TUint KPageSize = 4096; +__ASSERT_COMPILE(sizeof(RAllocatorHelper) == 9*4); + +// RAllocatorHelper + +HUEXPORT_C RAllocatorHelper::RAllocatorHelper() + : iAllocatorAddress(0), iAllocatorType(EUnknown), iInfo(NULL), iValidInfo(0), iTempSlabBitmap(NULL), iPageCache(NULL), iPageCacheAddr(0) +#ifdef __KERNEL_MODE__ + , iChunk(NULL) +#endif + { + } + +namespace LtkUtils + { + class THeapInfo + { + public: + THeapInfo() + { + ClearStats(); + } + + void ClearStats() + { + memclr(this, sizeof(THeapInfo)); + } + + TInt iAllocatedSize; // number of bytes in allocated cells (excludes free cells, cell header overhead) + TInt iCommittedSize; // amount of memory actually committed (includes cell header overhead, gaps smaller than an MMU page) + TInt iAllocationCount; // number of allocations currently + TInt iMaxCommittedSize; // or thereabouts + TInt iMinCommittedSize; + TInt iUnusedPages; + TInt iCommittedFreeSpace; + // Heap-only stats + TInt iHeapFreeCellCount; + // Hybrid-only stats + TInt iDlaAllocsSize; + TInt iDlaAllocsCount; + TInt iDlaFreeSize; + TInt iDlaFreeCount; + TInt iSlabAllocsSize; + TInt iSlabAllocsCount; + TInt iPageAllocsSize; + TInt iPageAllocsCount; + TInt iSlabFreeCellSize; + TInt iSlabFreeCellCount; + TInt iSlabFreeSlabSize; + TInt iSlabFreeSlabCount; + }; + } + +const TInt KTempBitmapSize = 256; // KMaxSlabPayload / mincellsize, technically. Close enough. + +#ifdef __KERNEL_MODE__ + +TInt RAllocatorHelper::OpenKernelHeap() + { + _LIT(KName, "SvHeap"); + NKern::ThreadEnterCS(); + DObjectCon* chunkContainer = Kern::Containers()[EChunk]; + chunkContainer->Wait(); + const TInt chunkCount = chunkContainer->Count(); + DChunk* foundChunk = NULL; + for(TInt i=0; iNameBuf() && chunk->NameBuf()->Find(KName) != KErrNotFound) + { + // Found it. No need to open it, we can be fairly confident the kernel heap isn't going to disappear from under us + foundChunk = chunk; + break; + } + } + iChunk = foundChunk; + chunkContainer->Signal(); +#ifdef __WINS__ + TInt err = OpenChunkHeap((TLinAddr)foundChunk->Base(), 0); // It looks like DChunk::iBase/DChunk::iFixedBase should both be ok for the kernel chunk +#else + // Copied from P::KernelInfo + const TRomHeader& romHdr=Epoc::RomHeader(); + const TRomEntry* primaryEntry=(const TRomEntry*)Kern::SuperPage().iPrimaryEntry; + const TRomImageHeader* primaryImageHeader=(const TRomImageHeader*)primaryEntry->iAddressLin; + TLinAddr stack = romHdr.iKernDataAddress + Kern::RoundToPageSize(romHdr.iTotalSvDataSize); + TLinAddr heap = stack + Kern::RoundToPageSize(primaryImageHeader->iStackSize); + TInt err = OpenChunkHeap(heap, 0); // aChunkMaxSize is only used for trying the middle of the chunk for hybrid allocatorness, and the kernel heap doesn't use that (thankfully). So we can safely pass in zero. + +#endif + if (!err) err = FinishConstruction(); + NKern::ThreadLeaveCS(); + return err; + } + +#else + +HUEXPORT_C TInt RAllocatorHelper::Open(RAllocator* aAllocator) + { + iAllocatorAddress = (TLinAddr)aAllocator; + TInt udeb = EuserIsUdeb(); + if (udeb < 0) return udeb; // error + + TInt err = IdentifyAllocatorType(udeb); + if (!err) + { + err = FinishConstruction(); // Allocate everything up front + } + if (!err) + { + // We always stealth our own allocations, again to avoid tripping up allocator checks + SetCellNestingLevel(iInfo, -1); + SetCellNestingLevel(iTempSlabBitmap, -1); + SetCellNestingLevel(iPageCache, -1); + } + return err; + } + +#endif + +TInt RAllocatorHelper::FinishConstruction() + { + TInt err = KErrNone; + KERN_ENTER_CS(); + if (!iInfo) + { + iInfo = new THeapInfo; + if (!iInfo) err = KErrNoMemory; + } + if (!err && !iTempSlabBitmap) + { + iTempSlabBitmap = (TUint8*)MEM::Alloc(KTempBitmapSize); + if (!iTempSlabBitmap) err = KErrNoMemory; + } + if (!err && !iPageCache) + { + iPageCache = MEM::Alloc(KPageSize); + if (!iPageCache) err = KErrNoMemory; + } + + if (err) + { + delete iInfo; + iInfo = NULL; + MEM::Free(iTempSlabBitmap); + iTempSlabBitmap = NULL; + MEM::Free(iPageCache); + iPageCache = NULL; + } + KERN_LEAVE_CS(); + return err; + } + +TInt RAllocatorHelper::ReadWord(TLinAddr aLocation, TUint32& aResult) const + { + // Check if we can satisfy the read from the cache + if (aLocation >= iPageCacheAddr) + { + TUint offset = aLocation - iPageCacheAddr; + if (offset < KPageSize) + { + aResult = ((TUint32*)iPageCache)[offset >> 2]; + return KErrNone; + } + } + + // If we reach here, not in page cache. Try and read in the new page + if (iPageCache) + { + TLinAddr pageAddr = aLocation & ~(KPageSize-1); + TInt err = ReadData(pageAddr, iPageCache, KPageSize); + if (!err) + { + iPageCacheAddr = pageAddr; + aResult = ((TUint32*)iPageCache)[(aLocation - iPageCacheAddr) >> 2]; + return KErrNone; + } + } + + // All else fails, try just reading it uncached + return ReadData(aLocation, &aResult, sizeof(TUint32)); + } + +TInt RAllocatorHelper::ReadByte(TLinAddr aLocation, TUint8& aResult) const + { + // Like ReadWord but 8-bit + + // Check if we can satisfy the read from the cache + if (aLocation >= iPageCacheAddr) + { + TUint offset = aLocation - iPageCacheAddr; + if (offset < KPageSize) + { + aResult = ((TUint8*)iPageCache)[offset]; + return KErrNone; + } + } + + // If we reach here, not in page cache. Try and read in the new page + if (iPageCache) + { + TLinAddr pageAddr = aLocation & ~(KPageSize-1); + TInt err = ReadData(pageAddr, iPageCache, KPageSize); + if (!err) + { + iPageCacheAddr = pageAddr; + aResult = ((TUint8*)iPageCache)[(aLocation - iPageCacheAddr)]; + return KErrNone; + } + } + + // All else fails, try just reading it uncached + return ReadData(aLocation, &aResult, sizeof(TUint8)); + } + + +TInt RAllocatorHelper::WriteWord(TLinAddr aLocation, TUint32 aWord) + { + // Invalidate the page cache if necessary + if (aLocation >= iPageCacheAddr && aLocation - iPageCacheAddr < KPageSize) + { + iPageCacheAddr = 0; + } + + return WriteData(aLocation, &aWord, sizeof(TUint32)); + } + +TInt RAllocatorHelper::ReadData(TLinAddr aLocation, TAny* aResult, TInt aSize) const + { + // RAllocatorHelper base class impl is for allocators in same address space, so just copy it + memcpy(aResult, (const TAny*)aLocation, aSize); + return KErrNone; + } + +TInt RAllocatorHelper::WriteData(TLinAddr aLocation, const TAny* aData, TInt aSize) + { + memcpy((TAny*)aLocation, aData, aSize); + return KErrNone; + } + +#ifdef __KERNEL_MODE__ + +LtkUtils::RKernelSideAllocatorHelper::RKernelSideAllocatorHelper() + : iThread(NULL) + {} + +void LtkUtils::RKernelSideAllocatorHelper::Close() + { + NKern::ThreadEnterCS(); + if (iThread) + { + iThread->Close(NULL); + } + iThread = NULL; + RAllocatorHelper::Close(); + NKern::ThreadLeaveCS(); + } + +TInt LtkUtils::RKernelSideAllocatorHelper::ReadData(TLinAddr aLocation, TAny* aResult, TInt aSize) const + { + return Kern::ThreadRawRead(iThread, (const TAny*)aLocation, aResult, aSize); + } + +TInt LtkUtils::RKernelSideAllocatorHelper::WriteData(TLinAddr aLocation, const TAny* aData, TInt aSize) + { + return Kern::ThreadRawWrite(iThread, (TAny*)aLocation, aData, aSize); + } + +TInt LtkUtils::RKernelSideAllocatorHelper::TryLock() + { + return KErrNotSupported; + } + +void LtkUtils::RKernelSideAllocatorHelper::TryUnlock() + { + // Not supported + } + +TInt LtkUtils::RKernelSideAllocatorHelper::OpenUserHeap(TUint aThreadId, TLinAddr aAllocatorAddress, TBool aEuserIsUdeb) + { + NKern::ThreadEnterCS(); + DObjectCon* threads = Kern::Containers()[EThread]; + threads->Wait(); + iThread = Kern::ThreadFromId(aThreadId); + if (iThread && iThread->Open() != KErrNone) + { + // Failed to open + iThread = NULL; + } + threads->Signal(); + NKern::ThreadLeaveCS(); + if (!iThread) return KErrNotFound; + iAllocatorAddress = aAllocatorAddress; + TInt err = IdentifyAllocatorType(aEuserIsUdeb); + if (err) Close(); + return err; + } + +#endif // __KERNEL_MODE__ + +TInt RAllocatorHelper::OpenChunkHeap(TLinAddr aChunkBase, TInt aChunkMaxSize) + { + iAllocatorAddress = aChunkBase; +#ifdef __KERNEL_MODE__ + // Must be in CS + // Assumes that this only ever gets called for the kernel heap. Otherwise goes through RKernelSideAllocatorHelper::OpenUserHeap. + TInt udeb = EFalse; // We can't figure this out until after we've got the heap +#else + // Assumes the chunk isn't the kernel heap. It's not a good idea to try messing with the kernel heap from user side... + TInt udeb = EuserIsUdeb(); + if (udeb < 0) return udeb; // error +#endif + + TInt err = IdentifyAllocatorType(udeb); + if (err == KErrNone && iAllocatorType == EAllocator) + { + // We've no reason to assume it's an allocator because we don't know the iAllocatorAddress actually is an RAllocator* + err = KErrNotFound; + } + if (err) + { + TInt oldErr = err; + TAllocatorType oldType = iAllocatorType; + // Try middle of chunk, in case it's an RHybridHeap + iAllocatorAddress += aChunkMaxSize / 2; + err = IdentifyAllocatorType(udeb); + if (err || iAllocatorType == EAllocator) + { + // No better than before + iAllocatorAddress = aChunkBase; + iAllocatorType = oldType; + err = oldErr; + } + } +#ifdef __KERNEL_MODE__ + if (err == KErrNone) + { + // Now we know the allocator, we can figure out the udeb-ness + RAllocator* kernelAllocator = reinterpret_cast(iAllocatorAddress); + kernelAllocator->DebugFunction(RAllocator::ESetFail, (TAny*)9999, (TAny*)0); // Use an invalid fail reason - this should have no effect on the operation of the heap + TInt err = kernelAllocator->DebugFunction(7, NULL, NULL); // 7 is RAllocator::TAllocDebugOp::EGetFail + if (err == 9999) + { + // udeb new + udeb = ETrue; + } + else if (err == KErrNotSupported) + { + // Old heap - fall back to slightly nasty non-thread-safe method + kernelAllocator->DebugFunction(RAllocator::ESetFail, (TAny*)RAllocator::EFailNext, (TAny*)1); + TAny* res = Kern::Alloc(4); + if (res) udeb = ETrue; + Kern::Free(res); + } + else + { + // it's new urel + } + + // Put everything back + kernelAllocator->DebugFunction(RAllocator::ESetFail, (TAny*)RAllocator::ENone, (TAny*)0); + // And update the type now we know the udeb-ness for certain + err = IdentifyAllocatorType(udeb); + } +#endif + return err; + } + + +// The guts of RAllocatorHelper + +enum TWhatToGet + { + ECommitted = 1, + EAllocated = 2, + ECount = 4, + EMaxSize = 8, + EUnusedPages = 16, + ECommittedFreeSpace = 32, + EMinSize = 64, + EHybridStats = 128, + }; + +class RHackAllocator : public RAllocator + { +public: + using RAllocator::iHandles; + using RAllocator::iTotalAllocSize; + using RAllocator::iCellCount; + }; + +class RHackHeap : public RHeap + { +public: + // Careful, only allowed to use things that are still in the new RHeap, and are still in the same place + using RHeap::iMaxLength; + using RHeap::iChunkHandle; + using RHeap::iLock; + using RHeap::iBase; + using RHeap::iAlign; + using RHeap::iTop; + }; + +const TInt KChunkSizeOffset = 30*4; +const TInt KPageMapOffset = 141*4; +//const TInt KDlOnlyOffset = 33*4; +const TInt KMallocStateOffset = 34*4; +const TInt KMallocStateTopSizeOffset = 3*4; +const TInt KMallocStateTopOffset = 5*4; +const TInt KMallocStateSegOffset = 105*4; +const TInt KUserHybridHeapSize = 186*4; +const TInt KSparePageOffset = 167*4; +const TInt KPartialPageOffset = 165*4; +const TInt KFullSlabOffset = 166*4; +const TInt KSlabAllocOffset = 172*4; +const TInt KSlabParentOffset = 1*4; +const TInt KSlabChild1Offset = 2*4; +const TInt KSlabChild2Offset = 3*4; +const TInt KSlabPayloadOffset = 4*4; +const TInt KSlabsetSize = 4; + +#ifdef TEST_HYBRIDHEAP_ASSERTS +__ASSERT_COMPILE(_FOFF(RHybridHeap, iChunkSize) == KChunkSizeOffset); +__ASSERT_COMPILE(_FOFF(RHybridHeap, iPageMap) == KPageMapOffset); +__ASSERT_COMPILE(_FOFF(RHybridHeap, iGlobalMallocState) == KMallocStateOffset); +__ASSERT_COMPILE(sizeof(malloc_state) == 107*4); +__ASSERT_COMPILE(_FOFF(malloc_state, iTopSize) == KMallocStateTopSizeOffset); +__ASSERT_COMPILE(_FOFF(malloc_state, iTop) == KMallocStateTopOffset); +__ASSERT_COMPILE(_FOFF(malloc_state, iSeg) == KMallocStateSegOffset); +__ASSERT_COMPILE(sizeof(RHybridHeap) == KUserHybridHeapSize); +__ASSERT_COMPILE(_FOFF(RHybridHeap, iSparePage) == KSparePageOffset); +__ASSERT_COMPILE(_FOFF(RHybridHeap, iPartialPage) == KPartialPageOffset); +__ASSERT_COMPILE(_FOFF(RHybridHeap, iSlabAlloc) == KSlabAllocOffset); +__ASSERT_COMPILE(_FOFF(slab, iParent) == KSlabParentOffset); +__ASSERT_COMPILE(_FOFF(slab, iChild1) == KSlabChild1Offset); +__ASSERT_COMPILE(_FOFF(slab, iChild2) == KSlabChild2Offset); +__ASSERT_COMPILE(_FOFF(slab, iPayload) == KSlabPayloadOffset); +__ASSERT_COMPILE(sizeof(slabset) == KSlabsetSize); +#endif + +TInt RAllocatorHelper::TryLock() + { +#ifdef __KERNEL_MODE__ + NKern::ThreadEnterCS(); + DMutex* m = *(DMutex**)(iAllocatorAddress + _FOFF(RHackHeap, iLock)); + if (m) Kern::MutexWait(*m); + return KErrNone; +#else + if (iAllocatorType != EUnknown && iAllocatorType != EAllocator) + { + RFastLock& lock = *reinterpret_cast(iAllocatorAddress + _FOFF(RHackHeap, iLock)); + lock.Wait(); + return KErrNone; + } + return KErrNotSupported; +#endif + } + +void RAllocatorHelper::TryUnlock() + { +#ifdef __KERNEL_MODE__ + DMutex* m = *(DMutex**)(iAllocatorAddress + _FOFF(RHackHeap, iLock)); + if (m) Kern::MutexSignal(*m); + NKern::ThreadLeaveCS(); +#else + if (iAllocatorType != EUnknown && iAllocatorType != EAllocator) + { + RFastLock& lock = *reinterpret_cast(iAllocatorAddress + _FOFF(RHackHeap, iLock)); + lock.Signal(); + } +#endif + } + +HUEXPORT_C void RAllocatorHelper::Close() + { + KERN_ENTER_CS(); + iAllocatorType = EUnknown; + iAllocatorAddress = 0; + delete iInfo; + iInfo = NULL; + iValidInfo = 0; + MEM::Free(iTempSlabBitmap); + iTempSlabBitmap = NULL; + MEM::Free(iPageCache); + iPageCache = NULL; + iPageCacheAddr = 0; + KERN_LEAVE_CS(); + } + +TInt RAllocatorHelper::IdentifyAllocatorType(TBool aAllocatorIsUdeb) + { + iAllocatorType = EUnknown; + + TUint32 handlesPtr = 0; + TInt err = ReadWord(iAllocatorAddress + _FOFF(RHackAllocator, iHandles), handlesPtr); + + if (err) return err; + if (handlesPtr == iAllocatorAddress + _FOFF(RHackHeap, iChunkHandle) || handlesPtr == iAllocatorAddress + _FOFF(RHackHeap, iLock)) + { + // It's an RHeap of some kind - I doubt any other RAllocator subclass will use iHandles in this way + TUint32 base = 0; + err = ReadWord(iAllocatorAddress + _FOFF(RHackHeap, iBase), base); + if (err) return err; + TInt objsize = (TInt)base - (TInt)iAllocatorAddress; + if (objsize <= 32*4) + { + // Old RHeap + iAllocatorType = aAllocatorIsUdeb ? EUdebOldRHeap : EUrelOldRHeap; + } + else + { + // new hybrid heap - bigger than the old one. Likewise figure out if udeb or urel. + iAllocatorType = aAllocatorIsUdeb ? EUdebHybridHeap : EUrelHybridHeap; + } + } + else + { + iAllocatorType = EAllocator; + } + return KErrNone; + } + +HUEXPORT_C TInt RAllocatorHelper::SetCellNestingLevel(TAny* aCell, TInt aNestingLevel) + { + TInt err = KErrNone; + + switch (iAllocatorType) + { + case EUdebOldRHeap: + case EUdebHybridHeap: + // By this reckoning, they're in the same place amazingly + { + TLinAddr nestingAddr = (TLinAddr)aCell - 8; + err = WriteWord(nestingAddr, aNestingLevel); + break; + } + default: + break; + } + return err; + } + +HUEXPORT_C TInt RAllocatorHelper::GetCellNestingLevel(TAny* aCell, TInt& aNestingLevel) + { + switch (iAllocatorType) + { + case EUdebOldRHeap: + case EUdebHybridHeap: + // By this reckoning, they're in the same place amazingly + { + TLinAddr nestingAddr = (TLinAddr)aCell - 8; + return ReadWord(nestingAddr, (TUint32&)aNestingLevel); + } + default: + return KErrNotSupported; + } + } + +TInt RAllocatorHelper::RefreshDetails(TUint aMask) + { + TInt err = FinishConstruction(); + if (err) return err; + + // Invalidate the page cache + iPageCacheAddr = 0; + + TryLock(); + err = DoRefreshDetails(aMask); + TryUnlock(); + return err; + } + +const TInt KHeapWalkStatsForOldHeap = (EUnusedPages|ECommittedFreeSpace); +const TInt KHeapWalkStatsForNewHeap = (EAllocated|ECount|EUnusedPages|ECommittedFreeSpace|EHybridStats); + +TInt RAllocatorHelper::DoRefreshDetails(TUint aMask) + { + TInt err = KErrNotSupported; + switch (iAllocatorType) + { + case EUrelOldRHeap: + case EUdebOldRHeap: + { + if (aMask & ECommitted) + { + // The old RHeap::Size() used to use iTop - iBase, which was effectively chunkSize - sizeof(RHeap) + // I think that for CommittedSize we should include the size of the heap object, just as it includes + // the size of heap cell metadata and overhead. Plus it makes sure the committedsize is a multiple of the page size + TUint32 top = 0; + //TUint32 base = 0; + //err = ReadWord(iAllocatorAddress + _FOFF(RHackHeap, iBase), base); + //if (err) return err; + err = ReadWord(iAllocatorAddress + _FOFF(RHackHeap, iTop), top); + if (err) return err; + + //iInfo->iCommittedSize = top - base; + iInfo->iCommittedSize = top - iAllocatorAddress; + iValidInfo |= ECommitted; + } + if (aMask & EAllocated) + { + TUint32 allocSize = 0; + err = ReadWord(iAllocatorAddress + _FOFF(RHackAllocator, iTotalAllocSize), allocSize); + if (err) return err; + iInfo->iAllocatedSize = allocSize; + iValidInfo |= EAllocated; + } + if (aMask & ECount) + { + TUint32 count = 0; + err = ReadWord(iAllocatorAddress + _FOFF(RHackAllocator, iCellCount), count); + if (err) return err; + iInfo->iAllocationCount = count; + iValidInfo |= ECount; + } + if (aMask & EMaxSize) + { + TUint32 maxlen = 0; + err = ReadWord(iAllocatorAddress + _FOFF(RHackHeap, iMaxLength), maxlen); + if (err) return err; + iInfo->iMaxCommittedSize = maxlen; + iValidInfo |= EMaxSize; + } + if (aMask & EMinSize) + { + TUint32 minlen = 0; + err = ReadWord(iAllocatorAddress + _FOFF(RHackHeap, iMaxLength) - 4, minlen); // This isn't a typo! iMinLength is 4 bytes before iMaxLength, on old heap ONLY + if (err) return err; + iInfo->iMinCommittedSize = minlen; + iValidInfo |= EMinSize; + } + if (aMask & KHeapWalkStatsForOldHeap) + { + // Need a heap walk + iInfo->ClearStats(); + iValidInfo = 0; + err = DoWalk(&WalkForStats, NULL); + if (err == KErrNone) iValidInfo |= KHeapWalkStatsForOldHeap; + } + return err; + } + case EUrelHybridHeap: + case EUdebHybridHeap: + { + TBool needWalk = EFalse; + if (aMask & ECommitted) + { + // RAllocator::Size uses iChunkSize - sizeof(RHybridHeap); + // We can't do exactly the same, because we can't calculate sizeof(RHybridHeap), only ROUND_UP(sizeof(RHybridHeap), iAlign) + // And if fact we don't bother and just use iChunkSize + TUint32 chunkSize = 0; + err = ReadWord(iAllocatorAddress + KChunkSizeOffset, chunkSize); + if (err) return err; + //TUint32 baseAddr = 0; + //err = ReadWord(iAllocatorAddress + _FOFF(RHackHeap, iBase), baseAddr); + //if (err) return err; + iInfo->iCommittedSize = chunkSize; // - (baseAddr - iAllocatorAddress); + iValidInfo |= ECommitted; + } + if (aMask & (EAllocated|ECount)) + { + if (iAllocatorType == EUdebHybridHeap) + { + // Easy, just get them from the counter + TUint32 totalAlloc = 0; + err = ReadWord(iAllocatorAddress + _FOFF(RHackAllocator, iTotalAllocSize), totalAlloc); + if (err) return err; + iInfo->iAllocatedSize = totalAlloc; + iValidInfo |= EAllocated; + + TUint32 cellCount = 0; + err = ReadWord(iAllocatorAddress + _FOFF(RHackAllocator, iCellCount), cellCount); + if (err) return err; + iInfo->iAllocationCount = cellCount; + iValidInfo |= ECount; + } + else + { + // A heap walk is needed + needWalk = ETrue; + } + } + if (aMask & EMaxSize) + { + TUint32 maxlen = 0; + err = ReadWord(iAllocatorAddress + _FOFF(RHackHeap, iMaxLength), maxlen); + if (err) return err; + iInfo->iMaxCommittedSize = maxlen; + iValidInfo |= EMaxSize; + } + if (aMask & EMinSize) + { + TUint32 minlen = 0; + err = ReadWord(iAllocatorAddress + _FOFF(RHackHeap, iAlign) + 4*4, minlen); // iMinLength is in different place to old RHeap + if (err) return err; + iInfo->iMinCommittedSize = minlen; + iValidInfo |= EMinSize; + } + if (aMask & (EUnusedPages|ECommittedFreeSpace|EHybridStats)) + { + // EAllocated and ECount have already been taken care of above + needWalk = ETrue; + } + + if (needWalk) + { + iInfo->ClearStats(); + iValidInfo = 0; + err = DoWalk(&WalkForStats, NULL); + if (err == KErrNone) iValidInfo |= KHeapWalkStatsForNewHeap; + } + return err; + } + default: + return KErrNotSupported; + } + } + +TInt RAllocatorHelper::CheckValid(TUint aMask) + { + if ((iValidInfo & aMask) == aMask) + { + return KErrNone; + } + else + { + return RefreshDetails(aMask); + } + } + +HUEXPORT_C TInt RAllocatorHelper::CommittedSize() + { + TInt err = CheckValid(ECommitted); + if (err) return err; + return iInfo->iCommittedSize; + } + +HUEXPORT_C TInt RAllocatorHelper::AllocatedSize() + { + TInt err = CheckValid(EAllocated); + if (err) return err; + return iInfo->iAllocatedSize; + } + +HUEXPORT_C TInt RAllocatorHelper::AllocationCount() + { + TInt err = CheckValid(ECount); + if (err) return err; + return iInfo->iAllocationCount; + } + +HUEXPORT_C TInt RAllocatorHelper::RefreshDetails() + { + return RefreshDetails(iValidInfo); + } + +HUEXPORT_C TInt RAllocatorHelper::MaxCommittedSize() + { + TInt err = CheckValid(EMaxSize); + if (err) return err; + return iInfo->iMaxCommittedSize; + } + +HUEXPORT_C TInt RAllocatorHelper::MinCommittedSize() + { + TInt err = CheckValid(EMinSize); + if (err) return err; + return iInfo->iMinCommittedSize; + } + +HUEXPORT_C TInt RAllocatorHelper::AllocCountForCell(TAny* aCell) const + { + TUint32 allocCount = 0; + switch (iAllocatorType) + { + case EUdebOldRHeap: + case EUdebHybridHeap: // Both are in the same place, amazingly + { + TLinAddr allocCountAddr = (TLinAddr)aCell - 4; + TInt err = ReadWord(allocCountAddr, allocCount); + if (err) return err; + return (TInt)allocCount; + } + default: + return KErrNotSupported; + } + } + +struct SContext3 + { + RAllocatorHelper::TWalkFunc3 iOrigWalkFn; + TAny* iOrigContext; + }; + +TBool RAllocatorHelper::DispatchClientWalkCallback(RAllocatorHelper& aHelper, TAny* aContext, RAllocatorHelper::TExtendedCellType aCellType, TLinAddr aCellPtr, TInt aCellLength) + { + WalkForStats(aHelper, NULL, aCellType, aCellPtr, aCellLength); + SContext3* context = static_cast(aContext); + return (*context->iOrigWalkFn)(aHelper, context->iOrigContext, aCellType, aCellPtr, aCellLength); + } + +HUEXPORT_C TInt RAllocatorHelper::Walk(TWalkFunc3 aCallbackFn, TAny* aContext) + { + // Might as well take the opportunity of updating our stats at the same time as walking the heap for the client + SContext3 context = { aCallbackFn, aContext }; + + TInt err = FinishConstruction(); // In case this hasn't been done yet + if (err) return err; + + TryLock(); + err = DoWalk(&DispatchClientWalkCallback, &context); + TryUnlock(); + return err; + } + +TInt RAllocatorHelper::DoWalk(TWalkFunc3 aCallbackFn, TAny* aContext) + { + TInt err = KErrNotSupported; + switch (iAllocatorType) + { + case EUdebOldRHeap: + case EUrelOldRHeap: + err = OldSkoolWalk(aCallbackFn, aContext); + break; + case EUrelHybridHeap: + case EUdebHybridHeap: + err = NewHotnessWalk(aCallbackFn, aContext); + break; + default: + err = KErrNotSupported; + break; + } + return err; + } + +struct SContext + { + RAllocatorHelper::TWalkFunc iOrigWalkFn; + TAny* iOrigContext; + }; + +struct SContext2 + { + RAllocatorHelper::TWalkFunc2 iOrigWalkFn; + TAny* iOrigContext; + }; + +#define New2Old(aNew) (((aNew)&RAllocatorHelper::EAllocationMask) ? RAllocatorHelper::EAllocation : ((aNew)&RAllocatorHelper::EFreeMask) ? RAllocatorHelper::EFreeSpace : RAllocatorHelper::EBadness) + +TBool DispatchOldTWalkFuncCallback(RAllocatorHelper& /*aHelper*/, TAny* aContext, RAllocatorHelper::TExtendedCellType aCellType, TLinAddr aCellPtr, TInt aCellLength) + { + SContext* context = static_cast(aContext); + return (*context->iOrigWalkFn)(context->iOrigContext, New2Old(aCellType), aCellPtr, aCellLength); + } + +TBool DispatchOldTWalk2FuncCallback(RAllocatorHelper& aHelper, TAny* aContext, RAllocatorHelper::TExtendedCellType aCellType, TLinAddr aCellPtr, TInt aCellLength) + { + SContext2* context = static_cast(aContext); + return (*context->iOrigWalkFn)(aHelper, context->iOrigContext, New2Old(aCellType), aCellPtr, aCellLength); + } + +HUEXPORT_C TInt RAllocatorHelper::Walk(TWalkFunc aCallbackFn, TAny* aContext) + { + // For backwards compatability insert a compatability callback to map between the different types of callback that clients requested + SContext context = { aCallbackFn, aContext }; + return Walk(&DispatchOldTWalkFuncCallback, &context); + } + +HUEXPORT_C TInt RAllocatorHelper::Walk(TWalkFunc2 aCallbackFn, TAny* aContext) + { + SContext2 context = { aCallbackFn, aContext }; + return Walk(&DispatchOldTWalk2FuncCallback, &context); + } + + +TInt RAllocatorHelper::OldSkoolWalk(TWalkFunc3 aCallbackFn, TAny* aContext) + { + TLinAddr pC = 0; + TInt err = ReadWord(iAllocatorAddress + _FOFF(RHackHeap, iBase), pC); // pC = iBase; // allocated cells + if (err) return err; + TLinAddr pF = iAllocatorAddress + _FOFF(RHackHeap, iAlign) + 3*4; // pF = &iFree; // free cells + + TLinAddr top = 0; + err = ReadWord(iAllocatorAddress + _FOFF(RHackHeap, iTop), top); + if (err) return err; + const TInt KAllocatedCellHeaderSize = iAllocatorType == EUdebOldRHeap ? 12 : 4; + TInt minCell = 0; + err = ReadWord(iAllocatorAddress + _FOFF(RHackHeap, iAlign) + 4, (TUint32&)minCell); + if (err) return err; + TInt align = 0; + err = ReadWord(iAllocatorAddress + _FOFF(RHackHeap, iAlign), (TUint32&)align); + if (err) return err; + + FOREVER + { + err = ReadWord(pF+4, pF); // pF = pF->next; // next free cell + if (err) return err; + TLinAddr pFnext = 0; + if (pF) err = ReadWord(pF + 4, pFnext); + if (err) return err; + + if (!pF) + { + pF = top; // to make size checking work + } + else if (pF>=top || (pFnext && pFnext<=pF) ) + { + // free cell pointer off the end or going backwards + //Unlock(); + (*aCallbackFn)(*this, aContext, EHeapBadFreeCellAddress, pF, 0); + return KErrCorrupt; + } + else + { + TInt l; // = pF->len + err = ReadWord(pF, (TUint32&)l); + if (err) return err; + if (llen; + err = ReadWord(pC, (TUint32&)l); + if (err) return err; + if (l pF) + { + // cell overlaps next free cell + //Unlock(); + (*aCallbackFn)(*this, aContext, EHeapBadAllocatedCellAddress, pC, l); + return KErrCorrupt; + } + pC = pN; + } + if (pF == top) + break; // reached end of heap + TInt pFlen = 0; + err = ReadWord(pF, (TUint32&)pFlen); + if (err) return err; + pC = pF + pFlen; // pC = __NEXT_CELL(pF); // step to next allocated cell + TBool shouldContinue = (*aCallbackFn)(*this, aContext, EHeapFreeCell, pF, pFlen); + if (!shouldContinue) return KErrNone; + } + return KErrNone; + } + +HUEXPORT_C TInt RAllocatorHelper::CountUnusedPages() + { + TInt err = CheckValid(EUnusedPages); + if (err) return err; + return iInfo->iUnusedPages; + } + +HUEXPORT_C TInt RAllocatorHelper::CommittedFreeSpace() + { + TInt err = CheckValid(ECommittedFreeSpace); + if (err) return err; + return iInfo->iCommittedFreeSpace; + } + +#define ROUND_DOWN(val, pow2) ((val) & ~((pow2)-1)) +#define ROUND_UP(val, pow2) ROUND_DOWN((val) + (pow2) - 1, (pow2)) + +HUEXPORT_C TLinAddr RAllocatorHelper::AllocatorAddress() const + { + return iAllocatorAddress; + } + +TBool RAllocatorHelper::WalkForStats(RAllocatorHelper& aSelf, TAny* /*aContext*/, TExtendedCellType aType, TLinAddr aCellPtr, TInt aCellLength) + { + //ASSERT(aCellLength >= 0); + THeapInfo& info = *aSelf.iInfo; + + TInt pagesSpanned = 0; // The number of pages that fit entirely inside the payload of this cell + if ((TUint)aCellLength > KPageSize) + { + TLinAddr nextPageAlignedAddr = ROUND_UP(aCellPtr, KPageSize); + pagesSpanned = ROUND_DOWN(aCellPtr + aCellLength - nextPageAlignedAddr, KPageSize) / KPageSize; + } + + if (aSelf.iAllocatorType == EUrelOldRHeap || aSelf.iAllocatorType == EUdebOldRHeap) + { + if (aType & EFreeMask) + { + info.iUnusedPages += pagesSpanned; + info.iCommittedFreeSpace += aCellLength; + info.iHeapFreeCellCount++; + } + } + else + { + if (aType & EAllocationMask) + { + info.iAllocatedSize += aCellLength; + info.iAllocationCount++; + } + else if (aType & EFreeMask) + { + // I *think* that DLA will decommit pages from inside free cells... + TInt committedLen = aCellLength - (pagesSpanned * KPageSize); + info.iCommittedFreeSpace += committedLen; + } + + switch (aType) + { + case EDlaAllocation: + info.iDlaAllocsSize += aCellLength; + info.iDlaAllocsCount++; + break; + case EPageAllocation: + info.iPageAllocsSize += aCellLength; + info.iPageAllocsCount++; + break; + case ESlabAllocation: + info.iSlabAllocsSize += aCellLength; + info.iSlabAllocsCount++; + break; + case EDlaFreeCell: + info.iDlaFreeSize += aCellLength; + info.iDlaFreeCount++; + break; + case ESlabFreeCell: + info.iSlabFreeCellSize += aCellLength; + info.iSlabFreeCellCount++; + break; + case ESlabFreeSlab: + info.iSlabFreeSlabSize += aCellLength; + info.iSlabFreeSlabCount++; + break; + default: + break; + } + } + + return ETrue; + } + +#define PAGESHIFT 12 + +TUint RAllocatorHelper::PageMapOperatorBrackets(unsigned ix, TInt& err) const + { + //return 1U&(iBase[ix>>3] >> (ix&7)); + TUint32 basePtr = 0; + err = ReadWord(iAllocatorAddress + KPageMapOffset, basePtr); + if (err) return 0; + + TUint8 res = 0; + err = ReadByte(basePtr + (ix >> 3), res); + if (err) return 0; + + return 1U&(res >> (ix&7)); + } + + +TInt RAllocatorHelper::PageMapFind(TUint start, TUint bit, TInt& err) + { + TUint32 iNbits = 0; + err = ReadWord(iAllocatorAddress + KPageMapOffset + 4, iNbits); + if (err) return 0; + + if (start>= 1; + if (bits == 0) + return 1; + bits = PageMapBits(pos+2,2,err); + if (err) return 0; + if ((bits & 1) == 0) + return 2 + (bits>>1); + else if ((bits>>1) == 0) + { + return PageMapBits(pos+4, 4,err); + } + else + { + return PageMapBits(pos+4, 18,err); + } + } + +TUint RAllocatorHelper::PageMapBits(unsigned ix, unsigned len, TInt& err) + { + int l=len; + unsigned val=0; + unsigned bit=0; + while (--l>=0) + { + //val |= (*this)[ix++]<= 0 && err == KErrNone;) + { + int npage = PagedDecode(ix, err); + if (err) return err; + // Introduce paged buffer to the walk function + TLinAddr bfr = membase + (1 << (PAGESHIFT-1))*ix; + int len = npage << PAGESHIFT; + if ( (TUint)len > KPageSize ) + { // If buffer is not larger than one page it must be a slab page mapped into bitmap + if (iAllocatorType == EUdebHybridHeap) + { + bfr += 8; + len -= 8; + } + shouldContinue = (*aCallbackFn)(*this, aContext, EPageAllocation, bfr, len); + if (!shouldContinue) return KErrNone; + } + ix += (npage<<1); + } + if (err) return err; + + // Slab + TUint32 sparePage = 0; + err = ReadWord(iAllocatorAddress + KSparePageOffset, sparePage); + if (err) return err; + if (sparePage) + { + //Walk(wi, iSparePage, iPageSize, EGoodFreeCell, ESlabSpare); // Introduce Slab spare page to the walk function + // This counts as 4 spare slabs + for (TInt i = 0; i < 4; i++) + { + shouldContinue = (*aCallbackFn)(*this, aContext, ESlabFreeSlab, sparePage + SLABSIZE*i, SLABSIZE); + if (!shouldContinue) return KErrNone; + } + } + + //TreeWalk(&iFullSlab, &SlabFullInfo, i, wi); + TInt err = TreeWalk(iAllocatorAddress + KFullSlabOffset, ESlabFullInfo, aCallbackFn, aContext, shouldContinue); + if (err || !shouldContinue) return err; + for (int ix = 0; ix < (MAXSLABSIZE>>2); ++ix) + { + TUint32 partialAddr = iAllocatorAddress + KSlabAllocOffset + ix*KSlabsetSize; + //TreeWalk(&iSlabAlloc[ix].iPartial, &SlabPartialInfo, i, wi); + err = TreeWalk(partialAddr, ESlabPartialInfo, aCallbackFn, aContext, shouldContinue); + if (err || !shouldContinue) return err; + } + //TreeWalk(&iPartialPage, &SlabEmptyInfo, i, wi); + TreeWalk(iAllocatorAddress + KPartialPageOffset, ESlabEmptyInfo, aCallbackFn, aContext, shouldContinue); + } + + // DLA +#define CHUNK_OVERHEAD (sizeof(TUint)) +#define CHUNK_ALIGN_MASK (7) +#define CHUNK2MEM(p) ((TLinAddr)(p) + 8) +#define MEM2CHUNK(mem) ((TLinAddr)(p) - 8) +/* chunk associated with aligned address A */ +#define ALIGN_OFFSET(A)\ + ((((TLinAddr)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\ + ((8 - ((TLinAddr)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK)) +#define ALIGN_AS_CHUNK(A) ((A) + ALIGN_OFFSET(CHUNK2MEM(A))) +#define CINUSE_BIT 2 +#define INUSE_BITS 3 + + TUint32 topSize = 0; + err = ReadWord(iAllocatorAddress + KMallocStateOffset + KMallocStateTopSizeOffset, topSize); + if (err) return err; + + TUint32 top = 0; + err = ReadWord(iAllocatorAddress + KMallocStateOffset + KMallocStateTopOffset, top); + if (err) return err; + + TInt max = ((topSize-1) & ~CHUNK_ALIGN_MASK) - CHUNK_OVERHEAD; + if ( max < 0 ) + max = 0; + + TBool shouldContinue = (*aCallbackFn)(*this, aContext, EDlaFreeCell, top, max); + if (!shouldContinue) return KErrNone; + + TUint32 mallocStateSegBase = 0; + err = ReadWord(iAllocatorAddress + KMallocStateOffset + KMallocStateSegOffset, mallocStateSegBase); + if (err) return err; + + for (TLinAddr q = ALIGN_AS_CHUNK(mallocStateSegBase); q != top; /*q = NEXT_CHUNK(q)*/) + { + TUint32 qhead = 0; + err = ReadWord(q + 4, qhead); + if (err) return err; + //TInt sz = CHUNKSIZE(q); + TInt sz = qhead & ~(INUSE_BITS); + if (!(qhead & CINUSE_BIT)) + { + //Walk(wi, CHUNK2MEM(q), sz, EGoodFreeCell, EDougLeaAllocator); // Introduce DL free buffer to the walk function + shouldContinue = (*aCallbackFn)(*this, aContext, EDlaFreeCell, CHUNK2MEM(q), sz); + if (!shouldContinue) return KErrNone; + } + else + { + //Walk(wi, CHUNK2MEM(q), (sz- CHUNK_OVERHEAD), EGoodAllocatedCell, EDougLeaAllocator); // Introduce DL allocated buffer to the walk function + TLinAddr addr = CHUNK2MEM(q); + TInt size = sz - CHUNK_OVERHEAD; + if (iAllocatorType == EUdebHybridHeap) + { + size -= 8; + addr += 8; + } + shouldContinue = (*aCallbackFn)(*this, aContext, EDlaAllocation, addr, size); + if (!shouldContinue) return KErrNone; + } + // This is q = NEXT_CHUNK(q) expanded + q = q + sz; + } + return KErrNone; + } + +TInt RAllocatorHelper::TreeWalk(TUint32 aSlabRoot, TInt aSlabType, TWalkFunc3 aCallbackFn, TAny* aContext, TBool& shouldContinue) + { + const TSlabType type = (TSlabType)aSlabType; + + TUint32 s = 0; + TInt err = ReadWord(aSlabRoot, s); + if (err) return err; + //slab* s = *root; + if (!s) + return KErrNone; + + for (;;) + { + //slab* c; + //while ((c = s->iChild1) != 0) + // s = c; // walk down left side to end + TUint32 c; + for(;;) + { + err = ReadWord(s + KSlabChild1Offset, c); + if (err) return err; + if (c == 0) break; + else s = c; + } + for (;;) + { + //TODOf(s, i, wi); + //TODO __HEAP_CORRUPTED_TEST_STATIC + TUint32 h; + err = ReadWord(s, h); // = aSlab->iHeader; + if (err) return err; + TUint32 size = (h&0x0003f000)>>12; //SlabHeaderSize(h); + TUint debugheadersize = 0; + if (iAllocatorType == EUdebHybridHeap) debugheadersize = 8; + TUint32 usedCount = (((h&0x0ffc0000)>>18) + 4) / size; // (SlabHeaderUsedm4(h) + 4) / size; + switch (type) + { + case ESlabFullInfo: + { + TUint32 count = usedCount; + TUint32 i = 0; + while ( i < count ) + { + TUint32 addr = s + KSlabPayloadOffset + i*size; //&aSlab->iPayload[i*size]; + shouldContinue = (*aCallbackFn)(*this, aContext, ESlabAllocation, addr + debugheadersize, size - debugheadersize); + if (!shouldContinue) return KErrNone; + i++; + } + break; + } + case ESlabPartialInfo: + { + //TODO __HEAP_CORRUPTED_TEST_STATIC + TUint32 count = KMaxSlabPayload / size; + TUint32 freeOffset = (h & 0xff) << 2; + if (freeOffset == 0) + { + // TODO Shouldn't happen for a slab on the partial list + } + memset(iTempSlabBitmap, 1, KTempBitmapSize); // Everything defaults to in use + TUint wildernessCount = count - usedCount; + while (freeOffset) + { + wildernessCount--; + TInt idx = (freeOffset-KSlabPayloadOffset)/size; + LOG("iTempSlabBitmap freeOffset %d index %d", freeOffset, idx); + iTempSlabBitmap[idx] = 0; // Mark it as free + + TUint32 addr = s + freeOffset; + TUint8 nextCell = 0; + err = ReadByte(addr, nextCell); + if (err) return err; + freeOffset = ((TUint32)nextCell) << 2; + } + memset(iTempSlabBitmap + count - wildernessCount, 0, wildernessCount); // Mark the wilderness as free + for (TInt i = 0; i < count; i++) + { + TLinAddr addr = s + KSlabPayloadOffset + i*size; + if (iTempSlabBitmap[i]) + { + // In use + shouldContinue = (*aCallbackFn)(*this, aContext, ESlabAllocation, addr + debugheadersize, size - debugheadersize); + } + else + { + // Free + shouldContinue = (*aCallbackFn)(*this, aContext, ESlabFreeCell, addr, size); + } + if (!shouldContinue) return KErrNone; + } + break; + } + case ESlabEmptyInfo: + { + // Check which slabs of this page are empty + TUint32 pageAddr = ROUND_DOWN(s, KPageSize); + TUint32 headerForPage = 0; + err = ReadWord(pageAddr, headerForPage); + if (err) return err; + TUint32 slabHeaderPageMap = (headerForPage & 0x00000f00)>>8; // SlabHeaderPagemap(unsigned h) + for (TInt slabIdx = 0; slabIdx < 4; slabIdx++) + { + if (slabHeaderPageMap & (1<iPayload[i*size]; + shouldContinue = (*aCallbackFn)(*this, aContext, ESlabFreeSlab, addr, KMaxSlabPayload); + if (!shouldContinue) return KErrNone; + } + } + break; + } + } + + //c = s->iChild2; + err = ReadWord(s + KSlabChild2Offset, c); + if (err) return err; + + if (c) + { // one step down right side, now try and walk down left + s = c; + break; + } + for (;;) + { // loop to walk up right side + TUint32 pp = 0; + err = ReadWord(s + KSlabParentOffset, pp); + if (err) return err; + //slab** pp = s->iParent; + if (pp == aSlabRoot) + return KErrNone; +#define SlabFor(x) ROUND_DOWN(x, SLABSIZE) + s = SlabFor(pp); + //if (pp == &s->iChild1) + if (pp == s + KSlabChild1Offset) + break; + } + } + } + } + +HUEXPORT_C TInt RAllocatorHelper::SizeForCellType(TExtendedCellType aType) + { + if (aType & EBadnessMask) return KErrArgument; + if (aType == EAllocationMask) return AllocatedSize(); + + if (iAllocatorType == EUdebOldRHeap || iAllocatorType == EUrelOldRHeap) + { + switch (aType) + { + case EHeapAllocation: + return AllocatedSize(); + case EHeapFreeCell: + case EFreeMask: + return CommittedFreeSpace(); + default: + return KErrNotSupported; + } + } + else if (iAllocatorType == EUrelHybridHeap || iAllocatorType == EUdebHybridHeap) + { + TInt err = CheckValid(EHybridStats); + if (err) return err; + + switch (aType) + { + case EHeapAllocation: + case EHeapFreeCell: + return KErrNotSupported; + case EDlaAllocation: + return iInfo->iDlaAllocsSize; + case EPageAllocation: + return iInfo->iPageAllocsSize; + case ESlabAllocation: + return iInfo->iSlabAllocsSize; + case EDlaFreeCell: + return iInfo->iDlaFreeSize; + case ESlabFreeCell: + return iInfo->iSlabFreeCellSize; + case ESlabFreeSlab: + return iInfo->iSlabFreeSlabSize; + case EFreeMask: + // Note this isn't the same as asking for CommittedFreeSpace(). SizeForCellType(EFreeMask) may include decommitted pages that lie inside a free cell + return iInfo->iDlaFreeSize + iInfo->iSlabFreeCellSize + iInfo->iSlabFreeSlabSize; + default: + return KErrNotSupported; + } + } + else + { + return KErrNotSupported; + } + } + +HUEXPORT_C TInt RAllocatorHelper::CountForCellType(TExtendedCellType aType) + { + if (aType & EBadnessMask) return KErrArgument; + if (aType == EAllocationMask) return AllocationCount(); + + if (iAllocatorType == EUdebOldRHeap || iAllocatorType == EUrelOldRHeap) + { + switch (aType) + { + case EHeapAllocation: + return AllocationCount(); + case EHeapFreeCell: + case EFreeMask: + { + TInt err = CheckValid(ECommittedFreeSpace); + if (err) return err; + return iInfo->iHeapFreeCellCount; + } + default: + return KErrNotSupported; + } + } + else if (iAllocatorType == EUrelHybridHeap || iAllocatorType == EUdebHybridHeap) + { + TInt err = CheckValid(EHybridStats); + if (err) return err; + + switch (aType) + { + case EHeapAllocation: + case EHeapFreeCell: + return KErrNotSupported; + case EDlaAllocation: + return iInfo->iDlaAllocsCount; + case EPageAllocation: + return iInfo->iPageAllocsCount; + case ESlabAllocation: + return iInfo->iSlabAllocsCount; + case EDlaFreeCell: + return iInfo->iDlaFreeCount; + case ESlabFreeCell: + return iInfo->iSlabFreeCellCount; + case ESlabFreeSlab: + return iInfo->iSlabFreeSlabCount; + case EFreeMask: + // This isn't a hugely meaningful value, but if that's what they asked for... + return iInfo->iDlaFreeCount + iInfo->iSlabFreeCellCount + iInfo->iSlabFreeSlabCount; + default: + return KErrNotSupported; + } + } + else + { + return KErrNotSupported; + } + } + +HUEXPORT_C TBool LtkUtils::RAllocatorHelper::AllocatorIsUdeb() const + { + return iAllocatorType == EUdebOldRHeap || iAllocatorType == EUdebHybridHeap; + } + + +HUEXPORT_C const TDesC& LtkUtils::RAllocatorHelper::Description() const + { + _LIT(KRHeap, "RHeap"); + _LIT(KRHybridHeap, "RHybridHeap"); + _LIT(KUnknown, "Unknown"); + switch (iAllocatorType) + { + case EUrelOldRHeap: + case EUdebOldRHeap: + return KRHeap; + case EUrelHybridHeap: + case EUdebHybridHeap: + return KRHybridHeap; + case EAllocator: + case EUnknown: + default: + return KUnknown; + } + } + +#ifdef __KERNEL_MODE__ + +DChunk* LtkUtils::RAllocatorHelper::OpenUnderlyingChunk() + { + // Enter and leave in CS and with no locks held. On exit the returned DChunk has been Open()ed. + TInt err = iChunk->Open(); + if (err) return NULL; + return iChunk; + } + +DChunk* LtkUtils::RKernelSideAllocatorHelper::OpenUnderlyingChunk() + { + if (iAllocatorType != EUrelOldRHeap && iAllocatorType != EUdebOldRHeap && iAllocatorType != EUrelHybridHeap && iAllocatorType != EUdebHybridHeap) return NULL; + // Note RKernelSideAllocatorHelper doesn't use or access RAllocatorHelper::iChunk, because we figure out the chunk handle in a different way. + // It is for this reason that iChunk is private, to remove temptation + + // Enter and leave in CS and with no locks held. On exit the returned DChunk has been Open()ed. + TUint32 chunkHandle = 0; + TInt err = ReadData(iAllocatorAddress + _FOFF(RHackHeap, iChunkHandle), &chunkHandle, sizeof(TUint32)); + if (err) return NULL; + + NKern::LockSystem(); + DChunk* result = (DChunk*)Kern::ObjectFromHandle(iThread, chunkHandle, EChunk); + if (result && result->Open() != KErrNone) + { + result = NULL; + } + NKern::UnlockSystem(); + return result; + } + +LtkUtils::RAllocatorHelper::TType LtkUtils::RAllocatorHelper::GetType() const + { + switch (iAllocatorType) + { + case EUrelOldRHeap: + case EUdebOldRHeap: + return ETypeRHeap; + case EUrelHybridHeap: + case EUdebHybridHeap: + return ETypeRHybridHeap; + case EAllocator: + case EUnknown: + default: + return ETypeUnknown; + } + } + +#else + +TInt LtkUtils::RAllocatorHelper::EuserIsUdeb() + { + TAny* buf = User::Alloc(4096); + if (!buf) return KErrNoMemory; + RAllocator* dummyHeap = UserHeap::FixedHeap(buf, 4096, 4, ETrue); + if (!dummyHeap) return KErrNoMemory; // Don't think this can happen + + dummyHeap->__DbgSetAllocFail(RAllocator::EFailNext, 1); + TAny* ptr = dummyHeap->Alloc(4); + // Because we specified singleThreaded=ETrue we can allow dummyHeap to just go out of scope here + User::Free(buf); + + if (ptr) + { + // Clearly the __DbgSetAllocFail had no effect so we must be urel + // We don't need to free ptr because it came from the dummy heap + return EFalse; + } + else + { + return ETrue; + } + } + +#ifndef STANDALONE_ALLOCHELPER + +#include +HUEXPORT_C void LtkUtils::MakeHeapCellInvisible(TAny* aCell) + { + RAllocatorHelper helper; + TInt err = helper.Open(&User::Allocator()); + if (err == KErrNone) + { + helper.SetCellNestingLevel(aCell, -1); + helper.Close(); + } + } +#endif // STANDALONE_ALLOCHELPER + +#endif