Copyright © Microsoft Corporation. This document is an archived reproduction of a version originally published by Microsoft. It may have slight formatting modifications for consistency and to improve readability. |
//================================================== // MemDiff - Matt Pietrek 1999 // Microsoft Systems Journal, November 1999 // FILE: MemDiffDemo.cpp //================================================== #include <windows.h> #include "memdiff.h" int main() { HANDLE h1, h2; // // Create a new heap, and allocate a bunch of memory. The // HEAP_ZERO_MEMORY forces all the allocated pages in // HANDLE hHeap1 = HeapCreate( 0, 0, 0 ); HeapAlloc( hHeap1, HEAP_ZERO_MEMORY, 0x7000 ); // // Take the initial snapshot // h1 = MDTakeSnapshot( GetCurrentProcess() ); // Get rid of hHeap1. This test ensures that we see something in // the Snapshot1 details report HeapDestroy( hHeap1 ); // // Load, then free a DLL. Extra memory paged into the // working set, but not freed *should* show up. // HMODULE hModule = LoadLibrary( "WININET.DLL" ); FreeLibrary( hModule ); // // Allocate some memory, and leave it around // PVOID p = VirtualAlloc( 0, 0x1000, MEM_COMMIT, PAGE_READWRITE ); memset( p, 0, 0x1000 ); // Touch it! HANDLE hHeap2 = HeapCreate( 0, 0, 0 ); HeapAlloc( hHeap2, HEAP_ZERO_MEMORY, 0x1234 ); HANDLE hFileMapping = CreateFileMapping( INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, 0x2000, 0 ); if ( hFileMapping ) { PVOID p2 = MapViewOfFile(hFileMapping, FILE_MAP_WRITE, 0, 0, 0x2000); if ( p2 ) memset( p2, 0, 0x2000 ); } // // Take the second snapshot // h2 = MDTakeSnapshot( GetCurrentProcess() ); // // Compare the two snapshots to generate a report // MDCompareSnapshot( h1, h2, GetStdHandle(STD_OUTPUT_HANDLE), false ); // // Free the snapshot handles (aka "memory") // MDFreeSnapshot( h1 ); MDFreeSnapshot( h2 ); return 0; }
Figure 2 Nonverbose Output
Memory Difference: 36KB Memory allocated: 72KB Memory freed: 36KB Private memory: 20KB Shared(potentially): 52KB Snapshot1 (items are no longer in memory) =============================================================================== Address Size Owner 008B0000 36K <unknown> Snapshot2 (these items have been paged in) =============================================================================== Address Size Owner 00900000 4K VirtualAlloc/Stack/Other 00910000 16K Heap 00950000 8K VirtualAlloc/Stack/Other 77E62000 8K D:\WINNT\system32\USER32.dll 77E92000 4K D:\WINNT\system32\KERNEL32.dll 77F72000 20K D:\WINNT\system32\GDI32.DLL 77F89000 12K D:\WINNT\System32\ntdll.dll
Figure 3 Verbose Output
Snapshot2 (these items have been paged in) =============================================================================== RW:read-write RO:read-only EX:executable CW:copy-on-write SH:shared Address Size Attribute Owner 00900000 4K RW VirtualAlloc/Stack/Other 00910000 16K RW Heap 00950000 8K RW SH VirtualAlloc/Stack/Other 77E62000 8K RO EX SH D:\WINNT\system32\USER32.dll 77E92000 4K RO EX SH D:\WINNT\system32\KERNEL32.dll 77F72000 20K RO EX SH D:\WINNT\system32\GDI32.DLL 77F89000 12K RO EX SH D:\WINNT\System32\ntdll.dll
Figure 4 memdiff.cpp
//================================================== // MemDiff - Matt Pietrek 1999 // Microsoft Systems Journal, November 1999 // FILE: memdiff.cpp //================================================== #include "StdAfx.h" #include <stdarg.h> #include "memdiff.h" //=================================================================== static int __cdecl MDSort( const void *pElem1, const void *pElem2 ); void FilterOutCommonPages( HANDLE hSnapshot1, HANDLE hSnapshot2 ); void SummaryReport( HANDLE hSnapshot1, HANDLE hSnapshot2, HANDLE hOutputFile ); void DetailedReport(HANDLE hSnapshot1, HANDLE hSnapshot2, HANDLE hOutputFile, bool fVerbose = false ); void MDPrintf( HANDLE hOutputFile, char * pszFmt, ... ); //=================================================================== typedef struct MEMDIFF_SNAPSHOT { DWORD m_signature; DWORD m_dwAllocSize; DWORD m_fCompared; // 0 initially, 1 after filtering common pages DWORD m_nPages; // Must come last!!! } * PMEMDIFF_SNAPSHOT; #define SNAPSHOT_SIGNATURE 0x50594F52 static DWORD g_kbMultiplier; //=================================================================== extern "C" HANDLE __stdcall MDTakeSnapshot( HANDLE hProcess ) { unsigned dwAlloc = 0; top: dwAlloc += 0x2000; PMEMDIFF_SNAPSHOT pSnapshot; pSnapshot = (PMEMDIFF_SNAPSHOT)VirtualAlloc(0, dwAlloc, MEM_COMMIT, PAGE_READWRITE); if ( !pSnapshot ) return 0; PVOID pPageInfoStart = &pSnapshot->m_nPages; if ( 0 == QueryWorkingSet( hProcess, pPageInfoStart, dwAlloc - sizeof(*pSnapshot) ) ) { VirtualFree( pSnapshot, 0, MEM_RELEASE ); return 0; } else // It succeded.... But... { // Were there more pages than we had buffer size for? if ( pSnapshot->m_nPages > (dwAlloc / sizeof(DWORD)) ) { if ( dwAlloc < 0x20000 ) // Limits us to 128MB on an x86 { VirtualFree( pSnapshot, 0, MEM_RELEASE ); pSnapshot = 0; goto top; } else return 0; } } pSnapshot->m_signature = SNAPSHOT_SIGNATURE; pSnapshot->m_fCompared = 0; pSnapshot->m_dwAllocSize = dwAlloc; return (HANDLE) pSnapshot; } extern "C" BOOL __stdcall MDCompareSnapshot( HANDLE hSnapshot1, HANDLE hSnapshot2, HANDLE hOutputFile, bool fVerbose ) { if ( !hSnapshot1 || !hSnapshot2 ) return false; if ( IsBadReadPtr( (PVOID)hSnapshot1, sizeof(MEMDIFF_SNAPSHOT) ) ) return false; if ( IsBadReadPtr( (PVOID)hSnapshot2, sizeof(MEMDIFF_SNAPSHOT) ) ) return false; if ( ((PMEMDIFF_SNAPSHOT)hSnapshot1)->m_signature != SNAPSHOT_SIGNATURE ) return false; if ( ((PMEMDIFF_SNAPSHOT)hSnapshot2)->m_signature != SNAPSHOT_SIGNATURE ) return false; if ( ((PMEMDIFF_SNAPSHOT)hSnapshot1)->m_fCompared || ((PMEMDIFF_SNAPSHOT)hSnapshot2)->m_fCompared ) { MDPrintf( hOutputFile, "One or both snapshots has already been compared against." "\r\nSnapshots are only good for one compare.\r\n" ); return false; } SYSTEM_INFO sysInfo; GetSystemInfo( &sysInfo ); g_kbMultiplier = sysInfo.dwPageSize/1024; FilterOutCommonPages( hSnapshot1, hSnapshot2 ); SummaryReport( hSnapshot1, hSnapshot2, hOutputFile ); MDPrintf( hOutputFile, "\r\n" ); DetailedReport( hSnapshot1, hSnapshot2, hOutputFile, fVerbose ); return true; } extern "C" BOOL __stdcall MDFreeSnapshot( HANDLE hSnapshot ) { if ( ((PMEMDIFF_SNAPSHOT)hSnapshot)->m_signature != SNAPSHOT_SIGNATURE ) return false; return VirtualFree( hSnapshot, 0, MEM_RELEASE ); } /*========================================================================*/ // Helper function routines // static int __cdecl MDSort( const void *pElem1, const void *pElem2 ) { DWORD elem1 = *(PDWORD)pElem1; DWORD elem2 = *(PDWORD)pElem2; if ( elem1 > elem2 ) return 1; else if ( elem1 < elem2 ) return -1; else return 0; } void FilterOutCommonPages( HANDLE hSnapshot1, HANDLE hSnapshot2 ) { PMEMDIFF_SNAPSHOT pSnapshot1 = (PMEMDIFF_SNAPSHOT)hSnapshot1; PMEMDIFF_SNAPSHOT pSnapshot2 = (PMEMDIFF_SNAPSHOT)hSnapshot2; pSnapshot1->m_fCompared = 1; // So we don't use it again pSnapshot2->m_fCompared = 1; // So we don't use it again DWORD nPages1 = pSnapshot1->m_nPages; DWORD nPages2 = pSnapshot2->m_nPages; PDWORD pPages1 = (PDWORD)(pSnapshot1 + 1); PDWORD pPages2 = (PDWORD)(pSnapshot2 + 1); qsort( pPages1, nPages1, sizeof(DWORD), MDSort ); qsort( pPages2, nPages2, sizeof(DWORD), MDSort ); // // Clean this up! Basically, set all pages that are the same // in both snapshots to the value '0' // DWORD jStart = 0; for ( DWORD i = 0; i < nPages1; i++ ) { DWORD pAddr1 = pPages1[i] & 0xFFFFF000; for ( DWORD j = jStart; j < nPages2; j++ ) { DWORD pAddr2 = pPages2[j] & 0xFFFFF000; if ( pAddr1 == pAddr2 ) { pPages1[i] = pPages2[j] = 0; jStart = j+1; break; } if ( pAddr2 > pAddr1 ) break; } } // now filter out the pages used by snapshot1 for snapshot1's data for ( i = 0; i < nPages1; i++ ) { DWORD pAddr1 = pPages1[i] & 0xFFFFF000; if ( (pAddr1 >= (DWORD)pSnapshot1) && (pAddr1 < ((DWORD)pSnapshot1 + pSnapshot1->m_dwAllocSize)) ) { pPages1[i] = 0; } } // now filter out the pages used by snapshot2 for snapshot2's data for ( i = 0; i < nPages2; i++ ) { DWORD pAddr2 = pPages2[i] & 0xFFFFF000; if ( (pAddr2 >= (DWORD)pSnapshot2) && (pAddr2 < ((DWORD)pSnapshot2 + pSnapshot2->m_dwAllocSize)) ) { pPages2[i] = 0; } } } void SummaryReport( HANDLE hSnapshot1, HANDLE hSnapshot2, HANDLE hOutputFile ) { PMEMDIFF_SNAPSHOT pSnapshot1 = (PMEMDIFF_SNAPSHOT)hSnapshot1; PMEMDIFF_SNAPSHOT pSnapshot2 = (PMEMDIFF_SNAPSHOT)hSnapshot2; DWORD nPages1 = pSnapshot1->m_nPages; DWORD nPages2 = pSnapshot2->m_nPages; PDWORD pPages1 = (PDWORD)(pSnapshot1 + 1); PDWORD pPages2 = (PDWORD)(pSnapshot2 + 1); DWORD nPagesLeft1 = 0, nPagesShared1 = 0; DWORD nPagesLeft2 = 0, nPagesShared2 = 0; for ( DWORD i = 0; i < nPages1; i++ ) { if ( 0 == pPages1[i] ) continue; nPagesLeft1++; if ( pPages1[i] & 0x100 ) nPagesShared1++; } for ( i = 0; i < nPages2; i++ ) { if ( 0 == pPages2[i] ) continue; nPagesLeft2++; if ( pPages2[i] & 0x100 ) nPagesShared2++; } MDPrintf( hOutputFile, "Memory Difference: %uKB\r\n", (long)((nPagesLeft2 - nPagesLeft1)) * g_kbMultiplier ); MDPrintf( hOutputFile, "Memory allocated: %uKB\r\n", nPagesLeft2 * g_kbMultiplier ); MDPrintf( hOutputFile, "Memory freed: %uKB\r\n", nPagesLeft1 * g_kbMultiplier ); MDPrintf( hOutputFile, "Private memory: %uKB Shared(potentially): %uKB\r\n", (nPagesLeft2 - nPagesShared2) * g_kbMultiplier, (nPagesShared2 * g_kbMultiplier) ); } void DetailedReportHelper(HANDLE hSnapshot, HANDLE hOutputFile, bool fVerbose) { MDPrintf( hOutputFile, "===============================================" "================================\r\n" ); if ( fVerbose ) { MDPrintf( hOutputFile, "RW:read-write RO:read-only EX:executable " "CW:copy-on-write SH:shared\r\n" ); MDPrintf( hOutputFile, "Address Size Attribute Owner\r\n" ); } else { MDPrintf( hOutputFile, "Address Size Owner\r\n" ); } PMEMDIFF_SNAPSHOT pSnapshot = (PMEMDIFF_SNAPSHOT)hSnapshot; DWORD nPages = pSnapshot->m_nPages; PDWORD pPages = (PDWORD)(pSnapshot + 1); CProcessHeaps heaps; for ( DWORD i = 0; i < nPages; ) // we bump up 'i' at end { if ( 0 == pPages[i] ) // Skip empty (common) pages { i++; continue; } // // Gather basic information about this page // PVOID pAddr = (PVOID)(pPages[i] & 0xFFFFF000); DWORD pageAttrib = pPages[i] & 0x00000FFF; MEMORY_BASIC_INFORMATION mbi; VirtualQuery(pAddr, &mbi, sizeof(mbi)); PVOID allocationBase = mbi.AllocationBase; // // Now coalesce this page with adjoining, similar pages // DWORD nCoalescedPages = 1; DWORD j; if ( fVerbose ) // Pages must be contiguous, and share the same { // attributes and allocation base for ( j=i+1; j < nPages; j++ ) { if ( 0 == pPages[j] ) break; if ( (pPages[j] & 0x00000FFF) != pageAttrib ) break; VirtualQuery( (PVOID)pPages[j], &mbi, sizeof(mbi)); if ( mbi.AllocationBase != allocationBase ) break; nCoalescedPages++; } } else // Pages must share the same allocation base { for ( j=i+1; j < nPages; j++ ) { if ( 0 == pPages[j] ) continue; VirtualQuery( (PVOID)pPages[j], &mbi, sizeof(mbi)); if ( mbi.AllocationBase != allocationBase ) break; nCoalescedPages++; } } DWORD nProcessedPages = j - i; char szPageAttrib[128] = {0}; if ( (pageAttrib & 5) == 5 ) { strcat( szPageAttrib, "CW " ); } else { if ( pageAttrib & 1 ) strcat( szPageAttrib, "RO " ); if ( pageAttrib & 2 ) strcat( szPageAttrib, "EX " ); if ( pageAttrib & 4 ) strcat( szPageAttrib, "RW " ); } if ( pageAttrib & 0x100 ) { strcat( szPageAttrib, "SH " ); } char szPageOwner[MAX_PATH]; szPageOwner[0] = 0; if ( 0 == allocationBase ) { strcpy( szPageOwner, "<unknown>" ); } else if ( !GetModuleFileName((HINSTANCE)allocationBase, szPageOwner, sizeof(szPageOwner)) ) { if ( heaps.IsAddressInHeaps( allocationBase) ) { wsprintf( szPageOwner, "Heap" ); } else if ( GetMappedFileNameA( GetCurrentProcess(), allocationBase, szPageOwner, sizeof(szPageOwner))) { // GetMappedFileNameW is just a call to // NtQueryVirtualMemory( 2 ). (0 is VirtualQueryEx); } else wsprintf( szPageOwner, "VirtualAlloc/Stack/Other" ); } if ( fVerbose ) { MDPrintf( hOutputFile, "%08X %5uK %-9s %s\r\n", pAddr, nCoalescedPages * g_kbMultiplier, szPageAttrib, szPageOwner ); } else { MDPrintf( hOutputFile, "%08X %5uK %s\r\n", pAddr, nCoalescedPages * g_kbMultiplier, szPageOwner ); } i += nProcessedPages; } } void DetailedReport(HANDLE hSnapshot1, HANDLE hSnapshot2, HANDLE hOutputFile, bool fVerbose) { MDPrintf(hOutputFile, "Snapshot1 (items are no longer in memory)\r\n"); DetailedReportHelper( hSnapshot1, hOutputFile, fVerbose ); MDPrintf( hOutputFile, "\r\n" ); MDPrintf(hOutputFile, "Snapshot2 (these items have been paged in)\r\n"); DetailedReportHelper( hSnapshot2, hOutputFile, fVerbose ); } void MDPrintf( HANDLE hOutputFile, char * pszFmt, ... ) { va_list marker; va_start( marker, pszFmt ); char szBuffer[1024]; int retValue = wvsprintf( szBuffer, pszFmt, marker ); if ( retValue ) { DWORD dwWritten; WriteFile( hOutputFile, szBuffer, retValue, &dwWritten, 0 ); } va_end( marker ); }