Five bugs identified and patched in retail Asheron's Call client: - v3b: palette refcount over-increment (3-byte NOP at two sites) - v5: RenderSurface PurgeResource no-op stub (vtable slot 2 thunk) - v11: two dangling-pointer crash guards (NULL-check + reorder) - v14: CEnvCell::Destroy ClipPlaneList leak (18-byte JMP to cleanup thunk) - v22: unpacker stale-pointer SEH guard (whole-function __try/__except) All five ship in leakfix.dll (117 KB, SHA d282f23c…) which is loaded by acclient.exe at process start via PE import table patching by tools/install_leakfix.py. Controlled 15-client fleet soak: unpatched control died at 26h with palette exhaustion; all 14 patched clients survived past that point and reached ≥5-day uptime. Residual ~15 MB/h growth traced to d3d9.dll's internal slab allocator (260KB surface backing buffers retained after Release). See REPORT.md §10 for the full investigation; conclusion is that it's unfixable from outside d3d9. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
63 lines
2.5 KiB
PowerShell
63 lines
2.5 KiB
PowerShell
# take_snapshot.ps1
|
|
# One-shot umdh snapshot helper. Auto-finds acclient PID and the next snap_NNN filename.
|
|
#
|
|
# Usage:
|
|
# take_snapshot.ps1 -PhaseDir artifacts\phase1
|
|
#
|
|
# Requires: gflags +ust on acclient.exe (one-time, see Phase 1 step 1),
|
|
# _NT_SYMBOL_PATH set to the directory containing acclient.pdb.
|
|
|
|
param(
|
|
[Parameter(Mandatory=$true)][string]$PhaseDir,
|
|
[int]$ProcessId = 0, # 0 = auto-discover via Get-Process
|
|
[string]$UmdhExe = "C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\umdh.exe",
|
|
[int]$TimeoutSec = 600 # umdh on a fat 32-bit AC client can take 60-200s
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
if (-not (Test-Path $PhaseDir)) { New-Item -ItemType Directory -Path $PhaseDir -Force | Out-Null }
|
|
if (-not (Test-Path $UmdhExe)) { throw "umdh.exe not found at $UmdhExe" }
|
|
if (-not $env:_NT_SYMBOL_PATH) { $env:_NT_SYMBOL_PATH = "C:\Users\acbot\leakhunt\pdb" }
|
|
|
|
if ($ProcessId -eq 0) {
|
|
$procs = Get-Process -Name acclient -ErrorAction SilentlyContinue
|
|
if (-not $procs) { throw "acclient.exe not running" }
|
|
if ($procs.Count -gt 1) { throw "multiple acclient.exe instances; specify -ProcessId" }
|
|
$ProcessId = $procs[0].Id
|
|
}
|
|
|
|
# Find next snap_NNN filename
|
|
$existing = Get-ChildItem -Path $PhaseDir -Filter "snap_*.txt" -ErrorAction SilentlyContinue |
|
|
ForEach-Object { [int]([regex]::Match($_.BaseName, '\d+').Value) }
|
|
$next = if ($existing) { ($existing | Measure-Object -Maximum).Maximum + 1 } else { 1 }
|
|
$Out = Join-Path $PhaseDir ("snap_{0:D3}.txt" -f $next)
|
|
|
|
Write-Host "[$(Get-Date -Format HH:mm:ss)] umdh -p:$ProcessId -f:$Out"
|
|
|
|
$argList = @("-p:$ProcessId", "-f:$Out")
|
|
$p = Start-Process -FilePath $UmdhExe -ArgumentList $argList -NoNewWindow -PassThru -RedirectStandardError "$Out.err"
|
|
if (-not $p.WaitForExit($TimeoutSec * 1000)) {
|
|
Stop-Process -Id $p.Id -Force
|
|
throw "umdh timed out after $TimeoutSec s"
|
|
}
|
|
|
|
if (-not (Test-Path $Out)) { throw "umdh produced no output (exit $($p.ExitCode))" }
|
|
$size = (Get-Item $Out).Length
|
|
|
|
# Pull the leading metadata + a quick stats summary
|
|
$head = (Get-Content $Out -TotalCount 6) -join "`n"
|
|
$totalAllocs = (Select-String -Path $Out -Pattern '^\d+ bytes \+' -AllMatches).Matches.Count
|
|
|
|
Write-Host ("[$(Get-Date -Format HH:mm:ss)] OK: {0} ({1:N0} bytes; {2} alloc-stack records)" -f $Out, $size, $totalAllocs)
|
|
Write-Host "--- head ---"
|
|
Write-Host $head
|
|
|
|
# Emit metadata for the orchestrator
|
|
[pscustomobject]@{
|
|
Path = $Out
|
|
SizeBytes = $size
|
|
Records = $totalAllocs
|
|
Pid = $ProcessId
|
|
Timestamp = (Get-Date).ToString("o")
|
|
}
|