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.

674 lines
21 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. locks.c
  5. Abstract:
  6. WinDbg Extension Api
  7. Author:
  8. Ramon J San Andres (ramonsa) 5-Nov-1993
  9. Environment:
  10. User Mode.
  11. Revision History:
  12. --*/
  13. #include "precomp.h"
  14. #pragma hdrstop
  15. DECLARE_API( locks )
  16. /*++
  17. Routine Description:
  18. Dump kernel mode resource locks
  19. Arguments:
  20. arg - [-V] [-P] [Address]
  21. Return Value:
  22. None
  23. --*/
  24. {
  25. UCHAR Buffer[256];
  26. LONG ActiveCount;
  27. ULONG ContentionCount;
  28. ULONG64 Displacement;
  29. BOOLEAN DisplayZero;
  30. ULONG64 End;
  31. USHORT Flag;
  32. ULONG Index;
  33. USHORT NumberOfExclusiveWaiters;
  34. USHORT NumberOfSharedWaiters;
  35. BOOLEAN Performance;
  36. ULONG64 PerformanceData;
  37. ULONG TableSize;
  38. ULONG64 ResourceHead;
  39. ULONG64 Next;
  40. ULONG Result;
  41. ULONG64 ResourceToDump;
  42. ULONG64 Resource;
  43. ULONG64 DdkResource;
  44. ULONG i;
  45. ULONG j;
  46. ULONG64 Thread;
  47. LONG ThreadCount;
  48. UCHAR DdkThreadCount;
  49. BOOLEAN Verbose;
  50. PUCHAR s;
  51. ULONG TotalLocks;
  52. ULONG TotalUsedLocks;
  53. ULONG SkippedLocks;
  54. ULONG SizeOfListEntry, SizeofOwnerEntry;
  55. ULONG InitialOwnerThreadsOffset, OwnerThreadsOffset;
  56. ULONG dwProcessor=0;
  57. HRESULT hr = S_OK;
  58. INIT_API();
  59. GetCurrentProcessor(Client, &dwProcessor, NULL);
  60. ResourceToDump = 0;
  61. DisplayZero = FALSE;
  62. Performance = FALSE;
  63. Verbose = FALSE;
  64. s = (PSTR)args;
  65. while ( s != NULL && *s ) {
  66. if (*s == '-' || *s == '/') {
  67. while (*++s) {
  68. switch (*s) {
  69. case 'D':
  70. case 'd':
  71. DisplayZero = TRUE;
  72. break;
  73. case 'P':
  74. case 'p':
  75. Performance = TRUE;
  76. break;
  77. case 'V':
  78. case 'v':
  79. Verbose = TRUE;
  80. break;
  81. case ' ':
  82. goto gotBlank;
  83. default:
  84. dprintf( "KD: !locks invalid option flag '-%c'\n", *s );
  85. break;
  86. }
  87. }
  88. } else if (*s != ' ') {
  89. ResourceToDump = GetExpression(s);
  90. s = strpbrk( s, " " );
  91. } else {
  92. gotBlank:
  93. s++;
  94. }
  95. }
  96. //
  97. // Dump performance data if requested.
  98. //
  99. if (Performance != FALSE) {
  100. UCHAR ResPerf[]="nt!_RESOURCE_PERFORMANCE_DATA";
  101. ULONG TotalResourceCount, ActiveResourceCount, ExclusiveAcquire;
  102. ULONG SharedFirstLevel, SharedSecondLevel, StarveFirstLevel, StarveSecondLevel;
  103. ULONG WaitForExclusive, OwnerTableExpands, MaximumTableExpand;
  104. ULONG HashTableOffset;
  105. dprintf("**** Dump Resource Performance Data ****\n\n");
  106. PerformanceData = GetExpression("nt!ExpResourcePerformanceData");
  107. if ((PerformanceData == 0) ||
  108. GetFieldValue(PerformanceData, ResPerf,"TotalResourceCount",TotalResourceCount)) {
  109. //
  110. // The target build does not support resource performance data.
  111. //
  112. dprintf("%08p: No resource performance data available\n", PerformanceData);
  113. } else {
  114. GetFieldOffset(ResPerf, "HashTable", &HashTableOffset);
  115. GetFieldValue(PerformanceData, ResPerf, "ActiveResourceCount", ActiveResourceCount);
  116. GetFieldValue(PerformanceData, ResPerf,"ExclusiveAcquire", ExclusiveAcquire);
  117. GetFieldValue(PerformanceData, ResPerf, "SharedFirstLevel", SharedFirstLevel);
  118. GetFieldValue(PerformanceData, ResPerf,"SharedSecondLevel", SharedSecondLevel);
  119. GetFieldValue(PerformanceData, ResPerf, "StarveFirstLevel", StarveFirstLevel);
  120. GetFieldValue(PerformanceData, ResPerf, "StarveSecondLevel", StarveSecondLevel);
  121. GetFieldValue(PerformanceData, ResPerf, "WaitForExclusive", WaitForExclusive);
  122. GetFieldValue(PerformanceData, ResPerf, "OwnerTableExpands", OwnerTableExpands);
  123. GetFieldValue(PerformanceData, ResPerf, "MaximumTableExpand", MaximumTableExpand);
  124. //
  125. // Output the summary statistics.
  126. //
  127. dprintf("Total resources initialized : %u\n",
  128. TotalResourceCount);
  129. dprintf("Currently active resources : %u\n",
  130. ActiveResourceCount);
  131. dprintf("Exclusive resource acquires : %u\n",
  132. ExclusiveAcquire);
  133. dprintf("Shared resource acquires (fl) : %u\n",
  134. SharedFirstLevel);
  135. dprintf("Shared resource acquires (sl) : %u\n",
  136. SharedSecondLevel);
  137. dprintf("Starve resource acquires (fl) : %u\n",
  138. StarveFirstLevel);
  139. dprintf("Starve resource acquires (sl) : %u\n",
  140. StarveSecondLevel);
  141. dprintf("Shared wait resource acquires : %u\n",
  142. WaitForExclusive);
  143. dprintf("Owner table expansions : %u\n",
  144. OwnerTableExpands);
  145. dprintf("Maximum table expansion : %u\n\n",
  146. MaximumTableExpand);
  147. //
  148. // Dump the inactive resource statistics.
  149. //
  150. dprintf(" Inactive Resource Statistics\n");
  151. dprintf("Contention Number Initialization Address\n\n");
  152. SizeOfListEntry = GetTypeSize("nt!_LIST_ENTRY");
  153. for (Index = 0; Index < RESOURCE_HASH_TABLE_SIZE; Index += 1) {
  154. End = HashTableOffset + PerformanceData + SizeOfListEntry * Index;
  155. GetFieldValue(End,"nt!_LIST_ENTRY","Flink",Next);
  156. while (Next != End) {
  157. ULONG64 Address;
  158. ULONG ContentionCount, Number;
  159. if (CheckControlC()) {
  160. break;
  161. }
  162. if (!GetFieldValue(Next,
  163. "nt!_RESOURCE_HASH_ENTRY",
  164. "Address",
  165. Address)) {
  166. GetSymbol(Address, Buffer, &Displacement);
  167. GetFieldValue(Next,"nt!_RESOURCE_HASH_ENTRY","Number",Number);
  168. GetFieldValue(Next,"nt!_RESOURCE_HASH_ENTRY","ContentionCount",ContentionCount);
  169. dprintf("%10d %6d %s",
  170. ContentionCount,
  171. Number,
  172. Buffer);
  173. if (Displacement != 0) {
  174. dprintf("+0x%x", Displacement);
  175. }
  176. dprintf("\n");
  177. }
  178. GetFieldValue(Next,"nt!_RESOURCE_HASH_ENTRY","ListEntry.Flink", Next);
  179. }
  180. }
  181. //
  182. // Dump the active resource statistics.
  183. //
  184. dprintf("\n Active Resource Statistics\n");
  185. dprintf("Resource Contention Initialization Address\n\n");
  186. //
  187. // Read the resource listhead and check if it is empty.
  188. //
  189. ResourceHead = GetNtDebuggerData( ExpSystemResourcesList );
  190. if ((ResourceHead == 0) ||
  191. (!GetFieldValue(ResourceHead,
  192. "nt!_LIST_ENTRY",
  193. "Flink",
  194. Next) == FALSE)) {
  195. dprintf("%08p: Unable to get value of ExpSystemResourcesList\n", ResourceHead );
  196. hr = E_INVALIDARG;
  197. goto exitBangLocks;
  198. }
  199. if (Next == 0) {
  200. dprintf("ExpSystemResourcesList is NULL!\n");
  201. hr = E_INVALIDARG;
  202. goto exitBangLocks;
  203. }
  204. //
  205. // Scan the resource list and dump the resource information.
  206. //
  207. while(Next != ResourceHead) {
  208. ULONG64 Address;
  209. if (CheckControlC()) {
  210. break;
  211. }
  212. Resource = Next; // SystemResourcesList is the first element in struct
  213. // CONTAINING_RECORD(Next, ERESOURCE, SystemResourcesList);
  214. if (!GetFieldValue(Resource,
  215. "nt!_ERESOURCE",
  216. "ContentionCount",
  217. ContentionCount) == FALSE) {
  218. dprintf("%08p: Unable to read _ERESOURCE\n", Resource);
  219. continue;
  220. } else {
  221. GetFieldValue(Resource,"nt!_ERESOURCE","Address",Address);
  222. GetFieldValue(Resource,"nt!_ERESOURCE","ContentionCount",ContentionCount);
  223. if ((ContentionCount != 0) ||
  224. (DisplayZero != FALSE)) {
  225. GetSymbol(Address,
  226. Buffer,
  227. &Displacement);
  228. dprintf("%08p %10d %s",
  229. Resource,
  230. ContentionCount,
  231. Buffer);
  232. if (Displacement != 0) {
  233. dprintf("+0x%x", Displacement);
  234. }
  235. dprintf("\n");
  236. }
  237. }
  238. GetFieldValue(Resource,"nt!_ERESOURCE","SystemResourcesList.Flink",Next);
  239. }
  240. dprintf("\n");
  241. //
  242. // Dump the active fast mutex statistics.
  243. //
  244. dprintf("\n Active Fast Mutex Statistics\n");
  245. dprintf("Address Contention Fast Mutex Name\n\n");
  246. //
  247. // Dump statistics for static fast mutexes.
  248. //
  249. DumpStaticFastMutex("CmpKcbLock");
  250. DumpStaticFastMutex("FsRtlCreateLockInfo");
  251. DumpStaticFastMutex("MmPageFileCreationLock");
  252. DumpStaticFastMutex("MmSectionCommitMutex");
  253. DumpStaticFastMutex("MmSectionBasedMutex");
  254. DumpStaticFastMutex("ObpRootDirectoryMutex");
  255. DumpStaticFastMutex("PspActiveProcessMutex");
  256. DumpStaticFastMutex("PspProcessLockMutex");
  257. DumpStaticFastMutex("PspProcessSecurityLock");
  258. DumpStaticFastMutex("SepLsaQueueLock");
  259. dprintf("\n");
  260. }
  261. hr = E_INVALIDARG;
  262. goto exitBangLocks;
  263. }
  264. //
  265. // Dump remaining lock data.
  266. //
  267. if (ResourceToDump == 0) {
  268. dprintf("**** DUMP OF ALL RESOURCE OBJECTS ****\n");
  269. ResourceHead = GetNtDebuggerData( ExpSystemResourcesList );
  270. if ( !ResourceHead ||
  271. (GetFieldValue(ResourceHead,
  272. "nt!_LIST_ENTRY",
  273. "Flink",
  274. Next) != FALSE)) {
  275. dprintf("%08p: Unable to get value of ExpSystemResourcesList\n", ResourceHead );
  276. hr = E_INVALIDARG;
  277. goto exitBangLocks;
  278. }
  279. if (Next == 0) {
  280. dprintf("ExpSystemResourcesList is NULL!\n");
  281. hr = E_INVALIDARG;
  282. goto exitBangLocks;
  283. }
  284. } else {
  285. Next = 0;
  286. ResourceHead = 1;
  287. }
  288. TotalLocks = 0;
  289. TotalUsedLocks = 0;
  290. SkippedLocks = 0;
  291. // Get the offset of OwnerThreads in ERESOURCE
  292. if (GetFieldOffset("nt!_ERESOURCE", "OwnerThreads", &OwnerThreadsOffset)) {
  293. dprintf("Cannot get _ERESOURCE type\n");
  294. hr = E_INVALIDARG;
  295. goto exitBangLocks;
  296. }
  297. if (!(SizeofOwnerEntry = GetTypeSize("nt!_OWNER_ENTRY"))) {
  298. dprintf("Cannot get nt!_OWNER_ENTRY type\n");
  299. hr = E_INVALIDARG;
  300. goto exitBangLocks;
  301. }
  302. while(Next != ResourceHead) {
  303. ULONG64 OwnerThreads, OwnerCounts, OwnerTable;
  304. if (Next != 0) {
  305. Resource = Next;// SystemResourcesList is the first element of struct ERESOURCE
  306. // CONTAINING_RECORD(Next,ERESOURCE,SystemResourcesList);
  307. } else {
  308. Resource = ResourceToDump;
  309. }
  310. /*
  311. if ( GetFieldValue( Resource,
  312. "NTDDK_ERESOURCE",
  313. "OwnerThreads",
  314. OwnerThreads) ) {
  315. dprintf("%08lx: Unable to read NTDDK_ERESOURCE\n", Resource );
  316. break;
  317. }*/
  318. //
  319. // Detect here if this is an NtDdk resource, and behave
  320. // appropriatelty. If the OwnerThreads is a pointer to the initial
  321. // owner threads array (this must take into account that the LOCAL
  322. // data structure is a copy of what's in the remote machine in a
  323. // different address)
  324. //
  325. // DdkResource = (PNTDDK_ERESOURCE)&ResourceContents;
  326. {
  327. DdkResource = 0;
  328. GetFieldValue( Resource,"nt!_ERESOURCE","ActiveCount", ActiveCount);
  329. GetFieldValue( Resource,"nt!_ERESOURCE","ContentionCount",ContentionCount);
  330. GetFieldValue( Resource,"nt!_ERESOURCE","NumberOfExclusiveWaiters",NumberOfExclusiveWaiters);
  331. GetFieldValue( Resource,"nt!_ERESOURCE","NumberOfSharedWaiters",NumberOfSharedWaiters);
  332. GetFieldValue( Resource,"nt!_ERESOURCE","Flag",Flag);
  333. GetFieldValue( Resource,"nt!_ERESOURCE","OwnerTable",OwnerTable);
  334. TableSize = 0;
  335. if (OwnerTable != 0) {
  336. if (GetFieldValue(OwnerTable,
  337. "nt!_OWNER_ENTRY",
  338. "TableSize",
  339. TableSize)) {
  340. dprintf("\n%08p: Unable to read TableSize for resource\n", OwnerTable);
  341. break;
  342. }
  343. }
  344. }
  345. TotalLocks++;
  346. if ((ResourceToDump != 0) || Verbose || (ActiveCount != 0)) {
  347. EXPRLastDump = Resource;
  348. if (SkippedLocks) {
  349. dprintf("\n");
  350. SkippedLocks = 0;
  351. }
  352. dprintf("\n");
  353. dumpSymbolicAddress(Resource, Buffer, TRUE);
  354. dprintf("Resource @ %s", Buffer );
  355. if (ActiveCount == 0) {
  356. dprintf(" Available\n");
  357. } else if (Flag & ResourceOwnedExclusive) {
  358. TotalUsedLocks++;
  359. dprintf(" Exclusively owned\n");
  360. } else {
  361. TotalUsedLocks++;
  362. dprintf(" Shared %u owning threads\n", ActiveCount);
  363. }
  364. if (ContentionCount != 0) {
  365. dprintf(" Contention Count = %u\n", ContentionCount);
  366. }
  367. if (NumberOfSharedWaiters != 0) {
  368. dprintf(" NumberOfSharedWaiters = %u\n", NumberOfSharedWaiters);
  369. }
  370. if (NumberOfExclusiveWaiters != 0) {
  371. dprintf(" NumberOfExclusiveWaiters = %u\n", NumberOfExclusiveWaiters);
  372. }
  373. if (ActiveCount != 0) {
  374. ULONG ThreadType;
  375. j = 0;
  376. dprintf(" Threads: ");
  377. if (DdkResource == 0) {
  378. GetFieldValue( Resource + OwnerThreadsOffset, "nt!_OWNER_ENTRY","OwnerThread",Thread);
  379. GetFieldValue( Resource + OwnerThreadsOffset, "nt!_OWNER_ENTRY","OwnerCount",ThreadCount);
  380. if (Thread != 0) {
  381. j++;
  382. dprintf("%08p-%02x ", Thread, ThreadCount);
  383. if (Thread & 3) {
  384. dprintf("*** Unknown owner, possibly FileSystem");
  385. j=4;
  386. } else if (GetFieldValue(Thread, "nt!_ETHREAD", "Tcb.Header.Type", ThreadType) ||
  387. (ThreadType != ThreadObject)) {
  388. dprintf("*** Invalid thread");
  389. j=4;
  390. }
  391. if (Verbose) {
  392. dprintf("\n\n");
  393. DumpThread(dwProcessor, " ", Thread, 0xf );
  394. }
  395. }
  396. GetFieldValue( Resource + OwnerThreadsOffset +SizeofOwnerEntry,
  397. "nt!_OWNER_ENTRY","OwnerThread",Thread);
  398. GetFieldValue( Resource + OwnerThreadsOffset +SizeofOwnerEntry,
  399. "nt!_OWNER_ENTRY","OwnerCount",ThreadCount);
  400. if (Thread != 0) {
  401. j++;
  402. dprintf("%08p-%02x ", Thread, ThreadCount);
  403. if (Thread & 3) {
  404. dprintf("*** Unknown owner, possibly FileSystem");
  405. j=4;
  406. } else if (GetFieldValue(Thread, "nt!_ETHREAD", "Tcb.Header.Type", ThreadType) ||
  407. (ThreadType != ThreadObject)) {
  408. dprintf("*** Invalid thread");
  409. j=4;
  410. }
  411. if (Verbose) {
  412. dprintf("\n\n");
  413. DumpThread( dwProcessor, " ", Thread, 0xf );
  414. }
  415. }
  416. }
  417. for (i = DdkResource ? 0 : 1; i < TableSize; i++) {
  418. {
  419. GetFieldValue( OwnerTable + SizeofOwnerEntry*i,
  420. "nt!_OWNER_ENTRY","OwnerThread",Thread);
  421. GetFieldValue( OwnerTable + SizeofOwnerEntry*i,
  422. "nt!_OWNER_ENTRY","OwnerCount",ThreadCount);
  423. }
  424. if ((Thread == 0) && (ThreadCount == 0)) {
  425. continue;
  426. }
  427. if (j == 4) {
  428. j = 0;
  429. dprintf("\n ");
  430. }
  431. dprintf("%08p-%02x ", Thread, ThreadCount);
  432. j++;
  433. if (Thread & 3) {
  434. dprintf("*** Unknown owner, possibly FileSystem");
  435. j=4;
  436. } else if (GetFieldValue(Thread, "nt!_ETHREAD", "Tcb.Header.Type", ThreadType) ||
  437. (ThreadType != ThreadObject)) {
  438. dprintf("*** Invalid thread");
  439. j=4;
  440. }
  441. //
  442. // In verbose mode also dump the thread stacks
  443. //
  444. if (Verbose) {
  445. dprintf("\n\n");
  446. DumpThread( dwProcessor, " ", Thread, 0xf );
  447. }
  448. if ( CheckControlC() ) {
  449. hr = E_INVALIDARG;
  450. goto exitBangLocks;
  451. }
  452. }
  453. if (j) {
  454. dprintf("\n");
  455. }
  456. }
  457. } else {
  458. if ((SkippedLocks++ % 32) == 0) {
  459. if (SkippedLocks == 1) {
  460. dprintf("KD: Scanning for held locks." );
  461. } else {
  462. dprintf("." );
  463. }
  464. }
  465. }
  466. if (ResourceToDump != 0) {
  467. break;
  468. }
  469. GetFieldValue( Resource,"nt!_ERESOURCE","SystemResourcesList.Flink", Next);
  470. if ( CheckControlC() ) {
  471. hr = E_INVALIDARG;
  472. goto exitBangLocks;
  473. }
  474. }
  475. if (SkippedLocks) {
  476. dprintf("\n");
  477. }
  478. dprintf( "%u total locks", TotalLocks );
  479. if (TotalUsedLocks) {
  480. dprintf( ", %u locks currently held", TotalUsedLocks );
  481. }
  482. dprintf("\n");
  483. exitBangLocks:
  484. EXIT_API();
  485. return hr;
  486. }
  487. VOID
  488. DumpStaticFastMutex (
  489. IN PCHAR Name
  490. )
  491. /*++
  492. Routine Description:
  493. This function dumps the contention statistics for a fast mutex.
  494. Arguments:
  495. Name - Supplies a pointer to the symbol name for the fast mutex.
  496. Return Value:
  497. None.
  498. --*/
  499. {
  500. ULONG64 FastMutex;
  501. ULONG Contention;
  502. ULONG Result;
  503. //
  504. // Get the address of the fast mutex, read the fast mutex contents,
  505. // and dump the contention data.
  506. //
  507. FastMutex = GetExpression(Name);
  508. if ((FastMutex != 0) &&
  509. (!GetFieldValue(FastMutex,
  510. "nt!_FAST_MUTEX",
  511. "Contention",
  512. Contention))) {
  513. dprintf("%08p %10u %s\n",
  514. FastMutex,
  515. Contention,
  516. &Name[0]);
  517. }
  518. return;
  519. }