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.

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