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>
48 lines
1.9 KiB
Text
48 lines
1.9 KiB
Text
$$ cdb scripting template — attach to acclient.exe, set non-blocking
|
|
$$ breakpoints on suspected allocator functions, count hits, auto-detach.
|
|
$$
|
|
$$ Usage:
|
|
$$ cdb.exe -pn acclient.exe -cf <this-file> -logo <output.log>
|
|
$$
|
|
$$ Or attach by PID:
|
|
$$ cdb.exe -p <pid> -cf <this-file> -logo <output.log>
|
|
$$
|
|
$$ Tips:
|
|
$$ - `gc` = "go conditional" — continue without breaking the debuggee
|
|
$$ - `qd` = "quit detached" — leaves the debuggee running, exits cdb
|
|
$$ - Counter $t0..$t19 are persistent across breakpoint hits
|
|
$$ - Don't put `;` inside breakpoint action strings without escaping —
|
|
$$ cdb's command parser splits on `;` even inside actions.
|
|
|
|
.logopen /t leak-trace.log
|
|
|
|
$$ Symbol path — local PDB only, no symbol server.
|
|
.sympath C:\leak-hunt\pdb
|
|
.symopt+ 0x40
|
|
.reload /f acclient.exe
|
|
|
|
$$ Verify the symbol we care about resolves (replace as needed)
|
|
$$ x acclient!CChatManager::AddLine
|
|
|
|
$$ ============================================================
|
|
$$ Counters
|
|
$$ ============================================================
|
|
r $t0 = 0 $$ alloc-site hits
|
|
r $t1 = 0 $$ free-site hits
|
|
r $t2 = 0 $$ unmatched (leak candidate) hits
|
|
|
|
$$ ============================================================
|
|
$$ Breakpoint pattern: increment counter, log every Nth, auto-detach at M
|
|
$$ ============================================================
|
|
$$ Replace <ALLOC_FN> and <FREE_FN> with the suspected function names.
|
|
|
|
bp acclient!<ALLOC_FN> "r $t0 = @$t0 + 1; .if (@$t0 % 1000 == 0) { .printf \"alloc hits: %d\\n\", @$t0 }; .if (@$t0 >= 100000) { .printf \"AUTO-DETACH at %d\\n\", @$t0; qd } .else { gc }"
|
|
|
|
bp acclient!<FREE_FN> "r $t1 = @$t1 + 1; .if (@$t1 % 1000 == 0) { .printf \"free hits: %d\\n\", @$t1 }; gc"
|
|
|
|
$$ Optional: dump `this` struct on first hit
|
|
$$ bp acclient!<ALLOC_FN> "r $t0 = @$t0 + 1; .if (@$t0 == 1) { dt acclient!<ClassName> @ecx }; gc"
|
|
|
|
g
|
|
|
|
.logclose
|