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