Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1910 lines
54 KiB

/*++
Copyright (c) 1990-2000 Microsoft Corporation
Module Name:
ntsdexts.c
Abstract:
This function contains the default ntsd debugger extensions
Revision History:
Daniel Mihai (DMihai) 18-Feb-2001
Add !htrace - useful for dumping handle trace information.
--*/
#include "ntsdextp.h"
DECLARE_API( version )
{
OSVERSIONINFOA VersionInformation;
HKEY hkey;
DWORD cb, dwType;
CHAR szCurrentType[128];
CHAR szCSDString[3+128];
INIT_API();
VersionInformation.dwOSVersionInfoSize = sizeof(VersionInformation);
if (!GetVersionEx( &VersionInformation )) {
dprintf("GetVersionEx failed - %u\n", GetLastError());
goto Exit;
}
szCurrentType[0] = '\0';
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
"Software\\Microsoft\\Windows NT\\CurrentVersion",
0,
KEY_READ,
&hkey
) == NO_ERROR
) {
cb = sizeof(szCurrentType);
if (hkey) {
if (RegQueryValueEx(hkey, "CurrentType", NULL, &dwType, szCurrentType, &cb ) != 0) {
szCurrentType[0] = '\0';
}
RegCloseKey(hkey);
}
}
if (VersionInformation.szCSDVersion[0]) {
_snprintf(szCSDString, sizeof(szCSDString) / sizeof(szCSDString[0]), ": %s", VersionInformation.szCSDVersion);
szCSDString[(sizeof(szCSDString) / sizeof(szCSDString[0])) - 1] = 0;
}
else {
szCSDString[0] = '\0';
}
dprintf("Version %d.%d (Build %d%s) %s\n",
VersionInformation.dwMajorVersion,
VersionInformation.dwMinorVersion,
VersionInformation.dwBuildNumber,
szCSDString,
szCurrentType
);
Exit:
EXIT_API();
}
DECLARE_API( help )
{
INIT_API();
while (*args == ' ')
args++;
if (*args == '\0') {
dprintf("ntsdexts help:\n\n");
dprintf("!critSec csAddress - Dump a critical section\n");
dprintf("!dp [v] [pid | pcsr_process] - Dump CSR process\n");
dprintf("!dreg -[d|w] <keyPath>[![<valueName> | *]] - Dump registry information\n");
dprintf("!dt [v] pcsr_thread - Dump CSR thread\n");
dprintf("!error value - Decode error value\n");
dprintf("!gatom - Dump the global atom table\n");
dprintf("!handle [handle] - Dump handle information\n");
dprintf("!help [cmd] - Displays this list or gives details on command\n");
dprintf("!locks [-v][-o] - Dump all Critical Sections in process\n");
dprintf("!version - Dump system version and build number\n");
} else {
if (*args == '!')
args++;
if (strcmp( args, "handle") == 0) {
dprintf("!handle [handle [flags [type]]] - Dump handle information\n");
dprintf(" If no handle specified, all handles are dumped.\n");
dprintf(" Flags are bits indicating greater levels of detail.\n");
dprintf("If the handle is 0 or -1, all handles are scanned. If the handle is not\n");
dprintf("zero, that particular handle is examined. The flags are as follows:\n");
dprintf(" 1 - Get type information (default)\n");
dprintf(" 2 - Get basic information\n");
dprintf(" 4 - Get name information\n");
dprintf(" 8 - Get object specific info (where available)\n");
dprintf("\n");
dprintf("If Type is specified, only object of that type are scanned. Type is a\n");
dprintf("standard NT type name, e.g. Event, Semaphore, etc. Case sensitive, of\n");
dprintf("course.\n");
dprintf("\n");
dprintf("Examples:\n");
dprintf("\n");
dprintf(" !handle -- dumps the types of all the handles, and a summary table\n");
dprintf(" !handle 0 0 -- dumps a summary table of all the open handles\n");
dprintf(" !handle 0 f -- dumps everything we can find about a handle.\n");
dprintf(" !handle 0 f Event\n");
dprintf(" -- dumps everything we can find about open events\n");
} else if (strcmp( args, "gflag") == 0) {
dprintf("If a value is not given then displays the current bits set in\n");
dprintf("NTDLL!NtGlobalFlag variable. Otherwise value can be one of the\n");
dprintf("following:\n");
dprintf("\n");
dprintf(" -? - displays a list of valid flag abbreviations\n");
dprintf(" number - 32-bit number that becomes the new value stored into\n");
dprintf(" NtGlobalFlag\n");
dprintf(" +number - specifies one or more bits to set in NtGlobalFlag\n");
dprintf(" +abbrev - specifies a single bit to set in NtGlobalFlag\n");
dprintf(" -number - specifies one or more bits to clear in NtGlobalFlag\n");
dprintf(" -abbrev - specifies a single bit to clear in NtGlobalFlag\n");
} else {
dprintf("Invalid command. No help available\n");
}
}
EXIT_API();
}
VOID
DumpStackBackTraceIndex(
IN USHORT BackTraceIndex
)
{
#if i386
BOOL b;
PRTL_STACK_TRACE_ENTRY pBackTraceEntry;
RTL_STACK_TRACE_ENTRY BackTraceEntry;
ULONG i;
CHAR Symbol[ 1024 ];
ULONG_PTR Displacement;
ULONG NumberOfEntriesAdded;
PRTL_STACK_TRACE_ENTRY *EntryIndexArray; // Indexed by [-1 .. -NumberOfEntriesAdded]
PSTACK_TRACE_DATABASE *pRtlpStackTraceDataBase;
PSTACK_TRACE_DATABASE RtlpStackTraceDataBase;
STACK_TRACE_DATABASE StackTraceDataBase;
pRtlpStackTraceDataBase = (PSTACK_TRACE_DATABASE *)GetExpression( "NTDLL!RtlpStackTraceDataBase" );
if (pRtlpStackTraceDataBase == NULL) {
dprintf( "HEAPEXT: Unable to get address of NTDLL!RtlpStackTraceDataBase\n" );
}
if ((BackTraceIndex != 0) && (pRtlpStackTraceDataBase != NULL)) {
b = ReadMemory( (ULONG_PTR)pRtlpStackTraceDataBase,
&RtlpStackTraceDataBase,
sizeof( RtlpStackTraceDataBase ),
NULL
);
if (!b || RtlpStackTraceDataBase == NULL) {
return;
}
b = ReadMemory( (ULONG_PTR)RtlpStackTraceDataBase,
&StackTraceDataBase,
sizeof( StackTraceDataBase ),
NULL
);
if (!b) {
return;
}
if (BackTraceIndex < StackTraceDataBase.NumberOfEntriesAdded) {
b = ReadMemory( (ULONG_PTR)(StackTraceDataBase.EntryIndexArray - BackTraceIndex),
&pBackTraceEntry,
sizeof( pBackTraceEntry ),
NULL
);
if (!b) {
dprintf( " unable to read stack back trace index (%x) entry at %p\n",
BackTraceIndex,
(StackTraceDataBase.EntryIndexArray - BackTraceIndex)
);
return;
}
b = ReadMemory( (ULONG_PTR)pBackTraceEntry,
&BackTraceEntry,
sizeof( BackTraceEntry ),
NULL
);
if (!b) {
dprintf( " unable to read stack back trace entry at %p\n",
pBackTraceEntry
);
return;
}
dprintf( "\n Stack trace (%u) at %x:\n", BackTraceIndex, pBackTraceEntry );
for (i=0; i<BackTraceEntry.Depth; i++) {
GetSymbol( (LPVOID)BackTraceEntry.BackTrace[ i ],
Symbol,
&Displacement
);
dprintf( " %08x: %s", BackTraceEntry.BackTrace[ i ], Symbol );
if (Displacement != 0) {
dprintf( "+0x%p", Displacement );
}
dprintf( "\n" );
}
}
}
#endif
return;
}
PLIST_ENTRY
DumpCritSec(
DWORD_PTR dwAddrCritSec,
DWORD_PTR dwAddrDebugInfo,
BOOLEAN bDumpIfUnowned,
BOOLEAN bOrphaned
)
/*++
Routine Description:
This function is called as an NTSD extension to format and dump
the contents of the specified critical section.
Arguments:
dwAddrCritSec - Supplies the address of the critical section to
be dumped or NULL if dumping via debug info
dwAddrDebugInfo - Supllies the address of a critical section debug info
struct to be dumped or NULL if the critical section address is passed in
bDumpIfUnowned - TRUE means to dump the critical section even if
it is currently unowned.
bOrphaned - TRUE: means that the caller only wants to know if the debuginfo does
not point to a valid critical section
Return Value:
Pointer to the next critical section in the list for the process or
NULL if no more critical sections.
--*/
{
USHORT i;
CHAR Symbol[1024];
DWORD_PTR Displacement;
CRITICAL_SECTION CriticalSection;
CRITICAL_SECTION_DEBUG DebugInfo;
BOOL b;
PLIST_ENTRY Next=NULL;
if (dwAddrDebugInfo != (DWORD_PTR)NULL) {
//
// the address of the debug info was passes in, read it in from the debugged process
//
b = ReadMemory( dwAddrDebugInfo,
&DebugInfo,
sizeof(DebugInfo),
NULL
);
if ( !b ) {
dprintf(" NTSDEXTS: Unable to read RTL_CRITICAL_SECTION_DEBUG at %p\n", dwAddrDebugInfo );
return NULL;
}
//
// get the critical section from the debug info
//
dwAddrCritSec=(DWORD_PTR)DebugInfo.CriticalSection;
//
// set the next pointer now. It is only used when the debuginfo is passed in
//
Next=DebugInfo.ProcessLocksList.Flink;
} else {
//
// the debug info address was zero, the critical section address better not be too
//
if (dwAddrCritSec == (DWORD_PTR)NULL) {
//
// If the debuginfo value was not valid, then the critical section value must be
//
return NULL;
}
}
//
// we should now have a pointer to the critical section, either passed in or read from
// the debug info
//
//
// Read the critical section from the debuggees address space into our
// own.
//
b = ReadMemory( dwAddrCritSec,
&CriticalSection,
sizeof(CriticalSection),
NULL
);
if ( !b ) {
if (bDumpIfUnowned || bOrphaned) {
dprintf("\nCritSec at %p could not be read\n",dwAddrCritSec);
dprintf("Perhaps the critical section was a global variable in a dll that was unloaded?\n");
if (dwAddrDebugInfo != (DWORD_PTR)NULL) {
if (bOrphaned) {
DumpStackBackTraceIndex(DebugInfo.CreatorBackTraceIndex);
}
}
}
return Next;
}
if (dwAddrDebugInfo != (DWORD_PTR)NULL) {
//
// the debug info address was passed in, make sure the critical section that
// it pointed to points back to it.
//
if ((DWORD_PTR)CriticalSection.DebugInfo != dwAddrDebugInfo) {
//
// this critical section does not point back to debug info that we got it from
//
CRITICAL_SECTION_DEBUG OtherDebugInfo;
//
// now lets try to read in the debug info that this critical section points to,
// to see if it does point back the critical section in question
//
ZeroMemory(&OtherDebugInfo,sizeof(OtherDebugInfo));
b = ReadMemory( (ULONG_PTR)CriticalSection.DebugInfo,
&OtherDebugInfo,
sizeof(DebugInfo),
NULL
);
if ( !b ) {
//
// we could not read the debug info pointed to by the critical section,
// probably means the critical section has been trashed
//
if (bDumpIfUnowned || bOrphaned) {
dprintf("\nCritSec at %p does not point back to the debug info at %p\n",dwAddrCritSec,dwAddrDebugInfo);
dprintf("Perhaps the memory that held the critical section has been reused without calling DeleteCriticalSection() ?\n");
if (bOrphaned) {
DumpStackBackTraceIndex(DebugInfo.CreatorBackTraceIndex);
}
}
} else {
//
// we were able to read in the debug info, see if it points to this new
// critical section
//
if ((DWORD_PTR)OtherDebugInfo.CriticalSection == dwAddrCritSec) {
//
// the debug info points back to the critical section.
// The definitely means that it was re-initialized.
//
if (bDumpIfUnowned || bOrphaned) {
GetSymbol((LPVOID)dwAddrCritSec,Symbol,&Displacement);
dprintf(
"\nThe CritSec %s+%lx at %p has been RE-INITIALIZED.\n",
Symbol,
Displacement,
dwAddrCritSec
);
dprintf("The critical section points to DebugInfo at %p instead of %p\n",(DWORD_PTR)CriticalSection.DebugInfo,dwAddrDebugInfo);
if (bOrphaned) {
DumpStackBackTraceIndex(DebugInfo.CreatorBackTraceIndex);
}
}
} else {
//
// The debug info does not point back the critical section, probably means that
// the critical section was trashed
//
if (bDumpIfUnowned || bOrphaned) {
dprintf("\nCritSec at %p does not point back to the debug info at %p\n",dwAddrCritSec,dwAddrDebugInfo);
dprintf("Perhaps the memory that held the critical section has been reused without calling DeleteCriticalSection() ?\n");
if (bOrphaned) {
DumpStackBackTraceIndex(DebugInfo.CreatorBackTraceIndex);
}
}
}
}
}
} else {
//
// we need to read in the debug info from the critical section since it was not passed in
//
ZeroMemory(&DebugInfo,sizeof(DebugInfo));
b = ReadMemory( (ULONG_PTR)CriticalSection.DebugInfo,
&DebugInfo,
sizeof(DebugInfo),
NULL
);
if ( !b ) {
//
// use this to signal that we could not read the debuginfo for the critical section
//
CriticalSection.DebugInfo=NULL;
dprintf("\nDebugInfo for CritSec at %p could not be read\n",dwAddrCritSec);
dprintf("Probably NOT an initialized critical section.\n");
} else {
//
// we were able to read in the debug info, see if it valid
//
if ((DWORD_PTR)DebugInfo.CriticalSection != dwAddrCritSec) {
//
// The debug info does not point back to the critical section
//
dprintf("\nDebugInfo for CritSec at %p does not point back to the critical section\n",dwAddrCritSec);
dprintf("NOT an initialized critical section.\n");
}
}
}
//
// we should now have read in both the critical section and debug info for that critical section
//
if (bOrphaned) {
//
// the user only wanted to check for orphaned critical sections
//
return Next;
}
//
// Dump the critical section
//
if ( CriticalSection.LockCount == -1 && !bDumpIfUnowned) {
//
// Lock is not held and the user does not want verbose output
//
return Next;
}
//
// Get the symbolic name of the critical section
//
dprintf("\n");
GetSymbol((LPVOID)dwAddrCritSec,Symbol,&Displacement);
dprintf(
"CritSec %s+%lx at %p\n",
Symbol,
Displacement,
dwAddrCritSec
);
if ( CriticalSection.LockCount == -1) {
dprintf("LockCount NOT LOCKED\n");
} else {
dprintf("LockCount %ld\n",CriticalSection.LockCount);
}
dprintf("RecursionCount %ld\n",CriticalSection.RecursionCount);
dprintf("OwningThread %lx\n",CriticalSection.OwningThread);
if (CriticalSection.DebugInfo != NULL) {
//
// we have the debug info
//
dprintf("EntryCount %lx\n",DebugInfo.EntryCount);
dprintf("ContentionCount %lx\n",DebugInfo.ContentionCount);
}
if ( CriticalSection.LockCount != -1) {
dprintf("*** Locked\n");
}
return Next;
}
DECLARE_API( critsec )
{
DWORD_PTR dwAddrCritSec;
INIT_API();
//
// Evaluate the argument string to get the address of
// the critical section to dump.
//
dwAddrCritSec = GetExpression(args);
if ( !dwAddrCritSec ) {
goto Exit;
}
DumpCritSec(dwAddrCritSec,0,TRUE,FALSE);
Exit:
EXIT_API();
}
DECLARE_API( locks )
/*++
Routine Description:
This function is called as an NTSD extension to display all
critical sections in the target process.
Return Value:
None.
--*/
{
BOOL b;
CRITICAL_SECTION_DEBUG DebugInfo;
PVOID AddrListHead;
LIST_ENTRY ListHead;
PLIST_ENTRY Next;
BOOLEAN Verbose;
BOOLEAN Orphaned=FALSE;
LPCSTR p;
DWORD NumberOfCriticalSections;
INIT_API();
Verbose = FALSE;
p = (LPSTR)args;
while ( p != NULL && *p ) {
if ( *p == '-' ) {
p++;
switch ( *p ) {
case 'V':
case 'v':
Verbose = TRUE;
p++;
break;
case 'o':
case 'O':
Orphaned=TRUE;
p++;
break;
case ' ':
goto gotBlank;
default:
dprintf( "NTSDEXTS: !locks invalid option flag '-%c'\n", *p );
break;
}
}
else {
gotBlank:
p++;
}
}
if (Orphaned) {
dprintf( "Looking for orphaned critical sections\n" );
}
//
// Locate the address of the list head.
//
AddrListHead = (PVOID)GetExpression("ntdll!RtlCriticalSectionList");
if ( !AddrListHead ) {
dprintf( "NTSDEXTS: Unable to resolve ntdll!RtlCriticalSectionList\n" );
dprintf( "NTSDEXTS: Please check your symbols\n" );
goto Exit;
}
//
// Read the list head
//
b = ReadMemory( (ULONG_PTR)AddrListHead,
&ListHead,
sizeof(ListHead),
NULL
);
if ( !b ) {
dprintf( "NTSDEXTS: Unable to read memory at ntdll!RtlCriticalSectionList\n" );
goto Exit;
}
Next = ListHead.Flink;
(CheckControlC)();
NumberOfCriticalSections=0;
//
// Walk the list of critical sections
//
while ( Next != AddrListHead ) {
Next=DumpCritSec(
0,
(DWORD_PTR)CONTAINING_RECORD( Next, RTL_CRITICAL_SECTION_DEBUG, ProcessLocksList),
Verbose,
Orphaned
);
if (Next == NULL) {
dprintf( "\nStopped scanning because of problem reading critical section debug info\n");
break;
}
NumberOfCriticalSections++;
if ((CheckControlC)()) {
dprintf( "\nStopped scanning because of control-C\n");
break;
}
}
dprintf( "\nScanned %d critical sections\n",NumberOfCriticalSections);
Exit:
EXIT_API();
}
//
// Simple routine to convert from hex into a string of characters.
// Used by debugger extensions.
//
// by scottlu
//
char *
HexToString(
ULONG_PTR dw,
CHAR *pch
)
{
if (dw > 0xf) {
pch = HexToString(dw >> 4, pch);
dw &= 0xf;
}
*pch++ = ((dw >= 0xA) ? ('A' - 0xA) : '0') + (CHAR)dw;
*pch = 0;
return pch;
}
//
// dt == dump thread
//
// dt [v] pcsr_thread
// v == verbose (structure)
//
// by scottlu
//
DECLARE_API( dt )
{
char chVerbose;
CSR_THREAD csrt;
ULONG_PTR dw;
BOOL b;
INIT_API();
while (*args == ' ')
args++;
chVerbose = ' ';
if (*args == 'v')
chVerbose = *args++;
dw = GetExpression(args);
b = ReadMemory( dw, &csrt, sizeof(csrt), NULL);
if ( !b ) {
dprintf( "NTSDEXTS: Unable to read memory\n" );
goto Exit;
}
//
// Print simple thread info if the user did not ask for verbose.
//
if (chVerbose == ' ') {
dprintf("Thread %08lx, Process %08lx, ClientId %lx.%lx, Flags %lx, Ref Count %lx\n",
dw,
csrt.Process,
csrt.ClientId.UniqueProcess,
csrt.ClientId.UniqueThread,
csrt.Flags,
csrt.ReferenceCount);
goto Exit;
}
dprintf("PCSR_THREAD @ %08lx:\n"
"\t+%04lx Link.Flink %08lx\n"
"\t+%04lx Link.Blink %08lx\n"
"\t+%04lx Process %08lx\n",
dw,
FIELD_OFFSET(CSR_THREAD, Link.Flink), csrt.Link.Flink,
FIELD_OFFSET(CSR_THREAD, Link.Blink), csrt.Link.Blink,
FIELD_OFFSET(CSR_THREAD, Process), csrt.Process);
dprintf(
"\t+%04lx WaitBlock %08lx\n"
"\t+%04lx ClientId.UniqueProcess %08lx\n"
"\t+%04lx ClientId.UniqueThread %08lx\n"
"\t+%04lx ThreadHandle %08lx\n",
FIELD_OFFSET(CSR_THREAD, WaitBlock), csrt.WaitBlock,
FIELD_OFFSET(CSR_THREAD, ClientId.UniqueProcess), csrt.ClientId.UniqueProcess,
FIELD_OFFSET(CSR_THREAD, ClientId.UniqueThread), csrt.ClientId.UniqueThread,
FIELD_OFFSET(CSR_THREAD, ThreadHandle), csrt.ThreadHandle);
dprintf(
"\t+%04lx Flags %08lx\n"
"\t+%04lx ReferenceCount %08lx\n"
"\t+%04lx HashLinks.Flink %08lx\n"
"\t+%04lx HashLinks.Blink %08lx\n",
FIELD_OFFSET(CSR_THREAD, Flags), csrt.Flags,
FIELD_OFFSET(CSR_THREAD, ReferenceCount), csrt.ReferenceCount,
FIELD_OFFSET(CSR_THREAD, HashLinks.Flink), csrt.HashLinks.Flink,
FIELD_OFFSET(CSR_THREAD, HashLinks.Blink), csrt.HashLinks.Blink);
Exit:
EXIT_API();
}
//
// dp == dump process
//
// dp [v] [pid | pcsr_process]
// v == verbose (structure + thread list)
// no process == dump process list
//
// by scottlu
//
DECLARE_API( dp )
{
PLIST_ENTRY ListHead, ListNext;
char ach[80];
char chVerbose;
PCSR_PROCESS pcsrpT;
CSR_PROCESS csrp;
PCSR_PROCESS pcsrpRoot;
PCSR_THREAD pcsrt;
ULONG_PTR dwProcessId;
ULONG_PTR dw;
DWORD_PTR dwRootProcess;
BOOL b;
INIT_API();
while (*args == ' ')
args++;
chVerbose = ' ';
if (*args == 'v')
chVerbose = *args++;
dwRootProcess = GetExpression("csrsrv!CsrRootProcess");
if ( !dwRootProcess ) {
goto Exit;
}
b = ReadMemory( dwRootProcess, &pcsrpRoot, sizeof(pcsrpRoot), NULL);
if ( !b ) {
dprintf( "NTSDEXTS: Unable to read RootProcess\n" );
goto Exit;
}
//
// See if user wants all processes. If so loop through them.
//
if (*args == 0) {
ListHead = &pcsrpRoot->ListLink;
b = ReadMemory( (ULONG_PTR)(&ListHead->Flink), &ListNext, sizeof(ListNext), NULL);
if ( !b ) {
dprintf( "NTSDEXTS: Unable to read ListNext\n" );
goto Exit;
}
while (ListNext != ListHead) {
pcsrpT = CONTAINING_RECORD(ListNext, CSR_PROCESS, ListLink);
ach[0] = chVerbose;
ach[1] = ' ';
HexToString((ULONG_PTR)pcsrpT, &ach[2]);
dp(Client, ach);
b = ReadMemory( (ULONG_PTR)(&ListNext->Flink), &ListNext, sizeof(ListNext), NULL);
if ( !b ) {
dprintf( "NTSDEXTS: Unable to read ListNext\n" );
goto Exit;
}
}
dprintf("---\n");
goto Exit;
}
//
// User wants specific process structure. Evaluate to find id or process
// pointer.
//
dw = (ULONG)GetExpression(args);
ListHead = &pcsrpRoot->ListLink;
b = ReadMemory( (ULONG_PTR)(&ListHead->Flink), &ListNext, sizeof(ListNext), NULL);
if ( !b ) {
dprintf( "NTSDEXTS: Unable to read ListNext\n" );
goto Exit;
}
while (ListNext != ListHead) {
pcsrpT = CONTAINING_RECORD(ListNext, CSR_PROCESS, ListLink);
b = ReadMemory( (ULONG_PTR)(&ListNext->Flink), &ListNext, sizeof(ListNext), NULL);
if ( !b ) {
dprintf( "NTSDEXTS: Unable to read ListNext\n" );
goto Exit;
}
b = ReadMemory( (ULONG_PTR)(&pcsrpT->ClientId.UniqueProcess), &dwProcessId, sizeof(dwProcessId), NULL);
if ( !b ) {
dprintf( "NTSDEXTS: Unable to read ListNext\n" );
goto Exit;
}
if (dw == dwProcessId) {
dw = (ULONG_PTR)pcsrpT;
break;
}
}
pcsrpT = (PCSR_PROCESS)dw;
b = ReadMemory( (ULONG_PTR)pcsrpT, &csrp, sizeof(csrp), NULL);
if ( !b ) {
dprintf( "NTSDEXTS: Unable to read RootProcess\n" );
goto Exit;
}
//
// If not verbose, print simple process info.
//
if (chVerbose == ' ') {
dprintf("Process %08lx, Id %p, Seq# %lx, Flags %lx, Ref Count %lx\n",
pcsrpT,
csrp.ClientId.UniqueProcess,
csrp.SequenceNumber,
csrp.Flags,
csrp.ReferenceCount);
goto Exit;
}
dprintf("PCSR_PROCESS @ %08lx:\n"
"\t+%04lx ListLink.Flink %08lx\n"
"\t+%04lx ListLink.Blink %08lx\n",
pcsrpT,
FIELD_OFFSET(CSR_PROCESS, ListLink.Flink), csrp.ListLink.Flink,
FIELD_OFFSET(CSR_PROCESS, ListLink.Blink), csrp.ListLink.Blink);
dprintf(
"\t+%04lx ThreadList.Flink %08lx\n"
"\t+%04lx ThreadList.Blink %08lx\n"
"\t+%04lx NtSession %08lx\n"
"\t+%04lx ExpectedVersion %08lx\n",
FIELD_OFFSET(CSR_PROCESS, ThreadList.Flink), csrp.ThreadList.Flink,
FIELD_OFFSET(CSR_PROCESS, ThreadList.Blink), csrp.ThreadList.Blink,
FIELD_OFFSET(CSR_PROCESS, NtSession), csrp.NtSession,
FIELD_OFFSET(CSR_PROCESS, ExpectedVersion), csrp.ExpectedVersion);
dprintf(
"\t+%04lx ClientPort %08lx\n"
"\t+%04lx ClientViewBase %08lx\n"
"\t+%04lx ClientViewBounds %08lx\n"
"\t+%04lx ClientId.UniqueProcess %08lx\n",
FIELD_OFFSET(CSR_PROCESS, ClientPort), csrp.ClientPort,
FIELD_OFFSET(CSR_PROCESS, ClientViewBase), csrp.ClientViewBase,
FIELD_OFFSET(CSR_PROCESS, ClientViewBounds), csrp.ClientViewBounds,
FIELD_OFFSET(CSR_PROCESS, ClientId.UniqueProcess), csrp.ClientId.UniqueProcess);
dprintf(
"\t+%04lx ProcessHandle %08lx\n"
"\t+%04lx SequenceNumber %08lx\n"
"\t+%04lx Flags %08lx\n"
"\t+%04lx DebugFlags %08lx\n",
FIELD_OFFSET(CSR_PROCESS, ProcessHandle), csrp.ProcessHandle,
FIELD_OFFSET(CSR_PROCESS, SequenceNumber), csrp.SequenceNumber,
FIELD_OFFSET(CSR_PROCESS, Flags), csrp.Flags,
FIELD_OFFSET(CSR_PROCESS, DebugFlags), csrp.DebugFlags);
dprintf(
"\t+%04lx DebugUserInterface %08lx\n"
"\t+%04lx ReferenceCount %08lx\n"
"\t+%04lx ProcessGroupId %08lx\n"
"\t+%04lx ProcessGroupSequence %08lx\n",
FIELD_OFFSET(CSR_PROCESS, DebugUserInterface.UniqueProcess), csrp.DebugUserInterface.UniqueProcess,
FIELD_OFFSET(CSR_PROCESS, ReferenceCount), csrp.ReferenceCount,
FIELD_OFFSET(CSR_PROCESS, ProcessGroupId), csrp.ProcessGroupId,
FIELD_OFFSET(CSR_PROCESS, ProcessGroupSequence), csrp.ProcessGroupSequence);
dprintf(
"\t+%04lx fVDM %08lx\n"
"\t+%04lx ThreadCount %08lx\n"
"\t+%04lx PriorityClass %08lx\n"
"\t+%04lx ShutdownLevel %08lx\n"
"\t+%04lx ShutdownFlags %08lx\n",
FIELD_OFFSET(CSR_PROCESS, fVDM), csrp.fVDM,
FIELD_OFFSET(CSR_PROCESS, ThreadCount), csrp.ThreadCount,
FIELD_OFFSET(CSR_PROCESS, PriorityClass), csrp.PriorityClass,
FIELD_OFFSET(CSR_PROCESS, ShutdownLevel), csrp.ShutdownLevel,
FIELD_OFFSET(CSR_PROCESS, ShutdownFlags), csrp.ShutdownFlags);
//
// Now dump simple thread info for this processes' threads.
//
ListHead = &pcsrpT->ThreadList;
b = ReadMemory( (ULONG_PTR)(&ListHead->Flink), &ListNext, sizeof(ListNext), NULL);
if ( !b ) {
dprintf( "NTSDEXTS: Unable to read ListNext\n" );
goto Exit;
}
dprintf("Threads:\n");
while (ListNext != ListHead) {
pcsrt = CONTAINING_RECORD(ListNext, CSR_THREAD, Link);
//
// Make sure this pcsrt is somewhat real so we don't loop forever.
//
b = ReadMemory( (ULONG_PTR)(&pcsrt->ClientId.UniqueProcess), &dwProcessId, sizeof(dwProcessId), NULL);
if ( !b ) {
dprintf( "NTSDEXTS: Unable to read ListNext\n" );
goto Exit;
}
if (dwProcessId != (DWORD_PTR)csrp.ClientId.UniqueProcess) {
dprintf("Invalid thread. Probably invalid argument to this extension.\n");
goto Exit;
}
HexToString((ULONG_PTR)pcsrt, ach);
dt(Client, ach);
b = ReadMemory( (ULONG_PTR)(&ListNext->Flink), &ListNext, sizeof(ListNext), NULL);
if ( !b ) {
dprintf( "NTSDEXTS: Unable to read ListNext\n" );
goto Exit;
}
}
Exit:
EXIT_API();
}
VOID
DllsExtension(
PCSTR args,
PPEB ProcessPeb
);
DECLARE_API( gatom )
/*++
Routine Description:
This function is called as an NTSD extension to dump the global atom table
kept in kernel mode
Called as:
!gatom
Return Value:
None.
--*/
{
NTSTATUS Status;
ATOM_TABLE_INFORMATION TableInfo;
PATOM_TABLE_INFORMATION pTableInfo = NULL;
PATOM_BASIC_INFORMATION pBasicInfo = NULL;
ULONG RequiredLength, MaxLength, i;
INIT_API();
dprintf("\nGlobal atom table ");
Status = NtQueryInformationAtom( RTL_ATOM_INVALID_ATOM,
AtomTableInformation,
&TableInfo,
sizeof( TableInfo ),
&RequiredLength
);
if (Status != STATUS_INFO_LENGTH_MISMATCH) {
dprintf( " - cant get information - %x\n", Status );
goto Exit;
}
RequiredLength += 100 * sizeof( RTL_ATOM );
pTableInfo = LocalAlloc( 0, RequiredLength );
if (pTableInfo == NULL) {
dprintf( " - cant allocate memory for %u atoms\n", RequiredLength / sizeof( RTL_ATOM ) );
goto Exit;
}
Status = NtQueryInformationAtom( RTL_ATOM_INVALID_ATOM,
AtomTableInformation,
pTableInfo,
RequiredLength,
&RequiredLength
);
if (!NT_SUCCESS( Status )) {
dprintf( " - cant get information about %x atoms - %x\n", RequiredLength / sizeof( RTL_ATOM ), Status );
goto Exit;
}
MaxLength = sizeof( *pBasicInfo ) + RTL_ATOM_MAXIMUM_NAME_LENGTH;
pBasicInfo = LocalAlloc( 0, MaxLength );
if (!pBasicInfo) {
dprintf("LocalAlloc failed.\n");
goto Exit;
}
for (i=0; i<pTableInfo->NumberOfAtoms; i++) {
Status = NtQueryInformationAtom( pTableInfo->Atoms[ i ],
AtomBasicInformation,
pBasicInfo,
MaxLength,
&RequiredLength
);
if (!NT_SUCCESS( Status )) {
dprintf( "%hx *** query failed (%x)\n", pTableInfo->Atoms[ i ], Status );
}
else {
dprintf( "%hx(%2d) = %ls (%d)%s\n",
pTableInfo->Atoms[ i ],
pBasicInfo->UsageCount,
pBasicInfo->Name,
pBasicInfo->NameLength,
pBasicInfo->Flags & RTL_ATOM_PINNED ? " pinned" : ""
);
}
}
Exit:
if (pTableInfo != NULL) {
LocalFree (pTableInfo);
}
if (pBasicInfo != NULL) {
LocalFree (pBasicInfo);
}
EXIT_API();
}
/*++
Routine Description:
This function is called as an NTSD extension to mimic the !handle
kd command. This will walk through the debuggee's handle table
and duplicate the handle into the ntsd process, then call NtQueryobjectInfo
to find out what it is.
Called as:
!handle [handle [flags [Type]]]
If the handle is 0 or -1, all handles are scanned. If the handle is not
zero, that particular handle is examined. The flags are as follows
(corresponding to secexts.c):
1 - Get type information (default)
2 - Get basic information
4 - Get name information
8 - Get object specific info (where available)
If Type is specified, only object of that type are scanned. Type is a
standard NT type name, e.g. Event, Semaphore, etc. Case sensitive, of
course.
Examples:
!handle -- dumps the types of all the handles, and a summary table
!handle 0 0 -- dumps a summary table of all the open handles
!handle 0 f -- dumps everything we can find about a handle.
!handle 0 f Event
-- dumps everything we can find about open events
--*/
DECLARE_API( handle )
{
HANDLE hThere;
DWORD Type;
DWORD Mask;
DWORD HandleCount;
NTSTATUS Status;
DWORD Total;
DWORD TypeCounts[TYPE_MAX];
DWORD Handle;
DWORD Hits;
DWORD Matches;
DWORD ObjectType;
BOOL GetDirect;
ULONG SessionType;
ULONG SessionQual;
INIT_API();
Mask = GHI_TYPE ;
hThere = INVALID_HANDLE_VALUE;
Type = 0;
while (*args == ' ') {
args++;
}
if ( strcmp( args, "-?" ) == 0 )
{
help(Client, "handle" );
goto Exit;
}
hThere = (PVOID) GetExpression( args );
while (*args && (*args != ' ') ) {
args++;
}
while (*args == ' ') {
args++;
}
if (*args) {
Mask = (DWORD)GetExpression( args );
}
while (*args && (*args != ' ') ) {
args++;
}
while (*args == ' ') {
args++;
}
if (*args) {
Type = GetObjectTypeIndex( (LPSTR)args );
if (Type == (DWORD) -1 ) {
dprintf("Unknown type '%s'\n", args );
goto Exit;
}
}
//
// if they specified 0, they just want the summary. Make sure nothing
// sneaks out.
//
if ( Mask == 0 ) {
Mask = GHI_SILENT;
}
//
// If this is a dump debug session,
// check and see whether we can retrieve handle
// information through the engine interface.
//
if (g_ExtControl == NULL ||
g_ExtControl->lpVtbl->
GetDebuggeeType(g_ExtControl, &SessionType, &SessionQual) != S_OK) {
SessionType = DEBUG_CLASS_USER_WINDOWS;
SessionQual = DEBUG_USER_WINDOWS_PROCESS;
}
if (SessionType == DEBUG_CLASS_USER_WINDOWS &&
SessionQual != DEBUG_USER_WINDOWS_PROCESS) {
// This is a dump or remote session so we have to use
// the stored handle information accessible
// through the interface.
if (g_ExtData2 == NULL ||
g_ExtData2->lpVtbl->
ReadHandleData(g_ExtData2, 0, DEBUG_HANDLE_DATA_TYPE_HANDLE_COUNT,
&HandleCount, sizeof(HandleCount),
NULL) != S_OK) {
dprintf("Unable to read handle information\n");
goto Exit;
}
GetDirect = FALSE;
} else {
// This is a live session so we can make direct NT calls.
// More information is available this way so we use it
// whenever we can.
GetDirect = TRUE;
}
//
// hThere of 0 indicates all handles.
//
if ((hThere == 0) || (hThere == INVALID_HANDLE_VALUE)) {
if (GetDirect) {
Status = NtQueryInformationProcess( g_hCurrentProcess,
ProcessHandleCount,
&HandleCount,
sizeof( HandleCount ),
NULL );
if ( !NT_SUCCESS( Status ) ) {
goto Exit;
}
}
Hits = 0;
Handle = 0;
Matches = 0;
ZeroMemory( TypeCounts, sizeof(TypeCounts) );
while ( Hits < HandleCount ) {
if ( Type ) {
if (GetHandleInfo( GetDirect, g_hCurrentProcess,
(HANDLE) (DWORD_PTR) Handle,
GHI_TYPE | GHI_SILENT,
&ObjectType ) ) {
Hits++;
if ( ObjectType == Type ) {
GetHandleInfo( GetDirect, g_hCurrentProcess,
(HANDLE)(DWORD_PTR)Handle,
Mask,
&ObjectType );
Matches ++;
}
}
} else {
if (GetHandleInfo( GetDirect, g_hCurrentProcess,
(HANDLE)(DWORD_PTR)Handle,
GHI_TYPE | GHI_SILENT,
&ObjectType) ) {
Hits++;
TypeCounts[ ObjectType ] ++;
GetHandleInfo( GetDirect, g_hCurrentProcess,
(HANDLE)(DWORD_PTR)Handle,
Mask,
&ObjectType );
}
}
Handle += 4;
}
if ( Type == 0 ) {
dprintf( "%d Handles\n", Hits );
dprintf( "Type \tCount\n");
for (Type = 0; Type < TYPE_MAX ; Type++ ) {
if (TypeCounts[Type]) {
dprintf("%-15ws\t%d\n", pszTypeNames[Type], TypeCounts[Type]);
}
}
} else {
dprintf("%d handles of type %ws\n", Matches, pszTypeNames[Type] );
}
} else {
GetHandleInfo( GetDirect, g_hCurrentProcess, hThere, Mask, &Type );
}
Exit:
EXIT_API();
}
DECLARE_API( threadtoken )
{
HANDLE hToken ;
NTSTATUS Status ;
INIT_API();
dprintf("\n\n!threadtoken has been replaced by !token (in exts.dll).\n\n");
EXIT_API();
}
#define PAGE_ALL (PAGE_READONLY|\
PAGE_READWRITE|\
PAGE_WRITECOPY|\
PAGE_EXECUTE|\
PAGE_EXECUTE_READ|\
PAGE_EXECUTE_READWRITE|\
PAGE_EXECUTE_WRITECOPY|\
PAGE_NOACCESS)
VOID
printflags(
DWORD Flags
)
{
switch (Flags & PAGE_ALL) {
case PAGE_READONLY:
dprintf("PAGE_READONLY");
break;
case PAGE_READWRITE:
dprintf("PAGE_READWRITE");
break;
case PAGE_WRITECOPY:
dprintf("PAGE_WRITECOPY");
break;
case PAGE_EXECUTE:
dprintf("PAGE_EXECUTE");
break;
case PAGE_EXECUTE_READ:
dprintf("PAGE_EXECUTE_READ");
break;
case PAGE_EXECUTE_READWRITE:
dprintf("PAGE_EXECUTE_READWRITE");
break;
case PAGE_EXECUTE_WRITECOPY:
dprintf("PAGE_EXECUTE_WRITECOPY");
break;
case PAGE_NOACCESS:
if ((Flags & ~PAGE_NOACCESS) == 0) {
dprintf("PAGE_NOACCESS");
break;
} // else fall through
default:
dprintf("*** Invalid page protection ***\n");
return;
break;
}
if (Flags & PAGE_NOCACHE) {
dprintf(" + PAGE_NOCACHE");
}
if (Flags & PAGE_GUARD) {
dprintf(" + PAGE_GUARD");
}
dprintf("\n");
}
/*++
Routine Description:
This function is called as an NTSD extension to dump registry information
Called as:
!dreg -[d|w] <keyPath>[![<valueName> | *]]
Arguments:
hCurrentProcess - Supplies a handle to the current process (at the
time the extension was called).
hCurrentThread - Supplies a handle to the current thread (at the
time the extension was called).
CurrentPc - Supplies the current pc at the time the extension is
called.
lpExtensionApis - Supplies the address of the functions callable
by this extension.
args - Supplies the pattern and expression for this
command.
Return Value:
None.
--*/
DECLARE_API( dreg )
{
DWORD opts = 1;
INIT_API();
// Skip past leading spaces
while (*args == ' ')
{
args++;
}
if (*args == '-')
{
args++;
switch (*args)
{
case 'd':
opts = 4;
break;
case 'w':
opts = 2;
break;
default:
opts = 1;
break;
}
if (*args)
{
// expect a space between options
args++;
// Skip past leading spaces
while (*args == ' ')
{
args++;
}
}
}
Idreg(opts, (LPSTR)args);
EXIT_API();
}
NTSTATUS
GetHandleTraceInfo(
PPROCESS_HANDLE_TRACING_QUERY *Info,
PULONG BufferSize
)
{
NTSTATUS Status = NO_ERROR;
ULONG CurrentBufferSize = *BufferSize;
if (*BufferSize == 0)
{
CurrentBufferSize = sizeof (PROCESS_HANDLE_TRACING_QUERY);
}
while (TRUE) {
//
// Allocate a new buffer
//
*Info = (PPROCESS_HANDLE_TRACING_QUERY)malloc (CurrentBufferSize);
if (*Info == NULL) {
dprintf ("ERROR: Cannot allocate buffer with size 0x%p\n",
CurrentBufferSize);
Status = ERROR_OUTOFMEMORY;
goto DoneAll;
}
ZeroMemory( *Info,
CurrentBufferSize );
Status = NtQueryInformationProcess (g_hCurrentProcess,
ProcessHandleTracing,
*Info,
CurrentBufferSize,
NULL );
if( NT_SUCCESS (Status) ) {
//
// We have all the information ready
//
break;
}
CurrentBufferSize = sizeof (PROCESS_HANDLE_TRACING_QUERY) + (*Info)->TotalTraces * sizeof ((*Info)->HandleTrace[ 0 ]);
free (*Info);
*Info = NULL;
if( CheckControlC() ) {
goto DoneAll;
}
if (Status != STATUS_INFO_LENGTH_MISMATCH) {
//
// No reason to try querying again
//
if (Status == STATUS_INVALID_PARAMETER) {
dprintf ("App verifier handle tracing is not enabled for this process.\n");
}
else {
dprintf ("Query process information failed, status 0x%X\n",
Status);
}
goto DoneAll;
}
//
// Try allocating another buffer with the new size
//
}
*BufferSize = CurrentBufferSize;
return NO_ERROR;
DoneAll:
free (*Info);
*Info = NULL;
return Status;
}
HRESULT
_EFN_GetHandleTrace(
PDEBUG_CLIENT Client,
ULONG TraceType,
ULONG StartIndex,
PULONG64 HandleValue,
PULONG64 StackFunctions,
ULONG StackTraceSize
)
/*+++
This extension function returns handle trace info for a given type of trace,
or handle.
Arguments:
If TraceType is set it returns first trace found of that type.
If StartIndex is set it starts looking for traces from that index onwards
If HandleValue is set it returns first stack found of that handle
Return Value:
S_OK for success.
--*/
{
HRESULT Hr = E_FAIL;
PPROCESS_HANDLE_TRACING_QUERY Info = NULL;
ULONG InfoSize = 0;
ULONG CrtStackTrace;
INIT_API();
if (NT_SUCCESS(Hr = GetHandleTraceInfo(&Info, &InfoSize)) && (Info != NULL))
{
for (CrtStackTrace = StartIndex; CrtStackTrace < Info->TotalTraces; CrtStackTrace += 1) {
if( CheckControlC() ) {
CrtStackTrace += 1;
goto Done;
}
if (*HandleValue == 0 || *HandleValue == (ULONG64) Info->HandleTrace[ CrtStackTrace ].Handle) {
if (Info->HandleTrace[ CrtStackTrace ].Type == TraceType ||
TraceType == 0)
{
ULONG CapturedAddressIndex;
PVOID *CrtStack;
PVOID CapturedAddress;
*HandleValue = (ULONG64) Info->HandleTrace[ CrtStackTrace ].Handle;
Hr = S_OK;
for (CapturedAddressIndex = 0, CrtStack = &Info->HandleTrace[ CrtStackTrace ].Stacks[ 0 ];
CapturedAddressIndex < (sizeof(Info->HandleTrace[ CrtStackTrace ].Stacks) /
sizeof(Info->HandleTrace[ CrtStackTrace ].Stacks[0]));
CapturedAddressIndex += 1, CrtStack += 1) {
if( CheckControlC() ) {
CrtStackTrace += 1;
goto Done;
}
CapturedAddress = *CrtStack;
if (CapturedAddress == NULL) {
//
// Done with dumping this stack trace
//
goto Done;
}
if (StackTraceSize > CapturedAddressIndex)
{
if (sizeof(ULONG64) == sizeof(PVOID))
{
StackFunctions[CapturedAddressIndex] = (ULONG64) (LONG64) (ULONG_PTR) CapturedAddress;
} else
{
StackFunctions[CapturedAddressIndex] = (ULONG64) (LONG64) (LONG) (ULONG_PTR) CapturedAddress;
}
}
}
goto Done;
}
}
}
}
Done:
if (Info)
{
free (Info);
}
EXIT_API();
return Hr;
}
/*++
Routine Description:
This function is called as an NTSD extension to dump handle tracing information
Called as:
!htrace [handle]
Arguments:
args - Supplies the pattern and expression for this
command.
Return Value:
None.
--*/
DECLARE_API( htrace )
{
HANDLE Handle;
PPROCESS_HANDLE_TRACING_QUERY Info;
ULONG_PTR Displacement;
NTSTATUS Status;
ULONG CurrentBufferSize;
ULONG CrtStackTrace;
ULONG EntriesDisplayed;
ULONG CapturedAddressIndex;
PVOID *CrtStack;
PVOID CapturedAddress;
SYSTEM_BASIC_INFORMATION SysBasicInfo;
CHAR Symbol[ 1024 ];
INIT_API();
Info = NULL;
CrtStackTrace = 0;
EntriesDisplayed = 0;
//
// Did the user ask for some help?
//
if (strcmp (args, "-?") == 0 ||
strcmp (args, "?") == 0 ||
strcmp (args, "-h") == 0) {
dprintf ("!htrace [handle]\n");
goto DoneAll;
}
//
// Get the handle from the command line
//
Handle = (HANDLE)GetExpression (args);
//
// Get the stack traces using NtQueryInformationProcess
//
Info = NULL; CurrentBufferSize = 0;
Status = GetHandleTraceInfo(&Info, &CurrentBufferSize);
if (!NT_SUCCESS(Status))
{
goto DoneAll;
}
//
// If we have 0 stack traces there is nothing we can dump
//
if (Info->TotalTraces == 0) {
dprintf( "No stack traces available.\n" );
goto DoneAll;
}
//
// Find out the highest user address because
// we will skip kernel mode addresses from the stack traces.
//
Status = NtQuerySystemInformation (SystemBasicInformation,
&SysBasicInfo,
sizeof (SysBasicInfo),
NULL);
if (!NT_SUCCESS (Status)) {
dprintf ("Query system basic information failed, status 0x%X\n",
Status);
goto DoneAll;
}
//
// Dump all the stack traces.
//
for (CrtStackTrace = 0; CrtStackTrace < Info->TotalTraces; CrtStackTrace += 1) {
if( CheckControlC() ) {
CrtStackTrace += 1;
goto DoneDumping;
}
if (Handle == 0 || Handle == Info->HandleTrace[ CrtStackTrace ].Handle) {
EntriesDisplayed += 1;
dprintf ("--------------------------------------\n"
"Handle = 0x%p - ",
Info->HandleTrace[ CrtStackTrace ].Handle);
switch( Info->HandleTrace[ CrtStackTrace ].Type ) {
case HANDLE_TRACE_DB_OPEN:
dprintf( "OPEN:\n" );
break;
case HANDLE_TRACE_DB_CLOSE:
dprintf( "CLOSE:\n" );
break;
case HANDLE_TRACE_DB_BADREF:
dprintf( "*** BAD REFERENCE ***:\n" );
break;
default:
dprintf( "Invalid operation type: %u\n",
Info->HandleTrace[ CrtStackTrace ].Type );
goto DoneDumping;
}
for (CapturedAddressIndex = 0, CrtStack = &Info->HandleTrace[ CrtStackTrace ].Stacks[ 0 ];
CapturedAddressIndex < (sizeof(Info->HandleTrace[ CrtStackTrace ].Stacks) /
sizeof(Info->HandleTrace[ CrtStackTrace ].Stacks[0]));
CapturedAddressIndex += 1, CrtStack += 1) {
if( CheckControlC() ) {
CrtStackTrace += 1;
goto DoneDumping;
}
CapturedAddress = *CrtStack;
if (CapturedAddress == NULL) {
//
// Done with dumping this stack trace
//
break;
}
if ((ULONG_PTR)CapturedAddress > SysBasicInfo.MaximumUserModeAddress) {
//
// Skip kernel-mode addresses
//
continue;
}
GetSymbol (CapturedAddress,
Symbol,
&Displacement);
dprintf ("0x%p: %s+0x%p\n",
CapturedAddress,
Symbol,
Displacement );
}
}
}
DoneDumping:
dprintf ("\n--------------------------------------\n"
"Parsed 0x%X stack traces.\n"
"Dumped 0x%X stack traces.\n",
CrtStackTrace,
EntriesDisplayed);
DoneAll:
if (Info != NULL) {
free (Info);
}
EXIT_API();
}