IDebugDataSpaces2::QueryVirtual doesn’t act the same as VirtualQuery

One of my debugger extensions commands uses IDebugDataSpaces2::QueryVirtual to iterate through the target’s address space to find particular size allocations (regions that are used for the TEB if you must know).  The code was working fine but on x64 dumps, I found that it was running quite slow.

Looking in to it, I found out that IDebugDataSpaces2::QueryVirtual doesn’t act the same as VirtualQuery. When it encounters an area that doesn’t contain an allocation, it doesn’t return a MEM_FREE ‘State’ value in the MEMORY_BASIC_INFORMATION64 structure for the region (like VirtualQuery does), instead it jumps forward to the next region.  Thus the pointer provided may not be in the region returned.

In my code, I had this iterator:

ulPtr += memInfo.RegionSize;

I was only using a 0x1800 byte increment because that was the size of the region jumped to. The performance problem stemmed from the size of the jump, from a Base Address of 0x13’00000000 to 0xffffff1e’00000000.  Needless to say, it took a while to traverse between the two (over 30 seconds).

A quick change to this solved everything:

    ulPtr = memInfo.BaseAddress + memInfo.RegionSize;

 

Here is how to correctly do a Lookup and how to do a Loop.

/////////////////////////////////////
// Lookup
/////////////////////////////////////

HRESULT GetMemInfo(IDebugDataSpaces2* pDebugDataSpaces2, ULONG64 ulPtr, MEMORY_BASIC_INFORMATION64* pMemInfo)

{
  if ((pDebugDataSpaces2 == NULL) || (pMemInfo == NULL))
return E_INVALIDARG;

  MEMORY_BASIC_INFORMATION64 memInfo;
  if (SUCCEEDED(pDebugDataSpaces2->QueryVirtual(ulPtr, &memInfo)))
  {
    if ((ulPtr >= memInfo.BaseAddress) &&
        (ulPtr < (memInfo.BaseAddress+memInfo.RegionSize)))
    {
      memcpy(pMemInfo, &memInfo, sizeof(MEMORY_BASIC_INFORMATION64));
      return S_OK;
    }
  }
  return E_FAIL;
}

/////////////////////////////////////
// Loop
/////////////////////////////////////

HRESULT LoopMemInfo(IDebugDataSpaces2* pDebugDataSpaces2)
{
  if (pDebugDataSpaces2 == NULL)
return E_INVALIDARG;

  ULONG64 ulPtr = 0;

  MEMORY_BASIC_INFORMATION64 memInfo;

  while ((ulPtr != 0) && (SUCCEEDED(pDebugDataSpaces2->QueryVirtual(ulPtr, &memInfo))))
  {
    // Note, ulPtr may not be within the memory region!
    // ...
    ulPtr = memInfo.BaseAddress + memInfo.RegionSize;
  }
return S_OK;
}

 

/////////////////////////////////////