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