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.

1285 lines
43 KiB

  1. //+-----------------------------------------------------------------------------
  2. //
  3. // File: lockperf.cxx
  4. //
  5. // Contents: Implementation of lock monitoring for locks used by ole32.dll
  6. //
  7. // Classes: CLockPerfTracker
  8. //
  9. // History: 20-Dec-98 mprabhu Created
  10. //
  11. //------------------------------------------------------------------------------
  12. #if LOCK_PERF==1
  13. #include <ole2int.h>
  14. #include <lockperf.hxx>
  15. // gbLockPerf == (Is lock monitoring ON?)
  16. // (It is possible to turn this on & off multiple times during a single test run.)
  17. // Ideally, it would be good to turn it OFF after proc-attach, let the test
  18. // run for a while and then turn it ON. This will get the startup skew out of
  19. // the way.
  20. BOOL gbLockPerf = TRUE;
  21. // Lock perf tracker. Gathers perf measurements for all locks.
  22. CLockPerfTracker gLockTracker;
  23. // static member table of the global tracker class.
  24. CFileLineEntry CLockPerfTracker::_FileLineData[MAX_LOCKPERF_FILEENTRY];
  25. // static member table of the global tracker: this keeps a track of shared time.
  26. CLockEntry CLockPerfTracker::_LockData[MAX_LOCKS];
  27. // counts of locks & {file,line} instances tracked.
  28. ULONG CLockPerfTracker::_numLocks;
  29. ULONG CLockPerfTracker::_numCritSecs;
  30. // perf frequency on the system
  31. LARGE_INTEGER CLockPerfTracker::_liFreq;
  32. //Flagged if tables get full
  33. BOOL gbLockPerfOverflow=FALSE;
  34. //Count of entries in the shared table (code critical sections)
  35. LONG glFileLine = 0;
  36. //Strings to used to print report
  37. LPSTR gszLockPerfErr = "##### Error: LockPerfOverFlow!!";
  38. char gszLockPerfBuf[256];
  39. //Used to debug the perf monitoring code.
  40. #define perfPrint(x)
  41. #if DBG==1
  42. #define LockAssert(X) if (!(X)) wsprintfA(gszLockPerfBuf,#X ## "\n"),OutputDebugStringA(gszLockPerfBuf);
  43. #else
  44. #define LockAssert(X)
  45. #endif
  46. // **** Functions to manage the Private TLS used by LockPerf *********
  47. // Heap Handle (copied from tls.cxx)
  48. extern HANDLE g_hHeap;
  49. #define HEAP_SERIALIZE 0
  50. DWORD gTlsLockPerfIndex = 0xFFFFFFFF;
  51. //345678901234567890123456789012345678901234567890123456789012345678901234567890
  52. //+-----------------------------------------------------------------------------
  53. //
  54. // Function: AllocLockPerfPvtTlsData
  55. //
  56. // Synopsis: Allocates the Tls data for a thread (at Thread_Attach)
  57. //
  58. // History: 20-Dec-98 mprabhu Created
  59. //
  60. //------------------------------------------------------------------------------
  61. HRESULT AllocLockPerfPvtTlsData()
  62. {
  63. void *pMem = HeapAlloc(g_hHeap,
  64. HEAP_SERIALIZE,
  65. TLS_LOCKPERF_MAX*sizeof(CTlsLockPerf));
  66. if (!pMem)
  67. {
  68. LockAssert(!"Could not alloc private Tls data for LockPerf");
  69. return E_OUTOFMEMORY;
  70. }
  71. LockAssert(gTlsLockPerfIndex!=0xFFFFFFFF);
  72. memset(pMem, 0, TLS_LOCKPERF_MAX*sizeof(CTlsLockPerf));
  73. TlsSetValue(gTlsLockPerfIndex, pMem);
  74. return S_OK;
  75. }
  76. //+-----------------------------------------------------------------------------
  77. //
  78. // Function: FreeLockPerfPvtTlsData
  79. //
  80. // Synopsis: Frees the Tls data for a thread (at Thread_Detach)
  81. //
  82. // History: 20-Dec-98 mprabhu Created
  83. //
  84. //------------------------------------------------------------------------------
  85. // REVIEW: What about cases when DllMain is not called with Thread_Detach?
  86. void FreeLockPerfPvtTlsData()
  87. {
  88. LockAssert(gTlsLockPerfIndex!=0xFFFFFFFF);
  89. void *pMem = TlsGetValue(gTlsLockPerfIndex);
  90. if (pMem)
  91. {
  92. HeapFree(g_hHeap, HEAP_SERIALIZE, pMem);
  93. }
  94. }
  95. //**** End: TLS functions ********************************************
  96. //+-----------------------------------------------------------------------------
  97. //
  98. // Member: CLockPerfTracker::Init(), public
  99. //
  100. // Synopsis: Initializes lock perf data, marking entries as unused.
  101. // Called during Proc_Attach
  102. //
  103. // History: 20-Dec-98 mprabhu Created
  104. //
  105. //------------------------------------------------------------------------------
  106. HRESULT CLockPerfTracker::Init()
  107. {
  108. QueryPerformanceFrequency(&_liFreq);
  109. // We use lpLockPtr to tell if an entry in these tables is in-use.
  110. for (int i=0;i<MAX_LOCKPERF_FILEENTRY;i++)
  111. {
  112. _FileLineData[i].lpLockPtr = NULL;
  113. }
  114. for (i=0; i<MAX_LOCKS; i++)
  115. {
  116. _LockData[i].lpLockPtr = NULL;
  117. }
  118. _numLocks = 0;
  119. _numCritSecs = 0;
  120. return S_OK;
  121. }
  122. //+-----------------------------------------------------------------------------
  123. //
  124. // Member: CLockPerfTracker::RegisterLock(), public
  125. //
  126. // Synopsis: Must be called by the lock creation function.
  127. // lpLockPtr == this-ptr of the lock (or unique ptr)
  128. // bReadWrite == TRUE for Reader-Writer Locks.
  129. //
  130. // History: 20-Dec-98 mprabhu Created
  131. //
  132. //------------------------------------------------------------------------------
  133. void CLockPerfTracker::RegisterLock(void *lpLockPtr, BOOL bReadWrite)
  134. {
  135. BOOL bDone = FALSE;
  136. int i=0;
  137. while (!bDone)
  138. {
  139. while (_LockData[i].lpLockPtr)
  140. {
  141. i++;
  142. }
  143. LockAssert(i < MAX_LOCKS);
  144. // REVIEW: 64-bit implications of this cast?
  145. bDone = !(InterlockedCompareExchange((LONG *)&_LockData[i].lpLockPtr,
  146. (LONG)lpLockPtr,
  147. NULL));
  148. }
  149. _LockData[i].dwTotalWriteWaitTime = 0;
  150. _LockData[i].dwTotalWriteEntrys = 0;
  151. _LockData[i].dwTotalWriteLockTime = 0;
  152. _LockData[i].pszLockName = NULL;
  153. // REVIEW: These 5 could be skipped for non reader-writer locks?
  154. _LockData[i].dwSharedTime = 0;
  155. _LockData[i].dwNumReaders = 0;
  156. _LockData[i].dwTotalReadWaitTime = 0;
  157. _LockData[i].dwTotalReadLockTime = 0;
  158. _LockData[i].dwTotalReadEntrys = 0;
  159. }
  160. //+-----------------------------------------------------------------------------
  161. //
  162. // Function: Hash
  163. //
  164. // Synopsis: Maps line #s to index in the perf data table.
  165. //
  166. // History: 20-Dec-98 mprabhu Created
  167. //
  168. //------------------------------------------------------------------------------
  169. ULONG Hash (ULONG line)
  170. {
  171. // buckets of 256, 128, 64, 64 totaling to MAX_LOCKPERF_FILEENTRY
  172. // this was based on a grep in Ole32 tree for LOCK macro (Sep98):
  173. // Call counts:
  174. // 137 from 1 - 1024
  175. // 92 from 1024 - 2048
  176. // 45 from 2048 - 3072
  177. // 28 from 3072 - 4096
  178. // 10 from 4096 - 5120
  179. // 11 from 5124 - 6144
  180. // 8 from 6148 - 7168
  181. // highest lineNum was 6872 in marshal.cxx
  182. // The Hash function & array size may need updating if the highest lineNum
  183. // goes beyond 7176 or if any bucket gets maxed out.
  184. ULONG base, offset;
  185. if (line < 1024)
  186. {
  187. base = 0;
  188. offset = line>>2; //0 to 255
  189. }
  190. else if (line < 2048)
  191. {
  192. base = 256;
  193. offset = (line-1024)>>3; //0 to 127
  194. }
  195. else if (line < 3072)
  196. {
  197. base = 384;
  198. offset = (line-2048)>>4; //0 to 63
  199. }
  200. else
  201. { //this covers lines from 3072 to 7168
  202. base = 448;
  203. offset = (line-3072)>>6; //0 to 63
  204. }
  205. return base+offset;
  206. }
  207. //+-----------------------------------------------------------------------------
  208. //
  209. // Function: GetTlsLockPerfEntry
  210. //
  211. // Synopsis: Finds the entry in Tls for a lock or returns a free entry
  212. //
  213. // History: 20-Dec-98 mprabhu Created
  214. //
  215. //------------------------------------------------------------------------------
  216. CTlsLockPerf *GetTlsLockPerfEntry(void *lpLockPtr)
  217. {
  218. CTlsLockPerf *lpEntry = NULL, *lpFree = NULL;
  219. CTlsLockPerf *lpCurrent = (CTlsLockPerf *)TlsGetValue(gTlsLockPerfIndex);
  220. for (int i=0; i<TLS_LOCKPERF_MAX; i++)
  221. {
  222. if (lpCurrent->_dwFlags & TLS_LOCKPERF_INUSE)
  223. {
  224. if (lpCurrent->_lpLockPtr == lpLockPtr)
  225. {
  226. lpEntry = lpCurrent;
  227. break;
  228. }
  229. }
  230. else if (!lpFree)
  231. {
  232. // Remember the first free entry in case we need it.
  233. lpFree = lpCurrent;
  234. }
  235. lpCurrent++;
  236. }
  237. return (lpEntry!=NULL) ? lpEntry : lpFree;
  238. }
  239. //+-----------------------------------------------------------------------------
  240. //
  241. // Member: CLockPerfTracker::ReaderWaiting(), public
  242. //
  243. // Synopsis: Called by the lock code when a thread attempts to enter
  244. // a critical section for reading.
  245. //
  246. // History: 20-Dec-98 mprabhu Created
  247. //
  248. //------------------------------------------------------------------------------
  249. void CLockPerfTracker::ReaderWaiting(const char*pszFile,
  250. DWORD dwLine,
  251. const char* pszLockName,
  252. void *lpLockPtr)
  253. {
  254. if (gbLockPerf)
  255. {
  256. // Just call WriterWaiting till we need different semantics...
  257. WriterWaiting(pszFile,
  258. dwLine,
  259. pszLockName,
  260. lpLockPtr, FALSE /*bWriter*/);
  261. }
  262. }
  263. //+-----------------------------------------------------------------------------
  264. //
  265. // Member: CLockPerfTracker::WriterWaiting(), public
  266. //
  267. // Synopsis: Called by the lock code when a thread attempts to enter
  268. // a critical section for writing.
  269. //
  270. // History: 20-Dec-98 mprabhu Created
  271. //
  272. //------------------------------------------------------------------------------
  273. void CLockPerfTracker::WriterWaiting(const char*pszFile,
  274. DWORD dwLine,
  275. const char* pszLockName,
  276. void *lpLockPtr,
  277. BOOL bWriter /*default TRUE*/)
  278. {
  279. if (gbLockPerf)
  280. {
  281. CTlsLockPerf *pTlsLP = GetTlsLockPerfEntry(lpLockPtr);
  282. // Will assert if we are getting more than TLS_LOCKPERF_MAX
  283. // locks one after another without releasing any.
  284. LockAssert(pTlsLP);
  285. if (pTlsLP->_dwFlags & TLS_LOCKPERF_INUSE)
  286. {
  287. // Recursion on the lock. Request for the lock while holding it.
  288. // Can't be waiting recursively!
  289. LockAssert(pTlsLP->_dwFlags & TLS_LOCKPERF_ENTERED);
  290. }
  291. else
  292. {
  293. ULONG idx;
  294. ULONG loc = FindOrCreateFileTableEntry(pszFile,
  295. dwLine,
  296. pszLockName,
  297. lpLockPtr,
  298. bWriter,
  299. &idx);
  300. LockAssert(loc < MAX_LOCKPERF_FILEENTRY);
  301. //save table indices in TLS, for quick access later.
  302. pTlsLP->_loc = loc;
  303. pTlsLP->_idx = idx;
  304. pTlsLP->_lpLockPtr = lpLockPtr;
  305. pTlsLP->_dwFlags = TLS_LOCKPERF_WAITING | TLS_LOCKPERF_INUSE; //new Tls entry!
  306. pTlsLP->_dwRecursion = 0; //we set this to 1 upon the first xxxEntered
  307. //store request time in TLS (last thing done to not skew lock wait/use times)
  308. QueryPerformanceCounter(&pTlsLP->_liRequest);
  309. }
  310. }
  311. }
  312. //+-----------------------------------------------------------------------------
  313. //
  314. // Member: CLockPerfTracker::ReaderEntered(), public
  315. //
  316. // Synopsis: Called by lock code when a thread is granted access to
  317. // a critical section for reading.
  318. //
  319. // History: 20-Dec-98 mprabhu Created
  320. //
  321. //------------------------------------------------------------------------------
  322. // REVIEW: Life will be easier for us if we enforce that the lock code
  323. // must detect and inform if this is the first reader in the critical
  324. // section. (Similarly for the last reader leaving.)
  325. //------------------------------------------------------------------------------
  326. void CLockPerfTracker::ReaderEntered(const char*pszFile,
  327. DWORD dwLine,
  328. const char* pszLockName,
  329. void *lpLockPtr)
  330. {
  331. if (gbLockPerf)
  332. {
  333. ULONG idx = FindLockTableEntry(lpLockPtr);
  334. if (InterlockedIncrement((LONG*)&_LockData[idx].dwNumReaders)==1)
  335. {
  336. QueryPerformanceCounter(&_LockData[idx].liEntered);
  337. }
  338. // rest of the work is done by WriterEntered
  339. WriterEntered(pszFile, dwLine, pszLockName, lpLockPtr, /*bWriter*/ FALSE);
  340. }
  341. }
  342. //+-----------------------------------------------------------------------------
  343. //
  344. // Member: CLockPerfTracker::WriterEntered(), public
  345. //
  346. // Synopsis: Called by lock code when a thread is granted access to
  347. // a critical section for writing.
  348. //
  349. // History: 20-Dec-98 mprabhu Created
  350. //
  351. //------------------------------------------------------------------------------
  352. void CLockPerfTracker::WriterEntered(const char*pszFile,
  353. DWORD dwLine,
  354. const char* pszLockName,
  355. void *lpLockPtr,
  356. BOOL bWriter /*default TRUE*/)
  357. {
  358. if (gbLockPerf)
  359. {
  360. CTlsLockPerf *pTlsLP = GetTlsLockPerfEntry(lpLockPtr);
  361. /*
  362. // REVIEW: Should we force lock implementation to call ReaderWaiting/WriterWaiting
  363. // even if there is no reason to wait? In that case the following assertion is true.
  364. // There has to be an entry, either marked waiting or entered (recursive lock)
  365. LockAssert( pTlsLP && ((pTlsLP->_dwFlags & TLS_LOCKPERF_WAITING) || (pTlsLP->_dwFlags & TLS_LOCKPERF_ENTERED)) );
  366. */
  367. if (!(pTlsLP->_dwFlags & TLS_LOCKPERF_INUSE))
  368. {
  369. // Someone called xxxEntered directly (without calling xxxWaiting)
  370. ULONG idx;
  371. ULONG loc = FindOrCreateFileTableEntry(pszFile,
  372. dwLine,
  373. pszLockName,
  374. lpLockPtr,
  375. bWriter,
  376. &idx);
  377. LockAssert(loc < MAX_LOCKPERF_FILEENTRY);
  378. // save the table indices in TLS, for quick access later.
  379. pTlsLP->_loc = loc;
  380. pTlsLP->_idx = idx;
  381. pTlsLP->_lpLockPtr = lpLockPtr;
  382. pTlsLP->_dwFlags = TLS_LOCKPERF_ENTERED | TLS_LOCKPERF_INUSE;
  383. pTlsLP->_dwRecursion = 0;
  384. QueryPerformanceCounter(&pTlsLP->_liEntered);
  385. pTlsLP->_liRequest = pTlsLP->_liEntered;
  386. }
  387. else if (pTlsLP->_dwFlags & TLS_LOCKPERF_WAITING)
  388. {
  389. QueryPerformanceCounter(&pTlsLP->_liEntered);
  390. // Not waiting any more.
  391. pTlsLP->_dwFlags |= TLS_LOCKPERF_ENTERED;
  392. pTlsLP->_dwFlags &= ~TLS_LOCKPERF_WAITING;
  393. }
  394. pTlsLP->_dwRecursion++; // 1 means first level entry (i.e. no recursion)
  395. }
  396. }
  397. //+-----------------------------------------------------------------------------
  398. //
  399. // Member: CLockPerfTracker::ReaderLeaving(), public
  400. //
  401. // Synopsis: Called by the lock code when a reader is leaving a critical
  402. // section.
  403. //
  404. // History: 20-Dec-98 mprabhu Created
  405. //
  406. //------------------------------------------------------------------------------
  407. void CLockPerfTracker::ReaderLeaving(void *lpLockPtr)
  408. {
  409. if (gbLockPerf)
  410. {
  411. ULONG idx = FindLockTableEntry(lpLockPtr);
  412. LARGE_INTEGER liEntered = _LockData[idx].liEntered;
  413. if (InterlockedDecrement((LONG*)&_LockData[idx].dwNumReaders) == 0)
  414. {
  415. // Last reader leaving
  416. LARGE_INTEGER liDelta, liRem;
  417. QueryPerformanceCounter(&liDelta);
  418. liDelta = RtlLargeIntegerSubtract(liDelta, liEntered);
  419. liDelta = RtlExtendedIntegerMultiply(liDelta,1000000);
  420. liDelta = RtlLargeIntegerDivide(liDelta, _liFreq, &liRem);
  421. LockAssert(liDelta.HighPart == 0); // no one must hold a lock for so long!
  422. // This must be done inter-locked in case someother thread does
  423. // a 0->1, 1->0 transition while one thread is in this block.
  424. InterlockedExchangeAdd((LONG*)&_LockData[idx].dwSharedTime,
  425. liDelta.LowPart);
  426. }
  427. //Call WriterLeaving to do the rest.
  428. WriterLeaving(lpLockPtr);
  429. }
  430. }
  431. //+-----------------------------------------------------------------------------
  432. //
  433. // Member: CLockPerfTracker::WriterLeaving(), public
  434. //
  435. // Synopsis: Called by the lock code when a writer is leaving a critical
  436. // section.
  437. //
  438. // History: 20-Dec-98 mprabhu Created
  439. //
  440. //------------------------------------------------------------------------------
  441. void CLockPerfTracker::WriterLeaving(void *lpLockPtr)
  442. {
  443. if (gbLockPerf)
  444. {
  445. CTlsLockPerf *pTlsLP = GetTlsLockPerfEntry(lpLockPtr);
  446. // There has be to an entry marked entered!
  447. LockAssert(pTlsLP && (pTlsLP->_dwFlags & TLS_LOCKPERF_ENTERED));
  448. pTlsLP->_dwRecursion--;
  449. if (pTlsLP->_dwRecursion == 0)
  450. {
  451. // The thread is *really* leaving the lock. Do the math!
  452. LARGE_INTEGER liUnlockTime;
  453. QueryPerformanceCounter(&liUnlockTime);
  454. UpdateFileTableEntry(pTlsLP, &liUnlockTime);
  455. // Mark the Tls entry as free.
  456. pTlsLP->_dwFlags &= ~TLS_LOCKPERF_INUSE;
  457. }
  458. else
  459. {
  460. // The thread is still in the lock!
  461. LockAssert(pTlsLP->_dwFlags & TLS_LOCKPERF_ENTERED);
  462. }
  463. }
  464. }
  465. //+-----------------------------------------------------------------------------
  466. //
  467. // Member: CLockPerfTracker::ReportContention(), public
  468. //
  469. // Synopsis: Must be called by the lock destroy/cleanup function.
  470. // This is single threaded by definition.
  471. //
  472. // History: 20-Dec-98 mprabhu Created
  473. //
  474. //------------------------------------------------------------------------------
  475. void CLockPerfTracker::ReportContention(void *lpLockPtr,
  476. DWORD dwWriteEntrys,
  477. DWORD dwWriterContention,
  478. DWORD dwReadEntrys,
  479. DWORD dwReaderContention)
  480. {
  481. // This happens during DLL_PROCESS_DETACH hence single-threaded
  482. for (int i=0; i<MAX_LOCKS; i++)
  483. {
  484. if (_LockData[i].lpLockPtr == lpLockPtr)
  485. {
  486. _LockData[i].dwWriterContentionCount = dwWriterContention;
  487. _LockData[i].dwReaderContentionCount = dwReaderContention;
  488. // These asserts may not be very useful since some locks are entered
  489. // before lock monitoring can be started! Also, monitoring can be
  490. // turned OFF and ON in windows.
  491. // LockAssert( _LockData[i].dwTotalWriteEntrys == dwWriteEntrys);
  492. // LockAssert( _LockData[i].dwTotalReadEntrys == dwReadEntrys);
  493. break;
  494. }
  495. }
  496. LockAssert( i<MAX_LOCKS );
  497. }
  498. //+-----------------------------------------------------------------------------
  499. //
  500. // Member: CLockPerfTracker::FindLockTableEntry(), private
  501. //
  502. // Synopsis: Finds the entry for a critical section in the lock table.
  503. //
  504. // History: 20-Dec-98 mprabhu Created
  505. //
  506. //------------------------------------------------------------------------------
  507. ULONG CLockPerfTracker::FindLockTableEntry(void *lpLockPtr)
  508. {
  509. for (int idx=0; idx<MAX_LOCKS; idx++)
  510. {
  511. if (_LockData[idx].lpLockPtr == lpLockPtr)
  512. {
  513. return idx;
  514. }
  515. }
  516. LockAssert(!"Lock not registered for monitoring!");
  517. return MAX_LOCKS-1; // just to avoid AVs
  518. }
  519. //+-----------------------------------------------------------------------------
  520. //
  521. // Member: CLockPerfTracker::FindOrCreateFileTableEntry(), private
  522. //
  523. // Synopsis: Finds the entry (or creates one) for a critical section guarded
  524. // by a lock at a {file, line}.
  525. //
  526. // History: 20-Dec-98 mprabhu Created
  527. //
  528. //------------------------------------------------------------------------------
  529. ULONG CLockPerfTracker::FindOrCreateFileTableEntry(const char*pszFile,
  530. DWORD dwLine,
  531. const char* pszLockName,
  532. void *lpLockPtr,
  533. BOOL bWriter,
  534. DWORD *lpLockTableIndex)
  535. {
  536. BOOL bFoundEntry = FALSE;
  537. CFileLineEntry *pNewTableEntry = NULL;
  538. ULONG loc = Hash(dwLine);
  539. if (loc >= MAX_LOCKPERF_FILEENTRY)
  540. {
  541. LockAssert(!"Lock PerfTable full! Increase size.");
  542. gbLockPerfOverflow = TRUE;
  543. loc = MAX_LOCKPERF_FILEENTRY-1;
  544. goto errRet;
  545. }
  546. // If hashing works well this should not take too much time.
  547. while (!bFoundEntry)
  548. {
  549. while (_FileLineData[loc].lpLockPtr && loc<MAX_LOCKPERF_FILEENTRY)
  550. {
  551. if ( (_FileLineData[loc].dwLine==dwLine)
  552. &&(_FileLineData[loc].pszFile==pszFile) )
  553. {
  554. bFoundEntry = TRUE;
  555. break; //done
  556. }
  557. loc++;
  558. }
  559. if (loc >= MAX_LOCKPERF_FILEENTRY)
  560. {
  561. gbLockPerfOverflow = TRUE;
  562. loc = MAX_LOCKPERF_FILEENTRY-1;
  563. goto errRet;
  564. }
  565. if (!bFoundEntry && !( InterlockedCompareExchange(
  566. (LONG*)&_FileLineData[loc].lpLockPtr,
  567. (LONG)lpLockPtr,
  568. NULL) )
  569. )
  570. {
  571. // We are seeing a new critical section in the code base
  572. bFoundEntry = TRUE;
  573. pNewTableEntry = &_FileLineData[loc];
  574. InterlockedIncrement(&glFileLine); // Global count of code CritSec locations
  575. }
  576. }
  577. if (pNewTableEntry)
  578. { // finish rest of initialization the entry is secured for this code location.
  579. // REVIEW: Ignoring races here.
  580. pNewTableEntry->bWriteCritSec = bWriter;
  581. pNewTableEntry->dwNumEntrys = 0;
  582. pNewTableEntry->pszLockName = pszLockName;
  583. pNewTableEntry->pszFile = pszFile;
  584. pNewTableEntry->dwLine = dwLine;
  585. pNewTableEntry->dwWaitTime = 0;
  586. pNewTableEntry->dwLockedTime = 0;
  587. pNewTableEntry->ulLockTableIdx = FindLockTableEntry(lpLockPtr);
  588. }
  589. errRet:
  590. *lpLockTableIndex = _FileLineData[loc].ulLockTableIdx;
  591. return loc;
  592. }
  593. //+-----------------------------------------------------------------------------
  594. //
  595. // Member: CLockPerfTracker::UpdateFileTableEntry(), private
  596. //
  597. // Synopsis: Adds the waiting time and the locked time for this visit
  598. // to the cumulative data for the {file,line} entry.
  599. //
  600. // History: 20-Dec-98 mprabhu Created
  601. //
  602. //------------------------------------------------------------------------------
  603. void CLockPerfTracker::UpdateFileTableEntry(CTlsLockPerf *pTlsEntry,
  604. LARGE_INTEGER *pliUnlockTime)
  605. {
  606. LockAssert( (pTlsEntry->_dwFlags & TLS_LOCKPERF_INUSE)
  607. && (pTlsEntry->_dwFlags & TLS_LOCKPERF_ENTERED) );
  608. ULONG idx = pTlsEntry->_idx;
  609. ULONG loc = pTlsEntry->_loc;
  610. LockAssert(loc>=0 && loc < MAX_LOCKPERF_FILEENTRY);
  611. LARGE_INTEGER liRem;
  612. LARGE_INTEGER liWait = RtlLargeIntegerSubtract(pTlsEntry->_liEntered,
  613. pTlsEntry->_liRequest);
  614. liWait = RtlExtendedIntegerMultiply(liWait,1000000);
  615. liWait = RtlLargeIntegerDivide(liWait,_liFreq,&liRem); // liWait is now in micro-seconds
  616. LockAssert(liWait.HighPart == 0); // hopefully no one waits for so long!!
  617. LARGE_INTEGER liLocked = RtlLargeIntegerSubtract(*pliUnlockTime,
  618. pTlsEntry->_liEntered);
  619. liLocked = RtlExtendedIntegerMultiply(liLocked,1000000);
  620. liLocked = RtlLargeIntegerDivide(liLocked, _liFreq, &liRem);
  621. LockAssert(liLocked.HighPart == 0); // no one must hold a lock for so long!
  622. if (_FileLineData[loc].bWriteCritSec)
  623. { // Since this is a write location, the lock itself guarantees exclusion
  624. _FileLineData[loc].dwNumEntrys++;
  625. _FileLineData[loc].dwWaitTime += liWait.LowPart;
  626. _FileLineData[loc].dwLockedTime += liLocked.LowPart;
  627. /*
  628. This needs to be here if we wish to compare entry counts reported by
  629. the lock with our own. For now this is in ProcessPerfData
  630. _LockData[idx].dwTotalWriteEntrys++;
  631. _LockData[idx].dwTotalWriteWaitTime += liWait.LowPart;
  632. _LockData[idx].dwTotalWriteLockTime += liLocked.LowPart;
  633. */
  634. }
  635. else
  636. { // This is a read location.
  637. // Hence we have to exclude other readers from updating data
  638. InterlockedIncrement( (LONG*) &_FileLineData[loc].dwNumEntrys );
  639. InterlockedExchangeAdd( (LONG*) &_FileLineData[loc].dwWaitTime, liWait.LowPart );
  640. InterlockedExchangeAdd( (LONG*) &_FileLineData[loc].dwLockedTime, liLocked.LowPart );
  641. /*
  642. This needs to be here if we wish to compare entry counts reported by
  643. the lock with our own. For now this is in ProcessPerfData
  644. InterlockedIncrement( (LONG*) &_LockData[idx].dwTotalReadEntrys );
  645. InterlockedExchangeAdd( (LONG*) &_LockData[idx].dwTotalReadWaitTime, liWait.LowPart );
  646. InterlockedExchangeAdd( (LONG*) &_LockData[idx].dwTotalReadLockTime, liLocked.LowPart );
  647. */
  648. }
  649. #if 0
  650. // Turn this ON, if you want a live log of every Update.
  651. wsprintfA(gszLockPerfBuf,"\n Lock at %-25s : line %u : Entry # %u",
  652. (pTableEntry->pszFile[1]==':') ? pTableEntry->pszFile+24
  653. : pTableEntry->pszFile,
  654. pTableEntry->dwLine,
  655. pTableEntry->dwNumEntrys);
  656. OutputDebugStringA(gszLockPerfBuf);
  657. wsprintfA(gszLockPerfBuf," Held for: %u mic-sec.",liLockHeld.LowPart);
  658. OutputDebugStringA(gszLockPerfBuf);
  659. #endif
  660. }
  661. #define HOGLIST_TIME 0
  662. #define HOGLIST_ENTRY 1
  663. #define HOGLIST_AVGTIME 2
  664. //+-----------------------------------------------------------------------------
  665. //
  666. // Member: CLockPerfTracker::UpdateHoggers(), private
  667. //
  668. // Synopsis: Manages top ten lists.
  669. // This is done only during Proc_detach (hence thread safe!)
  670. //
  671. // History: 20-Dec-98 mprabhu Created
  672. //
  673. //------------------------------------------------------------------------------
  674. void CLockPerfTracker::UpdateHoggers(ULONG* hogList, ULONG index, ULONG listType)
  675. {
  676. int i,j;
  677. switch (listType)
  678. {
  679. case HOGLIST_TIME:
  680. for (i=0; i<10; i++)
  681. {
  682. if (hogList[i]==-1)
  683. {
  684. break;
  685. }
  686. else if (_FileLineData[hogList[i]].dwLockedTime
  687. < _FileLineData[index].dwLockedTime)
  688. {
  689. break;
  690. }
  691. }
  692. break;
  693. case HOGLIST_ENTRY:
  694. for (i=0; i<10; i++)
  695. {
  696. if (hogList[i]==-1)
  697. {
  698. break;
  699. }
  700. else if (_FileLineData[hogList[i]].dwNumEntrys
  701. < _FileLineData[index].dwNumEntrys)
  702. {
  703. break;
  704. }
  705. }
  706. break;
  707. case HOGLIST_AVGTIME:
  708. for (i=0; i<10; i++)
  709. {
  710. if (hogList[i]==-1)
  711. {
  712. break;
  713. }
  714. else if (_FileLineData[hogList[i]].dwAvgTime
  715. < _FileLineData[index].dwAvgTime)
  716. {
  717. break;
  718. }
  719. }
  720. break;
  721. default:
  722. break;
  723. }
  724. if (i<10)
  725. {
  726. for (j=9; j>i;j--)
  727. {
  728. hogList[j] = hogList[j-1] ;
  729. }
  730. hogList[i] = index;
  731. }
  732. }
  733. //+-----------------------------------------------------------------------------
  734. //
  735. // Function: PercentToString
  736. //
  737. // Synopsis: Converts numbers like 74326 to "74.33".
  738. // We do not have float printing support in retail builds.
  739. //
  740. // History: 20-Dec-98 mprabhu Created
  741. //
  742. //------------------------------------------------------------------------------
  743. // The percent argument is passed in 1000 times magnified.
  744. char gPerc[7];
  745. inline char *PercentToString(long percent)
  746. {
  747. //round-off
  748. percent = percent/10 + ((percent%10 >= 5)?1:0);
  749. //set to fixed length
  750. percent+=10000;
  751. //create room for decimal point (4th char)
  752. percent = (percent/100)*1000 + (percent%100);
  753. _itoa(percent,gPerc,10);
  754. gPerc[0] = gPerc[0]-1; //remove the 10000 we added.
  755. gPerc[3] = '.';
  756. return gPerc + (gPerc[0]=='0'? (gPerc[1]=='0'?2:1):0) ;
  757. }
  758. //+-----------------------------------------------------------------------------
  759. //
  760. // Member: CLockPerfTracker::OutputFileTableEntry, private
  761. //
  762. // Synopsis: Prints out cumulative data for a {file,line} entry
  763. // We are doing this during Process_Detach, hence thread-safe.
  764. //
  765. // History: 20-Dec-98 mprabhu Created
  766. //
  767. //------------------------------------------------------------------------------
  768. void CLockPerfTracker::OutputFileTableEntry(ULONG index,
  769. ULONG bByName,
  770. ULONG percent)
  771. {
  772. ULONG trimFileName;
  773. CFileLineEntry *pTableEntry = &_FileLineData[index];
  774. if (pTableEntry->pszFile[1]==':')
  775. {
  776. trimFileName = 24;
  777. }
  778. else
  779. {
  780. trimFileName = 0;
  781. }
  782. if (bByName)
  783. {
  784. wsprintfA(gszLockPerfBuf,"\n %20s %4d %s %6d %7u %6s %% ",
  785. pTableEntry->pszFile+trimFileName,
  786. pTableEntry->dwLine,
  787. pTableEntry->bWriteCritSec ? "Write" : " Read",
  788. pTableEntry->dwNumEntrys,
  789. pTableEntry->dwLockedTime,
  790. PercentToString(percent));
  791. OutputDebugStringA(gszLockPerfBuf);
  792. }
  793. else
  794. {
  795. wsprintfA(gszLockPerfBuf,"\n %20s %4d %-14.14s %s %5d %8u %8u",
  796. pTableEntry->pszFile+trimFileName,
  797. pTableEntry->dwLine,
  798. pTableEntry->pszLockName,
  799. pTableEntry->bWriteCritSec?"Write":" Read",
  800. pTableEntry->dwNumEntrys,
  801. pTableEntry->dwLockedTime,
  802. pTableEntry->dwAvgTime);
  803. OutputDebugStringA(gszLockPerfBuf);
  804. }
  805. }
  806. #define TITLES_1 \
  807. wsprintfA(gszLockPerfBuf,"\n File Line LockName Type Entrys TotalTime Avg/Entry"); OutputDebugStringA(gszLockPerfBuf)
  808. #define TITLES_2 \
  809. wsprintfA(gszLockPerfBuf,"\n File Line Type Entrys TotalTime %%-Time "); OutputDebugStringA(gszLockPerfBuf)
  810. #define SEPARATOR_1 \
  811. wsprintfA(gszLockPerfBuf,"\n ==================== ===== ============ ===== ====== ========= ========="); OutputDebugStringA(gszLockPerfBuf)
  812. #define SEPARATOR_2 \
  813. wsprintfA(gszLockPerfBuf,"\n ==================== ===== ====== ====== ========= ======"); OutputDebugStringA(gszLockPerfBuf)
  814. //+-----------------------------------------------------------------------------
  815. //
  816. // Member: CLockPerfTracker::OutputHoggers(), private
  817. //
  818. // Synopsis: Prints out a top ten list given an array of indices for the same.
  819. //
  820. // History: 20-Dec-98 mprabhu Created
  821. //
  822. //------------------------------------------------------------------------------
  823. void CLockPerfTracker::OutputHoggers(ULONG *hogList)
  824. {
  825. TITLES_1;
  826. SEPARATOR_1;
  827. for (int i=0; i<10; i++)
  828. {
  829. if (hogList[i]!=-1)
  830. {
  831. OutputFileTableEntry(hogList[i],0,0);
  832. }
  833. }
  834. SEPARATOR_1;
  835. }
  836. //+-----------------------------------------------------------------------------
  837. //
  838. // Member: CLockPerfTracker::ProcessPerfData(), public
  839. //
  840. // Synopsis: Organizes the perf table data by lockName.
  841. // Prints lock summary data, top-ten lists.
  842. // This is done only during Proc_detach (hence thread safe!)
  843. //
  844. // History: 20-Dec-98 mprabhu Created
  845. //
  846. //------------------------------------------------------------------------------
  847. void CLockPerfTracker::ProcessPerfData()
  848. {
  849. // #### Begin : organize data
  850. CFileLineEntry *pTableEntry;
  851. ULONG hogListTime[10];
  852. ULONG hogListEntry[10];
  853. ULONG hogListAvgTime[10];
  854. ULONG iName;
  855. DWORD totalLockTime = 0; // lock sharing not reflected!
  856. DWORD totalRealLockTime = 0; // takes lock sharing into account!
  857. DWORD totalLocksCreated = 0; // some locks may never get used!
  858. for (int i=0; i<10; i++)
  859. {
  860. hogListTime[i] = -1;
  861. hogListEntry[i] = -1;
  862. hogListAvgTime[i] = -1;
  863. }
  864. for (i=0; i<MAX_LOCKPERF_FILEENTRY; i++)
  865. {
  866. if (_FileLineData[i].lpLockPtr)
  867. {
  868. pTableEntry = &_FileLineData[i];
  869. if (pTableEntry->dwNumEntrys)
  870. {
  871. pTableEntry->dwAvgTime =
  872. pTableEntry->dwLockedTime/pTableEntry->dwNumEntrys;
  873. }
  874. else
  875. {
  876. pTableEntry->dwAvgTime = 0;
  877. }
  878. UpdateHoggers(hogListTime,i, 0);
  879. UpdateHoggers(hogListEntry,i, 1);
  880. UpdateHoggers(hogListAvgTime,i, 2);
  881. _numCritSecs++;
  882. //REVIEW: how should we take shared time into account?
  883. totalLockTime = totalLockTime + pTableEntry->dwLockedTime;
  884. iName = FindLockTableEntry(_FileLineData[i].lpLockPtr);
  885. if (_LockData[iName].pszLockName==NULL)
  886. {
  887. // First file table entry for this lock
  888. // We use pszLockName==NULL to tell if a lock got used!
  889. _LockData[iName].pszLockName = pTableEntry->pszLockName;
  890. _LockData[iName].dwHead = i;
  891. _LockData[iName].dwTail = i;
  892. _numLocks++;
  893. }
  894. else
  895. { //CritSec is already in our list.
  896. _FileLineData[_LockData[iName].dwTail].dwNext = i;
  897. _LockData[iName].dwTail = i;
  898. }
  899. if (pTableEntry->bWriteCritSec)
  900. {
  901. _LockData[iName].dwTotalWriteLockTime += pTableEntry->dwLockedTime;
  902. _LockData[iName].dwTotalWriteEntrys += pTableEntry->dwNumEntrys;
  903. _LockData[iName].dwTotalWriteWaitTime += pTableEntry->dwWaitTime;
  904. }
  905. else
  906. {
  907. _LockData[iName].dwTotalReadLockTime += pTableEntry->dwLockedTime;
  908. _LockData[iName].dwTotalReadEntrys += pTableEntry->dwNumEntrys;
  909. _LockData[iName].dwTotalReadWaitTime += pTableEntry->dwWaitTime;
  910. }
  911. } // if In Use
  912. } // for each table entry
  913. // #### End : organize data
  914. wsprintfA(gszLockPerfBuf,
  915. "\n\n ============= LOCK_PERF: TOP TEN LISTS ===========\n");
  916. OutputDebugStringA(gszLockPerfBuf);
  917. wsprintfA(gszLockPerfBuf,
  918. "\n\n ============= Top Ten Hoggers by total time ===========\n");
  919. OutputDebugStringA(gszLockPerfBuf);
  920. OutputHoggers(hogListTime);
  921. wsprintfA(gszLockPerfBuf,
  922. "\n\n ============= Top Ten Hoggers by crit-sec Entrys ===========\n");
  923. OutputDebugStringA(gszLockPerfBuf);
  924. OutputHoggers(hogListEntry);
  925. wsprintfA(gszLockPerfBuf,
  926. "\n\n ============= Top Ten Hoggers by avg time per Entry ===========\n");
  927. OutputDebugStringA(gszLockPerfBuf);
  928. OutputHoggers(hogListAvgTime);
  929. SEPARATOR_1;
  930. wsprintfA(gszLockPerfBuf,
  931. "\n\n ============= LOCK_PERF: OVERALL LOCK STATS ===========\n");
  932. OutputDebugStringA(gszLockPerfBuf);
  933. for (i=0; i<MAX_LOCKS; i++)
  934. {
  935. if (_LockData[i].lpLockPtr)
  936. {
  937. totalLocksCreated++;
  938. if (_LockData[i].pszLockName)
  939. {
  940. // lock got used!
  941. totalRealLockTime += _LockData[i].dwTotalWriteLockTime;
  942. if (_LockData[i].dwTotalReadEntrys)
  943. {
  944. totalRealLockTime += _LockData[i].dwSharedTime;
  945. }
  946. }
  947. }
  948. }
  949. wsprintfA(gszLockPerfBuf,
  950. "\n\n TOTAL locks created = %u",
  951. totalLocksCreated);
  952. OutputDebugStringA(gszLockPerfBuf);
  953. wsprintfA(gszLockPerfBuf,
  954. "\n TOTAL locks used = %u",
  955. _numLocks);
  956. OutputDebugStringA(gszLockPerfBuf);
  957. wsprintfA(gszLockPerfBuf,
  958. "\n\n TOTAL code critSec areas covered = %u",
  959. _numCritSecs);
  960. OutputDebugStringA(gszLockPerfBuf);
  961. wsprintfA(gszLockPerfBuf,
  962. "\n\n\n TOTAL time spent in all critSecs = %u micro-sec\n\n",
  963. totalLockTime);
  964. OutputDebugStringA(gszLockPerfBuf);
  965. wsprintfA(gszLockPerfBuf,
  966. " [This is the sum of individual thread times.\n This does not take overlaps (shared work) into account.]");
  967. OutputDebugStringA(gszLockPerfBuf);
  968. wsprintfA(gszLockPerfBuf,
  969. "\n\n\n TOTAL real time spent in all critSecs = %u micro-sec\n\n",
  970. totalRealLockTime);
  971. OutputDebugStringA(gszLockPerfBuf);
  972. wsprintfA(gszLockPerfBuf," [This takes shared time into account.]");
  973. OutputDebugStringA(gszLockPerfBuf);
  974. wsprintfA(gszLockPerfBuf,
  975. "\n\n ## Warning ##: The total time counters overflow in about 70 minutes!\n\n");
  976. OutputDebugStringA(gszLockPerfBuf);
  977. }
  978. //+-----------------------------------------------------------------------------
  979. //
  980. // Member: CLockPerfTracker::OutputPerfData(), public
  981. //
  982. // Synopsis: Prints out the {file,line} table.
  983. // This is done only during Proc_detach (hence thread safe!)
  984. //
  985. // History: 20-Dec-98 mprabhu Created
  986. //
  987. //------------------------------------------------------------------------------
  988. void CLockPerfTracker::OutputPerfData()
  989. {
  990. if (glFileLine) //if monitoring was ON at any time!
  991. {
  992. if (gbLockPerfOverflow)
  993. {
  994. wsprintfA(gszLockPerfBuf,
  995. "\n ### Warning: Overflow in lock perf data buffers!\n \
  996. ### Increase array sizes and recompile!!\n");
  997. OutputDebugStringA(gszLockPerfBuf);
  998. }
  999. ULONG iStart, iNext, iEnd;
  1000. const char *pszThisFile;
  1001. ULONG iName;
  1002. LARGE_INTEGER liPerc, liDiv, liRem ;
  1003. //TITLES_1;
  1004. //SEPARATOR_1;
  1005. ProcessPerfData();
  1006. // #### Begin: output by LockName:
  1007. SEPARATOR_2;
  1008. wsprintfA(gszLockPerfBuf,
  1009. "\n\n ============= LOCK_PERF: PER LOCK STATS ===========\n");
  1010. OutputDebugStringA(gszLockPerfBuf);
  1011. BOOL bShared;
  1012. int iShared;
  1013. for (iName=0; iName<_numLocks; iName++)
  1014. {
  1015. if (_LockData[iName].pszLockName) { //if the lock got used
  1016. SEPARATOR_2;
  1017. wsprintfA(gszLockPerfBuf,
  1018. "\n\n\n #### ++++++++ Summary for %14s , this = %lx ++++++++ ####",
  1019. _LockData[iName].pszLockName,
  1020. (DWORD)_LockData[iName].lpLockPtr);
  1021. OutputDebugStringA(gszLockPerfBuf);
  1022. wsprintfA(gszLockPerfBuf,
  1023. "\n\n WrtLockTime:%12u WrtEntrys:%14u\n WrtWait:%16u WrtContention:%10u \n",
  1024. _LockData[iName].dwTotalWriteLockTime,
  1025. _LockData[iName].dwTotalWriteEntrys,
  1026. _LockData[iName].dwTotalWriteWaitTime,
  1027. _LockData[iName].dwWriterContentionCount);
  1028. OutputDebugStringA(gszLockPerfBuf);
  1029. wsprintfA(gszLockPerfBuf,
  1030. "\n RdLockTime:%13u RdEntrys:%15u\n RdWait:%17u RdContention:%11u \n",
  1031. _LockData[iName].dwTotalReadLockTime,
  1032. _LockData[iName].dwTotalReadEntrys,
  1033. _LockData[iName].dwTotalReadWaitTime,
  1034. _LockData[iName].dwReaderContentionCount);
  1035. OutputDebugStringA(gszLockPerfBuf);
  1036. wsprintfA(gszLockPerfBuf,"\n Shared Read Time: %10u\n",
  1037. _LockData[iName].dwSharedTime);
  1038. OutputDebugStringA(gszLockPerfBuf);
  1039. TITLES_2;
  1040. SEPARATOR_2;
  1041. iNext = _LockData[iName].dwHead;
  1042. iEnd = _LockData[iName].dwTail;
  1043. while (1)
  1044. {
  1045. liPerc.HighPart = 0;
  1046. liPerc.LowPart = _FileLineData[iNext].dwLockedTime;
  1047. liPerc = RtlExtendedIntegerMultiply(liPerc,100000);
  1048. liDiv.HighPart = 0;
  1049. if (_FileLineData[iNext].bWriteCritSec)
  1050. {
  1051. liDiv.LowPart = _LockData[iName].dwTotalWriteLockTime;
  1052. }
  1053. else
  1054. {
  1055. liDiv.LowPart = _LockData[iName].dwTotalReadLockTime;
  1056. }
  1057. liPerc = RtlLargeIntegerDivide(liPerc, liDiv, &liRem);
  1058. OutputFileTableEntry(iNext,1, liPerc.LowPart);
  1059. if (iNext == iEnd)
  1060. break;
  1061. iNext = _FileLineData[iNext].dwNext;
  1062. }
  1063. SEPARATOR_2;
  1064. wsprintfA(gszLockPerfBuf,
  1065. "\n ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ \n");
  1066. OutputDebugStringA(gszLockPerfBuf);
  1067. } //if the lock got used!
  1068. }
  1069. SEPARATOR_2;
  1070. // #### End: output by LockName:
  1071. SEPARATOR_1;
  1072. #if 0 //This lists lock data by fileNames
  1073. // #### Begin: output by fileName : location:
  1074. wsprintfA(gszLockPerfBuf,
  1075. "\n\n\n ======= CritSec data listed by FileName: =======\n");
  1076. OutputDebugStringA(gszLockPerfBuf);
  1077. TITLES_1;
  1078. SEPARATOR_1;
  1079. pszThisFile = NULL;
  1080. i=0;
  1081. while (!_FileLineData[i].bInUse)
  1082. i++;
  1083. iNext = i;
  1084. while (iNext < MAX_LOCKPERF_FILEENTRY)
  1085. {
  1086. iStart = iNext;
  1087. iNext = MAX_LOCKPERF_FILEENTRY;
  1088. pszThisFile= _FileLineData[iStart].pszFile;
  1089. for (i=iStart;i<MAX_LOCKPERF_FILEENTRY;i++)
  1090. {
  1091. if (_FileLineData[i].bInUse)
  1092. {
  1093. if (pszThisFile==_FileLineData[i].pszFile)
  1094. {
  1095. OutputFileTableEntry(i,0,0);
  1096. _FileLineData[i].bInUse = FALSE;
  1097. }
  1098. else if (iNext==MAX_LOCKPERF_FILEENTRY)
  1099. {
  1100. iNext=i;
  1101. }
  1102. }
  1103. }
  1104. }
  1105. // #### End: output by fileName : location:
  1106. #endif //0 This lists lock data by fileNames
  1107. } //if glFileLine
  1108. }
  1109. #endif //LOCK_PERF==1