Source code of Windows XP (NT5)
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.

2432 lines
59 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. vfpdlock.c
  5. Abstract:
  6. Detect deadlocks in arbitrary synchronization objects.
  7. Author:
  8. Jordan Tigani (jtigani) 2-May-2000
  9. Silviu Calinoiu (silviuc) 9-May-2000
  10. Revision History:
  11. --*/
  12. //
  13. // TO DO LIST
  14. //
  15. // - detect a resource allocated on the stack of current thread and give
  16. // warning about this.
  17. // - clean deletion of resources, threads, nodes
  18. // - get rid of FirstNode hack
  19. // - create nodes based on (R, T, Stk).
  20. // - keep deleted nodes around if other nodes in the path are still alive.
  21. // See ISSUE in ViDeadlockDeleteRange.
  22. // - Make sure NodeCount is updated all over the place.
  23. //
  24. #define _BUGGY_ 1
  25. //
  26. // ISSUE --
  27. // This ifdef lets us move the code back and forth between the kernel
  28. // and the buggy driver -- the latter is for testing purposes
  29. //
  30. #if _BUGGY_
  31. #include <ntddk.h>
  32. #include "deadlock.h"
  33. #else
  34. #include "vfdef.h"
  35. #endif
  36. //#include "vfpdlock.h"
  37. //
  38. // Deadlock detection structures.
  39. //
  40. // There are three important structures involved: THREAD, RESOURCE, NODE.
  41. //
  42. // For every active thread in the system that holds at least one resource
  43. // the package maintains a THREAD structure. This gets created when a thread
  44. // acquires first resource and gets destroyed when thread releases the last
  45. // resource. If a thread does not hold any resource it will not have a
  46. // corresponding THREAD structure.
  47. //
  48. // For every resource in the system there is a RESOURCE structure. A dead resource
  49. // might still have a RESOURCE laying around because the algorithm to garbage
  50. // collect old resources is of the lazy type.
  51. //
  52. // Every acquisition of a resource is modeled by a NODE structure. When a thread
  53. // acquires resource B while holding A the package will create a NODE for B and link
  54. // it to the node for A. Beware that this is a very general description of what
  55. // happens.
  56. //
  57. // There are three important functions that make the interface with the outside
  58. // world.
  59. //
  60. // ViDeadlockAddResource hook for resource initialization
  61. // ViDeadlockQueryAcquireResource checks for deadlock before resource acquisition
  62. // ViDeadlockAcquireResource hook for resource acquire
  63. // ViDeadlockReleaseResource hook for resource release
  64. //
  65. // Unfortunately almost no kernel synchronization object has a delete routine
  66. // therefore we need to lazily garbage collect any zombie resources from our
  67. // structures.
  68. //
  69. //
  70. // Did we initialize the verifier deadlock detection package?
  71. // If this variable is false no detection will be done whatsoever.
  72. //
  73. BOOLEAN ViDeadlockDetectionInitialized = FALSE;
  74. //
  75. // Enable/disable the deadlock detection package. This can be used
  76. // to disable temporarily the deadlock detection package.
  77. //
  78. BOOLEAN ViDeadlockDetectionEnabled =
  79. #if _BUGGY_
  80. TRUE;
  81. #else
  82. FALSE;
  83. #endif
  84. #define VI_DEADLOCK_FLAG_RECURSIVE_ACQUISITION_OK 0x1
  85. #define VI_DEADLOCK_FLAG_NO_INITIALIZATION_FUNCTION 0x2
  86. ULONG ViDeadlockResourceTypeInfo[ViDeadlockTypeMaximum] =
  87. {
  88. // ViDeadlockUnknown //
  89. 0,
  90. // ViDeadlockMutex//
  91. VI_DEADLOCK_FLAG_RECURSIVE_ACQUISITION_OK,
  92. // ViDeadlockFastMutex //
  93. VI_DEADLOCK_FLAG_NO_INITIALIZATION_FUNCTION,
  94. };
  95. //
  96. // Max depth of stack traces captured.
  97. //
  98. #define VI_MAX_STACK_DEPTH 8
  99. NTSYSAPI
  100. USHORT
  101. NTAPI
  102. RtlCaptureStackBackTrace(
  103. IN ULONG FramesToSkip,
  104. IN ULONG FramesToCapture,
  105. OUT PVOID *BackTrace,
  106. OUT PULONG BackTraceHash
  107. );
  108. //
  109. // Deadlock specific issues (bugs)
  110. //
  111. // SELF_DEADLOCK
  112. //
  113. // Acquire resource recursively.
  114. //
  115. // DEADLOCK_DETECTED
  116. //
  117. // Plain deadlock. Need the previous information
  118. // messages to build a deadlock proof.
  119. //
  120. // UNINITIALIZED_RESOURCE
  121. //
  122. // Acquiring a resource that was never initialized.
  123. //
  124. // UNEXPECTED_RELEASE
  125. //
  126. // Releasing a resource which is not the last one
  127. // acquired by the current thread.
  128. //
  129. // UNEXPECTED_THREAD
  130. //
  131. // Current thread does not have any resources
  132. // acquired.
  133. //
  134. // MULTIPLE_INITIALIZATION
  135. //
  136. // Attempting to initialize a second time the same
  137. // resource.
  138. //
  139. #define VI_DEADLOCK_ISSUE_DEADLOCK_SELF_DEADLOCK 0x1000
  140. #define VI_DEADLOCK_ISSUE_DEADLOCK_DETECTED 0x1001
  141. #define VI_DEADLOCK_ISSUE_UNINITIALIZED_RESOURCE 0x1002
  142. #define VI_DEADLOCK_ISSUE_UNEXPECTED_RELEASE 0x1003
  143. #define VI_DEADLOCK_ISSUE_UNEXPECTED_THREAD 0x1004
  144. #define VI_DEADLOCK_ISSUE_MULTIPLE_INITIALIZATION 0x1005
  145. #define VI_DEADLOCK_ISSUE_THREAD_HOLDS_RESOURCES 0x1006
  146. //
  147. // VI_DEADLOCK_NODE
  148. //
  149. typedef struct _VI_DEADLOCK_NODE {
  150. //
  151. // Node representing the acquisition of the previous resource.
  152. //
  153. struct _VI_DEADLOCK_NODE * Parent;
  154. //
  155. // Node representing the next resource acquisitions, that are
  156. // done after acquisition of the current resource.
  157. //
  158. struct _LIST_ENTRY ChildrenList;
  159. //
  160. // Field used to chain siblings in the tree. A parent node has the
  161. // ChildrenList field as the head of the children list that is chained
  162. // with the Siblings field.
  163. //
  164. struct _LIST_ENTRY SiblingsList;
  165. //
  166. // List of nodes representing the same resource acquisition
  167. // as the current node but in different contexts (lock combinations).
  168. //
  169. struct _LIST_ENTRY ResourceList;
  170. //
  171. // Back pointer to the descriptor for this resource.
  172. // If the node has been marked for deletion then the
  173. // ResourceAddress field should be used and it has the address
  174. // of the kernel resource address involved. The Root pointer is
  175. // no longer valid because we deallocate the RESOURCE structure
  176. // when it gets deleted.
  177. //
  178. union {
  179. struct _VI_DEADLOCK_RESOURCE * Root;
  180. PVOID ResourceAddress;
  181. };
  182. //
  183. // The number of nodes that are below this one at any depth.
  184. // This counter is used in the node deletion algorithms. It is
  185. // incremented by one on all ancestors of a node created (resource
  186. // acquired) and decremented by one when resource gets deleted.
  187. // If a root of a tree has NodeCount equal to zero the whole tree
  188. // will be deleted.
  189. //
  190. ULONG NodeCount;
  191. //
  192. // When we find a deadlock, we keep this info around in order to
  193. // be able to identify the parties involved who have caused
  194. // the deadlock.
  195. //
  196. PKTHREAD Thread;
  197. PVOID StackTrace[VI_MAX_STACK_DEPTH];
  198. } VI_DEADLOCK_NODE, *PVI_DEADLOCK_NODE;
  199. //
  200. // VI_DEADLOCK_RESOURCE
  201. //
  202. typedef struct _VI_DEADLOCK_RESOURCE {
  203. //
  204. // Since we may need to clean up different kinds of resources
  205. // in different ways, keep track of what kind of resource
  206. // this is.
  207. //
  208. VI_DEADLOCK_RESOURCE_TYPE Type;
  209. //
  210. // The address of the synchronization object used by the kernel.
  211. //
  212. PVOID ResourceAddress;
  213. //
  214. // The thread that curently owns the resource
  215. // (null if no owner)
  216. //
  217. PKTHREAD ThreadOwner;
  218. //
  219. // List of resource nodes representing acquisitions of this resource.
  220. //
  221. LIST_ENTRY ResourceList;
  222. //
  223. // Number of resource nodes created for this resource.
  224. // ISSUE: Why do we need this counter ? (silviuc)
  225. //
  226. ULONG NodeCount;
  227. //
  228. // List used for chaining resources from a hash bucket.
  229. //
  230. LIST_ENTRY HashChainList;
  231. //
  232. // Stack trace of the resource creator.
  233. //
  234. PVOID InitializeStackTrace [VI_MAX_STACK_DEPTH];
  235. } VI_DEADLOCK_RESOURCE, * PVI_DEADLOCK_RESOURCE;
  236. //
  237. // VI_DEADLOCK_THREAD
  238. //
  239. typedef struct _VI_DEADLOCK_THREAD {
  240. //
  241. // Kernel thread address
  242. //
  243. PKTHREAD Thread;
  244. //
  245. // The node representing the last resource acquisition made by
  246. // this thread.
  247. //
  248. PVI_DEADLOCK_NODE CurrentNode;
  249. //
  250. // Thread list. It is used for chaining into a hash bucket.
  251. //
  252. LIST_ENTRY ListEntry;
  253. } VI_DEADLOCK_THREAD, *PVI_DEADLOCK_THREAD;
  254. typedef struct _VI_DEADLOCK_PARTICIPANT {
  255. //
  256. // Address of participant -- could be a resource
  257. // address or a resource node, depending on whether
  258. // NodeInformation is set
  259. //
  260. // NULL participant means that there are no more
  261. // participants
  262. //
  263. PVOID Participant;
  264. //
  265. // True: Participant is type VI_DEADLOCK_NODE
  266. // False: Participant is a PVOID and shouldn't be deref'd
  267. //
  268. BOOLEAN NodeInformation;
  269. } VI_DEADLOCK_PARTICIPANT, *PVI_DEADLOCK_PARTICIPANT;
  270. //
  271. // Deadlock resource and thread databases.
  272. //
  273. //
  274. #define VI_DEADLOCK_HASH_BINS 1
  275. PLIST_ENTRY ViDeadlockResourceDatabase;
  276. PLIST_ENTRY ViDeadlockThreadDatabase;
  277. ULONG ViDeadlockNumberParticipants;
  278. PVI_DEADLOCK_PARTICIPANT ViDeadlockParticipation;
  279. //
  280. // Performance counters
  281. //
  282. ULONG ViDeadlockNumberOfNodes;
  283. ULONG ViDeadlockNumberOfResources;
  284. ULONG ViDeadlockNumberOfThreads;
  285. //
  286. // Maximum recursion depth for deadlock detection algorithm.
  287. //
  288. #define VI_DEADLOCK_MAXIMUM_DEGREE 4
  289. ULONG ViDeadlockMaximumDegree;
  290. //
  291. // Verifier deadlock detection pool tag.
  292. //
  293. #define VI_DEADLOCK_TAG 'kclD'
  294. //
  295. // Global `deadlock lock database' lock
  296. //
  297. KSPIN_LOCK ViDeadlockDatabaseLock;
  298. PKTHREAD ViDeadlockDatabaseOwner;
  299. #define LOCK_DEADLOCK_DATABASE(OldIrql) \
  300. KeAcquireSpinLock(&ViDeadlockDatabaseLock, (OldIrql)); \
  301. ViDeadlockDatabaseOwner = KeGetCurrentThread ();
  302. #define UNLOCK_DEADLOCK_DATABASE(OldIrql) \
  303. ViDeadlockDatabaseOwner = NULL; \
  304. KeReleaseSpinLock(&ViDeadlockDatabaseLock, OldIrql);
  305. //
  306. // Internal deadlock detection functions
  307. //
  308. VOID
  309. ViDeadlockDetectionInitialize(
  310. );
  311. PLIST_ENTRY
  312. ViDeadlockDatabaseHash(
  313. IN PLIST_ENTRY Database,
  314. IN PVOID Address
  315. );
  316. PVI_DEADLOCK_RESOURCE
  317. ViDeadlockSearchResource(
  318. IN PVOID ResourceAddress
  319. );
  320. BOOLEAN
  321. ViDeadlockAddResource(
  322. IN PVOID Resource,
  323. IN VI_DEADLOCK_RESOURCE_TYPE Type
  324. );
  325. BOOLEAN
  326. ViDeadlockQueryAcquireResource(
  327. IN PVOID Resource,
  328. IN VI_DEADLOCK_RESOURCE_TYPE Type
  329. );
  330. BOOLEAN
  331. ViDeadlockSimilarNode (
  332. IN PVOID Resource,
  333. IN PKTHREAD Thread,
  334. IN PVOID * Trace,
  335. IN PVI_DEADLOCK_NODE Node
  336. );
  337. VOID
  338. ViDeadlockAcquireResource(
  339. IN PVOID Resource,
  340. IN VI_DEADLOCK_RESOURCE_TYPE Type
  341. );
  342. VOID
  343. ViDeadlockReleaseResource(
  344. IN PVOID Resource
  345. );
  346. BOOLEAN
  347. ViDeadlockAnalyze(
  348. IN PVOID ResourceAddress,
  349. IN PVI_DEADLOCK_NODE CurrentNode,
  350. IN ULONG Degree
  351. );
  352. PVI_DEADLOCK_THREAD
  353. ViDeadlockSearchThread (
  354. PKTHREAD Thread
  355. );
  356. PVI_DEADLOCK_THREAD
  357. ViDeadlockAddThread (
  358. PKTHREAD Thread
  359. );
  360. VOID
  361. ViDeadlockDeleteThread (
  362. PVI_DEADLOCK_THREAD Thread
  363. );
  364. PVOID
  365. ViDeadlockAllocate (
  366. SIZE_T Size
  367. );
  368. VOID
  369. ViDeadlockFree (
  370. PVOID Object
  371. );
  372. VOID
  373. ViDeadlockReportIssue (
  374. ULONG_PTR Param1,
  375. ULONG_PTR Param2,
  376. ULONG_PTR Param3,
  377. ULONG_PTR Param4
  378. );
  379. VOID
  380. ViDeadlockAddParticipant(
  381. PVOID ResourceAddress,
  382. PVI_DEADLOCK_NODE FirstParticipant, OPTIONAL
  383. PVI_DEADLOCK_NODE SecondParticipant,
  384. ULONG Degree
  385. );
  386. VOID
  387. ViDeadlockDeleteResource (
  388. PVI_DEADLOCK_RESOURCE Resource
  389. );
  390. VOID
  391. ViDeadlockDeleteTree (
  392. PVI_DEADLOCK_NODE Root
  393. );
  394. BOOLEAN
  395. ViDeadlockIsNodeMarkedForDeletion (
  396. PVI_DEADLOCK_NODE Node
  397. );
  398. PVOID
  399. ViDeadlockGetNodeResourceAddress (
  400. PVI_DEADLOCK_NODE Node
  401. );
  402. #ifdef ALLOC_PRAGMA
  403. #if ! _BUGGY_
  404. #pragma alloc_text(INIT, ViDeadlockDetectionInitialize)
  405. #pragma alloc_text(PAGEVRFY, ViDeadlockAnalyze)
  406. #pragma alloc_text(PAGEVRFY, ViDeadlockDatabaseHash)
  407. #pragma alloc_text(PAGEVRFY, ViDeadlockSearchResource)
  408. #pragma alloc_text(PAGEVRFY, ViDeadlockAddResource)
  409. #pragma alloc_text(PAGEVRFY, ViDeadlockSimilarNode)
  410. #pragma alloc_text(PAGEVRFY, ViDeadlockAcquireResource)
  411. #pragma alloc_text(PAGEVRFY, ViDeadlockReleaseResource)
  412. #pragma alloc_text(PAGEVRFY, ViDeadlockSearchThread)
  413. #pragma alloc_text(PAGEVRFY, ViDeadlockAddThread)
  414. #pragma alloc_text(PAGEVRFY, ViDeadlockDeleteThread)
  415. #pragma alloc_text(PAGEVRFY, ViDeadlockAllocate)
  416. #pragma alloc_text(PAGEVRFY, ViDeadlockFree)
  417. #pragma alloc_text(PAGEVRFY, ViDeadlockReportIssue)
  418. #pragma alloc_text(PAGEVRFY, ViDeadlockAddParticipant)
  419. #pragma alloc_text(PAGEVRFY, ViDeadlockDeleteMemoryRange);
  420. #pragma alloc_text(PAGEVRFY, ViDeadlockDeleteResource);
  421. #pragma alloc_text(PAGEVRFY, ViDeadlockWholeTree);
  422. #pragma alloc_text(PAGEVRFY, ViDeadlockIsNodeMarkedForDeletion);
  423. #pragma alloc_text(PAGEVRFY, ViDeadlockGetNodeResourceAddress);
  424. #endif
  425. #endif
  426. /////////////////////////////////////////////////////////////////////
  427. ///////////////////// Initialization and deadlock database management
  428. /////////////////////////////////////////////////////////////////////
  429. PLIST_ENTRY
  430. ViDeadlockDatabaseHash(
  431. IN PLIST_ENTRY Database,
  432. IN PVOID Address
  433. )
  434. /*++
  435. Routine Description:
  436. This routine determines hashes the resource address into the deadlock database.
  437. The hash bin is represented by a list entry
  438. silviuc: very simple minded hash table.
  439. Arguments:
  440. ResourceAddress: Address of the resource that is being hashed
  441. Return Value:
  442. PLIST_ENTRY -- the list entry associated with the hash bin we land in.
  443. --*/
  444. {
  445. return Database + ((ULONG_PTR)Address % VI_DEADLOCK_HASH_BINS);
  446. }
  447. VOID
  448. ViDeadlockDetectionInitialize(
  449. )
  450. /*++
  451. Routine Description:
  452. This routine initializes the data structures necessary for detecting
  453. deadlocks in kernel synchronization objects.
  454. Arguments:
  455. None.
  456. Return Value:
  457. None. Sets ViDeadlockDetectionInitialized to TRUE if successful.
  458. Environment:
  459. System initialization only.
  460. --*/
  461. {
  462. ULONG I;
  463. SIZE_T TableSize;
  464. SIZE_T ParticipationTableSize;
  465. //
  466. // Allocate hash tables for resources and threads.
  467. //
  468. TableSize = sizeof (LIST_ENTRY) * VI_DEADLOCK_HASH_BINS;
  469. ViDeadlockResourceDatabase = ViDeadlockAllocate (TableSize);
  470. if (!ViDeadlockResourceDatabase) {
  471. return;
  472. }
  473. ViDeadlockThreadDatabase = ViDeadlockAllocate (TableSize);
  474. if (!ViDeadlockThreadDatabase) {
  475. ViDeadlockFree (ViDeadlockResourceDatabase);
  476. return;
  477. }
  478. //
  479. // Initialize all.
  480. //
  481. for (I = 0; I < VI_DEADLOCK_HASH_BINS; I += 1) {
  482. InitializeListHead(&ViDeadlockResourceDatabase[I]);
  483. InitializeListHead(&ViDeadlockThreadDatabase[I]);
  484. }
  485. KeInitializeSpinLock(&ViDeadlockDatabaseLock);
  486. ViDeadlockMaximumDegree = VI_DEADLOCK_MAXIMUM_DEGREE;
  487. ViDeadlockNumberParticipants = FALSE;
  488. ViDeadlockDetectionInitialized = TRUE;
  489. //ViDeadlockDetectionEnabled = TRUE;
  490. }
  491. /////////////////////////////////////////////////////////////////////
  492. //////////////////////////////////////////// Deadlock detection logic
  493. /////////////////////////////////////////////////////////////////////
  494. BOOLEAN
  495. ViDeadlockAnalyze(
  496. IN PVOID ResourceAddress,
  497. IN PVI_DEADLOCK_NODE AcquiredNode,
  498. IN ULONG Degree
  499. )
  500. /*++
  501. Routine Description:
  502. This routine determines whether the acquisition of a certain resource
  503. could result in a deadlock.
  504. The routine assumes the deadlock database lock is held.
  505. Arguments:
  506. ResourceAddress - address of the resource that will be acquired
  507. AcquiredNode - a graph describing which resources have been acquired by
  508. the current thread.
  509. Return Value:
  510. True if deadlock detected, false otherwise.
  511. --*/
  512. {
  513. PVI_DEADLOCK_RESOURCE CurrentResource;
  514. PVI_DEADLOCK_NODE CurrentAcquiredNode;
  515. PVI_DEADLOCK_NODE CurrentNode;
  516. PVI_DEADLOCK_NODE CurrentParent;
  517. BOOLEAN FoundDeadlock;
  518. PLIST_ENTRY Current;
  519. ASSERT (ViDeadlockDatabaseOwner == KeGetCurrentThread ());
  520. ASSERT (AcquiredNode);
  521. //
  522. // Stop recursion if it gets to deep.
  523. //
  524. if (Degree > ViDeadlockMaximumDegree) {
  525. return FALSE;
  526. }
  527. FoundDeadlock = FALSE;
  528. CurrentAcquiredNode = AcquiredNode;
  529. //
  530. // Loop over all nodes containing same resource as all of the Acquired nodes
  531. // parameter. For each such node we will traverse the Parent chain
  532. // to check if ResourceAddress appears at some point. If it does
  533. // we found a two way deadlock (caused by two threads).
  534. //
  535. while(CurrentAcquiredNode != NULL) {
  536. //
  537. // Check for a self cycle.
  538. //
  539. if (ViDeadlockGetNodeResourceAddress(CurrentAcquiredNode) == ResourceAddress) {
  540. ViDeadlockAddParticipant(ResourceAddress, NULL, CurrentAcquiredNode, Degree);
  541. FoundDeadlock = TRUE;
  542. goto Exit;
  543. }
  544. CurrentResource = CurrentAcquiredNode->Root;
  545. Current = CurrentResource->ResourceList.Flink;
  546. while (Current != &(CurrentResource->ResourceList)) {
  547. CurrentNode = CONTAINING_RECORD (Current,
  548. VI_DEADLOCK_NODE,
  549. ResourceList);
  550. CurrentParent = CurrentNode->Parent;
  551. //
  552. // Traverse the parent chain looking for two-way deadlocks.
  553. //
  554. while (CurrentParent != NULL) {
  555. if (ViDeadlockGetNodeResourceAddress(CurrentParent) == ResourceAddress) {
  556. FoundDeadlock = TRUE;
  557. if (! Degree) {
  558. ViDeadlockAddParticipant(ResourceAddress,
  559. NULL,
  560. CurrentAcquiredNode,
  561. Degree);
  562. ViDeadlockAddParticipant(ResourceAddress,
  563. CurrentNode,
  564. CurrentParent,
  565. Degree);
  566. } else {
  567. ViDeadlockAddParticipant(ResourceAddress,
  568. AcquiredNode,
  569. CurrentParent,
  570. Degree);
  571. }
  572. goto Exit;
  573. }
  574. CurrentParent = CurrentParent->Parent;
  575. }
  576. //
  577. // Move on to the next node (AcquiredNode->Root type of nodes).
  578. //
  579. Current = Current->Flink;
  580. }
  581. CurrentAcquiredNode = CurrentAcquiredNode->Parent;
  582. }
  583. CurrentAcquiredNode = AcquiredNode;
  584. while(CurrentAcquiredNode != NULL) {
  585. //
  586. // In order to find multiway deadlocks we traverse the Parent chain
  587. // a second time and enter into recursion. This way we can detect
  588. // cycles in the graph that are caused by multiple threads (up to Degree).
  589. //
  590. CurrentResource = CurrentAcquiredNode->Root;
  591. Current = CurrentResource->ResourceList.Flink;
  592. while (Current != &(CurrentResource->ResourceList)) {
  593. CurrentNode = CONTAINING_RECORD (Current,
  594. VI_DEADLOCK_NODE,
  595. ResourceList);
  596. //
  597. // Loop again over parents but this time get into recursion.
  598. // We could have done this in the loop above but we want to first search
  599. // completely the existing graph (tree actually) and only after that
  600. // traverse it recursively.
  601. //
  602. CurrentParent = CurrentNode->Parent;
  603. while (CurrentParent != NULL) {
  604. FoundDeadlock = ViDeadlockAnalyze (ResourceAddress,
  605. CurrentParent,
  606. Degree + 1);
  607. if (FoundDeadlock) {
  608. if (! Degree) {
  609. ViDeadlockAddParticipant(ResourceAddress,
  610. CurrentNode,
  611. CurrentParent,
  612. Degree);
  613. ViDeadlockAddParticipant(ResourceAddress,
  614. NULL,
  615. CurrentAcquiredNode,
  616. Degree);
  617. } else {
  618. ViDeadlockAddParticipant(ResourceAddress,
  619. AcquiredNode,
  620. CurrentParent,
  621. Degree);
  622. }
  623. goto Exit;
  624. }
  625. CurrentParent = CurrentParent->Parent;
  626. }
  627. //
  628. // Move on to the next node (AcquiredNode->Root type of nodes).
  629. //
  630. Current = Current->Flink;
  631. }
  632. CurrentAcquiredNode = CurrentAcquiredNode->Parent;
  633. }
  634. Exit:
  635. if (FoundDeadlock && Degree == 0) {
  636. ViDeadlockReportIssue (VI_DEADLOCK_ISSUE_DEADLOCK_DETECTED,
  637. (ULONG_PTR)ResourceAddress,
  638. (ULONG_PTR)CurrentAcquiredNode,
  639. 0);
  640. }
  641. return FoundDeadlock;
  642. #
  643. }
  644. /////////////////////////////////////////////////////////////////////
  645. ///////////////////////////////////////////////// Resource management
  646. /////////////////////////////////////////////////////////////////////
  647. PVI_DEADLOCK_RESOURCE
  648. ViDeadlockSearchResource(
  649. IN PVOID ResourceAddress
  650. )
  651. /*++
  652. Routine Description:
  653. This routine finds the resource descriptor structure for a
  654. resource if one exists.
  655. Arguments:
  656. ResourceAddress: Address of the resource in question (as used by
  657. the kernel).
  658. Return Value:
  659. PVI_DEADLOCK_RESOURCE structure describing the resource, if available,
  660. or else NULL
  661. Note. The caller of the function should hold the database lock.
  662. --*/
  663. {
  664. PLIST_ENTRY ListHead;
  665. PLIST_ENTRY Current;
  666. PVI_DEADLOCK_RESOURCE Resource;
  667. ASSERT (ViDeadlockDatabaseOwner == KeGetCurrentThread ());
  668. ListHead = ViDeadlockDatabaseHash (ViDeadlockResourceDatabase, ResourceAddress);
  669. if (IsListEmpty(ListHead)) {
  670. return NULL;
  671. }
  672. Current = ListHead->Flink;
  673. while (Current != ListHead) {
  674. Resource = CONTAINING_RECORD(Current,
  675. VI_DEADLOCK_RESOURCE,
  676. HashChainList);
  677. if (Resource->ResourceAddress == ResourceAddress) {
  678. return Resource;
  679. }
  680. Current = Current->Flink;
  681. }
  682. return NULL;
  683. }
  684. BOOLEAN
  685. ViDeadlockAddResource(
  686. IN PVOID Resource,
  687. IN VI_DEADLOCK_RESOURCE_TYPE Type
  688. )
  689. /*++
  690. Routine Description:
  691. This routine adds an entry for a new resource to our deadlock detection
  692. database.
  693. Arguments:
  694. Resource: Address of the resource in question as used by the kernel.
  695. Type: Type of the resource.
  696. Return Value:
  697. True if we created and initialized a new RESOURCE structure.
  698. Note. The caller of the function should not hold the database
  699. lock.
  700. --*/
  701. {
  702. PLIST_ENTRY hashBin;
  703. PVI_DEADLOCK_RESOURCE resourceRoot;
  704. PVI_DEADLOCK_NODE resourceNode;
  705. KIRQL OldIrql;
  706. ULONG HashValue;
  707. //
  708. // If we aren't initialized or package is not enabled
  709. // we return immediately.
  710. //
  711. if (! (ViDeadlockDetectionInitialized && ViDeadlockDetectionEnabled)) {
  712. return FALSE;
  713. }
  714. //
  715. // Check if this resource was initialized before.
  716. // This would be a bug.
  717. //
  718. LOCK_DEADLOCK_DATABASE( &OldIrql );
  719. resourceRoot = ViDeadlockSearchResource (Resource);
  720. if (resourceRoot) {
  721. ViDeadlockReportIssue (VI_DEADLOCK_ISSUE_MULTIPLE_INITIALIZATION,
  722. (ULONG_PTR)Resource,
  723. (ULONG_PTR)resourceRoot,
  724. 0);
  725. UNLOCK_DEADLOCK_DATABASE( OldIrql);
  726. return TRUE;
  727. }
  728. UNLOCK_DEADLOCK_DATABASE( OldIrql);
  729. //
  730. // Allocate the memory for the root of the new resource's tree.
  731. //
  732. resourceRoot = ViDeadlockAllocate (sizeof(VI_DEADLOCK_RESOURCE));
  733. if (NULL == resourceRoot) {
  734. return FALSE;
  735. }
  736. RtlZeroMemory(resourceRoot, sizeof(VI_DEADLOCK_RESOURCE));
  737. //
  738. // Fill information about resource.
  739. //
  740. resourceRoot->Type = Type;
  741. resourceRoot->ResourceAddress = Resource;
  742. InitializeListHead (&resourceRoot->ResourceList);
  743. resourceRoot->NodeCount = 0;
  744. //
  745. // Capture the stack trace of the guy that creates the resource first.
  746. // This should happen when resource gets initialized.
  747. //
  748. RtlCaptureStackBackTrace (0, // silviuc: how many frames to skip?
  749. VI_MAX_STACK_DEPTH,
  750. resourceRoot->InitializeStackTrace,
  751. &HashValue);
  752. //
  753. // Figure out which hash bin this resource corresponds to.
  754. //
  755. hashBin = ViDeadlockDatabaseHash(ViDeadlockResourceDatabase, Resource);
  756. //
  757. // Now add to the list corresponding to the current hash bin
  758. //
  759. LOCK_DEADLOCK_DATABASE( &OldIrql );
  760. InsertHeadList(hashBin,
  761. &(resourceRoot->HashChainList));
  762. ViDeadlockNumberOfResources += 1;
  763. UNLOCK_DEADLOCK_DATABASE( OldIrql);
  764. return TRUE;
  765. }
  766. BOOLEAN
  767. ViDeadlockQueryAcquireResource(
  768. IN PVOID Resource,
  769. IN VI_DEADLOCK_RESOURCE_TYPE Type
  770. )
  771. /*++
  772. Routine Description:
  773. This routine makes sure that it is ok to acquire the resource without
  774. causing a deadlock. .
  775. Arguments:
  776. Resource: Address of the resource in question as used by kernel.
  777. Type: Type of the resource.
  778. Return Value:
  779. None.
  780. --*/
  781. {
  782. PKTHREAD CurrentThread;
  783. PVI_DEADLOCK_THREAD ThreadEntry;
  784. KIRQL OldIrql;
  785. PVI_DEADLOCK_NODE CurrentNode;
  786. PVI_DEADLOCK_RESOURCE ResourceRoot;
  787. PLIST_ENTRY Current;
  788. BOOLEAN FoundDeadlock;
  789. ULONG DeadlockFlags;
  790. //
  791. // If we are not initialized or package is not enabled
  792. // we return immediately.
  793. //
  794. if (! (ViDeadlockDetectionInitialized && ViDeadlockDetectionEnabled)) {
  795. return FALSE;
  796. }
  797. FoundDeadlock = FALSE;
  798. CurrentThread = KeGetCurrentThread();
  799. DeadlockFlags = ViDeadlockResourceTypeInfo[Type];
  800. LOCK_DEADLOCK_DATABASE( &OldIrql );
  801. //
  802. // Look for this thread in our thread list.
  803. // Add a new thread if it is not in the list.
  804. //
  805. ThreadEntry = ViDeadlockSearchThread (CurrentThread);
  806. if (ThreadEntry == NULL) {
  807. //
  808. // Threads without allocations can't cause deadlocks
  809. //
  810. goto Exit;
  811. }
  812. //
  813. // Check if this resource is already in our database
  814. //
  815. ResourceRoot = ViDeadlockSearchResource (Resource);
  816. //
  817. // Resources that we haven't seen before can't cause deadlocks
  818. //
  819. if (ResourceRoot == NULL) {
  820. goto Exit;
  821. }
  822. ASSERT (ResourceRoot);
  823. ASSERT (ThreadEntry);
  824. //
  825. // Check if thread holds any resources.
  826. // Threads that don't have any resources
  827. // yet can't cause deadlocks
  828. //
  829. if (ThreadEntry->CurrentNode == NULL) {
  830. goto Exit;
  831. }
  832. //
  833. // If we get here, the current thread had already acquired resources.
  834. //
  835. //
  836. // Find whether the link already exists. We are looking for a direct
  837. // link between ThreadEntry->CurrentNode and Resource (parameter).
  838. //
  839. Current = ThreadEntry->CurrentNode->ChildrenList.Flink;
  840. while (Current != &(ThreadEntry->CurrentNode->ChildrenList)) {
  841. CurrentNode = CONTAINING_RECORD (Current,
  842. VI_DEADLOCK_NODE,
  843. SiblingsList);
  844. if (ViDeadlockGetNodeResourceAddress(CurrentNode) == Resource) {
  845. //
  846. // We have found a link.
  847. // A link that already exists doesn't have to be
  848. // checked for a deadlock because it would have
  849. // been caught when the link was created...
  850. // so we can just update the pointers and exit.
  851. //
  852. ThreadEntry->CurrentNode = CurrentNode;
  853. goto Exit;
  854. }
  855. Current = Current->Flink;
  856. }
  857. //
  858. // Now we know that we're in it for the long haul ..
  859. // doesn't cause a deadlock
  860. //
  861. CurrentNode = NULL;
  862. //
  863. // We will analyze deadlock if the resource just about to be acquired
  864. // was acquired before and there are nodes in the graph for the
  865. // resource.
  866. //
  867. if (ViDeadlockAnalyze(Resource, ThreadEntry->CurrentNode, 0)) {
  868. //
  869. // Go back to ground 0 with this thread
  870. //
  871. ThreadEntry->CurrentNode = NULL;
  872. FoundDeadlock = TRUE;
  873. }
  874. //
  875. // Exit point.
  876. //
  877. Exit:
  878. //
  879. // Release deadlock database and return.
  880. //
  881. UNLOCK_DEADLOCK_DATABASE( OldIrql );
  882. return FoundDeadlock;
  883. }
  884. BOOLEAN
  885. ViDeadlockSimilarNode (
  886. IN PVOID Resource,
  887. IN PKTHREAD Thread,
  888. IN PVOID * Trace,
  889. IN PVI_DEADLOCK_NODE Node
  890. )
  891. {
  892. SIZE_T Index;
  893. if (Resource == ViDeadlockGetNodeResourceAddress(Node) &&
  894. Thread == Node->Thread) {
  895. Index = RtlCompareMemory (Trace, Node->StackTrace, sizeof (Node->StackTrace));
  896. if (Index == sizeof (Node->StackTrace)) {
  897. return TRUE;
  898. }
  899. }
  900. return FALSE;
  901. }
  902. VOID
  903. ViDeadlockAcquireResource(
  904. IN PVOID Resource,
  905. IN VI_DEADLOCK_RESOURCE_TYPE Type
  906. )
  907. /*++
  908. Routine Description:
  909. This routine makes sure that it is ok to acquire the resource without
  910. causing a deadlock. It will also update the resource graph with the new
  911. resource acquisition.
  912. Arguments:
  913. Resource: Address of the resource in question as used by kernel.
  914. Type: Type of the resource.
  915. Return Value:
  916. None.
  917. --*/
  918. {
  919. PKTHREAD CurrentThread;
  920. PVI_DEADLOCK_THREAD ThreadEntry;
  921. KIRQL OldIrql;
  922. PVI_DEADLOCK_NODE CurrentNode;
  923. PVI_DEADLOCK_RESOURCE ResourceRoot;
  924. PLIST_ENTRY Current;
  925. ULONG HashValue;
  926. ULONG DeadlockFlags;
  927. BOOLEAN CreatingRootNode = FALSE;
  928. PVOID Trace [VI_MAX_STACK_DEPTH];
  929. //
  930. // If we are not initialized or package is not enabled
  931. // we return immediately.
  932. //
  933. if (! (ViDeadlockDetectionInitialized && ViDeadlockDetectionEnabled)) {
  934. return;
  935. }
  936. CurrentThread = KeGetCurrentThread();
  937. DeadlockFlags = ViDeadlockResourceTypeInfo[Type];
  938. //
  939. // Capture stack trace. We will need it to figure out
  940. // if we've been in this state before. We will skip two frames
  941. // for ViDeadlockAcquireResource and the verifier thunk that
  942. // calls it.
  943. //
  944. RtlZeroMemory (Trace, sizeof Trace);
  945. RtlCaptureStackBackTrace (
  946. 2,
  947. VI_MAX_STACK_DEPTH,
  948. Trace,
  949. &HashValue);
  950. //
  951. // Lock the deadlock database.
  952. //
  953. LOCK_DEADLOCK_DATABASE( &OldIrql );
  954. //
  955. // Look for this thread in our thread list.
  956. // Add a new thread if it is not in the list.
  957. //
  958. ThreadEntry = ViDeadlockSearchThread (CurrentThread);
  959. if (ThreadEntry == NULL) {
  960. //
  961. // Note that ViDeadlockAddThread will drop the lock
  962. // while allocating memory and then reacquire it.
  963. //
  964. ThreadEntry = ViDeadlockAddThread (CurrentThread);
  965. if (ThreadEntry == NULL) {
  966. //
  967. // If we cannot allocate a new thread entry then
  968. // no deadlock detection will happen.
  969. //
  970. UNLOCK_DEADLOCK_DATABASE( OldIrql );
  971. return;
  972. }
  973. }
  974. //
  975. // Check if this resource is already in our database
  976. //
  977. ResourceRoot = ViDeadlockSearchResource (Resource);
  978. if (ResourceRoot == NULL) {
  979. //
  980. // Could not find the resource descriptor.
  981. //
  982. if ((DeadlockFlags & VI_DEADLOCK_FLAG_NO_INITIALIZATION_FUNCTION)) {
  983. //
  984. // Certain resource types have no initialization function ..
  985. // in which case we'll get an 'acquire' without an 'add'
  986. // first -- this is entirely OK
  987. //
  988. UNLOCK_DEADLOCK_DATABASE( OldIrql );
  989. if (FALSE == ViDeadlockAddResource(Resource, Type) ) {
  990. return;
  991. }
  992. LOCK_DEADLOCK_DATABASE( &OldIrql );
  993. //
  994. // Note that even though we dropped the lock, we don't have
  995. // to reobtain the thread entry pointer -- since thread
  996. // entry for the current thread can't have gone away.
  997. //
  998. ResourceRoot = ViDeadlockSearchResource (Resource);
  999. } else {
  1000. //
  1001. // This resource type does have an initialization function --
  1002. // and it wasn't called. This is bad.
  1003. //
  1004. ViDeadlockReportIssue (VI_DEADLOCK_ISSUE_UNINITIALIZED_RESOURCE,
  1005. (ULONG_PTR)Resource,
  1006. 0,
  1007. 0);
  1008. //
  1009. // ISSUE (silviuc) Difficult to recover from this failure.
  1010. // We will complain during release that resource was not acquired.
  1011. //
  1012. ThreadEntry->CurrentNode = NULL;
  1013. UNLOCK_DEADLOCK_DATABASE( OldIrql );
  1014. return;
  1015. }
  1016. }
  1017. //
  1018. // At this point we have a THREAD and a RESOURCE to play with.
  1019. // In addition we are just about to acquire the resource which means
  1020. // there should not be another thread owning it.
  1021. //
  1022. ASSERT (ResourceRoot);
  1023. ASSERT (ThreadEntry);
  1024. ASSERT (NULL == ResourceRoot->ThreadOwner);
  1025. //
  1026. // Check if thread holds any resources. If it does we will have to determine
  1027. // at that local point in the dependency graph if we need to create a
  1028. // new node. If this is the first resource acquired by the thread we need
  1029. // to create a new root node or reuse one created in the past.
  1030. //
  1031. // A node created in the past will match the current situation if the same
  1032. // thread acquires, the same resource is acquired and the stack traces match.
  1033. // A node represents a triad (Thread, Resource, StackTrace).
  1034. //
  1035. if (ThreadEntry->CurrentNode != NULL) {
  1036. //
  1037. // If we get here, the current thread had already acquired resources.
  1038. // Must now do three things ...
  1039. //
  1040. // 1. if link already exists, update pointers and exit
  1041. // 2. otherwise create a new node
  1042. // 3. check for deadlocks
  1043. //
  1044. //
  1045. // Find whether the link already exists. We are looking for a direct
  1046. // link between ThreadEntry->CurrentNode and Resource (parameter).
  1047. //
  1048. Current = ThreadEntry->CurrentNode->ChildrenList.Flink;
  1049. while (Current != &(ThreadEntry->CurrentNode->ChildrenList)) {
  1050. CurrentNode = CONTAINING_RECORD (Current,
  1051. VI_DEADLOCK_NODE,
  1052. SiblingsList);
  1053. Current = Current->Flink;
  1054. if (ViDeadlockSimilarNode (Resource, CurrentThread, Trace, CurrentNode)) {
  1055. //
  1056. // We have found a link.
  1057. // A link that already exists doesn't have to be
  1058. // checked for a deadlock because it would have
  1059. // been caught when the link was created...
  1060. // so we can just update the pointers to reflect the new
  1061. // resource acquired and exit.
  1062. //
  1063. ThreadEntry->CurrentNode = CurrentNode;
  1064. goto Exit;
  1065. }
  1066. }
  1067. //
  1068. // Now we know that we're in it for the long haul .. we must create a new
  1069. // link and make sure that it doesn't cause a deadlock
  1070. //
  1071. CurrentNode = NULL;
  1072. //
  1073. // We will analyze deadlock if the resource just about to be acquired
  1074. // was acquired before and there are nodes in the graph for the
  1075. // resource.
  1076. //
  1077. if (ResourceRoot->NodeCount > 0) {
  1078. if (ViDeadlockAnalyze(Resource, ThreadEntry->CurrentNode, 0)) {
  1079. //
  1080. // Go back to ground 0 with this thread if a deadlock has been
  1081. // detected.
  1082. //
  1083. ThreadEntry->CurrentNode = NULL;
  1084. ResourceRoot->ThreadOwner = NULL;
  1085. goto Exit;
  1086. }
  1087. }
  1088. }
  1089. else {
  1090. //
  1091. // Thread does not have any resources acquired. We have to figure out
  1092. // if this is a scenario we have encountered in the past by looking
  1093. // at all nodes (that are roots) for the resource to be acquired.
  1094. //
  1095. PLIST_ENTRY Current;
  1096. PVI_DEADLOCK_NODE Node;
  1097. BOOLEAN FoundNode = FALSE;
  1098. Current = ResourceRoot->ResourceList.Flink;
  1099. while (Current != &(ResourceRoot->ResourceList)) {
  1100. Node = CONTAINING_RECORD (Current,
  1101. VI_DEADLOCK_NODE,
  1102. ResourceList);
  1103. Current = Node->ResourceList.Flink;
  1104. if (Node->Parent == NULL) {
  1105. if (ViDeadlockSimilarNode (Resource, CurrentThread, Trace, Node)) {
  1106. FoundNode = TRUE;
  1107. break;
  1108. }
  1109. }
  1110. }
  1111. if (FoundNode) {
  1112. ThreadEntry->CurrentNode = Node;
  1113. goto Exit;
  1114. }
  1115. else {
  1116. CreatingRootNode = TRUE;
  1117. }
  1118. }
  1119. //
  1120. // At this moment we know for sure the new link will not cause
  1121. // a deadlock. We will create the new resource node.
  1122. //
  1123. CurrentNode = ViDeadlockAllocate (sizeof (VI_DEADLOCK_NODE));
  1124. if (CurrentNode != NULL) {
  1125. //
  1126. // Initialize the new resource node
  1127. //
  1128. RtlZeroMemory (CurrentNode, sizeof *CurrentNode);
  1129. CurrentNode->Parent = ThreadEntry->CurrentNode;
  1130. CurrentNode->Root = ResourceRoot;
  1131. InitializeListHead (&(CurrentNode->ChildrenList));
  1132. //
  1133. // Add to the children list of the parent.
  1134. //
  1135. if (! CreatingRootNode) {
  1136. InsertHeadList(&(ThreadEntry->CurrentNode->ChildrenList),
  1137. &(CurrentNode->SiblingsList));
  1138. }
  1139. //
  1140. // Register the new resource node in the list of nodes maintained
  1141. // for this resource.
  1142. //
  1143. InsertHeadList(&(ResourceRoot->ResourceList),
  1144. &(CurrentNode->ResourceList));
  1145. ResourceRoot->NodeCount += 1;
  1146. //
  1147. // Update NodeCount for all NODEs all the way up to the
  1148. // root of the tree.
  1149. //
  1150. {
  1151. PVI_DEADLOCK_NODE Parent;
  1152. Parent = CurrentNode->Parent;
  1153. while (Parent != NULL) {
  1154. Parent->NodeCount += 1;
  1155. Parent = Parent->Parent;
  1156. }
  1157. }
  1158. }
  1159. //
  1160. // Update current resource held by thread.
  1161. //
  1162. // NOTE -- Do this even if the allocation FAILS --
  1163. // Since when the resource is released, we won't
  1164. // recognize it (since the node could not be allocated)
  1165. // and we'll bugcheck.
  1166. //
  1167. ThreadEntry->CurrentNode = CurrentNode;
  1168. //
  1169. // Exit point.
  1170. //
  1171. Exit:
  1172. //
  1173. // Add information we use to identify the culprit should
  1174. // a deadlock occur
  1175. //
  1176. if (CurrentNode) {
  1177. CurrentNode->Thread = CurrentThread;
  1178. ResourceRoot->ThreadOwner = CurrentThread;
  1179. RtlCopyMemory (CurrentNode->StackTrace, Trace, sizeof Trace);
  1180. }
  1181. //
  1182. // Release deadlock database and return.
  1183. //
  1184. UNLOCK_DEADLOCK_DATABASE( OldIrql );
  1185. return;
  1186. }
  1187. VOID
  1188. ViDeadlockReleaseResource(
  1189. IN PVOID Resource
  1190. )
  1191. /*++
  1192. Routine Description:
  1193. This routine does the maintenance necessary to release resources from our
  1194. deadlock detection database.
  1195. Arguments:
  1196. Resource: Address of the resource in question.
  1197. Return Value:
  1198. None.
  1199. --*/
  1200. {
  1201. PKTHREAD CurrentThread;
  1202. PVI_DEADLOCK_THREAD ThreadEntry;
  1203. KIRQL OldIrql;
  1204. PVI_DEADLOCK_NODE CurrentNode;
  1205. PVI_DEADLOCK_RESOURCE ResourceRoot;
  1206. ASSERT (ViDeadlockDatabaseOwner != KeGetCurrentThread());
  1207. //
  1208. // If we aren't initialized or package is not enabled
  1209. // we return immediately.
  1210. //
  1211. if (! (ViDeadlockDetectionInitialized && ViDeadlockDetectionEnabled)) {
  1212. return;
  1213. }
  1214. CurrentThread = KeGetCurrentThread();
  1215. LOCK_DEADLOCK_DATABASE( &OldIrql );
  1216. ResourceRoot = ViDeadlockSearchResource (Resource);
  1217. if (ResourceRoot == NULL) {
  1218. //
  1219. // This is probably bad but we con't complain since
  1220. // we may have faild an allocation -- we don't want
  1221. // to accuse somebody of foul play just because
  1222. // our allocation function failed
  1223. //
  1224. // ISSUE (silviuc): should complain if no allocation ever failed.
  1225. //
  1226. UNLOCK_DEADLOCK_DATABASE( OldIrql );
  1227. return;
  1228. }
  1229. if (ResourceRoot->ThreadOwner == NULL) {
  1230. //
  1231. // Most likely someone is releasing a resource that
  1232. // was never acquired. However the other possibility
  1233. // is that we have failed an allocation. So we can't
  1234. // complain -- but we can't really do anything either
  1235. //
  1236. // ISSUE (silviuc): should complain if no allocation ever failed.
  1237. //
  1238. UNLOCK_DEADLOCK_DATABASE( OldIrql );
  1239. return;
  1240. }
  1241. //
  1242. // Look for this thread in our thread list,
  1243. // Note we are looking actually for the thread
  1244. // that acquired the resource -- not the current one
  1245. // It should, in fact be the current one, but if
  1246. // the resource is being released in a different thread
  1247. // from the one it was acquired in, we need the original.
  1248. //
  1249. ThreadEntry = ViDeadlockSearchThread (ResourceRoot->ThreadOwner);
  1250. if (NULL == ThreadEntry) {
  1251. //
  1252. // This can happen when we recover from an unexpected release --
  1253. // there is still a therad owner for the resource but we nuked the
  1254. // thread entry.
  1255. // Indicate that we no longer hold this resource.
  1256. //
  1257. // ISSUE (silviuc): So, we do not need to complain here ?
  1258. //
  1259. ResourceRoot->ThreadOwner = NULL;
  1260. UNLOCK_DEADLOCK_DATABASE( OldIrql );
  1261. return;
  1262. }
  1263. if (ResourceRoot->ThreadOwner != CurrentThread) {
  1264. //
  1265. // Someone acquired a resource but it was
  1266. // released in another thread. This is bad
  1267. // design.
  1268. //
  1269. DbgPrint("Thread %p acquired resource %p but thread %p released it\n",
  1270. ThreadEntry->Thread, Resource, CurrentThread );
  1271. ViDeadlockReportIssue (VI_DEADLOCK_ISSUE_UNEXPECTED_THREAD,
  1272. (ULONG_PTR)ResourceRoot,
  1273. (ULONG_PTR)ThreadEntry,
  1274. (ULONG_PTR)ViDeadlockSearchThread(CurrentThread)
  1275. );
  1276. //
  1277. // If we don't want this to be fatal, in order to
  1278. // continue we must pretend that the current
  1279. // thread is the resource's owner.
  1280. //
  1281. CurrentThread = ResourceRoot->ThreadOwner;
  1282. }
  1283. //
  1284. // Wipe out the resource owner since resource will be released.
  1285. //
  1286. ResourceRoot->ThreadOwner = NULL;
  1287. //
  1288. // Check the case where the thread does not appear to hold a resource.
  1289. // ISSUE (silviuc): We kind of cluttered the code to make it restartable
  1290. // after an error. It might be better to clean it up even if not restartable.
  1291. //
  1292. if(NULL == ThreadEntry->CurrentNode) {
  1293. //
  1294. // This will happen when we recover from a deadlock
  1295. //
  1296. UNLOCK_DEADLOCK_DATABASE( OldIrql );
  1297. return;
  1298. }
  1299. //
  1300. // All nodes must have a root -- just make sure because we're deref'ing
  1301. // it in a minute
  1302. //
  1303. ASSERT (ThreadEntry->CurrentNode->Root);
  1304. //
  1305. // Found the thread list entry
  1306. //
  1307. if (ViDeadlockGetNodeResourceAddress(ThreadEntry->CurrentNode) != Resource) {
  1308. //
  1309. // Getting here means that soembody acquires a then b then tries
  1310. // to release b before a. This is bad.
  1311. //
  1312. //
  1313. // ISSUE (jtigani): -- flesh out reporting -- so we can prove that this
  1314. // actually happenned.
  1315. //
  1316. DbgPrint("ERROR: Must release resources in reverse-order\n");
  1317. DbgPrint("Resource %p acquired before resource %p -- \n"
  1318. "Current thread is trying to release it first\n",
  1319. Resource,
  1320. ViDeadlockGetNodeResourceAddress(ThreadEntry->CurrentNode));
  1321. ViDeadlockReportIssue (VI_DEADLOCK_ISSUE_UNEXPECTED_RELEASE,
  1322. (ULONG_PTR)Resource,
  1323. (ULONG_PTR)ThreadEntry->CurrentNode,
  1324. (ULONG_PTR)CurrentThread);
  1325. //
  1326. // The thread state is hosed.
  1327. // Try to keep going.
  1328. //
  1329. ThreadEntry->CurrentNode = NULL;
  1330. } else {
  1331. //
  1332. // Indicate that we have released the curent node
  1333. //
  1334. ThreadEntry->CurrentNode = ThreadEntry->CurrentNode->Parent;
  1335. }
  1336. //
  1337. // If thread does not hold resources anymore we will destroy the
  1338. // thread information.
  1339. //
  1340. if (ThreadEntry->CurrentNode == NULL) {
  1341. ViDeadlockDeleteThread (ThreadEntry);
  1342. }
  1343. UNLOCK_DEADLOCK_DATABASE(OldIrql);
  1344. }
  1345. /////////////////////////////////////////////////////////////////////
  1346. /////////////////////////////////////////////////// Thread management
  1347. /////////////////////////////////////////////////////////////////////
  1348. PVI_DEADLOCK_THREAD
  1349. ViDeadlockSearchThread (
  1350. PKTHREAD Thread
  1351. )
  1352. /*++
  1353. Routine Description:
  1354. This routine searches for a thread in the thread database.
  1355. The function assumes the deadlock database lock is held.
  1356. Arguments:
  1357. Thread - thread address
  1358. Return Value:
  1359. Address of VI_DEADLOCK_THREAD structure if thread was found.
  1360. Null otherwise.
  1361. --*/
  1362. {
  1363. PLIST_ENTRY Current;
  1364. PVI_DEADLOCK_THREAD ThreadInfo;
  1365. PLIST_ENTRY HashBin;
  1366. ASSERT (ViDeadlockDatabaseOwner == KeGetCurrentThread ());
  1367. ThreadInfo = NULL;
  1368. HashBin = ViDeadlockDatabaseHash(ViDeadlockThreadDatabase, Thread);
  1369. if (IsListEmpty(HashBin)) {
  1370. return NULL;
  1371. }
  1372. Current = HashBin->Flink;
  1373. while (Current != HashBin) {
  1374. ThreadInfo = CONTAINING_RECORD (Current,
  1375. VI_DEADLOCK_THREAD,
  1376. ListEntry);
  1377. if (ThreadInfo->Thread == Thread) {
  1378. return ThreadInfo;
  1379. }
  1380. Current = Current->Flink;
  1381. }
  1382. return NULL;
  1383. }
  1384. PVI_DEADLOCK_THREAD
  1385. ViDeadlockAddThread (
  1386. PKTHREAD Thread
  1387. )
  1388. /*++
  1389. Routine Description:
  1390. This routine adds a new thread to the thread database.
  1391. The function assumes the deadlock database lock is held. It will
  1392. drop the lock while allocating memory for the thread structure and
  1393. then reacquire the lock. Special attention in the caller of this
  1394. function (ViDeadlockAcquireResource) because any internal pointer
  1395. should be reobtained since the lock was dropped.
  1396. Arguments:
  1397. Thread - thread address
  1398. Return Value:
  1399. Address of the VI_DEADLOCK_THREAD resource just added.
  1400. Null if allocation failed.
  1401. --*/
  1402. {
  1403. KIRQL OldIrql;
  1404. PVI_DEADLOCK_THREAD ThreadInfo;
  1405. PLIST_ENTRY HashBin;
  1406. ASSERT (ViDeadlockDatabaseOwner == KeGetCurrentThread ());
  1407. //
  1408. // It is safe to play with OldIrql like below because this function
  1409. // is called from ViDeadlockAcquireResource with irql raised at DPC
  1410. // level.
  1411. //
  1412. OldIrql = DISPATCH_LEVEL;
  1413. UNLOCK_DEADLOCK_DATABASE (OldIrql);
  1414. ThreadInfo = ViDeadlockAllocate (sizeof(VI_DEADLOCK_THREAD));
  1415. LOCK_DEADLOCK_DATABASE (&OldIrql);
  1416. if (ThreadInfo == NULL) {
  1417. return NULL;
  1418. }
  1419. RtlZeroMemory (ThreadInfo, sizeof *ThreadInfo);
  1420. ThreadInfo->Thread = Thread;
  1421. HashBin = ViDeadlockDatabaseHash(ViDeadlockThreadDatabase, Thread);
  1422. InsertHeadList (HashBin,
  1423. &ThreadInfo->ListEntry);
  1424. ViDeadlockNumberOfThreads += 1;
  1425. return ThreadInfo;
  1426. }
  1427. VOID
  1428. ViDeadlockDeleteThread (
  1429. PVI_DEADLOCK_THREAD Thread
  1430. )
  1431. /*++
  1432. Routine Description:
  1433. This routine deletes a thread.
  1434. Arguments:
  1435. Thread - thread address
  1436. Return Value:
  1437. None.
  1438. --*/
  1439. {
  1440. KIRQL OldIrql;
  1441. VI_DEADLOCK_THREAD ThreadInfo;
  1442. PLIST_ENTRY Current;
  1443. BOOLEAN Result;
  1444. PKTHREAD CurrentThread;
  1445. PLIST_ENTRY HashBin;
  1446. CurrentThread = KeGetCurrentThread ();
  1447. ASSERT (ViDeadlockDatabaseOwner == CurrentThread);
  1448. ASSERT (Thread && Thread->Thread == CurrentThread);
  1449. ASSERT (Thread->CurrentNode == NULL);
  1450. RemoveEntryList (&(Thread->ListEntry));
  1451. ViDeadlockNumberOfThreads -= 1;
  1452. }
  1453. /////////////////////////////////////////////////////////////////////
  1454. /////////////////////////////////////////////////////// Allocate/Free
  1455. /////////////////////////////////////////////////////////////////////
  1456. PVOID
  1457. ViDeadlockAllocate (
  1458. SIZE_T Size
  1459. )
  1460. {
  1461. return ExAllocatePoolWithTag(NonPagedPool, Size, VI_DEADLOCK_TAG);
  1462. }
  1463. VOID
  1464. ViDeadlockFree (
  1465. PVOID Object
  1466. )
  1467. {
  1468. ExFreePool (Object);
  1469. }
  1470. /////////////////////////////////////////////////////////////////////
  1471. /////////////////////////////////////// Error reporting and debugging
  1472. /////////////////////////////////////////////////////////////////////
  1473. ULONG ViDeadlockDebug = 0x01;
  1474. VOID
  1475. ViDeadlockReportIssue (
  1476. ULONG_PTR Param1,
  1477. ULONG_PTR Param2,
  1478. ULONG_PTR Param3,
  1479. ULONG_PTR Param4
  1480. )
  1481. {
  1482. if ((ViDeadlockDebug & 0x01)) {
  1483. DbgPrint ("Verifier: deadlock: stop: %u %p %p %p %p \n",
  1484. DRIVER_VERIFIER_DETECTED_VIOLATION,
  1485. Param1,
  1486. Param2,
  1487. Param3,
  1488. Param4);
  1489. DbgBreakPoint ();
  1490. }
  1491. else {
  1492. KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
  1493. Param1,
  1494. Param2,
  1495. Param3,
  1496. Param4);
  1497. }
  1498. ViDeadlockNumberParticipants = FALSE;
  1499. }
  1500. VOID
  1501. ViDeadlockAddParticipant(
  1502. PVOID ResourceAddress,
  1503. PVI_DEADLOCK_NODE FirstParticipant, OPTIONAL
  1504. PVI_DEADLOCK_NODE SecondParticipant,
  1505. ULONG Degree
  1506. )
  1507. {
  1508. ULONG Participants;
  1509. if (0 == ViDeadlockNumberParticipants) {
  1510. Participants = Degree + 2;
  1511. ViDeadlockParticipation = ViDeadlockAllocate(
  1512. sizeof(VI_DEADLOCK_PARTICIPANT) * (2 * Participants + 1)
  1513. );
  1514. RtlZeroMemory(
  1515. ViDeadlockParticipation,
  1516. sizeof(VI_DEADLOCK_PARTICIPANT) * (2 * Participants + 1)
  1517. );
  1518. DbgPrint("|**********************************************************************\n");
  1519. DbgPrint("|** \n");
  1520. DbgPrint("|** Deadlock detected trying to acquire synchronization object at \n");
  1521. DbgPrint("|** address %p (%d-way deadlock)\n",
  1522. ResourceAddress,
  1523. Participants
  1524. );
  1525. if (ViDeadlockParticipation) {
  1526. DbgPrint("|** For more information, type \n");
  1527. DbgPrint("|** !deadlock\n");
  1528. DbgPrint("|** \n");
  1529. DbgPrint("|**********************************************************************\n");
  1530. } else {
  1531. DbgPrint("|** More information is not available because memory could\n");
  1532. DbgPrint("|** not be allocated to save the state information");
  1533. DbgPrint("|** \n");
  1534. DbgPrint("|**********************************************************************\n");
  1535. return;
  1536. }
  1537. }
  1538. #if 0
  1539. DbgPrint ("Verifier: deadlock: message: participant1 @ %p, participant2 @ %p, \n",
  1540. (FirstParticipant) ?
  1541. ViDeadlockGetNodeResourceAddress(FirstParticipant) :
  1542. ResourceAddress,
  1543. ViDeadlockGetNodeResourceAddress(SecondParticipant)
  1544. );
  1545. #endif
  1546. if (FirstParticipant) {
  1547. ViDeadlockParticipation[ViDeadlockNumberParticipants].NodeInformation =
  1548. TRUE;
  1549. ViDeadlockParticipation[ViDeadlockNumberParticipants].Participant =
  1550. FirstParticipant;
  1551. } else {
  1552. ViDeadlockParticipation[ViDeadlockNumberParticipants].NodeInformation =
  1553. FALSE;
  1554. ViDeadlockParticipation[ViDeadlockNumberParticipants].Participant =
  1555. ViDeadlockSearchResource(ResourceAddress);
  1556. }
  1557. ViDeadlockParticipation[ViDeadlockNumberParticipants+1].NodeInformation =
  1558. TRUE;
  1559. ViDeadlockParticipation[ViDeadlockNumberParticipants+1].Participant =
  1560. SecondParticipant;
  1561. ViDeadlockNumberParticipants +=2;
  1562. }
  1563. /////////////////////////////////////////////////////////////////////
  1564. //////////////////////////////////////////////////// Resource cleanup
  1565. /////////////////////////////////////////////////////////////////////
  1566. VOID
  1567. ViDeadlockDeleteMemoryRange(
  1568. IN PVOID Address,
  1569. IN SIZE_T Size
  1570. )
  1571. /*++
  1572. Routine Description:
  1573. This routine is called whenever some region of kernel virtual space
  1574. is no longer valid. We need this hook because most kernel resources
  1575. do not have a "delete resource" function and we need to figure out
  1576. what resources are not valid. Otherwise our dependency graph will
  1577. become populated by many zombie resources.
  1578. The important moments when the function gets called are ExFreePool
  1579. (and friends) and driver unloading. Dynamic and static memory are the
  1580. main regions where a resource gets allocated. There can be the possibility
  1581. of a resource allocated on the stack but this is a very weird scenario.
  1582. We might need to detect this and flag it as a potential issue.
  1583. If a resource or thread lives within the range specified then all graph
  1584. paths with nodes reachable from the resource or thread will be wiped out.
  1585. ISSUE (silviuc). This policy might lose some cases.
  1586. For instance if T1 acquires ABC,
  1587. then C is deleted and then T2 acquires BA this is a potential deadlock but
  1588. we will not catch it because when C gets deleted the whole ABC path will
  1589. disappear. Right now we have no solution for that. If we do not delete there
  1590. is no way we can decide when to wipe regions of the graph withouth creating
  1591. accumulations of zombies. One solution would be to keep nodes around and delete
  1592. them only if all nodes in the tree are supposed to be deleted.
  1593. Arguments:
  1594. Address - start address of the range to be deleted.
  1595. Size - size in bytes of the range to be deleted.
  1596. Return Value:
  1597. None.
  1598. --*/
  1599. {
  1600. ULONG Index;
  1601. PLIST_ENTRY Current;
  1602. PVI_DEADLOCK_RESOURCE Resource;
  1603. PVI_DEADLOCK_THREAD Thread;
  1604. KIRQL OldIrql;
  1605. LOCK_DEADLOCK_DATABASE(&OldIrql)
  1606. //
  1607. // Iterate all resources and delete the ones contained in the
  1608. // memory range.
  1609. //
  1610. for (Index = 0; Index < VI_DEADLOCK_HASH_BINS; Index += 1) {
  1611. Current = ViDeadlockResourceDatabase[Index].Flink;
  1612. while (Current != &(ViDeadlockResourceDatabase[Index])) {
  1613. Resource = CONTAINING_RECORD (Current,
  1614. VI_DEADLOCK_RESOURCE,
  1615. HashChainList);
  1616. Current = Current->Flink;
  1617. if ((PVOID)(Resource->ResourceAddress) >= Address &&
  1618. ((ULONG_PTR)(Resource->ResourceAddress) <= ((ULONG_PTR)Address + Size))) {
  1619. ViDeadlockDeleteResource (Resource);
  1620. }
  1621. }
  1622. }
  1623. //
  1624. // Iterate all threads and delete the ones contained in the
  1625. // memory range. Note that it is actually a bug if we find a
  1626. // thread to be deleted because this means thread is dying while
  1627. // holding some resources.
  1628. //
  1629. for (Index = 0; Index < VI_DEADLOCK_HASH_BINS; Index += 1) {
  1630. Current = ViDeadlockThreadDatabase[Index].Flink;
  1631. while (Current != &(ViDeadlockThreadDatabase[Index])) {
  1632. Thread = CONTAINING_RECORD (Current,
  1633. VI_DEADLOCK_THREAD,
  1634. ListEntry);
  1635. Current = Current->Flink;
  1636. if ((PVOID)(Thread->Thread) >= Address &&
  1637. ((ULONG_PTR)(Thread->Thread) <= ((ULONG_PTR)Address + Size))) {
  1638. ViDeadlockReportIssue (VI_DEADLOCK_ISSUE_THREAD_HOLDS_RESOURCES,
  1639. (ULONG_PTR)Thread,
  1640. (ULONG_PTR)(Thread->CurrentNode),
  1641. 0);
  1642. }
  1643. }
  1644. }
  1645. UNLOCK_DEADLOCK_DATABASE(OldIrql)
  1646. }
  1647. BOOLEAN
  1648. ViDeadlockIsNodeMarkedForDeletion (
  1649. PVI_DEADLOCK_NODE Node
  1650. )
  1651. {
  1652. ASSERT (Node);
  1653. if (Node->ResourceList.Flink == NULL) {
  1654. return TRUE;
  1655. }
  1656. else {
  1657. return FALSE;
  1658. }
  1659. }
  1660. PVOID
  1661. ViDeadlockGetNodeResourceAddress (
  1662. PVI_DEADLOCK_NODE Node
  1663. )
  1664. {
  1665. if (ViDeadlockIsNodeMarkedForDeletion(Node)) {
  1666. return Node->ResourceAddress;
  1667. }
  1668. else {
  1669. return Node->Root->ResourceAddress;
  1670. }
  1671. }
  1672. VOID
  1673. ViDeadlockDeleteResource (
  1674. PVI_DEADLOCK_RESOURCE Resource
  1675. )
  1676. {
  1677. PLIST_ENTRY Current;
  1678. PVI_DEADLOCK_NODE Node;
  1679. PVI_DEADLOCK_NODE Parent;
  1680. PVI_DEADLOCK_NODE Root;
  1681. ASSERT (Resource != NULL);
  1682. //
  1683. // Traverse the list of nodes representing acquisition of this resource
  1684. // and mark them all as deleted. If the NodeCount of the root becomes zero
  1685. // then we can delete the whole tree under the root.
  1686. //
  1687. Current = Resource->ResourceList.Flink;
  1688. while (Current != &(Resource->ResourceList)) {
  1689. Node = CONTAINING_RECORD (Current,
  1690. VI_DEADLOCK_NODE,
  1691. ResourceList);
  1692. Current = Current->Flink;
  1693. //
  1694. // Mark node as deleted
  1695. //
  1696. Node->ResourceList.Flink = NULL;
  1697. Node->ResourceList.Blink = NULL;
  1698. Node->ResourceAddress = Node->Root->ResourceAddress;
  1699. //
  1700. // Update NodeCount all the way to the root.
  1701. //
  1702. Parent = Node->Parent;
  1703. Root = Node;
  1704. while (Parent != NULL) {
  1705. Parent->NodeCount -= 1;
  1706. Root = Parent;
  1707. Parent = Parent->Parent;
  1708. }
  1709. //
  1710. // If all nodes in the tree have been marked as deleted
  1711. // it is time to delete and deallocate the whole tree.
  1712. //
  1713. if (Root->NodeCount == 0) {
  1714. ViDeadlockDeleteTree (Root);
  1715. }
  1716. }
  1717. //
  1718. // Delete the resource structure.
  1719. //
  1720. ViDeadlockFree (Node->Root);
  1721. }
  1722. VOID
  1723. ViDeadlockDeleteTree (
  1724. PVI_DEADLOCK_NODE Root
  1725. )
  1726. {
  1727. PLIST_ENTRY Current;
  1728. PVI_DEADLOCK_NODE Node;
  1729. Current = Root->ChildrenList.Flink;
  1730. while (Current != &(Root->ChildrenList)) {
  1731. Node = CONTAINING_RECORD (Current,
  1732. VI_DEADLOCK_NODE,
  1733. SiblingsList);
  1734. Current = Current->Flink;
  1735. ViDeadlockDeleteTree (Node);
  1736. }
  1737. ViDeadlockFree (Root);
  1738. }