Leaked source code of windows server 2003
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.

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