tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

prtrace.c (21720B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 /*
      7 ** prtrace.c -- NSPR Trace Instrumentation
      8 **
      9 ** Implement the API defined in prtrace.h
     10 **
     11 **
     12 **
     13 */
     14 
     15 #include <string.h>
     16 #include "primpl.h"
     17 
     18 #define DEFAULT_TRACE_BUFSIZE (1024 * 1024)
     19 #define DEFAULT_BUFFER_SEGMENTS 2
     20 
     21 /*
     22 ** Enumerate states in a RName structure
     23 */
     24 typedef enum TraceState { Running = 1, Suspended = 2 } TraceState;
     25 
     26 /*
     27 ** Define QName structure
     28 */
     29 typedef struct QName {
     30  PRCList link;
     31  PRCList rNameList;
     32  char name[PRTRACE_NAME_MAX + 1];
     33 } QName;
     34 
     35 /*
     36 ** Define RName structure
     37 */
     38 typedef struct RName {
     39  PRCList link;
     40  PRLock* lock;
     41  QName* qName;
     42  TraceState state;
     43  char name[PRTRACE_NAME_MAX + 1];
     44  char desc[PRTRACE_DESC_MAX + 1];
     45 } RName;
     46 
     47 /*
     48 ** The Trace Facility database
     49 **
     50 */
     51 static PRLogModuleInfo* lm;
     52 
     53 static PRLock* traceLock; /* Facility Lock */
     54 static PRCList qNameList; /* anchor to all QName structures */
     55 static TraceState traceState = Running;
     56 
     57 /*
     58 ** in-memory trace buffer controls
     59 */
     60 static PRTraceEntry* tBuf; /* pointer to buffer */
     61 static PRInt32
     62    bufSize; /* size of buffer, in bytes, rounded up to sizeof(PRTraceEntry) */
     63 static volatile PRInt32 next; /* index to next PRTraceEntry */
     64 static PRInt32 last;          /* index of highest numbered trace entry */
     65 
     66 /*
     67 ** Real-time buffer capture controls
     68 */
     69 static PRInt32 fetchLastSeen = 0;
     70 static PRBool fetchLostData = PR_FALSE;
     71 
     72 /*
     73 ** Buffer write-to-file controls
     74 */
     75 static PRLock* logLock;    /* Sync lock */
     76 static PRCondVar* logCVar; /* Sync Condidtion Variable */
     77 /*
     78 ** Inter-thread state communication.
     79 ** Controling thread writes to logOrder under protection of logCVar
     80 ** the logging thread reads logOrder and sets logState on Notify.
     81 **
     82 ** logSegments, logCount, logLostData must be read and written under
     83 ** protection of logLock, logCVar.
     84 **
     85 */
     86 static enum LogState {
     87  LogNotRunning, /* Initial state */
     88  LogReset,      /* Causes logger to re-calc controls */
     89  LogActive,     /* Logging in progress, set only by log thread */
     90  LogSuspend,    /* Suspend Logging */
     91  LogResume,     /* Resume Logging => LogActive */
     92  LogStop        /* Stop the log thread */
     93 } logOrder,
     94    logState, localState;   /* controlling state variables */
     95 static PRInt32 logSegments; /* Number of buffer segments */
     96 static PRInt32 logEntries;  /* number of Trace Entries in the buffer */
     97 static PRInt32
     98    logEntriesPerSegment;   /* number of PRTraceEntries per buffer segment */
     99 static PRInt32 logSegSize;  /* size of buffer segment */
    100 static PRInt32 logCount;    /* number of segments pending output */
    101 static PRInt32 logLostData; /* number of lost log buffer segments */
    102 
    103 /*
    104 ** end Trace Database
    105 **
    106 */
    107 
    108 /*
    109 ** _PR_InitializeTrace() -- Initialize the trace facility
    110 */
    111 static void NewTraceBuffer(PRInt32 size) {
    112  /*
    113  ** calculate the size of the buffer
    114  ** round down so that each segment has the same number of
    115  ** trace entries
    116  */
    117  logSegments = DEFAULT_BUFFER_SEGMENTS;
    118  logEntries = size / sizeof(PRTraceEntry);
    119  logEntriesPerSegment = logEntries / logSegments;
    120  logEntries = logSegments * logEntriesPerSegment;
    121  bufSize = logEntries * sizeof(PRTraceEntry);
    122  logSegSize = logEntriesPerSegment * sizeof(PRTraceEntry);
    123  PR_ASSERT(bufSize != 0);
    124  PR_LOG(lm, PR_LOG_ERROR,
    125         ("NewTraceBuffer: logSegments: %ld, logEntries: %ld, "
    126          "logEntriesPerSegment: %ld, logSegSize: %ld",
    127          logSegments, logEntries, logEntriesPerSegment, logSegSize));
    128 
    129  tBuf = PR_Malloc(bufSize);
    130  if (tBuf == NULL) {
    131    PR_LOG(lm, PR_LOG_ERROR, ("PRTrace: Failed to get trace buffer"));
    132    PR_ASSERT(0);
    133  } else {
    134    PR_LOG(lm, PR_LOG_NOTICE,
    135           ("PRTrace: Got trace buffer of size: %ld, at %p", bufSize, tBuf));
    136  }
    137 
    138  next = 0;
    139  last = logEntries - 1;
    140  logCount = 0;
    141  logLostData = PR_TRUE; /* not really on first call */
    142  logOrder = LogReset;
    143 
    144 } /* end NewTraceBuffer() */
    145 
    146 /*
    147 ** _PR_InitializeTrace() -- Initialize the trace facility
    148 */
    149 static void _PR_InitializeTrace(void) {
    150  /* The lock pointer better be null on this call */
    151  PR_ASSERT(traceLock == NULL);
    152 
    153  traceLock = PR_NewLock();
    154  PR_ASSERT(traceLock != NULL);
    155 
    156  PR_Lock(traceLock);
    157 
    158  PR_INIT_CLIST(&qNameList);
    159 
    160  lm = PR_NewLogModule("trace");
    161 
    162  bufSize = DEFAULT_TRACE_BUFSIZE;
    163  NewTraceBuffer(bufSize);
    164 
    165  /* Initialize logging controls */
    166  logLock = PR_NewLock();
    167  logCVar = PR_NewCondVar(logLock);
    168 
    169  PR_Unlock(traceLock);
    170  return;
    171 } /* end _PR_InitializeTrace() */
    172 
    173 /*
    174 ** Create a Trace Handle
    175 */
    176 PR_IMPLEMENT(PRTraceHandle)
    177 PR_CreateTrace(const char* qName,      /* QName for this trace handle */
    178               const char* rName,      /* RName for this trace handle */
    179               const char* description /* description for this trace handle */
    180 ) {
    181  QName* qnp;
    182  RName* rnp;
    183  PRBool matchQname = PR_FALSE;
    184 
    185  /* Self initialize, if necessary */
    186  if (traceLock == NULL) {
    187    _PR_InitializeTrace();
    188  }
    189 
    190  /* Validate input arguments */
    191  PR_ASSERT(strlen(qName) <= PRTRACE_NAME_MAX);
    192  PR_ASSERT(strlen(rName) <= PRTRACE_NAME_MAX);
    193  PR_ASSERT(strlen(description) <= PRTRACE_DESC_MAX);
    194 
    195  PR_LOG(lm, PR_LOG_DEBUG,
    196         ("PRTRACE: CreateTrace: Qname: %s, RName: %s", qName, rName));
    197 
    198  /* Lock the Facility */
    199  PR_Lock(traceLock);
    200 
    201  /* Do we already have a matching QName? */
    202  if (!PR_CLIST_IS_EMPTY(&qNameList)) {
    203    qnp = (QName*)PR_LIST_HEAD(&qNameList);
    204    do {
    205      if (strcmp(qnp->name, qName) == 0) {
    206        matchQname = PR_TRUE;
    207        break;
    208      }
    209      qnp = (QName*)PR_NEXT_LINK(&qnp->link);
    210    } while (qnp != (QName*)&qNameList);
    211  }
    212  /*
    213  ** If we did not find a matching QName,
    214  **    allocate one and initialize it.
    215  **    link it onto the qNameList.
    216  **
    217  */
    218  if (matchQname != PR_TRUE) {
    219    qnp = PR_NEWZAP(QName);
    220    PR_ASSERT(qnp != NULL);
    221    PR_INIT_CLIST(&qnp->link);
    222    PR_INIT_CLIST(&qnp->rNameList);
    223    strcpy(qnp->name, qName);
    224    PR_APPEND_LINK(&qnp->link, &qNameList);
    225  }
    226 
    227  /* Do we already have a matching RName? */
    228  if (!PR_CLIST_IS_EMPTY(&qnp->rNameList)) {
    229    rnp = (RName*)PR_LIST_HEAD(&qnp->rNameList);
    230    do {
    231      /*
    232      ** No duplicate RNames are allowed within a QName
    233      **
    234      */
    235      PR_ASSERT(strcmp(rnp->name, rName));
    236      rnp = (RName*)PR_NEXT_LINK(&rnp->link);
    237    } while (rnp != (RName*)&qnp->rNameList);
    238  }
    239 
    240  /* Get a new RName structure; initialize its members */
    241  rnp = PR_NEWZAP(RName);
    242  PR_ASSERT(rnp != NULL);
    243  PR_INIT_CLIST(&rnp->link);
    244  strcpy(rnp->name, rName);
    245  strcpy(rnp->desc, description);
    246  rnp->lock = PR_NewLock();
    247  rnp->state = Running;
    248  if (rnp->lock == NULL) {
    249    PR_ASSERT(0);
    250  }
    251 
    252  PR_APPEND_LINK(&rnp->link, &qnp->rNameList); /* add RName to QName's rnList */
    253  rnp->qName = qnp; /* point the RName to the QName */
    254 
    255  /* Unlock the Facility */
    256  PR_Unlock(traceLock);
    257  PR_LOG(lm, PR_LOG_DEBUG,
    258         ("PRTrace: Create: QName: %s %p, RName: %s %p\n\t", qName, qnp, rName,
    259          rnp));
    260 
    261  return ((PRTraceHandle)rnp);
    262 } /* end  PR_CreateTrace() */
    263 
    264 /*
    265 **
    266 */
    267 PR_IMPLEMENT(void)
    268 PR_DestroyTrace(PRTraceHandle handle /* Handle to be destroyed */
    269 ) {
    270  RName* rnp = (RName*)handle;
    271  QName* qnp = rnp->qName;
    272 
    273  PR_LOG(lm, PR_LOG_DEBUG,
    274         ("PRTrace: Deleting: QName: %s, RName: %s", qnp->name, rnp->name));
    275 
    276  /* Lock the Facility */
    277  PR_Lock(traceLock);
    278 
    279  /*
    280  ** Remove RName from the list of RNames in QName
    281  ** and free RName
    282  */
    283  PR_LOG(lm, PR_LOG_DEBUG, ("PRTrace: Deleting RName: %s, %p", rnp->name, rnp));
    284  PR_REMOVE_LINK(&rnp->link);
    285  PR_Free(rnp->lock);
    286  PR_DELETE(rnp);
    287 
    288  /*
    289  ** If this is the last RName within QName
    290  **   remove QName from the qNameList and free it
    291  */
    292  if (PR_CLIST_IS_EMPTY(&qnp->rNameList)) {
    293    PR_LOG(lm, PR_LOG_DEBUG,
    294           ("PRTrace: Deleting unused QName: %s, %p", qnp->name, qnp));
    295    PR_REMOVE_LINK(&qnp->link);
    296    PR_DELETE(qnp);
    297  }
    298 
    299  /* Unlock the Facility */
    300  PR_Unlock(traceLock);
    301  return;
    302 } /* end PR_DestroyTrace()  */
    303 
    304 /*
    305 ** Create a TraceEntry in the trace buffer
    306 */
    307 PR_IMPLEMENT(void)
    308 PR_Trace(PRTraceHandle handle, /* use this trace handle */
    309         PRUint32 userData0,   /* User supplied data word 0 */
    310         PRUint32 userData1,   /* User supplied data word 1 */
    311         PRUint32 userData2,   /* User supplied data word 2 */
    312         PRUint32 userData3,   /* User supplied data word 3 */
    313         PRUint32 userData4,   /* User supplied data word 4 */
    314         PRUint32 userData5,   /* User supplied data word 5 */
    315         PRUint32 userData6,   /* User supplied data word 6 */
    316         PRUint32 userData7    /* User supplied data word 7 */
    317 ) {
    318  PRTraceEntry* tep;
    319  PRInt32 mark;
    320 
    321  if ((traceState == Suspended) || (((RName*)handle)->state == Suspended)) {
    322    return;
    323  }
    324 
    325  /*
    326  ** Get the next trace entry slot w/ minimum delay
    327  */
    328  PR_Lock(traceLock);
    329 
    330  tep = &tBuf[next++];
    331  if (next > last) {
    332    next = 0;
    333  }
    334  if (fetchLostData == PR_FALSE && next == fetchLastSeen) {
    335    fetchLostData = PR_TRUE;
    336  }
    337 
    338  mark = next;
    339 
    340  PR_Unlock(traceLock);
    341 
    342  /*
    343  ** We have a trace entry. Fill it in.
    344  */
    345  tep->thread = PR_GetCurrentThread();
    346  tep->handle = handle;
    347  tep->time = PR_Now();
    348  tep->userData[0] = userData0;
    349  tep->userData[1] = userData1;
    350  tep->userData[2] = userData2;
    351  tep->userData[3] = userData3;
    352  tep->userData[4] = userData4;
    353  tep->userData[5] = userData5;
    354  tep->userData[6] = userData6;
    355  tep->userData[7] = userData7;
    356 
    357  /* When buffer segment is full, signal trace log thread to run */
    358  if ((mark % logEntriesPerSegment) == 0) {
    359    PR_Lock(logLock);
    360    logCount++;
    361    PR_NotifyCondVar(logCVar);
    362    PR_Unlock(logLock);
    363    /*
    364    ** Gh0D! This is awful!
    365    ** Anyway, to minimize lost trace data segments,
    366    ** I inserted the PR_Sleep(0) to cause a context switch
    367    ** so that the log thread could run.
    368    ** I know, it perturbs the universe and may cause
    369    ** funny things to happen in the optimized builds.
    370    ** Take it out, lose data; leave it in risk Heisenberg.
    371    */
    372    /* PR_Sleep(0); */
    373  }
    374 
    375  return;
    376 } /* end PR_Trace() */
    377 
    378 /*
    379 **
    380 */
    381 PR_IMPLEMENT(void)
    382 PR_SetTraceOption(PRTraceOption command, /* One of the enumerated values */
    383                  void* value            /* command value or NULL */
    384 ) {
    385  RName* rnp;
    386 
    387  switch (command) {
    388    case PRTraceBufSize:
    389      PR_Lock(traceLock);
    390      PR_Free(tBuf);
    391      bufSize = *(PRInt32*)value;
    392      NewTraceBuffer(bufSize);
    393      PR_Unlock(traceLock);
    394      PR_LOG(lm, PR_LOG_DEBUG,
    395             ("PRSetTraceOption: PRTraceBufSize: %ld", bufSize));
    396      break;
    397 
    398    case PRTraceEnable:
    399      rnp = *(RName**)value;
    400      rnp->state = Running;
    401      PR_LOG(lm, PR_LOG_DEBUG, ("PRSetTraceOption: PRTraceEnable: %p", rnp));
    402      break;
    403 
    404    case PRTraceDisable:
    405      rnp = *(RName**)value;
    406      rnp->state = Suspended;
    407      PR_LOG(lm, PR_LOG_DEBUG, ("PRSetTraceOption: PRTraceDisable: %p", rnp));
    408      break;
    409 
    410    case PRTraceSuspend:
    411      traceState = Suspended;
    412      PR_LOG(lm, PR_LOG_DEBUG, ("PRSetTraceOption: PRTraceSuspend"));
    413      break;
    414 
    415    case PRTraceResume:
    416      traceState = Running;
    417      PR_LOG(lm, PR_LOG_DEBUG, ("PRSetTraceOption: PRTraceResume"));
    418      break;
    419 
    420    case PRTraceSuspendRecording:
    421      PR_Lock(logLock);
    422      logOrder = LogSuspend;
    423      PR_NotifyCondVar(logCVar);
    424      PR_Unlock(logLock);
    425      PR_LOG(lm, PR_LOG_DEBUG, ("PRSetTraceOption: PRTraceSuspendRecording"));
    426      break;
    427 
    428    case PRTraceResumeRecording:
    429      PR_LOG(lm, PR_LOG_DEBUG, ("PRSetTraceOption: PRTraceResumeRecording"));
    430      if (logState != LogSuspend) {
    431        break;
    432      }
    433      PR_Lock(logLock);
    434      logOrder = LogResume;
    435      PR_NotifyCondVar(logCVar);
    436      PR_Unlock(logLock);
    437      break;
    438 
    439    case PRTraceStopRecording:
    440      PR_Lock(logLock);
    441      logOrder = LogStop;
    442      PR_NotifyCondVar(logCVar);
    443      PR_Unlock(logLock);
    444      PR_LOG(lm, PR_LOG_DEBUG, ("PRSetTraceOption: PRTraceStopRecording"));
    445      break;
    446 
    447    case PRTraceLockHandles:
    448      PR_LOG(lm, PR_LOG_DEBUG, ("PRSetTraceOption: PRTraceLockTraceHandles"));
    449      PR_Lock(traceLock);
    450      break;
    451 
    452    case PRTraceUnLockHandles:
    453      PR_LOG(lm, PR_LOG_DEBUG, ("PRSetTraceOption: PRTraceUnLockHandles"));
    454      PR_Unlock(traceLock);
    455      break;
    456 
    457    default:
    458      PR_LOG(lm, PR_LOG_ERROR,
    459             ("PRSetTraceOption: Invalid command %ld", command));
    460      PR_ASSERT(0);
    461      break;
    462  } /* end switch() */
    463  return;
    464 } /* end  PR_SetTraceOption() */
    465 
    466 /*
    467 **
    468 */
    469 PR_IMPLEMENT(void)
    470 PR_GetTraceOption(PRTraceOption command, /* One of the enumerated values */
    471                  void* value            /* command value or NULL */
    472 ) {
    473  switch (command) {
    474    case PRTraceBufSize:
    475      *((PRInt32*)value) = bufSize;
    476      PR_LOG(lm, PR_LOG_DEBUG,
    477             ("PRGetTraceOption: PRTraceBufSize: %ld", bufSize));
    478      break;
    479 
    480    default:
    481      PR_LOG(lm, PR_LOG_ERROR,
    482             ("PRGetTraceOption: Invalid command %ld", command));
    483      PR_ASSERT(0);
    484      break;
    485  } /* end switch() */
    486  return;
    487 } /* end PR_GetTraceOption() */
    488 
    489 /*
    490 **
    491 */
    492 PR_IMPLEMENT(PRTraceHandle)
    493 PR_GetTraceHandleFromName(const char* qName, /* QName search argument */
    494                          const char* rName  /* RName search argument */
    495 ) {
    496  const char *qn, *rn, *desc;
    497  PRTraceHandle qh, rh = NULL;
    498  RName* rnp = NULL;
    499 
    500  PR_LOG(lm, PR_LOG_DEBUG,
    501         ("PRTrace: GetTraceHandleFromName:\n\t"
    502          "QName: %s, RName: %s",
    503          qName, rName));
    504 
    505  qh = PR_FindNextTraceQname(NULL);
    506  while (qh != NULL) {
    507    rh = PR_FindNextTraceRname(NULL, qh);
    508    while (rh != NULL) {
    509      PR_GetTraceNameFromHandle(rh, &qn, &rn, &desc);
    510      if ((strcmp(qName, qn) == 0) && (strcmp(rName, rn) == 0)) {
    511        rnp = (RName*)rh;
    512        goto foundIt;
    513      }
    514      rh = PR_FindNextTraceRname(rh, qh);
    515    }
    516    qh = PR_FindNextTraceQname(NULL);
    517  }
    518 
    519 foundIt:
    520  PR_LOG(lm, PR_LOG_DEBUG, ("PR_Counter: GetConterHandleFromName: %p", rnp));
    521  return (rh);
    522 } /* end PR_GetTraceHandleFromName() */
    523 
    524 /*
    525 **
    526 */
    527 PR_IMPLEMENT(void)
    528 PR_GetTraceNameFromHandle(
    529    PRTraceHandle handle,    /* handle as search argument */
    530    const char** qName,      /* pointer to associated QName */
    531    const char** rName,      /* pointer to associated RName */
    532    const char** description /* pointer to associated description */
    533 ) {
    534  RName* rnp = (RName*)handle;
    535  QName* qnp = rnp->qName;
    536 
    537  *qName = qnp->name;
    538  *rName = rnp->name;
    539  *description = rnp->desc;
    540 
    541  PR_LOG(lm, PR_LOG_DEBUG,
    542         ("PRTrace: GetConterNameFromHandle: "
    543          "QNp: %p, RNp: %p,\n\tQName: %s, RName: %s, Desc: %s",
    544          qnp, rnp, qnp->name, rnp->name, rnp->desc));
    545 
    546  return;
    547 } /* end PR_GetTraceNameFromHandle() */
    548 
    549 /*
    550 **
    551 */
    552 PR_IMPLEMENT(PRTraceHandle)
    553 PR_FindNextTraceQname(PRTraceHandle handle) {
    554  QName* qnp = (QName*)handle;
    555 
    556  if (PR_CLIST_IS_EMPTY(&qNameList)) {
    557    qnp = NULL;
    558  } else if (qnp == NULL) {
    559    qnp = (QName*)PR_LIST_HEAD(&qNameList);
    560  } else if (PR_NEXT_LINK(&qnp->link) == &qNameList) {
    561    qnp = NULL;
    562  } else {
    563    qnp = (QName*)PR_NEXT_LINK(&qnp->link);
    564  }
    565 
    566  PR_LOG(lm, PR_LOG_DEBUG,
    567         ("PRTrace: FindNextQname: Handle: %p, Returns: %p", handle, qnp));
    568 
    569  return ((PRTraceHandle)qnp);
    570 } /* end PR_FindNextTraceQname() */
    571 
    572 /*
    573 **
    574 */
    575 PR_IMPLEMENT(PRTraceHandle)
    576 PR_FindNextTraceRname(PRTraceHandle rhandle, PRTraceHandle qhandle) {
    577  RName* rnp = (RName*)rhandle;
    578  QName* qnp = (QName*)qhandle;
    579 
    580  if (PR_CLIST_IS_EMPTY(&qnp->rNameList)) {
    581    rnp = NULL;
    582  } else if (rnp == NULL) {
    583    rnp = (RName*)PR_LIST_HEAD(&qnp->rNameList);
    584  } else if (PR_NEXT_LINK(&rnp->link) == &qnp->rNameList) {
    585    rnp = NULL;
    586  } else {
    587    rnp = (RName*)PR_NEXT_LINK(&rnp->link);
    588  }
    589 
    590  PR_LOG(lm, PR_LOG_DEBUG,
    591         ("PRTrace: FindNextRname: Rhandle: %p, QHandle: %p, Returns: %p",
    592          rhandle, qhandle, rnp));
    593 
    594  return ((PRTraceHandle)rnp);
    595 } /* end PR_FindNextTraceRname() */
    596 
    597 /*
    598 **
    599 */
    600 static PRFileDesc* InitializeRecording(void) {
    601  char* logFileName;
    602  PRFileDesc* logFile;
    603 
    604  /* Self initialize, if necessary */
    605  if (traceLock == NULL) {
    606    _PR_InitializeTrace();
    607  }
    608 
    609  PR_LOG(lm, PR_LOG_DEBUG, ("PR_RecordTraceEntries: begins"));
    610 
    611  logLostData = 0; /* reset at entry */
    612  logState = LogReset;
    613 
    614  /* Get the filename for the logfile from the environment */
    615  logFileName = PR_GetEnvSecure("NSPR_TRACE_LOG");
    616  if (logFileName == NULL) {
    617    PR_LOG(lm, PR_LOG_ERROR,
    618           ("RecordTraceEntries: Environment variable not defined. Exiting"));
    619    return NULL;
    620  }
    621 
    622  /* Open the logfile */
    623  logFile = PR_Open(logFileName, PR_WRONLY | PR_CREATE_FILE, 0666);
    624  if (logFile == NULL) {
    625    PR_LOG(
    626        lm, PR_LOG_ERROR,
    627        ("RecordTraceEntries: Cannot open %s as trace log file. OS error: %ld",
    628         logFileName, PR_GetOSError()));
    629    return NULL;
    630  }
    631  return logFile;
    632 } /* end InitializeRecording() */
    633 
    634 /*
    635 **
    636 */
    637 static void ProcessOrders(void) {
    638  switch (logOrder) {
    639    case LogReset:
    640      logOrder = logState = localState;
    641      PR_LOG(lm, PR_LOG_DEBUG, ("RecordTraceEntries: LogReset"));
    642      break;
    643 
    644    case LogSuspend:
    645      localState = logOrder = logState = LogSuspend;
    646      PR_LOG(lm, PR_LOG_DEBUG, ("RecordTraceEntries: LogSuspend"));
    647      break;
    648 
    649    case LogResume:
    650      localState = logOrder = logState = LogActive;
    651      PR_LOG(lm, PR_LOG_DEBUG, ("RecordTraceEntries: LogResume"));
    652      break;
    653 
    654    case LogStop:
    655      logOrder = logState = LogStop;
    656      PR_LOG(lm, PR_LOG_DEBUG, ("RecordTraceEntries: LogStop"));
    657      break;
    658 
    659    default:
    660      PR_LOG(lm, PR_LOG_ERROR,
    661             ("RecordTraceEntries: Invalid logOrder: %ld", logOrder));
    662      PR_ASSERT(0);
    663      break;
    664  } /* end switch() */
    665  return;
    666 } /* end ProcessOrders() */
    667 
    668 /*
    669 **
    670 */
    671 static void WriteTraceSegment(PRFileDesc* logFile, void* buf, PRInt32 amount) {
    672  PRInt32 rc;
    673 
    674  PR_LOG(lm, PR_LOG_ERROR,
    675         ("WriteTraceSegment: Buffer: %p, Amount: %ld", buf, amount));
    676  rc = PR_Write(logFile, buf, amount);
    677  if (rc == -1)
    678    PR_LOG(
    679        lm, PR_LOG_ERROR,
    680        ("RecordTraceEntries: PR_Write() failed. Error: %ld", PR_GetError()));
    681  else if (rc != amount)
    682    PR_LOG(lm, PR_LOG_ERROR,
    683           ("RecordTraceEntries: PR_Write() Tried to write: %ld, Wrote: %ld",
    684            amount, rc));
    685  else
    686    PR_LOG(lm, PR_LOG_DEBUG,
    687           ("RecordTraceEntries: PR_Write(): Buffer: %p, bytes: %ld", buf,
    688            amount));
    689 
    690  return;
    691 } /* end WriteTraceSegment() */
    692 
    693 /*
    694 **
    695 */
    696 PR_IMPLEMENT(void)
    697 PR_RecordTraceEntries(void) {
    698  PRFileDesc* logFile;
    699  PRInt32 lostSegments;
    700  PRInt32 currentSegment = 0;
    701  void* buf;
    702  PRBool doWrite;
    703 
    704  logFile = InitializeRecording();
    705  if (logFile == NULL) {
    706    PR_LOG(lm, PR_LOG_DEBUG, ("PR_RecordTraceEntries: Failed to initialize"));
    707    return;
    708  }
    709 
    710  /* Do this until told to stop */
    711  while (logState != LogStop) {
    712    PR_Lock(logLock);
    713 
    714    while ((logCount == 0) && (logOrder == logState)) {
    715      PR_WaitCondVar(logCVar, PR_INTERVAL_NO_TIMEOUT);
    716    }
    717 
    718    /* Handle state transitions */
    719    if (logOrder != logState) {
    720      ProcessOrders();
    721    }
    722 
    723    /* recalculate local controls */
    724    if (logCount) {
    725      lostSegments = logCount - logSegments;
    726      if (lostSegments > 0) {
    727        logLostData += (logCount - logSegments);
    728        logCount = (logCount % logSegments);
    729        currentSegment = logCount;
    730        PR_LOG(lm, PR_LOG_DEBUG,
    731               ("PR_RecordTraceEntries: LostData segments: %ld", logLostData));
    732      } else {
    733        logCount--;
    734      }
    735 
    736      buf = tBuf + (logEntriesPerSegment * currentSegment);
    737      if (++currentSegment >= logSegments) {
    738        currentSegment = 0;
    739      }
    740      doWrite = PR_TRUE;
    741    } else {
    742      doWrite = PR_FALSE;
    743    }
    744 
    745    PR_Unlock(logLock);
    746 
    747    if (doWrite == PR_TRUE) {
    748      if (localState != LogSuspend) {
    749        WriteTraceSegment(logFile, buf, logSegSize);
    750      } else
    751        PR_LOG(lm, PR_LOG_DEBUG,
    752               ("RecordTraceEntries: PR_Write(): is suspended"));
    753    }
    754 
    755  } /* end while(logState...) */
    756 
    757  PR_Close(logFile);
    758  PR_LOG(lm, PR_LOG_DEBUG, ("RecordTraceEntries: exiting"));
    759  return;
    760 } /* end  PR_RecordTraceEntries() */
    761 
    762 /*
    763 **
    764 */
    765 PR_IMPLEMENT(PRIntn)
    766 PR_GetTraceEntries(PRTraceEntry* buffer, /* where to write output */
    767                   PRInt32 count,        /* number to get */
    768                   PRInt32* found        /* number you got */
    769 ) {
    770  PRInt32 rc;
    771  PRInt32 copied = 0;
    772 
    773  PR_Lock(traceLock);
    774 
    775  /*
    776  ** Depending on where the LastSeen and Next indices are,
    777  ** copy the trace buffer in one or two pieces.
    778  */
    779  PR_LOG(lm, PR_LOG_ERROR,
    780         ("PR_GetTraceEntries: Next: %ld, LastSeen: %ld", next, fetchLastSeen));
    781 
    782  if (fetchLastSeen <= next) {
    783    while ((count-- > 0) && (fetchLastSeen < next)) {
    784      *(buffer + copied++) = *(tBuf + fetchLastSeen++);
    785    }
    786    PR_LOG(lm, PR_LOG_ERROR,
    787           ("PR_GetTraceEntries: Copied: %ld, LastSeen: %ld", copied,
    788            fetchLastSeen));
    789  } else /* copy in 2 parts */
    790  {
    791    while (count-- > 0 && fetchLastSeen <= last) {
    792      *(buffer + copied++) = *(tBuf + fetchLastSeen++);
    793    }
    794    fetchLastSeen = 0;
    795 
    796    PR_LOG(lm, PR_LOG_ERROR,
    797           ("PR_GetTraceEntries: Copied: %ld, LastSeen: %ld", copied,
    798            fetchLastSeen));
    799 
    800    while (count-- > 0 && fetchLastSeen < next) {
    801      *(buffer + copied++) = *(tBuf + fetchLastSeen++);
    802    }
    803    PR_LOG(lm, PR_LOG_ERROR,
    804           ("PR_GetTraceEntries: Copied: %ld, LastSeen: %ld", copied,
    805            fetchLastSeen));
    806  }
    807 
    808  *found = copied;
    809  rc = (fetchLostData == PR_TRUE) ? 1 : 0;
    810  fetchLostData = PR_FALSE;
    811 
    812  PR_Unlock(traceLock);
    813  return rc;
    814 } /* end PR_GetTraceEntries() */
    815 
    816 /* end prtrace.c */