/* 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 */
