1 ////////////////////////////////////////////////////////////////////
2 // Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
4 // This file was released under the GPLv2 on June 2015.
5 ////////////////////////////////////////////////////////////////////
14 This file contains all source code related to DeadLock Detector.
24 /// define the file specific bug-check id
25 #define UDF_BUG_CHECK_ID UDF_FILE_DLD
28 /// Resource event (ExclusiveWaiters)
29 #define RESOURCE_EVENT_TAG 'vEeR'
30 /// Resource semaphore (SharedWaiters)
31 #define RESOURCE_SEMAFORE_TAG 'eSeR'
32 /// Resource owner table (OwnerTable)
33 #define RESOURCE_TABLE_TAG 'aTeR'
35 /// Maxmum recurse level while exploring thread-resource aquisition graf
36 #define DLD_MAX_REC_LEVEL 40
38 /// Maximum supported number of threads (initialized by DLDInit())
39 ULONG MaxThreadCount
= 0;
42 PTHREAD_STRUCT DLDThreadTable
;
44 LARGE_INTEGER DLDpTimeout
;
46 ULONG DLDpResourceTimeoutCount
= 0x2;
48 THREAD_REC_BLOCK DLDThreadAcquireChain
[DLD_MAX_REC_LEVEL
];
50 /// Initialize deadlock detector
51 VOID
DLDInit(ULONG MaxThrdCount
/// Maximum supported number of threads
53 if (KeNumberProcessors
>1) {
54 UDFPrint(("Deadlock Detector is designed for uniprocessor machines only!\n"));
57 DLDpTimeout
.QuadPart
= -40000000I64
;
59 MaxThreadCount
= MaxThrdCount
;
60 DLDThreadTable
= (PTHREAD_STRUCT
) DLDAllocatePool(MaxThreadCount
*sizeof(THREAD_STRUCT
));
61 RtlZeroMemory(DLDThreadTable
, sizeof(THREAD_STRUCT
)*MaxThreadCount
);
66 DLDFreePool(DLDThreadTable
);
70 PTHREAD_STRUCT
DLDAllocFindThread(ULONG ThreadId
) {
72 PTHREAD_STRUCT Temp
= DLDThreadTable
;
73 ULONG FirstEmpty
= -1;
75 while (i
<MaxThreadCount
) {
76 if (Temp
->ThreadId
== ThreadId
) {
78 } else if (FirstEmpty
== -1 && !Temp
->ThreadId
) {
84 // Not found. Allocate new one.
85 if (i
== MaxThreadCount
) {
86 if (FirstEmpty
== -1) {
87 UDFPrint(("Not enough table entries. Try to increase MaxThrdCount on next build"));
92 Temp
= DLDThreadTable
+ i
;
94 RtlZeroMemory(Temp
, sizeof(THREAD_STRUCT
));
95 Temp
->ThreadId
= ThreadId
;
100 PTHREAD_STRUCT
DLDFindThread(ULONG ThreadId
) {
102 PTHREAD_STRUCT Temp
= DLDThreadTable
;
103 ULONG FirstEmpty
= -1;
106 while (i
<MaxThreadCount
) {
107 if (Temp
->ThreadId
== ThreadId
) {
117 BOOLEAN
DLDProcessResource(PERESOURCE Resource
,
118 PTHREAD_STRUCT ThrdStruct
,
122 /// TRUE Indicates deadlock
123 BOOLEAN
DLDProcessThread(PTHREAD_STRUCT ThrdOwner
,
124 PTHREAD_STRUCT ThrdStruct
,
128 if (ThrdOwner
== ThrdStruct
) {
129 // ERESOURCE wait cycle. Deadlock detected.
130 UDFPrint(("DLD: *********DEADLOCK DETECTED*********\n"));
131 UDFPrint(("Thread %x holding resource %x\n",ThrdOwner
->ThreadId
,Resource
));
135 for (int i
=RecLevel
+1;i
<DLD_MAX_REC_LEVEL
;i
++) {
136 if (DLDThreadAcquireChain
[i
].Thread
->ThreadId
== ThrdOwner
->ThreadId
) {
137 // ERESOURCE wait cycle. Deadlock detected.
138 UDFPrint(("DLD: *********DEADLOCK DETECTED*********\n"));
139 UDFPrint(("Thread %x holding resource %x\n",ThrdOwner
->ThreadId
,Resource
));
140 for (int j
=RecLevel
+1;j
<=i
;j
++) {
141 UDFPrint((" awaited by thread %x at (BugCheckId:%x:Line:%d) holding resource %x\n",
142 DLDThreadAcquireChain
[i
].Thread
->ThreadId
,
143 DLDThreadAcquireChain
[i
].Thread
->BugCheckId
,
144 DLDThreadAcquireChain
[i
].Thread
->Line
,
151 DLDThreadAcquireChain
[RecLevel
].Thread
= ThrdOwner
;
152 DLDThreadAcquireChain
[RecLevel
].HoldingResource
= Resource
;
154 // Find resource, awaited by thread
155 if (ThrdOwner
->WaitingResource
) {
156 if (DLDProcessResource(ThrdOwner
->WaitingResource
, ThrdStruct
,RecLevel
)) {
157 UDFPrint((" awaited by thread %x at (BugCheckId:%x:Line:%d) holding resource %x\n",
159 ThrdOwner
->BugCheckId
,
170 /// TRUE Indicates deadlock
171 BOOLEAN
DLDProcessResource( PERESOURCE Resource
, // resource to process
172 PTHREAD_STRUCT ThrdStruct
, // thread structure of caller's thread
173 ULONG RecLevel
) // current recurse level
181 // If resource is free, just return. Not possible, but we must check.
182 if (!Resource
->ActiveCount
) {
186 PTHREAD_STRUCT ThreadOwner
;
189 if (Resource
->Flag
& ResourceOwnedExclusive
|| (Resource
->OwnerThreads
[1].OwnerCount
== 1)) {
193 // Find thread owning this resource
194 if (Resource
->Flag
& ResourceOwnedExclusive
) {
195 ThreadOwner
= DLDFindThread(Resource
->OwnerThreads
[0].OwnerThread
);
197 ThreadOwner
= DLDFindThread(Resource
->OwnerThreads
[1].OwnerThread
);
200 BOOLEAN Result
= FALSE
;
202 Result
= DLDProcessThread(ThreadOwner
, ThrdStruct
, Resource
,RecLevel
-1);
208 for (i
=0; i
<Resource
->OwnerThreads
[0].TableSize
; i
++) {
209 if (Resource
->OwnerTable
[i
].OwnerThread
) {
211 ThreadOwner
= DLDFindThread(Resource
->OwnerTable
[i
].OwnerThread
);
212 if (ThreadOwner
&& DLDProcessThread(ThreadOwner
, ThrdStruct
, Resource
,RecLevel
-1)) {
225 VOID
DLDpWaitForResource(
226 IN PERESOURCE Resource
,
227 IN DISPATCHER_HEADER
*DispatcherObject
,
228 IN PTHREAD_STRUCT ThrdStruct
231 ULONG ResourceWaitCount
= 0;
233 Resource
->ContentionCount
++;
235 while (KeWaitForSingleObject(DispatcherObject
,Executive
,KernelMode
,FALSE
,&DLDpTimeout
) == STATUS_TIMEOUT
) {
236 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
237 if (++ResourceWaitCount
>DLDpResourceTimeoutCount
) {
239 ResourceWaitCount
= 0;
241 if (DLDProcessResource(Resource
, ThrdStruct
,DLD_MAX_REC_LEVEL
)) {
242 UDFPrint((" which thread %x has tried to acquire at (BugCheckId:%x:Line:%d)\n",
243 ThrdStruct
->ThreadId
,
244 ThrdStruct
->BugCheckId
,
252 // End of priority boosts
253 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
261 VOID
DLDpAcquireResourceExclusiveLite(
262 IN PERESOURCE Resource
,
263 IN ERESOURCE_THREAD Thread
,
270 if (!(Resource
->ExclusiveWaiters
)) {
272 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
273 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql2
);
275 // If ExclusiveWaiters Event not yet allocated allocate new one
276 if (!(Resource
->ExclusiveWaiters
)) {
277 Resource
->ExclusiveWaiters
= (PKEVENT
)ExAllocatePoolWithTag(NonPagedPool
, sizeof(KEVENT
),RESOURCE_EVENT_TAG
);
278 KeInitializeEvent(Resource
->ExclusiveWaiters
,SynchronizationEvent
,FALSE
);
280 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql2
);
281 DLDAcquireExclusive(Resource
,BugCheckId
,Line
);
284 Resource
->NumberOfExclusiveWaiters
++;
286 PTHREAD_STRUCT ThrdStruct
= DLDAllocFindThread(Thread
);
289 // Set WaitingResource for current thread
290 ThrdStruct
->WaitingResource
= Resource
;
291 ThrdStruct
->BugCheckId
= BugCheckId
;
292 ThrdStruct
->Line
= Line
;
294 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
296 DLDpWaitForResource(Resource
,&(Resource
->ExclusiveWaiters
->Header
),ThrdStruct
);
298 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
300 ThrdStruct
->WaitingResource
= NULL
;
301 ThrdStruct
->ThreadId
= 0;
302 ThrdStruct
->BugCheckId
= 0;
303 ThrdStruct
->Line
= 0;
304 Resource
->OwnerThreads
[0].OwnerThread
= Thread
;
306 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
313 VOID
DLDAcquireExclusive(PERESOURCE Resource
,
320 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
322 ERESOURCE_THREAD Thread
= (ERESOURCE_THREAD
)PsGetCurrentThread();
324 if (!Resource
->ActiveCount
) goto SimpleAcquire
;
325 if ((Resource
->Flag
& ResourceOwnedExclusive
) && Resource
->OwnerThreads
[0].OwnerThread
== Thread
) {
326 Resource
->OwnerThreads
[0].OwnerCount
++;
327 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
331 DLDpAcquireResourceExclusiveLite(Resource
, Thread
, oldIrql
,BugCheckId
,Line
);
336 Resource
->Flag
|= ResourceOwnedExclusive
;
337 Resource
->ActiveCount
= 1;
338 Resource
->OwnerThreads
[0].OwnerThread
= Thread
;
339 Resource
->OwnerThreads
[0].OwnerCount
= 1;
341 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
345 POWNER_ENTRY
DLDpFindCurrentThread(
346 IN PERESOURCE Resource
,
347 IN ERESOURCE_THREAD Thread
350 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
) return &(Resource
->OwnerThreads
[0]);
351 if (Resource
->OwnerThreads
[1].OwnerThread
== Thread
) return &(Resource
->OwnerThreads
[1]);
353 POWNER_ENTRY LastEntry
, CurrentEntry
, FirstEmptyEntry
= NULL
;
354 if (!(Resource
->OwnerThreads
[1].OwnerThread
)) FirstEmptyEntry
= &(Resource
->OwnerThreads
[1]);
356 CurrentEntry
= Resource
->OwnerTable
;
357 LastEntry
= &(Resource
->OwnerTable
[Resource
->OwnerThreads
[0].TableSize
]);
359 while (CurrentEntry
!= LastEntry
) {
360 if (CurrentEntry
->OwnerThread
== Thread
) {
361 PCHAR CurrentThread
= (PCHAR
)PsGetCurrentThread();
362 *((PULONG
)(CurrentThread
+ 0x136)) = CurrentEntry
- Resource
->OwnerTable
;
365 if (!(CurrentEntry
->OwnerThread
)) {
366 FirstEmptyEntry
= CurrentEntry
;
370 if (FirstEmptyEntry
) {
371 PCHAR CurrentThread
= (PCHAR
)PsGetCurrentThread();
372 *((PULONG
)(CurrentThread
+ 0x136)) = FirstEmptyEntry
- Resource
->OwnerTable
;
373 return FirstEmptyEntry
;
378 USHORT OldSize
= Resource
->OwnerThreads
[0].TableSize
;
380 if (OldSize
) NewSize
= OldSize
+ 4;
381 POWNER_ENTRY NewEntry
= (POWNER_ENTRY
)ExAllocatePoolWithTag(NonPagedPool
, sizeof(OWNER_ENTRY
)*NewSize
,RESOURCE_TABLE_TAG
);
382 RtlZeroMemory(NewEntry
,sizeof(OWNER_ENTRY
)*NewSize
);
383 if (Resource
->OwnerTable
) {
384 RtlMoveMemory(NewEntry
,Resource
->OwnerTable
,sizeof(OWNER_ENTRY
)*OldSize
);
385 ExFreePool(Resource
->OwnerTable
);
387 Resource
->OwnerTable
= NewEntry
;
389 PCHAR CurrentThread
= (PCHAR
)PsGetCurrentThread();
390 *((PULONG
)(CurrentThread
+ 0x136)) = OldSize
;
391 Resource
->OwnerThreads
[0].TableSize
= NewSize
;
393 return &(NewEntry
[OldSize
]);
398 VOID
DLDAcquireShared(PERESOURCE Resource
,
401 BOOLEAN WaitForExclusive
)
406 ERESOURCE_THREAD Thread
= (ERESOURCE_THREAD
)PsGetCurrentThread();
407 POWNER_ENTRY pOwnerEntry
;
409 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
411 if (!Resource
->ActiveCount
) {
412 Resource
->Flag
&= ~ResourceOwnedExclusive
;
413 Resource
->ActiveCount
= 1;
414 Resource
->OwnerThreads
[1].OwnerThread
= Thread
;
415 Resource
->OwnerThreads
[1].OwnerCount
= 1;
416 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
420 if (Resource
->Flag
& ResourceOwnedExclusive
) {
421 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
) {
422 Resource
->OwnerThreads
[0].OwnerCount
++;
423 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
427 pOwnerEntry
= DLDpFindCurrentThread(Resource
, 0);
430 // owned shared by some thread(s)
432 pOwnerEntry
= DLDpFindCurrentThread(Resource
, Thread
);
434 if (!WaitForExclusive
&& pOwnerEntry
->OwnerThread
== Thread
) {
435 pOwnerEntry
->OwnerCount
++;
436 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
440 if (!(Resource
->NumberOfExclusiveWaiters
)) {
442 pOwnerEntry
->OwnerThread
= Thread
;
443 pOwnerEntry
->OwnerCount
= 1;
444 Resource
->ActiveCount
++;
445 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
451 if (!(Resource
->SharedWaiters
)) {
452 Resource
->SharedWaiters
= (PKSEMAPHORE
)ExAllocatePoolWithTag(NonPagedPool
, sizeof(KSEMAPHORE
),RESOURCE_SEMAFORE_TAG
);
453 KeInitializeSemaphore(Resource
->SharedWaiters
,0,0x7fffffff);
456 Resource
->NumberOfSharedWaiters
++;
458 PTHREAD_STRUCT ThrdStruct
= DLDAllocFindThread(Thread
);
461 // Set WaitingResource for current thread
462 ThrdStruct
->WaitingResource
= Resource
;
463 ThrdStruct
->BugCheckId
= BugCheckId
;
464 ThrdStruct
->Line
= Line
;
466 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
468 DLDpWaitForResource(Resource
,&(Resource
->SharedWaiters
->Header
),ThrdStruct
);
470 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
472 pOwnerEntry
= DLDpFindCurrentThread(Resource
, Thread
);
473 pOwnerEntry
->OwnerThread
= Thread
;
474 pOwnerEntry
->OwnerCount
= 1;
476 ThrdStruct
->WaitingResource
= NULL
;
477 ThrdStruct
->ThreadId
= 0;
478 ThrdStruct
->BugCheckId
= 0;
479 ThrdStruct
->Line
= 0;
481 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);