/plugin marketplace add https://github.com/ADN-DevTech/acad-memory-leak-skill.git
/plugin install acad-memory-leak@acad-memory-leak-skill
/reload-plugins
Verify with /plugin list. Once installed, the skill auto-triggers on prompts like
"check for memory leak", "detect memory leak in AutoCAD", or "validate memory leak fix"
(see SKILL.md for the full trigger list).
A collection of instrumentation scripts and templates for validating memory leaks in AutoCAD or RealDWG C++ projects using real measurement data.
This skill was developed during the investigation of ACD-58279
(AcDbMText::text() GDI handle leak in RealDWG 27.0) and captures the exact toolchain
used to confirm the bug and verify the fix.
leaktest.exe is not a general tool. It is the reproduction executable built from
the repro project (realdwg-mtext-text-leak). Its only job is to reproduce a known
leak in a tight, measurable loop so instrumentation tools can observe it.
When adapting this skill to a new leak:
- Build your own repro executable (or ARX plugin — see
arx/) that triggers the suspected leaking code path in a loop. - Feed that exe path to the scripts in
scripts/in place ofleaktest.exe.
This skill is about AI-assisted instrumentation, not just running scripts:
- Hypothesize — AI reads the symptom (Private Bytes grow ~0.9 MB/call) and
narrows suspect components (
Win32FontHandle, GDI pool). - Instrument — scripts capture Private Bytes, ETW stacks, memory region snapshots.
- Validate — AI interprets results: is growth linear with iterations? which DLL tops the outstanding allocation stack?
- Verify fix — re-run same scripts against patched DLL; compare first/last MB.
Symbols resolve Autodesk DLL stack frames (e.g. acdb26.dll!AcFontManager::getFontMetrics)
to function names in WPA. Without them, WPA shows raw addresses — you can still see
which DLL is allocating (by address range) but not the exact function.
| Audience | Symbol path |
|---|---|
| Autodesk internal | srv*C:\symbols*http://<internal-server>/symbols |
| Public / external | srv*C:\symbols*https://symbols.autodesk.com |
If https://symbols.autodesk.com returns no symbols for your build:
Autodesk may not yet have published stripped PDBs for that release.
This skill is still useful — it confirms the leak exists, sizes it, and
identifies the Autodesk DLL by address range. To get full stack resolution:
Raise a support ticket at https://aps.autodesk.com/get-help and attach the memory leak evidence (Private Bytes CSV + ETL or WPA screenshot). Include the exact DLL version (
Get-Item acdb26.dll | Select VersionInfo). Autodesk support can reproduce internally with full symbols and identify the leaking call site.
| Tool | Purpose | Where |
|---|---|---|
| Windows Performance Toolkit | wpr.exe, wpa.exe for ETW traces |
Windows SDK installer |
| Windows Debugging Tools | gflags.exe for UST |
Windows SDK installer |
xperf.exe |
Legacy heap+kernel trace | Part of WPT |
| Python 3 + matplotlib | Chart generation (plot_leak.py) |
pip install matplotlib |
| Admin PowerShell | ETW and xperf require elevation | — |
Edit config.ps1 first — set paths for your environment:
# RealDWG target
$REALDWG_ROOT = "D:\RealDWG\RD27\RealDWG 2027" # <-- PLACEHOLDER
$REALDWG_EXE = "$REALDWG_ROOT\leaktest.exe" # your repro exe
# AutoCAD target
$ACAD_EXE = "C:\Program Files\Autodesk\AutoCAD 2027\acad.exe" # <-- PLACEHOLDER
$ARX_PLUGIN = "D:\dev\MyLeakRepro\x64\Release\MyLeakRepro.arx" # <-- PLACEHOLDERBest first step. Measures Private Bytes every 200 ms. Confirms leak exists and sizes it.
# Set pauses so poller captures baseline + peak
$env:PAUSE_BEFORE_MS = "3000"
$env:PAUSE_AFTER_MS = "5000"
.\scripts\run_with_metering.ps1 `
-ExePath "D:\RealDWG\RD27\RealDWG 2027\leaktest.exe" `
-Iterations 50 -Mode writeread -Tag leak -OutDir .\evidenceInterpret output:
First Private MB: 10 <- baseline (before loop)
Peak Private MB: 90 <- worst-case
Last Private MB: 88 <- still held at exit = leak confirmed
If Last ~= First → no leak. If Last >> First → leak confirmed.
Run twice: once with leaking code, once with control (no-op or fixed). Compare.
Good when you need heap stack trace and can interactively control start/stop.
REM As Administrator:
memxperfex.bat <PID>
REM Exercise the target, then press any key.
REM Opens combined.etl in WPA automatically.WPA: Memory → Heap Allocations Outstanding → Outstanding Heap Allocations by Stack Sort by Size descending. Top Autodesk frame = leaking site.
Wraps WPR + process launch + Private Bytes metering in one script. Captures VirtualAlloc stacks — finds non-heap pool allocator leaks.
# As Administrator:
.\scripts\run_with_wpa.ps1 `
-ExePath "D:\RealDWG\RD27\RealDWG 2027\leaktest.exe" `
-Iterations 10 -Mode writeread -Tag leak_etw -OutDir .\evidence `
-AcadSymSrv -OpenWpaWPA analysis:
- Memory → VirtualAlloc Commit Lifetime → Outstanding Commits by Stack
- Memory → Heap Allocations Outstanding → Outstanding Heap Allocations by Stack
- Filter
Process = leaktest.exe, sortSizedescending - Top frame inside
acdb*.dll/acpal.dll= allocator call site
Distinguishes heap leak vs VirtualAlloc pool leak. Take two snapshots bracketing the test loop, compare CSVs.
# 1. Start process (ensure PAUSE_BEFORE_MS is set so it doesn't exit)
# 2. Note PID from Task Manager or stdout
.\scripts\dump_regions.ps1 -ProcId <PID> -Tag before -OutDir .\evidence
# 3. Trigger leak iterations
.\scripts\dump_regions.ps1 -ProcId <PID> -Tag after -OutDir .\evidence
# 4. Compare regions_before.csv vs regions_after.csv:
# New large "Private" AllocBase entries = leaked pool region.python scripts/plot_leak.py \
--leak evidence/run_leak.csv \
--control evidence/run_fixed.csv \
--out evidence/compare.png \
--title "AcDbMText::text() — before vs after fix"Use when the leaking API is only reachable from inside a live AutoCAD session (not available in RealDWG standalone).
-
Build
templates/arx/leak_repro_arx.cppas an ObjectARX plugin. Replace<ACAD_SDK_ROOT>in project properties with your ObjectARX SDK path. -
Load in AutoCAD:
APPLOAD -> select MyLeakRepro.arxOr via script:
(command "APPLOAD" "D:\\dev\\MyLeakRepro\\x64\\Release\\MyLeakRepro.arx") (command "LEAKTEST") -
Measure
acad.exememory externally:# Find acad.exe PID first $pid = (Get-Process acad).Id # Option A: snapshot regions before/after LEAKTEST command .\scripts\dump_regions.ps1 -ProcId $pid -Tag before -OutDir .\evidence # (run LEAKTEST in AutoCAD) .\scripts\dump_regions.ps1 -ProcId $pid -Tag after -OutDir .\evidence # Option B: xperf heap trace (admin) — attach to acad.exe PID memxperfex.bat $pid
-
For ETW trace wrapping acad.exe launch:
.\scripts\run_with_wpa.ps1 ` -ExePath "C:\Program Files\Autodesk\AutoCAD 2027\acad.exe" ` -AcadScript "D:\dev\MyLeakRepro\run_leak.scr" ` -Tag acad_leak -OutDir .\evidence -AcadSymSrv -OpenWpa
| Step | Command | Pass condition |
|---|---|---|
| 1. Confirm leak | run_with_metering.ps1 -Tag leak |
Last MB >> First MB |
| 2. Apply fix | Replace patched DLL | — |
| 3. Confirm fix | run_with_metering.ps1 -Tag fixed |
Last MB ≈ First MB |
| 4. Visualize | plot_leak.py --leak ... --control ... |
Fixed line is flat |
| 5. ETL confirm | run_with_wpa.ps1 -Tag fixed_etw |
No outstanding allocs in WPA |
templates/example/ contains a real repro project used to find and verify ACD-58279
(AcDbMText::text() GDI handle leak, RealDWG 27.0).
| File | Purpose |
|---|---|
templates/example/leaktest.cpp |
Minimal C++ repro exe — 5 test modes, env-var controls, ~350 lines |
templates/example/leaktest.vcxproj |
VS 2022 project, $(REALDWG_SDK) placeholder for SDK path |
- Single exe, one leaking call isolated — loop calls the suspect API N times, nothing else
- Modes —
shared/newentity/newdb/readfile/writereadlet you bisect which code path leaks - Env-var controls —
ITERATIONS,CONTENT_REPEATS,CONTENT_PLAIN,USE_RTF— no recompile to vary parameters USE_RTFnegative control — swap the leaking call for an equivalent non-leaking call to confirm the repro is not measuring noisePAUSE_BEFORE_MS/PAUSE_AFTER_MS— pause before/after the loop so the metering script captures baseline and peak
set REALDWG_SDK=D:\RealDWG\RD27\RealDWG 2027
msbuild example\leaktest.vcxproj /p:Configuration=Release /p:Platform=x64Output: %REALDWG_SDK%\leaktest.exe. Copy to the same folder as acdb26.dll.
| Run | Iter | First MB | Peak MB | Last MB |
|---|---|---|---|---|
| Leaked (acpal.dll original) | 300 | 10.3 | 425+ | 425+ |
| Fixed (patched acpal.dll) | 50 | 7.3 | 13.9 | 10.7 |
Root cause: Win32FontHandle destructor in PAL/Win32/font_utilities.cpp created
HFONT via CreateFontIndirect() but never called DeleteObject(). Every call to
AcDbMText::text() on a file-loaded entity orphaned 1–2 GDI kernel objects.
Fix: Deselect font (SelectObject → SYSTEM_FONT), then DeleteObject(mWin32Font)
in destructor. Add releaseFont() for ownership transfer to HT_Font_Descriptor cache.
acad-memory-leak-skill\
├── .claude-plugin\
│ ├── plugin.json Claude Code plugin manifest
│ └── marketplace.json Marketplace listing (single-plugin repo)
├── README.md this file
├── SKILL.md Claude Code skill — triggers + quick start
├── config.ps1 path configuration (edit before use)
├── core\
│ ├── workflow.md full instrumentation workflow + troubleshooting
│ └── output-format.md how to interpret and present results
├── scripts\
│ ├── run_with_metering.ps1 Level 1: Private Bytes metering, no elevation
│ ├── memxperfex.bat Level 2: xperf heap trace, interactive
│ ├── run_with_wpa.ps1 Level 3: ETW/WPA automated trace, elevation
│ ├── dump_regions.ps1 Level 4: VirtualQueryEx region snapshot
│ └── plot_leak.py Chart: leak vs control/fixed curve
└── templates\
├── example\
│ ├── leaktest.cpp Real repro exe (ACD-58279, RealDWG 27.0)
│ └── leaktest.vcxproj VS 2022 project — set $(REALDWG_SDK) before building
└── arx\
└── leak_repro_arx.cpp AutoCAD ARX plugin template
Built with Claude Skill Creator. Verified by a human on a real leak (ACD-58279).