From claude-review on #80.
SessionKey.target comes from engine.target_identity(), unique per owned DebugEngine and bumped again on end_session(). LayoutCache is a static map pruned only by invalidate(), reached solely from the WinDbg extension's notify callbacks — never by a host using the query API directly.
So the primary documented path (DebugEngine::new() → query::find_tag() → end_session()) inserts a PoolLayout per engine and per teardown that is never reused or freed: unbounded growth plus repeated PDB symbol re-resolution.
This is the same failure I removed for the refresh case in the same PR (see the comment at query.rs:184-190) and reintroduced via target. Fixing the staleness bug created a leak on the other side.
Options: prune LayoutCache by kernel_base rather than whole SessionKey; give it a bound/LRU; or key layout on kernel_base alone while keeping target on the snapshot cache only — layouts depend on the image, not the target instance, which is probably the real answer.
From claude-review on #80.
SessionKey.targetcomes fromengine.target_identity(), unique per ownedDebugEngineand bumped again onend_session().LayoutCacheis a static map pruned only byinvalidate(), reached solely from the WinDbg extension's notify callbacks — never by a host using the query API directly.So the primary documented path (
DebugEngine::new()→query::find_tag()→end_session()) inserts aPoolLayoutper engine and per teardown that is never reused or freed: unbounded growth plus repeated PDB symbol re-resolution.This is the same failure I removed for the refresh case in the same PR (see the comment at query.rs:184-190) and reintroduced via
target. Fixing the staleness bug created a leak on the other side.Options: prune
LayoutCacheby kernel_base rather than wholeSessionKey; give it a bound/LRU; or key layout onkernel_basealone while keepingtargeton the snapshot cache only — layouts depend on the image, not the target instance, which is probably the real answer.