1073 lines
26 KiB
C
1073 lines
26 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1998 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
stacks.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
WinDbg Extension Api
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Adrian J. Oney (adriao) 07-28-1998
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
User Mode.
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "precomp.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
typedef enum _KTHREAD_STATE {
|
||
|
Initialized,
|
||
|
Ready,
|
||
|
Running,
|
||
|
Standby,
|
||
|
Terminated,
|
||
|
Waiting,
|
||
|
Transition
|
||
|
} KTHREAD_STATE;
|
||
|
|
||
|
typedef enum {
|
||
|
|
||
|
NO_STACK_ACTION = 0,
|
||
|
SKIP_FRAME,
|
||
|
SKIP_THREAD
|
||
|
|
||
|
} STACKS_ACTION, *PSTACKS_ACTION;
|
||
|
|
||
|
#define ETHREAD_NOT_READABLE 1
|
||
|
#define THREAD_VALID 2
|
||
|
#define FIRST_THREAD_VALID 3
|
||
|
#define NO_THREADS 4
|
||
|
|
||
|
struct _BLOCKER_TREE ;
|
||
|
typedef struct _BLOCKER_TREE BLOCKER_TREE, *PBLOCKER_TREE ;
|
||
|
|
||
|
BOOL
|
||
|
StacksValidateProcess(
|
||
|
IN ULONG64 RealProcessBase
|
||
|
);
|
||
|
|
||
|
BOOL
|
||
|
StacksValidateThread(
|
||
|
IN ULONG64 RealThreadBase
|
||
|
);
|
||
|
|
||
|
VOID StacksDumpProcessAndThread(
|
||
|
IN ULONG64 RealProcessBase,
|
||
|
IN ULONG ThreadDesc,
|
||
|
IN ULONG64 RealThreadBase,
|
||
|
IN PBLOCKER_TREE BlockerTree,
|
||
|
IN ULONG Verbosity,
|
||
|
IN char * Filter
|
||
|
);
|
||
|
|
||
|
VOID StacksGetThreadStateName(
|
||
|
IN ULONG ThreadState,
|
||
|
OUT PCHAR Dest
|
||
|
);
|
||
|
|
||
|
VOID
|
||
|
DumpThreadBlockageInfo (
|
||
|
IN char *pad,
|
||
|
IN ULONG64 RealThreadBase,
|
||
|
IN ULONG Verbosity
|
||
|
);
|
||
|
|
||
|
typedef enum {
|
||
|
|
||
|
STACK_WALK_DUMP_STARTING = 0,
|
||
|
STACK_WALK_DUMP_NOT_RESIDENT,
|
||
|
STACK_WALK_DUMP_ENTRY,
|
||
|
STACK_WALK_DUMP_FINISHED
|
||
|
|
||
|
} WALK_STAGE;
|
||
|
|
||
|
typedef struct {
|
||
|
|
||
|
ULONG Verbosity;
|
||
|
BOOLEAN FirstEntry;
|
||
|
char* ThreadState;
|
||
|
char* ThreadBlocker;
|
||
|
ULONG ProcessCid;
|
||
|
ULONG ThreadCid;
|
||
|
ULONG64 ThreadBlockerDisplacement;
|
||
|
|
||
|
} STACK_DUMP_CONTEXT, *PSTACK_DUMP_CONTEXT;
|
||
|
|
||
|
typedef BOOLEAN (*PFN_FRAME_WALK_CALLBACK)(
|
||
|
IN WALK_STAGE WalkStage,
|
||
|
IN ULONG64 RealThreadBase,
|
||
|
IN PVOID Context,
|
||
|
IN char * Buffer,
|
||
|
IN ULONG64 Offset
|
||
|
);
|
||
|
|
||
|
VOID
|
||
|
ForEachFrameOnThread(
|
||
|
IN ULONG64 RealThreadBase,
|
||
|
IN PFN_FRAME_WALK_CALLBACK Callback,
|
||
|
IN PVOID Context
|
||
|
);
|
||
|
|
||
|
BOOLEAN
|
||
|
StacksDumpStackCallback(
|
||
|
IN WALK_STAGE WalkStage,
|
||
|
IN ULONG64 RealThreadBase,
|
||
|
IN PVOID Context,
|
||
|
IN char * Buffer,
|
||
|
IN ULONG64 Offset
|
||
|
);
|
||
|
|
||
|
extern ULONG64 STeip, STebp, STesp;
|
||
|
static ULONG64 PspCidTable;
|
||
|
|
||
|
ULONG64 ProcessLastDump;
|
||
|
ULONG64 ThreadLastDump;
|
||
|
|
||
|
ULONG TotalProcessCommit;
|
||
|
|
||
|
struct _BLOCKER_TREE {
|
||
|
char const *Symbolic ;
|
||
|
STACKS_ACTION Action ;
|
||
|
PBLOCKER_TREE Child ;
|
||
|
PBLOCKER_TREE Sibling ;
|
||
|
PBLOCKER_TREE Parent ;
|
||
|
BOOL Nested ;
|
||
|
} ;
|
||
|
|
||
|
VOID
|
||
|
AnalyzeThread(
|
||
|
IN ULONG64 RealThreadBase,
|
||
|
IN PBLOCKER_TREE BlockerTree,
|
||
|
IN char * Filter,
|
||
|
OUT PCHAR BlockSymbol,
|
||
|
OUT ULONG64 *BlockDisplacement,
|
||
|
OUT BOOLEAN *SkipThread
|
||
|
);
|
||
|
|
||
|
BOOL
|
||
|
BlockerTreeWalk(
|
||
|
IN OUT PBLOCKER_TREE *blockerHead,
|
||
|
IN char *szSymbolic,
|
||
|
IN STACKS_ACTION Action
|
||
|
);
|
||
|
|
||
|
PBLOCKER_TREE
|
||
|
BlockerTreeBuild(
|
||
|
VOID
|
||
|
) ;
|
||
|
|
||
|
VOID
|
||
|
BlockerTreeFree(
|
||
|
IN PBLOCKER_TREE BlockerTree
|
||
|
) ;
|
||
|
|
||
|
DECLARE_API( stacks )
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Dumps the active process list.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
ULONG Result;
|
||
|
ULONG64 Next;
|
||
|
ULONG64 NextThread;
|
||
|
ULONG64 ProcessHead;
|
||
|
ULONG64 Process;
|
||
|
ULONG64 Thread;
|
||
|
ULONG64 UserProbeAddress;
|
||
|
ULONG Verbosity = 0, ThreadCount = 0;
|
||
|
ULONG ActProcOffset, ThrdListOffset, ThrdEntryOffset;
|
||
|
PBLOCKER_TREE blockerTree ;
|
||
|
char szFilter[256];
|
||
|
|
||
|
blockerTree = BlockerTreeBuild() ;
|
||
|
|
||
|
if (sscanf(args, "%x %s", &Verbosity, szFilter) < 2) {
|
||
|
|
||
|
szFilter[0] = '\0';
|
||
|
}
|
||
|
|
||
|
dprintf("Proc.Thread .Thread ThreadState Blocker\n") ;
|
||
|
|
||
|
UserProbeAddress = GetNtDebuggerDataValue(MmUserProbeAddress);
|
||
|
ProcessHead = GetNtDebuggerData( PsActiveProcessHead );
|
||
|
if (!ProcessHead) {
|
||
|
dprintf("Unable to get value of PsActiveProcessHead!\n");
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
if (GetFieldValue( ProcessHead, "nt!_LIST_ENTRY", "Flink", Next )) {
|
||
|
dprintf("Unable to get value of PsActiveProcessHead.Flink\n");
|
||
|
goto Exit ;
|
||
|
}
|
||
|
|
||
|
if (Next == 0) {
|
||
|
dprintf("PsActiveProcessHead is NULL!\n");
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
GetFieldOffset("nt!_EPROCESS", "ActiveProcessLinks", &ActProcOffset);
|
||
|
GetFieldOffset("nt!_EPROCESS", "Pcb.ThreadListHead", &ThrdListOffset);
|
||
|
GetFieldOffset("nt!_KTHREAD", "ThreadListEntry", &ThrdEntryOffset);
|
||
|
|
||
|
while(Next != ProcessHead) {
|
||
|
ULONG64 FirstThread;
|
||
|
|
||
|
Process = Next - ActProcOffset;
|
||
|
|
||
|
if (GetFieldValue( Process, "nt!_EPROCESS", "Pcb.ThreadListHead.Flink", FirstThread )) {
|
||
|
dprintf("Unable to read nt!_EPROCESS at %p\n",Process);
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
NextThread = FirstThread;
|
||
|
if (!StacksValidateProcess(Process)) {
|
||
|
|
||
|
dprintf("Process list damaged, or maybe symbols are incorrect?\n%p\n",Process);
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
if (NextThread == Process + ThrdListOffset) {
|
||
|
|
||
|
StacksDumpProcessAndThread(Process, NO_THREADS, 0, blockerTree, Verbosity, szFilter) ;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
while ( NextThread != Process + ThrdListOffset) {
|
||
|
ULONG64 ThreadFlink;
|
||
|
|
||
|
Thread = NextThread - ThrdEntryOffset;
|
||
|
if (GetFieldValue(Thread,
|
||
|
"nt!_ETHREAD",
|
||
|
"Tcb.ThreadListEntry.Flink",
|
||
|
ThreadFlink)) {
|
||
|
|
||
|
StacksDumpProcessAndThread(Process, ETHREAD_NOT_READABLE, 0, blockerTree, Verbosity, szFilter) ;
|
||
|
|
||
|
dprintf("Unable to read _ETHREAD at %lx\n",Thread);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!StacksValidateThread(Thread)) {
|
||
|
|
||
|
StacksDumpProcessAndThread( Process, ETHREAD_NOT_READABLE, 0, blockerTree, Verbosity, szFilter) ;
|
||
|
} else if (NextThread == FirstThread) {
|
||
|
|
||
|
ThreadCount++;
|
||
|
StacksDumpProcessAndThread(Process, FIRST_THREAD_VALID, Thread, blockerTree, Verbosity, szFilter) ;
|
||
|
} else {
|
||
|
|
||
|
ThreadCount++;
|
||
|
StacksDumpProcessAndThread(Process, THREAD_VALID, Thread, blockerTree, Verbosity, szFilter) ;
|
||
|
}
|
||
|
|
||
|
NextThread = ThreadFlink;
|
||
|
if (CheckControlC()) {
|
||
|
goto Exit;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
EXPRLastDump = Process;
|
||
|
ProcessLastDump = Process;
|
||
|
dprintf("\n");
|
||
|
GetFieldValue( Process, "nt!_EPROCESS", "ActiveProcessLinks.Flink", Next);
|
||
|
|
||
|
if (CheckControlC()) {
|
||
|
goto Exit;
|
||
|
}
|
||
|
}
|
||
|
Exit:
|
||
|
BlockerTreeFree(blockerTree) ;
|
||
|
dprintf("\nThreads Processed: %d\n", ThreadCount);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
StacksValidateProcess(
|
||
|
IN ULONG64 RealProcessBase
|
||
|
)
|
||
|
{
|
||
|
ULONG Type;
|
||
|
|
||
|
GetFieldValue(RealProcessBase, "nt!_EPROCESS", "Pcb.Header.Type", Type);
|
||
|
if (Type != ProcessObject) {
|
||
|
dprintf("TYPE mismatch for process object at %p\n",RealProcessBase);
|
||
|
return FALSE;
|
||
|
}
|
||
|
return TRUE ;
|
||
|
}
|
||
|
|
||
|
VOID StacksDumpProcessAndThread(
|
||
|
IN ULONG64 RealProcessBase,
|
||
|
IN ULONG ThreadDesc,
|
||
|
IN ULONG64 RealThreadBase,
|
||
|
IN PBLOCKER_TREE BlockerTree,
|
||
|
IN ULONG Verbosity,
|
||
|
IN char * Filter
|
||
|
)
|
||
|
{
|
||
|
ULONG NumberOfHandles;
|
||
|
ULONG Result;
|
||
|
CHAR Buf[512];
|
||
|
CHAR ThreadState[13] ;
|
||
|
CHAR ThreadBlocker[256] ;
|
||
|
UINT i ;
|
||
|
ULONG64 ObjectTable, ThreadBlockerDisplacement;
|
||
|
CHAR *ThreadStateName ;
|
||
|
ULONG UniqueProcessCid;
|
||
|
STACK_DUMP_CONTEXT dumpData;
|
||
|
BOOLEAN SkipThread;
|
||
|
|
||
|
NumberOfHandles = 0;
|
||
|
GetFieldValue(RealProcessBase, "nt!_EPROCESS", "ImageFileName", Buf);
|
||
|
|
||
|
InitTypeRead(RealProcessBase, nt!_EPROCESS);
|
||
|
|
||
|
if (ObjectTable = ReadField(ObjectTable)) {
|
||
|
GetFieldValue(ObjectTable, "nt!_HANDLE_TABLE", "HandleCount", NumberOfHandles);
|
||
|
}
|
||
|
|
||
|
if (Buf[0] == '\0' ) {
|
||
|
strcpy((char *)Buf,"System Process");
|
||
|
}
|
||
|
|
||
|
UniqueProcessCid = (ULONG) ReadField(UniqueProcessId);
|
||
|
|
||
|
if (!RealThreadBase) {
|
||
|
|
||
|
switch(ThreadDesc) {
|
||
|
|
||
|
case NO_THREADS:
|
||
|
|
||
|
dprintf(" [%p %s]\n", RealProcessBase, Buf) ;
|
||
|
|
||
|
if ((Verbosity > 0) && (Filter[0] == '\0')) {
|
||
|
dprintf("%4lx.------ NOTHREADS\n",
|
||
|
UniqueProcessCid
|
||
|
);
|
||
|
}
|
||
|
|
||
|
break ;
|
||
|
|
||
|
case ETHREAD_NOT_READABLE:
|
||
|
|
||
|
dprintf("%4lx.------ NO ETHREAD DATA\n",
|
||
|
UniqueProcessCid
|
||
|
);
|
||
|
|
||
|
break ;
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
InitTypeRead(RealThreadBase, nt!_ETHREAD);
|
||
|
|
||
|
ASSERT(((ULONG) ReadField(Cid.UniqueProcess)) == UniqueProcessCid);
|
||
|
|
||
|
StacksGetThreadStateName((ULONG) ReadField(Tcb.State), ThreadState) ;
|
||
|
i=strlen(ThreadState) ;
|
||
|
while(i<11) ThreadState[i++]=' ' ;
|
||
|
ThreadState[i]='\0' ;
|
||
|
|
||
|
AnalyzeThread(
|
||
|
RealThreadBase,
|
||
|
BlockerTree,
|
||
|
Filter,
|
||
|
ThreadBlocker,
|
||
|
&ThreadBlockerDisplacement,
|
||
|
&SkipThread
|
||
|
);
|
||
|
|
||
|
if (ThreadDesc == FIRST_THREAD_VALID) {
|
||
|
|
||
|
dprintf(" [%p %s]\n", RealProcessBase, Buf) ;
|
||
|
}
|
||
|
|
||
|
if (SkipThread && ((Verbosity == 0) || (Filter[0]))) {
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
dumpData.Verbosity = Verbosity;
|
||
|
dumpData.ThreadState = ThreadState;
|
||
|
dumpData.ThreadBlocker = ThreadBlocker;
|
||
|
dumpData.ThreadBlockerDisplacement = ThreadBlockerDisplacement;
|
||
|
dumpData.ProcessCid = UniqueProcessCid;
|
||
|
dumpData.ThreadCid = (ULONG) ReadField(Cid.UniqueThread);
|
||
|
|
||
|
ForEachFrameOnThread(
|
||
|
RealThreadBase,
|
||
|
StacksDumpStackCallback,
|
||
|
(PVOID) &dumpData
|
||
|
);
|
||
|
}
|
||
|
|
||
|
BOOLEAN
|
||
|
StacksDumpStackCallback(
|
||
|
IN WALK_STAGE WalkStage,
|
||
|
IN ULONG64 RealThreadBase,
|
||
|
IN PVOID Context,
|
||
|
IN char * Buffer,
|
||
|
IN ULONG64 Offset
|
||
|
)
|
||
|
{
|
||
|
PSTACK_DUMP_CONTEXT dumpData = (PSTACK_DUMP_CONTEXT) Context;
|
||
|
|
||
|
switch(WalkStage) {
|
||
|
|
||
|
case STACK_WALK_DUMP_STARTING:
|
||
|
|
||
|
dprintf("%4lx.%06lx %08p %s",
|
||
|
dumpData->ProcessCid,
|
||
|
dumpData->ThreadCid,
|
||
|
RealThreadBase,
|
||
|
dumpData->ThreadState
|
||
|
);
|
||
|
|
||
|
dumpData->FirstEntry = TRUE;
|
||
|
return TRUE;
|
||
|
|
||
|
case STACK_WALK_DUMP_FINISHED:
|
||
|
|
||
|
dumpData->FirstEntry = FALSE;
|
||
|
dprintf("\n");
|
||
|
return TRUE;
|
||
|
|
||
|
case STACK_WALK_DUMP_NOT_RESIDENT:
|
||
|
case STACK_WALK_DUMP_ENTRY:
|
||
|
|
||
|
if (dumpData->FirstEntry) {
|
||
|
|
||
|
dumpData->FirstEntry = FALSE;
|
||
|
} else {
|
||
|
|
||
|
dprintf("\n ");
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (WalkStage == STACK_WALK_DUMP_NOT_RESIDENT) {
|
||
|
|
||
|
switch(dumpData->Verbosity) {
|
||
|
|
||
|
case 0:
|
||
|
case 1:
|
||
|
case 2:
|
||
|
dprintf("Stack paged out");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
switch(dumpData->Verbosity) {
|
||
|
|
||
|
case 0:
|
||
|
case 1:
|
||
|
dprintf("%s", dumpData->ThreadBlocker);
|
||
|
if (dumpData->ThreadBlockerDisplacement) {
|
||
|
dprintf( "+0x%1p", dumpData->ThreadBlockerDisplacement );
|
||
|
}
|
||
|
dprintf("\n");
|
||
|
return FALSE;
|
||
|
|
||
|
case 2:
|
||
|
dprintf("%s", Buffer);
|
||
|
if (Offset) {
|
||
|
dprintf( "+0x%1p", Offset );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
UCHAR *StacksWaitReasonList[] = {
|
||
|
"Executive",
|
||
|
"FreePage",
|
||
|
"PageIn",
|
||
|
"PoolAllocation",
|
||
|
"DelayExecution",
|
||
|
"Suspended",
|
||
|
"UserRequest",
|
||
|
"WrExecutive",
|
||
|
"WrFreePage",
|
||
|
"WrPageIn",
|
||
|
"WrPoolAllocation",
|
||
|
"WrDelayExecution",
|
||
|
"WrSuspended",
|
||
|
"WrUserRequest",
|
||
|
"WrEventPairHigh",
|
||
|
"WrEventPairLow",
|
||
|
"WrLpcReceive",
|
||
|
"WrLpcReply",
|
||
|
"WrVirtualMemory",
|
||
|
"WrPageOut",
|
||
|
"Spare1",
|
||
|
"Spare2",
|
||
|
"Spare3",
|
||
|
"Spare4",
|
||
|
"Spare5",
|
||
|
"Spare6",
|
||
|
"Spare7"};
|
||
|
|
||
|
|
||
|
VOID StacksGetThreadStateName(
|
||
|
IN ULONG ThreadState,
|
||
|
OUT PCHAR Dest
|
||
|
)
|
||
|
{
|
||
|
switch (ThreadState) {
|
||
|
case Initialized: strcpy(Dest, "INITIALIZED"); break;
|
||
|
case Ready: strcpy(Dest, "READY"); break;
|
||
|
case Running: strcpy(Dest, "RUNNING"); break;
|
||
|
case Standby: strcpy(Dest, "STANDBY"); break;
|
||
|
case Terminated: strcpy(Dest, "TERMINATED"); break;
|
||
|
case Waiting: strcpy(Dest, "Blocked"); break;
|
||
|
case Transition: strcpy(Dest, "TRANSITION"); break;
|
||
|
default: strcpy(Dest, "????") ; break ;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
StacksValidateThread (
|
||
|
IN ULONG64 RealThreadBase
|
||
|
)
|
||
|
{
|
||
|
ULONG Type;
|
||
|
|
||
|
GetFieldValue(RealThreadBase, "nt!_ETHREAD", "Tcb.Header.Type", Type);
|
||
|
if (Type != ThreadObject) {
|
||
|
dprintf("TYPE mismatch for thread object at %p\n",RealThreadBase);
|
||
|
return FALSE;
|
||
|
}
|
||
|
return TRUE ;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
DumpThreadBlockageInfo (
|
||
|
IN char *Pad,
|
||
|
IN ULONG64 RealThreadBase,
|
||
|
IN ULONG Verbosity
|
||
|
)
|
||
|
{
|
||
|
#define MAX_STACK_FRAMES 40
|
||
|
TIME_FIELDS Times;
|
||
|
LARGE_INTEGER RunTime;
|
||
|
ULONG64 Address;
|
||
|
ULONG Result;
|
||
|
ULONG64 WaitBlock;
|
||
|
ULONG WaitOffset;
|
||
|
ULONG64 Process;
|
||
|
CHAR Buffer[80];
|
||
|
ULONG KeTimeIncrement;
|
||
|
ULONG TimeIncrement;
|
||
|
ULONG frames = 0;
|
||
|
ULONG i;
|
||
|
ULONG displacement;
|
||
|
ULONG64 WaitBlockList;
|
||
|
ULONG IrpOffset;
|
||
|
|
||
|
InitTypeRead(RealThreadBase, nt!_ETHREAD);
|
||
|
|
||
|
if ((ULONG) ReadField(Tcb.State) == Waiting) {
|
||
|
dprintf("%s (%s) %s %s\n",
|
||
|
Pad,
|
||
|
StacksWaitReasonList[(ULONG) ReadField(Tcb.WaitReason)],
|
||
|
((ULONG) ReadField(Tcb.WaitMode)==KernelMode) ? "KernelMode" : "UserMode",(ULONG) ReadField(Tcb.Alertable) ? "Alertable" : "Non-Alertable");
|
||
|
if ( ReadField(Tcb.SuspendCount) ) {
|
||
|
dprintf("SuspendCount %lx\n",(ULONG) ReadField(Tcb.SuspendCount));
|
||
|
}
|
||
|
if ( ReadField(Tcb.FreezeCount) ) {
|
||
|
dprintf("FreezeCount %lx\n",(ULONG) ReadField(Tcb.FreezeCount));
|
||
|
}
|
||
|
|
||
|
WaitBlockList = ReadField(Tcb.WaitBlockList);
|
||
|
|
||
|
if (InitTypeRead(WaitBlockList,nt!KWAIT_BLOCK)) {
|
||
|
dprintf("%sunable to get Wait object\n",Pad);
|
||
|
goto BadWaitBlock;
|
||
|
}
|
||
|
|
||
|
do {
|
||
|
ULONG64 Object, NextWaitBlock, OwnerThread;
|
||
|
ULONG Limit;
|
||
|
|
||
|
dprintf("%s %lx ",Pad, Object = ReadField(Object));
|
||
|
NextWaitBlock = ReadField(NextWaitBlock);
|
||
|
|
||
|
if (InitTypeRead(Object,nt!KMUTANT)) {
|
||
|
|
||
|
dprintf("%sunable to get Wait object\n",Pad);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
GetFieldValue(Object, "nt!KSEMAPHORE", "Limit", Limit);
|
||
|
GetFieldValue(Object, "nt!KSEMAPHORE", "OwnerThread",OwnerThread);
|
||
|
switch (ReadField(Header.Type)) {
|
||
|
case EventNotificationObject:
|
||
|
dprintf("NotificationEvent\n");
|
||
|
break;
|
||
|
case EventSynchronizationObject:
|
||
|
dprintf("SynchronizationEvent\n");
|
||
|
break;
|
||
|
case SemaphoreObject:
|
||
|
dprintf("Semaphore Limit 0x%p\n",
|
||
|
Limit);
|
||
|
break;
|
||
|
case ThreadObject:
|
||
|
dprintf("Thread\n");
|
||
|
break;
|
||
|
case TimerNotificationObject:
|
||
|
dprintf("NotificationTimer\n");
|
||
|
break;
|
||
|
case TimerSynchronizationObject:
|
||
|
dprintf("SynchronizationTimer\n");
|
||
|
break;
|
||
|
case EventPairObject:
|
||
|
dprintf("EventPair\n");
|
||
|
break;
|
||
|
case ProcessObject:
|
||
|
dprintf("ProcessObject\n");
|
||
|
break;
|
||
|
case MutantObject:
|
||
|
dprintf("Mutant - owning thread %p\n",
|
||
|
OwnerThread);
|
||
|
break;
|
||
|
default:
|
||
|
dprintf("Unknown\n");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (NextWaitBlock == WaitBlockList) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (InitTypeRead(NextWaitBlock,nt!KWAIT_BLOCK)) {
|
||
|
dprintf("%sunable to get Wait object\n",Pad);
|
||
|
break;
|
||
|
}
|
||
|
} while ( TRUE );
|
||
|
}
|
||
|
|
||
|
BadWaitBlock:
|
||
|
|
||
|
//
|
||
|
// Re-intialize thread read
|
||
|
//
|
||
|
InitTypeRead(RealThreadBase, nt!_ETHREAD);
|
||
|
|
||
|
if ( ReadField(LpcReplyMessageId) != 0) {
|
||
|
dprintf("%sWaiting for reply to LPC MessageId %08x:\n",Pad, (ULONG) ReadField(LpcReplyMessageId));
|
||
|
}
|
||
|
|
||
|
if (Address = ReadField(LpcReplyMessage)) {
|
||
|
ULONG64 Entry_Flink, Entry_Blink;
|
||
|
|
||
|
dprintf("%sPending LPC Reply Message:\n",Pad);
|
||
|
|
||
|
if (GetFieldValue(Address, "nt!_LPCP_MESSAGE", "Entry.Blink", Entry_Blink)) {
|
||
|
dprintf("unable to get LPC msg\n");
|
||
|
} else {
|
||
|
GetFieldValue(Address, "nt!_LPCP_MESSAGE", "Entry.Flink", Entry_Flink);
|
||
|
dprintf("%s %08p: [%08p,%08p]\n",
|
||
|
Pad, Address, Entry_Blink, Entry_Flink
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
GetFieldOffset("nt!_ETHREAD", "IrpList", &IrpOffset);
|
||
|
|
||
|
if (ReadField(IrpList.Flink) != ReadField(IrpList.Blink) ||
|
||
|
ReadField(IrpList.Flink) != RealThreadBase + IrpOffset
|
||
|
) {
|
||
|
|
||
|
ULONG64 IrpListHead = RealThreadBase + IrpOffset;
|
||
|
ULONG64 Next;
|
||
|
ULONG Counter = 0;
|
||
|
ULONG IrpThrdOff;
|
||
|
|
||
|
Next = ReadField(IrpList.Flink);
|
||
|
|
||
|
GetFieldOffset("nt!_IRP", "ThreadListEntry", &IrpThrdOff);
|
||
|
|
||
|
dprintf("%sIRP List:\n",Pad);
|
||
|
while ((Next != IrpListHead) && (Counter < 17)) {
|
||
|
ULONG Irp_Type=0, Irp_Size=0, Irp_Flags=0;
|
||
|
ULONG64 Irp_MdlAddress=0;
|
||
|
|
||
|
Counter += 1;
|
||
|
|
||
|
Address = Next - IrpThrdOff;
|
||
|
|
||
|
GetFieldValue(Address, "nt!_IRP", "Type", Irp_Type);
|
||
|
GetFieldValue(Address, "nt!_IRP", "Size", Irp_Size);
|
||
|
GetFieldValue(Address, "nt!_IRP", "Flags", Irp_Flags);
|
||
|
GetFieldValue(Address, "nt!_IRP", "MdlAddress", Irp_MdlAddress);
|
||
|
GetFieldValue(Address, "nt!_IRP", "ThreadListEntry.Flink", Next);
|
||
|
|
||
|
dprintf("%s %08p: (%04x,%04x) Flags: %08lx Mdl: %08lp\n",
|
||
|
Pad,Address,Irp_Type,Irp_Size,Irp_Flags,Irp_MdlAddress);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
ForEachFrameOnThread(
|
||
|
IN ULONG64 RealThreadBase,
|
||
|
IN PFN_FRAME_WALK_CALLBACK Callback,
|
||
|
IN PVOID Context
|
||
|
)
|
||
|
{
|
||
|
#define MAX_STACK_FRAMES 40
|
||
|
TIME_FIELDS Times;
|
||
|
LARGE_INTEGER RunTime;
|
||
|
ULONG Address;
|
||
|
ULONG Result;
|
||
|
CHAR Buffer[256];
|
||
|
ULONG KeTimeIncrement;
|
||
|
ULONG TimeIncrement;
|
||
|
ULONG frames = 0;
|
||
|
ULONG i;
|
||
|
ULONG64 displacement, tcb_KernelStackResident;
|
||
|
EXTSTACKTRACE64 stk[MAX_STACK_FRAMES];
|
||
|
|
||
|
InitTypeRead(RealThreadBase, nt!_ETHREAD);
|
||
|
|
||
|
tcb_KernelStackResident = ReadField(Tcb.KernelStackResident);
|
||
|
|
||
|
if (!tcb_KernelStackResident) {
|
||
|
|
||
|
if (Callback(STACK_WALK_DUMP_STARTING, RealThreadBase, Context, NULL, 0)) {
|
||
|
|
||
|
Callback(STACK_WALK_DUMP_NOT_RESIDENT, RealThreadBase, Context, NULL, 0);
|
||
|
Callback(STACK_WALK_DUMP_FINISHED, RealThreadBase, Context, NULL, 0);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
SetThreadForOperation64( &RealThreadBase );
|
||
|
frames = StackTrace( 0, 0, 0, stk, MAX_STACK_FRAMES );
|
||
|
|
||
|
|
||
|
if (!Callback(STACK_WALK_DUMP_STARTING, RealThreadBase, Context, NULL, 0)) {
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (i=0; i<frames; i++) {
|
||
|
|
||
|
Buffer[0] = '!';
|
||
|
GetSymbol(stk[i].ProgramCounter, Buffer, &displacement);
|
||
|
|
||
|
if (!Callback(
|
||
|
STACK_WALK_DUMP_ENTRY,
|
||
|
RealThreadBase,
|
||
|
Context,
|
||
|
Buffer,
|
||
|
displacement)) {
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Callback(STACK_WALK_DUMP_FINISHED, RealThreadBase, Context, NULL, 0);
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
AnalyzeThread(
|
||
|
IN ULONG64 RealThreadBase,
|
||
|
IN PBLOCKER_TREE BlockerTree,
|
||
|
IN char * Filter,
|
||
|
OUT PCHAR BlockBuffer,
|
||
|
OUT ULONG64 *BlockerDisplacement,
|
||
|
OUT BOOLEAN *SkipThread
|
||
|
)
|
||
|
{
|
||
|
#define MAX_STACK_FRAMES 40
|
||
|
TIME_FIELDS Times;
|
||
|
LARGE_INTEGER RunTime;
|
||
|
ULONG Address;
|
||
|
ULONG Result;
|
||
|
ULONG WaitOffset;
|
||
|
ULONG KeTimeIncrement;
|
||
|
ULONG TimeIncrement;
|
||
|
ULONG frames = 0;
|
||
|
ULONG i;
|
||
|
ULONG64 displacement, tcb_KernelStackResident;
|
||
|
PBLOCKER_TREE blockerCur ;
|
||
|
EXTSTACKTRACE64 stk[MAX_STACK_FRAMES];
|
||
|
BOOLEAN filterMatch, blockerMatch;
|
||
|
CHAR tempFrame[256], lcFilter[256], lcFrame[256];
|
||
|
|
||
|
InitTypeRead(RealThreadBase, nt!_ETHREAD);
|
||
|
|
||
|
tcb_KernelStackResident = ReadField(Tcb.KernelStackResident);
|
||
|
|
||
|
if (!tcb_KernelStackResident) {
|
||
|
|
||
|
*SkipThread = TRUE;
|
||
|
*BlockerDisplacement = 0;
|
||
|
BlockBuffer[0] = '\0';
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
SetThreadForOperation64( &RealThreadBase );
|
||
|
frames = StackTrace( 0, 0, 0, stk, MAX_STACK_FRAMES );
|
||
|
|
||
|
*SkipThread = FALSE;
|
||
|
|
||
|
BlockBuffer[0] = '!';
|
||
|
|
||
|
if (Filter[0]) {
|
||
|
|
||
|
strcpy(lcFilter, Filter);
|
||
|
_strlwr(lcFilter);
|
||
|
}
|
||
|
|
||
|
if (frames == 0) {
|
||
|
|
||
|
strcpy(BlockBuffer, "?? Kernel stack not resident ??") ;
|
||
|
*SkipThread = TRUE;
|
||
|
} else {
|
||
|
|
||
|
if (ReadField(Tcb.State) == Running) {
|
||
|
|
||
|
GetSymbol(stk[0].ProgramCounter, BlockBuffer, &displacement);
|
||
|
*BlockerDisplacement = displacement;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
blockerMatch = FALSE;
|
||
|
filterMatch = FALSE;
|
||
|
|
||
|
for(i=0; i<frames; i++) {
|
||
|
|
||
|
GetSymbol(stk[i].ProgramCounter, tempFrame, &displacement);
|
||
|
if ((!filterMatch) && Filter[0]) {
|
||
|
|
||
|
strcpy(lcFrame, tempFrame);
|
||
|
_strlwr(lcFrame);
|
||
|
if (strstr(lcFrame, lcFilter)) {
|
||
|
|
||
|
filterMatch = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
blockerCur = BlockerTree;
|
||
|
if ((!blockerMatch) &&
|
||
|
(!BlockerTreeWalk(&blockerCur, tempFrame, SKIP_FRAME))) {
|
||
|
|
||
|
blockerMatch = TRUE;
|
||
|
strcpy(BlockBuffer, tempFrame);
|
||
|
*BlockerDisplacement = displacement;
|
||
|
if (filterMatch || (Filter[0]=='\0')) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
blockerCur = BlockerTree;
|
||
|
if (Filter[0]) {
|
||
|
|
||
|
if (!filterMatch) {
|
||
|
|
||
|
*SkipThread = TRUE;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
if (BlockerTreeWalk(&blockerCur, BlockBuffer, SKIP_THREAD)) {
|
||
|
|
||
|
*SkipThread = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#define BEGIN_TREE()
|
||
|
#define END_TREE()
|
||
|
#define DECLARE_ENTRY(foo, action) BlockerTreeDeclareEntry(foo, action)
|
||
|
#define BEGIN_LIST() BlockerTreeListBegin()
|
||
|
#define END_LIST() BlockerTreeListEnd()
|
||
|
|
||
|
PBLOCKER_TREE gpCurrentBlocker ;
|
||
|
|
||
|
VOID
|
||
|
BlockerTreeListBegin(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
//dprintf("Nest for %x\n", gpCurrentBlocker) ;
|
||
|
ASSERT(!gpCurrentBlocker->Nested) ;
|
||
|
gpCurrentBlocker->Nested = TRUE ;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
BlockerTreeListEnd(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
//dprintf("Unnest for %x\n", gpCurrentBlocker) ;
|
||
|
gpCurrentBlocker = gpCurrentBlocker->Parent ;
|
||
|
ASSERT(gpCurrentBlocker->Nested) ;
|
||
|
gpCurrentBlocker->Nested = FALSE ;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
BlockerTreeDeclareEntry(
|
||
|
const char *szSymbolic,
|
||
|
STACKS_ACTION StacksAction
|
||
|
)
|
||
|
{
|
||
|
PBLOCKER_TREE blockerEntry ;
|
||
|
|
||
|
blockerEntry = (PBLOCKER_TREE) malloc(sizeof(BLOCKER_TREE)) ;
|
||
|
if (!blockerEntry) {
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
memset(blockerEntry, 0, sizeof(BLOCKER_TREE)) ;
|
||
|
blockerEntry->Symbolic = szSymbolic ;
|
||
|
blockerEntry->Action = StacksAction;
|
||
|
|
||
|
if (gpCurrentBlocker->Nested) {
|
||
|
ASSERT(!gpCurrentBlocker->Child) ;
|
||
|
//dprintf("Child %x for %x\n", blockerEntry, gpCurrentBlocker) ;
|
||
|
blockerEntry->Parent = gpCurrentBlocker ;
|
||
|
gpCurrentBlocker->Child = blockerEntry ;
|
||
|
} else {
|
||
|
ASSERT(!gpCurrentBlocker->Sibling) ;
|
||
|
//dprintf("sibling %x for %x\n", blockerEntry, gpCurrentBlocker) ;
|
||
|
blockerEntry->Parent = gpCurrentBlocker->Parent ;
|
||
|
gpCurrentBlocker->Sibling = blockerEntry ;
|
||
|
}
|
||
|
gpCurrentBlocker = blockerEntry ;
|
||
|
}
|
||
|
|
||
|
PBLOCKER_TREE
|
||
|
BlockerTreeBuild(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
BLOCKER_TREE blockerHead ;
|
||
|
|
||
|
memset(&blockerHead, 0, sizeof(BLOCKER_TREE)) ;
|
||
|
|
||
|
gpCurrentBlocker = &blockerHead ;
|
||
|
|
||
|
//
|
||
|
// Generate the list...
|
||
|
//
|
||
|
#include "stacks.h"
|
||
|
|
||
|
//
|
||
|
// And return it.
|
||
|
//
|
||
|
return blockerHead.Sibling ;
|
||
|
}
|
||
|
|
||
|
VOID BlockerTreeFree(
|
||
|
PBLOCKER_TREE BlockerHead
|
||
|
)
|
||
|
{
|
||
|
PBLOCKER_TREE blockerCur, blockerNext ;
|
||
|
|
||
|
for(blockerCur = BlockerHead; blockerCur; blockerCur = blockerNext) {
|
||
|
if (blockerCur->Child) {
|
||
|
BlockerTreeFree(blockerCur->Child) ;
|
||
|
}
|
||
|
blockerNext = blockerCur->Sibling ;
|
||
|
free(blockerCur) ;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
BlockerTreeWalk(
|
||
|
IN OUT PBLOCKER_TREE *blockerHead,
|
||
|
IN char *szSymbolic,
|
||
|
IN STACKS_ACTION Action
|
||
|
)
|
||
|
{
|
||
|
PBLOCKER_TREE blockerCur ;
|
||
|
const char *blockString, *curString, *strptr;
|
||
|
char szStringCopy[512];
|
||
|
|
||
|
for(blockerCur = *blockerHead; blockerCur; blockerCur = blockerCur->Sibling) {
|
||
|
|
||
|
if (Action != blockerCur->Action) {
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
blockString = blockerCur->Symbolic;
|
||
|
curString = szSymbolic;
|
||
|
|
||
|
strptr = strstr(curString, "!.");
|
||
|
if (strptr) {
|
||
|
|
||
|
//
|
||
|
// This must be an ia64 symbol. Replace the !. with a nice simple !
|
||
|
//
|
||
|
strcpy(szStringCopy, curString);
|
||
|
strcpy(szStringCopy + (strptr - curString) + 1, strptr + 2);
|
||
|
curString = szStringCopy;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Special case "Our Kernel of Many Names"
|
||
|
//
|
||
|
if (!_strnicmp(blockString, "nt!", 3)) {
|
||
|
|
||
|
if ((!_strnicmp(curString, "ntoskrnl!", 9)) ||
|
||
|
(!_strnicmp(curString, "ntkrnlmp!", 9)) ||
|
||
|
(!_strnicmp(curString, "ntkrpamp!", 9)) ||
|
||
|
(!_strnicmp(curString, "ntkrnlpa!", 9))) {
|
||
|
|
||
|
blockString += 3;
|
||
|
curString += 9;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!_strcmpi(blockString, curString)) {
|
||
|
*blockerHead = blockerCur->Child;
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|