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