CHAPTER 3: System Mechanisms 92 95 97 102 111 117 119 122 125 131 138 141 146 148 152 157 165 168 170 169 Viewing the IDT Viewing the PIC and APIC Viewing the IRQL Using Kernel Profiler to Profile Execution Monitoring Interrupt and DPC Activity Viewing the Real User Start Address for Win32 Threads Unhandled Exceptions Viewing System Service Activity Exploring the Object Manager Viewing the Type Objects Viewing Open Handles with Nthandle Viewing the Handle Table with the Kernel Debugger Viewing Process Quotas Looking at the Base Named Objects Viewing Namespace Instancing Viewing Queued Spinlocks Looking at Wait Queues Listing System Worker Threads Enabling Image Loader Tracing and Viewing NtGlobalFlag Viewing LPC Port Objects EXPERIMENT Viewing the IDT You can view the contents of the IDT, including information on what trap handlers Windows 2000 has assigned to interrupts (including exceptions and IRQs), using the !idt command implemented in the Kdex2x86.dll debugger extension library. Passing the !idt command a 0 flag shows device driver ISRs that are registered for hardware device interrupts. The following example shows how you load the Kdex2x86.dll debugger extension library and what the output of the !idt command looks like: kd> .load kdex2x86 Loaded kdex2x86 extension DLL kd> !idt 0 00: 80463440 (ntkrnlmp!KiTrap00) 01: 80463590 (ntkrnlmp!KiTrap01) 02: 0000144e 03: 8046386c (ntkrnlmp!KiTrap03) 04: 804639d4 (ntkrnlmp!KiTrap04) 05: 80463b18 (ntkrnlmp!KiTrap05) 06: 80463c78 (ntkrnlmp!KiTrap06) 07: 804641bc (ntkrnlmp!KiTrap07) 08: 000014a8 09: 0a: 0b: 0c: 0d: 0e: 0f: 10: 11: 12: 80464558 80464660 8046478c 80464a90 80464c9c 80465708 80465aac 80465bb4 80465cd8 80465aac (ntkrnlmp!KiTrap09) (ntkrnlmp!KiTrap0A) (ntkrnlmp!KiTrap0B) (ntkrnlmp!KiTrap0C) (ntkrnlmp!KiTrap0D) (ntkrnlmp!KiTrap0E) (ntkrnlmp!KiTrap0F) (ntkrnlmp!KiTrap10) (ntkrnlmp!KiTrap11) (ntkrnlmp!KiTrap0F) 29: 2a: 2b: 2c: 2d: 2e: 2f: 30: 00000000 804628fe 804629f0 80462b10 8046375c 80462420 80465aac 80461a50 (ntkrnlmp!KiGetTickCount) (ntkrnlmp!KiCallbackReturn) (ntkrnlmp!KiSetLowWaitHighThread) (ntkrnlmp!KiDebugService) (ntkrnlmp!KiSystemService) (ntkrnlmp!KiTrap0F) (ntkrnlmp!KiStartUnexpectedRange) 51: 80461b9a (ntkrnlmp!KiUnexpectedInterrupt33) 52: 813dbbe4 (Vector:52,Irql:4,SyncIrql:5,Connected:TRUE, ¬ No:0,ShareVector:FALSE,Mode:Latched, ¬ ISR:i8042prt!I8042KeyboardInterruptService(f04b10cc)) 53: 80461bae (ntkrnlmp!KiUnexpectedInterrupt35) 54: 80461bb8 (ntkrnlmp!KiUnexpectedInterrupt36) d0: 80462090 (ntkrnlmp!KiUnexpectedInterrupt160) d1: 800638d4 (halmps!HalpClockInterrupt) d2: 804620a4 (ntkrnlmp!KiUnexpectedInterrupt162) Some of the interrupt numbers of interest in the example are in the range 0x0 to 0x10, which includes x86 exception interrupts (for example, a page fault, which is exception 0xe, is handled by KiTrap0E) and the range 0x2a to 0x2e, which includes the system service dispatcher and other software interrupts that the kernel uses internally as fast entry points into the kernel from environment subsystems. On the system used to provide the output for this experiment, the clock interrupt handler is at interrupt number 0xd1 and the keyboard device driver's (I8042prt.sys) keyboard ISR is at interrupt number 0x52. EXPERIMENT Viewing the PIC and APIC You can view the configuration of the PIC on a uniprocessor and the APIC on a multiprocessor by using the !pic and !apic I386kd (or Kd) commands, respectively. (You can't use LiveKd for this experiment because LiveKd can't access hardware.) Here's the output of the !pic command on a uniprocessor. (Note that the !pic command doesn't work if your system is using an APIC HAL.) kd> !pic ----- IRQ Number ----- 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F Physically in service: . . . . . . . . . . . . . . . . Physically masked: . . . . Y . Y Y . Y Y Y . Y . Y Physically requested: Y . . . . . . . . . . . . . . . Here's the output of the !apic command on a system running with the MPS HAL. The "0:" prefix for the I386kd prompt indicates that commands are running on processor 0, so this is the I/O APIC for processor 0: 0: kd> !apic Apic @ fffe0000 ID:1 (40011) LogDesc:01000000 DestFmt:ffffffff TimeCnt: 03f66780clk SpurVec:1f FaultVec:e3 error:80 Ipi Cmd: 000008e1 Vec:E1 FixedDel Lg:02000000 edg Timer..: 000300fd Vec:FD FixedDel Dest=Self edg Linti0.: 0001001f Vec:1F FixedDel Dest=Self edg Linti1.: 000084ff Vec:FF NMI Dest=Self lvl TMR: 93, a3 IRR: 41, d1, e3 ISR: d1 TPR FF masked masked The following output is for the !ioapic command, which displays the configuration of the I/O APIC, the interrupt controller component connected to devices. 0: kd> !ioapic IoApic @ ffd02000 Inti00.: 000100ff Inti01.: 00000962 Inti02.: 000100ff Inti03.: 00000971 Inti04.: 000100ff Inti05.: 00000961 Inti06.: 00010982 Inti07.: 000100ff Inti08.: 000008d1 Inti09.: 000100ff Inti0A.: 000100ff Inti0B.: 000100ff Inti0C.: 00000972 Inti0D.: 000100ff Inti0E.: 00000992 Inti0F.: 000100ff Inti10.: 000100ff Inti11.: 000100ff Inti12.: 000100ff Inti13.: 000100ff Inti14.: 0000a9a3 Inti15.: 0000a993 Inti16.: 000100ff Inti17.: 000100ff ID:8 (11) Arb:0 Vec:FF FixedDel Vec:62 LowestDl Vec:FF FixedDel Vec:71 LowestDl Vec:FF FixedDel Vec:61 LowestDl Vec:82 LowestDl Vec:FF FixedDel Vec:D1 FixedDel Vec:FF FixedDel Vec:FF FixedDel Vec:FF FixedDel Vec:72 LowestDl Vec:FF FixedDel Vec:92 LowestDl Vec:FF FixedDel Vec:FF FixedDel Vec:FF FixedDel Vec:FF FixedDel Vec:FF FixedDel Vec:A3 LowestDl Vec:93 LowestDl Vec:FF FixedDel Vec:FF FixedDel PhysDest:00 Lg:03000000 PhysDest:00 Lg:03000000 PhysDest:00 Lg:03000000 Lg:02000000 PhysDest:00 Lg:01000000 PhysDest:00 PhysDest:00 PhysDest:00 Lg:03000000 PhysDest:00 Lg:03000000 PhysDest:00 PhysDest:00 PhysDest:00 PhysDest:00 PhysDest:00 Lg:03000000 Lg:03000000 PhysDest:00 PhysDest:00 edg edg edg edg edg edg edg edg edg edg edg edg edg edg edg edg edg edg edg edg lvl lvl edg edg masked masked masked masked masked masked masked masked masked masked masked masked masked masked masked masked EXPERIMENT Viewing the IRQL A data structure called the processor control region (PCR) and its extension the processor control block (PRCB) contain information about the state of each processor in the system, such as the current IRQL, a pointer to the hardware IDT, the currently running thread, and the next thread selected to run. The kernel and the HAL use this information to perform architecture-specific and machine-specific actions. Portions of the PCR and PRCB structures are defined publicly in the Windows 2000 Device Driver Kit (DDK) header file Ntddk.h, so examine that file if you want a complete definition of these structures. You can view the contents of the PCR with the kernel debugger by using the !pcr command: kd> !pcr PCR Processor 0 @ffdff000 NtTib.ExceptionList: NtTib.StackBase: NtTib.StackLimit: NtTib.SubSystemTib: NtTib.Version: NtTib.UserPointer: NtTib.SelfTib: f8effc68 f8effdf0 f8efd000 00000000 00000000 00000000 7ffde000 SelfPcr: Prcb: Irql: IRR: IDR: InterruptMode: IDT: GDT: TSS: ffdff000 ffdff120 00000000 00000000 ffff28e8 00000000 80036400 80036000 802b5000 CurrentThread: 81638020 NextThread: 00000000 IdleThread: 8046bdf0 EXPERIMENT Using Kernel Profiler to Profile Execution You can use the Kernel Profiler tool that comes with the Windows 2000 resource kits to enable the system profiling timer, collect samples of the code that is executing when the timer fires, and display a summary showing the frequency distribution across image files and functions. Kernel Profiler is most useful in situations in which performance-critical code is running in a repeatable manner and you want to obtain a breakdown of where the system is spending time when the code executes. For the output to be useful, Kernel Profiler requires that the Windows 2000 symbols be installed on your system. Below is sample output from Kernel Profiler after collecting information for 30 seconds on a system that was relatively idle. (Notice that the majority of the samples were in KiIdleLoop, the idle thread loop, which is explained in Chapter 6.) C:\kernprof -a -d -x -p -v -t 5 30 Symbols loaded: 80400000 ntoskrnl.exe delaying for 30 seconds... report on values with 5 hits end of delay Processor 0: 30404 Total hits 30404 Total hits PROCESSOR 0 28708 5 5 6 79 8 6 6 28384 9 6 5 5 0 0 0 0 0 0 0 0 0 0 0 0 ntoskrnl.exe --Total Hits-ntoskrnl.exe memmove 0x08045AEF0 0 3 ntoskrnl.exe ExAcquireResourceExclusiveLite 0x080413A00 0 3 ntoskrnl.exe ExReleaseResourceLite 0x080413E5E 0 3 ntoskrnl.exe KiXMMIZeroPageNoSave 0x080430B88 0 3 ntoskrnl.exe MiInsertPageInList 0x080442E1E 0 3 ntoskrnl.exe MiRemovePageByColor 0x080443756 0 3 ntoskrnl.exe ObReferenceObjectByHandle 0x08044986C 0 3 ntoskrnl.exe KiIdleLoop 0x08045E98C 0 3 ntoskrnl.exe KiSystemService 0x08045F4A0 0 3 ntoskrnl.exe ExAllocatePoolWithTag 0x080465080 0 3 ntoskrnl.exe SepPrivilegeCheck 0x0804F7638 0 3 ntoskrnl.exe IopParseDevice 0x0804B8F20 0 3 563 5 22 0 0 hal.dll --Total Hits-hal.dll KfRaiseIrql 0x080062E90 0 2 hal.dll KfLowerIrql 0x080062F00 0 2 5 14 503 0 0 0 hal.dll READ_PORT_UCHAR 0x080067960 0 2 hal.dll WRITE_PORT_UCHAR 0x0800679C8 0 2 hal.dll HalAcpiC1Idle 0x080068334 0 2 6 atapi.sys --Total Hits-- 15 Fastfat.sys --Total Hits-- 109 6 15 7 7 0 0 0 0 8 229 22 Ntfs.SYS Ntfs.SYS Ntfs.SYS Ntfs.SYS Ntfs.sys --Total Hits-NtfsInitializeIrpContext 0x0F99CE432 0 2 NtfsCommonWrite 0x0F99CF480 0 2 NtfsCommonCleanup 0x0F99D6190 0 2 NtfsQueryDirectory 0x0F99DC430 0 2 win32k.sys --Total Hits-0 ntdll.dll --Total Hits-ntdll.dll RtlIsValidHandle 0x077F9ADEE 0 2 5 14 17 7 8 8 8 24 17 33 21 5 0 0 0 0 0 0 0 0 0 0 0 0 ntdll.dll ntdll.dll ntdll.dll ntdll.dll ntdll.dll ntdll.dll ntdll.dll ntdll.dll ntdll.dll ntdll.dll ntdll.dll ntdll.dll 756 ZwReleaseSemaphore 0x077F8872C 0 2 RtlpFreeToHeapLookaside 0x077FB0358 0 2 RtlpInterlockedPushEntrySList 0x077FB7620 0 2 RtlTimeToTimeFields 0x077FA7BFE 0 2 RtlEnterCriticalSection 0x077F87B30 0 2 wcslen 0x077FB3D6E 0 2 RtlpAllocateFromHeapLookaside 0x077FB02E4 0 2 RtlpInterlockedPopEntrySList 0x077FB75FC 0 2 RtlpFindAndCommitPages 0x077FC8DF6 0 2 RtlAllocateHeap 0x077FC6BF8 0 2 RtlFreeHeap 0x077FC7426 0 2 RtlAllocateHeap 0x077FC6BF8 0 2 User Mode --Total Hits-- (NO SYMBOLS) Context Switch Information Find any processor Find last processor Idle any processor Idle current processor Idle last processor Preempt any processor Preempt current processor Preempt last processor Switch to idle Total context switches 0 0 0 0 0 0 0 0 0 3255 EXPERIMENT Monitoring Interrupt and DPC Activity Using the Windows 2000 Performance tool, you can watch the percentage of time your system spends on handling interrupts and DPCs. The Processor object has % Interrupt Time and % DPC Time counters, including Total and per-processor instances, which means you can monitor the activity on a per-CPU or a systemwide basis. These objects also have counters to measure the number of interrupts and DPCs per second. One situation in which you might want to look at these counters is if your system is spending an inordinate amount of time in kernel mode and you can't attribute all the kernel-mode CPU time to processes. If total kernel-mode CPU time is greater than the total kernel-mode CPU time of all processes, the remaining time has to be interrupts or DPCs because time spent at interrupt level and DPC level isn't charged to any thread's CPU time performance counter. Be aware that there are inherent inaccuracies in the way Windows 2000 accounts for CPU time, inaccuracies that relate to the granularity of the system timer. (See the section "Quantum Accounting" in Chapter 6 for an explanation of how time accounting works.) EXPERIMENT Viewing the Real User Start Address for Win32 Threads The fact that each Win32 thread begins execution in a system-supplied function (and not the user-supplied function) explains why the start address for thread 0 is the same for every Win32 process in the system (and why the start addresses for secondary threads are also the same). The start address for thread 0 in Win32 processes is the Win32 start-ofprocess function; the start address for any other threads would be the Win32 start-ofthread function. To see the user-supplied function address, use the Tlist utility in the Windows 2000 Support Tools. Type tlist process-name or tlist process-id to get the detailed process output that includes this information. For example, compare the thread start addresses for the Windows Explorer process as reported by Pstat (in the Platform SDK) and Tlist: C:\> pstat pid:3f8 pri: 8 Hnd: 329 Pf: 80043 Ws: tid pri Ctx Swtch StrtAddr User Time 7c 9 16442 77E878C1 0:00:01.241 42c 11 157888 77E92C50 0:00:07.110 44c 8 6357 77E92C50 0:00:00.070 1cc 8 3318 77E92C50 0:00:00.030 4620K explorer.exe Kernel Time State 0:00:01.251 Wait:UserRequest 0:00:34.309 Wait:UserRequest 0:00:00.140 Wait:UserRequest 0:00:00.070 Wait:DelayExecution C:\> tlist explorer 1016 explorer.exe Program Manager CWD: C:\ CmdLine: Explorer.exe VirtualSize: 25348 KB PeakVirtualSize: 31052 KB WorkingSetSize: 1804 KB PeakWorkingSetSize: 3276 KB NumberOfThreads: 4 149 Win32StartAddr:0x01009dbd LastErr:0x0000007e State:Waiting 86 Win32StartAddr:0x77c5d4a5 LastErr:0x00000000 State:Waiting 62 Win32StartAddr:0x00000977 LastErr:0x00000000 State:Waiting 179 Win32StartAddr:0x0100d8d4 LastErr:0x00000002 State:Waiting The start address of thread 0 reported by Pstat is the internal Win32 start-of-process function; the start addresses for threads 1 through 3 are the internal Win32 start-of-thread functions. Tlist, on the other hand, shows the user-supplied Win32 start address (the user function called by the internal Win32 start function). EXPERIMENT Unhandled Exceptions To see an example Dr. Watson log file, run the program \Tools\Accvio.exe from this book's companion CD. This program generates a memory access violation by attempting to write to address 0, which is always an invalid address in Win32 processes. (See Table 7-6 in Chapter 7.) 1. Run the Registry Editor, and locate HKLM\SOFTWARE\ Microsoft\Windows NT\CurrentVersion\AeDebug. 2. If the Debugger value is "drwtsn32 -p %ld -e %ld -g," your system is set up to run Dr. Watson as the default debugger. Proceed to step 4. 3. If the value of Debugger was not set up to run Drwtsn32.exe, you can still test Dr. Watson by temporarily installing it and then restoring your previous debugger settings: o Save the current value somewhere (for example, in a Notepad file or in the current paste buffer). o Select Run from the taskbar Start menu, and then enter drwtsn32 i. (This initializes the Debugger field to run Dr. Watson.) 4. Run the test program \Tools\Accvio.exe. 5. The Program Error message box should come up—when the log file and crash dump have been created, the button will change from Cancel to OK. (Note: This error message occurs by default only on Windows 2000 Professional installations of Dr. Watson—Windows 2000 Server by default will not show a message box, but the dump files will still be created. You also might see a different error message if Dr. Watson is not your default debugger.) 6. Click OK to dismiss the message box. 7. Run Drwtsn32.exe. (Select Run from the Start menu, and then enter drwtsn32.) 8. In the list of Application Errors, click on the last entry and then click the View button—the portion of the Dr. Watson log file containing the details of the access violation from Accvio.exe will be displayed. (For details on the log file format, press Help in the Dr. Watson For Windows 2000 dialog box and select Dr. Watson Log File Overview.) 9. If the original value of Debugger wasn't the default Dr. Watson settings, restore the saved value from step 1. As another experiment, try changing the value of Debugger to another program, such as Notepad.exe (Notepad editor) or Sol.exe (Solitaire). Rerun Accvio.exe, and notice that whatever program is specified in the Debugger value is run—that is, there's no validation that the program defined in Debugger is actually a debugger. Make sure you restore your registry settings. (As noted in step 3b, to reset to the system default Dr. Watson settings, type drwtsn32 -i in the Run dialog box or at a command prompt.) EXPERIMENT Viewing System Service Activity You can monitor system service activity by watching the System Calls/Sec performance counter in the System object. Run the Performance tool, and in chart view, click the Add button to add a counter to the chart; select the System object, select the System Calls/Sec counter, and then click the Add button to add the counter to the chart. EXPERIMENT Exploring the Object Manager Throughout this section, you'll find experiments that show you how to peer into the object manager database. These experiments use the following tools, which you should become familiar with if you aren't already: Object viewer There are two versions of this tool: the version from www.sysinternals.com (on this book's companion CD as \Sysint\Winobj.exe) and a different version in (the Platform SDK (in \Program Files\Microsoft Platform SDK\Bin\ Winnt\Winobj.exe). The object viewer from www.sysinternals.com displays more accurate information about objects (such as the reference count, the number of open handles, security descriptors, and so forth) than the object viewer in the Platform SDK does. Open handles Two tools from www.sysinternals.com show open handles: a GUI tool (on this book's companion CD in \Sysint\Handleex.exe) and a command-line tool (on the companion CD in \Sysint\Nthandle.exe). The Windows 2000 resource kits include another tool that shows open handles, called Oh.exe. The kernel debugger !handle command. The object viewer provides a way to traverse the namespace that the object manager maintains. (As we'll explain later, not all objects have names.) Try running the WinObj object manager utility from the companion CD and examining the layout, shown here: In the Windows 2000 Resource Kit Tools Help for OH, you'll find out that if object tracking—an internal debugging feature in the executive—isn't enabled, OH will enable it by setting a Windows 2000 global flag in the registry and then rebooting your system. Neither Nthandle nor Handleex from the companion CD require object tracking. (You can enable object tracking by manually setting the flag and rebooting your system. See the section "Windows 2000 Global Flags" for more on global flags.) Because this flag uses additional memory to track object usage information, you should disable it with the Gflags utility after you've experimented with OH and then reboot your system again. EXPERIMENT Viewing the Type Objects You can see the list of type objects declared to the object manager with the Object Viewer utility on this book's companion CD. Just run \Sysint\Winobj.exe, and then in the Winobj object manager open the \ObjectTypes directory, as shown here: EXPERIMENT Viewing Open Handles with Nthandle As shown in the following example, the Nthandle tool (\Sysint\ Nthandle.exe, which you'll find on this book's companion CD) can display the handles open by any or all processes: C:\>nthandle -a -p system Handle V1.2 Copyright (C) 1997-2000 Mark Russinovich Systems Internals - http://www.sysinternals.com ------------------------------------------------------------------System pid: 8 4: Process 8: Key \REGISTRY c: Thread 10: Key HKLM\SYSTEM\ControlSet003\Control\ProductOptions 14: Key HKLM\SYSTEM\Setup 18: Key HKLM\SYSTEM\ControlSet003\Control\IDConfigDB\CurrentDockInfo 1c: Key HKLM\SYSTEM\ControlSet003\Hardware Profiles\Current 20: Key HKLM\HARDWARE\DESCRIPTION\SYSTEM\MultifunctionAdapter The display above shows the first eight open handles in the System process. The process name and ID are displayed first, followed by a line for each handle. The handle value, object type, and object name are shown for each handle. Because we specified the -a flag, handles to objects that don't have names (handle numbers 0x4, 0xc, and 0x18) are included. EXPERIMENT Viewing the Handle Table with the Kernel Debugger The !handle command in the kernel debugger takes three arguments: !handle <handle index> <flags> <processid> The handle index identifies the handle entry in the handle table. (Zero means display all handles.) The first handle is index 4, the second 8, and so on. For example, typing !handle 4 will show the first handle for the current process. The flags you can specify are a bitmask, where bit 0 means display only the information in the handle entry, bit 1 means display free handles (not just used handles), and bit 2 means display information about the object that the handle refers to. The following command displays full details about the handle table for process ID 0x408: kd> !handle 0 7 408 processor number 0 Searching for Process with Cid == 408 PROCESS 865f0790 SessionId: 0 Cid: 0408 Peb: 7ffdf000 ParentCid: 01dc DirBase: 04fd3000 ObjectTable: 856ca888 TableSize: 21. Image: i386kd.exe Handle Table at e2125000 with 21 Entries in use 0000: free handle 0004: Object: e20da2e0 GrantedAccess: 000f001f Object: e20da2e0 Type: (81491b80) Section ObjectHeader: e20da2c8 HandleCount: 1 PointerCount: 1 0008: Object: 80b13330 GrantedAccess: 00100003 Object: 80b13330 Type: (81495100) Event ObjectHeader: 80b13318 HandleCount: 1 PointerCount: 1 EXPERIMENT Viewing Process Quotas You can view the paged pool, nonpaged pool, and page file current usage, peak usage, and quota (limit) for a process with the Process Explode utility, Pview.exe, available on www.reskit.com. (The Performance tool displays only the usage information, not the quotas.) In the following example, the process selected has a peak paged pool usage of 1062 KB, current usage of 1028 KB, and a quota of 1504 KB: EXPERIMENT Looking at the Base Named Objects You can see the list of base objects that have names with the Object Viewer utility on this book's companion CD. (Another version of this utility is also available with the Platform SDK.) Run \Sysint\Winobj.exe, and click on \BaseNamedObjects, as shown here: The named objects are shown on the right. The icons indicate the object type. Mutexes are indicated with a stop sign. Sections (Win32 file mapping objects) are shown as memory chips. Events are shown as exclamation points. Semaphores are indicated with an icon that resembles a traffic signal. Symbolic links have icons that are curved arrows. EXPERIMENT Viewing Namespace Instancing You can see the object manager instance the namespace on a Windows 2000 Server, Advanced Server, or Datacenter Server system on which you install Terminal Services. Logon to the server using the Terminal Services client, and run the \Sysint\Winobj.exe utility from this book's companion CD. Click on the \Sessions directory, and you'll see a subdirectory with a numeric name for each active remote session. If you open one of these directories, you'll see subdirectories named \DosDevices, \Windows, and \BaseNamedObjects, which are the local namespace subdirectories of the session. The following screen shot shows a local namespace: EXPERIMENT Viewing Queued Spinlocks You can view the state of queued spinlocks by using the !qlock kernel debugger command. This command is meaningful only on a multiprocessor system because uniprocessor HALs don't implement spinlocks. In the following example, the dispatcher database queued spinlock is held by processor 1, and the other queued spinlocks are not acquired. (The dispatcher database is described in Chapter 6.) kd> !qlocks Key: O = Owner, 1n = Wait order, blank = not owned/waiting, C = Corrupt Lock Name KE KE MM MM CC CC - Dispatcher Context Swap PFN System Space Vacb Master 0 1 O Processor Number 2 3 4 5 6 7 8 9 10 11 12 13 14 15 EXPERIMENT Looking at Wait Queues Although many process viewer utilities indicate whether a thread is in a wait state (and if so, what kind of wait), you can see the list of objects a thread is waiting on only with the kernel debugger !thread command. For example, the following excerpt from the output of a !process command shows that the thread is waiting on an event object: kd> !process THREAD 80618030 Cid 97.7f Teb: 7ffde000 Win32Thread: e199cea8 WAIT: (WrUserRequest) UserMode Non-Alertable 805b4ab0 SynchronizationEvent Although the kernel debugger doesn't have a command for formatting the contents of a dispatcher header, we know the layout (described in the previous section "Data Structures") so we can interpret its contents manually: kd> dd 805b4ab0 0x805B4AB0 00040001 00000000 8061809c 8061809c ..........a...a. From this, we can ascertain that no other threads are waiting on this event object because the wait list head forward and backward pointers (the third and fourth 32-bit values) point to the same location (a single wait block). Dumping the wait block (at address 0x8061809c) yields the following: kd> dd 8061809c 0x8061809C 805b4ab8 805b4ab8 80618030 805b4ab0 .J[..J[.0.a..J[. 0x806180AC 8061809c 00010000 00000000 00000000 ..a............. The first two 32-bit values point to the list head of the wait blocks in the dispatcher header. The third 32-bit value is the pointer to the thread object. The fourth value points to the dispatcher object itself. The fifth value (0x8061809c) is the pointer to the next wait block. From this, we can conclude that the thread is not waiting on any other objects, since the next wait block field points to the wait block itself. EXPERIMENT Listing System Worker Threads You can use the !exqueue kernel debugger command to see a listing of system worker threads classified by their type: kd> !exqueue Dumping ExWorkerQueue: 8046A5C0 **** Critical WorkQueue( current = 0 maximum = 1 ) THREAD 818a2d40 Cid 8.c Teb: 00000000 Win32Thread: THREAD 818a2ac0 Cid 8.10 Teb: 00000000 Win32Thread: THREAD 818a2840 Cid 8.14 Teb: 00000000 Win32Thread: THREAD 818a25c0 Cid 8.18 Teb: 00000000 Win32Thread: THREAD 818a2340 Cid 8.1c Teb: 00000000 Win32Thread: **** Delayed WorkQueue( current THREAD 818a20c0 Cid 8.20 Teb: THREAD 818a1020 Cid 8.24 Teb: THREAD 818a1da0 Cid 8.28 Teb: 00000000 00000000 00000000 00000000 00000000 WAIT WAIT WAIT WAIT WAIT = 0 maximum = 1 ) 00000000 Win32Thread: 00000000 WAIT 00000000 Win32Thread: 00000000 WAIT 00000000 Win32Thread: 00000000 WAIT **** HyperCritical WorkQueue( current = 0 maximum = 1 ) THREAD 818a1b20 Cid 8.2c Teb: 00000000 Win32Thread: 00000000 WAIT EXPERIMENT Enabling Image Loader Tracing and Viewing NtGlobalFlag To see an example of the detailed tracing information you can obtain by setting global flags, try running Gflags on a system booted with the kernel debugger that is connected to a host system running Kd or Windbg, or that is running LiveKd. As an example, try enabling the Show Loader Snaps flag. To do this, select Kernel Mode, click the Show Loader Snaps check box, and click the Apply button. Then run an image on this machine, and in the kernel debugger you'll see volumes of output like the following: LDR: PID: 0xb8 started - 'notepad' LDR: NEW PROCESS Image Path: C:\WINNT\system32\notepad.exe (notepad.exe) Current Directory: C:\ddk\bin Search Path: C:\WINNT\System32;C:\WINNT\system;C:\WINNT LDR: notepad.exe bound to comdlg32.dll LDR: ntdll.dll used by comdlg32.dll LDR: Snapping imports for comdlg32.dll from ntdll.dll LDR: KERNEL32.dll loaded. - Calling init routine at 77f01000 LDR: RPCRT4.dll loaded. - Calling init routine at 77e1b6d5 LDR: ADVAPI32.dll loaded. - Calling init routine at 77dc1000 LDR: USER32.dll loaded. - Calling init routine at 77e78037 You can use the !gflags and !gflag kernel debugger commands to view the state of the NtGlobalFlag kernel variable. The !gflags command lists all the flags, indicating which ones are enabled, whereas !gflag reports only the flags that are enabled. kd> !gflags NT!NtGlobalFlag 0x4400 STOP_ON_EXCEPTION DEBUG_INITIAL_COMMAND HEAP_ENABLE_TAIL_CHECK HEAP_VALIDATE_PARAMETERS *POOL_ENABLE_TAGGING USER_STACK_TRACE_DB *MAINTAIN_OBJECT_TYPELIST ENABLE_CSRDEBUG DISABLE_PAGE_KERNEL_STACKS ENABLE_CLOSE_EXCEPTIONS ENABLE_HANDLE_TYPE_TAGGING DEBUG_INITIAL_COMMAND_EX SHOW_LDR_SNAPS STOP_ON_HUNG_GUI HEAP_ENABLE_FREE_CHECK HEAP_VALIDATE_ALL HEAP_ENABLE_TAGGING KERNEL_STACK_TRACE_DB HEAP_ENABLE_TAG_BY_DLL ENABLE_KDEBUG_SYMBOL_LOAD HEAP_DISABLE_COALESCING ENABLE_EXCEPTION_LOGGING HEAP_PAGE_ALLOCS DISABLE_DBGPRINT kd> !gflag NtGlobalFlag at 8046a164 Current NtGlobalFlag contents: 0x00004400 ptg - Enable pool tagging otl - Maintain a list of objects for each type EXPERIMENT Viewing LPC Port Objects You can see named LPC port objects with the Object Viewer utility included on this book's companion CD. Run \Sysint\Winobj.exe, and select the root directory. A plug icon identifies the port objects, as shown here: To see the LPC port objects used by RPC, select the \RPC Control directory, as shown here: You can also view LPC port objects by using the !lpc kernel debugger command. The command accepts parameters that direct it to show LPC ports, LPC messages, and threads that are waiting or sending LPC messages. To view the Lsass authentication port (the port that Winlogon sends logon requests to), first obtain a list of the ports on the system: kd> !lpc Usage: !lpc - Display this help !lpc message [MessageId] - Display the message with a given ID and all related information If MessageId is not specified, dump all messages !lpc port [PortAddress] - Display the port information !lpc scan PortAddress - Search this port and any connected port !lpc thread [ThreadAddr] - Search the thread in rundown port queues and display the port info If ThreadAddr is missing, display all threads marked as doing some lpc operations kd> !lpc port Scanning 206 objects 1 Port: 0xe1360320 Connection: 0xe1360320 Communication: 0x00000000 'SeRmCommandPort' 1 Port: 0xe136bc20 Connection: 0xe136bc20 Communication: 0x00000000 'SmApiPort' 1 Port: 0xe133ba80 Connection: 0xe133ba80 Communication: 0x00000000 'DbgSsApiPort' 1 Port: 0xe13606e0 Connection: 0xe13606e0 Communication: 0x00000000 'DbgUiApiPort' 1 Port: 0xe205f040 Connection: 0xe205f040 Communication: 0x00000000 'LsaAuthenticationPort' Locate the port named LsaAuthenticationPort in the output and then examine it by passing its address to the !lpc command: kd> !lpc port 0xe205f040 Server connection port e205f040 Name: LsaAuthenticationPort Handles: 1 References: 37 Server process : ff7d56c0 (lsass.exe) Queue semaphore : ff7bfcc8 Semaphore state 0 (0x0) The message queue is empty The LpcDataInfoChainHead queue is empty