michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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: ** prtrace.c -- NSPR Trace Instrumentation michael@0: ** michael@0: ** Implement the API defined in prtrace.h michael@0: ** michael@0: ** michael@0: ** michael@0: */ michael@0: michael@0: #include michael@0: #include "primpl.h" michael@0: michael@0: michael@0: #define DEFAULT_TRACE_BUFSIZE ( 1024 * 1024 ) michael@0: #define DEFAULT_BUFFER_SEGMENTS 2 michael@0: michael@0: /* michael@0: ** Enumerate states in a RName structure michael@0: */ michael@0: typedef enum TraceState michael@0: { michael@0: Running = 1, michael@0: Suspended = 2 michael@0: } TraceState; michael@0: michael@0: /* michael@0: ** Define QName structure michael@0: */ michael@0: typedef struct QName michael@0: { michael@0: PRCList link; michael@0: PRCList rNameList; michael@0: char name[PRTRACE_NAME_MAX+1]; michael@0: } QName; michael@0: michael@0: /* michael@0: ** Define RName structure michael@0: */ michael@0: typedef struct RName michael@0: { michael@0: PRCList link; michael@0: PRLock *lock; michael@0: QName *qName; michael@0: TraceState state; michael@0: char name[PRTRACE_NAME_MAX+1]; michael@0: char desc[PRTRACE_DESC_MAX+1]; michael@0: } RName; michael@0: michael@0: michael@0: /* michael@0: ** The Trace Facility database michael@0: ** michael@0: */ michael@0: static PRLogModuleInfo *lm; michael@0: michael@0: static PRLock *traceLock; /* Facility Lock */ michael@0: static PRCList qNameList; /* anchor to all QName structures */ michael@0: static TraceState traceState = Running; michael@0: michael@0: /* michael@0: ** in-memory trace buffer controls michael@0: */ michael@0: static PRTraceEntry *tBuf; /* pointer to buffer */ michael@0: static PRInt32 bufSize; /* size of buffer, in bytes, rounded up to sizeof(PRTraceEntry) */ michael@0: static volatile PRInt32 next; /* index to next PRTraceEntry */ michael@0: static PRInt32 last; /* index of highest numbered trace entry */ michael@0: michael@0: /* michael@0: ** Real-time buffer capture controls michael@0: */ michael@0: static PRInt32 fetchLastSeen = 0; michael@0: static PRBool fetchLostData = PR_FALSE; michael@0: michael@0: /* michael@0: ** Buffer write-to-file controls michael@0: */ michael@0: static PRLock *logLock; /* Sync lock */ michael@0: static PRCondVar *logCVar; /* Sync Condidtion Variable */ michael@0: /* michael@0: ** Inter-thread state communication. michael@0: ** Controling thread writes to logOrder under protection of logCVar michael@0: ** the logging thread reads logOrder and sets logState on Notify. michael@0: ** michael@0: ** logSegments, logCount, logLostData must be read and written under michael@0: ** protection of logLock, logCVar. michael@0: ** michael@0: */ michael@0: static enum LogState michael@0: { michael@0: LogNotRunning, /* Initial state */ michael@0: LogReset, /* Causes logger to re-calc controls */ michael@0: LogActive, /* Logging in progress, set only by log thread */ michael@0: LogSuspend, /* Suspend Logging */ michael@0: LogResume, /* Resume Logging => LogActive */ michael@0: LogStop /* Stop the log thread */ michael@0: } logOrder, logState, localState; /* controlling state variables */ michael@0: static PRInt32 logSegments; /* Number of buffer segments */ michael@0: static PRInt32 logEntries; /* number of Trace Entries in the buffer */ michael@0: static PRInt32 logEntriesPerSegment; /* number of PRTraceEntries per buffer segment */ michael@0: static PRInt32 logSegSize; /* size of buffer segment */ michael@0: static PRInt32 logCount; /* number of segments pending output */ michael@0: static PRInt32 logLostData; /* number of lost log buffer segments */ michael@0: michael@0: /* michael@0: ** end Trace Database michael@0: ** michael@0: */ michael@0: michael@0: /* michael@0: ** _PR_InitializeTrace() -- Initialize the trace facility michael@0: */ michael@0: static void NewTraceBuffer( PRInt32 size ) michael@0: { michael@0: /* michael@0: ** calculate the size of the buffer michael@0: ** round down so that each segment has the same number of michael@0: ** trace entries michael@0: */ michael@0: logSegments = DEFAULT_BUFFER_SEGMENTS; michael@0: logEntries = size / sizeof(PRTraceEntry); michael@0: logEntriesPerSegment = logEntries / logSegments; michael@0: logEntries = logSegments * logEntriesPerSegment; michael@0: bufSize = logEntries * sizeof(PRTraceEntry); michael@0: logSegSize = logEntriesPerSegment * sizeof(PRTraceEntry); michael@0: PR_ASSERT( bufSize != 0); michael@0: PR_LOG( lm, PR_LOG_ERROR, michael@0: ("NewTraceBuffer: logSegments: %ld, logEntries: %ld, logEntriesPerSegment: %ld, logSegSize: %ld", michael@0: logSegments, logEntries, logEntriesPerSegment, logSegSize )); michael@0: michael@0: michael@0: tBuf = PR_Malloc( bufSize ); michael@0: if ( tBuf == NULL ) michael@0: { michael@0: PR_LOG( lm, PR_LOG_ERROR, michael@0: ("PRTrace: Failed to get trace buffer")); michael@0: PR_ASSERT( 0 ); michael@0: } michael@0: else michael@0: { michael@0: PR_LOG( lm, PR_LOG_NOTICE, michael@0: ("PRTrace: Got trace buffer of size: %ld, at %p", bufSize, tBuf)); michael@0: } michael@0: michael@0: next = 0; michael@0: last = logEntries -1; michael@0: logCount = 0; michael@0: logLostData = PR_TRUE; /* not really on first call */ michael@0: logOrder = LogReset; michael@0: michael@0: } /* end NewTraceBuffer() */ michael@0: michael@0: /* michael@0: ** _PR_InitializeTrace() -- Initialize the trace facility michael@0: */ michael@0: static void _PR_InitializeTrace( void ) michael@0: { michael@0: /* The lock pointer better be null on this call */ michael@0: PR_ASSERT( traceLock == NULL ); michael@0: michael@0: traceLock = PR_NewLock(); michael@0: PR_ASSERT( traceLock != NULL ); michael@0: michael@0: PR_Lock( traceLock ); michael@0: michael@0: PR_INIT_CLIST( &qNameList ); michael@0: michael@0: lm = PR_NewLogModule("trace"); michael@0: michael@0: bufSize = DEFAULT_TRACE_BUFSIZE; michael@0: NewTraceBuffer( bufSize ); michael@0: michael@0: /* Initialize logging controls */ michael@0: logLock = PR_NewLock(); michael@0: logCVar = PR_NewCondVar( logLock ); michael@0: michael@0: PR_Unlock( traceLock ); michael@0: return; michael@0: } /* end _PR_InitializeTrace() */ michael@0: michael@0: /* michael@0: ** Create a Trace Handle michael@0: */ michael@0: PR_IMPLEMENT(PRTraceHandle) michael@0: PR_CreateTrace( michael@0: const char *qName, /* QName for this trace handle */ michael@0: const char *rName, /* RName for this trace handle */ michael@0: const char *description /* description for this trace handle */ michael@0: ) michael@0: { michael@0: QName *qnp; michael@0: RName *rnp; michael@0: PRBool matchQname = PR_FALSE; michael@0: michael@0: /* Self initialize, if necessary */ michael@0: if ( traceLock == NULL ) michael@0: _PR_InitializeTrace(); michael@0: michael@0: /* Validate input arguments */ michael@0: PR_ASSERT( strlen(qName) <= PRTRACE_NAME_MAX ); michael@0: PR_ASSERT( strlen(rName) <= PRTRACE_NAME_MAX ); michael@0: PR_ASSERT( strlen(description) <= PRTRACE_DESC_MAX ); michael@0: michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("PRTRACE: CreateTrace: Qname: %s, RName: %s", qName, rName)); michael@0: michael@0: /* Lock the Facility */ michael@0: PR_Lock( traceLock ); michael@0: michael@0: /* Do we already have a matching QName? */ michael@0: if (!PR_CLIST_IS_EMPTY( &qNameList )) michael@0: { michael@0: qnp = (QName *) PR_LIST_HEAD( &qNameList ); michael@0: do { michael@0: if ( strcmp(qnp->name, qName) == 0) michael@0: { michael@0: matchQname = PR_TRUE; michael@0: break; michael@0: } michael@0: qnp = (QName *)PR_NEXT_LINK( &qnp->link ); michael@0: } while( qnp != (QName *)&qNameList ); michael@0: } michael@0: /* michael@0: ** If we did not find a matching QName, michael@0: ** allocate one and initialize it. michael@0: ** link it onto the qNameList. michael@0: ** michael@0: */ michael@0: if ( matchQname != PR_TRUE ) michael@0: { michael@0: qnp = PR_NEWZAP( QName ); michael@0: PR_ASSERT( qnp != NULL ); michael@0: PR_INIT_CLIST( &qnp->link ); michael@0: PR_INIT_CLIST( &qnp->rNameList ); michael@0: strcpy( qnp->name, qName ); michael@0: PR_APPEND_LINK( &qnp->link, &qNameList ); michael@0: } michael@0: michael@0: /* Do we already have a matching RName? */ michael@0: if (!PR_CLIST_IS_EMPTY( &qnp->rNameList )) michael@0: { michael@0: rnp = (RName *) PR_LIST_HEAD( &qnp->rNameList ); michael@0: do { michael@0: /* michael@0: ** No duplicate RNames are allowed within a QName michael@0: ** michael@0: */ michael@0: PR_ASSERT( strcmp(rnp->name, rName)); michael@0: rnp = (RName *)PR_NEXT_LINK( &rnp->link ); michael@0: } while( rnp != (RName *)&qnp->rNameList ); michael@0: } michael@0: michael@0: /* Get a new RName structure; initialize its members */ michael@0: rnp = PR_NEWZAP( RName ); michael@0: PR_ASSERT( rnp != NULL ); michael@0: PR_INIT_CLIST( &rnp->link ); michael@0: strcpy( rnp->name, rName ); michael@0: strcpy( rnp->desc, description ); michael@0: rnp->lock = PR_NewLock(); michael@0: rnp->state = Running; michael@0: if ( rnp->lock == NULL ) michael@0: { michael@0: PR_ASSERT(0); michael@0: } michael@0: michael@0: PR_APPEND_LINK( &rnp->link, &qnp->rNameList ); /* add RName to QName's rnList */ michael@0: rnp->qName = qnp; /* point the RName to the QName */ michael@0: michael@0: /* Unlock the Facility */ michael@0: PR_Unlock( traceLock ); michael@0: PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: Create: QName: %s %p, RName: %s %p\n\t", michael@0: qName, qnp, rName, rnp )); michael@0: michael@0: return((PRTraceHandle)rnp); michael@0: } /* end PR_CreateTrace() */ michael@0: michael@0: /* michael@0: ** michael@0: */ michael@0: PR_IMPLEMENT(void) michael@0: PR_DestroyTrace( michael@0: PRTraceHandle handle /* Handle to be destroyed */ michael@0: ) michael@0: { michael@0: RName *rnp = (RName *)handle; michael@0: QName *qnp = rnp->qName; michael@0: michael@0: PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: Deleting: QName: %s, RName: %s", michael@0: qnp->name, rnp->name)); michael@0: michael@0: /* Lock the Facility */ michael@0: PR_Lock( traceLock ); michael@0: michael@0: /* michael@0: ** Remove RName from the list of RNames in QName michael@0: ** and free RName michael@0: */ michael@0: PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: Deleting RName: %s, %p", michael@0: rnp->name, rnp)); michael@0: PR_REMOVE_LINK( &rnp->link ); michael@0: PR_Free( rnp->lock ); michael@0: PR_DELETE( rnp ); michael@0: michael@0: /* michael@0: ** If this is the last RName within QName michael@0: ** remove QName from the qNameList and free it michael@0: */ michael@0: if ( PR_CLIST_IS_EMPTY( &qnp->rNameList ) ) michael@0: { michael@0: PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: Deleting unused QName: %s, %p", michael@0: qnp->name, qnp)); michael@0: PR_REMOVE_LINK( &qnp->link ); michael@0: PR_DELETE( qnp ); michael@0: } michael@0: michael@0: /* Unlock the Facility */ michael@0: PR_Unlock( traceLock ); michael@0: return; michael@0: } /* end PR_DestroyTrace() */ michael@0: michael@0: /* michael@0: ** Create a TraceEntry in the trace buffer michael@0: */ michael@0: PR_IMPLEMENT(void) michael@0: PR_Trace( michael@0: PRTraceHandle handle, /* use this trace handle */ michael@0: PRUint32 userData0, /* User supplied data word 0 */ michael@0: PRUint32 userData1, /* User supplied data word 1 */ michael@0: PRUint32 userData2, /* User supplied data word 2 */ michael@0: PRUint32 userData3, /* User supplied data word 3 */ michael@0: PRUint32 userData4, /* User supplied data word 4 */ michael@0: PRUint32 userData5, /* User supplied data word 5 */ michael@0: PRUint32 userData6, /* User supplied data word 6 */ michael@0: PRUint32 userData7 /* User supplied data word 7 */ michael@0: ) michael@0: { michael@0: PRTraceEntry *tep; michael@0: PRInt32 mark; michael@0: michael@0: if ( (traceState == Suspended ) michael@0: || ( ((RName *)handle)->state == Suspended )) michael@0: return; michael@0: michael@0: /* michael@0: ** Get the next trace entry slot w/ minimum delay michael@0: */ michael@0: PR_Lock( traceLock ); michael@0: michael@0: tep = &tBuf[next++]; michael@0: if ( next > last ) michael@0: next = 0; michael@0: if ( fetchLostData == PR_FALSE && next == fetchLastSeen ) michael@0: fetchLostData = PR_TRUE; michael@0: michael@0: mark = next; michael@0: michael@0: PR_Unlock( traceLock ); michael@0: michael@0: /* michael@0: ** We have a trace entry. Fill it in. michael@0: */ michael@0: tep->thread = PR_GetCurrentThread(); michael@0: tep->handle = handle; michael@0: tep->time = PR_Now(); michael@0: tep->userData[0] = userData0; michael@0: tep->userData[1] = userData1; michael@0: tep->userData[2] = userData2; michael@0: tep->userData[3] = userData3; michael@0: tep->userData[4] = userData4; michael@0: tep->userData[5] = userData5; michael@0: tep->userData[6] = userData6; michael@0: tep->userData[7] = userData7; michael@0: michael@0: /* When buffer segment is full, signal trace log thread to run */ michael@0: if (( mark % logEntriesPerSegment) == 0 ) michael@0: { michael@0: PR_Lock( logLock ); michael@0: logCount++; michael@0: PR_NotifyCondVar( logCVar ); michael@0: PR_Unlock( logLock ); michael@0: /* michael@0: ** Gh0D! This is awful! michael@0: ** Anyway, to minimize lost trace data segments, michael@0: ** I inserted the PR_Sleep(0) to cause a context switch michael@0: ** so that the log thread could run. michael@0: ** I know, it perturbs the universe and may cause michael@0: ** funny things to happen in the optimized builds. michael@0: ** Take it out, lose data; leave it in risk Heisenberg. michael@0: */ michael@0: /* PR_Sleep(0); */ michael@0: } michael@0: michael@0: return; michael@0: } /* end PR_Trace() */ michael@0: michael@0: /* michael@0: ** michael@0: */ michael@0: PR_IMPLEMENT(void) michael@0: PR_SetTraceOption( michael@0: PRTraceOption command, /* One of the enumerated values */ michael@0: void *value /* command value or NULL */ michael@0: ) michael@0: { michael@0: RName * rnp; michael@0: michael@0: switch ( command ) michael@0: { michael@0: case PRTraceBufSize : michael@0: PR_Lock( traceLock ); michael@0: PR_Free( tBuf ); michael@0: bufSize = *(PRInt32 *)value; michael@0: NewTraceBuffer( bufSize ); michael@0: PR_Unlock( traceLock ); michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("PRSetTraceOption: PRTraceBufSize: %ld", bufSize)); michael@0: break; michael@0: michael@0: case PRTraceEnable : michael@0: rnp = *(RName **)value; michael@0: rnp->state = Running; michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("PRSetTraceOption: PRTraceEnable: %p", rnp)); michael@0: break; michael@0: michael@0: case PRTraceDisable : michael@0: rnp = *(RName **)value; michael@0: rnp->state = Suspended; michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("PRSetTraceOption: PRTraceDisable: %p", rnp)); michael@0: break; michael@0: michael@0: case PRTraceSuspend : michael@0: traceState = Suspended; michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("PRSetTraceOption: PRTraceSuspend")); michael@0: break; michael@0: michael@0: case PRTraceResume : michael@0: traceState = Running; michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("PRSetTraceOption: PRTraceResume")); michael@0: break; michael@0: michael@0: case PRTraceSuspendRecording : michael@0: PR_Lock( logLock ); michael@0: logOrder = LogSuspend; michael@0: PR_NotifyCondVar( logCVar ); michael@0: PR_Unlock( logLock ); michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("PRSetTraceOption: PRTraceSuspendRecording")); michael@0: break; michael@0: michael@0: case PRTraceResumeRecording : michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("PRSetTraceOption: PRTraceResumeRecording")); michael@0: if ( logState != LogSuspend ) michael@0: break; michael@0: PR_Lock( logLock ); michael@0: logOrder = LogResume; michael@0: PR_NotifyCondVar( logCVar ); michael@0: PR_Unlock( logLock ); michael@0: break; michael@0: michael@0: case PRTraceStopRecording : michael@0: PR_Lock( logLock ); michael@0: logOrder = LogStop; michael@0: PR_NotifyCondVar( logCVar ); michael@0: PR_Unlock( logLock ); michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("PRSetTraceOption: PRTraceStopRecording")); michael@0: break; michael@0: michael@0: case PRTraceLockHandles : michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("PRSetTraceOption: PRTraceLockTraceHandles")); michael@0: PR_Lock( traceLock ); michael@0: break; michael@0: michael@0: case PRTraceUnLockHandles : michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("PRSetTraceOption: PRTraceUnLockHandles")); michael@0: PR_Unlock( traceLock ); michael@0: break; michael@0: michael@0: default: michael@0: PR_LOG( lm, PR_LOG_ERROR, michael@0: ("PRSetTraceOption: Invalid command %ld", command )); michael@0: PR_ASSERT( 0 ); michael@0: break; michael@0: } /* end switch() */ michael@0: return; michael@0: } /* end PR_SetTraceOption() */ michael@0: michael@0: /* michael@0: ** michael@0: */ michael@0: PR_IMPLEMENT(void) michael@0: PR_GetTraceOption( michael@0: PRTraceOption command, /* One of the enumerated values */ michael@0: void *value /* command value or NULL */ michael@0: ) michael@0: { michael@0: switch ( command ) michael@0: { michael@0: case PRTraceBufSize : michael@0: *((PRInt32 *)value) = bufSize; michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("PRGetTraceOption: PRTraceBufSize: %ld", bufSize )); michael@0: break; michael@0: michael@0: default: michael@0: PR_LOG( lm, PR_LOG_ERROR, michael@0: ("PRGetTraceOption: Invalid command %ld", command )); michael@0: PR_ASSERT( 0 ); michael@0: break; michael@0: } /* end switch() */ michael@0: return; michael@0: } /* end PR_GetTraceOption() */ michael@0: michael@0: /* michael@0: ** michael@0: */ michael@0: PR_IMPLEMENT(PRTraceHandle) michael@0: PR_GetTraceHandleFromName( michael@0: const char *qName, /* QName search argument */ michael@0: const char *rName /* RName search argument */ michael@0: ) michael@0: { michael@0: const char *qn, *rn, *desc; michael@0: PRTraceHandle qh, rh = NULL; michael@0: RName *rnp = NULL; michael@0: michael@0: PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: GetTraceHandleFromName:\n\t" michael@0: "QName: %s, RName: %s", qName, rName )); michael@0: michael@0: qh = PR_FindNextTraceQname( NULL ); michael@0: while (qh != NULL) michael@0: { michael@0: rh = PR_FindNextTraceRname( NULL, qh ); michael@0: while ( rh != NULL ) michael@0: { michael@0: PR_GetTraceNameFromHandle( rh, &qn, &rn, &desc ); michael@0: if ( (strcmp( qName, qn ) == 0) michael@0: && (strcmp( rName, rn ) == 0 )) michael@0: { michael@0: rnp = (RName *)rh; michael@0: goto foundIt; michael@0: } michael@0: rh = PR_FindNextTraceRname( rh, qh ); michael@0: } michael@0: qh = PR_FindNextTraceQname( NULL ); michael@0: } michael@0: michael@0: foundIt: michael@0: PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetConterHandleFromName: %p", rnp )); michael@0: return(rh); michael@0: } /* end PR_GetTraceHandleFromName() */ michael@0: michael@0: /* michael@0: ** michael@0: */ michael@0: PR_IMPLEMENT(void) michael@0: PR_GetTraceNameFromHandle( michael@0: PRTraceHandle handle, /* handle as search argument */ michael@0: const char **qName, /* pointer to associated QName */ michael@0: const char **rName, /* pointer to associated RName */ michael@0: const char **description /* pointer to associated description */ michael@0: ) michael@0: { michael@0: RName *rnp = (RName *)handle; michael@0: QName *qnp = rnp->qName; michael@0: michael@0: *qName = qnp->name; michael@0: *rName = rnp->name; michael@0: *description = rnp->desc; michael@0: michael@0: PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: GetConterNameFromHandle: " michael@0: "QNp: %p, RNp: %p,\n\tQName: %s, RName: %s, Desc: %s", michael@0: qnp, rnp, qnp->name, rnp->name, rnp->desc )); michael@0: michael@0: return; michael@0: } /* end PR_GetTraceNameFromHandle() */ michael@0: michael@0: /* michael@0: ** michael@0: */ michael@0: PR_IMPLEMENT(PRTraceHandle) michael@0: PR_FindNextTraceQname( michael@0: PRTraceHandle handle michael@0: ) michael@0: { michael@0: QName *qnp = (QName *)handle; michael@0: michael@0: if ( PR_CLIST_IS_EMPTY( &qNameList )) michael@0: qnp = NULL; michael@0: else if ( qnp == NULL ) michael@0: qnp = (QName *)PR_LIST_HEAD( &qNameList ); michael@0: else if ( PR_NEXT_LINK( &qnp->link ) == &qNameList ) michael@0: qnp = NULL; michael@0: else michael@0: qnp = (QName *)PR_NEXT_LINK( &qnp->link ); michael@0: michael@0: PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: FindNextQname: Handle: %p, Returns: %p", michael@0: handle, qnp )); michael@0: michael@0: return((PRTraceHandle)qnp); michael@0: } /* end PR_FindNextTraceQname() */ michael@0: michael@0: /* michael@0: ** michael@0: */ michael@0: PR_IMPLEMENT(PRTraceHandle) michael@0: PR_FindNextTraceRname( michael@0: PRTraceHandle rhandle, michael@0: PRTraceHandle qhandle michael@0: ) michael@0: { michael@0: RName *rnp = (RName *)rhandle; michael@0: QName *qnp = (QName *)qhandle; michael@0: michael@0: michael@0: if ( PR_CLIST_IS_EMPTY( &qnp->rNameList )) michael@0: rnp = NULL; michael@0: else if ( rnp == NULL ) michael@0: rnp = (RName *)PR_LIST_HEAD( &qnp->rNameList ); michael@0: else if ( PR_NEXT_LINK( &rnp->link ) == &qnp->rNameList ) michael@0: rnp = NULL; michael@0: else michael@0: rnp = (RName *)PR_NEXT_LINK( &rnp->link ); michael@0: michael@0: PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: FindNextRname: Rhandle: %p, QHandle: %p, Returns: %p", michael@0: rhandle, qhandle, rnp )); michael@0: michael@0: return((PRTraceHandle)rnp); michael@0: } /* end PR_FindNextTraceRname() */ michael@0: michael@0: /* michael@0: ** michael@0: */ michael@0: static PRFileDesc * InitializeRecording( void ) michael@0: { michael@0: char *logFileName; michael@0: PRFileDesc *logFile; michael@0: michael@0: /* Self initialize, if necessary */ michael@0: if ( traceLock == NULL ) michael@0: _PR_InitializeTrace(); michael@0: michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("PR_RecordTraceEntries: begins")); michael@0: michael@0: logLostData = 0; /* reset at entry */ michael@0: logState = LogReset; michael@0: michael@0: #ifdef XP_UNIX michael@0: if ((getuid() != geteuid()) || (getgid() != getegid())) { michael@0: return NULL; michael@0: } michael@0: #endif /* XP_UNIX */ michael@0: michael@0: /* Get the filename for the logfile from the environment */ michael@0: logFileName = PR_GetEnv( "NSPR_TRACE_LOG" ); michael@0: if ( logFileName == NULL ) michael@0: { michael@0: PR_LOG( lm, PR_LOG_ERROR, michael@0: ("RecordTraceEntries: Environment variable not defined. Exiting")); michael@0: return NULL; michael@0: } michael@0: michael@0: /* Open the logfile */ michael@0: logFile = PR_Open( logFileName, PR_WRONLY | PR_CREATE_FILE, 0666 ); michael@0: if ( logFile == NULL ) michael@0: { michael@0: PR_LOG( lm, PR_LOG_ERROR, michael@0: ("RecordTraceEntries: Cannot open %s as trace log file. OS error: %ld", michael@0: logFileName, PR_GetOSError())); michael@0: return NULL; michael@0: } michael@0: return logFile; michael@0: } /* end InitializeRecording() */ michael@0: michael@0: /* michael@0: ** michael@0: */ michael@0: static void ProcessOrders( void ) michael@0: { michael@0: switch ( logOrder ) michael@0: { michael@0: case LogReset : michael@0: logOrder = logState = localState; michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("RecordTraceEntries: LogReset")); michael@0: break; michael@0: michael@0: case LogSuspend : michael@0: localState = logOrder = logState = LogSuspend; michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("RecordTraceEntries: LogSuspend")); michael@0: break; michael@0: michael@0: case LogResume : michael@0: localState = logOrder = logState = LogActive; michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("RecordTraceEntries: LogResume")); michael@0: break; michael@0: michael@0: case LogStop : michael@0: logOrder = logState = LogStop; michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("RecordTraceEntries: LogStop")); michael@0: break; michael@0: michael@0: default : michael@0: PR_LOG( lm, PR_LOG_ERROR, michael@0: ("RecordTraceEntries: Invalid logOrder: %ld", logOrder )); michael@0: PR_ASSERT( 0 ); michael@0: break; michael@0: } /* end switch() */ michael@0: return ; michael@0: } /* end ProcessOrders() */ michael@0: michael@0: /* michael@0: ** michael@0: */ michael@0: static void WriteTraceSegment( PRFileDesc *logFile, void *buf, PRInt32 amount ) michael@0: { michael@0: PRInt32 rc; michael@0: michael@0: michael@0: PR_LOG( lm, PR_LOG_ERROR, michael@0: ("WriteTraceSegment: Buffer: %p, Amount: %ld", buf, amount)); michael@0: rc = PR_Write( logFile, buf , amount ); michael@0: if ( rc == -1 ) michael@0: PR_LOG( lm, PR_LOG_ERROR, michael@0: ("RecordTraceEntries: PR_Write() failed. Error: %ld", PR_GetError() )); michael@0: else if ( rc != amount ) michael@0: PR_LOG( lm, PR_LOG_ERROR, michael@0: ("RecordTraceEntries: PR_Write() Tried to write: %ld, Wrote: %ld", amount, rc)); michael@0: else michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("RecordTraceEntries: PR_Write(): Buffer: %p, bytes: %ld", buf, amount)); michael@0: michael@0: return; michael@0: } /* end WriteTraceSegment() */ michael@0: michael@0: /* michael@0: ** michael@0: */ michael@0: PR_IMPLEMENT(void) michael@0: PR_RecordTraceEntries( michael@0: void michael@0: ) michael@0: { michael@0: PRFileDesc *logFile; michael@0: PRInt32 lostSegments; michael@0: PRInt32 currentSegment = 0; michael@0: void *buf; michael@0: PRBool doWrite; michael@0: michael@0: logFile = InitializeRecording(); michael@0: if ( logFile == NULL ) michael@0: { michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("PR_RecordTraceEntries: Failed to initialize")); michael@0: return; michael@0: } michael@0: michael@0: /* Do this until told to stop */ michael@0: while ( logState != LogStop ) michael@0: { michael@0: michael@0: PR_Lock( logLock ); michael@0: michael@0: while ( (logCount == 0) && ( logOrder == logState ) ) michael@0: PR_WaitCondVar( logCVar, PR_INTERVAL_NO_TIMEOUT ); michael@0: michael@0: /* Handle state transitions */ michael@0: if ( logOrder != logState ) michael@0: ProcessOrders(); michael@0: michael@0: /* recalculate local controls */ michael@0: if ( logCount ) michael@0: { michael@0: lostSegments = logCount - logSegments; michael@0: if ( lostSegments > 0 ) michael@0: { michael@0: logLostData += ( logCount - logSegments ); michael@0: logCount = (logCount % logSegments); michael@0: currentSegment = logCount; michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("PR_RecordTraceEntries: LostData segments: %ld", logLostData)); michael@0: } michael@0: else michael@0: { michael@0: logCount--; michael@0: } michael@0: michael@0: buf = tBuf + ( logEntriesPerSegment * currentSegment ); michael@0: if (++currentSegment >= logSegments ) michael@0: currentSegment = 0; michael@0: doWrite = PR_TRUE; michael@0: } michael@0: else michael@0: doWrite = PR_FALSE; michael@0: michael@0: PR_Unlock( logLock ); michael@0: michael@0: if ( doWrite == PR_TRUE ) michael@0: { michael@0: if ( localState != LogSuspend ) michael@0: WriteTraceSegment( logFile, buf, logSegSize ); michael@0: else michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("RecordTraceEntries: PR_Write(): is suspended" )); michael@0: } michael@0: michael@0: } /* end while(logState...) */ michael@0: michael@0: PR_Close( logFile ); michael@0: PR_LOG( lm, PR_LOG_DEBUG, michael@0: ("RecordTraceEntries: exiting")); michael@0: return; michael@0: } /* end PR_RecordTraceEntries() */ michael@0: michael@0: /* michael@0: ** michael@0: */ michael@0: PR_IMPLEMENT(PRIntn) michael@0: PR_GetTraceEntries( michael@0: PRTraceEntry *buffer, /* where to write output */ michael@0: PRInt32 count, /* number to get */ michael@0: PRInt32 *found /* number you got */ michael@0: ) michael@0: { michael@0: PRInt32 rc; michael@0: PRInt32 copied = 0; michael@0: michael@0: PR_Lock( traceLock ); michael@0: michael@0: /* michael@0: ** Depending on where the LastSeen and Next indices are, michael@0: ** copy the trace buffer in one or two pieces. michael@0: */ michael@0: PR_LOG( lm, PR_LOG_ERROR, michael@0: ("PR_GetTraceEntries: Next: %ld, LastSeen: %ld", next, fetchLastSeen)); michael@0: michael@0: if ( fetchLastSeen <= next ) michael@0: { michael@0: while (( count-- > 0 ) && (fetchLastSeen < next )) michael@0: { michael@0: *(buffer + copied++) = *(tBuf + fetchLastSeen++); michael@0: } michael@0: PR_LOG( lm, PR_LOG_ERROR, michael@0: ("PR_GetTraceEntries: Copied: %ld, LastSeen: %ld", copied, fetchLastSeen)); michael@0: } michael@0: else /* copy in 2 parts */ michael@0: { michael@0: while ( count-- > 0 && fetchLastSeen <= last ) michael@0: { michael@0: *(buffer + copied++) = *(tBuf + fetchLastSeen++); michael@0: } michael@0: fetchLastSeen = 0; michael@0: michael@0: PR_LOG( lm, PR_LOG_ERROR, michael@0: ("PR_GetTraceEntries: Copied: %ld, LastSeen: %ld", copied, fetchLastSeen)); michael@0: michael@0: while ( count-- > 0 && fetchLastSeen < next ) michael@0: { michael@0: *(buffer + copied++) = *(tBuf + fetchLastSeen++); michael@0: } michael@0: PR_LOG( lm, PR_LOG_ERROR, michael@0: ("PR_GetTraceEntries: Copied: %ld, LastSeen: %ld", copied, fetchLastSeen)); michael@0: } michael@0: michael@0: *found = copied; michael@0: rc = ( fetchLostData == PR_TRUE )? 1 : 0; michael@0: fetchLostData = PR_FALSE; michael@0: michael@0: PR_Unlock( traceLock ); michael@0: return rc; michael@0: } /* end PR_GetTraceEntries() */ michael@0: michael@0: /* end prtrace.c */