718 lines
13 KiB
C++
718 lines
13 KiB
C++
/*++
|
|
|
|
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 )
|
|
|