CTRTEST is a test program designed to allow the Extensible Performance Counter developer to test their performance DLL in an isolated scenario. Normally, the developer must run the DLL in some performance monitoring client (e.g. Performance Monitor) which calls the DLL in the context of lots of other functions making debugging and troubleshooting difficult.
CTRTEST allows the developer of performance DLL's to test, debug and stress their DLL's without the need or influence of any other program or function. CTRTEST also provides a way to stress test the DLL in a multi-threaded environment. Although function calls on a particular performance library are synchronized within the process, it is important that the library support features such as Open/Close ref-counting in order to operate correctly in a multiple threaded environment. This is especially important in that this emulates the environment that the DLL will operate in when performance counters are monitored by a remote client. This is also a very critical test since if the DLL fails during remote performance monitoring it can cause the entire system to fail (i.e. "Blue Screen").
CTRTEST also allows the developer to run their DLL's in combination with other specific Counter DLL's in order to test for possible interaction problems.
Testing with ctrtest.exe is a method of “black box” testing which attempts to establish a benchmark for determining whether a performance library can be considered to be “trusted”. A trusted performance library will not be subjected to the level of run-time validation in Wbemperf.dll (via WMI) and Perflib (via RegQueryValuEx( HKey_PerformanceData ) ) that un-trusted performance libraries undergo. The reduced amount of error checking means higher performance from applications retrieving data from trusted performance libraries.
The command line syntax of CTRTEST is
CTRTEST <path to ini file>
where:
<path to ini file> is the location of the CTRTEST.INI file that contains the execution parameters. If this is not provided, CTRTEST will default to the current working directory for the location of the CTRTEST.INI file.
The format of the INI file is as follows:
[main]
NumThreads=10
CycleCount=100
LoopCount=1
Random=1
StopOnError=11
NumObjects=2
Object0=perfproc
Object1=perfos
Counter1=238
NumThreads is the number of threads that will run during the test at the same time. Each thread will run the same "Open, Collect X n, Close" cycle that is specified by CycleCount.
CycleCount is the number of times CTRTEST will call Open, Collect and Close in that sequence. This entry must be a decimal number.
LoopCount is the number of times CTRTEST will call the collect function between the Open and Close functions during each cylce. This entry must be a decimal number.
Random controls randomization on each cycle. If this value is non-zero, each time a cycle is run, CTRTEST will randomly choose a performance library to query.
StopOnError controls the behavior of ctrtest when an error is encountered. A value of 0 indicates that the application should continue running after an error is encountered, a 1 will cause the threads to gracefully terminate and the application will stop. A value of 2 will cause a debug breakpoint to be thrown, and processing will terminate immediately terminate at the offending error check.
NumObjects indicates how many performance DLL's CTRTEST should work with.
Objectn specifies the name of the service the performance counters are registered under. CTRTEST will only test counter DLL's that have been registered by LODCTR. This is also the name that your DLL will be listed with in the EXCTRLST list box. If your DLL supports more than one service entry, then you'll need to repeat the test for each service.
Countern specifies the query string that will be passed into the corresponding performance DLL's Collect function. If no value is specified, CTRTEST will default to use "Global".
For example, in the sample INI file above, when CTRTEST executes, it will spawn 10 threads that will each perform 100 Open/Collect/Close Cycles. During each cycle, Collect will be called once. On each cycle on each thread, CTRTEST will randomly choose between the "perfproc" and "perfos" performance DLL's. When the Collect function is called for "perfproc", CTRTEST will specify a query string of "Global". When the Collect function is called for "perfos", CTRTEST will specify a query string of "238" (which in this case is the "Processor" object).
While CTRTEST will spew statistics to the console (these can be redirected to a file for later processing). It's also important to monitor your test run using Performance Monitor. You should monitor the following counters from the Process object:
-- Private Bytes
-- Handle Count
-- Thread Count
While the test is running, these counters should not increase during the test. They should reach some operating level and stay there or below, but not slope up. If the plot of any of these counters goes up, then you probably have a resource leak somewhere in the DLL. You should run this test both from within a debugger as well as on it's own. Running in a debugger will allow you to catch any exceptions that may or may not be handled. Also running the test inside other tools such as NuMega's BoundsChecker can help spot memory and other resource leaks.
CTRTEST will also check that returned buffers do not have memory overwrite errors or that the returned buffer sizes make sense (e.g. less than or equal to the size of the specified buffer). If it encounters these sorts of conditions, it will stop processing.
Before you attempt to run your counter DLL in a "retail" or "production" environment, it should run cleanly with the following settings (This is the bare minimum test):
[main]
NumThreads=10
CycleCount=100
LoopCount=100
NumObjects=1
Object0=<your service name here>
Ideally you'll run more thorough tests such as
[main]
NumThreads=10
CycleCount=1000
LoopCount=100
NumObjects=1
Object0=<your service name here>
You should also try settings such as the following to check for potential interaction problems:
[main]
NumThreads=10
CycleCount=1000
LoopCount=100
Randomt=1
NumObjects=5
Object0=<your service name here>
Object1=perfproc
Object2=perfos
Object3=perfnet
Object4=perfdisk
You can use EXCTRLST to get a list of all Performance DLL's on a system, if you are suspecting interaction problems with another DLL other than the base ones mentioned above.
Test |
Goal |
Error
Checks |
|
|
|
Open entry point call on a performance library |
Opens performance library without failing. |
Assertion |
Collect entry point call on a performance library |
Collects performance blob consisting of 0 or more
bytes. |
Assertion; Blob offset not equal to advance in blob
pointer; Heap Corruption |
Close entry point call on a performance library |
Closes performance library without failing. |
Assertion |
Multiple overlapping calls to Open, Collect and Close
entry points on a performance library |
Open / close reference counting succeeds and
collection can occur. |
Assertion |
Processing multiple performance libraries on shared
thread |
Ensure that performance libraries may co-exist in
shared heap space. |
Assertion; Heap Corruption |
Blob boundary validation |
Ensure that the blob returned from the Collection
function did not over-write guard byte data outside of the allocated
buffer |
Assertion; Guard byte overwrite; Blob size comparison
to allocated buffer size |
Blob offset validation |
Walk the blob and ensure that all offsets resolve to
a value within the blob boundaries |
Assertion; Boundary validation |
Buffer size sanity check |
Ensure that the memory allocation required by the
performance blob does not exceed a maximum threshold (MAX_BUFFER_SIZE). |
Assertion; Out-of-memory; Max. buffer size |
Buffer size validation | Ensure that the value of the buffer size returned from the collect function is less than the size of the allocated buffer | |
Entry point latency |
Total wait time for obtaining the perflib-specific
mutex is less than a maximum threshold |
WaitForSingleObject timeout |
Enumerate Class Definitions |
Number of Class Definitions specified in Collect
parameter should be the same as the number within the performance blob |
Mismatch |
Blob size and pointer advancement |
The blob size returned from the collect function
should be equal to the blob pointer displacement |
Equivalence |
Blob offset validation |
Ensure that blob offsets are non-zero |
Equivalence |
Instance Validation |
Validate number of instances stored for each class is
equal to the number of instances specified by NumInstances in the
PERF_OBJECT_TYPE block. |
Mismatch |
Singleton Validation |
Validate that singleton object has the same number of
counters as specified by NumCounters in the PERF_OBJECT_TYPE block. |
Mismatch |
Counter Validation |
Validate that the number of counter definitions
stored for each instance is equal to the number of counters specified by
NumCounters in the PERF_OBJECT_TYPE block. |
Mismatch |
8 byte alignment |
Check that the returned buffer is 8-byte aligned. |
Masking |