Scripts/spike-etw-share.ps1
|
#Requires -Version 5.1 # Run on the file server (powershell.exe 5.1, Administrator). # Phase 0 gate: prove SMB2 Response Read LengthRead tracks a large sequential copy. # # Abort: event 111 LengthRead stays near 0 while _listen445 / NIC Out show the copy. # Go: LengthRead is in the same ballpark as TCP 445 (not exact). # # Sequence: # 1. powershell.exe -File spike-etw-share.ps1 -DumpKeywords # 2. On the CLIENT: net use * /delete (or disconnect the share), then net use again # so TreeConnect events fire. Warm sessions have no event 4/104/600. # 3. powershell.exe -File spike-etw-share.ps1 -Seconds 180 # 4. During those 180s, copy the large file from \\FILESERVER\Storage # 5. Compare LengthReadSum to _listen445 / NIC Out. Keep the keyword dump. [CmdletBinding(DefaultParameterSetName = 'Help')] param( [Parameter(ParameterSetName = 'Dump')] [switch]$DumpKeywords, [Parameter(ParameterSetName = 'Run')] [int]$Seconds = 120, [Parameter(ParameterSetName = 'Logman')] [switch]$LogmanStart, [Parameter(ParameterSetName = 'LogmanStop')] [switch]$LogmanStop ) $ErrorActionPreference = 'Continue' $provider = 'Microsoft-Windows-SMBServer' $session = 'SMBeatEtwSpike' $etl = Join-Path $env:TEMP 'SMBeat-etw-spike.etl' function Show-SMBeatEtwSpikeHelp { Write-Host 'Usage (Administrator, on the file server):' Write-Host ' .\spike-etw-share.ps1 -DumpKeywords' Write-Host ' .\spike-etw-share.ps1 -Seconds 180 # then remount + copy' Write-Host ' .\spike-etw-share.ps1 -LogmanStart # file session if in-process is not wanted' Write-Host ' .\spike-etw-share.ps1 -LogmanStop' Write-Host '' Write-Host 'Gate: remount the share first (new TreeConnect), then copy ~5.5 GiB.' Write-Host 'Abort if LengthRead ~ 0 while _listen445 shows the copy.' } function Dump-SMBeatEtwKeywords { Write-Host '=== wevtutil gp Microsoft-Windows-SMBServer /ge /gm ===' Write-Host 'Record keyword names and masks for Performance vs Analytic.' Write-Host 'Do not guess masks from blogs. Event 40000 (packet bytes) must stay out of the collector.' Write-Host '' & wevtutil.exe gp $provider /ge /gm } if ($PSCmdlet.ParameterSetName -eq 'Help') { Show-SMBeatEtwSpikeHelp return } if ($DumpKeywords) { Dump-SMBeatEtwKeywords return } if ($LogmanStart) { if (Test-Path -LiteralPath $etl) { Remove-Item -LiteralPath $etl -Force -ErrorAction SilentlyContinue } & logman.exe stop $session -ets 2>$null | Out-Null & logman.exe create trace $session -p $provider 0xffffffffffffffff 5 -o $etl -ets Write-Host ('Started {0} -> {1}' -f $session, $etl) Write-Host 'Remount the share, copy the file, then: .\spike-etw-share.ps1 -LogmanStop' return } if ($LogmanStop) { & logman.exe stop $session -ets Write-Host ('Stopped. ETL: {0}' -f $etl) Write-Host 'Inspect with tracerpt or Get-WinEvent -Path if the ETL is readable.' Write-Host 'Count events 4, 104, 111, 112, 600. Sum 111 LengthRead vs _listen445.' return } $modRoot = Split-Path -Parent $PSScriptRoot $manifest = Join-Path $modRoot 'SMBeat.psd1' if (-not (Test-Path -LiteralPath $manifest)) { Write-Host 'SMBeat.psd1 not found next to Scripts. Copy this repo onto the server or Install-Module SMBeat.' return } Import-Module $manifest -Force & (Get-Module SMBeat) { Initialize-SMBeatEtwShareNative } | Out-Null $err = [SMBeat.Native.EtwShare]::Start() if ($err -ne 0) { Write-Host ('FAIL: ETW session start returned {0}. Run as Administrator. Stop leftover SMBeat-SMBServer if needed.' -f $err) return } Write-Host ('ETW session running for {0}s. Remount Storage, then copy the large file NOW.' -f $Seconds) Start-Sleep -Seconds $Seconds $snap = [SMBeat.Native.EtwShare]::Snapshot() [SMBeat.Native.EtwShare]::Stop() Write-Host '' Write-Host '=== spike result ===' Write-Host ('Event 4 TreeConnect request : {0}' -f $snap.Count4) Write-Host ('Event 104 TreeConnect response: {0}' -f $snap.Count104) Write-Host ('Event 111 Response Read : {0}' -f $snap.Count111) Write-Host ('Event 112 Response Write : {0}' -f $snap.Count112) Write-Host ('Event 600 TreeConnect alloc : {0}' -f $snap.Count600) Write-Host ('LengthRead sum : {0} bytes ({1:N3} GiB)' -f $snap.LengthReadTotal, ($snap.LengthReadTotal / 1GB)) Write-Host ('LengthWritten sum : {0} bytes ({1:N3} GiB)' -f $snap.LengthWrittenTotal, ($snap.LengthWrittenTotal / 1GB)) Write-Host ('Mapped shares : {0}' -f $snap.MappedTrees) Write-Host ('Unknown trees : {0}' -f $snap.UnknownTrees) Write-Host ('LostEvents : {0}' -f $snap.LostEvents) Write-Host ('LastError : {0}' -f $snap.LastError) Write-Host '' Write-Host 'Shares flushed this window:' [SMBeat.Native.EtwShare]::Flush() | Format-Table Name, Sent, Received -AutoSize Write-Host '' Write-Host 'Compare LengthRead sum to the last _listen445 flush (Scripts\check.ps1).' Write-Host 'ABORT if LengthRead is ~0 while TCP 445 shows the copy. Do not ship a fake share source.' Write-Host 'GO if LengthRead is near the copy size (headers and other sessions explain a gap).' Write-Host 'Warm sessions without a new net use land on (unknown) until reconnect.' |