michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: ** File: instrumt.c michael@0: ** Description: This test is for the NSPR debug aids defined in michael@0: ** prcountr.h, prtrace.h, prolock.h michael@0: ** michael@0: ** The test case tests the three debug aids in NSPR: michael@0: ** michael@0: ** Diagnostic messages can be enabled using "instrumt -v 6" michael@0: ** This sets the msgLevel to something that PR_LOG() likes. michael@0: ** Also define in the environment "NSPR_LOG_MODULES=Test:6" michael@0: ** michael@0: ** CounterTest() tests the counter facility. This test michael@0: ** creates 4 threads. Each thread either increments, decrements, michael@0: ** adds to or subtracts from a counter, depending on an argument michael@0: ** passed to the thread at thread-create time. Each of these threads michael@0: ** does COUNT_LIMIT iterations doing its thing. When all 4 threads michael@0: ** are done, the result of the counter is evaluated. If all was atomic, michael@0: ** the the value of the counter should be zero. michael@0: ** michael@0: ** TraceTest(): michael@0: ** This test mingles with the counter test. Counters trace. michael@0: ** A thread to extract trace entries on the fly is started. michael@0: ** A thread to dump trace entries to a file is started. michael@0: ** michael@0: ** OrderedLockTest(): michael@0: ** michael@0: ** michael@0: ** michael@0: ** michael@0: ** michael@0: */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #define COUNT_LIMIT (10 * ( 1024)) michael@0: michael@0: #define SMALL_TRACE_BUFSIZE ( 60 * 1024 ) michael@0: michael@0: typedef enum michael@0: { michael@0: CountLoop = 1, michael@0: TraceLoop = 2, michael@0: TraceFlow = 3 michael@0: } TraceTypes; michael@0: michael@0: michael@0: PRLogModuleLevel msgLevel = PR_LOG_ALWAYS; michael@0: michael@0: PRBool help = PR_FALSE; michael@0: PRBool failed = PR_FALSE; michael@0: michael@0: michael@0: PRLogModuleInfo *lm; michael@0: PRMonitor *mon; michael@0: PRInt32 activeThreads = 0; michael@0: PR_DEFINE_COUNTER( hCounter ); michael@0: PR_DEFINE_TRACE( hTrace ); michael@0: michael@0: static void Help(void) michael@0: { michael@0: printf("Help? ... Ha!\n"); michael@0: } michael@0: michael@0: static void ListCounters(void) michael@0: { michael@0: PR_DEFINE_COUNTER( qh ); michael@0: PR_DEFINE_COUNTER( rh ); michael@0: const char *qn, *rn, *dn; michael@0: const char **qname = &qn, **rname = &rn, **desc = &dn; michael@0: PRUint32 tCtr; michael@0: michael@0: PR_INIT_COUNTER_HANDLE( qh, NULL ); michael@0: PR_FIND_NEXT_COUNTER_QNAME(qh, qh ); michael@0: while ( qh != NULL ) michael@0: { michael@0: PR_INIT_COUNTER_HANDLE( rh, NULL ); michael@0: PR_FIND_NEXT_COUNTER_RNAME(rh, rh, qh ); michael@0: while ( rh != NULL ) michael@0: { michael@0: PR_GET_COUNTER_NAME_FROM_HANDLE( rh, qname, rname, desc ); michael@0: PR_GET_COUNTER(tCtr, rh); michael@0: PR_LOG( lm, msgLevel, michael@0: ( "QName: %s RName: %s Desc: %s Value: %ld\n", michael@0: qn, rn, dn, tCtr )); michael@0: PR_FIND_NEXT_COUNTER_RNAME(rh, rh, qh ); michael@0: } michael@0: PR_FIND_NEXT_COUNTER_QNAME(qh, qh); michael@0: } michael@0: return; michael@0: } /* end ListCounters() */ michael@0: michael@0: static void ListTraces(void) michael@0: { michael@0: PR_DEFINE_TRACE( qh ); michael@0: PR_DEFINE_TRACE( rh ); michael@0: const char *qn, *rn, *dn; michael@0: const char **qname = &qn, **rname = &rn, **desc = &dn; michael@0: michael@0: PR_INIT_TRACE_HANDLE( qh, NULL ); michael@0: PR_FIND_NEXT_TRACE_QNAME(qh, qh ); michael@0: while ( qh != NULL ) michael@0: { michael@0: PR_INIT_TRACE_HANDLE( rh, NULL ); michael@0: PR_FIND_NEXT_TRACE_RNAME(rh, rh, qh ); michael@0: while ( rh != NULL ) michael@0: { michael@0: PR_GET_TRACE_NAME_FROM_HANDLE( rh, qname, rname, desc ); michael@0: PR_LOG( lm, msgLevel, michael@0: ( "QName: %s RName: %s Desc: %s", michael@0: qn, rn, dn )); michael@0: PR_FIND_NEXT_TRACE_RNAME(rh, rh, qh ); michael@0: } michael@0: PR_FIND_NEXT_TRACE_QNAME(qh, qh); michael@0: } michael@0: return; michael@0: } /* end ListCounters() */ michael@0: michael@0: michael@0: static PRInt32 one = 1; michael@0: static PRInt32 two = 2; michael@0: static PRInt32 three = 3; michael@0: static PRInt32 four = 4; michael@0: michael@0: /* michael@0: ** Thread to iteratively count something. michael@0: */ michael@0: static void PR_CALLBACK CountSomething( void *arg ) michael@0: { michael@0: PRInt32 switchVar = *((PRInt32 *)arg); michael@0: PRInt32 i; michael@0: michael@0: PR_LOG( lm, msgLevel, michael@0: ("CountSomething: begin thread %ld", switchVar )); michael@0: michael@0: for ( i = 0; i < COUNT_LIMIT ; i++) michael@0: { michael@0: switch ( switchVar ) michael@0: { michael@0: case 1 : michael@0: PR_INCREMENT_COUNTER( hCounter ); michael@0: break; michael@0: case 2 : michael@0: PR_DECREMENT_COUNTER( hCounter ); michael@0: break; michael@0: case 3 : michael@0: PR_ADD_TO_COUNTER( hCounter, 1 ); michael@0: break; michael@0: case 4 : michael@0: PR_SUBTRACT_FROM_COUNTER( hCounter, 1 ); michael@0: break; michael@0: default : michael@0: PR_ASSERT( 0 ); michael@0: break; michael@0: } michael@0: PR_TRACE( hTrace, CountLoop, switchVar, i, 0, 0, 0, 0, 0 ); michael@0: } /* end for() */ michael@0: michael@0: PR_LOG( lm, msgLevel, michael@0: ("CounterSomething: end thread %ld", switchVar )); michael@0: michael@0: PR_EnterMonitor(mon); michael@0: --activeThreads; michael@0: PR_Notify( mon ); michael@0: PR_ExitMonitor(mon); michael@0: michael@0: return; michael@0: } /* end CountSomething() */ michael@0: michael@0: /* michael@0: ** Create the counter threads. michael@0: */ michael@0: static void CounterTest( void ) michael@0: { michael@0: PRThread *t1, *t2, *t3, *t4; michael@0: PRIntn i = 0; michael@0: PR_DEFINE_COUNTER( tc ); michael@0: PR_DEFINE_COUNTER( zCounter ); michael@0: michael@0: PR_LOG( lm, msgLevel, michael@0: ("Begin CounterTest")); michael@0: michael@0: /* michael@0: ** Test Get and Set of a counter. michael@0: ** michael@0: */ michael@0: PR_CREATE_COUNTER( zCounter, "Atomic", "get/set test", "test get and set of counter" ); michael@0: PR_SET_COUNTER( zCounter, 9 ); michael@0: PR_GET_COUNTER( i, zCounter ); michael@0: if ( i != 9 ) michael@0: { michael@0: failed = PR_TRUE; michael@0: PR_LOG( lm, msgLevel, michael@0: ("Counter set/get failed")); michael@0: } michael@0: michael@0: activeThreads += 4; michael@0: PR_CREATE_COUNTER( hCounter, "Atomic", "SMP Tests", "test atomic nature of counter" ); michael@0: michael@0: PR_GET_COUNTER_HANDLE_FROM_NAME( tc, "Atomic", "SMP Tests" ); michael@0: PR_ASSERT( tc == hCounter ); michael@0: michael@0: t1 = PR_CreateThread(PR_USER_THREAD, michael@0: CountSomething, &one, michael@0: PR_PRIORITY_NORMAL, michael@0: PR_GLOBAL_THREAD, michael@0: PR_UNJOINABLE_THREAD, michael@0: 0); michael@0: PR_ASSERT(t1); michael@0: michael@0: t2 = PR_CreateThread(PR_USER_THREAD, michael@0: CountSomething, &two, michael@0: PR_PRIORITY_NORMAL, michael@0: PR_GLOBAL_THREAD, michael@0: PR_UNJOINABLE_THREAD, michael@0: 0); michael@0: PR_ASSERT(t2); michael@0: michael@0: t3 = PR_CreateThread(PR_USER_THREAD, michael@0: CountSomething, &three, michael@0: PR_PRIORITY_NORMAL, michael@0: PR_GLOBAL_THREAD, michael@0: PR_UNJOINABLE_THREAD, michael@0: 0); michael@0: PR_ASSERT(t3); michael@0: michael@0: t4 = PR_CreateThread(PR_USER_THREAD, michael@0: CountSomething, &four, michael@0: PR_PRIORITY_NORMAL, michael@0: PR_GLOBAL_THREAD, michael@0: PR_UNJOINABLE_THREAD, michael@0: 0); michael@0: PR_ASSERT(t4); michael@0: michael@0: PR_LOG( lm, msgLevel, michael@0: ("Counter Threads started")); michael@0: michael@0: ListCounters(); michael@0: return; michael@0: } /* end CounterTest() */ michael@0: michael@0: /* michael@0: ** Thread to dump trace buffer to a file. michael@0: */ michael@0: static void PR_CALLBACK RecordTrace(void *arg ) michael@0: { michael@0: PR_RECORD_TRACE_ENTRIES(); michael@0: michael@0: PR_EnterMonitor(mon); michael@0: --activeThreads; michael@0: PR_Notify( mon ); michael@0: PR_ExitMonitor(mon); michael@0: michael@0: return; michael@0: } /* end RecordTrace() */ michael@0: michael@0: michael@0: michael@0: #define NUM_TRACE_RECORDS ( 10000 ) michael@0: /* michael@0: ** Thread to extract and print trace entries from the buffer. michael@0: */ michael@0: static void PR_CALLBACK SampleTrace( void *arg ) michael@0: { michael@0: #if defined(DEBUG) || defined(FORCE_NSPR_TRACE) michael@0: PRInt32 found, rc; michael@0: PRTraceEntry *foundEntries; michael@0: PRInt32 i; michael@0: michael@0: foundEntries = (PRTraceEntry *)PR_Malloc( NUM_TRACE_RECORDS * sizeof(PRTraceEntry)); michael@0: PR_ASSERT(foundEntries != NULL ); michael@0: michael@0: do michael@0: { michael@0: rc = PR_GetTraceEntries( foundEntries, NUM_TRACE_RECORDS, &found); michael@0: PR_LOG( lm, msgLevel, michael@0: ("SampleTrace: Lost Data: %ld found: %ld", rc, found )); michael@0: michael@0: if ( found != 0) michael@0: { michael@0: for ( i = 0 ; i < found; i++ ) michael@0: { michael@0: PR_LOG( lm, msgLevel, michael@0: ("SampleTrace, detail: Thread: %p, Time: %llX, UD0: %ld, UD1: %ld, UD2: %8.8ld", michael@0: (foundEntries +i)->thread, michael@0: (foundEntries +i)->time, michael@0: (foundEntries +i)->userData[0], michael@0: (foundEntries +i)->userData[1], michael@0: (foundEntries +i)->userData[2] )); michael@0: } michael@0: } michael@0: PR_Sleep(PR_MillisecondsToInterval(50)); michael@0: } michael@0: while( found != 0 && activeThreads >= 1 ); michael@0: michael@0: PR_Free( foundEntries ); michael@0: michael@0: PR_EnterMonitor(mon); michael@0: --activeThreads; michael@0: PR_Notify( mon ); michael@0: PR_ExitMonitor(mon); michael@0: michael@0: PR_LOG( lm, msgLevel, michael@0: ("SampleTrace(): exiting")); michael@0: michael@0: #endif michael@0: return; michael@0: } /* end RecordTrace() */ michael@0: michael@0: /* michael@0: ** Basic trace test. michael@0: */ michael@0: static void TraceTest( void ) michael@0: { michael@0: PRInt32 i; michael@0: PRInt32 size; michael@0: PR_DEFINE_TRACE( th ); michael@0: PRThread *t1, *t2; michael@0: michael@0: PR_LOG( lm, msgLevel, michael@0: ("Begin TraceTest")); michael@0: michael@0: size = SMALL_TRACE_BUFSIZE; michael@0: PR_SET_TRACE_OPTION( PRTraceBufSize, &size ); michael@0: PR_GET_TRACE_OPTION( PRTraceBufSize, &i ); michael@0: michael@0: PR_CREATE_TRACE( th, "TraceTest", "tt2", "A description for the trace test" ); michael@0: PR_CREATE_TRACE( th, "TraceTest", "tt3", "A description for the trace test" ); michael@0: PR_CREATE_TRACE( th, "TraceTest", "tt4", "A description for the trace test" ); michael@0: PR_CREATE_TRACE( th, "TraceTest", "tt5", "A description for the trace test" ); michael@0: PR_CREATE_TRACE( th, "TraceTest", "tt6", "A description for the trace test" ); michael@0: PR_CREATE_TRACE( th, "TraceTest", "tt7", "A description for the trace test" ); michael@0: PR_CREATE_TRACE( th, "TraceTest", "tt8", "A description for the trace test" ); michael@0: michael@0: PR_CREATE_TRACE( th, "Trace Test", "tt0", "QName is Trace Test, not TraceTest" ); michael@0: PR_CREATE_TRACE( th, "Trace Test", "tt1", "QName is Trace Test, not TraceTest" ); michael@0: PR_CREATE_TRACE( th, "Trace Test", "tt2", "QName is Trace Test, not TraceTest" ); michael@0: PR_CREATE_TRACE( th, "Trace Test", "tt3", "QName is Trace Test, not TraceTest" ); michael@0: PR_CREATE_TRACE( th, "Trace Test", "tt4", "QName is Trace Test, not TraceTest" ); michael@0: PR_CREATE_TRACE( th, "Trace Test", "tt5", "QName is Trace Test, not TraceTest" ); michael@0: PR_CREATE_TRACE( th, "Trace Test", "tt6", "QName is Trace Test, not TraceTest" ); michael@0: PR_CREATE_TRACE( th, "Trace Test", "tt7", "QName is Trace Test, not TraceTest" ); michael@0: PR_CREATE_TRACE( th, "Trace Test", "tt8", "QName is Trace Test, not TraceTest" ); michael@0: PR_CREATE_TRACE( th, "Trace Test", "tt9", "QName is Trace Test, not TraceTest" ); michael@0: PR_CREATE_TRACE( th, "Trace Test", "tt10", "QName is Trace Test, not TraceTest" ); michael@0: michael@0: michael@0: michael@0: activeThreads += 2; michael@0: t1 = PR_CreateThread(PR_USER_THREAD, michael@0: RecordTrace, NULL, michael@0: PR_PRIORITY_NORMAL, michael@0: PR_GLOBAL_THREAD, michael@0: PR_UNJOINABLE_THREAD, michael@0: 0); michael@0: PR_ASSERT(t1); michael@0: michael@0: t2 = PR_CreateThread(PR_USER_THREAD, michael@0: SampleTrace, 0, michael@0: PR_PRIORITY_NORMAL, michael@0: PR_GLOBAL_THREAD, michael@0: PR_UNJOINABLE_THREAD, michael@0: 0); michael@0: PR_ASSERT(t2); michael@0: michael@0: ListTraces(); michael@0: michael@0: PR_GET_TRACE_HANDLE_FROM_NAME( th, "TraceTest","tt1" ); michael@0: PR_ASSERT( th == hTrace ); michael@0: michael@0: PR_LOG( lm, msgLevel, michael@0: ("End TraceTest")); michael@0: return; michael@0: } /* end TraceTest() */ michael@0: michael@0: michael@0: /* michael@0: ** Ordered lock test. michael@0: */ michael@0: static void OrderedLockTest( void ) michael@0: { michael@0: PR_LOG( lm, msgLevel, michael@0: ("Begin OrderedLockTest")); michael@0: michael@0: michael@0: } /* end OrderedLockTest() */ michael@0: michael@0: michael@0: int main(int argc, char **argv) michael@0: { michael@0: #if defined(DEBUG) || defined(FORCE_NSPR_TRACE) michael@0: PRUint32 counter; michael@0: PLOptStatus os; michael@0: PLOptState *opt = PL_CreateOptState(argc, argv, "hdv:"); michael@0: lm = PR_NewLogModule("Test"); michael@0: michael@0: while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) michael@0: { michael@0: if (PL_OPT_BAD == os) continue; michael@0: switch (opt->option) michael@0: { michael@0: case 'v': /* verbose mode */ michael@0: msgLevel = (PRLogModuleLevel)atol( opt->value); michael@0: break; michael@0: case 'h': /* help message */ michael@0: Help(); michael@0: help = PR_TRUE; michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: PL_DestroyOptState(opt); michael@0: michael@0: PR_CREATE_TRACE( hTrace, "TraceTest", "tt1", "A description for the trace test" ); michael@0: mon = PR_NewMonitor(); michael@0: PR_EnterMonitor( mon ); michael@0: michael@0: TraceTest(); michael@0: CounterTest(); michael@0: OrderedLockTest(); michael@0: michael@0: /* Wait for all threads to exit */ michael@0: while ( activeThreads > 0 ) { michael@0: if ( activeThreads == 1 ) michael@0: PR_SET_TRACE_OPTION( PRTraceStopRecording, NULL ); michael@0: PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); michael@0: PR_GET_COUNTER( counter, hCounter ); michael@0: } michael@0: PR_ExitMonitor( mon ); michael@0: michael@0: /* michael@0: ** Evaluate results michael@0: */ michael@0: PR_GET_COUNTER( counter, hCounter ); michael@0: if ( counter != 0 ) michael@0: { michael@0: failed = PR_TRUE; michael@0: PR_LOG( lm, msgLevel, michael@0: ("Expected counter == 0, found: %ld", counter)); michael@0: printf("FAIL\n"); michael@0: } michael@0: else michael@0: { michael@0: printf("PASS\n"); michael@0: } michael@0: michael@0: michael@0: PR_DESTROY_COUNTER( hCounter ); michael@0: michael@0: PR_DestroyMonitor( mon ); michael@0: michael@0: PR_TRACE( hTrace, TraceFlow, 0xfff,0,0,0,0,0,0); michael@0: PR_DESTROY_TRACE( hTrace ); michael@0: #else michael@0: printf("Test not defined\n"); michael@0: #endif michael@0: return 0; michael@0: } /* main() */ michael@0: /* end instrumt.c */ michael@0: