wenmo8 发布的文章

我最近在处理一个问题,服务器在几天内耗尽了NonPagedPool。通常,我们只需要使用像PoolMon这样的工具来识别有问题的pool标记,然后使用本文中的方法找到使用该pool标记的驱动程序。然而,让这个案例有趣的是pool标记,而且我们无法使用常规方法识别驱动程序。你一会儿就会明白我的意思了。支持给我提供了服务器处于状态时的内核转储,这就是我发现的。
让我们先看看虚拟内存的使用情况:

2: kd> !vm*** Virtual Memory Usage ***Physical Memory:851420 ( 3405680Kb)

Page File: \
??\C:\pagefile.sys

Current:
3584000 Kb Free Space: 3568552Kb

Minimum:
3584000 Kb Maximum: 3584000Kb

Available Pages:
573277 ( 2293108Kb)

ResAvail Pages:
800628 ( 3202512Kb)

Locked IO Pages:
1067 ( 4268Kb)

Free System PTEs:
25102 ( 100408Kb)

Free NP PTEs:
335 ( 1340Kb)

Free Special NP:
0 ( 0Kb)

Modified Pages:
22 ( 88Kb)

Modified PF Pages:
22 ( 88Kb)

NonPagedPool Usage:
31369 ( 125476Kb) ß Very high

NonPagedPool Max:
31986 ( 127944Kb)********** Excessive NonPaged Pool Usage *****PagedPool0 Usage: 19071 ( 76284Kb)

PagedPool
1 Usage: 735 ( 2940Kb)

PagedPool
2 Usage: 747 ( 2988Kb)

PagedPool
3 Usage: 720 ( 2880Kb)

PagedPool
4 Usage: 746 ( 2984Kb)

PagedPool Usage:
22019 ( 88076Kb)

PagedPool Maximum:
38912 ( 155648Kb)********** 3 pool allocations have failed **********

今天我想谈谈一个常见的错误,我们在日常工作钟许多案例中都看到了这一点。它涉及到驱动程序占用内核堆栈上的过多空间,从而导致内核堆栈溢出,然后将通过以下错误检查之一使系统崩溃:

1. STOP 0x7F: UNEXPECTED_KERNEL_MODE_TRAP当参数1设置为EXCEPTION_DOUBLE_FAULT时,这是由于覆盖内核堆栈的末尾而导致的。

2. STOP 0x1E: KMODE_EXCEPTION_NOT_HANDLED, 0x7E: SYSTEM_THREAD_EXCEPTION_NOT_HANDLED, or 0x8E: KERNEL_MODE_EXCEPTION_NOT_HANDLED, 异常代码为STATUS_ACCESS_VIOLATION,表示内存访问冲突。

3. STOP 0x2B: PANIC_STACK_SWITCH, 这通常发生在内核模式驱动程序使用太多堆栈空间时。

内核堆栈概述

系统中的每个线程都分配有一个内核模式堆栈。运行在任何内核模式线程(无论是系统线程还是驱动程序创建的线程)上的代码都使用该线程的内核模式堆栈,除非该代码是DPC,在这种情况下,它在某些平台上使用处理器的DPC堆栈。堆栈负增长。这意味着堆栈的开始(底部)的地址高于堆栈的结束(顶部)。例如,让我们保持堆栈的开头是0x80f1000,这是堆栈指针(ESP)指向的位置。如果将一个DWORD值推送到堆栈上,它的地址将是0x80f0ffc。下一个DWORD值将存储在0x80f0ff8,以此类推,直到分配的堆栈的限制(顶部)。堆栈顶部以保护页为边界,以检测溢出。

内核模式堆栈的大小因不同的硬件平台而异。例如:

  • 在基于x86的平台上,内核模式堆栈是12K。
  • 在基于x64的平台上,内核模式堆栈为24K(基于x64的平台包括使用AMD64体系结构的处理器和使用Intel EM64T体系结构的处理器的系统)。
  • 在基于安腾的平台上,内核模式堆栈是32K,有一个32K后备存储。(如果处理器的寄存器文件中的寄存器用完,它将使用后备存储器来保存寄存器的内容,直到分配函数返回为止。这不会直接影响堆栈分配,但操作系统在基于安腾的平台上使用的寄存器比在其他平台上使用的寄存器多,这使得驱动程序可以使用的堆栈相对更多。)

上面列出的堆栈大小是系统施加的硬限制,所有驱动程序都需要保守地使用空间,以便它们能够共存

我每天都看很多转储文件。在这种情况下,我喜欢充分利用windbg可定制的外观和感觉。实际上,我在DMP文件和CMD文件之间有一个关联设置,每当我双击一个转储文件时,这个文件就会加载我自定义的颜色工作区。我喜欢有彩色源代码和调试命令窗口输出的黑色背景。
下面是我典型的调试会话的快照。

 

 

下面告诉你的设置方法。


  1. 创建以下CMD文件并将其放入您的路径中。在我的系统里叫D.CMD

    echooff
    Title kd.exe
    -z %1C:
    CD\debuggers
    start C:\Debuggers\windbg.exe
    -z %1 -W color

我想谈谈我们经常处理的一个常见问题。我们的任务通常是在用户模式进程/应用程序中查找哪些函数正在使用CPU。通常,用户会发现一个应用程序使用的CPU比他们预期的要多,这可能会影响整个系统的性能和响应能力。

对于这个练习,我编写了一些人为的示例代码,称为EATCPU。它包含在博客文章的底部。任务管理器的下图显示EATCPU消耗41%的CPU时间。客户或用户可能会告诉您,这种情况“通常”不会发生。问“正常”是什么总是好的。在这种情况下,我们认为正常值为~10%。

 

最好的情况是对运行在高CPU级别的进程进行实时调试。如果您有幸拥有一个客户/用户,该客户/用户允许您进行远程调试,并且问题在需要时重现,那么您可以采取以下操作。

您需要安装Windows调试工具,并设置符号路径。如果有可能,请获取正在调试的应用程序的符号。我们假设你是支持上述计划的专家。如果是内部编写的,从开发者那里获取符号。如果是来自第三方的,供应商可能愿意为您提供他们产品的符号。

下一件事是附用windbg.exe加进程。

在debuggers目录中,输入TLIST,这将列出您的进程。获取进程id,然后运行WinDBG.EXE –p PROCESSID,或者如果您调试的是eatcup之类的程序,则可以运行WINDBG C:\program\EATCPU.EXE.

附加调试器或在调试器中启动进程后,重现问题。

Microsoft (R) Windows Debugger Version 6.8.0001.0Copyright (c) Microsoft Corporation. All rights reserved.***** WARNING: Your debugger is probably out-of-date.***** Check http://dbg for updates.
CommandLine: eatcpu.exe

Symbol search path is: srv
*C:\symbols*\\symbols\symbols

Executable search path is:

ModLoad:
004000000041a000 eatcpu.exe

ModLoad: 779b0000 77b00000 ntdll.dll

ModLoad:
76780000 76890000C:\Windows\syswow64\kernel32.dll

ModLoad: 62bb0000 62cd1000 C:\Windows\WinSxS\x86_microsoft.vc80.debugcrt_1fc8b3b9a1e18e3b_8.
0.50727.762_none_24c8a196583ff03b\MSVCR80D.dll

ModLoad: 75cb0000 75d5a000 C:\Windows\syswow64\msvcrt.dll

(
1090.164): Break instruction exception - code 80000003(first chance)

eax
=00000000 ebx=00000000 ecx=712b0000 edx=00000000 esi=fffffffe edi=77a90094

eip
=779c0004 esp=0017faf8 ebp=0017fb28 iopl=0nv up ei pl zr na pe nc

cs
=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246ntdll!DbgBreakPoint:

779c0004
cc int 3 0:000>g

(
1090.11d4): Break instruction exception - code 80000003(first chance)

eax
=7efa3000 ebx=00000000 ecx=00000000 edx=77a1d894 esi=00000000 edi=00000000eip=779c0004 esp=0109ff74 ebp=0109ffa0 iopl=0nv up ei pl zr na pe nc

cs
=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246ntdll!DbgBreakPoint:

779c0004
cc int 3 0:007> .sympath SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols Symbol search path is: SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols 0:007>g

(
1090.17d4): Break instruction exception - code 80000003(first chance)

eax
=7efa3000 ebx=00000000 ecx=00000000 edx=77a1d894 esi=00000000 edi=00000000eip=779c0004 esp=0109ff74 ebp=0109ffa0 iopl=0nv up ei pl zr na pe nc

cs
=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246ntdll!DbgBreakPoint:

779c0004
cc int 3

我最近注意到的一个趋势是,在尝试KB304101(PoolUsageMaximum)之后,MmSt标签的使用率仍然很高,这是一种趋势。内存管理器将这些池分配用于节对象原型pte。通常只有两种选择:1)升级到64位平台,或2)减小卷的大小。但我们可能想知道哪些映射文件正在使用这个内存。这是如何做到的。从!memusage开始。

5: kd> !memusage

loading PFN database

loading (
100%complete)

Compiling memory usage data (
99%Complete).

Zeroed:
19073 ( 76292kb)

Free:
0 ( 0kb)

Standby:
1468824 (5875296kb)

Modified:
368 ( 1472kb)

ModifiedNoWrite:
1927 ( 7708kb)

Active
/Valid: 605772 (2423088kb)

Transition:
0 ( 0kb)

Bad:
0 ( 0kb)

Unknown:
0 ( 0kb)

TOTAL:
2095964 (8383856kb)

Building kernel map

Finished building kernel map

Scanning PFN database
- (100% complete)