|
|
/*++
Copyright (c) 1999-1999 Microsoft Corporation
Module Name:
vm.cxx
Abstract:
This module contains an NTSD debugger extension for dumping various virtual memory statistics.
Author:
Keith Moore (keithmo) 15-Jan-1999
Revision History:
--*/
#include "inetdbgp.h"
//
// Private constants.
//
#define SMALL_REGION (64 * 1024)
#define MEDIUM_REGION (1 * 1024 * 1024)
#define IS_SMALL(c) ((c) <= SMALL_REGION)
#define IS_MEDIUM(c) (((c) > SMALL_REGION) && ((c) <= MEDIUM_REGION))
#define IS_LARGE(c) ((c) > MEDIUM_REGION)
#define PRINTF_FORMAT "%-7s %*s %*s %*s %*s\n"
#define CCH_ULONG_COMMAS sizeof("4,294,967,296")
//
// Private types.
//
typedef struct _INDIVIDUAL_STAT { SIZE_T MinimumSize; SIZE_T MaximumSize; SIZE_T TotalSize; SIZE_T BlockCount;
} INDIVIDUAL_STAT, *PINDIVIDUAL_STAT;
typedef struct _VM_STATS { INDIVIDUAL_STAT Summary; INDIVIDUAL_STAT Small; INDIVIDUAL_STAT Medium; INDIVIDUAL_STAT Large;
} VM_STATS, *PVM_STATS;
typedef struct PROTECT_MASK { DWORD Bit; PSTR Name;
} PROTECT_MASK, *PPROTECT_MASK;
//
// Private globals.
//
PROTECT_MASK ProtectMasks[] = { { PAGE_NOACCESS, "NA" },
{ PAGE_NOCACHE, "NC" },
{ PAGE_GUARD, "G" },
{ PAGE_READONLY, "Rd" },
{ PAGE_READWRITE, "RdWr" },
{ PAGE_WRITECOPY, "WrCp" },
{ PAGE_EXECUTE, "Ex" },
{ PAGE_EXECUTE_READ, "ExRd" },
{ PAGE_EXECUTE_READWRITE, "ExRdWr" },
{ PAGE_EXECUTE_WRITECOPY, "ExWrCp" } };
#define NUM_PROTECT_MASKS (sizeof(ProtectMasks) / sizeof(ProtectMasks[0]))
//
// Private functions.
//
PSTR ULongLongToString( IN ULONGLONG Value, OUT PSTR Buffer ) {
PSTR p1; PSTR p2; CHAR ch; INT digit; INT count; BOOL needComma; INT length;
//
// Handling zero specially makes everything else a bit easier.
//
if( Value == 0 ) { Buffer[0] = '0'; Buffer[1] = '\0'; return Buffer; }
//
// Pull the least signifigant digits off the value and store them
// into the buffer. Note that this will store the digits in the
// reverse order.
//
p1 = p2 = Buffer; count = 3; needComma = FALSE;
while( Value != 0 ) {
if( needComma ) { *p1++ = ','; needComma = FALSE; }
digit = (INT)( Value % 10 ); Value = Value / 10;
*p1++ = '0' + digit;
count--; if( count == 0 ) { count = 3; needComma = TRUE; }
}
length = DIFF(p1 - Buffer);
//
// Reverse the digits in the buffer.
//
*p1-- = '\0';
while( p1 > p2 ) {
ch = *p1; *p1 = *p2; *p2 = ch;
p2++; p1--;
}
return Buffer;
} // ULongLongToString
VOID InitVmStats( OUT PVM_STATS Stats ) { ZeroMemory( Stats, sizeof(*Stats) ); Stats->Summary.MinimumSize = (SIZE_T)-1L; Stats->Small.MinimumSize = (SIZE_T)-1L; Stats->Medium.MinimumSize = (SIZE_T)-1L; Stats->Large.MinimumSize = (SIZE_T)-1L;
} // InitVmStats
VOID UpdateIndividualStat( IN OUT PINDIVIDUAL_STAT Stat, IN SIZE_T BlockSize ) { Stat->BlockCount++; Stat->TotalSize += BlockSize;
if( BlockSize > Stat->MaximumSize ) { Stat->MaximumSize = BlockSize; }
if( BlockSize < Stat->MinimumSize ) { Stat->MinimumSize = BlockSize; }
} // UpdateIndividualStat
VOID UpdateVmStats( IN OUT PVM_STATS Stats, IN SIZE_T BlockSize ) { UpdateIndividualStat( &Stats->Summary, BlockSize );
if( IS_SMALL(BlockSize) ) { UpdateIndividualStat( &Stats->Small, BlockSize ); }
if( IS_MEDIUM(BlockSize) ) { UpdateIndividualStat( &Stats->Medium, BlockSize ); }
if( IS_LARGE(BlockSize) ) { UpdateIndividualStat( &Stats->Large, BlockSize ); }
} // UpdateVmStats
VOID PrintVmStatsHeader( VOID ) { dprintf( PRINTF_FORMAT, "TYPE", CCH_ULONG_COMMAS, "MINIMUM", CCH_ULONG_COMMAS, "MAXIMUM", CCH_ULONG_COMMAS, "AVERAGE", CCH_ULONG_COMMAS, "BLK COUNT" );
printf( PRINTF_FORMAT, "~~~~", CCH_ULONG_COMMAS, "~~~~~~~", CCH_ULONG_COMMAS, "~~~~~~~", CCH_ULONG_COMMAS, "~~~~~~~", CCH_ULONG_COMMAS, "~~~~~~~~~" );
} // PrintVmStatsHeader
VOID PrintIndividualStat( IN PSTR Name, IN PINDIVIDUAL_STAT Stat ) { SIZE_T average; SIZE_T minsize; CHAR minStr[CCH_ULONG_COMMAS]; CHAR maxStr[CCH_ULONG_COMMAS]; CHAR avgStr[CCH_ULONG_COMMAS]; CHAR countStr[CCH_ULONG_COMMAS];
if( Stat->BlockCount == 0 ) { average = 0; minsize = 0; } else { average = Stat->TotalSize / Stat->BlockCount; minsize = Stat->MinimumSize; }
dprintf( PRINTF_FORMAT, Name, CCH_ULONG_COMMAS, ULongLongToString( (ULONGLONG)minsize, minStr ), CCH_ULONG_COMMAS, ULongLongToString( (ULONGLONG)Stat->MaximumSize, maxStr ), CCH_ULONG_COMMAS, ULongLongToString( (ULONGLONG)average, avgStr ), CCH_ULONG_COMMAS, ULongLongToString( (ULONGLONG)Stat->BlockCount, countStr ) );
} // PrintIndividualStat
VOID PrintVmStats( IN PSTR Name, IN PVM_STATS Stats ) { dprintf( "%s:\n", Name );
PrintIndividualStat( "Small", &Stats->Small ); PrintIndividualStat( "Medium", &Stats->Medium ); PrintIndividualStat( "Large", &Stats->Large ); PrintIndividualStat( "Summary", &Stats->Summary );
dprintf( "\n" );
} // PrintVmStats
PSTR VmProtectToString( IN DWORD Protect, OUT PSTR Buffer ) { INT i; PPROTECT_MASK mask;
Buffer[0] = '\0';
for( i = 0, mask = &ProtectMasks[0] ; (i < NUM_PROTECT_MASKS) && (Protect != 0) ; i++, mask++ ) { if( mask->Bit & Protect ) { Protect &= ~mask->Bit; if( Buffer[0] != '\0' ) { strcat( Buffer, "|" ); } strcat( Buffer, mask->Name ); } }
if( Protect != 0 ) { if( Buffer[0] != '\0' ) { strcat( Buffer, "|" ); } sprintf( Buffer + strlen(Buffer), "%08lx", Protect ); }
return Buffer;
} // VmProtectToString
PSTR VmStateToString( IN DWORD State, OUT PSTR Buffer ) { PSTR result; CHAR invalidStr[sizeof("12345678")];
switch( State ) { case MEM_COMMIT: result = "Commit"; break;
case MEM_RESERVE: result = "Reserve"; break;
case MEM_FREE: result = "Free"; break;
default: sprintf( invalidStr, "%08lx", State ); result = invalidStr; break; }
strcpy( Buffer, result ); return Buffer;
} // VmStateToString
PSTR VmTypeToString( IN DWORD Type, OUT PSTR Buffer ) { PSTR result; CHAR invalidStr[sizeof("12345678")];
switch( Type ) { case MEM_PRIVATE: result = "Private"; break;
case MEM_MAPPED: result = "Mapped"; break;
case MEM_IMAGE: result = "Image"; break;
case 0: result = ""; break;
default: sprintf( invalidStr, "%08lx", Type ); result = invalidStr; break; }
strcpy( Buffer, result ); return Buffer;
} // VmTypeToString
/************************************************************
* Dump Virtual Memory Info ************************************************************/
DECLARE_API( vmstat )
/*++
Routine Description:
This function is called as an NTSD extension to format and dump virtual memory statistics.
Arguments:
hCurrentProcess - Supplies a handle to the current process (at the time the extension was called).
hCurrentThread - Supplies a handle to the current thread (at the time the extension was called).
CurrentPc - Supplies the current pc at the time the extension is called.
lpExtensionApis - Supplies the address of the functions callable by this extension.
lpArgumentString - Supplies the asciiz string that describes the ansi string to be dumped.
Return Value:
None.
--*/
{
NTSTATUS status; ULONG_PTR address; MEMORY_BASIC_INFORMATION memInfo; VM_STATS freeStats; VM_STATS reserveStats; VM_STATS commitStats; VM_STATS privateStats; VM_STATS mappedStats; VM_STATS imageStats;
INIT_API();
//
// Setup.
//
InitVmStats( &freeStats ); InitVmStats( &reserveStats ); InitVmStats( &commitStats ); InitVmStats( &privateStats ); InitVmStats( &mappedStats ); InitVmStats( &imageStats );
address = 0;
//
// Scan the virtual address space.
//
for( ; ; ) { status = NtQueryVirtualMemory( hCurrentProcess, (PVOID)address, MemoryBasicInformation, &memInfo, sizeof(memInfo), NULL );
if( !NT_SUCCESS(status) ) { break; }
//
// Interpret the memory state.
//
switch( memInfo.State ) { case MEM_FREE: UpdateVmStats( &freeStats, memInfo.RegionSize ); break;
case MEM_RESERVE: UpdateVmStats( &reserveStats, memInfo.RegionSize ); break;
case MEM_COMMIT: UpdateVmStats( &commitStats, memInfo.RegionSize ); break; }
//
// Interpret the memory type.
//
switch( memInfo.Type ) { case MEM_PRIVATE: UpdateVmStats( &privateStats, memInfo.RegionSize ); break;
case MEM_MAPPED: UpdateVmStats( &mappedStats, memInfo.RegionSize ); break;
case MEM_IMAGE: UpdateVmStats( &imageStats, memInfo.RegionSize ); break; }
//
// Advance to the next block.
//
address += memInfo.RegionSize; }
//
// Dump it.
//
PrintVmStatsHeader(); PrintVmStats( "Free", &freeStats ); PrintVmStats( "Reserve", &reserveStats ); PrintVmStats( "Commit", &commitStats ); PrintVmStats( "Private", &privateStats ); PrintVmStats( "Mapped", &mappedStats ); PrintVmStats( "Image", &imageStats );
} // DECLARE_API( vmstat )
DECLARE_API( vmmap )
/*++
Routine Description:
This function is called as an NTSD extension to format and dump the debugee's virtual memory address space.
Arguments:
hCurrentProcess - Supplies a handle to the current process (at the time the extension was called).
hCurrentThread - Supplies a handle to the current thread (at the time the extension was called).
CurrentPc - Supplies the current pc at the time the extension is called.
lpExtensionApis - Supplies the address of the functions callable by this extension.
lpArgumentString - Supplies the asciiz string that describes the ansi string to be dumped.
Return Value:
None.
--*/
{
NTSTATUS status; ULONG_PTR address; MEMORY_BASIC_INFORMATION memInfo; CHAR protectStr[32]; CHAR aprotectStr[32]; CHAR stateStr[16]; CHAR typeStr[16];
INIT_API();
//
// Setup.
//
address = 0;
dprintf( "%-*s %-*s %-*s %-13s %-13s %-8s %-8s\n", sizeof(PVOID) * 2, "Start", sizeof(PVOID) * 2, "Stop", sizeof(PVOID) * 2, "Length", "AllocProtect", "Protect", "State", "Type" );
//
// Scan the virtual address space.
//
for( ; ; ) { status = NtQueryVirtualMemory( hCurrentProcess, (PVOID)address, MemoryBasicInformation, &memInfo, sizeof(memInfo), NULL );
if( !NT_SUCCESS(status) ) { break; }
//
// Dump the current entry.
//
dprintf( "%p-%p %p %-13s %-13s %-8s %-8s\n", memInfo.BaseAddress, (ULONG_PTR)memInfo.BaseAddress + memInfo.RegionSize - 1, memInfo.RegionSize, VmProtectToString( memInfo.AllocationProtect, aprotectStr ), VmProtectToString( memInfo.Protect, protectStr ), VmStateToString( memInfo.State, stateStr ), VmTypeToString( memInfo.Type, typeStr ) );
//
// Advance to the next block.
//
address += memInfo.RegionSize; }
} // DECLARE_API( vmmap )
|