/*++
*
* Copyright (c) 1996-2007 Ardence, a Citrix Company. All rights reserved.
*
* Module Name:
*
* Srtm.c
*
* Abstract:
*
* System Response Time Measurement (SRTM) utility. This utility measures
* timer delivery latencies using the RTX synchronized clock and timers.
* It then prints out this information.
*
* It optionaly uses the timer to drive the sound speaker (a 500 us timer
* will generate a 1 KHz square wave). Any significant timing jitter will
* produce a frequency wobble easily picked up by the human ear.
*
* Environment:
*
* RTX application (Win32 or RTSS).
*
* Revision History:
*
* 29-Jan-97 Updated timer calls for revised RTAPI specification
* 26-Feb-97 Improved resolution and average calculation
* 23-Jun-97 Improved clock offset calculation
* 12-Jul-97 Bucket size depends on clock resolution
* 12-Dec-97 Improved accuracy when latencies are longer then period
*
--*/
#include "windows.h"
#include "stdio.h"
#include "rtapi.h"
#define SPEAKER_PORT ((PUCHAR)0x61) // Port IO address for speaker
#define SPEAKER_BIT (0x02) // Speaker gate bit
#define NBUCKETS 1000 // Maximum number of buckets
//**** Error Codes ****//
#define NO_ERRORS 0
#define ERROR_OCCURED -1
//
// Local data.
//
LONG ClockResolution; // Clock resolution (100 ns units)
LONG ClockTimerPeriod; // Clock timer period (100 ns units)
LONG TimerPeriod; // Actual timer period (100 ns units)
LONG OutOfRangeCount; // Number of counts beyond range
LONG MaxLatency; // Maximum latency (100 ns units)
LONG Buckets[NBUCKETS]; // Histogram data
LONG BucketSize; // Histogram bucket size
LARGE_INTEGER Period; // Timer period
LARGE_INTEGER PreviousTime; // Previous timer expiration time
LARGE_INTEGER StartTime; // Start time of sampling run
BOOL Sound = FALSE; // Generate sound (square wave)
BOOL Hist = FALSE; // Display the histogram
BOOL Fastest = FALSE; // Use the fastest available timer
//
// Local function prototypes.
//
int RTFCNDCL TimerHandler (PVOID unused);
void helpMsg();
//
// **** MAIN ****
//
int main(int argc, char *argv[])
{
LARGE_INTEGER x, avg;
HANDLE hTimer;
LONG i, k, min, tot;
LONG sampleTime = 1000 * 15;
PCHAR msg = "ms";
ULONG stackSize = 0;
LONG usSize = 10;
ULONG nbytes = 1;
LONG decClockRes = 50;
int defaults = 1;
//
// Set default timer period to 1 MS.
//
Period.QuadPart = 10000;
//
// If there are no command arguments, then setup some defaults.
//
if (argc <= defaults)
{
Fastest = TRUE;
Sound = TRUE;
Hist = TRUE;
}
else
{
//
// Parse command line flags.
//
while (++argv && (--argc >= defaults) && ((argv[0][0]=='/') || (argv[0][0]=='-')))
{
if (argv[0][2]!='\0')
{
helpMsg();
return ERROR_OCCURED;
}
switch (argv[0][1])
{
case '?':
helpMsg();
return NO_ERROR;
break;
case 'h':
case 'H':
Hist = TRUE;
break;
case 's':
case 'S':
Sound = TRUE;
break;
case '1':
Period.QuadPart = 100000;
break;
case 'f':
case 'F':
Fastest = TRUE;
break;
default:
printf("Srtm: Error: Unknown flag.\n\n");
helpMsg();
return ERROR_OCCURED;
break;
} // end switch
} // end while
//
// Calculate the total time to sample (in milliseconds).
//
if ( (argc <= 0) || ((sampleTime= 1000 * atoi( argv[0])) <= 0) )
{
printf("\nSrtm: Error: Sample period is illegal or zero.\n\n");
helpMsg();
return ERROR_OCCURED;
}
} // end else
//
// Enable I/O to speaker port.
//
if (Sound == TRUE && !RtEnablePortIo( SPEAKER_PORT, nbytes))
{
printf("Srtm: Error: Can't enable port IO. GetLastError = %d\n", GetLastError());
return ERROR_OCCURED;
}
//
// Get the fastest clock resolution and clock timer period.
//
if (!RtGetClockResolution( CLOCK_FASTEST, &x))
{
printf("Srtm: Error: Could not get the clock resolution. GetLastError = %d\n", GetLastError());
return ERROR_OCCURED;
}
ClockResolution = (LONG) x.QuadPart;
if (!RtGetClockTimerPeriod( CLOCK_FASTEST, &x))
{
printf("Srtm: Error: Could not get timer period. GetLastError = %d\n", GetLastError());
return ERROR_OCCURED;
}
ClockTimerPeriod = (LONG) x.QuadPart;
//
// If requested, then use fastest available timer period.
//
if (Fastest)
Period.QuadPart = ClockTimerPeriod;
TimerPeriod = (LONG) Period.QuadPart;
//
// Print info about the clock and timer.
//
printf("\nSRTM v2.0 timer delivery latencies for a periodic RTX timer:\n");
printf(" Timer Period = %d us, Clock Resolution = %d.%1d us.\n",
TimerPeriod/10, ClockResolution/10, ClockResolution%10);
//
// Based on clock resolution, set the bucket size to 1 us or 1 ms.
//
if (ClockResolution <= decClockRes)
BucketSize = 10;
else
BucketSize = 10000;
//
// Setup and start a periodic timer.
//
if (!(hTimer = RtCreateTimer(NULL, // Security - NULL is none
stackSize, // Stack size - 0 is use default
TimerHandler, // Timer handler
NULL, // NULL context (argument to handler)
RT_PRIORITY_MAX, // Priority
CLOCK_FASTEST))) // Always use fastest available clock
{
printf("Srtm: Error: Could not create the timer. GetLastError = %d\n", GetLastError());
return ERROR_OCCURED;
}
if (!RtSetTimerRelative( hTimer, &Period, &Period))
{
printf("SRTM: ERROR: Could not set and start the timer. GetLastError = %d\n", GetLastError());
RtDeleteTimer( hTimer);
return ERROR_OCCURED;
}
//
// Wait for the sampling time and then stop the timer.
//
SleepEx( sampleTime, FALSE);
if(!RtDeleteTimer( hTimer))
{
printf("Srtm: Error: Could not delete timer. GetLastError = %d\n", GetLastError());
return ERROR_OCCURED;
}
//
// Calculate and print total ticks, minimum and average values.
//
min = tot = 0;
avg.QuadPart = 0;
for (i = 0; i < NBUCKETS; i++)
{
if (Buckets[i])
{
tot += Buckets[i];
avg.QuadPart += ((_int64)i)*Buckets[i] + Buckets[i]/2;
if (min==0)
min = i + 1;
}
}
if (tot!=0)
avg.QuadPart = ((BucketSize/10)*avg.QuadPart)/tot;
printf(" Sample Period = %d s, Total Ticks = %d.\n", sampleTime/1000, tot);
printf("\nSummary:\n");
printf(" Minimum = %d us, Average = %d us, Maximum = %d us\n",
(min-1)*(BucketSize/10), avg.LowPart, (MaxLatency+5)/10);
//
// If requested, display the histogram.
// Collapse empty buckets to reduce print out space.
//
if (Hist)
{
if (BucketSize == usSize)
msg = "us";
printf("\nHistogram:\n");
for (i = 0, k = 0; i < NBUCKETS; i++)
{
if (Buckets[i])