Hi all,
I just implemented HeapWalk:
-----------
/***********************************************************************
* HeapWalk (KERNEL32.344)
* Enumerates the memory blocks in a specified heap.
* See HEAP_Dump() for info on heap structure.
*
* TODO
* - use Tls*() instead of current* ?
* - handling of PROCESS_HEAP_ENTRY_MOVEABLE and
* PROCESS_HEAP_ENTRY_DDESHARE (needs heap.c support)
* - use real region indices throughout heap.c ?
*
* RETURNS
* TRUE: Success
* FALSE: Failure
*/
static HEAP *currentheap = NULL;
static SUBHEAP *currentsubheap = NULL;
static int currentregionindex = 0;
BOOL WINAPI HeapWalk(
HANDLE heap, /* [in] Handle to heap to enumerate */
LPPROCESS_HEAP_ENTRY entry /* [out] Pointer to structure of enumeration info */
) {
HEAP *heapPtr = HEAP_GetPtr(heap);
BOOL ret = FALSE;
char *ptr;
BOOL bFirstElement = FALSE;
if (!heapPtr || !entry)
{
SetLastError(ERROR_INVALID_PARAMETER);
goto HW_end;
}
/* set ptr to the next arena to be examined */
if (!entry->lpData) /* first call (init) ? */
{
TRACE("begin walking of heap 0x%08x.\n", heap);
currentheap = heapPtr;
currentsubheap = &heapPtr->subheap;
currentregionindex = 0;
ptr = (char*)currentsubheap + currentsubheap->headerSize;
bFirstElement = TRUE;
}
else
{
if (heapPtr != currentheap)
{ /* is this the right way to handle this ? */
ERR("heap given doesn't match currently enumerated heap !\n");
SetLastError(ERROR_INVALID_PARAMETER);
goto HW_end;
}
ptr = entry->lpData + entry->cbData;
if (ptr > (char *)currentsubheap + currentsubheap->size - 1)
{ /* proceed with next subheap */
if (!(currentsubheap = currentsubheap->next))
{ /* successfully finished */
TRACE("end.\n");
SetLastError(ERROR_NO_MORE_ITEMS);
goto HW_end;
}
currentregionindex++;
ptr = (char*)currentsubheap + currentsubheap->headerSize;
bFirstElement = TRUE;
}
}
entry->wFlags = 0;
if (*(DWORD *)ptr & ARENA_FLAG_FREE)
{
ARENA_FREE *pArena = (ARENA_FREE *)ptr;
/*TRACE("free, magic: %04x\n", pArena->magic);*/
entry->lpData = pArena + 1;
entry->cbData = pArena->size & ARENA_SIZE_MASK;
entry->cbOverhead = sizeof(ARENA_FREE);
entry->wFlags = PROCESS_HEAP_UNCOMMITTED_RANGE;
}
else
{
ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
/*TRACE("busy, magic: %04x\n", pArena->magic);*/
entry->lpData = pArena + 1;
entry->cbData = pArena->size & ARENA_SIZE_MASK;
entry->cbOverhead = sizeof(ARENA_INUSE);
entry->wFlags = PROCESS_HEAP_ENTRY_BUSY;
/* FIXME: can't handle PROCESS_HEAP_ENTRY_MOVEABLE
and PROCESS_HEAP_ENTRY_DDESHARE yet */
}
entry->iRegionIndex = currentregionindex;
if (bFirstElement)
{
entry->wFlags = PROCESS_HEAP_REGION;
entry->Foo.Region.dwCommittedSize = currentsubheap->commitSize;
entry->Foo.Region.dwUnCommittedSize =
currentsubheap->size - currentsubheap->commitSize;
entry->Foo.Region.lpFirstBlock = /* first valid block */
currentsubheap + currentsubheap->headerSize;
entry->Foo.Region.lpLastBlock = /* first invalid block */
currentsubheap + currentsubheap->size;
}
ret = TRUE;
HW_end:
if (!(heapPtr->flags & HEAP_NO_SERIALIZE)) HeapUnlock( heap );
return ret;
}
-----------
Now some questions:
I need to track the currently accessed subheap and other things by using
static HEAP *currentheap = NULL;
static SUBHEAP *currentsubheap = NULL;
static int currentregionindex = 0;
.
This is everything else but threadsafe :-\
Getting this threadsafe for one process would be possible using Tls*().
However with access by several processes concurrently it will mess up.
Any solution on how to do this properly ?
And some other things: I currently use a rather faked "currentregionindex"
instead of the "real" region index of the corresponding subheap
(I assume "region" in Windows speak means "subheap" in Wine speak)
as used in Windows.
Maybe we should add a region index to our subheaps ?
And finally I can't implement yet the actions when
PROCESS_HEAP_ENTRY_MOVEABLE or PROCESS_HEAP_ENTRY_DDESHARE is encountered,
as Wine's heap.c simply doesn't implement these flags.
Any thoughts ?
If not then I'll probably just submit this (this thing seems to work
quite well so far)
Andreas Mohr