902 lines
24 KiB
C
902 lines
24 KiB
C
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/*
|
|
** 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 *)&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 *)&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;
|
|
|
|
/* Get the filename for the logfile from the environment */
|
|
logFileName = PR_GetEnvSecure( "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 */
|