| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| /* ***** BEGIN LICENSE BLOCK ***** |
| * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
| * |
| * The contents of this file are subject to the Mozilla Public License Version |
| * 1.1 (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * http://www.mozilla.org/MPL/ |
| * |
| * Software distributed under the License is distributed on an "AS IS" basis, |
| * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
| * for the specific language governing rights and limitations under the |
| * License. |
| * |
| * The Original Code is the Netscape Portable Runtime (NSPR). |
| * |
| * The Initial Developer of the Original Code is |
| * Netscape Communications Corporation. |
| * Portions created by the Initial Developer are Copyright (C) 1998-2000 |
| * the Initial Developer. All Rights Reserved. |
| * |
| * Contributor(s): |
| * |
| * Alternatively, the contents of this file may be used under the terms of |
| * either the GNU General Public License Version 2 or later (the "GPL"), or |
| * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
| * in which case the provisions of the GPL or the LGPL are applicable instead |
| * of those above. If you wish to allow use of your version of this file only |
| * under the terms of either the GPL or the LGPL, and not to allow others to |
| * use your version of this file under the terms of the MPL, indicate your |
| * decision by deleting the provisions above and replace them with the notice |
| * and other provisions required by the GPL or the LGPL. If you do not delete |
| * the provisions above, a recipient may use your version of this file under |
| * the terms of any one of the MPL, the GPL or the LGPL. |
| * |
| * ***** END LICENSE BLOCK ***** */ |
| |
| /* |
| ** 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 ); |
| tCtr = 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 */ |
| |