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