Skip to content

Commit 57190ae

Browse files
Douggemajkhoury
authored andcommitted
I've made two changes to improve performance of ReClass when analyzing processes with lots of memory regions.
Summary - Changed memory map to std::map from vector Changed module list to std::map from vector Changed lookups for both finding pages/addresses in the memory map and finding modules by address to use the std::map lookup instead of iterating through the whole vector. This took my draw times from ~3000ms to ~30ms. These changes took my draw time from ~3200ms per frame to ~28ms per frame. Instead of walking the entire memory space of the target process on a 5 second timer to generate the memory map, it is now done incrementally, with ~10ms of work being done before yielding and picking up where it left off the next time the UpdateMemoryMap timer fires (every 30ms). This got rid of the stutter that would happen every 5 seconds when the memory map was updated. Details: ******** Memory Map Address Lookup Changes *********** ReClass maintains a model of the memory map of the target process. It uses VirtualQueryEx to get information on memory regions and stores that information in a vector. When drawing a cell that has no type applied to it, Reclass interprets the value at the cell as an address and, if the address is mapped in the target process, it prints the address at the end of the cell highlighted in red. It determines whether an address is mapped by using that memory map model. The relevant code is: BOOLEAN IsMemory( ULONG_PTR Address ) { for (size_t i = 0; i < g_MemMap.size( ); i++) { if ((Address >= g_MemMap[i].Start) && (Address <= g_MemMap[i].End)) return true; } return false; } It iterates through each region in the memory map model and checks if the address falls within that region. In my case, I had ~280,000 memory regions in the model so each call to IsMemory was brutal. If I made a class with more than a handful of base cells in it, I was getting 3 second render times which basically made ReClass unusable for me. (280k regions seemed so crazy that I double checked that it wasn't some mistake. Turns out I had heap pages turned on for DayZ from back when I had last fuzzed it) My solution to this was to change g_MemMap from a vector to a std::map keyed on the last byte of each region. Then the best match region can be looked up with a call to g_MemMap->lower_bound(), and a comparison of the region base address against the address to check if Address is actually within the region. BOOLEAN IsMemory( ULONG_PTR Address ) { auto containingBlock = g_MemMap.lower_bound(Address); if (containingBlock != g_MemMap.end() && containingBlock->second.Start <= Address) { return true; } return false; } std::map lookups are O(logn) while the vector crawl is obviously O(n). This is almost certainly not a big deal for more reasonable size memory maps but it made a huge difference for my 280k example. As stated above, my frame draw time went from ~3200ms to ~28ms with this change alone. ******** Memory Map Generation Changes *********** The next hiccup was that every 5 seconds when Reclass would re-generate the target process memory map, ReClass would hang for upwards of a second. This makes sense - 280k calls to VirtualQueryEx is expensive. My solution to this was to generate the memory map a little bit at a time and spread the work out across a couple of seconds for a smoother frame rate. I added UpdateMemoryMapIncremental, which works on crawling the target memory map for ~10ms at a time. Once the whole memory map has been captured, it's dumped into g_MemMap and the process starts over. On the initial frame (no mem map info yet), it will run through the entire memory map non stop so that the user doesn't have to wait for pointer highlighting. This eliminated the 5 second hang up, and I haven't noticed any mem map lagging or side effects as a result. I made the same change to the module mapping as the memory map (changed from vector to std::map) but didn't do any beyond that because they seemed to not make a noticeable difference. ******** Smaller Changes *********** A few places walked the memory map, I changed those to use IsMemory() instead. Likewise for a few places that walked the Modules list on their own, I changed those to use GetModule.
1 parent aa53ba5 commit 57190ae

12 files changed

+169
-115
lines changed

ReClass/CClassView.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,7 @@ void CClassView::OnRButtonDown( UINT nFlags, CPoint point )
714714
}
715715

716716
void CClassView::OnPaint( )
717-
{
717+
{
718718
CPaintDC PaintDC( this ); // draw context for painting
719719
CMemDC MemDC( PaintDC, this );
720720
CDC *Dc = &MemDC.GetDC( );

ReClass/CMainFrame.cpp

+2-4
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,8 @@ int CMainFrame::OnCreate( LPCREATESTRUCT lpCreateStruct )
191191
Color = static_cast<CMFCRibbonColorButton*>(m_RibbonBar.FindByID( ID_BUTTON_CLR_CUSTOM )); Color->SetColor( g_clrCustom );
192192
Color = static_cast<CMFCRibbonColorButton*>(m_RibbonBar.FindByID( ID_BUTTON_CLR_HEX )); Color->SetColor( g_clrHex );
193193

194-
//
195-
// Update memory map every 5 seconds
196-
//
197-
SetTimer( TIMER_MEMORYMAP_UPDATE, 5000, NULL );
194+
195+
SetTimer( TIMER_MEMORYMAP_UPDATE, 30, NULL );
198196

199197
return 0;
200198
}

ReClass/CNodeBase.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ int CNodeBase::ResolveRTTI( ULONG_PTR Address, int x, const PVIEWINFO View, int
422422
#endif
423423

424424
x = AddText( View, x, y, g_clrOffset, HS_RTTI, _T( "%s" ), RttiString.GetString( ) );
425-
425+
426426
return x;
427427
}
428428

ReClass/DialogModules.cpp

+8-7
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ END_MESSAGE_MAP( )
6363

6464
void CDialogModules::BuildList( )
6565
{
66-
for (UINT idx = 0; idx < g_MemMapModules.size( ); idx++)
66+
int idx = 0;
67+
for (auto mi : g_MemMapModules)
6768
{
68-
MemMapInfo moduleInfo = g_MemMapModules[idx];
69-
69+
MemMapInfo& moduleInfo = mi.second;
7070
SHFILEINFO sfi = { 0 };
7171
SHGetFileInfo( moduleInfo.Path, FILE_ATTRIBUTE_NORMAL, &sfi, sizeof( SHFILEINFO ), SHGFI_ICON | SHGFI_USEFILEATTRIBUTES );
7272
m_ModuleIcons.Add( sfi.hIcon );
@@ -91,6 +91,7 @@ void CDialogModules::BuildList( )
9191
const_cast<LPCTSTR>(strSize),
9292
static_cast<LPARAM>(moduleInfo.Start)
9393
);
94+
idx += 1;
9495
}
9596
}
9697

@@ -127,11 +128,11 @@ BOOL CDialogModules::OnInitDialog( )
127128

128129
inline int CDialogModules::FindModuleByName( const TCHAR* szName )
129130
{
130-
for (UINT id = 0; id < g_MemMapModules.size( ); id++)
131+
for (auto mi : g_MemMapModules)
131132
{
132-
MemMapInfo moduleInfo = g_MemMapModules[id];
133+
MemMapInfo& moduleInfo = mi.second;
133134
if (_tcsicmp( moduleInfo.Name, szName ) == 0)
134-
return id;
135+
return mi.first;
135136
}
136137
return -1;
137138
}
@@ -151,7 +152,7 @@ void CDialogModules::SetSelected( )
151152
int nItem = m_ModuleList.GetNextSelectedItem( pos );
152153
nItem = FindModuleByName( m_ModuleList.GetItemText( nItem, 0 ) );
153154

154-
MemMapInfo mod = g_MemMapModules[nItem];
155+
MemMapInfo& mod = g_MemMapModules[nItem];
155156

156157
if (g_bSymbolResolution && m_SymbolLoad.GetCheck( ) == BST_CHECKED)
157158
g_ReClassApp.m_pSymbolLoader->LoadSymbolsForModule( mod.Path, mod.Start, mod.Size );

ReClass/DialogProcSelect.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -290,10 +290,11 @@ void CDialogProcSelect::OnAttachButton( )
290290
ProgressBar.SetStep( 1 );
291291
ProgressBar.SetText( _T( "Symbols loading: " ) );
292292

293-
for (size_t i = 0; i < g_MemMapModules.size( ); i++)
293+
int i = 0;
294+
for (auto mi : g_MemMapModules)
294295
{
295296
TCHAR ProgressText[256];
296-
MemMapInfo *CurrentModule = &g_MemMapModules[i];
297+
MemMapInfo* CurrentModule = &mi.second;
297298

298299
ProgressBar.SetRange32( 0, (int)g_MemMapModules.size( ) );
299300

@@ -316,6 +317,7 @@ void CDialogProcSelect::OnAttachButton( )
316317
if (!AfxGetApp( )->PumpMessage( ))
317318
::PostQuitMessage( 0 );
318319
}
320+
i += 1;
319321
}
320322

321323
StatusBar->SetPaneText( 1, _T( "" ) );

ReClass/ReClassEx.rc

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// Microsoft Visual C++ generated resource script.
1+
#pragma code_page(65001)
2+
// Microsoft Visual C++ generated resource script.
23
//
34
#include "resource.h"
45

ReClass/ReClassEx.vcxproj

+6-6
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<RootNamespace>ReClassEx</RootNamespace>
2424
<Keyword>MFCProj</Keyword>
2525
<ProjectName>ReClassEx</ProjectName>
26-
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
26+
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
2727
</PropertyGroup>
2828
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
2929
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
@@ -32,31 +32,31 @@
3232
<WholeProgramOptimization>true</WholeProgramOptimization>
3333
<CharacterSet>Unicode</CharacterSet>
3434
<UseOfMfc>Static</UseOfMfc>
35-
<PlatformToolset>v141</PlatformToolset>
35+
<PlatformToolset>v142</PlatformToolset>
3636
</PropertyGroup>
3737
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
3838
<ConfigurationType>Application</ConfigurationType>
3939
<UseDebugLibraries>false</UseDebugLibraries>
4040
<WholeProgramOptimization>true</WholeProgramOptimization>
4141
<CharacterSet>Unicode</CharacterSet>
4242
<UseOfMfc>Static</UseOfMfc>
43-
<PlatformToolset>v141</PlatformToolset>
43+
<PlatformToolset>v142</PlatformToolset>
4444
</PropertyGroup>
4545
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
4646
<ConfigurationType>Application</ConfigurationType>
4747
<UseDebugLibraries>false</UseDebugLibraries>
4848
<WholeProgramOptimization>true</WholeProgramOptimization>
4949
<CharacterSet>Unicode</CharacterSet>
5050
<UseOfMfc>Static</UseOfMfc>
51-
<PlatformToolset>v141</PlatformToolset>
51+
<PlatformToolset>v142</PlatformToolset>
5252
</PropertyGroup>
5353
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
5454
<ConfigurationType>Application</ConfigurationType>
5555
<UseDebugLibraries>false</UseDebugLibraries>
5656
<WholeProgramOptimization>true</WholeProgramOptimization>
57-
<CharacterSet>MultiByte</CharacterSet>
57+
<CharacterSet>Unicode</CharacterSet>
5858
<UseOfMfc>Static</UseOfMfc>
59-
<PlatformToolset>v141</PlatformToolset>
59+
<PlatformToolset>v142</PlatformToolset>
6060
</PropertyGroup>
6161
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
6262
<ImportGroup Label="ExtensionSettings">

0 commit comments

Comments
 (0)