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.

930 lines
28 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. qlocks.c
  5. Abstract:
  6. WinDbg Extension Api
  7. Author:
  8. David N. Cutler (davec) 25-Sep-1999
  9. Environment:
  10. User Mode.
  11. Revision History:
  12. --*/
  13. #include "precomp.h"
  14. #pragma hdrstop
  15. //
  16. // Define queued lock data.
  17. //
  18. #define NUMBER_PROCESSORS 32
  19. #define NUMBER_PROCESSORS_X86 32
  20. #define NUMBER_PROCESSORS_IA64 64
  21. #if (NUMBER_PROCESSORS_IA64 < MAXIMUM_PROCESSORS)
  22. #error "Update NUMBER_PROCESSORS definition"
  23. #endif
  24. UCHAR Key[NUMBER_PROCESSORS];
  25. #define KEY_CORRUPT 255
  26. #define KEY_OWNER 254
  27. #define KEY_NOTHING 253
  28. typedef struct KSPIN_LOCK_QUEUE_READ {
  29. ULONG64 Next;
  30. ULONG64 Lock;
  31. } KSPIN_LOCK_QUEUE_READ;
  32. KSPIN_LOCK_QUEUE_READ LockQueue[NUMBER_PROCESSORS_IA64][LockQueueMaximumLock];
  33. ULONG64 ProcessorBlock[NUMBER_PROCESSORS_IA64];
  34. ULONG64 SpinLock[LockQueueMaximumLock];
  35. typedef struct _LOCK_NAME {
  36. KSPIN_LOCK_QUEUE_NUMBER Number;
  37. PCHAR Name;
  38. } LOCK_NAME, *PLOCK_NAME;
  39. LOCK_NAME LockName[] = {
  40. { LockQueueDispatcherLock, "KE - Dispatcher " },
  41. { LockQueueContextSwapLock, "KE - Context Swap " },
  42. { LockQueuePfnLock, "MM - PFN " },
  43. { LockQueueSystemSpaceLock, "MM - System Space " },
  44. { LockQueueVacbLock, "CC - Vacb " },
  45. { LockQueueMasterLock, "CC - Master " },
  46. { LockQueueNonPagedPoolLock, "EX - NonPagedPool " },
  47. { LockQueueIoCancelLock, "IO - Cancel " },
  48. { LockQueueWorkQueueLock, "EX - WorkQueue " },
  49. { LockQueueIoVpbLock, "IO - Vpb " },
  50. { LockQueueIoDatabaseLock, "IO - Database " },
  51. { LockQueueIoCompletionLock, "IO - Completion " },
  52. { LockQueueNtfsStructLock, "NTFS - Struct " },
  53. { LockQueueAfdWorkQueueLock, "AFD - WorkQueue " },
  54. { LockQueueBcbLock, "CC - Bcb " },
  55. { LockQueueMaximumLock, NULL },
  56. };
  57. //
  58. // Define forward referenced prototypes.
  59. //
  60. ULONG
  61. ProcessorIndex (
  62. ULONG64 LockAddress,
  63. ULONG LockIndex
  64. );
  65. DECLARE_API( qlocks )
  66. /*++
  67. Routine Description:
  68. Dump kernel mode queued spinlock status.
  69. Arguments:
  70. None.
  71. Return Value:
  72. None.
  73. --*/
  74. {
  75. BOOL Corrupt;
  76. ULONG HighestProcessor;
  77. ULONG Index;
  78. KSPIN_LOCK_QUEUE_READ *LockOwner;
  79. ULONG Last;
  80. ULONG Loop;
  81. ULONG64 MemoryAddress;
  82. ULONG Number;
  83. ULONG Result;
  84. CHAR Sequence;
  85. ULONG LockQueueOffset;
  86. ULONG Processor;
  87. ULONG PtrSize = DBG_PTR_SIZE;
  88. ULONG SizeOfQ = GetTypeSize("nt!KSPIN_LOCK_QUEUE");
  89. ULONG MaximumProcessors;
  90. MaximumProcessors = (UCHAR) GetUlongValue("NT!KeNumberProcessors");
  91. // IsPtr64() ? NUMBER_PROCESSORS_IA64 : NUMBER_PROCESSORS_X86;
  92. //
  93. // Get address of processor block array and read entire array.
  94. //
  95. MemoryAddress = GetExpression("nt!KiProcessorBlock");
  96. if (MemoryAddress == 0) {
  97. //
  98. // Either the processor block address is zero or the processor
  99. // block array could not be read.
  100. //
  101. dprintf("Unable to read processor block array\n");
  102. return E_INVALIDARG;
  103. }
  104. HighestProcessor = 0;
  105. for (Index = 0; Index < MaximumProcessors; Index++) {
  106. if (!ReadPointer(MemoryAddress + Index*PtrSize, &ProcessorBlock[Index])) {
  107. dprintf("Unable to read processor block array\n");
  108. return E_INVALIDARG;
  109. }
  110. if (ProcessorBlock[Index] != 0) {
  111. HighestProcessor = Index;
  112. }
  113. }
  114. if (GetFieldOffset("nt!KPRCB", "LockQueue", &LockQueueOffset)) {
  115. dprintf("Unable to read KPRCB.LockQueue offset.\n");
  116. return E_INVALIDARG;
  117. }
  118. //
  119. // Read the lock queue information for each processor.
  120. //
  121. for (Index = 0; Index < MaximumProcessors; Index += 1) {
  122. RtlZeroMemory(&LockQueue[Index][0],
  123. sizeof(KSPIN_LOCK_QUEUE_READ) * LockQueueMaximumLock);
  124. if (ProcessorBlock[Index] != 0) {
  125. ULONG j;
  126. for (j=0; j< LockQueueMaximumLock; j++) {
  127. if (GetFieldValue(ProcessorBlock[Index] + LockQueueOffset + j*SizeOfQ,
  128. "nt!KSPIN_LOCK_QUEUE",
  129. "Next",
  130. LockQueue[Index][j].Next)) {
  131. //
  132. // Lock queue information could not be read for the respective
  133. // processor.
  134. //
  135. dprintf("Unable to read lock queue information for processor %d @ %p\n",
  136. Index, ProcessorBlock[Index]);
  137. return E_INVALIDARG;
  138. }
  139. GetFieldValue(ProcessorBlock[Index] + LockQueueOffset + j*SizeOfQ,
  140. "nt!KSPIN_LOCK_QUEUE",
  141. "Lock",
  142. LockQueue[Index][j].Lock);
  143. }
  144. }
  145. }
  146. //
  147. // Read the spin lock information for each queued lock.
  148. //
  149. for (Index = 0; Index < LockQueueMaximumLock; Index += 1) {
  150. SpinLock[Index] = 0;
  151. if (LockQueue[0][Index].Lock != 0) {
  152. if (GetFieldValue(LockQueue[0][Index].Lock & ~(LOCK_QUEUE_WAIT | LOCK_QUEUE_OWNER),
  153. "nt!PVOID", // KSPIN_LOCK == ULONG_PTR, this would sign-extens it
  154. NULL,
  155. SpinLock[Index])) {
  156. //
  157. // Spin lock information could not be read for the respective
  158. // queued lock.
  159. //
  160. dprintf("Unable to read spin lock information for queued lock %d\n",
  161. Index);
  162. return E_INVALIDARG;
  163. }
  164. }
  165. }
  166. //
  167. // Verify that the kernel spin lock array is not corrupt. Each entry in
  168. // this array should either be zero or contain the address of the correct
  169. // lock queue entry in one of the processor control blocks.
  170. //
  171. Corrupt = FALSE;
  172. for (Index = 0; Index < LockQueueMaximumLock && (LockName[Index].Name != NULL); Index += 1) {
  173. if (SpinLock[Index] != 0) {
  174. if (ProcessorIndex(SpinLock[Index], Index) == 0) {
  175. Corrupt = TRUE;
  176. dprintf("Kernel spin lock %s is corrupt.\n", LockName[Index].Name);
  177. }
  178. }
  179. }
  180. //
  181. // Verify that all lock queue entries are not corrupt. Each lock queue
  182. // entry should either have a next field of NULL of contain the address
  183. // of the correct lock queue entry in one of the processor control blocks.
  184. //
  185. for (Loop = 0; Loop < NUMBER_PROCESSORS; Loop += 1) {
  186. for (Index = 0; Index < LockQueueMaximumLock; Index += 1) {
  187. if (LockQueue[Loop][Index].Next != 0) {
  188. if (ProcessorIndex(LockQueue[Loop][Index].Next, Index) == 0) {
  189. Corrupt = TRUE;
  190. dprintf("Lock entry %d for processor %d is corrupt\n",
  191. Index,
  192. Loop);
  193. }
  194. }
  195. }
  196. }
  197. if (Corrupt != FALSE) {
  198. return E_INVALIDARG;
  199. }
  200. //
  201. // Output key information and headings.
  202. //
  203. dprintf("Key: O = Owner, 1-n = Wait order, blank = not owned/waiting, C = Corrupt\n\n");
  204. dprintf(" Processor Number\n");
  205. dprintf(" Lock Name ");
  206. for (Index = 0; Index <= HighestProcessor; Index++) {
  207. dprintf("%3d", Index);
  208. }
  209. dprintf("\n\n");
  210. //
  211. // Process each queued lock and output owner information.
  212. //
  213. for (Index = 0; Index < LockQueueMaximumLock && (LockName[Index].Name != NULL); Index += 1) {
  214. if (Index != (ULONG) LockName[Index].Number) {
  215. dprintf("ERROR: extension bug: name array does not match queued lock list!\n");
  216. break;
  217. }
  218. dprintf("%s", LockName[Index].Name);
  219. //
  220. // If the lock is owned, then find the owner and any waiters. Output
  221. // the owner and waiters in order.
  222. //
  223. // If the lock is not owned, then check the consistency of lock queue
  224. // entries. They should all contain next pointer of NULL and both the
  225. // owner and wait flags should be clear.
  226. //
  227. RtlFillMemory(&Key[0], NUMBER_PROCESSORS, KEY_NOTHING);
  228. if (SpinLock[Index] != 0) {
  229. LockOwner = NULL;
  230. for (Loop = 0; Loop < NUMBER_PROCESSORS; Loop += 1) {
  231. if (LockQueue[Loop][Index].Lock & LOCK_QUEUE_OWNER) {
  232. LockOwner = &LockQueue[Loop][Index];
  233. break;
  234. }
  235. }
  236. //
  237. // If the lock owner was not found, then assume that the kernel
  238. // spin lock points to the owner and the owner bit has not been
  239. // set yet. Otherwise, fill out the owner/wait key array.
  240. //
  241. if (LockOwner == NULL) {
  242. Number = ProcessorIndex(SpinLock[Index], Index);
  243. Key[Number - 1] = KEY_OWNER;
  244. //
  245. // The owner processor has been determined by the kernel
  246. // spin lock address. Check to determine if any of the
  247. // lock queue entries are corrupt and fill in the key
  248. // array accordingly. A corrupt lock queue entry is one
  249. // that has a non NULL next field or one of the owner or
  250. // wait flags is set.
  251. //
  252. for (Loop = 0; Loop < NUMBER_PROCESSORS; Loop += 1) {
  253. if ((LockQueue[Loop][Index].Next != 0) ||
  254. (LockQueue[Loop][Index].Lock & (LOCK_QUEUE_WAIT | LOCK_QUEUE_OWNER))) {
  255. Key[Loop] = KEY_CORRUPT;
  256. }
  257. }
  258. } else {
  259. //
  260. // The lock owner was found. Attempt to construct the wait
  261. // chain.
  262. //
  263. Key[Loop] = KEY_OWNER;
  264. Last = Loop;
  265. Sequence = 0;
  266. while (LockOwner->Next != 0) {
  267. Number = ProcessorIndex(LockOwner->Next, Index);
  268. if (Key[Number - 1] == KEY_NOTHING) {
  269. Last = Number - 1;
  270. Sequence += 1;
  271. Key[Last] = Sequence;
  272. LockOwner = &LockQueue[Last][Index];
  273. } else {
  274. //
  275. // The wait chain loops back on itself. Mark the
  276. // entry as corrupt and scan the other entries to
  277. // detemine if they are also corrupt.
  278. //
  279. Key[Last] = KEY_CORRUPT;
  280. for (Loop = 0; Loop < NUMBER_PROCESSORS; Loop += 1) {
  281. if ((LockQueue[Loop][Index].Next != 0) ||
  282. (LockQueue[Loop][Index].Lock & (LOCK_QUEUE_WAIT | LOCK_QUEUE_OWNER))) {
  283. if (Key[Loop] == KEY_NOTHING) {
  284. Key[Loop] = KEY_CORRUPT;
  285. }
  286. }
  287. }
  288. break;
  289. }
  290. }
  291. //
  292. // If the lock owner next field is NULL, then the wait
  293. // search ended normally. Check to determine if the kernel
  294. // spin lock points to the last entry in the queue.
  295. //
  296. if (LockOwner->Next == 0) {
  297. Number = ProcessorIndex(SpinLock[Index], Index);
  298. if (Last != (Number - 1)) {
  299. Sequence += 1;
  300. Key[Number - 1] = Sequence;
  301. }
  302. }
  303. }
  304. } else {
  305. //
  306. // The kernel spin lock is not owned. Check to determine if any
  307. // of the lock queue entries are corrupt and fill in the key
  308. // array accordingly. A corrupt entry is one that has a non NULL
  309. // next field or one of the owner or wait flags is set.
  310. //
  311. for (Loop = 0; Loop < NUMBER_PROCESSORS; Loop += 1) {
  312. if ((LockQueue[Loop][Index].Next != 0) ||
  313. (LockQueue[Loop][Index].Lock & (LOCK_QUEUE_WAIT | LOCK_QUEUE_OWNER))) {
  314. Key[Loop] = KEY_CORRUPT;
  315. }
  316. }
  317. }
  318. for (Processor = 0; Processor <= HighestProcessor; Processor++) {
  319. switch (Key[Processor]) {
  320. case KEY_CORRUPT:
  321. dprintf(" C");
  322. break;
  323. case KEY_OWNER:
  324. dprintf(" O");
  325. break;
  326. case KEY_NOTHING:
  327. dprintf(" ");
  328. break;
  329. default:
  330. dprintf("%3d", Key[Processor]);
  331. break;
  332. }
  333. }
  334. dprintf("\n");
  335. }
  336. dprintf("\n");
  337. return S_OK;
  338. }
  339. ULONG
  340. ProcessorIndex (
  341. ULONG64 LockAddress,
  342. ULONG LockIndex
  343. )
  344. /*++
  345. Routine Description:
  346. This function computes the processor number of the respective processor
  347. given a lock queue address and the lock queue index.
  348. Arguments:
  349. LockQueue - Supplies a lock queue address in target memory.
  350. Return Value:
  351. Zero is returned if a matching processor is not found. Otherwise, the
  352. processor number plus one is returned.
  353. --*/
  354. {
  355. ULONG64 LockBase;
  356. ULONG Loop;
  357. ULONG SizeOfKprcb = GetTypeSize("nt!KPRCB");
  358. ULONG SizeOfQ = GetTypeSize("nt!KSPIN_LOCK_QUEUE");
  359. ULONG LockQueueOffset;
  360. if (GetFieldOffset("nt!KPRCB", "LockQueue", &LockQueueOffset)) {
  361. dprintf("Unable to read KPRCB type.\n");
  362. return 0;
  363. }
  364. //
  365. // Attempt to find the lock address in one of the processor control
  366. // blocks.
  367. //
  368. for (Loop = 0; Loop < NUMBER_PROCESSORS; Loop += 1) {
  369. if ((LockAddress >= ProcessorBlock[Loop]) &&
  370. (LockAddress < ProcessorBlock[Loop] + SizeOfKprcb)) {
  371. LockBase = ProcessorBlock[Loop] + LockQueueOffset;
  372. if (LockAddress == (LockBase + SizeOfQ * LockIndex)) {
  373. return Loop + 1;
  374. }
  375. }
  376. }
  377. return 0;
  378. }
  379. PUCHAR QueuedLockName[] = {
  380. "DispatcherLock",
  381. "ContextSwapLock",
  382. "PfnLock",
  383. "SystemSpaceLock",
  384. "VacbLock",
  385. "MasterLock",
  386. "NonPagedPoolLock",
  387. "IoCancelLock",
  388. "WorkQueueLock",
  389. "IoVpbLock",
  390. "IoDatabaseLock",
  391. "IoCompletionLock",
  392. "NtfsStructLock",
  393. "AfdWorkQueueLock",
  394. "BcbLock"
  395. };
  396. DECLARE_API( qlockperf )
  397. /*++
  398. Routine Description:
  399. Displays queued spin lock performance data (if present).
  400. Arguments:
  401. None.
  402. Return Value:
  403. None.
  404. --*/
  405. {
  406. //
  407. // The following structure is used to accumulate data about each
  408. // acquire/release pair for a lock.
  409. //
  410. typedef struct {
  411. union {
  412. ULONGLONG Key;
  413. struct {
  414. ULONG_PTR Releaser;
  415. ULONG_PTR Acquirer;
  416. };
  417. };
  418. ULONGLONG Time;
  419. ULONGLONG WaitTime;
  420. ULONG Count;
  421. ULONG Waiters;
  422. ULONG Depth;
  423. ULONG IncreasedDepth;
  424. ULONG Clean;
  425. } QLOCKDATA, *PQLOCKDATA;
  426. //
  427. // House keeping data for each lock.
  428. //
  429. typedef struct {
  430. //
  431. // The following fields are used to keep data from acquire
  432. // to release.
  433. //
  434. ULONGLONG AcquireTime;
  435. ULONGLONG WaitToAcquire;
  436. ULONG_PTR AcquirePoint;
  437. BOOLEAN Clean;
  438. //
  439. // Remaining fields accumulate global stats for this lock.
  440. //
  441. ULONG Count;
  442. ULONG Pairs;
  443. ULONG FailedTry;
  444. UCHAR MaxDepth;
  445. UCHAR PreviousDepth;
  446. ULONG NoWait;
  447. } QLOCKHOUSE, *PQLOCKHOUSE;
  448. ULONG64 TargetHouse;
  449. ULONG64 TargetLog;
  450. PQLOCKHOUSE LockHome;
  451. PQLOCKDATA LockData;
  452. QLOCKDATA TempEntry;
  453. ULONG LogEntrySize;
  454. ULONG LogSize;
  455. ULONG HouseEntrySize;
  456. ULONG HouseSize;
  457. ULONG NumberOfLocks;
  458. ULONG LockIndex;
  459. ULONG i, j;
  460. ULONG MaxEntriesPerLock;
  461. ULONG HighIndex;
  462. ULONGLONG HighTime;
  463. ULONGLONG TotalHoldTime;
  464. ULONG PercentageHeld;
  465. ULONG64 AcquiredAddress;
  466. ULONG64 ReleasedAddress;
  467. UCHAR AcquirePoint[80];
  468. UCHAR ReleasePoint[80];
  469. ULONG64 AcquireOffset;
  470. ULONG64 ReleaseOffset;
  471. BOOLEAN Verbose = FALSE;
  472. BOOLEAN Columnar = FALSE;
  473. BOOLEAN Interesting = FALSE;
  474. ULONG LockLow, LockHigh;
  475. //
  476. // First, see if we can do anything useful.
  477. //
  478. // For the moment, this is x86 only.
  479. //
  480. if (TargetMachine != IMAGE_FILE_MACHINE_I386) {
  481. dprintf("Sorry, don't know how to gather queued spinlock performance\n",
  482. "data on anything but an x86.\n");
  483. return E_INVALIDARG;
  484. }
  485. //
  486. // Parse arguments.
  487. //
  488. if (strstr(args, "?")) {
  489. //
  490. // Has asked for usage information. Give them the options
  491. // and an explanation of the output.
  492. //
  493. dprintf("usage: qlockperf [-v] [n]\n"
  494. " -v indicates verbose output (see below).\n"
  495. " -c verbose columnar output.\n"
  496. " -ci verbose columnar output, no totals.\n"
  497. " n supplies the lock number (default is all)\n\n"
  498. "Verbose output includes details of each lock acquire and\n"
  499. "release pair. Two lines per pair.\n\n"
  500. "Line 1: ppp A symbolic_address R symbolic_address\n"
  501. " ppp percentage, this pair for this lock (overall)\n"
  502. " A Acquire point\n"
  503. " R Release point\n\n"
  504. "Line 2:\n"
  505. " HT Hold Time total (average)\n"
  506. " This is the time from acquire to release.\n"
  507. " WT Wait Time total (average)\n"
  508. " This is the time waiting to acquire.\n"
  509. " C Count\n"
  510. " Number of times this pair occured.\n"
  511. " CA Clean Acquires (percentage)\n"
  512. " Number of times acquire did not wait.\n"
  513. " WC Waiter Count\n"
  514. " Number of processors waiting for this\n"
  515. " lock at release.\n"
  516. " avD Average number of waiters (at release).\n"
  517. " ID Increased Depth\n"
  518. " Number of times the queue length increased\n"
  519. " while the lock was held in this pair.\n"
  520. );
  521. return E_INVALIDARG;
  522. }
  523. if (strstr(args, "-c")) {
  524. Verbose = TRUE;
  525. Columnar = TRUE;
  526. }
  527. if (strstr(args, "-ci")) {
  528. Interesting = TRUE;
  529. }
  530. if (strstr(args, "-v")) {
  531. Verbose = TRUE;
  532. }
  533. LockLow = 0;
  534. LockHigh = 999;
  535. for (i = 0; args[i]; i++) {
  536. if ((args[i] >= '0') && (args[i] <= '9')) {
  537. LockLow = (ULONG)GetExpression(&args[i]);
  538. LockHigh = LockLow;
  539. }
  540. }
  541. TargetHouse = GetExpression("nt!KiQueuedSpinLockHouse");
  542. //
  543. // Checking for control C after the first operation that might
  544. // cause symbol load in case the user has bad symbols and is
  545. // trying to get out.
  546. //
  547. if (CheckControlC()) {
  548. return E_ABORT;
  549. }
  550. TargetLog = GetExpression("nt!KiQueuedSpinLockLog");
  551. LogEntrySize = GetTypeSize("nt!QLOCKDATA");
  552. LogSize = GetTypeSize("nt!KiQueuedSpinLockLog");
  553. HouseEntrySize = GetTypeSize("nt!QLOCKHOUSE");
  554. HouseSize = GetTypeSize("nt!KiQueuedSpinLockHouse");
  555. if (!(TargetHouse &&
  556. TargetLog &&
  557. LogEntrySize &&
  558. LogSize &&
  559. HouseEntrySize &&
  560. HouseSize)) {
  561. dprintf("Sorry, can't find required system data, perhaps this kernel\n",
  562. "was not built with QLOCK_STAT_GATHER defined?\n");
  563. return E_INVALIDARG;
  564. }
  565. if ((LogEntrySize != sizeof(QLOCKDATA)) ||
  566. (HouseEntrySize != sizeof(QLOCKHOUSE))) {
  567. dprintf("Structure sizes in the kernel and debugger extension don't\n",
  568. "match. This extension needs to be rebuild to match the\n",
  569. "running system.\n");
  570. return E_INVALIDARG;
  571. }
  572. NumberOfLocks = HouseSize / HouseEntrySize;
  573. MaxEntriesPerLock = LogSize / LogEntrySize / NumberOfLocks;
  574. dprintf("Kernel build with %d PRCB queued spinlocks\n", NumberOfLocks);
  575. dprintf("(maximum log entries per lock = %d)\n", MaxEntriesPerLock);
  576. if (LockHigh >= NumberOfLocks) {
  577. if (LockLow == LockHigh) {
  578. dprintf("User requested lock %d, system has only %d locks, quitting.\n",
  579. LockLow,
  580. NumberOfLocks);
  581. return E_INVALIDARG;
  582. }
  583. LockHigh = NumberOfLocks - 1;
  584. }
  585. if (NumberOfLocks > 16) {
  586. //
  587. // I don't believe it.
  588. //
  589. dprintf("The number of locks doesn't seem reasonable, giving up.\n");
  590. return E_INVALIDARG;
  591. }
  592. if (CheckControlC()) {
  593. return E_ABORT;
  594. }
  595. //
  596. // Allocate space to process the data for one lock at a time.
  597. //
  598. LockHome = LocalAlloc(LPTR, sizeof(*LockHome));
  599. LockData = LocalAlloc(LPTR, sizeof(*LockData) * MaxEntriesPerLock);
  600. if (!(LockHome && LockData)) {
  601. dprintf("Couldn't allocate memory for local copies of kernel data.\n",
  602. "unable to continue.\n");
  603. goto outtahere;
  604. }
  605. for (LockIndex = LockLow; LockIndex <= LockHigh; LockIndex++) {
  606. if ((!ReadMemory(TargetHouse + (LockIndex * sizeof(QLOCKHOUSE)),
  607. LockHome,
  608. sizeof(QLOCKHOUSE),
  609. &i)) || (i < sizeof(QLOCKHOUSE))) {
  610. dprintf("unable to read data for lock %d, quitting\n",
  611. LockIndex);
  612. return E_INVALIDARG;
  613. }
  614. if (CheckControlC()) {
  615. goto outtahere;
  616. }
  617. if (LockHome->Pairs == 0) {
  618. continue;
  619. }
  620. dprintf("\nLock %d %s\n", LockIndex, QueuedLockName[LockIndex]);
  621. dprintf(" Acquires %d (%d pairs)\n", LockHome->Count, LockHome->Pairs);
  622. dprintf(" Failed Tries %d\n", LockHome->FailedTry);
  623. dprintf(" Maximum Depth (at release) %d\n", LockHome->MaxDepth);
  624. dprintf(" No Waiters (at acquire) %d (%d%%)\n",
  625. LockHome->NoWait,
  626. LockHome->NoWait * 100 / LockHome->Count);
  627. //
  628. // Change the following to a parameter saying we want the
  629. // details.
  630. //
  631. if (Verbose) {
  632. ULONG Entries = LockHome->Pairs;
  633. PQLOCKDATA Entry;
  634. if ((!ReadMemory(TargetLog + (LockIndex * MaxEntriesPerLock * sizeof(QLOCKDATA)),
  635. LockData,
  636. Entries * sizeof(QLOCKDATA),
  637. &i)) || (i < (Entries * sizeof(QLOCKDATA)))) {
  638. dprintf("unable to read data for lock %d, quitting\n",
  639. LockIndex);
  640. return E_INVALIDARG;
  641. }
  642. if (CheckControlC()) {
  643. goto outtahere;
  644. }
  645. //
  646. // Sort table into longest duration.
  647. //
  648. TotalHoldTime = 0;
  649. for (i = 0; i < (Entries - 1); i++) {
  650. HighTime = LockData[i].Time;
  651. HighIndex = i;
  652. for (j = i + 1; j < Entries; j++) {
  653. if (LockData[j].Time > HighTime) {
  654. HighIndex = j;
  655. HighTime = LockData[j].Time;
  656. }
  657. }
  658. if (HighIndex != i) {
  659. //
  660. // Swap entries.
  661. //
  662. TempEntry = LockData[i];
  663. LockData[i] = LockData[HighIndex];
  664. LockData[HighIndex] = TempEntry;
  665. }
  666. TotalHoldTime += LockData[i].Time;
  667. }
  668. TotalHoldTime += LockData[Entries-1].Time;
  669. dprintf(" Total time held %I64ld\n");
  670. //
  671. // Print something!
  672. //
  673. if (Interesting) {
  674. dprintf("\n Average Average Count %% Av. %%\n"
  675. " %% Hold Wait 0w Dp Con\n");
  676. } else if (Columnar) {
  677. dprintf("\n Total Average Total Average Count Clean %% Waiters Av Increased %%\n"
  678. " %% Hold Hold Wait Wait 0w Dp Con\n");
  679. }
  680. for (i = 0; i < Entries; i++) {
  681. if (CheckControlC()) {
  682. goto outtahere;
  683. }
  684. Entry = &LockData[i];
  685. //
  686. // Sign extend if necessary.
  687. //
  688. if (!IsPtr64()) {
  689. AcquiredAddress = (ULONG64)(LONG64)(LONG)Entry->Acquirer;
  690. ReleasedAddress = (ULONG64)(LONG64)(LONG)Entry->Releaser;
  691. }
  692. //
  693. // Lookup the symbolic addresses.
  694. //
  695. GetSymbol(AcquiredAddress, AcquirePoint, &AcquireOffset);
  696. GetSymbol(ReleasedAddress, ReleasePoint, &ReleaseOffset);
  697. PercentageHeld = (ULONG)(Entry->Time * 100 / TotalHoldTime);
  698. if (Interesting) {
  699. dprintf("%3d%9d%9d%10d%4d%3d%4d %s+0x%x %s+0x%x\n",
  700. PercentageHeld,
  701. (ULONG)(Entry->Time / Entry->Count),
  702. (ULONG)(Entry->WaitTime / Entry->Count),
  703. Entry->Count,
  704. Entry->Clean * 100 / Entry->Count,
  705. Entry->Depth / Entry->Count,
  706. Entry->IncreasedDepth * 100 / Entry->Count,
  707. AcquirePoint, (ULONG)AcquireOffset,
  708. ReleasePoint, (ULONG)ReleaseOffset);
  709. } else if (Columnar) {
  710. dprintf("%3d %20I64ld%9d%20I64ld%9d",
  711. PercentageHeld,
  712. Entry->Time,
  713. (ULONG)(Entry->Time / Entry->Count),
  714. Entry->WaitTime,
  715. (ULONG)(Entry->WaitTime / Entry->Count));
  716. dprintf("%10d%10d%4d%10d%3d%10d%4d %s+0x%x %s+0x%x\n",
  717. Entry->Count,
  718. Entry->Clean,
  719. Entry->Clean * 100 / Entry->Count,
  720. Entry->Waiters,
  721. Entry->Depth / Entry->Count,
  722. Entry->IncreasedDepth,
  723. Entry->IncreasedDepth * 100 / Entry->Count,
  724. AcquirePoint, (ULONG)AcquireOffset,
  725. ReleasePoint, (ULONG)ReleaseOffset);
  726. } else {
  727. dprintf("%3d A %s+0x%x R %s+0x%x\n",
  728. PercentageHeld,
  729. AcquirePoint, (ULONG)AcquireOffset,
  730. ReleasePoint, (ULONG)ReleaseOffset);
  731. dprintf(" HT %I64ld (av %I64ld), WT %I64ld (av %I64ld), C %d, CA %d (%d%%) WC %d, (avD %d) ID %d (%d%%)\n",
  732. Entry->Time,
  733. Entry->Time / Entry->Count,
  734. Entry->WaitTime,
  735. Entry->WaitTime / Entry->Count,
  736. Entry->Count,
  737. Entry->Clean,
  738. Entry->Clean * 100 / Entry->Count,
  739. Entry->Waiters,
  740. Entry->Depth / Entry->Count,
  741. Entry->IncreasedDepth,
  742. Entry->IncreasedDepth * 100 / Entry->Count);
  743. dprintf("\n");
  744. }
  745. }
  746. }
  747. }
  748. outtahere:
  749. if (LockHome) {
  750. LocalFree(LockHome);
  751. }
  752. if (LockData) {
  753. LocalFree(LockData);
  754. }
  755. return S_OK;
  756. }