Figure 1 PSAPIWorkingSetDemo
PSAPIWorkingSetDemo.CPP
//=======================================
// PSAPI Working Set Demo - Matt Pietrek 1996
// Microsoft Systems Journal, November 1996
// FILE: "PSAPIWorkingSetDemo.CPP"
//=======================================
//....
void AddWorkingSetInfo( HWND hDlg, DWORD processID )
{
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, processID );
if ( !hProcess )
return;
HWND hWndLb = GetDlgItem( hDlg, IDC_LB_WORKING_SET_DETAILS );
__try
{
SendMessage( hWndLb, WM_SETREDRAW, FALSE, 0 ); // Disable LB updating
char szBuffer[MAX_PATH * 4];
if ( !QueryWorkingSet( hProcess, g_WorkingSetPages,
sizeof(g_WorkingSetPages) ) )
__leave;
DWORD cPages = g_WorkingSetPages[0]; // First DWORD is page count
DWORD cPrivatePages = 0;
DWORD cSharedPages = 0;
DWORD cPageTablePages = 0;
qsort( &g_WorkingSetPages[1], cPages, sizeof(DWORD), CompareDWORDs );
for ( DWORD i = 1; i <= cPages; i++ )
{
DWORD pageAddr = g_WorkingSetPages[i] & g_AbovePageMask;
DWORD pageFlags = g_WorkingSetPages[i] & g_SubPageMask;
DWORD thisPageAddr, nextPageAddr, nextPageFlags;
DWORD cPagesThisRange = 0;
// Loop through all subseqent pages that are contiguous in
// memory and have the same attributes
while ( i <= cPages )
{
cPagesThisRange++;
if ( i == cPages ) // Break out if it's the last page
break;
thisPageAddr = g_WorkingSetPages[i] & g_AbovePageMask;
nextPageAddr = g_WorkingSetPages[i+1] & g_AbovePageMask;
nextPageFlags = g_WorkingSetPages[i+1] & g_SubPageMask;
if ( (nextPageAddr == (thisPageAddr + g_PageSize)) &&
(nextPageFlags == pageFlags) )
{
i++; // Keep going
}
else // Noncontiguous page or different flags
break;
}
// Is it a page from outside of the "page table" region???
if ( (pageAddr < 0xC0000000) || (pageAddr > 0xE0000000) )
{
if ( pageFlags & 0x100 ) // 0x100 == shared
cSharedPages+= cPagesThisRange;
else // 0x000 == private
cPrivatePages+= cPagesThisRange;
}
else // It's a page from the page table region
cPageTablePages += cPagesThisRange;
PSTR pszBuffCurr = szBuffer; // ptr to working output string
pszBuffCurr += wsprintf(pszBuffCurr, "%08X %4uK",
pageAddr, cPagesThisRange * 4 );
// 0x100 == not private (i.e., shared)
pszBuffCurr += wsprintf( pszBuffCurr,
pageFlags & 0x100 ? " S": " P" );
// 0x01 == ReadOnly
// 0x04 == Writeable
// 0x05 == Copy On Write (special case)
if ( 0x5 == (pageFlags & 0x5) )
pszBuffCurr += wsprintf( pszBuffCurr, " CW" );
else if ( pageFlags & 0x1 )
pszBuffCurr += wsprintf( pszBuffCurr, " RO" );
else if ( pageFlags & 0x4 )
pszBuffCurr += wsprintf( pszBuffCurr, " RW" );
else
pszBuffCurr += wsprintf( pszBuffCurr, " ??" );
// 0x02 == Executable
pszBuffCurr += wsprintf( pszBuffCurr,
pageFlags & 0x2 ? " E " : " " );
char szMod[MAX_PATH];
char szSection[IMAGE_SIZEOF_SHORT_NAME+1];
DWORD uSection;
BOOL fFoundInfo;
// Get information (such as the module and section name) for the
// page that was faulted in
fFoundInfo = GetModuleNameAndSectionInfo(
hProcess,
(PVOID)pageAddr,
szMod, sizeof(szMod),
szSection, sizeof(szSection),
&uSection );
if ( fFoundInfo ) // We found Win32 module info!
{
if ( uSection ) // We even know which section!
{
pszBuffCurr += wsprintf(pszBuffCurr,"%s!%s(%u)",
szMod,szSection,uSection);
}
else // We have a module, but no section
{
pszBuffCurr += wsprintf( pszBuffCurr, "%s", szMod );
}
}
else
{
// Perhaps it's a memory-mapped file!
char szOwner[MAX_PATH];
if ( GetMappedFileNameA( hProcess, (PVOID)pageAddr,
szOwner, sizeof(szOwner) ) )
pszBuffCurr += wsprintf( pszBuffCurr, "%s", szOwner );
}
// Add the output string to the listbox (finally!)
SendMessage( hWndLb, LB_ADDSTRING, 0, (LPARAM) szBuffer );
}
//
// Fix this! page size should be determined dynamically!
//
wsprintf( szBuffer, "Total: %uK", cPages * 4 );
SetDlgItemText( hDlg, IDC_TOTAL_MEMORY, szBuffer );
wsprintf( szBuffer, "Private: %uK", cPrivatePages * 4 );
SetDlgItemText( hDlg, IDC_PRIVATE_MEMORY, szBuffer );
wsprintf( szBuffer, "Shared: %uK", cSharedPages * 4 );
SetDlgItemText( hDlg, IDC_SHARED_MEMORY, szBuffer );
wsprintf( szBuffer, "Page Tables: %uK", cPageTablePages * 4 );
SetDlgItemText( hDlg, IDC_PAGE_TABLES, szBuffer );
}
__finally
{
SendMessage( hWndLb, WM_SETREDRAW, TRUE, 0 ); // Enable LB updating
CloseHandle( hProcess );
}
}
PSAPIWorkingSetDelta.CPP
//=======================================
// PSAPI Working Set Demo - Matt Pietrek 1996
// Microsoft Systems Journal, November 1996
// FILE: "PSAPIWorkingSetDelta.CPP"
//=======================================
// ....
static PSAPI_WS_WATCH_INFORMATION g_WorkingSetChanges[MAX_WS_DELTA_PAGES];
void FillDeltaListbox( HWND hWndLb )
{
// Create a pointer to the g_WorkingSetChanges array
PPSAPI_WS_WATCH_INFORMATION pWSDelta
= (PPSAPI_WS_WATCH_INFORMATION) g_WorkingSetChanges;
SendMessage( hWndLb, WM_SETREDRAW, FALSE, 0 ); // Disable LB updating
// Loop through each entry in the array until we find an element that
// has both the FaultingPC and FaultingVA set to 0. This indicates the
// end of the array.
while ( pWSDelta->FaultingPc || pWSDelta->FaultingVa )
{
// Skip over entries above 2GB. They can't (easily) be mapped to
// module names, and the user can't do any about them anyway
if ( pWSDelta->FaultingPc >= (PVOID)0x80000000 )
{
pWSDelta++;
continue;
}
//
// Warning!!! If a long period transpires between the start and end of
// the WorkingSetDelta, or if memory is extremely low, it's possible
// to have multiple entries here with the same address.
//
char szOutBuffer[ MAX_PATH * 4 ];
PSTR pszBuffCurr = szOutBuffer;
pszBuffCurr += wsprintf( pszBuffCurr, "%08X %08X ",
pWSDelta->FaultingVa, pWSDelta->FaultingPc );
char szMod[MAX_PATH];
char szSection[IMAGE_SIZEOF_SHORT_NAME+1];
DWORD uSection;
BOOL fFoundInfo;
// Get information (such as the module and section name) for the
// page that was faulted in
fFoundInfo =
GetModuleNameAndSectionInfo(
g_WorkingSetHProcess,
pWSDelta->FaultingVa,
szMod, sizeof(szMod),
szSection, sizeof(szSection),
&uSection );
if ( fFoundInfo ) // The address is within a loaded Win32 module
{
if ( uSection )
pszBuffCurr += wsprintf( pszBuffCurr, "%s!%s(%u)",
szMod, szSection, uSection);
else
pszBuffCurr += wsprintf( pszBuffCurr, "%s", szMod);
}
else // Not an address in a Win32 module
{
// Could be a stack page, a heap page, or whatever!
pszBuffCurr += wsprintf( pszBuffCurr, "???" );
}
// Get information (such as the module and section name) for the
// page that caused the fault
fFoundInfo =
GetModuleNameAndSectionInfo(
g_WorkingSetHProcess,
pWSDelta->FaultingPc,
szMod, sizeof(szMod),
szSection, sizeof(szSection),
&uSection );
if ( fFoundInfo ) // The address is within a loaded Win32 module
{
if ( uSection )
pszBuffCurr += wsprintf( pszBuffCurr, " via %s!%s(%u)",
szMod, szSection, uSection);
}
// Dump everything out the listbox (finally!)
SendMessage( hWndLb, LB_ADDSTRING, 0, (LPARAM)szOutBuffer );
pWSDelta++; // Advance to next page which was faulted in
}
SendMessage( hWndLb, WM_SETREDRAW, TRUE, 0 ); // Enable LB updating
}
//...
BOOL StartWorkingSetDelta( DWORD pid )
{
// Open a process handle, and store it in (Eeek!) a Global variable
g_WorkingSetHProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pid );
if ( !g_WorkingSetHProcess )
return FALSE;
if ( !InitializeProcessForWsWatch( g_WorkingSetHProcess ) )
{
CloseHandle( g_WorkingSetHProcess );
g_WorkingSetHProcess = 0;
return FALSE;
}
// Warning! This function can "fail" (return FALSE) if there aren't
// any new WS pages to report. It can also fail if you pass it in
// a bogus HPROCESS. Unfortunately, there's no way to tell the two
// situations apart.
GetWsChanges( g_WorkingSetHProcess, g_WorkingSetChanges,
sizeof(g_WorkingSetChanges) );
return TRUE;
}
void EndWorkingSetDelta( HWND hWndOwner )
{
if ( !g_WorkingSetHProcess )
return;
if ( GetWsChanges( g_WorkingSetHProcess, g_WorkingSetChanges,
sizeof(g_WorkingSetChanges) ) )
{
// We got 1 or more WS changes. Report it
DialogBox( GetModuleHandle(0),
MAKEINTRESOURCE(IDD_PSAPIWorkingSetDelta),
hWndOwner,
(DLGPROC)DeltaDlgProc );
}
else
{
MessageBox( hWndOwner,
"No working set changes, or error",
"Working set delta",
MB_OK );
}
CloseHandle( g_WorkingSetHProcess );
g_WorkingSetHProcess = 0;
}
PSAPIHELPER.CPP
//=======================================
// PSAPI Working Set Demo - Matt Pietrek 1996
// Microsoft Systems Journal, November 1996
// FILE: PSAPIHELPER.CPP
//=======================================
#include <windows.h>
#pragma hdrstop
#include "psapi.h"
#include "PSAPIhelper.h"
BOOL GetModuleNameAndSectionInfo(
HANDLE hProcess,
PVOID p,
PSTR pszModuleName,
DWORD cbModuleName,
PSTR pszSectionName,
DWORD cbSectionName,
PDWORD puSectionNum )
{
MEMORY_BASIC_INFORMATION mbi;
if ( !VirtualQueryEx( hProcess, p, &mbi, sizeof(mbi)) )
return FALSE;
PVOID hModule = mbi.AllocationBase;
DWORD cbRead;
IMAGE_DOS_HEADER dosHdr;
if ( !ReadProcessMemory(hProcess, hModule,
&dosHdr, sizeof(dosHdr), &cbRead) )
return FALSE;
if ( IMAGE_DOS_SIGNATURE != dosHdr.e_magic )
return FALSE;
if ( dosHdr.e_lfarlc < 0x40 )
return FALSE;
DWORD peHdrOffs = (DWORD)hModule + dosHdr.e_lfanew;
IMAGE_NT_HEADERS ntHdr;
if ( !ReadProcessMemory(hProcess, (PVOID)peHdrOffs,
&ntHdr, sizeof(ntHdr), &cbRead ) )
return FALSE;
if ( IMAGE_NT_SIGNATURE != ntHdr.Signature )
return FALSE;
PVOID sectionHdrOffs = (PVOID)(
peHdrOffs
+ FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader )
+ ntHdr.FileHeader.SizeOfOptionalHeader );
#define MAX_SECTIONS 128
IMAGE_SECTION_HEADER sections[ MAX_SECTIONS ];
PIMAGE_SECTION_HEADER pSection;
DWORD cSections = min( ntHdr.FileHeader.NumberOfSections, MAX_SECTIONS );
if ( !ReadProcessMemory(hProcess,
sectionHdrOffs,
§ions,
cSections * IMAGE_SIZEOF_SECTION_HEADER,
&cbRead) )
return FALSE;
// Get the module name now, since we can't assume that we'll find
// a section name/index later. For instance, the RVA might be somewhere
// in the NT header page, rather than in a code/data section.
GetModuleBaseName( hProcess, hModule,
pszModuleName, cbModuleName );
pszSectionName[0] = 0; // Set to void values in case we bail out of the
*puSectionNum = 0; // loop below without finding a section.
DWORD rva = (DWORD)p - (DWORD)hModule;
pSection = (PIMAGE_SECTION_HEADER)§ions;
for ( DWORD i = 0; i < cSections; i++, pSection++ )
{
DWORD endRVA = pSection->VirtualAddress
+ max(pSection->SizeOfRawData, pSection->Misc.VirtualSize);
if ( pSection->VirtualAddress > rva ) // Is this section's RVA
break; // past the RVA we're after?
if ( (rva >= pSection->VirtualAddress) && (rva <= endRVA ) )
{
lstrcpyn( pszSectionName, (PSTR)pSection->Name,
min(IMAGE_SIZEOF_SHORT_NAME+1, cbSectionName) );
*puSectionNum = i+1;
return TRUE;
}
}
return TRUE;
}