diff options
Diffstat (limited to 'src/ToolBox/SOS/Strike/vm.cpp')
-rw-r--r-- | src/ToolBox/SOS/Strike/vm.cpp | 732 |
1 files changed, 732 insertions, 0 deletions
diff --git a/src/ToolBox/SOS/Strike/vm.cpp b/src/ToolBox/SOS/Strike/vm.cpp new file mode 100644 index 0000000000..e7e5701fc6 --- /dev/null +++ b/src/ToolBox/SOS/Strike/vm.cpp @@ -0,0 +1,732 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// ==++== +// + +// +// ==--== +/*++ + +Module Name: + + vm.cxx + +Abstract: + + This module contains an NTSD debugger extension for dumping various + virtual memory statistics. + +Revision History: + +--*/ + +#ifndef FEATURE_PAL +#include <tchar.h> + + +#include "strike.h" +#include "util.h" +#include "gcinfo.h" +#include "disasm.h" +#include <dbghelp.h> + +#include "corhdr.h" +#include "cor.h" +#include "dacprivate.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_HEAD "%-7s %*s %*s %*s %*s %*s\n" +#define PRINTF_FORMAT "%-7s %*sK %*sK %*sK %*s %*sK\n" + +#define CCH_ULONGLONG_COMMAS _countof("18,446,744,073,709,551,616") +#define CCH_ULONGLONG_MINIMUM_COMMAS (CCH_ULONGLONG_COMMAS - 3) +#define CCH_ULONGLONG_BLOCKCOUNT_COMMAS sizeof("1,000,000") + + +// +// 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_ecount (CCH_ULONGLONG_COMMAS) 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' + (CHAR) digit; + + count--; + if( count == 0 ) { + count = 3; + needComma = TRUE; + } + + } + + length = (INT)(((size_t)p1) - ((size_t)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 + ) +{ + ExtOut( + PRINTF_FORMAT_HEAD, + "TYPE", + CCH_ULONGLONG_MINIMUM_COMMAS, + "MINIMUM", + CCH_ULONGLONG_COMMAS, + "MAXIMUM", + CCH_ULONGLONG_COMMAS, + "AVERAGE", + CCH_ULONGLONG_BLOCKCOUNT_COMMAS, + "BLK COUNT", + CCH_ULONGLONG_COMMAS, + "TOTAL" + ); + + ExtOut( + PRINTF_FORMAT_HEAD, + "~~~~", + CCH_ULONGLONG_MINIMUM_COMMAS, + "~~~~~~~", + CCH_ULONGLONG_COMMAS, + "~~~~~~~", + CCH_ULONGLONG_COMMAS, + "~~~~~~~", + CCH_ULONGLONG_BLOCKCOUNT_COMMAS, + "~~~~~~~~~", + CCH_ULONGLONG_COMMAS, + "~~~~~" + ); + +} // PrintVmStatsHeader + +#define BYTES_TO_K(x) (x/1024) + +VOID +PrintIndividualStat( + ___in __in_z IN PSTR Name, + IN PINDIVIDUAL_STAT Stat + ) +{ + SIZE_T average; + SIZE_T minsize; + CHAR minStr[CCH_ULONGLONG_COMMAS]; + CHAR maxStr[CCH_ULONGLONG_COMMAS]; + CHAR avgStr[CCH_ULONGLONG_COMMAS]; + CHAR countStr[CCH_ULONGLONG_COMMAS]; + CHAR totalStr[CCH_ULONGLONG_COMMAS]; + + if( Stat->BlockCount == 0 ) { + average = 0; + minsize = 0; + } else { + average = Stat->TotalSize / Stat->BlockCount; + minsize = Stat->MinimumSize; + } + + ExtOut( + PRINTF_FORMAT, + Name, + CCH_ULONGLONG_MINIMUM_COMMAS, + ULongLongToString( + (ULONGLONG)BYTES_TO_K(minsize), + minStr + ), + CCH_ULONGLONG_COMMAS, + ULongLongToString( + (ULONGLONG)BYTES_TO_K(Stat->MaximumSize), + maxStr + ), + CCH_ULONGLONG_COMMAS, + ULongLongToString( + (ULONGLONG)BYTES_TO_K(average), + avgStr + ), + CCH_ULONGLONG_BLOCKCOUNT_COMMAS, + ULongLongToString( + (ULONGLONG)Stat->BlockCount, + countStr + ), + CCH_ULONGLONG_COMMAS, + ULongLongToString( + (ULONGLONG)BYTES_TO_K(Stat->BlockCount * average), + totalStr + ) + ); + +} // PrintIndividualStat + + +VOID +PrintVmStats( + ___in __in_z IN PSTR Name, + IN PVM_STATS Stats + ) +{ + ExtOut( "%s:\n", Name ); + + PrintIndividualStat( "Small", &Stats->Small ); + PrintIndividualStat( "Medium", &Stats->Medium ); + PrintIndividualStat( "Large", &Stats->Large ); + PrintIndividualStat( "Summary", &Stats->Summary ); + + ExtOut( "\n" ); + +} // PrintVmStats + +PSTR +VmProtectToString( + IN DWORD Protect, + __out_ecount(capacity_Buffer) OUT PSTR Buffer, + size_t capacity_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_s(Buffer,capacity_Buffer, "|" ); + } + strcat_s( Buffer, capacity_Buffer, mask->Name ); + } + } + + if( Protect != 0 ) { + if( Buffer[0] != '\0' ) { + strcat_s( Buffer, capacity_Buffer, "|" ); + } + size_t len_Buffer = strlen(Buffer); + size_t cbSizeInBytes = 0; + if (!ClrSafeInt<size_t>::subtraction(capacity_Buffer, len_Buffer, cbSizeInBytes)) + { + ExtOut("<integer underflow>\n"); + return Buffer; + } + sprintf_s( Buffer + len_Buffer, cbSizeInBytes, "%08lx", Protect ); + } + + return Buffer; + +} // VmProtectToString + +PSTR +VmStateToString( + IN DWORD State, + __out_ecount(capacity_Buffer) OUT PSTR Buffer, + size_t capacity_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_s(invalidStr,_countof(invalidStr), "%08lx", State ); + result = invalidStr; + break; + } + + strcpy_s( Buffer, capacity_Buffer, result ); + return Buffer; + +} // VmStateToString + +PSTR +VmTypeToString( + IN DWORD Type, + __out_ecount(capacity_Buffer) OUT PSTR Buffer, + size_t capacity_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_s(invalidStr,_countof(invalidStr), "%08lx", Type ); + result = invalidStr; + break; + } + strcpy_s( Buffer,capacity_Buffer, result ); + return Buffer; + +} // VmTypeToString + +/************************************************************ + * Dump Virtual Memory Info + ************************************************************/ + +void vmstat() + +/*++ + +Routine Description: + + This function is called as an NTSD extension to format and dump + virtual memory statistics. + +Arguments: + +Return Value: + + None. + +--*/ + +{ + + NTSTATUS status; + ULONG64 address; + MEMORY_BASIC_INFORMATION64 memInfo; + VM_STATS freeStats; + VM_STATS reserveStats; + VM_STATS commitStats; + VM_STATS privateStats; + VM_STATS mappedStats; + VM_STATS imageStats; + + // + // Setup. + // + + InitVmStats( &freeStats ); + InitVmStats( &reserveStats ); + InitVmStats( &commitStats ); + InitVmStats( &privateStats ); + InitVmStats( &mappedStats ); + InitVmStats( &imageStats ); + + address = 0; + + // + // Scan the virtual address space. + // + + for( ; ; ) { + status = g_ExtData2->QueryVirtual(address, &memInfo); + + if( !NT_SUCCESS(status) ) { + break; + } + + // + // Interpret the memory state. + // + + SIZE_T regionSize = (SIZE_T) memInfo.RegionSize; + + switch( memInfo.State ) { + + case MEM_FREE: + UpdateVmStats( &freeStats, regionSize ); + break; + + case MEM_RESERVE: + UpdateVmStats( &reserveStats, regionSize ); + break; + + case MEM_COMMIT: + UpdateVmStats( &commitStats, regionSize ); + break; + } + + // + // Interpret the memory type. + // + + switch( memInfo.Type ) { + case MEM_PRIVATE: + UpdateVmStats( &privateStats, regionSize ); + break; + + case MEM_MAPPED: + UpdateVmStats( &mappedStats, regionSize ); + break; + + case MEM_IMAGE: + UpdateVmStats( &imageStats, 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 ) + + +void vmmap() + +/*++ + +Routine Description: + + This function is called as an NTSD extension to format and dump + the debugee's virtual memory address space. + +Arguments: + +Return Value: + + None. + +--*/ + +{ + + NTSTATUS status; + ULONG64 address; + MEMORY_BASIC_INFORMATION64 memInfo; + CHAR protectStr[32]; + CHAR aprotectStr[32]; + CHAR stateStr[16]; + CHAR typeStr[16]; + + // + // Setup. + // + + address = 0; + + ExtOut( + "%-*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( ; ; ) { + + if (IsInterrupt()) + break; + + status = g_ExtData2->QueryVirtual(address, &memInfo); + + if( !NT_SUCCESS(status) ) { + break; + } + + // + // Dump the current entry. + // + + ExtOut( + "%p-%p %p %-13s %-13s %-8s %-8s\n", + SOS_PTR(memInfo.BaseAddress), + SOS_PTR(((ULONG_PTR)memInfo.BaseAddress + memInfo.RegionSize - 1)), + SOS_PTR(memInfo.RegionSize), + VmProtectToString( memInfo.AllocationProtect, aprotectStr, _countof(aprotectStr) ), + VmProtectToString( memInfo.Protect, protectStr, _countof(protectStr) ), + VmStateToString( memInfo.State, stateStr, _countof(stateStr) ), + VmTypeToString( memInfo.Type, typeStr , _countof(typeStr)) + ); + + // + // Advance to the next block. + // + + address += memInfo.RegionSize; + } + +} // DECLARE_API( vmmap ) + +#else + +#include <rotor_pal.h> +#include <assert.h> + +void vmstat() +{ + assert(false); +} + +void vmmap() +{ + + assert(false); +} + +#endif // #ifndef FEATURE_PAL |