1296 lines
34 KiB
C
1296 lines
34 KiB
C
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
deadlock.c
|
|
|
|
Abstract:
|
|
|
|
WinDbg Extension Api
|
|
|
|
Author:
|
|
|
|
Jordan Tigani (jtigani)
|
|
Silviu Calinoiu (silviuc)
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
Revision History:
|
|
|
|
5-30-00 File created (jtigani)
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// This has to be in sync with the definition from
|
|
// ntos\verifier\vfdeadlock.c
|
|
//
|
|
|
|
#define VI_DEADLOCK_HASH_BINS 0x1F
|
|
|
|
#if 0
|
|
typedef enum _VI_DEADLOCK_RESOURCE_TYPE {
|
|
ViDeadlockUnknown = 0,
|
|
ViDeadlockMutex,
|
|
ViDeadlockFastMutex,
|
|
ViDeadlockFastMutexUnsafe,
|
|
ViDeadlockSpinLock,
|
|
ViDeadlockQueuedSpinLock,
|
|
ViDeadlockTypeMaximum
|
|
} VI_DEADLOCK_RESOURCE_TYPE, *PVI_DEADLOCK_RESOURCE_TYPE;
|
|
#endif
|
|
|
|
PUCHAR ResourceTypes[] =
|
|
{
|
|
"Unknown",
|
|
"Mutex",
|
|
"Fast Mutex",
|
|
"Fast Mutex Unsafe",
|
|
"Spinlock",
|
|
"Queued Spinlock",
|
|
};
|
|
|
|
#define RESOURCE_TYPE_MAXIMUM 5
|
|
|
|
#define DEADLOCK_EXT_FLAG_DUMP_STACKS 1
|
|
#define DEADLOCK_EXT_FLAG_DUMP_NODES 2
|
|
#define DEADLOCK_EXT_FLAG_ANALYZE 4
|
|
|
|
extern
|
|
VOID
|
|
DumpSymbolicAddress(
|
|
ULONG64 Address,
|
|
PUCHAR Buffer,
|
|
BOOL AlwaysShowHex
|
|
);
|
|
|
|
#define MAX_DEADLOCK_PARTICIPANTS 32
|
|
|
|
|
|
#define VI_MAX_STACK_DEPTH 8
|
|
typedef struct _DEADLOCK_VECTOR
|
|
{
|
|
ULONG64 Thread;
|
|
ULONG64 Node;
|
|
ULONG64 ResourceAddress;
|
|
ULONG64 StackAddress;
|
|
ULONG64 ParentStackAddress;
|
|
ULONG64 ThreadEntry;
|
|
ULONG Type;
|
|
BOOLEAN TryAcquire;
|
|
} DEADLOCK_VECTOR, *PDEADLOCK_VECTOR;
|
|
|
|
extern
|
|
ULONG64
|
|
ReadPvoid (
|
|
ULONG64 Address
|
|
);
|
|
|
|
extern
|
|
ULONG
|
|
ReadUlong(
|
|
ULONG64 Address
|
|
);
|
|
|
|
|
|
//
|
|
// Forward declarations for local functions
|
|
//
|
|
|
|
VOID
|
|
PrintGlobalStatistics (
|
|
ULONG64 GlobalsAddress
|
|
);
|
|
|
|
BOOLEAN
|
|
SearchForResource (
|
|
ULONG64 GlobalsAddress,
|
|
ULONG64 ResourceAddress
|
|
);
|
|
|
|
BOOLEAN
|
|
SearchForThread (
|
|
ULONG64 GlobalsAddress,
|
|
ULONG64 ThreadAddress
|
|
);
|
|
|
|
BOOLEAN
|
|
AnalyzeResource (
|
|
ULONG64 Resource,
|
|
BOOLEAN Verbose
|
|
);
|
|
|
|
BOOLEAN
|
|
AnalyzeResources (
|
|
ULONG64 GlobalsAddress
|
|
);
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////// Deadlocks
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Defines copied from nt\base\ntos\verifier\vfdeadlock.c .
|
|
//
|
|
|
|
#define VI_DEADLOCK_ISSUE_SELF_DEADLOCK 0x1000
|
|
#define VI_DEADLOCK_ISSUE_DEADLOCK_DETECTED 0x1001
|
|
#define VI_DEADLOCK_ISSUE_UNINITIALIZED_RESOURCE 0x1002
|
|
#define VI_DEADLOCK_ISSUE_UNEXPECTED_RELEASE 0x1003
|
|
#define VI_DEADLOCK_ISSUE_UNEXPECTED_THREAD 0x1004
|
|
#define VI_DEADLOCK_ISSUE_MULTIPLE_INITIALIZATION 0x1005
|
|
#define VI_DEADLOCK_ISSUE_THREAD_HOLDS_RESOURCES 0x1006
|
|
#define VI_DEADLOCK_ISSUE_UNACQUIRED_RESOURCE 0x1007
|
|
|
|
|
|
|
|
#define DUMP_FIELD(Name) dprintf ("%-20s %I64u \n", #Name, ReadField (Name))
|
|
|
|
DECLARE_API( deadlock )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Verifier deadlock detection module extension.
|
|
|
|
Arguments:
|
|
|
|
arg - not used for now.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG64 GlobalsPointer;
|
|
ULONG64 GlobalsAddress;
|
|
ULONG64 InitializedAddress;
|
|
ULONG64 EnabledAddress;
|
|
ULONG64 InstigatorAddress;
|
|
ULONG64 ParticipantAddress;
|
|
ULONG64 LastResourceAddress;
|
|
ULONG64 RootAddress;
|
|
ULONG64 CurrentResourceAddress;
|
|
ULONG64 CurrentThread;
|
|
ULONG64 ThreadForChain;
|
|
ULONG64 CurrentStack;
|
|
ULONG64 NextStack;
|
|
ULONG64 SymbolOffset;
|
|
|
|
ULONG StackTraceSize;
|
|
|
|
ULONG Processor=0;
|
|
ULONG ParticipantOffset;
|
|
ULONG StackOffset;
|
|
ULONG ParentStackOffset;
|
|
ULONG InitializedValue;
|
|
ULONG EnabledValue;
|
|
ULONG NumberOfParticipants;
|
|
ULONG NumberOfResources;
|
|
ULONG NumberOfThreads;
|
|
ULONG ThreadNumber;
|
|
ULONG ResourceNumber;
|
|
ULONG ResourceType;
|
|
ULONG TryAcquireUsed;
|
|
|
|
|
|
ULONG PtrSize;
|
|
ULONG J, I;
|
|
|
|
BOOLEAN DumpStacks = FALSE;
|
|
BOOLEAN DumpNodes = FALSE;
|
|
BOOLEAN Analyze = FALSE;
|
|
|
|
ULONG64 Flags;
|
|
|
|
UCHAR SymbolName[512];
|
|
|
|
HANDLE CurrentThreadHandle = NULL;
|
|
|
|
DEADLOCK_VECTOR Participants[MAX_DEADLOCK_PARTICIPANTS+1];
|
|
|
|
ULONG64 Issue[4];
|
|
ULONG64 SearchAddress = 0;
|
|
|
|
//
|
|
// Check if help requested
|
|
//
|
|
|
|
if (strstr (args, "?")) {
|
|
|
|
dprintf ("\n");
|
|
dprintf ("!deadlock Statistics and deadlock layout \n");
|
|
dprintf ("!deadlock 3 Detailed deadlock layout \n");
|
|
dprintf ("!deadlock ADDRESS Search for ADDRESS among deadlock verifier data \n");
|
|
dprintf ("\n");
|
|
return S_OK;
|
|
}
|
|
|
|
Flags = GetExpression(args);
|
|
|
|
if (Flags > 0x10000000) {
|
|
|
|
SearchAddress = Flags;
|
|
}
|
|
else {
|
|
|
|
if (Flags & DEADLOCK_EXT_FLAG_DUMP_STACKS) {
|
|
DumpStacks = TRUE;
|
|
}
|
|
|
|
if (Flags & DEADLOCK_EXT_FLAG_DUMP_NODES) {
|
|
DumpNodes = TRUE;
|
|
}
|
|
|
|
if (Flags & DEADLOCK_EXT_FLAG_ANALYZE) {
|
|
Analyze = TRUE;
|
|
}
|
|
}
|
|
|
|
GlobalsPointer = (ULONG64) GetExpression ("nt!ViDeadlockGlobals");
|
|
EnabledAddress = (ULONG64) GetExpression ("nt!ViDeadlockDetectionEnabled");
|
|
|
|
if (GlobalsPointer == 0 || EnabledAddress == 0) {
|
|
dprintf ("Error: incorrect symbols for kernel \n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
GlobalsAddress = 0;
|
|
ReadPointer (GlobalsPointer, &GlobalsAddress);
|
|
EnabledValue = ReadUlong (EnabledAddress);
|
|
|
|
if (GlobalsAddress == 0) {
|
|
dprintf ("Deadlock detection not initialized \n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
InitializedValue = 1;
|
|
|
|
if (EnabledValue == 0) {
|
|
dprintf ("Deadlock detection not enabled \n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// Do a search if this is requested.
|
|
//
|
|
|
|
if (SearchAddress) {
|
|
|
|
BOOLEAN FoundSomething = FALSE;
|
|
|
|
dprintf ("Searching for %p ... \n", SearchAddress);
|
|
|
|
if (FoundSomething == FALSE) {
|
|
FoundSomething = SearchForResource (GlobalsAddress, SearchAddress);
|
|
}
|
|
|
|
if (FoundSomething == FALSE) {
|
|
FoundSomething = SearchForThread (GlobalsAddress, SearchAddress);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Analyze if this is needed.
|
|
//
|
|
|
|
if (Analyze) {
|
|
|
|
AnalyzeResources (GlobalsAddress);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Get the ViDeadlockIssue[0..3] vector.
|
|
//
|
|
|
|
{
|
|
ULONG ValueSize;
|
|
ULONG64 IssueAddress;
|
|
ULONG I;
|
|
|
|
ValueSize = DBG_PTR_SIZE;
|
|
|
|
IssueAddress = GetExpression ("nt!ViDeadlockIssue");
|
|
|
|
for (I = 0; I < 4; I += 1) {
|
|
|
|
ReadPointer (IssueAddress + I * ValueSize, &(Issue[I]));
|
|
}
|
|
|
|
if (Issue[0] == 0) {
|
|
|
|
dprintf ("\n");
|
|
PrintGlobalStatistics (GlobalsAddress);
|
|
dprintf ("\nNo deadlock verifier issues. \n");
|
|
|
|
return S_OK;
|
|
}
|
|
else {
|
|
|
|
if (ValueSize == 4) {
|
|
dprintf ("issue: %08X %08X %08X %08X \n",
|
|
Issue[0], Issue[1], Issue[2], Issue[3]);
|
|
}
|
|
else {
|
|
dprintf ("issue: %I64X %I64X %I64X %I64X \n",
|
|
Issue[0], Issue[1], Issue[2], Issue[3]);
|
|
}
|
|
}
|
|
|
|
switch (Issue[0]) {
|
|
|
|
case VI_DEADLOCK_ISSUE_SELF_DEADLOCK:
|
|
dprintf ("Resource %I64X is acquired recursively. \n", Issue[1]);
|
|
return S_OK;
|
|
|
|
case VI_DEADLOCK_ISSUE_DEADLOCK_DETECTED:
|
|
break;
|
|
|
|
case VI_DEADLOCK_ISSUE_UNINITIALIZED_RESOURCE:
|
|
dprintf ("Resource %I64X is used before being initialized. \n", Issue[1]);
|
|
return S_OK;
|
|
|
|
case VI_DEADLOCK_ISSUE_UNEXPECTED_RELEASE:
|
|
dprintf ("Resource %I64X is released out of order. \n", Issue[2]);
|
|
return S_OK;
|
|
|
|
case VI_DEADLOCK_ISSUE_UNEXPECTED_THREAD:
|
|
dprintf ("Current thread is releasing resource %I64X which was acquired in thread %I64X. \n",
|
|
Issue[1], Issue[2]);
|
|
return S_OK;
|
|
|
|
case VI_DEADLOCK_ISSUE_MULTIPLE_INITIALIZATION:
|
|
dprintf ("Resource %I64X has already been initialized. \n", Issue[1]);
|
|
return S_OK;
|
|
|
|
case VI_DEADLOCK_ISSUE_THREAD_HOLDS_RESOURCES:
|
|
dprintf ("Deleting thread %I64X which still holds resource %I64X . \n",
|
|
Issue[1], Issue[2]);
|
|
return S_OK;
|
|
|
|
case VI_DEADLOCK_ISSUE_UNACQUIRED_RESOURCE:
|
|
dprintf ("Releasing resource %I64X that was never acquired. \n", Issue[1]);
|
|
return S_OK;
|
|
|
|
default:
|
|
dprintf ("Unrecognized issue code %I64X ! \n", Issue[0]);
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Figure out how big a pointer is
|
|
//
|
|
|
|
PtrSize = DBG_PTR_SIZE;
|
|
|
|
if (PtrSize == 0) {
|
|
dprintf ("Cannot get size of PVOID \n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
GetCurrentProcessor(Client, &Processor, &CurrentThreadHandle);
|
|
GetCurrentThreadAddr( Processor, &CurrentThread );
|
|
|
|
//
|
|
// Dump the globals structure
|
|
//
|
|
|
|
InitTypeRead (GlobalsAddress, nt!_VI_DEADLOCK_GLOBALS);
|
|
|
|
//
|
|
// Find out the address of the resource that causes the deadlock
|
|
//
|
|
|
|
InstigatorAddress = ReadField(Instigator);
|
|
|
|
NumberOfParticipants = (ULONG) ReadField(NumberOfParticipants);
|
|
|
|
if (NumberOfParticipants > MAX_DEADLOCK_PARTICIPANTS) {
|
|
dprintf("\nCannot have %x participants in a deadlock!\n",NumberOfParticipants);
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
if (0 == NumberOfParticipants) {
|
|
dprintf("\nNo deadlock detected\n");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
GetFieldOffset("nt!_VI_DEADLOCK_GLOBALS",
|
|
"Participant",
|
|
&ParticipantOffset
|
|
);
|
|
ParticipantAddress = GlobalsAddress + ParticipantOffset;
|
|
|
|
//
|
|
// Read the vector of VI_DEADLOCK_NODEs that
|
|
// participate in the deadlock.
|
|
//
|
|
//
|
|
|
|
for (J = 0; J < NumberOfParticipants; J++) {
|
|
Participants[J].Node = ReadPvoid(ParticipantAddress + J * PtrSize);
|
|
// dprintf("Participant %c: %08x\n", 'A' + J, Participants[J].Node);
|
|
}
|
|
|
|
//
|
|
// Gather the information we'll need to print out exact
|
|
// context for the deadlock.
|
|
//
|
|
GetFieldOffset("nt!_VI_DEADLOCK_NODE",
|
|
"StackTrace",
|
|
&StackOffset
|
|
);
|
|
GetFieldOffset("nt!_VI_DEADLOCK_NODE",
|
|
"ParentStackTrace",
|
|
&ParentStackOffset
|
|
);
|
|
|
|
|
|
//
|
|
// The stack trace size is 1 on free builds and 6 (or bigger) on
|
|
// checked builds. We assume that the ParentStackTrace field comes
|
|
// immediately after StackTrace field in the NODE structure.
|
|
//
|
|
|
|
StackTraceSize = (ParentStackOffset - StackOffset) / PtrSize;
|
|
|
|
for (J = 0; J < NumberOfParticipants; J++) {
|
|
|
|
InitTypeRead (Participants[J].Node, nt!_VI_DEADLOCK_NODE);
|
|
|
|
|
|
RootAddress = ReadField(Root);
|
|
|
|
GetFieldValue(RootAddress,
|
|
"nt!_VI_DEADLOCK_RESOURCE",
|
|
"ResourceAddress" ,
|
|
Participants[J].ResourceAddress
|
|
);
|
|
|
|
GetFieldValue(RootAddress,
|
|
"nt!_VI_DEADLOCK_RESOURCE",
|
|
"Type",
|
|
Participants[J].Type
|
|
);
|
|
|
|
|
|
if (Participants[J].Type > RESOURCE_TYPE_MAXIMUM) {
|
|
Participants[J].Type = 0;
|
|
}
|
|
|
|
Participants[J].ThreadEntry = ReadField(ThreadEntry);
|
|
Participants[J].StackAddress = Participants[J].Node + StackOffset;
|
|
Participants[J].ParentStackAddress = Participants[J].Node +
|
|
ParentStackOffset;
|
|
Participants[J].TryAcquire = (BOOLEAN) ReadField(OnlyTryAcquireUsed);
|
|
|
|
|
|
GetFieldValue(Participants[J].ThreadEntry,
|
|
"nt!_VI_DEADLOCK_THREAD",
|
|
"Thread",
|
|
Participants[J].Thread
|
|
);
|
|
|
|
|
|
}
|
|
|
|
if (Participants[0].ResourceAddress != InstigatorAddress) {
|
|
dprintf("\nDeadlock Improperly formed participant list\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// The last participant is the Instigator of the deadlock
|
|
//
|
|
Participants[NumberOfParticipants].Thread = CurrentThread;
|
|
Participants[NumberOfParticipants].Node = 0;
|
|
Participants[NumberOfParticipants].ResourceAddress = InstigatorAddress;
|
|
Participants[NumberOfParticipants].StackAddress = 0;
|
|
Participants[NumberOfParticipants].ParentStackAddress =
|
|
Participants[NumberOfParticipants-1].StackAddress;
|
|
Participants[NumberOfParticipants].Type =
|
|
Participants[0].Type;
|
|
Participants[NumberOfParticipants].TryAcquire = FALSE; // can't cause a deadlock with try
|
|
Participants[NumberOfParticipants].ThreadEntry = 0;
|
|
|
|
//
|
|
// At this point we have all of the raw data we need.
|
|
// We have to munge it up a bit so that we have the most
|
|
// recent data. For instance, take the simple deadlock AB-BA.
|
|
// The stack for A in the AB context may be wrong because
|
|
// another thread may have come and taken A at a different point.
|
|
// This is why we have the parent stack address.
|
|
//
|
|
// So the rules we have to adhere to are as follows:
|
|
// Where we have a chain, (eg ABC meaning A taken then B then C),
|
|
// the thread used will always be the thread for the last resource taken,
|
|
// and the stacks used will be the the childs parent stack where
|
|
// applicable.
|
|
//
|
|
// For example, if C was taken by thread 1, A & B would be munged
|
|
// to use thread 1. Since in order to get to C, A and B must have
|
|
// been taken by thread 1 at some point, even if the thread they
|
|
// have saved now is a different one. C would use its own stack,
|
|
// B would use C's parent stack, since that was the stack that
|
|
// B had been acquired with when C was taken, and A will use
|
|
// B's parent stack.
|
|
//
|
|
// We can identify the start of a chain when the same resource
|
|
// is on the participant list twice in a row.
|
|
//
|
|
|
|
LastResourceAddress = InstigatorAddress;
|
|
|
|
NumberOfResources = 0;
|
|
NumberOfThreads = 0;
|
|
|
|
for (J = 0; J <= NumberOfParticipants; J++) {
|
|
I = NumberOfParticipants - J;
|
|
|
|
CurrentResourceAddress = Participants[I].ResourceAddress;
|
|
|
|
if (CurrentResourceAddress == LastResourceAddress) {
|
|
|
|
//
|
|
// This is the beginning of a chain. Use the current
|
|
// stack and the current thread, and set the chain
|
|
// thread to ours
|
|
//
|
|
|
|
ThreadForChain = Participants[I].Thread;
|
|
CurrentStack = Participants[I].StackAddress;
|
|
NumberOfThreads++;
|
|
} else {
|
|
//
|
|
// This is a resource we haven't seen before
|
|
//
|
|
NumberOfResources++;
|
|
}
|
|
|
|
NextStack = Participants[I].ParentStackAddress;
|
|
|
|
|
|
Participants[I].StackAddress = CurrentStack;
|
|
Participants[I].Thread = ThreadForChain;
|
|
//
|
|
// Parent stack isn't used any more -- nullify it.
|
|
//
|
|
Participants[I].ParentStackAddress = 0;
|
|
|
|
CurrentStack = NextStack;
|
|
LastResourceAddress = CurrentResourceAddress;
|
|
}
|
|
|
|
//
|
|
// Now that we've munged the vectors, we can go ahead and print out the
|
|
// deadlock information.
|
|
//
|
|
|
|
dprintf("\nDeadlock detected (%d resources in %d threads):\n\n",NumberOfResources, NumberOfThreads);
|
|
|
|
if (! DumpStacks )
|
|
{
|
|
//
|
|
// Print out the 'short' form
|
|
// Example:
|
|
//
|
|
// !dealock detected:
|
|
// Thread 1: A B
|
|
// Thread 2: B C
|
|
// Thread 3: C A
|
|
//
|
|
// Thread 1 = <address>
|
|
// Thread 2 = <address>
|
|
// Thread 3 = <address>
|
|
//
|
|
// Lock A = <address> (spinlock)
|
|
// Lock B = <address> (mutex)
|
|
// Lock C = <address> (spinlock)
|
|
//
|
|
|
|
ThreadNumber = 0;
|
|
ResourceNumber = 0;
|
|
J=0;
|
|
|
|
//
|
|
// Dump out the deadlock topology
|
|
//
|
|
|
|
while (J <= NumberOfParticipants)
|
|
{
|
|
ThreadForChain = Participants[J].Thread;
|
|
dprintf("Thread %d: ",ThreadNumber);
|
|
|
|
do {
|
|
if (J == NumberOfParticipants) {
|
|
ResourceNumber = 0;
|
|
}
|
|
|
|
dprintf("%c ",
|
|
'A' + ResourceNumber
|
|
);
|
|
J++;
|
|
ResourceNumber++;
|
|
|
|
} while( J <= NumberOfParticipants && Participants[J].ResourceAddress != Participants[J-1].ResourceAddress);
|
|
|
|
dprintf("\n");
|
|
|
|
ThreadNumber++;
|
|
ResourceNumber--;
|
|
}
|
|
dprintf("\nWhere:\n");
|
|
|
|
//
|
|
// Dump out the thread addresses
|
|
//
|
|
|
|
ThreadNumber = 0;
|
|
ResourceNumber = 0;
|
|
J=0;
|
|
while (J <= NumberOfParticipants) {
|
|
|
|
ThreadForChain = Participants[J].Thread;
|
|
dprintf("Thread %d = %08x\n",ThreadNumber, ThreadForChain);
|
|
|
|
do {
|
|
|
|
if (J == NumberOfParticipants) {
|
|
ResourceNumber = 0;
|
|
}
|
|
J++;
|
|
ResourceNumber++;
|
|
|
|
} while( J <= NumberOfParticipants && Participants[J].ResourceAddress != Participants[J-1].ResourceAddress);
|
|
|
|
ThreadNumber++;
|
|
ResourceNumber--;
|
|
}
|
|
|
|
//
|
|
// Dump out the resource addresses
|
|
//
|
|
|
|
ThreadNumber = 0;
|
|
ResourceNumber = 0;
|
|
J=0;
|
|
#if 1
|
|
while (J < NumberOfParticipants)
|
|
{
|
|
while(J < NumberOfParticipants && Participants[J].ResourceAddress != Participants[J+1].ResourceAddress) {
|
|
|
|
if (Participants[J].ResourceAddress != Participants[J+1].ResourceAddress) {
|
|
CHAR Buffer[0xFF];
|
|
ULONG64 Displacement = 0;
|
|
GetSymbol(Participants[J].ResourceAddress, Buffer, &Displacement);
|
|
|
|
dprintf("Lock %c = %s", 'A' + ResourceNumber, Buffer );
|
|
if (Displacement != 0) {
|
|
dprintf("%s%x", (Displacement < 0xFFF)?"+0x":"",Displacement);
|
|
}
|
|
dprintf(" Type '%s' ",ResourceTypes[Participants[J].Type]);
|
|
dprintf("\n");
|
|
|
|
ResourceNumber++;
|
|
}
|
|
J++;
|
|
}
|
|
J++;
|
|
}
|
|
|
|
#endif
|
|
} else {
|
|
|
|
//
|
|
// Dump out verbose deadlock information -- with stacks
|
|
// Here is an exapmle:
|
|
//
|
|
// Deadlock detected (3 resources in 3 threads):
|
|
//
|
|
//Thread 0 (829785B0) took locks in the following order:
|
|
//
|
|
// Lock A (Spinlock) @ bfc7c254
|
|
// Node: 82887F88
|
|
// Stack: NDIS!ndisNotifyMiniports+0xC1
|
|
// NDIS!ndisPowerStateCallback+0x6E
|
|
// ntkrnlmp!ExNotifyCallback+0x72
|
|
// ntkrnlmp!PopDispatchCallback+0x13
|
|
// ntkrnlmp!PopPolicyWorkerNotify+0x8F
|
|
// ntkrnlmp!PopPolicyWorkerThread+0x10F
|
|
// ntkrnlmp!ExpWorkerThread+0x294
|
|
// ntkrnlmp!PspSystemThreadStartup+0x4B
|
|
//
|
|
// Lock B (Spinlock) @ 8283b87c
|
|
// Node: 82879148
|
|
// Stack: NDIS!ndisDereferenceRef+0x10F
|
|
// NDIS!ndisDereferenceDriver+0x3A
|
|
// NDIS!ndisNotifyMiniports+0xD1
|
|
// NDIS!ndisPowerStateCallback+0x6E
|
|
// ntkrnlmp!ExNotifyCallback+0x72
|
|
// ntkrnlmp!PopDispatchCallback+0x13
|
|
// ntkrnlmp!PopPolicyWorkerNotify+0x8F
|
|
// ntkrnlmp!PopPolicyWorkerThread+0x10F
|
|
//
|
|
//Thread 1 (829785B0) took locks in the following order:
|
|
//
|
|
// Lock B (Spinlock) @ 8283b87c
|
|
// Node: 82879008
|
|
// Stack: NDIS!ndisReferenceNextUnprocessedMiniport+0x3E
|
|
// NDIS!ndisNotifyMiniports+0xB3
|
|
// NDIS!ndisPowerStateCallback+0x6E
|
|
// ntkrnlmp!ExNotifyCallback+0x72
|
|
// ntkrnlmp!PopDispatchCallback+0x13
|
|
// ntkrnlmp!PopPolicyWorkerNotify+0x8F
|
|
// ntkrnlmp!PopPolicyWorkerThread+0x10F
|
|
// ntkrnlmp!ExpWorkerThread+0x294
|
|
//
|
|
// Lock C (Spinlock) @ 82862b48
|
|
// Node: 8288D008
|
|
// Stack: NDIS!ndisReferenceRef+0x10F
|
|
// NDIS!ndisReferenceMiniport+0x4A
|
|
// NDIS!ndisReferenceNextUnprocessedMiniport+0x70
|
|
// NDIS!ndisNotifyMiniports+0xB3
|
|
// NDIS!ndisPowerStateCallback+0x6E
|
|
// ntkrnlmp!ExNotifyCallback+0x72
|
|
// ntkrnlmp!PopDispatchCallback+0x13
|
|
// ntkrnlmp!PopPolicyWorkerNotify+0x8F
|
|
//
|
|
//Thread 2 (82978310) took locks in the following order:
|
|
//
|
|
// Lock C (Spinlock) @ 82862b48
|
|
// Node: 82904708
|
|
// Stack: NDIS!ndisPnPRemoveDevice+0x20B
|
|
// NDIS!ndisPnPDispatch+0x319
|
|
// ntkrnlmp!IopfCallDriver+0x62
|
|
// ntkrnlmp!IovCallDriver+0x9D
|
|
// ntkrnlmp!IopSynchronousCall+0xFA
|
|
// ntkrnlmp!IopRemoveDevice+0x11E
|
|
// ntkrnlmp!IopDeleteLockedDeviceNode+0x3AF
|
|
// ntkrnlmp!IopDeleteLockedDeviceNodes+0xF5
|
|
//
|
|
// Lock A (Spinlock) @ bfc7c254
|
|
// Stack: << Current stack >>
|
|
//
|
|
|
|
|
|
ThreadNumber = 0;
|
|
ResourceNumber = 0;
|
|
J=0;
|
|
|
|
while (J <= NumberOfParticipants) {
|
|
|
|
ThreadForChain = Participants[J].Thread;
|
|
dprintf("Thread %d: %08X",ThreadNumber, ThreadForChain);
|
|
if (DumpNodes && Participants[J].ThreadEntry) {
|
|
dprintf(" (ThreadEntry = %X)\n ", (ULONG) Participants[J].ThreadEntry);
|
|
}
|
|
dprintf(" took locks in the following order:\n\n");
|
|
|
|
|
|
//
|
|
// This is a do .. while so that we can never get an infinite loop.
|
|
//
|
|
do {
|
|
UINT64 CurrentStackAddress;
|
|
UINT64 StackFrame;
|
|
CHAR Buffer[0xFF];
|
|
ULONG64 Displacement = 0;
|
|
|
|
|
|
if (J == NumberOfParticipants) {
|
|
ResourceNumber = 0;
|
|
}
|
|
|
|
GetSymbol(Participants[J].ResourceAddress, Buffer, &Displacement);
|
|
|
|
dprintf(" Lock %c -- %s", 'A' + ResourceNumber, Buffer );
|
|
if (Displacement != 0) {
|
|
dprintf("%s%x", (Displacement < 0xFFF)?"+0x":"",Displacement);
|
|
}
|
|
dprintf(" (%s)\n",ResourceTypes[Participants[J].Type]);
|
|
|
|
|
|
if (DumpNodes && Participants[J].Node)
|
|
dprintf(" Node: %X\n", (ULONG) Participants[J].Node);
|
|
|
|
dprintf(" Stack: ");
|
|
|
|
CurrentStackAddress = Participants[J].StackAddress;
|
|
|
|
if (CurrentStackAddress == 0) {
|
|
|
|
dprintf ("<< Current stack >>\n");
|
|
|
|
} else {
|
|
|
|
for (I = 0; I < StackTraceSize; I++) {
|
|
|
|
SymbolName[0] = '\0';
|
|
StackFrame = ReadPvoid(CurrentStackAddress);
|
|
if (0 == StackFrame)
|
|
break;
|
|
|
|
GetSymbol(StackFrame, SymbolName, &SymbolOffset);
|
|
|
|
if (I) {
|
|
dprintf(" ");
|
|
}
|
|
|
|
if ((LONG64) SymbolOffset > 0 ) {
|
|
dprintf ("%s+0x%X\n",
|
|
SymbolName, (ULONG) SymbolOffset);
|
|
} else {
|
|
dprintf ("%X\n", (ULONG) StackFrame);
|
|
}
|
|
|
|
CurrentStackAddress += PtrSize;
|
|
}
|
|
}
|
|
|
|
dprintf("\n");
|
|
J++;
|
|
ResourceNumber++;
|
|
|
|
} while( J <= NumberOfParticipants && Participants[J].ResourceAddress != Participants[J-1].ResourceAddress);
|
|
|
|
ThreadNumber++;
|
|
ResourceNumber--;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
VOID
|
|
PrintGlobalStatistics (
|
|
ULONG64 GlobalsAddress
|
|
)
|
|
{
|
|
ULONG AllocationFailures;
|
|
ULONG64 MemoryUsed;
|
|
ULONG NodesTrimmed;
|
|
ULONG MaxNodesSearched;
|
|
ULONG SequenceNumber;
|
|
|
|
//
|
|
// Dump the globals structure
|
|
//
|
|
|
|
InitTypeRead (GlobalsAddress, nt!_VI_DEADLOCK_GLOBALS);
|
|
|
|
//
|
|
// Print some simple statistics
|
|
//
|
|
|
|
dprintf ("Resources: %u\n", (ULONG) ReadField (Resources[0]));
|
|
dprintf ("Nodes: %u\n", (ULONG) ReadField (Nodes[0]));
|
|
dprintf ("Threads: %u\n", (ULONG) ReadField (Threads[0]));
|
|
dprintf ("\n");
|
|
|
|
MemoryUsed = ReadField (BytesAllocated);
|
|
|
|
if (MemoryUsed > 1024 * 1024) {
|
|
dprintf ("%I64u bytes of kernel pool used.\n", MemoryUsed);
|
|
}
|
|
|
|
AllocationFailures = (ULONG) ReadField (AllocationFailures);
|
|
|
|
if (AllocationFailures > 0) {
|
|
dprintf ("Allocation failures encountered (%u).\n", AllocationFailures);
|
|
}
|
|
|
|
NodesTrimmed = (ULONG) ReadField (NodesTrimmedBasedOnAge);
|
|
dprintf ("Nodes trimmed based on age %u.\n", NodesTrimmed);
|
|
NodesTrimmed = (ULONG) ReadField (NodesTrimmedBasedOnCount);
|
|
dprintf ("Nodes trimmed based on count %u.\n", NodesTrimmed);
|
|
|
|
dprintf ("Analyze calls %u.\n", (ULONG) ReadField (SequenceNumber));
|
|
dprintf ("Maximum nodes searched %u.\n", (ULONG) ReadField (MaxNodesSearched));
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
SearchForResource (
|
|
ULONG64 GlobalsAddress,
|
|
ULONG64 ResourceAddress
|
|
)
|
|
{
|
|
ULONG I;
|
|
ULONG64 Bucket;
|
|
ULONG64 SizeOfBucket;
|
|
BOOLEAN ResourceFound = FALSE;
|
|
ULONG64 SizeOfResource;
|
|
ULONG FlinkOffset = 0;
|
|
ULONG64 Current;
|
|
ULONG64 CurrentResource;
|
|
ULONG Magic;
|
|
|
|
SizeOfBucket = GetTypeSize("LIST_ENTRY");
|
|
SizeOfResource = GetTypeSize("nt!_VI_DEADLOCK_RESOURCE");
|
|
|
|
GetFieldOffset("nt!_VI_DEADLOCK_RESOURCE",
|
|
"HashChainList",
|
|
&FlinkOffset);
|
|
|
|
if (SizeOfBucket == 0 || SizeOfResource == 0 || FlinkOffset == 0) {
|
|
dprintf ("Error: cannot get size for verifier types. \n");
|
|
return FALSE;
|
|
}
|
|
|
|
InitTypeRead (GlobalsAddress, nt!_VI_DEADLOCK_GLOBALS);
|
|
|
|
Bucket = ReadField (ResourceDatabase);
|
|
|
|
if (Bucket == 0) {
|
|
dprintf ("Error: cannot get resource database address. \n");
|
|
return FALSE;
|
|
}
|
|
|
|
for (I = 0; I < VI_DEADLOCK_HASH_BINS; I += 1) {
|
|
|
|
// traverse it ...
|
|
|
|
Current = ReadPvoid(Bucket);
|
|
|
|
while (Current != Bucket) {
|
|
|
|
InitTypeRead (Current - FlinkOffset, nt!_VI_DEADLOCK_RESOURCE);
|
|
CurrentResource = ReadField (ResourceAddress);
|
|
|
|
if (CurrentResource == ResourceAddress ||
|
|
ResourceAddress == Current - FlinkOffset) {
|
|
|
|
CurrentResource = Current - FlinkOffset;
|
|
ResourceFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
Current = ReadPvoid(Current);
|
|
|
|
if (CheckControlC()) {
|
|
dprintf ("\nSearch interrupted ... \n");
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (ResourceFound) {
|
|
break;
|
|
}
|
|
|
|
dprintf (".");
|
|
|
|
Bucket += SizeOfBucket;
|
|
|
|
}
|
|
|
|
dprintf ("\n");
|
|
|
|
if (ResourceFound == FALSE) {
|
|
|
|
dprintf ("No resource correspoding to %p has been found. \n",
|
|
ResourceAddress);
|
|
}
|
|
else {
|
|
|
|
dprintf ("Found a deadlock verifier resource descriptor @ %p \n",
|
|
CurrentResource);
|
|
|
|
}
|
|
|
|
return ResourceFound;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
SearchForThread (
|
|
ULONG64 GlobalsAddress,
|
|
ULONG64 ThreadAddress
|
|
)
|
|
{
|
|
ULONG I;
|
|
ULONG64 Bucket;
|
|
ULONG64 SizeOfBucket;
|
|
BOOLEAN ThreadFound = FALSE;
|
|
ULONG64 SizeOfThread;
|
|
ULONG FlinkOffset = 0;
|
|
ULONG64 Current;
|
|
ULONG64 CurrentThread;
|
|
|
|
SizeOfBucket = GetTypeSize("LIST_ENTRY");
|
|
SizeOfThread = GetTypeSize("nt!_VI_DEADLOCK_THREAD");
|
|
|
|
GetFieldOffset("nt!_VI_DEADLOCK_THREAD",
|
|
"ListEntry",
|
|
&FlinkOffset);
|
|
|
|
if (SizeOfBucket == 0 || SizeOfThread == 0 || FlinkOffset == 0) {
|
|
dprintf ("Error: cannot get size for verifier types. \n");
|
|
return FALSE;
|
|
}
|
|
|
|
InitTypeRead (GlobalsAddress, nt!_VI_DEADLOCK_GLOBALS);
|
|
|
|
Bucket = ReadField (ThreadDatabase);
|
|
|
|
if (Bucket == 0) {
|
|
dprintf ("Error: cannot get thread database address. \n");
|
|
return FALSE;
|
|
}
|
|
|
|
for (I = 0; I < VI_DEADLOCK_HASH_BINS; I += 1) {
|
|
|
|
// traverse it ...
|
|
|
|
Current = ReadPvoid(Bucket);
|
|
|
|
while (Current != Bucket) {
|
|
|
|
InitTypeRead (Current - FlinkOffset, nt!_VI_DEADLOCK_THREAD);
|
|
CurrentThread = ReadField (ThreadAddress);
|
|
|
|
if (CurrentThread == ThreadAddress ||
|
|
ThreadAddress == Current - FlinkOffset) {
|
|
|
|
CurrentThread = Current - FlinkOffset;
|
|
ThreadFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
Current = ReadPvoid(Current);
|
|
|
|
if (CheckControlC()) {
|
|
dprintf ("\nSearch interrupted ... \n");
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (ThreadFound) {
|
|
break;
|
|
}
|
|
|
|
dprintf (".");
|
|
|
|
Bucket += SizeOfBucket;
|
|
|
|
}
|
|
|
|
dprintf ("\n");
|
|
|
|
if (ThreadFound == FALSE) {
|
|
|
|
dprintf ("No thread correspoding to %p has been found. \n",
|
|
ThreadAddress);
|
|
}
|
|
else {
|
|
|
|
dprintf ("Found a deadlock verifier thread descriptor @ %p \n",
|
|
CurrentThread);
|
|
|
|
}
|
|
|
|
return ThreadFound;
|
|
}
|
|
|
|
|
|
VOID
|
|
DumpResourceStructure (
|
|
)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
ULONG
|
|
GetNodeLevel (
|
|
ULONG64 Node
|
|
)
|
|
{
|
|
ULONG Level = 0;
|
|
|
|
while (Node != 0) {
|
|
|
|
Level += 1;
|
|
|
|
if (Level > 12) {
|
|
dprintf ("Level > 12 !!! \n");
|
|
break;
|
|
}
|
|
|
|
InitTypeRead (Node, nt!_VI_DEADLOCK_NODE);
|
|
Node = ReadField (Parent);
|
|
}
|
|
|
|
return Level;
|
|
}
|
|
|
|
BOOLEAN
|
|
AnalyzeResource (
|
|
ULONG64 Resource,
|
|
BOOLEAN Verbose
|
|
)
|
|
{
|
|
ULONG64 Start;
|
|
ULONG64 Current;
|
|
ULONG64 Node;
|
|
ULONG64 Parent;
|
|
ULONG FlinkOffset;
|
|
ULONG RootsCount = 0;
|
|
ULONG NodesCount = 0;
|
|
ULONG Levels[8];
|
|
ULONG ResourceFlinkOffset;
|
|
ULONG I;
|
|
ULONG Level;
|
|
ULONG NodeCounter = 0;
|
|
|
|
ZeroMemory (Levels, sizeof Levels);
|
|
|
|
GetFieldOffset("nt!_VI_DEADLOCK_NODE",
|
|
"ResourceList",
|
|
&FlinkOffset);
|
|
|
|
GetFieldOffset("nt!_VI_DEADLOCK_RESOURCE",
|
|
"ResourceList",
|
|
&ResourceFlinkOffset);
|
|
|
|
InitTypeRead (Resource, nt!_VI_DEADLOCK_RESOURCE);
|
|
|
|
if (! Verbose) {
|
|
|
|
if (ReadField(NodeCount) < 4) {
|
|
return TRUE;
|
|
}
|
|
|
|
dprintf ("Resource (%p) : %I64u %I64u %I64u ",
|
|
Resource,
|
|
ReadField(Type),
|
|
ReadField(NodeCount),
|
|
ReadField(RecursionCount));
|
|
}
|
|
|
|
Start = Resource + ResourceFlinkOffset;
|
|
Current = ReadPvoid (Start);
|
|
|
|
while (Start != Current) {
|
|
|
|
Node = Current - FlinkOffset;
|
|
|
|
Level = (GetNodeLevel(Node) - 1) % 8;
|
|
Levels[Level] += 1;
|
|
|
|
NodesCount += 1;
|
|
|
|
if (NodesCount && NodesCount % 1000 == 0) {
|
|
dprintf (".");
|
|
}
|
|
|
|
Current = ReadPvoid (Current);
|
|
|
|
if (CheckControlC()) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
dprintf ("[");
|
|
for (I = 0; I < 8; I += 1) {
|
|
dprintf ("%u ", Levels[I]);
|
|
}
|
|
dprintf ("]\n");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
AnalyzeResources (
|
|
ULONG64 GlobalsAddress
|
|
)
|
|
/*++
|
|
|
|
This routine analyzes all resource to make sure we do not have
|
|
zombie nodes laying around.
|
|
|
|
--*/
|
|
{
|
|
ULONG I;
|
|
ULONG64 Bucket;
|
|
ULONG64 SizeOfBucket;
|
|
ULONG64 SizeOfResource;
|
|
ULONG FlinkOffset = 0;
|
|
ULONG64 Current;
|
|
ULONG64 CurrentResource;
|
|
ULONG Magic;
|
|
BOOLEAN Finished;
|
|
ULONG ResourceCount = 0;
|
|
|
|
dprintf ("Analyzing resources (%p) ... \n", GlobalsAddress);
|
|
|
|
SizeOfBucket = GetTypeSize("LIST_ENTRY");
|
|
SizeOfResource = GetTypeSize("nt!_VI_DEADLOCK_RESOURCE");
|
|
|
|
GetFieldOffset("nt!_VI_DEADLOCK_RESOURCE",
|
|
"HashChainList",
|
|
&FlinkOffset);
|
|
|
|
if (SizeOfBucket == 0 || SizeOfResource == 0 || FlinkOffset == 0) {
|
|
dprintf ("Error: cannot get size for verifier types. \n");
|
|
return FALSE;
|
|
}
|
|
|
|
InitTypeRead (GlobalsAddress, nt!_VI_DEADLOCK_GLOBALS);
|
|
|
|
Bucket = ReadField (ResourceDatabase);
|
|
|
|
if (Bucket == 0) {
|
|
dprintf ("Error: cannot get resource database address. \n");
|
|
return FALSE;
|
|
}
|
|
|
|
for (I = 0; I < VI_DEADLOCK_HASH_BINS; I += 1) {
|
|
|
|
// traverse it ...
|
|
|
|
Current = ReadPvoid(Bucket);
|
|
|
|
while (Current != Bucket) {
|
|
|
|
Finished = AnalyzeResource (Current - FlinkOffset, FALSE);
|
|
ResourceCount += 1;
|
|
|
|
if (ResourceCount % 256 == 0) {
|
|
dprintf (".\n");
|
|
}
|
|
|
|
Current = ReadPvoid(Current);
|
|
|
|
if (CheckControlC() || !Finished) {
|
|
dprintf ("\nSearch interrupted ... \n");
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
Bucket += SizeOfBucket;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|