413 lines
11 KiB
C
413 lines
11 KiB
C
/* -*- 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 */
|