Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
/*
** 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 */