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.

1295 lines
34 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. deadlock.c
  5. Abstract:
  6. WinDbg Extension Api
  7. Author:
  8. Jordan Tigani (jtigani)
  9. Silviu Calinoiu (silviuc)
  10. Environment:
  11. User Mode
  12. Revision History:
  13. 5-30-00 File created (jtigani)
  14. --*/
  15. #include "precomp.h"
  16. #pragma hdrstop
  17. //
  18. // This has to be in sync with the definition from
  19. // ntos\verifier\vfdeadlock.c
  20. //
  21. #define VI_DEADLOCK_HASH_BINS 0x1F
  22. #if 0
  23. typedef enum _VI_DEADLOCK_RESOURCE_TYPE {
  24. ViDeadlockUnknown = 0,
  25. ViDeadlockMutex,
  26. ViDeadlockFastMutex,
  27. ViDeadlockFastMutexUnsafe,
  28. ViDeadlockSpinLock,
  29. ViDeadlockQueuedSpinLock,
  30. ViDeadlockTypeMaximum
  31. } VI_DEADLOCK_RESOURCE_TYPE, *PVI_DEADLOCK_RESOURCE_TYPE;
  32. #endif
  33. PUCHAR ResourceTypes[] =
  34. {
  35. "Unknown",
  36. "Mutex",
  37. "Fast Mutex",
  38. "Fast Mutex Unsafe",
  39. "Spinlock",
  40. "Queued Spinlock",
  41. };
  42. #define RESOURCE_TYPE_MAXIMUM 5
  43. #define DEADLOCK_EXT_FLAG_DUMP_STACKS 1
  44. #define DEADLOCK_EXT_FLAG_DUMP_NODES 2
  45. #define DEADLOCK_EXT_FLAG_ANALYZE 4
  46. extern
  47. VOID
  48. DumpSymbolicAddress(
  49. ULONG64 Address,
  50. PUCHAR Buffer,
  51. BOOL AlwaysShowHex
  52. );
  53. #define MAX_DEADLOCK_PARTICIPANTS 32
  54. #define VI_MAX_STACK_DEPTH 8
  55. typedef struct _DEADLOCK_VECTOR
  56. {
  57. ULONG64 Thread;
  58. ULONG64 Node;
  59. ULONG64 ResourceAddress;
  60. ULONG64 StackAddress;
  61. ULONG64 ParentStackAddress;
  62. ULONG64 ThreadEntry;
  63. ULONG Type;
  64. BOOLEAN TryAcquire;
  65. } DEADLOCK_VECTOR, *PDEADLOCK_VECTOR;
  66. extern
  67. ULONG64
  68. ReadPvoid (
  69. ULONG64 Address
  70. );
  71. extern
  72. ULONG
  73. ReadUlong(
  74. ULONG64 Address
  75. );
  76. //
  77. // Forward declarations for local functions
  78. //
  79. VOID
  80. PrintGlobalStatistics (
  81. ULONG64 GlobalsAddress
  82. );
  83. BOOLEAN
  84. SearchForResource (
  85. ULONG64 GlobalsAddress,
  86. ULONG64 ResourceAddress
  87. );
  88. BOOLEAN
  89. SearchForThread (
  90. ULONG64 GlobalsAddress,
  91. ULONG64 ThreadAddress
  92. );
  93. BOOLEAN
  94. AnalyzeResource (
  95. ULONG64 Resource,
  96. BOOLEAN Verbose
  97. );
  98. BOOLEAN
  99. AnalyzeResources (
  100. ULONG64 GlobalsAddress
  101. );
  102. /////////////////////////////////////////////////////////////////////
  103. /////////////////////////////////////////////////////////// Deadlocks
  104. /////////////////////////////////////////////////////////////////////
  105. //
  106. // Defines copied from nt\base\ntos\verifier\vfdeadlock.c .
  107. //
  108. #define VI_DEADLOCK_ISSUE_SELF_DEADLOCK 0x1000
  109. #define VI_DEADLOCK_ISSUE_DEADLOCK_DETECTED 0x1001
  110. #define VI_DEADLOCK_ISSUE_UNINITIALIZED_RESOURCE 0x1002
  111. #define VI_DEADLOCK_ISSUE_UNEXPECTED_RELEASE 0x1003
  112. #define VI_DEADLOCK_ISSUE_UNEXPECTED_THREAD 0x1004
  113. #define VI_DEADLOCK_ISSUE_MULTIPLE_INITIALIZATION 0x1005
  114. #define VI_DEADLOCK_ISSUE_THREAD_HOLDS_RESOURCES 0x1006
  115. #define VI_DEADLOCK_ISSUE_UNACQUIRED_RESOURCE 0x1007
  116. #define DUMP_FIELD(Name) dprintf ("%-20s %I64u \n", #Name, ReadField (Name))
  117. DECLARE_API( deadlock )
  118. /*++
  119. Routine Description:
  120. Verifier deadlock detection module extension.
  121. Arguments:
  122. arg - not used for now.
  123. Return Value:
  124. None.
  125. --*/
  126. {
  127. ULONG64 GlobalsPointer;
  128. ULONG64 GlobalsAddress;
  129. ULONG64 InitializedAddress;
  130. ULONG64 EnabledAddress;
  131. ULONG64 InstigatorAddress;
  132. ULONG64 ParticipantAddress;
  133. ULONG64 LastResourceAddress;
  134. ULONG64 RootAddress;
  135. ULONG64 CurrentResourceAddress;
  136. ULONG64 CurrentThread;
  137. ULONG64 ThreadForChain;
  138. ULONG64 CurrentStack;
  139. ULONG64 NextStack;
  140. ULONG64 SymbolOffset;
  141. ULONG StackTraceSize;
  142. ULONG Processor=0;
  143. ULONG ParticipantOffset;
  144. ULONG StackOffset;
  145. ULONG ParentStackOffset;
  146. ULONG InitializedValue;
  147. ULONG EnabledValue;
  148. ULONG NumberOfParticipants;
  149. ULONG NumberOfResources;
  150. ULONG NumberOfThreads;
  151. ULONG ThreadNumber;
  152. ULONG ResourceNumber;
  153. ULONG ResourceType;
  154. ULONG TryAcquireUsed;
  155. ULONG PtrSize;
  156. ULONG J, I;
  157. BOOLEAN DumpStacks = FALSE;
  158. BOOLEAN DumpNodes = FALSE;
  159. BOOLEAN Analyze = FALSE;
  160. ULONG64 Flags;
  161. UCHAR SymbolName[512];
  162. HANDLE CurrentThreadHandle = NULL;
  163. DEADLOCK_VECTOR Participants[MAX_DEADLOCK_PARTICIPANTS+1];
  164. ULONG64 Issue[4];
  165. ULONG64 SearchAddress = 0;
  166. //
  167. // Check if help requested
  168. //
  169. if (strstr (args, "?")) {
  170. dprintf ("\n");
  171. dprintf ("!deadlock Statistics and deadlock layout \n");
  172. dprintf ("!deadlock 3 Detailed deadlock layout \n");
  173. dprintf ("!deadlock ADDRESS Search for ADDRESS among deadlock verifier data \n");
  174. dprintf ("\n");
  175. return S_OK;
  176. }
  177. Flags = GetExpression(args);
  178. if (Flags > 0x10000000) {
  179. SearchAddress = Flags;
  180. }
  181. else {
  182. if (Flags & DEADLOCK_EXT_FLAG_DUMP_STACKS) {
  183. DumpStacks = TRUE;
  184. }
  185. if (Flags & DEADLOCK_EXT_FLAG_DUMP_NODES) {
  186. DumpNodes = TRUE;
  187. }
  188. if (Flags & DEADLOCK_EXT_FLAG_ANALYZE) {
  189. Analyze = TRUE;
  190. }
  191. }
  192. GlobalsPointer = (ULONG64) GetExpression ("nt!ViDeadlockGlobals");
  193. EnabledAddress = (ULONG64) GetExpression ("nt!ViDeadlockDetectionEnabled");
  194. if (GlobalsPointer == 0 || EnabledAddress == 0) {
  195. dprintf ("Error: incorrect symbols for kernel \n");
  196. return E_INVALIDARG;
  197. }
  198. GlobalsAddress = 0;
  199. ReadPointer (GlobalsPointer, &GlobalsAddress);
  200. EnabledValue = ReadUlong (EnabledAddress);
  201. if (GlobalsAddress == 0) {
  202. dprintf ("Deadlock detection not initialized \n");
  203. return E_INVALIDARG;
  204. }
  205. InitializedValue = 1;
  206. if (EnabledValue == 0) {
  207. dprintf ("Deadlock detection not enabled \n");
  208. return E_INVALIDARG;
  209. }
  210. //
  211. // Do a search if this is requested.
  212. //
  213. if (SearchAddress) {
  214. BOOLEAN FoundSomething = FALSE;
  215. dprintf ("Searching for %p ... \n", SearchAddress);
  216. if (FoundSomething == FALSE) {
  217. FoundSomething = SearchForResource (GlobalsAddress, SearchAddress);
  218. }
  219. if (FoundSomething == FALSE) {
  220. FoundSomething = SearchForThread (GlobalsAddress, SearchAddress);
  221. }
  222. return S_OK;
  223. }
  224. //
  225. // Analyze if this is needed.
  226. //
  227. if (Analyze) {
  228. AnalyzeResources (GlobalsAddress);
  229. return S_OK;
  230. }
  231. //
  232. // Get the ViDeadlockIssue[0..3] vector.
  233. //
  234. {
  235. ULONG ValueSize;
  236. ULONG64 IssueAddress;
  237. ULONG I;
  238. ValueSize = DBG_PTR_SIZE;
  239. IssueAddress = GetExpression ("nt!ViDeadlockIssue");
  240. for (I = 0; I < 4; I += 1) {
  241. ReadPointer (IssueAddress + I * ValueSize, &(Issue[I]));
  242. }
  243. if (Issue[0] == 0) {
  244. dprintf ("\n");
  245. PrintGlobalStatistics (GlobalsAddress);
  246. dprintf ("\nNo deadlock verifier issues. \n");
  247. return S_OK;
  248. }
  249. else {
  250. if (ValueSize == 4) {
  251. dprintf ("issue: %08X %08X %08X %08X \n",
  252. Issue[0], Issue[1], Issue[2], Issue[3]);
  253. }
  254. else {
  255. dprintf ("issue: %I64X %I64X %I64X %I64X \n",
  256. Issue[0], Issue[1], Issue[2], Issue[3]);
  257. }
  258. }
  259. switch (Issue[0]) {
  260. case VI_DEADLOCK_ISSUE_SELF_DEADLOCK:
  261. dprintf ("Resource %I64X is acquired recursively. \n", Issue[1]);
  262. return S_OK;
  263. case VI_DEADLOCK_ISSUE_DEADLOCK_DETECTED:
  264. break;
  265. case VI_DEADLOCK_ISSUE_UNINITIALIZED_RESOURCE:
  266. dprintf ("Resource %I64X is used before being initialized. \n", Issue[1]);
  267. return S_OK;
  268. case VI_DEADLOCK_ISSUE_UNEXPECTED_RELEASE:
  269. dprintf ("Resource %I64X is released out of order. \n", Issue[2]);
  270. return S_OK;
  271. case VI_DEADLOCK_ISSUE_UNEXPECTED_THREAD:
  272. dprintf ("Current thread is releasing resource %I64X which was acquired in thread %I64X. \n",
  273. Issue[1], Issue[2]);
  274. return S_OK;
  275. case VI_DEADLOCK_ISSUE_MULTIPLE_INITIALIZATION:
  276. dprintf ("Resource %I64X has already been initialized. \n", Issue[1]);
  277. return S_OK;
  278. case VI_DEADLOCK_ISSUE_THREAD_HOLDS_RESOURCES:
  279. dprintf ("Deleting thread %I64X which still holds resource %I64X . \n",
  280. Issue[1], Issue[2]);
  281. return S_OK;
  282. case VI_DEADLOCK_ISSUE_UNACQUIRED_RESOURCE:
  283. dprintf ("Releasing resource %I64X that was never acquired. \n", Issue[1]);
  284. return S_OK;
  285. default:
  286. dprintf ("Unrecognized issue code %I64X ! \n", Issue[0]);
  287. return S_OK;
  288. }
  289. }
  290. //
  291. // Figure out how big a pointer is
  292. //
  293. PtrSize = DBG_PTR_SIZE;
  294. if (PtrSize == 0) {
  295. dprintf ("Cannot get size of PVOID \n");
  296. return E_INVALIDARG;
  297. }
  298. GetCurrentProcessor(Client, &Processor, &CurrentThreadHandle);
  299. GetCurrentThreadAddr( Processor, &CurrentThread );
  300. //
  301. // Dump the globals structure
  302. //
  303. InitTypeRead (GlobalsAddress, nt!_VI_DEADLOCK_GLOBALS);
  304. //
  305. // Find out the address of the resource that causes the deadlock
  306. //
  307. InstigatorAddress = ReadField(Instigator);
  308. NumberOfParticipants = (ULONG) ReadField(NumberOfParticipants);
  309. if (NumberOfParticipants > MAX_DEADLOCK_PARTICIPANTS) {
  310. dprintf("\nCannot have %x participants in a deadlock!\n",NumberOfParticipants);
  311. return E_INVALIDARG;
  312. }
  313. if (0 == NumberOfParticipants) {
  314. dprintf("\nNo deadlock detected\n");
  315. return S_OK;
  316. }
  317. GetFieldOffset("nt!_VI_DEADLOCK_GLOBALS",
  318. "Participant",
  319. &ParticipantOffset
  320. );
  321. ParticipantAddress = GlobalsAddress + ParticipantOffset;
  322. //
  323. // Read the vector of VI_DEADLOCK_NODEs that
  324. // participate in the deadlock.
  325. //
  326. //
  327. for (J = 0; J < NumberOfParticipants; J++) {
  328. Participants[J].Node = ReadPvoid(ParticipantAddress + J * PtrSize);
  329. // dprintf("Participant %c: %08x\n", 'A' + J, Participants[J].Node);
  330. }
  331. //
  332. // Gather the information we'll need to print out exact
  333. // context for the deadlock.
  334. //
  335. GetFieldOffset("nt!_VI_DEADLOCK_NODE",
  336. "StackTrace",
  337. &StackOffset
  338. );
  339. GetFieldOffset("nt!_VI_DEADLOCK_NODE",
  340. "ParentStackTrace",
  341. &ParentStackOffset
  342. );
  343. //
  344. // The stack trace size is 1 on free builds and 6 (or bigger) on
  345. // checked builds. We assume that the ParentStackTrace field comes
  346. // immediately after StackTrace field in the NODE structure.
  347. //
  348. StackTraceSize = (ParentStackOffset - StackOffset) / PtrSize;
  349. for (J = 0; J < NumberOfParticipants; J++) {
  350. InitTypeRead (Participants[J].Node, nt!_VI_DEADLOCK_NODE);
  351. RootAddress = ReadField(Root);
  352. GetFieldValue(RootAddress,
  353. "nt!_VI_DEADLOCK_RESOURCE",
  354. "ResourceAddress" ,
  355. Participants[J].ResourceAddress
  356. );
  357. GetFieldValue(RootAddress,
  358. "nt!_VI_DEADLOCK_RESOURCE",
  359. "Type",
  360. Participants[J].Type
  361. );
  362. if (Participants[J].Type > RESOURCE_TYPE_MAXIMUM) {
  363. Participants[J].Type = 0;
  364. }
  365. Participants[J].ThreadEntry = ReadField(ThreadEntry);
  366. Participants[J].StackAddress = Participants[J].Node + StackOffset;
  367. Participants[J].ParentStackAddress = Participants[J].Node +
  368. ParentStackOffset;
  369. Participants[J].TryAcquire = (BOOLEAN) ReadField(OnlyTryAcquireUsed);
  370. GetFieldValue(Participants[J].ThreadEntry,
  371. "nt!_VI_DEADLOCK_THREAD",
  372. "Thread",
  373. Participants[J].Thread
  374. );
  375. }
  376. if (Participants[0].ResourceAddress != InstigatorAddress) {
  377. dprintf("\nDeadlock Improperly formed participant list\n");
  378. return E_INVALIDARG;
  379. }
  380. //
  381. // The last participant is the Instigator of the deadlock
  382. //
  383. Participants[NumberOfParticipants].Thread = CurrentThread;
  384. Participants[NumberOfParticipants].Node = 0;
  385. Participants[NumberOfParticipants].ResourceAddress = InstigatorAddress;
  386. Participants[NumberOfParticipants].StackAddress = 0;
  387. Participants[NumberOfParticipants].ParentStackAddress =
  388. Participants[NumberOfParticipants-1].StackAddress;
  389. Participants[NumberOfParticipants].Type =
  390. Participants[0].Type;
  391. Participants[NumberOfParticipants].TryAcquire = FALSE; // can't cause a deadlock with try
  392. Participants[NumberOfParticipants].ThreadEntry = 0;
  393. //
  394. // At this point we have all of the raw data we need.
  395. // We have to munge it up a bit so that we have the most
  396. // recent data. For instance, take the simple deadlock AB-BA.
  397. // The stack for A in the AB context may be wrong because
  398. // another thread may have come and taken A at a different point.
  399. // This is why we have the parent stack address.
  400. //
  401. // So the rules we have to adhere to are as follows:
  402. // Where we have a chain, (eg ABC meaning A taken then B then C),
  403. // the thread used will always be the thread for the last resource taken,
  404. // and the stacks used will be the the childs parent stack where
  405. // applicable.
  406. //
  407. // For example, if C was taken by thread 1, A & B would be munged
  408. // to use thread 1. Since in order to get to C, A and B must have
  409. // been taken by thread 1 at some point, even if the thread they
  410. // have saved now is a different one. C would use its own stack,
  411. // B would use C's parent stack, since that was the stack that
  412. // B had been acquired with when C was taken, and A will use
  413. // B's parent stack.
  414. //
  415. // We can identify the start of a chain when the same resource
  416. // is on the participant list twice in a row.
  417. //
  418. LastResourceAddress = InstigatorAddress;
  419. NumberOfResources = 0;
  420. NumberOfThreads = 0;
  421. for (J = 0; J <= NumberOfParticipants; J++) {
  422. I = NumberOfParticipants - J;
  423. CurrentResourceAddress = Participants[I].ResourceAddress;
  424. if (CurrentResourceAddress == LastResourceAddress) {
  425. //
  426. // This is the beginning of a chain. Use the current
  427. // stack and the current thread, and set the chain
  428. // thread to ours
  429. //
  430. ThreadForChain = Participants[I].Thread;
  431. CurrentStack = Participants[I].StackAddress;
  432. NumberOfThreads++;
  433. } else {
  434. //
  435. // This is a resource we haven't seen before
  436. //
  437. NumberOfResources++;
  438. }
  439. NextStack = Participants[I].ParentStackAddress;
  440. Participants[I].StackAddress = CurrentStack;
  441. Participants[I].Thread = ThreadForChain;
  442. //
  443. // Parent stack isn't used any more -- nullify it.
  444. //
  445. Participants[I].ParentStackAddress = 0;
  446. CurrentStack = NextStack;
  447. LastResourceAddress = CurrentResourceAddress;
  448. }
  449. //
  450. // Now that we've munged the vectors, we can go ahead and print out the
  451. // deadlock information.
  452. //
  453. dprintf("\nDeadlock detected (%d resources in %d threads):\n\n",NumberOfResources, NumberOfThreads);
  454. if (! DumpStacks )
  455. {
  456. //
  457. // Print out the 'short' form
  458. // Example:
  459. //
  460. // !dealock detected:
  461. // Thread 1: A B
  462. // Thread 2: B C
  463. // Thread 3: C A
  464. //
  465. // Thread 1 = <address>
  466. // Thread 2 = <address>
  467. // Thread 3 = <address>
  468. //
  469. // Lock A = <address> (spinlock)
  470. // Lock B = <address> (mutex)
  471. // Lock C = <address> (spinlock)
  472. //
  473. ThreadNumber = 0;
  474. ResourceNumber = 0;
  475. J=0;
  476. //
  477. // Dump out the deadlock topology
  478. //
  479. while (J <= NumberOfParticipants)
  480. {
  481. ThreadForChain = Participants[J].Thread;
  482. dprintf("Thread %d: ",ThreadNumber);
  483. do {
  484. if (J == NumberOfParticipants) {
  485. ResourceNumber = 0;
  486. }
  487. dprintf("%c ",
  488. 'A' + ResourceNumber
  489. );
  490. J++;
  491. ResourceNumber++;
  492. } while( J <= NumberOfParticipants && Participants[J].ResourceAddress != Participants[J-1].ResourceAddress);
  493. dprintf("\n");
  494. ThreadNumber++;
  495. ResourceNumber--;
  496. }
  497. dprintf("\nWhere:\n");
  498. //
  499. // Dump out the thread addresses
  500. //
  501. ThreadNumber = 0;
  502. ResourceNumber = 0;
  503. J=0;
  504. while (J <= NumberOfParticipants) {
  505. ThreadForChain = Participants[J].Thread;
  506. dprintf("Thread %d = %08x\n",ThreadNumber, ThreadForChain);
  507. do {
  508. if (J == NumberOfParticipants) {
  509. ResourceNumber = 0;
  510. }
  511. J++;
  512. ResourceNumber++;
  513. } while( J <= NumberOfParticipants && Participants[J].ResourceAddress != Participants[J-1].ResourceAddress);
  514. ThreadNumber++;
  515. ResourceNumber--;
  516. }
  517. //
  518. // Dump out the resource addresses
  519. //
  520. ThreadNumber = 0;
  521. ResourceNumber = 0;
  522. J=0;
  523. #if 1
  524. while (J < NumberOfParticipants)
  525. {
  526. while(J < NumberOfParticipants && Participants[J].ResourceAddress != Participants[J+1].ResourceAddress) {
  527. if (Participants[J].ResourceAddress != Participants[J+1].ResourceAddress) {
  528. CHAR Buffer[0xFF];
  529. ULONG64 Displacement = 0;
  530. GetSymbol(Participants[J].ResourceAddress, Buffer, &Displacement);
  531. dprintf("Lock %c = %s", 'A' + ResourceNumber, Buffer );
  532. if (Displacement != 0) {
  533. dprintf("%s%x", (Displacement < 0xFFF)?"+0x":"",Displacement);
  534. }
  535. dprintf(" Type '%s' ",ResourceTypes[Participants[J].Type]);
  536. dprintf("\n");
  537. ResourceNumber++;
  538. }
  539. J++;
  540. }
  541. J++;
  542. }
  543. #endif
  544. } else {
  545. //
  546. // Dump out verbose deadlock information -- with stacks
  547. // Here is an exapmle:
  548. //
  549. // Deadlock detected (3 resources in 3 threads):
  550. //
  551. //Thread 0 (829785B0) took locks in the following order:
  552. //
  553. // Lock A (Spinlock) @ bfc7c254
  554. // Node: 82887F88
  555. // Stack: NDIS!ndisNotifyMiniports+0xC1
  556. // NDIS!ndisPowerStateCallback+0x6E
  557. // ntkrnlmp!ExNotifyCallback+0x72
  558. // ntkrnlmp!PopDispatchCallback+0x13
  559. // ntkrnlmp!PopPolicyWorkerNotify+0x8F
  560. // ntkrnlmp!PopPolicyWorkerThread+0x10F
  561. // ntkrnlmp!ExpWorkerThread+0x294
  562. // ntkrnlmp!PspSystemThreadStartup+0x4B
  563. //
  564. // Lock B (Spinlock) @ 8283b87c
  565. // Node: 82879148
  566. // Stack: NDIS!ndisDereferenceRef+0x10F
  567. // NDIS!ndisDereferenceDriver+0x3A
  568. // NDIS!ndisNotifyMiniports+0xD1
  569. // NDIS!ndisPowerStateCallback+0x6E
  570. // ntkrnlmp!ExNotifyCallback+0x72
  571. // ntkrnlmp!PopDispatchCallback+0x13
  572. // ntkrnlmp!PopPolicyWorkerNotify+0x8F
  573. // ntkrnlmp!PopPolicyWorkerThread+0x10F
  574. //
  575. //Thread 1 (829785B0) took locks in the following order:
  576. //
  577. // Lock B (Spinlock) @ 8283b87c
  578. // Node: 82879008
  579. // Stack: NDIS!ndisReferenceNextUnprocessedMiniport+0x3E
  580. // NDIS!ndisNotifyMiniports+0xB3
  581. // NDIS!ndisPowerStateCallback+0x6E
  582. // ntkrnlmp!ExNotifyCallback+0x72
  583. // ntkrnlmp!PopDispatchCallback+0x13
  584. // ntkrnlmp!PopPolicyWorkerNotify+0x8F
  585. // ntkrnlmp!PopPolicyWorkerThread+0x10F
  586. // ntkrnlmp!ExpWorkerThread+0x294
  587. //
  588. // Lock C (Spinlock) @ 82862b48
  589. // Node: 8288D008
  590. // Stack: NDIS!ndisReferenceRef+0x10F
  591. // NDIS!ndisReferenceMiniport+0x4A
  592. // NDIS!ndisReferenceNextUnprocessedMiniport+0x70
  593. // NDIS!ndisNotifyMiniports+0xB3
  594. // NDIS!ndisPowerStateCallback+0x6E
  595. // ntkrnlmp!ExNotifyCallback+0x72
  596. // ntkrnlmp!PopDispatchCallback+0x13
  597. // ntkrnlmp!PopPolicyWorkerNotify+0x8F
  598. //
  599. //Thread 2 (82978310) took locks in the following order:
  600. //
  601. // Lock C (Spinlock) @ 82862b48
  602. // Node: 82904708
  603. // Stack: NDIS!ndisPnPRemoveDevice+0x20B
  604. // NDIS!ndisPnPDispatch+0x319
  605. // ntkrnlmp!IopfCallDriver+0x62
  606. // ntkrnlmp!IovCallDriver+0x9D
  607. // ntkrnlmp!IopSynchronousCall+0xFA
  608. // ntkrnlmp!IopRemoveDevice+0x11E
  609. // ntkrnlmp!IopDeleteLockedDeviceNode+0x3AF
  610. // ntkrnlmp!IopDeleteLockedDeviceNodes+0xF5
  611. //
  612. // Lock A (Spinlock) @ bfc7c254
  613. // Stack: << Current stack >>
  614. //
  615. ThreadNumber = 0;
  616. ResourceNumber = 0;
  617. J=0;
  618. while (J <= NumberOfParticipants) {
  619. ThreadForChain = Participants[J].Thread;
  620. dprintf("Thread %d: %08X",ThreadNumber, ThreadForChain);
  621. if (DumpNodes && Participants[J].ThreadEntry) {
  622. dprintf(" (ThreadEntry = %X)\n ", (ULONG) Participants[J].ThreadEntry);
  623. }
  624. dprintf(" took locks in the following order:\n\n");
  625. //
  626. // This is a do .. while so that we can never get an infinite loop.
  627. //
  628. do {
  629. UINT64 CurrentStackAddress;
  630. UINT64 StackFrame;
  631. CHAR Buffer[0xFF];
  632. ULONG64 Displacement = 0;
  633. if (J == NumberOfParticipants) {
  634. ResourceNumber = 0;
  635. }
  636. GetSymbol(Participants[J].ResourceAddress, Buffer, &Displacement);
  637. dprintf(" Lock %c -- %s", 'A' + ResourceNumber, Buffer );
  638. if (Displacement != 0) {
  639. dprintf("%s%x", (Displacement < 0xFFF)?"+0x":"",Displacement);
  640. }
  641. dprintf(" (%s)\n",ResourceTypes[Participants[J].Type]);
  642. if (DumpNodes && Participants[J].Node)
  643. dprintf(" Node: %X\n", (ULONG) Participants[J].Node);
  644. dprintf(" Stack: ");
  645. CurrentStackAddress = Participants[J].StackAddress;
  646. if (CurrentStackAddress == 0) {
  647. dprintf ("<< Current stack >>\n");
  648. } else {
  649. for (I = 0; I < StackTraceSize; I++) {
  650. SymbolName[0] = '\0';
  651. StackFrame = ReadPvoid(CurrentStackAddress);
  652. if (0 == StackFrame)
  653. break;
  654. GetSymbol(StackFrame, SymbolName, &SymbolOffset);
  655. if (I) {
  656. dprintf(" ");
  657. }
  658. if ((LONG64) SymbolOffset > 0 ) {
  659. dprintf ("%s+0x%X\n",
  660. SymbolName, (ULONG) SymbolOffset);
  661. } else {
  662. dprintf ("%X\n", (ULONG) StackFrame);
  663. }
  664. CurrentStackAddress += PtrSize;
  665. }
  666. }
  667. dprintf("\n");
  668. J++;
  669. ResourceNumber++;
  670. } while( J <= NumberOfParticipants && Participants[J].ResourceAddress != Participants[J-1].ResourceAddress);
  671. ThreadNumber++;
  672. ResourceNumber--;
  673. }
  674. }
  675. return S_OK;
  676. }
  677. VOID
  678. PrintGlobalStatistics (
  679. ULONG64 GlobalsAddress
  680. )
  681. {
  682. ULONG AllocationFailures;
  683. ULONG64 MemoryUsed;
  684. ULONG NodesTrimmed;
  685. ULONG MaxNodesSearched;
  686. ULONG SequenceNumber;
  687. //
  688. // Dump the globals structure
  689. //
  690. InitTypeRead (GlobalsAddress, nt!_VI_DEADLOCK_GLOBALS);
  691. //
  692. // Print some simple statistics
  693. //
  694. dprintf ("Resources: %u\n", (ULONG) ReadField (Resources[0]));
  695. dprintf ("Nodes: %u\n", (ULONG) ReadField (Nodes[0]));
  696. dprintf ("Threads: %u\n", (ULONG) ReadField (Threads[0]));
  697. dprintf ("\n");
  698. MemoryUsed = ReadField (BytesAllocated);
  699. if (MemoryUsed > 1024 * 1024) {
  700. dprintf ("%I64u bytes of kernel pool used.\n", MemoryUsed);
  701. }
  702. AllocationFailures = (ULONG) ReadField (AllocationFailures);
  703. if (AllocationFailures > 0) {
  704. dprintf ("Allocation failures encountered (%u).\n", AllocationFailures);
  705. }
  706. NodesTrimmed = (ULONG) ReadField (NodesTrimmedBasedOnAge);
  707. dprintf ("Nodes trimmed based on age %u.\n", NodesTrimmed);
  708. NodesTrimmed = (ULONG) ReadField (NodesTrimmedBasedOnCount);
  709. dprintf ("Nodes trimmed based on count %u.\n", NodesTrimmed);
  710. dprintf ("Analyze calls %u.\n", (ULONG) ReadField (SequenceNumber));
  711. dprintf ("Maximum nodes searched %u.\n", (ULONG) ReadField (MaxNodesSearched));
  712. }
  713. BOOLEAN
  714. SearchForResource (
  715. ULONG64 GlobalsAddress,
  716. ULONG64 ResourceAddress
  717. )
  718. {
  719. ULONG I;
  720. ULONG64 Bucket;
  721. ULONG64 SizeOfBucket;
  722. BOOLEAN ResourceFound = FALSE;
  723. ULONG64 SizeOfResource;
  724. ULONG FlinkOffset = 0;
  725. ULONG64 Current;
  726. ULONG64 CurrentResource;
  727. ULONG Magic;
  728. SizeOfBucket = GetTypeSize("LIST_ENTRY");
  729. SizeOfResource = GetTypeSize("nt!_VI_DEADLOCK_RESOURCE");
  730. GetFieldOffset("nt!_VI_DEADLOCK_RESOURCE",
  731. "HashChainList",
  732. &FlinkOffset);
  733. if (SizeOfBucket == 0 || SizeOfResource == 0 || FlinkOffset == 0) {
  734. dprintf ("Error: cannot get size for verifier types. \n");
  735. return FALSE;
  736. }
  737. InitTypeRead (GlobalsAddress, nt!_VI_DEADLOCK_GLOBALS);
  738. Bucket = ReadField (ResourceDatabase);
  739. if (Bucket == 0) {
  740. dprintf ("Error: cannot get resource database address. \n");
  741. return FALSE;
  742. }
  743. for (I = 0; I < VI_DEADLOCK_HASH_BINS; I += 1) {
  744. // traverse it ...
  745. Current = ReadPvoid(Bucket);
  746. while (Current != Bucket) {
  747. InitTypeRead (Current - FlinkOffset, nt!_VI_DEADLOCK_RESOURCE);
  748. CurrentResource = ReadField (ResourceAddress);
  749. if (CurrentResource == ResourceAddress ||
  750. ResourceAddress == Current - FlinkOffset) {
  751. CurrentResource = Current - FlinkOffset;
  752. ResourceFound = TRUE;
  753. break;
  754. }
  755. Current = ReadPvoid(Current);
  756. if (CheckControlC()) {
  757. dprintf ("\nSearch interrupted ... \n");
  758. return TRUE;
  759. }
  760. }
  761. if (ResourceFound) {
  762. break;
  763. }
  764. dprintf (".");
  765. Bucket += SizeOfBucket;
  766. }
  767. dprintf ("\n");
  768. if (ResourceFound == FALSE) {
  769. dprintf ("No resource correspoding to %p has been found. \n",
  770. ResourceAddress);
  771. }
  772. else {
  773. dprintf ("Found a deadlock verifier resource descriptor @ %p \n",
  774. CurrentResource);
  775. }
  776. return ResourceFound;
  777. }
  778. BOOLEAN
  779. SearchForThread (
  780. ULONG64 GlobalsAddress,
  781. ULONG64 ThreadAddress
  782. )
  783. {
  784. ULONG I;
  785. ULONG64 Bucket;
  786. ULONG64 SizeOfBucket;
  787. BOOLEAN ThreadFound = FALSE;
  788. ULONG64 SizeOfThread;
  789. ULONG FlinkOffset = 0;
  790. ULONG64 Current;
  791. ULONG64 CurrentThread;
  792. SizeOfBucket = GetTypeSize("LIST_ENTRY");
  793. SizeOfThread = GetTypeSize("nt!_VI_DEADLOCK_THREAD");
  794. GetFieldOffset("nt!_VI_DEADLOCK_THREAD",
  795. "ListEntry",
  796. &FlinkOffset);
  797. if (SizeOfBucket == 0 || SizeOfThread == 0 || FlinkOffset == 0) {
  798. dprintf ("Error: cannot get size for verifier types. \n");
  799. return FALSE;
  800. }
  801. InitTypeRead (GlobalsAddress, nt!_VI_DEADLOCK_GLOBALS);
  802. Bucket = ReadField (ThreadDatabase);
  803. if (Bucket == 0) {
  804. dprintf ("Error: cannot get thread database address. \n");
  805. return FALSE;
  806. }
  807. for (I = 0; I < VI_DEADLOCK_HASH_BINS; I += 1) {
  808. // traverse it ...
  809. Current = ReadPvoid(Bucket);
  810. while (Current != Bucket) {
  811. InitTypeRead (Current - FlinkOffset, nt!_VI_DEADLOCK_THREAD);
  812. CurrentThread = ReadField (ThreadAddress);
  813. if (CurrentThread == ThreadAddress ||
  814. ThreadAddress == Current - FlinkOffset) {
  815. CurrentThread = Current - FlinkOffset;
  816. ThreadFound = TRUE;
  817. break;
  818. }
  819. Current = ReadPvoid(Current);
  820. if (CheckControlC()) {
  821. dprintf ("\nSearch interrupted ... \n");
  822. return TRUE;
  823. }
  824. }
  825. if (ThreadFound) {
  826. break;
  827. }
  828. dprintf (".");
  829. Bucket += SizeOfBucket;
  830. }
  831. dprintf ("\n");
  832. if (ThreadFound == FALSE) {
  833. dprintf ("No thread correspoding to %p has been found. \n",
  834. ThreadAddress);
  835. }
  836. else {
  837. dprintf ("Found a deadlock verifier thread descriptor @ %p \n",
  838. CurrentThread);
  839. }
  840. return ThreadFound;
  841. }
  842. VOID
  843. DumpResourceStructure (
  844. )
  845. {
  846. }
  847. ULONG
  848. GetNodeLevel (
  849. ULONG64 Node
  850. )
  851. {
  852. ULONG Level = 0;
  853. while (Node != 0) {
  854. Level += 1;
  855. if (Level > 12) {
  856. dprintf ("Level > 12 !!! \n");
  857. break;
  858. }
  859. InitTypeRead (Node, nt!_VI_DEADLOCK_NODE);
  860. Node = ReadField (Parent);
  861. }
  862. return Level;
  863. }
  864. BOOLEAN
  865. AnalyzeResource (
  866. ULONG64 Resource,
  867. BOOLEAN Verbose
  868. )
  869. {
  870. ULONG64 Start;
  871. ULONG64 Current;
  872. ULONG64 Node;
  873. ULONG64 Parent;
  874. ULONG FlinkOffset;
  875. ULONG RootsCount = 0;
  876. ULONG NodesCount = 0;
  877. ULONG Levels[8];
  878. ULONG ResourceFlinkOffset;
  879. ULONG I;
  880. ULONG Level;
  881. ULONG NodeCounter = 0;
  882. ZeroMemory (Levels, sizeof Levels);
  883. GetFieldOffset("nt!_VI_DEADLOCK_NODE",
  884. "ResourceList",
  885. &FlinkOffset);
  886. GetFieldOffset("nt!_VI_DEADLOCK_RESOURCE",
  887. "ResourceList",
  888. &ResourceFlinkOffset);
  889. InitTypeRead (Resource, nt!_VI_DEADLOCK_RESOURCE);
  890. if (! Verbose) {
  891. if (ReadField(NodeCount) < 4) {
  892. return TRUE;
  893. }
  894. dprintf ("Resource (%p) : %I64u %I64u %I64u ",
  895. Resource,
  896. ReadField(Type),
  897. ReadField(NodeCount),
  898. ReadField(RecursionCount));
  899. }
  900. Start = Resource + ResourceFlinkOffset;
  901. Current = ReadPvoid (Start);
  902. while (Start != Current) {
  903. Node = Current - FlinkOffset;
  904. Level = (GetNodeLevel(Node) - 1) % 8;
  905. Levels[Level] += 1;
  906. NodesCount += 1;
  907. if (NodesCount && NodesCount % 1000 == 0) {
  908. dprintf (".");
  909. }
  910. Current = ReadPvoid (Current);
  911. if (CheckControlC()) {
  912. return FALSE;
  913. }
  914. }
  915. dprintf ("[");
  916. for (I = 0; I < 8; I += 1) {
  917. dprintf ("%u ", Levels[I]);
  918. }
  919. dprintf ("]\n");
  920. return TRUE;
  921. }
  922. BOOLEAN
  923. AnalyzeResources (
  924. ULONG64 GlobalsAddress
  925. )
  926. /*++
  927. This routine analyzes all resource to make sure we do not have
  928. zombie nodes laying around.
  929. --*/
  930. {
  931. ULONG I;
  932. ULONG64 Bucket;
  933. ULONG64 SizeOfBucket;
  934. ULONG64 SizeOfResource;
  935. ULONG FlinkOffset = 0;
  936. ULONG64 Current;
  937. ULONG64 CurrentResource;
  938. ULONG Magic;
  939. BOOLEAN Finished;
  940. ULONG ResourceCount = 0;
  941. dprintf ("Analyzing resources (%p) ... \n", GlobalsAddress);
  942. SizeOfBucket = GetTypeSize("LIST_ENTRY");
  943. SizeOfResource = GetTypeSize("nt!_VI_DEADLOCK_RESOURCE");
  944. GetFieldOffset("nt!_VI_DEADLOCK_RESOURCE",
  945. "HashChainList",
  946. &FlinkOffset);
  947. if (SizeOfBucket == 0 || SizeOfResource == 0 || FlinkOffset == 0) {
  948. dprintf ("Error: cannot get size for verifier types. \n");
  949. return FALSE;
  950. }
  951. InitTypeRead (GlobalsAddress, nt!_VI_DEADLOCK_GLOBALS);
  952. Bucket = ReadField (ResourceDatabase);
  953. if (Bucket == 0) {
  954. dprintf ("Error: cannot get resource database address. \n");
  955. return FALSE;
  956. }
  957. for (I = 0; I < VI_DEADLOCK_HASH_BINS; I += 1) {
  958. // traverse it ...
  959. Current = ReadPvoid(Bucket);
  960. while (Current != Bucket) {
  961. Finished = AnalyzeResource (Current - FlinkOffset, FALSE);
  962. ResourceCount += 1;
  963. if (ResourceCount % 256 == 0) {
  964. dprintf (".\n");
  965. }
  966. Current = ReadPvoid(Current);
  967. if (CheckControlC() || !Finished) {
  968. dprintf ("\nSearch interrupted ... \n");
  969. return TRUE;
  970. }
  971. }
  972. Bucket += SizeOfBucket;
  973. }
  974. return TRUE;
  975. }