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