So, from what I can gather, vmmap
seems to be doing its own math (it does install a kernell mode driver to function properly) and not using the APIs in win32_process
.
You can kind of recalculate what it is doing in the GUI from the CLI using the outfile <filename>
arg which generates a .mmp
file which is just XML.
Actually sleuthing contributions to commit size does not seem to be a trivial task as "sharable" can either be 1 process's problem or many depending. Private Data is most often where you'll have leaks, but you if you're just concerned with size you have to consider image, address window extensions, AWE, etc.
vmmap
is a great tool but it does appear to be written for profiling the performance of a single process in mind.
The YouTube talk by Mark Russinovich (creator of sysinternals) is pretty instructive: Mysteries of Memory Management Revealed,with Mark Russinovich (Part 1). As others have mentioned, the Windows Internals book and Windows Troubleshooting books (also by Russinovich) are helpful too.
It is pretty frustrating this information isn't readily available within the OS.
example with notepad
This example will export a single snapshot from vmmap
for notepad and read it back. It then adds up the Commit
value from certain regions. This number seems to approximate what is shown the GUI.
vmmap_parse.ps1
Open a copy of notepad first. You can also open up vmmap
and examine the process to compare.
function New-VmMapLogFile {
#Requires -RunAsAdministrator
[CmdletBinding()]
param(
[Parameter(Mandatory=$True,ValueFromPipelineByPropertyName=$True)]
[Alias('id')]
[int32[]]
$Ids,
[Parameter(Mandatory=$False)]
[ValidateNotNullOrEmpty()]
[string]
$DestinationPath = (Join-Path $PWD.Path "vmmap_$(get-date -f 'yyyyMMdd_HHmmss')"),
[Parameter(Mandatory=$False)]
[ValidateNotNullOrEmpty()]
[string]
$VmMapPath = 'c:\sysinternals\vmmap.exe'
)
BEGIN {
if(-not (Test-Path -Path $DestinationPath)) {
New-Item -Path $DestinationPath -ItemType 'container' -ea 1 | out-null
}
if(-not (Test-Path -Path $VmMapPath -PathType 'leaf')) {
throw "vmmap missing"
}
}
PROCESS {
foreach($id in $Ids) {
$proc = $null
$proc = Get-Process -Id $id -ea 0
if(-not $proc) {
write-warning "cannot find PID $($id)"
continue
}
[System.IO.FileInfo]$outputfile = Join-Path $DestinationPath "$($id).mmp"
write-verbose "creating $($outputfile.FullName)"
$vmmapProcArgs = @{
Wait = $True
FilePath = $VmMapPath
ArgumentList = @(
'-accepteula',
'-p',$id,
'outputfile',$outputfile.FullName
)
WindowStyle = 'hidden'
}
Start-Process @vmmapProcArgs
if(@(0) -notcontains $LASTEXITCODE) {
write-warning "vmmap PID $($id) returned: $($LASTEXITCODE)"
}
}
}}
function Import-VMMapLogFiles {
param(
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[ValidateScript({ Test-Path -Path $_ -PathType 'container' })]
[string]
$DirectoryPath
)
write-verbose "Checking dir $($DirectoryPath) for *.mmp files"
$mmpFiles = @(Get-ChildItem -Path $DirectoryPath -File -Filter '*.mmp')
write-verbose "$($mmpFiles.Count) files found"
foreach($mmpFile in $mmpFiles) {
$objProps = [ordered]@{
PID = $vmmap.root.PID
Process = $vmmap.root.Process
}
# read XML file
[xml]$vmmap = Get-Content -Path $mmpFile.FullName -Raw
$regions = @($vmmap.root.Snapshots.Snapshot.MemoryRegions.Region)
$regionByType = $regions | Group-Object -Property 'Type'
# examine regions
$totalCommitKb = 0
foreach($r in $regionByType) {
$keyPrefix = ($r.Name.ToLower() -replace '\s+','_')
# filter regions
$validRegions = @()
switch($r.Name) {
'Private Data' {
# 4096 regions only
$validRegions = @($R.Group | Where-Object { $_.Size - 4096 })
}
default {
$validRegions = @($R.Group)
}
}
# commited sum
$commitKeyRegex = 'image|shareable|private_data|heap|stack'
$commitRegions = @($validRegions | where-object { $_.Commit -gt 0 })
$commitBytes = ($commitRegions | Select-Object -ExpandProperty Commit | Measure-Object -Sum | Select-Object -ExpandProperty Sum)
$commitKb = $commitBytes / 1KB
$commitRounded = [math]::Round($commitKb, 2)
if($keyPrefix -match $commitKeyRegex) {
$totalCommitKb += $commitRounded
}
# size sum
$sizeBytes = 0
$sizeBytes = $validRegions | Select-Object -ExpandProperty Size | Measure-Object -Sum | Select-Object -ExpandProperty Sum
$sizeKb = $sizeBytes / 1KB
$sizeKbRounded = [math]::Round($sizeKb, 2)
# add properties
$objProps["$($keyPrefix)_kb"] = $sizeKbRounded
$objProps["$($keyPrefix)_commit_kb"] = $commitRounded
}
$objProps['commit_total_kb'] = $totalCommitKb
[pscustomobject]$objProps
}
}
$tmpDir = Join-Path $PSScriptRoot 'notepad'
Remove-Item -Path $tmpDir -recurse -force -ea 0 | out-null
get-process -name 'notepad' -ea 1 | New-VmMapLogFile -DestinationPath $tmpDir
Import-VMMapLogFiles -Verbose -DirectoryPath $tmpDir
results
from gui

script output
PID : 15320
Process : "C:\Windows\System32\notepad.exe"
free_kb : 135287120640
free_commit_kb : 0
private_data_kb : 4238760
private_data_commit_kb : 316
unusable_kb : 2904
unusable_commit_kb : 0
thread_stack_kb : 512
thread_stack_commit_kb : 80
heap_(shareable)_kb : 64
heap_(shareable)_commit_kb : 64
shareable_kb : 2147510224
shareable_commit_kb : 46412
mapped_file_kb : 23040
mapped_file_commit_kb : 23040
heap_(private_data)_kb : 4516
heap_(private_data)_commit_kb : 636
image_(aslr)_kb : 52720
image_(aslr)_commit_kb : 52720
commit_total_kb : 123268