使用内核调试程序查找内核模式内存泄漏
内核调试器确定内核模式内存泄漏的精确位置。
启用池标记
必须先使用 GFlags 启用池标记。 GFlags 包含在 Windows 调试工具中。 启动 GFlags,选择“系统注册表”选项卡,检查“启用池标记”框,然后选择“应用”。 必须重启 Windows 才能使此设置生效。
在 Windows Server 2003 及更高版本的 Windows 上,始终启用池标记。
确定泄漏的池标记
若要确定哪个池标记与泄漏相关联,通常最简单的方法是使用 PoolMon 工具执行此步骤。 有关详细信息,请参阅 使用 PoolMon 查找 Kernel-Mode 内存泄漏。
或者,可以使用内核调试器查找与大型池分配关联的标记。 为此,请遵循以下过程:
使用 !poolused 扩展。 包括标志“4”以按分页内存使用对输出进行排序:
kd> !poolused 4 Sorting by Paged Pool Consumed Pool Used: NonPaged Paged Tag Allocs Used Allocs Used Abc 0 0 36405 33930272 Tron 0 0 552 7863232 IoN7 0 0 10939 998432 Gla5 1 128 2222 924352 Ggb 0 0 22 828384
确定哪个池标记与最大内存使用率相关联。 在此示例中,使用标记“Abc”的驱动程序使用的内存最多,几乎为 34 MB。 因此,内存泄漏最有可能在此驱动程序中。
查找泄漏
确定与泄漏关联的池标记后,请按照以下步骤查找泄漏本身:
使用 ed (Enter Values) 命令修改全局系统变量 PoolHitTag 的值。 每当使用与值匹配的池标记时,此全局变量会导致调试器中断。
将 PoolHitTag 设置为你怀疑为内存泄漏源的标记。 应指定模块名称“nt”以加快符号解析速度。 标记值必须以 little-endian 格式输入, (即向后) 。 由于池标记始终为四个字符,因此此标记实际上是 A-b-c 空间,而不仅仅是 A-b-c。 因此,请使用以下命令:
kd> ed nt!poolhittag ' cbA'
若要验证 PoolHitTag 的当前值,请使用 db (Display Memory) 命令:
kd> db nt!poolhittag L4 820f2ba4 41 62 63 20 Abc
每次使用 标记 Abc 分配或释放池时,调试器都会中断。 每次调试器中断其中一个分配或免费操作时,请使用 kb (显示堆栈回溯) 调试器命令查看堆栈跟踪。
使用此过程,可以确定驻留在内存中的哪些代码使用标记 Abc 过度分配池。
若要清除断点,请将 PoolHitTag 设置为零:
kd> ed nt!poolhittag 0
如果有多个不同位置分配了此标记的内存,并且这些内存位于已编写的应用程序或驱动程序中,则可以更改源代码,以便对每个分配使用唯一标记。
如果无法重新编译程序,但想要确定代码中导致泄漏的多个可能位置之一,则可以在每个位置取消组合代码,并使用调试器编辑驻留在内存中的此代码,以便每个实例使用不同的 (和以前未使用的) 池标记。 然后,允许系统运行几分钟或更长的时间。 经过一段时间后,使用调试器再次中断,并使用 !poolfind 扩展查找与每个新标记关联的所有池分配。