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