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
44 KiB
1285 lines
44 KiB
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// File: lockperf.cxx
|
|
//
|
|
// Contents: Implementation of lock monitoring for locks used by ole32.dll
|
|
//
|
|
// Classes: CLockPerfTracker
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
#if LOCK_PERF==1
|
|
|
|
#include <ole2int.h>
|
|
#include <lockperf.hxx>
|
|
|
|
// gbLockPerf == (Is lock monitoring ON?)
|
|
// (It is possible to turn this on & off multiple times during a single test run.)
|
|
// Ideally, it would be good to turn it OFF after proc-attach, let the test
|
|
// run for a while and then turn it ON. This will get the startup skew out of
|
|
// the way.
|
|
BOOL gbLockPerf = TRUE;
|
|
|
|
// Lock perf tracker. Gathers perf measurements for all locks.
|
|
CLockPerfTracker gLockTracker;
|
|
|
|
// static member table of the global tracker class.
|
|
CFileLineEntry CLockPerfTracker::_FileLineData[MAX_LOCKPERF_FILEENTRY];
|
|
|
|
// static member table of the global tracker: this keeps a track of shared time.
|
|
CLockEntry CLockPerfTracker::_LockData[MAX_LOCKS];
|
|
|
|
// counts of locks & {file,line} instances tracked.
|
|
ULONG CLockPerfTracker::_numLocks;
|
|
ULONG CLockPerfTracker::_numCritSecs;
|
|
|
|
// perf frequency on the system
|
|
LARGE_INTEGER CLockPerfTracker::_liFreq;
|
|
|
|
//Flagged if tables get full
|
|
BOOL gbLockPerfOverflow=FALSE;
|
|
|
|
//Count of entries in the shared table (code critical sections)
|
|
LONG glFileLine = 0;
|
|
|
|
//Strings to used to print report
|
|
LPSTR gszLockPerfErr = "##### Error: LockPerfOverFlow!!";
|
|
char gszLockPerfBuf[256];
|
|
|
|
//Used to debug the perf monitoring code.
|
|
#define perfPrint(x)
|
|
#if DBG==1
|
|
#define LockAssert(X) if (!(X)) wsprintfA(gszLockPerfBuf,#X ## "\n"),OutputDebugStringA(gszLockPerfBuf);
|
|
#else
|
|
#define LockAssert(X)
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// **** Functions to manage the Private TLS used by LockPerf *********
|
|
|
|
// Heap Handle (copied from tls.cxx)
|
|
extern HANDLE g_hHeap;
|
|
#define HEAP_SERIALIZE 0
|
|
|
|
DWORD gTlsLockPerfIndex = 0xFFFFFFFF;
|
|
|
|
//345678901234567890123456789012345678901234567890123456789012345678901234567890
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Function: AllocLockPerfPvtTlsData
|
|
//
|
|
// Synopsis: Allocates the Tls data for a thread (at Thread_Attach)
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
|
|
HRESULT AllocLockPerfPvtTlsData()
|
|
{
|
|
void *pMem = HeapAlloc(g_hHeap,
|
|
HEAP_SERIALIZE,
|
|
TLS_LOCKPERF_MAX*sizeof(CTlsLockPerf));
|
|
if (!pMem)
|
|
{
|
|
LockAssert(!"Could not alloc private Tls data for LockPerf");
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
LockAssert(gTlsLockPerfIndex!=0xFFFFFFFF);
|
|
memset(pMem, 0, TLS_LOCKPERF_MAX*sizeof(CTlsLockPerf));
|
|
|
|
TlsSetValue(gTlsLockPerfIndex, pMem);
|
|
return S_OK;
|
|
}
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Function: FreeLockPerfPvtTlsData
|
|
//
|
|
// Synopsis: Frees the Tls data for a thread (at Thread_Detach)
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
// REVIEW: What about cases when DllMain is not called with Thread_Detach?
|
|
|
|
void FreeLockPerfPvtTlsData()
|
|
{
|
|
LockAssert(gTlsLockPerfIndex!=0xFFFFFFFF);
|
|
void *pMem = TlsGetValue(gTlsLockPerfIndex);
|
|
if (pMem)
|
|
{
|
|
HeapFree(g_hHeap, HEAP_SERIALIZE, pMem);
|
|
}
|
|
}
|
|
|
|
//**** End: TLS functions ********************************************
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Member: CLockPerfTracker::Init(), public
|
|
//
|
|
// Synopsis: Initializes lock perf data, marking entries as unused.
|
|
// Called during Proc_Attach
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
|
|
HRESULT CLockPerfTracker::Init()
|
|
{
|
|
QueryPerformanceFrequency(&_liFreq);
|
|
|
|
// We use lpLockPtr to tell if an entry in these tables is in-use.
|
|
for (int i=0;i<MAX_LOCKPERF_FILEENTRY;i++)
|
|
{
|
|
_FileLineData[i].lpLockPtr = NULL;
|
|
}
|
|
for (i=0; i<MAX_LOCKS; i++)
|
|
{
|
|
_LockData[i].lpLockPtr = NULL;
|
|
}
|
|
_numLocks = 0;
|
|
_numCritSecs = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Member: CLockPerfTracker::RegisterLock(), public
|
|
//
|
|
// Synopsis: Must be called by the lock creation function.
|
|
// lpLockPtr == this-ptr of the lock (or unique ptr)
|
|
// bReadWrite == TRUE for Reader-Writer Locks.
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
|
|
void CLockPerfTracker::RegisterLock(void *lpLockPtr, BOOL bReadWrite)
|
|
{
|
|
BOOL bDone = FALSE;
|
|
int i=0;
|
|
|
|
while (!bDone)
|
|
{
|
|
while (_LockData[i].lpLockPtr)
|
|
{
|
|
i++;
|
|
}
|
|
LockAssert(i < MAX_LOCKS);
|
|
|
|
// REVIEW: 64-bit implications of this cast?
|
|
bDone = !(InterlockedCompareExchange((LONG *)&_LockData[i].lpLockPtr,
|
|
(LONG)lpLockPtr,
|
|
NULL));
|
|
}
|
|
|
|
_LockData[i].dwTotalWriteWaitTime = 0;
|
|
_LockData[i].dwTotalWriteEntrys = 0;
|
|
_LockData[i].dwTotalWriteLockTime = 0;
|
|
_LockData[i].pszLockName = NULL;
|
|
|
|
// REVIEW: These 5 could be skipped for non reader-writer locks?
|
|
_LockData[i].dwSharedTime = 0;
|
|
_LockData[i].dwNumReaders = 0;
|
|
|
|
_LockData[i].dwTotalReadWaitTime = 0;
|
|
_LockData[i].dwTotalReadLockTime = 0;
|
|
_LockData[i].dwTotalReadEntrys = 0;
|
|
|
|
}
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Function: Hash
|
|
//
|
|
// Synopsis: Maps line #s to index in the perf data table.
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
ULONG Hash (ULONG line)
|
|
{
|
|
// buckets of 256, 128, 64, 64 totaling to MAX_LOCKPERF_FILEENTRY
|
|
// this was based on a grep in Ole32 tree for LOCK macro (Sep98):
|
|
// Call counts:
|
|
// 137 from 1 - 1024
|
|
// 92 from 1024 - 2048
|
|
// 45 from 2048 - 3072
|
|
// 28 from 3072 - 4096
|
|
// 10 from 4096 - 5120
|
|
// 11 from 5124 - 6144
|
|
// 8 from 6148 - 7168
|
|
// highest lineNum was 6872 in marshal.cxx
|
|
// The Hash function & array size may need updating if the highest lineNum
|
|
// goes beyond 7176 or if any bucket gets maxed out.
|
|
|
|
ULONG base, offset;
|
|
if (line < 1024)
|
|
{
|
|
base = 0;
|
|
offset = line>>2; //0 to 255
|
|
}
|
|
else if (line < 2048)
|
|
{
|
|
base = 256;
|
|
offset = (line-1024)>>3; //0 to 127
|
|
}
|
|
else if (line < 3072)
|
|
{
|
|
base = 384;
|
|
offset = (line-2048)>>4; //0 to 63
|
|
}
|
|
else
|
|
{ //this covers lines from 3072 to 7168
|
|
base = 448;
|
|
offset = (line-3072)>>6; //0 to 63
|
|
}
|
|
return base+offset;
|
|
}
|
|
|
|
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Function: GetTlsLockPerfEntry
|
|
//
|
|
// Synopsis: Finds the entry in Tls for a lock or returns a free entry
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
CTlsLockPerf *GetTlsLockPerfEntry(void *lpLockPtr)
|
|
{
|
|
CTlsLockPerf *lpEntry = NULL, *lpFree = NULL;
|
|
|
|
CTlsLockPerf *lpCurrent = (CTlsLockPerf *)TlsGetValue(gTlsLockPerfIndex);
|
|
for (int i=0; i<TLS_LOCKPERF_MAX; i++)
|
|
{
|
|
if (lpCurrent->_dwFlags & TLS_LOCKPERF_INUSE)
|
|
{
|
|
if (lpCurrent->_lpLockPtr == lpLockPtr)
|
|
{
|
|
lpEntry = lpCurrent;
|
|
break;
|
|
}
|
|
}
|
|
else if (!lpFree)
|
|
{
|
|
// Remember the first free entry in case we need it.
|
|
lpFree = lpCurrent;
|
|
}
|
|
lpCurrent++;
|
|
}
|
|
return (lpEntry!=NULL) ? lpEntry : lpFree;
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Member: CLockPerfTracker::ReaderWaiting(), public
|
|
//
|
|
// Synopsis: Called by the lock code when a thread attempts to enter
|
|
// a critical section for reading.
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
void CLockPerfTracker::ReaderWaiting(const char*pszFile,
|
|
DWORD dwLine,
|
|
const char* pszLockName,
|
|
void *lpLockPtr)
|
|
{
|
|
if (gbLockPerf)
|
|
{
|
|
// Just call WriterWaiting till we need different semantics...
|
|
WriterWaiting(pszFile,
|
|
dwLine,
|
|
pszLockName,
|
|
lpLockPtr, FALSE /*bWriter*/);
|
|
}
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Member: CLockPerfTracker::WriterWaiting(), public
|
|
//
|
|
// Synopsis: Called by the lock code when a thread attempts to enter
|
|
// a critical section for writing.
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
void CLockPerfTracker::WriterWaiting(const char*pszFile,
|
|
DWORD dwLine,
|
|
const char* pszLockName,
|
|
void *lpLockPtr,
|
|
BOOL bWriter /*default TRUE*/)
|
|
{
|
|
if (gbLockPerf)
|
|
{
|
|
CTlsLockPerf *pTlsLP = GetTlsLockPerfEntry(lpLockPtr);
|
|
|
|
// Will assert if we are getting more than TLS_LOCKPERF_MAX
|
|
// locks one after another without releasing any.
|
|
LockAssert(pTlsLP);
|
|
|
|
if (pTlsLP->_dwFlags & TLS_LOCKPERF_INUSE)
|
|
{
|
|
// Recursion on the lock. Request for the lock while holding it.
|
|
// Can't be waiting recursively!
|
|
LockAssert(pTlsLP->_dwFlags & TLS_LOCKPERF_ENTERED);
|
|
}
|
|
else
|
|
{
|
|
ULONG idx;
|
|
ULONG loc = FindOrCreateFileTableEntry(pszFile,
|
|
dwLine,
|
|
pszLockName,
|
|
lpLockPtr,
|
|
bWriter,
|
|
&idx);
|
|
|
|
LockAssert(loc < MAX_LOCKPERF_FILEENTRY);
|
|
|
|
//save table indices in TLS, for quick access later.
|
|
pTlsLP->_loc = loc;
|
|
pTlsLP->_idx = idx;
|
|
pTlsLP->_lpLockPtr = lpLockPtr;
|
|
pTlsLP->_dwFlags = TLS_LOCKPERF_WAITING | TLS_LOCKPERF_INUSE; //new Tls entry!
|
|
pTlsLP->_dwRecursion = 0; //we set this to 1 upon the first xxxEntered
|
|
|
|
//store request time in TLS (last thing done to not skew lock wait/use times)
|
|
QueryPerformanceCounter(&pTlsLP->_liRequest);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Member: CLockPerfTracker::ReaderEntered(), public
|
|
//
|
|
// Synopsis: Called by lock code when a thread is granted access to
|
|
// a critical section for reading.
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
// REVIEW: Life will be easier for us if we enforce that the lock code
|
|
// must detect and inform if this is the first reader in the critical
|
|
// section. (Similarly for the last reader leaving.)
|
|
//------------------------------------------------------------------------------
|
|
void CLockPerfTracker::ReaderEntered(const char*pszFile,
|
|
DWORD dwLine,
|
|
const char* pszLockName,
|
|
void *lpLockPtr)
|
|
{
|
|
if (gbLockPerf)
|
|
{
|
|
ULONG idx = FindLockTableEntry(lpLockPtr);
|
|
if (InterlockedIncrement((LONG*)&_LockData[idx].dwNumReaders)==1)
|
|
{
|
|
QueryPerformanceCounter(&_LockData[idx].liEntered);
|
|
}
|
|
// rest of the work is done by WriterEntered
|
|
WriterEntered(pszFile, dwLine, pszLockName, lpLockPtr, /*bWriter*/ FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Member: CLockPerfTracker::WriterEntered(), public
|
|
//
|
|
// Synopsis: Called by lock code when a thread is granted access to
|
|
// a critical section for writing.
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
void CLockPerfTracker::WriterEntered(const char*pszFile,
|
|
DWORD dwLine,
|
|
const char* pszLockName,
|
|
void *lpLockPtr,
|
|
BOOL bWriter /*default TRUE*/)
|
|
{
|
|
if (gbLockPerf)
|
|
{
|
|
CTlsLockPerf *pTlsLP = GetTlsLockPerfEntry(lpLockPtr);
|
|
|
|
/*
|
|
// REVIEW: Should we force lock implementation to call ReaderWaiting/WriterWaiting
|
|
// even if there is no reason to wait? In that case the following assertion is true.
|
|
|
|
// There has to be an entry, either marked waiting or entered (recursive lock)
|
|
LockAssert( pTlsLP && ((pTlsLP->_dwFlags & TLS_LOCKPERF_WAITING) || (pTlsLP->_dwFlags & TLS_LOCKPERF_ENTERED)) );
|
|
*/
|
|
|
|
if (!(pTlsLP->_dwFlags & TLS_LOCKPERF_INUSE))
|
|
{
|
|
// Someone called xxxEntered directly (without calling xxxWaiting)
|
|
ULONG idx;
|
|
ULONG loc = FindOrCreateFileTableEntry(pszFile,
|
|
dwLine,
|
|
pszLockName,
|
|
lpLockPtr,
|
|
bWriter,
|
|
&idx);
|
|
LockAssert(loc < MAX_LOCKPERF_FILEENTRY);
|
|
|
|
// save the table indices in TLS, for quick access later.
|
|
pTlsLP->_loc = loc;
|
|
pTlsLP->_idx = idx;
|
|
pTlsLP->_lpLockPtr = lpLockPtr;
|
|
pTlsLP->_dwFlags = TLS_LOCKPERF_ENTERED | TLS_LOCKPERF_INUSE;
|
|
pTlsLP->_dwRecursion = 0;
|
|
|
|
QueryPerformanceCounter(&pTlsLP->_liEntered);
|
|
pTlsLP->_liRequest = pTlsLP->_liEntered;
|
|
}
|
|
else if (pTlsLP->_dwFlags & TLS_LOCKPERF_WAITING)
|
|
{
|
|
QueryPerformanceCounter(&pTlsLP->_liEntered);
|
|
// Not waiting any more.
|
|
pTlsLP->_dwFlags |= TLS_LOCKPERF_ENTERED;
|
|
pTlsLP->_dwFlags &= ~TLS_LOCKPERF_WAITING;
|
|
}
|
|
pTlsLP->_dwRecursion++; // 1 means first level entry (i.e. no recursion)
|
|
}
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Member: CLockPerfTracker::ReaderLeaving(), public
|
|
//
|
|
// Synopsis: Called by the lock code when a reader is leaving a critical
|
|
// section.
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
void CLockPerfTracker::ReaderLeaving(void *lpLockPtr)
|
|
{
|
|
if (gbLockPerf)
|
|
{
|
|
ULONG idx = FindLockTableEntry(lpLockPtr);
|
|
|
|
LARGE_INTEGER liEntered = _LockData[idx].liEntered;
|
|
if (InterlockedDecrement((LONG*)&_LockData[idx].dwNumReaders) == 0)
|
|
{
|
|
// Last reader leaving
|
|
LARGE_INTEGER liDelta, liRem;
|
|
QueryPerformanceCounter(&liDelta);
|
|
|
|
liDelta = RtlLargeIntegerSubtract(liDelta, liEntered);
|
|
liDelta = RtlExtendedIntegerMultiply(liDelta,1000000);
|
|
liDelta = RtlLargeIntegerDivide(liDelta, _liFreq, &liRem);
|
|
|
|
LockAssert(liDelta.HighPart == 0); // no one must hold a lock for so long!
|
|
|
|
// This must be done inter-locked in case someother thread does
|
|
// a 0->1, 1->0 transition while one thread is in this block.
|
|
InterlockedExchangeAdd((LONG*)&_LockData[idx].dwSharedTime,
|
|
liDelta.LowPart);
|
|
}
|
|
|
|
//Call WriterLeaving to do the rest.
|
|
WriterLeaving(lpLockPtr);
|
|
}
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Member: CLockPerfTracker::WriterLeaving(), public
|
|
//
|
|
// Synopsis: Called by the lock code when a writer is leaving a critical
|
|
// section.
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
void CLockPerfTracker::WriterLeaving(void *lpLockPtr)
|
|
{
|
|
if (gbLockPerf)
|
|
{
|
|
CTlsLockPerf *pTlsLP = GetTlsLockPerfEntry(lpLockPtr);
|
|
|
|
// There has be to an entry marked entered!
|
|
LockAssert(pTlsLP && (pTlsLP->_dwFlags & TLS_LOCKPERF_ENTERED));
|
|
|
|
pTlsLP->_dwRecursion--;
|
|
|
|
if (pTlsLP->_dwRecursion == 0)
|
|
{
|
|
// The thread is *really* leaving the lock. Do the math!
|
|
LARGE_INTEGER liUnlockTime;
|
|
QueryPerformanceCounter(&liUnlockTime);
|
|
UpdateFileTableEntry(pTlsLP, &liUnlockTime);
|
|
|
|
// Mark the Tls entry as free.
|
|
pTlsLP->_dwFlags &= ~TLS_LOCKPERF_INUSE;
|
|
}
|
|
else
|
|
{
|
|
// The thread is still in the lock!
|
|
LockAssert(pTlsLP->_dwFlags & TLS_LOCKPERF_ENTERED);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Member: CLockPerfTracker::ReportContention(), public
|
|
//
|
|
// Synopsis: Must be called by the lock destroy/cleanup function.
|
|
// This is single threaded by definition.
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
|
|
void CLockPerfTracker::ReportContention(void *lpLockPtr,
|
|
DWORD dwWriteEntrys,
|
|
DWORD dwWriterContention,
|
|
DWORD dwReadEntrys,
|
|
DWORD dwReaderContention)
|
|
{
|
|
// This happens during DLL_PROCESS_DETACH hence single-threaded
|
|
for (int i=0; i<MAX_LOCKS; i++)
|
|
{
|
|
if (_LockData[i].lpLockPtr == lpLockPtr)
|
|
{
|
|
_LockData[i].dwWriterContentionCount = dwWriterContention;
|
|
_LockData[i].dwReaderContentionCount = dwReaderContention;
|
|
|
|
// These asserts may not be very useful since some locks are entered
|
|
// before lock monitoring can be started! Also, monitoring can be
|
|
// turned OFF and ON in windows.
|
|
|
|
// LockAssert( _LockData[i].dwTotalWriteEntrys == dwWriteEntrys);
|
|
// LockAssert( _LockData[i].dwTotalReadEntrys == dwReadEntrys);
|
|
break;
|
|
}
|
|
}
|
|
LockAssert( i<MAX_LOCKS );
|
|
}
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Member: CLockPerfTracker::FindLockTableEntry(), private
|
|
//
|
|
// Synopsis: Finds the entry for a critical section in the lock table.
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
ULONG CLockPerfTracker::FindLockTableEntry(void *lpLockPtr)
|
|
{
|
|
for (int idx=0; idx<MAX_LOCKS; idx++)
|
|
{
|
|
if (_LockData[idx].lpLockPtr == lpLockPtr)
|
|
{
|
|
return idx;
|
|
}
|
|
}
|
|
LockAssert(!"Lock not registered for monitoring!");
|
|
return MAX_LOCKS-1; // just to avoid AVs
|
|
}
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Member: CLockPerfTracker::FindOrCreateFileTableEntry(), private
|
|
//
|
|
// Synopsis: Finds the entry (or creates one) for a critical section guarded
|
|
// by a lock at a {file, line}.
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
ULONG CLockPerfTracker::FindOrCreateFileTableEntry(const char*pszFile,
|
|
DWORD dwLine,
|
|
const char* pszLockName,
|
|
void *lpLockPtr,
|
|
BOOL bWriter,
|
|
DWORD *lpLockTableIndex)
|
|
{
|
|
BOOL bFoundEntry = FALSE;
|
|
CFileLineEntry *pNewTableEntry = NULL;
|
|
ULONG loc = Hash(dwLine);
|
|
|
|
if (loc >= MAX_LOCKPERF_FILEENTRY)
|
|
{
|
|
LockAssert(!"Lock PerfTable full! Increase size.");
|
|
gbLockPerfOverflow = TRUE;
|
|
loc = MAX_LOCKPERF_FILEENTRY-1;
|
|
goto errRet;
|
|
}
|
|
|
|
// If hashing works well this should not take too much time.
|
|
|
|
while (!bFoundEntry)
|
|
{
|
|
while (_FileLineData[loc].lpLockPtr && loc<MAX_LOCKPERF_FILEENTRY)
|
|
{
|
|
if ( (_FileLineData[loc].dwLine==dwLine)
|
|
&&(_FileLineData[loc].pszFile==pszFile) )
|
|
{
|
|
bFoundEntry = TRUE;
|
|
break; //done
|
|
}
|
|
loc++;
|
|
}
|
|
|
|
if (loc >= MAX_LOCKPERF_FILEENTRY)
|
|
{
|
|
gbLockPerfOverflow = TRUE;
|
|
loc = MAX_LOCKPERF_FILEENTRY-1;
|
|
goto errRet;
|
|
}
|
|
|
|
if (!bFoundEntry && !( InterlockedCompareExchange(
|
|
(LONG*)&_FileLineData[loc].lpLockPtr,
|
|
(LONG)lpLockPtr,
|
|
NULL) )
|
|
)
|
|
{
|
|
// We are seeing a new critical section in the code base
|
|
bFoundEntry = TRUE;
|
|
pNewTableEntry = &_FileLineData[loc];
|
|
InterlockedIncrement(&glFileLine); // Global count of code CritSec locations
|
|
}
|
|
}
|
|
|
|
if (pNewTableEntry)
|
|
{ // finish rest of initialization the entry is secured for this code location.
|
|
// REVIEW: Ignoring races here.
|
|
pNewTableEntry->bWriteCritSec = bWriter;
|
|
pNewTableEntry->dwNumEntrys = 0;
|
|
pNewTableEntry->pszLockName = pszLockName;
|
|
pNewTableEntry->pszFile = pszFile;
|
|
pNewTableEntry->dwLine = dwLine;
|
|
pNewTableEntry->dwWaitTime = 0;
|
|
pNewTableEntry->dwLockedTime = 0;
|
|
pNewTableEntry->ulLockTableIdx = FindLockTableEntry(lpLockPtr);
|
|
}
|
|
errRet:
|
|
*lpLockTableIndex = _FileLineData[loc].ulLockTableIdx;
|
|
return loc;
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Member: CLockPerfTracker::UpdateFileTableEntry(), private
|
|
//
|
|
// Synopsis: Adds the waiting time and the locked time for this visit
|
|
// to the cumulative data for the {file,line} entry.
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
void CLockPerfTracker::UpdateFileTableEntry(CTlsLockPerf *pTlsEntry,
|
|
LARGE_INTEGER *pliUnlockTime)
|
|
{
|
|
LockAssert( (pTlsEntry->_dwFlags & TLS_LOCKPERF_INUSE)
|
|
&& (pTlsEntry->_dwFlags & TLS_LOCKPERF_ENTERED) );
|
|
|
|
ULONG idx = pTlsEntry->_idx;
|
|
ULONG loc = pTlsEntry->_loc;
|
|
|
|
LockAssert(loc>=0 && loc < MAX_LOCKPERF_FILEENTRY);
|
|
|
|
LARGE_INTEGER liRem;
|
|
|
|
LARGE_INTEGER liWait = RtlLargeIntegerSubtract(pTlsEntry->_liEntered,
|
|
pTlsEntry->_liRequest);
|
|
liWait = RtlExtendedIntegerMultiply(liWait,1000000);
|
|
liWait = RtlLargeIntegerDivide(liWait,_liFreq,&liRem); // liWait is now in micro-seconds
|
|
|
|
LockAssert(liWait.HighPart == 0); // hopefully no one waits for so long!!
|
|
|
|
LARGE_INTEGER liLocked = RtlLargeIntegerSubtract(*pliUnlockTime,
|
|
pTlsEntry->_liEntered);
|
|
liLocked = RtlExtendedIntegerMultiply(liLocked,1000000);
|
|
liLocked = RtlLargeIntegerDivide(liLocked, _liFreq, &liRem);
|
|
|
|
LockAssert(liLocked.HighPart == 0); // no one must hold a lock for so long!
|
|
|
|
if (_FileLineData[loc].bWriteCritSec)
|
|
{ // Since this is a write location, the lock itself guarantees exclusion
|
|
_FileLineData[loc].dwNumEntrys++;
|
|
_FileLineData[loc].dwWaitTime += liWait.LowPart;
|
|
_FileLineData[loc].dwLockedTime += liLocked.LowPart;
|
|
|
|
/*
|
|
This needs to be here if we wish to compare entry counts reported by
|
|
the lock with our own. For now this is in ProcessPerfData
|
|
_LockData[idx].dwTotalWriteEntrys++;
|
|
_LockData[idx].dwTotalWriteWaitTime += liWait.LowPart;
|
|
_LockData[idx].dwTotalWriteLockTime += liLocked.LowPart;
|
|
*/
|
|
}
|
|
else
|
|
{ // This is a read location.
|
|
// Hence we have to exclude other readers from updating data
|
|
InterlockedIncrement( (LONG*) &_FileLineData[loc].dwNumEntrys );
|
|
InterlockedExchangeAdd( (LONG*) &_FileLineData[loc].dwWaitTime, liWait.LowPart );
|
|
InterlockedExchangeAdd( (LONG*) &_FileLineData[loc].dwLockedTime, liLocked.LowPart );
|
|
|
|
/*
|
|
This needs to be here if we wish to compare entry counts reported by
|
|
the lock with our own. For now this is in ProcessPerfData
|
|
InterlockedIncrement( (LONG*) &_LockData[idx].dwTotalReadEntrys );
|
|
InterlockedExchangeAdd( (LONG*) &_LockData[idx].dwTotalReadWaitTime, liWait.LowPart );
|
|
InterlockedExchangeAdd( (LONG*) &_LockData[idx].dwTotalReadLockTime, liLocked.LowPart );
|
|
*/
|
|
}
|
|
|
|
#if 0
|
|
// Turn this ON, if you want a live log of every Update.
|
|
wsprintfA(gszLockPerfBuf,"\n Lock at %-25s : line %u : Entry # %u",
|
|
(pTableEntry->pszFile[1]==':') ? pTableEntry->pszFile+24
|
|
: pTableEntry->pszFile,
|
|
pTableEntry->dwLine,
|
|
pTableEntry->dwNumEntrys);
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
|
|
wsprintfA(gszLockPerfBuf," Held for: %u mic-sec.",liLockHeld.LowPart);
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
#endif
|
|
}
|
|
|
|
#define HOGLIST_TIME 0
|
|
#define HOGLIST_ENTRY 1
|
|
#define HOGLIST_AVGTIME 2
|
|
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Member: CLockPerfTracker::UpdateHoggers(), private
|
|
//
|
|
// Synopsis: Manages top ten lists.
|
|
// This is done only during Proc_detach (hence thread safe!)
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
void CLockPerfTracker::UpdateHoggers(ULONG* hogList, ULONG index, ULONG listType)
|
|
{
|
|
int i,j;
|
|
|
|
switch (listType)
|
|
{
|
|
case HOGLIST_TIME:
|
|
for (i=0; i<10; i++)
|
|
{
|
|
if (hogList[i]==-1)
|
|
{
|
|
break;
|
|
}
|
|
else if (_FileLineData[hogList[i]].dwLockedTime
|
|
< _FileLineData[index].dwLockedTime)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case HOGLIST_ENTRY:
|
|
for (i=0; i<10; i++)
|
|
{
|
|
if (hogList[i]==-1)
|
|
{
|
|
break;
|
|
}
|
|
else if (_FileLineData[hogList[i]].dwNumEntrys
|
|
< _FileLineData[index].dwNumEntrys)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case HOGLIST_AVGTIME:
|
|
for (i=0; i<10; i++)
|
|
{
|
|
if (hogList[i]==-1)
|
|
{
|
|
break;
|
|
}
|
|
else if (_FileLineData[hogList[i]].dwAvgTime
|
|
< _FileLineData[index].dwAvgTime)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (i<10)
|
|
{
|
|
for (j=9; j>i;j--)
|
|
{
|
|
hogList[j] = hogList[j-1] ;
|
|
}
|
|
hogList[i] = index;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Function: PercentToString
|
|
//
|
|
// Synopsis: Converts numbers like 74326 to "74.33".
|
|
// We do not have float printing support in retail builds.
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
|
|
// The percent argument is passed in 1000 times magnified.
|
|
char gPerc[7];
|
|
inline char *PercentToString(long percent)
|
|
{
|
|
//round-off
|
|
percent = percent/10 + ((percent%10 >= 5)?1:0);
|
|
|
|
//set to fixed length
|
|
percent+=10000;
|
|
|
|
//create room for decimal point (4th char)
|
|
percent = (percent/100)*1000 + (percent%100);
|
|
|
|
_itoa(percent,gPerc,10);
|
|
|
|
gPerc[0] = gPerc[0]-1; //remove the 10000 we added.
|
|
gPerc[3] = '.';
|
|
|
|
return gPerc + (gPerc[0]=='0'? (gPerc[1]=='0'?2:1):0) ;
|
|
}
|
|
|
|
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Member: CLockPerfTracker::OutputFileTableEntry, private
|
|
//
|
|
// Synopsis: Prints out cumulative data for a {file,line} entry
|
|
// We are doing this during Process_Detach, hence thread-safe.
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
void CLockPerfTracker::OutputFileTableEntry(ULONG index,
|
|
ULONG bByName,
|
|
ULONG percent)
|
|
{
|
|
ULONG trimFileName;
|
|
CFileLineEntry *pTableEntry = &_FileLineData[index];
|
|
|
|
if (pTableEntry->pszFile[1]==':')
|
|
{
|
|
trimFileName = 24;
|
|
}
|
|
else
|
|
{
|
|
trimFileName = 0;
|
|
}
|
|
if (bByName)
|
|
{
|
|
wsprintfA(gszLockPerfBuf,"\n %20s %4d %s %6d %7u %6s %% ",
|
|
pTableEntry->pszFile+trimFileName,
|
|
pTableEntry->dwLine,
|
|
pTableEntry->bWriteCritSec ? "Write" : " Read",
|
|
pTableEntry->dwNumEntrys,
|
|
pTableEntry->dwLockedTime,
|
|
PercentToString(percent));
|
|
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
}
|
|
else
|
|
{
|
|
wsprintfA(gszLockPerfBuf,"\n %20s %4d %-14.14s %s %5d %8u %8u",
|
|
pTableEntry->pszFile+trimFileName,
|
|
pTableEntry->dwLine,
|
|
pTableEntry->pszLockName,
|
|
pTableEntry->bWriteCritSec?"Write":" Read",
|
|
pTableEntry->dwNumEntrys,
|
|
pTableEntry->dwLockedTime,
|
|
pTableEntry->dwAvgTime);
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
}
|
|
}
|
|
|
|
#define TITLES_1 \
|
|
wsprintfA(gszLockPerfBuf,"\n File Line LockName Type Entrys TotalTime Avg/Entry"); OutputDebugStringA(gszLockPerfBuf)
|
|
|
|
#define TITLES_2 \
|
|
wsprintfA(gszLockPerfBuf,"\n File Line Type Entrys TotalTime %%-Time "); OutputDebugStringA(gszLockPerfBuf)
|
|
|
|
#define SEPARATOR_1 \
|
|
wsprintfA(gszLockPerfBuf,"\n ==================== ===== ============ ===== ====== ========= ========="); OutputDebugStringA(gszLockPerfBuf)
|
|
|
|
#define SEPARATOR_2 \
|
|
wsprintfA(gszLockPerfBuf,"\n ==================== ===== ====== ====== ========= ======"); OutputDebugStringA(gszLockPerfBuf)
|
|
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Member: CLockPerfTracker::OutputHoggers(), private
|
|
//
|
|
// Synopsis: Prints out a top ten list given an array of indices for the same.
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
void CLockPerfTracker::OutputHoggers(ULONG *hogList)
|
|
{
|
|
TITLES_1;
|
|
SEPARATOR_1;
|
|
|
|
for (int i=0; i<10; i++)
|
|
{
|
|
if (hogList[i]!=-1)
|
|
{
|
|
OutputFileTableEntry(hogList[i],0,0);
|
|
}
|
|
}
|
|
SEPARATOR_1;
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Member: CLockPerfTracker::ProcessPerfData(), public
|
|
//
|
|
// Synopsis: Organizes the perf table data by lockName.
|
|
// Prints lock summary data, top-ten lists.
|
|
// This is done only during Proc_detach (hence thread safe!)
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
void CLockPerfTracker::ProcessPerfData()
|
|
{
|
|
// #### Begin : organize data
|
|
CFileLineEntry *pTableEntry;
|
|
|
|
ULONG hogListTime[10];
|
|
ULONG hogListEntry[10];
|
|
ULONG hogListAvgTime[10];
|
|
|
|
ULONG iName;
|
|
DWORD totalLockTime = 0; // lock sharing not reflected!
|
|
DWORD totalRealLockTime = 0; // takes lock sharing into account!
|
|
DWORD totalLocksCreated = 0; // some locks may never get used!
|
|
|
|
|
|
for (int i=0; i<10; i++)
|
|
{
|
|
hogListTime[i] = -1;
|
|
hogListEntry[i] = -1;
|
|
hogListAvgTime[i] = -1;
|
|
}
|
|
|
|
for (i=0; i<MAX_LOCKPERF_FILEENTRY; i++)
|
|
{
|
|
if (_FileLineData[i].lpLockPtr)
|
|
{
|
|
pTableEntry = &_FileLineData[i];
|
|
if (pTableEntry->dwNumEntrys)
|
|
{
|
|
pTableEntry->dwAvgTime =
|
|
pTableEntry->dwLockedTime/pTableEntry->dwNumEntrys;
|
|
}
|
|
else
|
|
{
|
|
pTableEntry->dwAvgTime = 0;
|
|
}
|
|
UpdateHoggers(hogListTime,i, 0);
|
|
UpdateHoggers(hogListEntry,i, 1);
|
|
UpdateHoggers(hogListAvgTime,i, 2);
|
|
_numCritSecs++;
|
|
|
|
//REVIEW: how should we take shared time into account?
|
|
totalLockTime = totalLockTime + pTableEntry->dwLockedTime;
|
|
|
|
iName = FindLockTableEntry(_FileLineData[i].lpLockPtr);
|
|
if (_LockData[iName].pszLockName==NULL)
|
|
{
|
|
// First file table entry for this lock
|
|
// We use pszLockName==NULL to tell if a lock got used!
|
|
_LockData[iName].pszLockName = pTableEntry->pszLockName;
|
|
_LockData[iName].dwHead = i;
|
|
_LockData[iName].dwTail = i;
|
|
|
|
_numLocks++;
|
|
}
|
|
else
|
|
{ //CritSec is already in our list.
|
|
_FileLineData[_LockData[iName].dwTail].dwNext = i;
|
|
_LockData[iName].dwTail = i;
|
|
}
|
|
|
|
if (pTableEntry->bWriteCritSec)
|
|
{
|
|
_LockData[iName].dwTotalWriteLockTime += pTableEntry->dwLockedTime;
|
|
_LockData[iName].dwTotalWriteEntrys += pTableEntry->dwNumEntrys;
|
|
_LockData[iName].dwTotalWriteWaitTime += pTableEntry->dwWaitTime;
|
|
}
|
|
else
|
|
{
|
|
_LockData[iName].dwTotalReadLockTime += pTableEntry->dwLockedTime;
|
|
_LockData[iName].dwTotalReadEntrys += pTableEntry->dwNumEntrys;
|
|
_LockData[iName].dwTotalReadWaitTime += pTableEntry->dwWaitTime;
|
|
}
|
|
} // if In Use
|
|
} // for each table entry
|
|
// #### End : organize data
|
|
|
|
wsprintfA(gszLockPerfBuf,
|
|
"\n\n ============= LOCK_PERF: TOP TEN LISTS ===========\n");
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
|
|
wsprintfA(gszLockPerfBuf,
|
|
"\n\n ============= Top Ten Hoggers by total time ===========\n");
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
OutputHoggers(hogListTime);
|
|
|
|
wsprintfA(gszLockPerfBuf,
|
|
"\n\n ============= Top Ten Hoggers by crit-sec Entrys ===========\n");
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
OutputHoggers(hogListEntry);
|
|
|
|
wsprintfA(gszLockPerfBuf,
|
|
"\n\n ============= Top Ten Hoggers by avg time per Entry ===========\n");
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
OutputHoggers(hogListAvgTime);
|
|
|
|
SEPARATOR_1;
|
|
wsprintfA(gszLockPerfBuf,
|
|
"\n\n ============= LOCK_PERF: OVERALL LOCK STATS ===========\n");
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
|
|
for (i=0; i<MAX_LOCKS; i++)
|
|
{
|
|
if (_LockData[i].lpLockPtr)
|
|
{
|
|
totalLocksCreated++;
|
|
if (_LockData[i].pszLockName)
|
|
{
|
|
// lock got used!
|
|
totalRealLockTime += _LockData[i].dwTotalWriteLockTime;
|
|
if (_LockData[i].dwTotalReadEntrys)
|
|
{
|
|
totalRealLockTime += _LockData[i].dwSharedTime;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
wsprintfA(gszLockPerfBuf,
|
|
"\n\n TOTAL locks created = %u",
|
|
totalLocksCreated);
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
|
|
wsprintfA(gszLockPerfBuf,
|
|
"\n TOTAL locks used = %u",
|
|
_numLocks);
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
wsprintfA(gszLockPerfBuf,
|
|
"\n\n TOTAL code critSec areas covered = %u",
|
|
_numCritSecs);
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
|
|
wsprintfA(gszLockPerfBuf,
|
|
"\n\n\n TOTAL time spent in all critSecs = %u micro-sec\n\n",
|
|
totalLockTime);
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
wsprintfA(gszLockPerfBuf,
|
|
" [This is the sum of individual thread times.\n This does not take overlaps (shared work) into account.]");
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
|
|
wsprintfA(gszLockPerfBuf,
|
|
"\n\n\n TOTAL real time spent in all critSecs = %u micro-sec\n\n",
|
|
totalRealLockTime);
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
wsprintfA(gszLockPerfBuf," [This takes shared time into account.]");
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
|
|
wsprintfA(gszLockPerfBuf,
|
|
"\n\n ## Warning ##: The total time counters overflow in about 70 minutes!\n\n");
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
}
|
|
|
|
//+-----------------------------------------------------------------------------
|
|
//
|
|
// Member: CLockPerfTracker::OutputPerfData(), public
|
|
//
|
|
// Synopsis: Prints out the {file,line} table.
|
|
// This is done only during Proc_detach (hence thread safe!)
|
|
//
|
|
// History: 20-Dec-98 mprabhu Created
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
void CLockPerfTracker::OutputPerfData()
|
|
{
|
|
if (glFileLine) //if monitoring was ON at any time!
|
|
{
|
|
if (gbLockPerfOverflow)
|
|
{
|
|
wsprintfA(gszLockPerfBuf,
|
|
"\n ### Warning: Overflow in lock perf data buffers!\n \
|
|
### Increase array sizes and recompile!!\n");
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
}
|
|
|
|
ULONG iStart, iNext, iEnd;
|
|
const char *pszThisFile;
|
|
|
|
ULONG iName;
|
|
LARGE_INTEGER liPerc, liDiv, liRem ;
|
|
|
|
|
|
//TITLES_1;
|
|
//SEPARATOR_1;
|
|
ProcessPerfData();
|
|
|
|
|
|
// #### Begin: output by LockName:
|
|
SEPARATOR_2;
|
|
|
|
wsprintfA(gszLockPerfBuf,
|
|
"\n\n ============= LOCK_PERF: PER LOCK STATS ===========\n");
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
BOOL bShared;
|
|
int iShared;
|
|
for (iName=0; iName<_numLocks; iName++)
|
|
{
|
|
if (_LockData[iName].pszLockName) { //if the lock got used
|
|
SEPARATOR_2;
|
|
wsprintfA(gszLockPerfBuf,
|
|
"\n\n\n #### ++++++++ Summary for %14s , this = %lx ++++++++ ####",
|
|
_LockData[iName].pszLockName,
|
|
(DWORD)_LockData[iName].lpLockPtr);
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
|
|
wsprintfA(gszLockPerfBuf,
|
|
"\n\n WrtLockTime:%12u WrtEntrys:%14u\n WrtWait:%16u WrtContention:%10u \n",
|
|
_LockData[iName].dwTotalWriteLockTime,
|
|
_LockData[iName].dwTotalWriteEntrys,
|
|
_LockData[iName].dwTotalWriteWaitTime,
|
|
_LockData[iName].dwWriterContentionCount);
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
|
|
wsprintfA(gszLockPerfBuf,
|
|
"\n RdLockTime:%13u RdEntrys:%15u\n RdWait:%17u RdContention:%11u \n",
|
|
_LockData[iName].dwTotalReadLockTime,
|
|
_LockData[iName].dwTotalReadEntrys,
|
|
_LockData[iName].dwTotalReadWaitTime,
|
|
_LockData[iName].dwReaderContentionCount);
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
|
|
wsprintfA(gszLockPerfBuf,"\n Shared Read Time: %10u\n",
|
|
_LockData[iName].dwSharedTime);
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
|
|
|
|
TITLES_2;
|
|
SEPARATOR_2;
|
|
iNext = _LockData[iName].dwHead;
|
|
iEnd = _LockData[iName].dwTail;
|
|
while (1)
|
|
{
|
|
liPerc.HighPart = 0;
|
|
liPerc.LowPart = _FileLineData[iNext].dwLockedTime;
|
|
liPerc = RtlExtendedIntegerMultiply(liPerc,100000);
|
|
liDiv.HighPart = 0;
|
|
if (_FileLineData[iNext].bWriteCritSec)
|
|
{
|
|
liDiv.LowPart = _LockData[iName].dwTotalWriteLockTime;
|
|
}
|
|
else
|
|
{
|
|
liDiv.LowPart = _LockData[iName].dwTotalReadLockTime;
|
|
}
|
|
|
|
liPerc = RtlLargeIntegerDivide(liPerc, liDiv, &liRem);
|
|
|
|
OutputFileTableEntry(iNext,1, liPerc.LowPart);
|
|
if (iNext == iEnd)
|
|
break;
|
|
iNext = _FileLineData[iNext].dwNext;
|
|
}
|
|
|
|
SEPARATOR_2;
|
|
wsprintfA(gszLockPerfBuf,
|
|
"\n ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ \n");
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
} //if the lock got used!
|
|
}
|
|
|
|
SEPARATOR_2;
|
|
// #### End: output by LockName:
|
|
|
|
SEPARATOR_1;
|
|
|
|
|
|
#if 0 //This lists lock data by fileNames
|
|
// #### Begin: output by fileName : location:
|
|
wsprintfA(gszLockPerfBuf,
|
|
"\n\n\n ======= CritSec data listed by FileName: =======\n");
|
|
OutputDebugStringA(gszLockPerfBuf);
|
|
|
|
TITLES_1;
|
|
SEPARATOR_1;
|
|
|
|
pszThisFile = NULL;
|
|
i=0;
|
|
while (!_FileLineData[i].bInUse)
|
|
i++;
|
|
iNext = i;
|
|
|
|
while (iNext < MAX_LOCKPERF_FILEENTRY)
|
|
{
|
|
iStart = iNext;
|
|
iNext = MAX_LOCKPERF_FILEENTRY;
|
|
pszThisFile= _FileLineData[iStart].pszFile;
|
|
for (i=iStart;i<MAX_LOCKPERF_FILEENTRY;i++)
|
|
{
|
|
if (_FileLineData[i].bInUse)
|
|
{
|
|
if (pszThisFile==_FileLineData[i].pszFile)
|
|
{
|
|
OutputFileTableEntry(i,0,0);
|
|
_FileLineData[i].bInUse = FALSE;
|
|
}
|
|
else if (iNext==MAX_LOCKPERF_FILEENTRY)
|
|
{
|
|
iNext=i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// #### End: output by fileName : location:
|
|
#endif //0 This lists lock data by fileNames
|
|
|
|
} //if glFileLine
|
|
}
|
|
|
|
#endif //LOCK_PERF==1
|