--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/perfsrv/memspy/Driver/Shared/heaputils.cpp Fri Sep 17 08:38:31 2010 +0300
@@ -0,0 +1,1684 @@
+// 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 <e32def.h>
+#include "slab.h"
+#include "page_alloc.h"
+#include "heap_hybrid.h"
+#endif
+
+#include "heaputils.h"
+
+#ifdef __KERNEL_MODE__
+
+#include <kern_priv.h>
+#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 <e32std.h>
+#define MEM User
+#define KERN_ENTER_CS()
+#define KERN_LEAVE_CS()
+//#include <e32debug.h>
+//#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; i<chunkCount; i++)
+ {
+ DChunk* chunk = (DChunk*)(*chunkContainer)[i];
+ if (chunk->NameBuf() && 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
+ TBool isTheKernelHeap = ETrue;
+#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
+ TBool isTheKernelHeap = EFalse;
+#endif
+
+ TInt err = IdentifyAllocatorType(udeb, isTheKernelHeap);
+ 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 && aChunkMaxSize > 0)
+ {
+ TInt oldErr = err;
+ TAllocatorType oldType = iAllocatorType;
+ // Try middle of chunk, in case it's an RHybridHeap
+ iAllocatorAddress += aChunkMaxSize / 2;
+ err = IdentifyAllocatorType(udeb, isTheKernelHeap);
+ 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<RAllocator*>(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 hybrid heap
+ 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, isTheKernelHeap);
+ }
+#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<RFastLock*>(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<RFastLock*>(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, TBool aIsTheKernelHeap)
+ {
+ iAllocatorType = EUnknown;
+
+ TUint32 handlesPtr = 0;
+ TInt err = ReadWord(iAllocatorAddress + _FOFF(RHackAllocator, iHandles), handlesPtr);
+
+ if (err) return err;
+ if (aIsTheKernelHeap ||
+ 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<SContext3*>(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<SContext*>(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<SContext2*>(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 (l<minCell || (l & (align-1)))
+ {
+ // free cell length invalid
+ //Unlock();
+ (*aCallbackFn)(*this, aContext, EHeapBadFreeCellSize, pF, l);
+ return KErrCorrupt;
+ }
+ }
+
+ while (pC!=pF) // walk allocated cells up to next free cell
+ {
+ TInt l; // pC->len;
+ err = ReadWord(pC, (TUint32&)l);
+ if (err) return err;
+ if (l<minCell || (l & (align-1)))
+ {
+ // allocated cell length invalid
+ //Unlock();
+ (*aCallbackFn)(*this, aContext, EHeapBadAllocatedCellSize, pC, l);
+ return KErrCorrupt;
+ }
+ TBool shouldContinue = (*aCallbackFn)(*this, aContext, EHeapAllocation, pC + KAllocatedCellHeaderSize, l - KAllocatedCellHeaderSize);
+ if (!shouldContinue) return KErrNone;
+
+ //SCell* pN = __NEXT_CELL(pC);
+ TLinAddr pN = pC + l;
+ if (pN > 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<iNbits) do
+ {
+ //if ((*this)[start]==bit)
+ if (PageMapOperatorBrackets(start, err) == bit || err)
+ return start;
+ } while (++start<iNbits);
+ return -1;
+ }
+
+TUint RAllocatorHelper::PagedDecode(TUint pos, TInt& err)
+ {
+ unsigned bits = PageMapBits(pos,2,err);
+ if (err) return 0;
+ bits >>= 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++]<<bit++;
+ val |= PageMapOperatorBrackets(ix++, err) << bit++;
+ if (err) return 0;
+ }
+ return val;
+ }
+
+enum TSlabType { ESlabFullInfo, ESlabPartialInfo, ESlabEmptyInfo };
+
+#ifndef TEST_HYBRIDHEAP_ASSERTS
+#define MAXSLABSIZE 56
+#define SLABSHIFT 10
+#define SLABSIZE (1 << SLABSHIFT)
+const TInt KMaxSlabPayload = SLABSIZE - KSlabPayloadOffset;
+#endif
+
+TInt RAllocatorHelper::NewHotnessWalk(TWalkFunc3 aCallbackFn, TAny* aContext)
+ {
+ // RHybridHeap does paged, slab then DLA, so that's what we do too
+ // Remember Kernel RHybridHeaps don't even have the page and slab members
+
+ TUint32 basePtr;
+ TInt err = ReadWord(iAllocatorAddress + _FOFF(RHackHeap, iBase), basePtr);
+ if (err) return err;
+ if (basePtr < iAllocatorAddress + KUserHybridHeapSize)
+ {
+ // Must be a kernel one - don't do page and slab
+ }
+ else
+ {
+ // Paged
+ TUint32 membase = 0;
+ err = ReadWord(iAllocatorAddress + KPageMapOffset + 8, membase);
+ if (err) return err;
+
+ TBool shouldContinue = ETrue;
+ for (int ix = 0;(ix = PageMapFind(ix,1,err)) >= 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<<slabIdx))
+ {
+ TUint32 addr = pageAddr + SLABSIZE*slabIdx + KSlabPayloadOffset; //&aSlab->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;
+ }
+ }
+ }
+ }
+
+// Really should be called TotalSizeForCellType(...)
+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 <fshell/ltkutils.h>
+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