michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * nssilock.c - NSS lock instrumentation wrapper functions michael@0: * michael@0: * NOTE - These are not public interfaces michael@0: * michael@0: * Implementation Notes: michael@0: * I've tried to make the instrumentation relatively non-intrusive. michael@0: * To do this, I have used a single PR_LOG() call in each michael@0: * instrumented function. There's room for improvement. michael@0: * michael@0: * michael@0: */ michael@0: michael@0: #include "prinit.h" michael@0: #include "prerror.h" michael@0: #include "prlock.h" michael@0: #include "prmem.h" michael@0: #include "prenv.h" michael@0: #include "prcvar.h" michael@0: #include "prio.h" michael@0: michael@0: #if defined(NEED_NSS_ILOCK) michael@0: #include "prlog.h" michael@0: #include "nssilock.h" michael@0: michael@0: /* michael@0: ** Declare the instrumented PZLock michael@0: */ michael@0: struct pzlock_s { michael@0: PRLock *lock; /* the PZLock to be instrumented */ michael@0: PRIntervalTime time; /* timestamp when the lock was aquired */ michael@0: nssILockType ltype; michael@0: }; michael@0: michael@0: /* michael@0: ** Declare the instrumented PZMonitor michael@0: */ michael@0: struct pzmonitor_s { michael@0: PRMonitor *mon; /* the PZMonitor to be instrumented */ michael@0: PRIntervalTime time; /* timestamp when the monitor was aquired */ michael@0: nssILockType ltype; michael@0: }; michael@0: michael@0: /* michael@0: ** Declare the instrumented PZCondVar michael@0: */ michael@0: struct pzcondvar_s { michael@0: PRCondVar *cvar; /* the PZCondVar to be instrumented */ michael@0: nssILockType ltype; michael@0: }; michael@0: michael@0: michael@0: /* michael@0: ** Define a CallOnce type to ensure serialized self-initialization michael@0: */ michael@0: static PRCallOnceType coNssILock; /* CallOnce type */ michael@0: static PRIntn nssILockInitialized; /* initialization done when 1 */ michael@0: static PRLogModuleInfo *nssILog; /* Log instrumentation to this handle */ michael@0: michael@0: michael@0: #define NUM_TT_ENTRIES 6000000 michael@0: static PRInt32 traceIndex = -1; /* index into trace table */ michael@0: static struct pzTrace_s *tt; /* pointer to trace table */ michael@0: static PRInt32 ttBufSize = (NUM_TT_ENTRIES * sizeof(struct pzTrace_s )); michael@0: static PRCondVar *ttCVar; michael@0: static PRLock *ttLock; michael@0: static PRFileDesc *ttfd; /* trace table file */ michael@0: michael@0: /* michael@0: ** Vtrace() -- Trace events, write events to external media michael@0: ** michael@0: ** Vtrace() records traced events in an in-memory trace table michael@0: ** when the trace table fills, Vtrace writes the entire table michael@0: ** to a file. michael@0: ** michael@0: ** data can be lost! michael@0: ** michael@0: */ michael@0: static void Vtrace( michael@0: nssILockOp op, michael@0: nssILockType ltype, michael@0: PRIntervalTime callTime, michael@0: PRIntervalTime heldTime, michael@0: void *lock, michael@0: PRIntn line, michael@0: char *file michael@0: ) { michael@0: PRInt32 idx; michael@0: struct pzTrace_s *tp; michael@0: michael@0: RetryTrace: michael@0: idx = PR_ATOMIC_INCREMENT( &traceIndex ); michael@0: while( NUM_TT_ENTRIES <= idx || op == FlushTT ) { michael@0: if( NUM_TT_ENTRIES == idx || op == FlushTT ) { michael@0: int writeSize = idx * sizeof(struct pzTrace_s); michael@0: PR_Lock(ttLock); michael@0: PR_Write( ttfd, tt, writeSize ); michael@0: traceIndex = -1; michael@0: PR_NotifyAllCondVar( ttCVar ); michael@0: PR_Unlock(ttLock); michael@0: goto RetryTrace; michael@0: } else { michael@0: PR_Lock(ttLock); michael@0: while( NUM_TT_ENTRIES < idx ) michael@0: PR_WaitCondVar(ttCVar, PR_INTERVAL_NO_WAIT); michael@0: PR_Unlock(ttLock); michael@0: goto RetryTrace; michael@0: } michael@0: } /* end while() */ michael@0: michael@0: /* create the trace entry */ michael@0: tp = tt + idx; michael@0: tp->threadID = PR_GetThreadID(PR_GetCurrentThread()); michael@0: tp->op = op; michael@0: tp->ltype = ltype; michael@0: tp->callTime = callTime; michael@0: tp->heldTime = heldTime; michael@0: tp->lock = lock; michael@0: tp ->line = line; michael@0: strcpy(tp->file, file ); michael@0: return; michael@0: } /* --- end Vtrace() --- */ michael@0: michael@0: /* michael@0: ** pz_TraceFlush() -- Force trace table write to file michael@0: ** michael@0: */ michael@0: extern void pz_TraceFlush( void ) michael@0: { michael@0: Vtrace( FlushTT, nssILockSelfServ, 0, 0, NULL, 0, "" ); michael@0: return; michael@0: } /* --- end pz_TraceFlush() --- */ michael@0: michael@0: /* michael@0: ** nssILockInit() -- Initialization for nssilock michael@0: ** michael@0: ** This function is called from the CallOnce mechanism. michael@0: */ michael@0: static PRStatus michael@0: nssILockInit( void ) michael@0: { michael@0: int i; michael@0: nssILockInitialized = 1; michael@0: michael@0: /* new log module */ michael@0: nssILog = PR_NewLogModule("nssilock"); michael@0: if ( NULL == nssILog ) { michael@0: return(PR_FAILURE); michael@0: } michael@0: michael@0: tt = PR_Calloc( NUM_TT_ENTRIES, sizeof(struct pzTrace_s)); michael@0: if (NULL == tt ) { michael@0: fprintf(stderr, "nssilock: can't allocate trace table\n"); michael@0: exit(1); michael@0: } michael@0: michael@0: ttfd = PR_Open( "xxxTTLog", PR_CREATE_FILE | PR_WRONLY, 0666 ); michael@0: if ( NULL == ttfd ) { michael@0: fprintf( stderr, "Oh Drat! Can't open 'xxxTTLog'\n"); michael@0: exit(1); michael@0: } michael@0: michael@0: ttLock = PR_NewLock(); michael@0: ttCVar = PR_NewCondVar(ttLock); michael@0: michael@0: return(PR_SUCCESS); michael@0: } /* --- end nssILockInit() --- */ michael@0: michael@0: extern PZLock * pz_NewLock( michael@0: nssILockType ltype, michael@0: char *file, michael@0: PRIntn line ) michael@0: { michael@0: PRStatus rc; michael@0: PZLock *lock; michael@0: michael@0: /* Self Initialize the nssILock feature */ michael@0: if (!nssILockInitialized) { michael@0: rc = PR_CallOnce( &coNssILock, nssILockInit ); michael@0: if ( PR_FAILURE == rc ) { michael@0: PR_SetError( PR_UNKNOWN_ERROR, 0 ); michael@0: return( NULL ); michael@0: } michael@0: } michael@0: michael@0: lock = PR_NEWZAP( PZLock ); michael@0: if ( NULL != lock ) { michael@0: lock->ltype = ltype; michael@0: lock->lock = PR_NewLock(); michael@0: if ( NULL == lock->lock ) { michael@0: PR_DELETE( lock ); michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: } michael@0: } else { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: } michael@0: michael@0: Vtrace( NewLock, ltype, 0, 0, lock, line, file ); michael@0: return(lock); michael@0: } /* --- end pz_NewLock() --- */ michael@0: michael@0: extern void michael@0: pz_Lock( michael@0: PZLock *lock, michael@0: char *file, michael@0: PRIntn line michael@0: ) michael@0: { michael@0: PRIntervalTime callTime; michael@0: michael@0: callTime = PR_IntervalNow(); michael@0: PR_Lock( lock->lock ); michael@0: lock->time = PR_IntervalNow(); michael@0: callTime = lock->time - callTime; michael@0: michael@0: Vtrace( Lock, lock->ltype, callTime, 0, lock, line, file ); michael@0: return; michael@0: } /* --- end pz_Lock() --- */ michael@0: michael@0: extern PRStatus michael@0: pz_Unlock( michael@0: PZLock *lock, michael@0: char *file, michael@0: PRIntn line michael@0: ) michael@0: { michael@0: PRStatus rc; michael@0: PRIntervalTime callTime, now, heldTime; michael@0: michael@0: callTime = PR_IntervalNow(); michael@0: rc = PR_Unlock( lock->lock ); michael@0: now = PR_IntervalNow(); michael@0: callTime = now - callTime; michael@0: heldTime = now - lock->time; michael@0: Vtrace( Unlock, lock->ltype, callTime, heldTime, lock, line, file ); michael@0: return( rc ); michael@0: } /* --- end pz_Unlock() --- */ michael@0: michael@0: extern void michael@0: pz_DestroyLock( michael@0: PZLock *lock, michael@0: char *file, michael@0: PRIntn line michael@0: ) michael@0: { michael@0: Vtrace( DestroyLock, lock->ltype, 0, 0, lock, line, file ); michael@0: PR_DestroyLock( lock->lock ); michael@0: PR_DELETE( lock ); michael@0: return; michael@0: } /* --- end pz_DestroyLock() --- */ michael@0: michael@0: michael@0: michael@0: extern PZCondVar * michael@0: pz_NewCondVar( michael@0: PZLock *lock, michael@0: char *file, michael@0: PRIntn line michael@0: ) michael@0: { michael@0: PZCondVar *cvar; michael@0: michael@0: cvar = PR_NEWZAP( PZCondVar ); michael@0: if ( NULL == cvar ) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: } else { michael@0: cvar->ltype = lock->ltype; michael@0: cvar->cvar = PR_NewCondVar( lock->lock ); michael@0: if ( NULL == cvar->cvar ) { michael@0: PR_DELETE( cvar ); michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: } michael@0: michael@0: } michael@0: Vtrace( NewCondVar, lock->ltype, 0, 0, cvar, line, file ); michael@0: return( cvar ); michael@0: } /* --- end pz_NewCondVar() --- */ michael@0: michael@0: extern void michael@0: pz_DestroyCondVar( michael@0: PZCondVar *cvar, michael@0: char *file, michael@0: PRIntn line michael@0: ) michael@0: { michael@0: Vtrace( DestroyCondVar, cvar->ltype, 0, 0, cvar, line, file ); michael@0: PR_DestroyCondVar( cvar->cvar ); michael@0: PR_DELETE( cvar ); michael@0: } /* --- end pz_DestroyCondVar() --- */ michael@0: michael@0: extern PRStatus michael@0: pz_WaitCondVar( michael@0: PZCondVar *cvar, michael@0: PRIntervalTime timeout, michael@0: char *file, michael@0: PRIntn line michael@0: ) michael@0: { michael@0: PRStatus rc; michael@0: PRIntervalTime callTime; michael@0: michael@0: callTime = PR_IntervalNow(); michael@0: rc = PR_WaitCondVar( cvar->cvar, timeout ); michael@0: callTime = PR_IntervalNow() - callTime; michael@0: michael@0: Vtrace( WaitCondVar, cvar->ltype, callTime, 0, cvar, line, file ); michael@0: return(rc); michael@0: } /* --- end pz_WaitCondVar() --- */ michael@0: michael@0: extern PRStatus michael@0: pz_NotifyCondVar( michael@0: PZCondVar *cvar, michael@0: char *file, michael@0: PRIntn line michael@0: ) michael@0: { michael@0: PRStatus rc; michael@0: michael@0: rc = PR_NotifyCondVar( cvar->cvar ); michael@0: michael@0: Vtrace( NotifyCondVar, cvar->ltype, 0, 0, cvar, line, file ); michael@0: return(rc); michael@0: } /* --- end pz_NotifyCondVar() --- */ michael@0: michael@0: extern PRStatus michael@0: pz_NotifyAllCondVar( michael@0: PZCondVar *cvar, michael@0: char *file, michael@0: PRIntn line michael@0: ) michael@0: { michael@0: PRStatus rc; michael@0: michael@0: rc = PR_NotifyAllCondVar( cvar->cvar ); michael@0: michael@0: Vtrace( NotifyAllCondVar, cvar->ltype, 0, 0, cvar, line, file ); michael@0: return(rc); michael@0: } /* --- end pz_NotifyAllCondVar() --- */ michael@0: michael@0: extern PZMonitor * michael@0: pz_NewMonitor( michael@0: nssILockType ltype, michael@0: char *file, michael@0: PRIntn line michael@0: ) michael@0: { michael@0: PRStatus rc; michael@0: PZMonitor *mon; michael@0: michael@0: /* Self Initialize the nssILock feature */ michael@0: if (!nssILockInitialized) { michael@0: rc = PR_CallOnce( &coNssILock, nssILockInit ); michael@0: if ( PR_FAILURE == rc ) { michael@0: PR_SetError( PR_UNKNOWN_ERROR, 0 ); michael@0: return( NULL ); michael@0: } michael@0: } michael@0: michael@0: mon = PR_NEWZAP( PZMonitor ); michael@0: if ( NULL != mon ) { michael@0: mon->ltype = ltype; michael@0: mon->mon = PR_NewMonitor(); michael@0: if ( NULL == mon->mon ) { michael@0: PR_DELETE( mon ); michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: } michael@0: } else { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: } michael@0: michael@0: Vtrace( NewMonitor, ltype, 0, 0, mon, line, file ); michael@0: return(mon); michael@0: } /* --- end pz_NewMonitor() --- */ michael@0: michael@0: extern void michael@0: pz_DestroyMonitor( michael@0: PZMonitor *mon, michael@0: char *file, michael@0: PRIntn line michael@0: ) michael@0: { michael@0: Vtrace( DestroyMonitor, mon->ltype, 0, 0, mon, line, file ); michael@0: PR_DestroyMonitor( mon->mon ); michael@0: PR_DELETE( mon ); michael@0: return; michael@0: } /* --- end pz_DestroyMonitor() --- */ michael@0: michael@0: extern void michael@0: pz_EnterMonitor( michael@0: PZMonitor *mon, michael@0: char *file, michael@0: PRIntn line michael@0: ) michael@0: { michael@0: PRIntervalTime callTime, now; michael@0: michael@0: callTime = PR_IntervalNow(); michael@0: PR_EnterMonitor( mon->mon ); michael@0: now = PR_IntervalNow(); michael@0: callTime = now - callTime; michael@0: if ( PR_GetMonitorEntryCount(mon->mon) == 1 ) { michael@0: mon->time = now; michael@0: } michael@0: Vtrace( EnterMonitor, mon->ltype, callTime, 0, mon, line, file ); michael@0: return; michael@0: } /* --- end pz_EnterMonitor() --- */ michael@0: michael@0: extern PRStatus michael@0: pz_ExitMonitor( michael@0: PZMonitor *mon, michael@0: char *file, michael@0: PRIntn line michael@0: ) michael@0: { michael@0: PRStatus rc; michael@0: PRIntervalTime callTime, now, heldTime; michael@0: PRIntn mec = PR_GetMonitorEntryCount( mon->mon ); michael@0: michael@0: heldTime = (PRIntervalTime)-1; michael@0: callTime = PR_IntervalNow(); michael@0: rc = PR_ExitMonitor( mon->mon ); michael@0: now = PR_IntervalNow(); michael@0: callTime = now - callTime; michael@0: if ( mec == 1 ) michael@0: heldTime = now - mon->time; michael@0: Vtrace( ExitMonitor, mon->ltype, callTime, heldTime, mon, line, file ); michael@0: return( rc ); michael@0: } /* --- end pz_ExitMonitor() --- */ michael@0: michael@0: extern PRIntn michael@0: pz_GetMonitorEntryCount( michael@0: PZMonitor *mon, michael@0: char *file, michael@0: PRIntn line michael@0: ) michael@0: { michael@0: return( PR_GetMonitorEntryCount(mon->mon)); michael@0: } /* --- end pz_GetMonitorEntryCount() --- */ michael@0: michael@0: michael@0: extern PRStatus michael@0: pz_Wait( michael@0: PZMonitor *mon, michael@0: PRIntervalTime ticks, michael@0: char *file, michael@0: PRIntn line michael@0: ) michael@0: { michael@0: PRStatus rc; michael@0: PRIntervalTime callTime; michael@0: michael@0: callTime = PR_IntervalNow(); michael@0: rc = PR_Wait( mon->mon, ticks ); michael@0: callTime = PR_IntervalNow() - callTime; michael@0: Vtrace( Wait, mon->ltype, callTime, 0, mon, line, file ); michael@0: return( rc ); michael@0: } /* --- end pz_Wait() --- */ michael@0: michael@0: extern PRStatus michael@0: pz_Notify( michael@0: PZMonitor *mon, michael@0: char *file, michael@0: PRIntn line michael@0: ) michael@0: { michael@0: PRStatus rc; michael@0: PRIntervalTime callTime; michael@0: michael@0: callTime = PR_IntervalNow(); michael@0: rc = PR_Notify( mon->mon ); michael@0: callTime = PR_IntervalNow() - callTime; michael@0: Vtrace( Notify, mon->ltype, callTime, 0, mon, line, file ); michael@0: return( rc ); michael@0: } /* --- end pz_Notify() --- */ michael@0: michael@0: extern PRStatus michael@0: pz_NotifyAll( michael@0: PZMonitor *mon, michael@0: char *file, michael@0: PRIntn line michael@0: ) michael@0: { michael@0: PRStatus rc; michael@0: PRIntervalTime callTime; michael@0: michael@0: callTime = PR_IntervalNow(); michael@0: rc = PR_NotifyAll( mon->mon ); michael@0: callTime = PR_IntervalNow() - callTime; michael@0: Vtrace( NotifyAll, mon->ltype, callTime, 0, mon, line, file ); michael@0: return( rc ); michael@0: } /* --- end pz_NotifyAll() --- */ michael@0: michael@0: #endif /* NEED_NSS_ILOCK */ michael@0: /* --- end nssilock.c --------------------------------- */