Introduction
This article shows how to get a graphical memory map of the virtual address space using OpenGL. The OpenGL code itself is based on André Stein's excellent tutorial located here.
Background
Mapping the virtual address space is useful for applications that make large memory allocations. In certain cases, these allocations can fail due to memory fragmentation. By seeing the virtual address space graphically, the user can visually see the fragmentation and identify which DLLs are causing the fragmentation.
Using the code
The code itself is fairly easy to understand. It is based on the VirtualQueryEx()
call, which gets information based on a process handle rather than the applications process.
The interesting work is done in the function DrawGLScene()
.
HANDLE process = OpenProcess( PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,false, m_PID );
First, we get the handle to the process we want to query via the PID supplied in the edit field of the dialog box. Pretty much MFC 101.
PVOID baseAddress = 0;
SIZE_T retVal = 1;
unsigned long numBlocks = 0;
while (retVal)
{
MEMORY_BASIC_INFORMATION memBlock;
retVal = VirtualQueryEx(process, baseAddress,&memBlock,sizeof(memBlock));
Here is the beginning of the while
-loop which loops over all the address space. The idea is to call VirtualQueryEx
on the lowest address, then add the size of the memory block, and call again, until it fails. That means we reached the end of the address space.
People interested in this topic might want to read Richter's book "Advanced Windows". I only have the old Win32 version, but in it, he explains using the MEMORY_BASIC_INFORMATION
data structure. Specifically, he explains the differences between blocks and regions. Good background, but I found that for graphical mapping purposes, I didn't really care about that. Mapping each block was fine.
if ( inSelect )
{
glPushName( numBlocks );
m_MemoryBlocks[numBlocks] = baseAddress;
}
This code sets up the OpenGL selection. I use the selection buffer to allow the user to mouse over any block and get information about it.
glBegin(GL_LINES);
if (retVal)
{
if (memBlock.State & MEM_FREE)
{
if (memBlock.RegionSize>largestFreeBlock)
largestFreeBlock = (unsigned long) memBlock.RegionSize;
totalFree += (unsigned long) memBlock.RegionSize;
if ( numBlocks == m_CurrentlySelected )
glColor3f( 1.0f, 1.0f, 1.0f );
else
glColor3f(m_Red,0.0f,0.0f);
DrawMemory( memBlock.RegionSize );
}
If we are a MEM_FREE
block, then we want to draw our line in Red (unless selected, draw White).
else
{
char mappedFile[255];
if ( GetMappedFileName( process, baseAddress, mappedFile, 255) )
{
totalDLL += (unsigned long) memBlock.RegionSize;
if ( numBlocks == m_CurrentlySelected )
glColor3f( 1.0f, 1.0f, 1.0f );
else
glColor3f(0.0f,1.0f,0.0f);
}
else
{
if ( numBlocks == m_CurrentlySelected )
glColor3f( 1.0f, 1.0f, 1.0f );
else
glColor3f(0.0f,0.0f,m_Blue);
}
DrawMemory( memBlock.RegionSize );
}
If we are not free, then check to see if we are a mapped file. If so, add to 'DLL' total (although these are all mapped files, not just DLLs). Draw as Green, unless selected. Otherwise, draw as Blue. I don't explore anymore how the block is used.
baseAddress = (void *) (((char *) baseAddress) + memBlock.RegionSize);
}
glEnd();
if ( inSelect )
glPopName();
numBlocks++;
}
Increment the base address, end the OpenGL drawing, pop the line off the selection buffer, and loop around again.
CMemSpyyDlg* theParent = (CMemSpyyDlg*) GetParent();
theParent->SetLargestFree( largestFreeBlock );
theParent->SetTotalDLL( totalDLL );
theParent->SetTotalFree( totalFree );
Lastly, update the various bits of information in the dialog.
Points of interest
- Would this have been easier with straight GDI? Ehhhh, maybe. But OpenGL is fun!
- I also added zooming/panning. Right-click drags zooms and left-click drags pans. Double right-click resets the view.
History
- 10/29/2007 - Initial version