MemSpyy
使用OpenGL映射虚拟内存地址空间。
引言
本文展示了如何使用OpenGL获取虚拟地址空间的图形内存映射。OpenGL代码本身基于André Stein的优秀教程,教程地址在此。
背景
虚拟地址空间映射对于进行大量内存分配的应用程序非常有用。在某些情况下,由于内存碎片,这些分配可能会失败。通过以图形方式查看虚拟地址空间,用户可以直观地看到碎片并识别哪些DLL导致了碎片。
使用代码
代码本身相当容易理解。它基于`VirtualQueryEx()`调用,该调用根据进程句柄而不是应用程序进程获取信息。
有趣的工作在`DrawGLScene()`函数中完成。
HANDLE process = OpenProcess( PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,false, m_PID );
首先,我们通过对话框编辑字段中提供的PID获取要查询的进程的句柄。基本上是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));
这是遍历所有地址空间的`while`循环的开头。其思想是在最低地址上调用`VirtualQueryEx`,然后添加内存块的大小,并再次调用,直到失败。这意味着我们到达了地址空间的末尾。
对这个主题感兴趣的人可能想阅读Richter的书“高级Windows”。我只有旧的Win32版本,但书中解释了如何使用`MEMORY_BASIC_INFORMATION`数据结构。具体来说,它解释了块和区域之间的区别。很好的背景知识,但我发现对于图形映射目的,我不太关心这一点。映射每个块就足够了。
if ( inSelect )
{
glPushName( numBlocks );
m_MemoryBlocks[numBlocks] = baseAddress;
}
此代码设置OpenGL选择。我使用选择缓冲区允许用户将鼠标悬停在任何块上并获取有关该块的信息。
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 );
}
如果我们是一个`MEM_FREE`块,那么我们想以红色绘制我们的线(除非被选中,则绘制白色)。
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 );
}
如果我们不是空闲的,那么检查我们是否是一个映射文件。如果是,则添加到“DLL”总数(尽管这些都是映射文件,而不仅仅是DLL)。绘制为绿色,除非被选中。否则,绘制为蓝色。我不再探索块是如何使用的。
baseAddress = (void *) (((char *) baseAddress) + memBlock.RegionSize);
}
glEnd();
if ( inSelect )
glPopName();
numBlocks++;
}
递增基地址,结束OpenGL绘图,从选择缓冲区弹出线条,然后再次循环。
CMemSpyyDlg* theParent = (CMemSpyyDlg*) GetParent();
theParent->SetLargestFree( largestFreeBlock );
theParent->SetTotalDLL( totalDLL );
theParent->SetTotalFree( totalFree );
最后,更新对话框中的各种信息。
关注点
- 使用纯GDI会更容易吗?嗯,也许吧。但是OpenGL很有趣!
- 我还添加了缩放/平移功能。右键单击拖动缩放,左键单击拖动平移。双击右键重置视图。
历史
- 2007年10月29日 - 初始版本