windows-nt/Source/XPSP1/NT/multimedia/dshow/tools/measure/measure.cpp
2020-09-26 16:20:57 +08:00

1054 lines
31 KiB
C++

// Copyright (c) 1994 - 1996 Microsoft Corporation. All Rights Reserved.
#pragma warning(disable: 4201 4514)
/*
It is very easy to fill large amounts of storage with data
(e.g. 5 filters in a graph,
5 interesting start/stop times per sample each = 10 events
30 samples per second
16 bytes per sample (8 bytes time, 8 bytes to identify incident)
= 24KB/sec)
this means that even a quite large buffer (I have in mind 64KB) will
overflow after a few seconds. Writing it out to a file is completely out
(don't want to take page faults in the middle of things - even writing to
memory carries some risk).
I want to have two kinds of data available at the end of the run:
1. A record of what actually happened (i.e. what the sequence of events
actually was for at least a few frames)
2. Statistical information (e.g. for inter-frame video times I would like
to see number, average, standard deviation, greatest, smallest.
The volume of information that the actual sequence of events generates means
that it may only hold a second or two of information. The statistical
information should take in the whole run. This means that information will
be logged two ways.
For the detailed record I log
<incident, type, time>
in a circular buffer and regularly over-write the oldest information.
For the statistical information I record the informatin in an array, where
the incident identifier is the index. the array elements hold
<Number of readings, sum, sum of squares, largest, smallest, latest>
The readings are differences (start..next or start..stop). This means that
the actual number of Noted events will be one more than the number of
"readings", whereas for Start-Stop events they will be equal. To fix this,
the number of readings is articifially initialised to -1. If Start sees
this number it resets it to 0.
Times will be in tens of microseconds (this allows up to about 1 3/4 hrs)
The statistics array will have room for up to 128 types of incident (this is
4K - i.e. one page. I hope this will ensure that it never gets
paged out and so causes negligible overhead.
*/
#include <Windows.h> // BOOL etc
#include <limits.h> // for INTT_MAX
#include <math.h> // for sqrt
#include <stdio.h> // for sprintf
#include "Measure.h"
#include "Perf.h" // ultra fast QueryPerformanceCounter for pentium
// forwards
enum {START, STOP, NOTE, RESET, INTGR, PAUSE, RUN}; // values for Type field
typedef struct {
LONGLONG Time; // microsec since class construction
int Id;
int Type;
int n; // the integer for Msr_Integer
} LogDatum;
typedef struct {
LONGLONG Latest; // microsec since class construction
LONGLONG SumSq; // sum of squares of entries for this incident
int Largest; // tenmicrosec
int Smallest; // tenmicrosec
int Sum; // sum of entries for this incident
int Number; // number of entries for this incident
// for Start/Stop it counts the stops
// for Note it counts the intervals (Number of Notes-1)
int iType; // STOP, NOTE, INTGR
} Stat;
#define MAXLOG 4096
static BOOL bInitOk; // Set to true once initialised
static LogDatum Log[MAXLOG]; // 64K circular buffer
static int NextLog; // Next slot to overwrite in the log buffer.
static BOOL bFull; // TRUE => buffer has wrapped at least once.
static BOOL bPaused; // TRUE => do not record. No log, no stats.
#define MAXSTAT 128
static Stat StatBuffer[MAXSTAT];
static int NextStat; // next free slot in StatBuffer.
static LPTSTR Incidents[MAXSTAT];// Names of incidents
static LONGLONG QPFreq;
static LONGLONG QPStart; // base time in perf counts
#ifdef DEBUG
static LONGLONG tLast; // last time - looks for going backwards
#endif
static CRITICAL_SECTION CSMeasure; // Controls access to list
// set it to 100000 for 10 microsecs
// if you fiddle with it then you have to rewrite Format.
#define UNIT 100000
// Times are printed as 9 digits - this means that we can go
// up to 9,999.999,99 secs or about 2 and 3/4 hours.
// ASSERT(condition, msg) e.g. ASSERT(x>1, "Too many xs");
#define ASSERT(_cond_, _msg_) \
if (!(_cond_)) Assert(_msg_, __FILE__, __LINE__)
// print out debug message box
void Assert( const CHAR *pText
, const CHAR *pFile
, INT iLine
)
{
CHAR Buffer[200];
sprintf(Buffer, "%s\nAt line %d file %s"
, pText, iLine, pFile);
INT MsgId = MessageBox( NULL, Buffer, TEXT("ASSERT Failed")
, MB_SYSTEMMODAL |MB_ICONHAND |MB_ABORTRETRYIGNORE);
switch (MsgId)
{
case IDABORT: /* Kill the application */
FatalAppExit(FALSE, TEXT("Application terminated"));
break;
case IDRETRY: /* Break into the debugger */
DebugBreak();
break;
case IDIGNORE: /* Ignore assertion continue executing */
break;
}
} // Assert
//=============================================================================
//
// Init
//
// Call this first.
//=============================================================================
void WINAPI Msr_Init()
{
// I would like this to be idempotent - that is, harmless if it
// gets called more than once. However that's not 100% possible
// At least we should be OK so long as it's not re-entered.
if (!bInitOk) {
bInitOk = TRUE;
InitializeCriticalSection(&CSMeasure);
NextLog = 0;
bFull = FALSE;
NextStat = 0;
LARGE_INTEGER li;
QUERY_PERFORMANCE_FREQUENCY(&li);
QPFreq = li.QuadPart;
QUERY_PERFORMANCE_COUNTER(&li);
QPStart = li.QuadPart;
#ifdef DEBUG
tLast = 0L;
#endif
Msr_Register("Scratch pad");
}
} // Msr_Init "constructor"
//=============================================================================
//
// ResetAll
//
// Do a Reset on every Incident that has been registered.
//=============================================================================
void WINAPI ResetAll()
{
EnterCriticalSection(&CSMeasure);
int i;
for (i = 0; i<NextStat; ++i) {
Msr_Reset(i);
}
LeaveCriticalSection(&CSMeasure);
} // ResetAll
//=============================================================================
//
// Pause
//
// Pause it all
//=============================================================================
void Pause()
{
if (!bInitOk) Msr_Init();
EnterCriticalSection(&CSMeasure);
bPaused = TRUE;
// log a PAUSE event for this id.
LARGE_INTEGER Time;
QUERY_PERFORMANCE_COUNTER(&Time);
// Get time in 10muSec from start - this gets the number small
// it's OK for nearly 6 hrs then an int overflows
LONGLONG Tim = (Time.QuadPart-QPStart) * UNIT / QPFreq;
Log[NextLog].Time = Tim;
Log[NextLog].Type = PAUSE;
Log[NextLog].Id = -1;
++NextLog;
if (NextLog==MAXLOG) {
NextLog = 0;
bFull = TRUE;
}
LeaveCriticalSection(&CSMeasure);
} // Pause
//=============================================================================
//
// Run
//
// Set it all running again
//=============================================================================
void Run()
{
if (!bInitOk) Msr_Init();
EnterCriticalSection(&CSMeasure);
bPaused = FALSE;
// log a RUN event for this id.
LARGE_INTEGER Time;
QUERY_PERFORMANCE_COUNTER(&Time);
// Get time in 10muSec from start - this gets the number small
// it's OK for nearly 6 hrs then an int overflows
LONGLONG Tim = (Time.QuadPart-QPStart) * UNIT / QPFreq;
Log[NextLog].Time = Tim;
Log[NextLog].Type = RUN;
Log[NextLog].Id = -1;
++NextLog;
if (NextLog==MAXLOG) {
NextLog = 0;
bFull = TRUE;
}
LeaveCriticalSection(&CSMeasure);
} // Run
//=============================================================================
//
// Msr_Control
//
// Do ResetAll, set bPaused to FALSE or TRUE according to iAction
//=============================================================================
void WINAPI Msr_Control(int iAction)
{
switch (iAction) {
case MSR_RESET_ALL:
ResetAll();
break;
case MSR_RUN:
Run();
break;
case MSR_PAUSE:
Pause();
break;
}
} // Msr_Control
//=============================================================================
//
// Terminate
//
// Call this last. It frees storage for names of incidents.
//=============================================================================
void WINAPI Msr_Terminate()
{
int i;
if (bInitOk) {
EnterCriticalSection(&CSMeasure);
for (i = 0; i<NextStat; ++i) {
free(Incidents[i]);
}
bInitOk = FALSE;
LeaveCriticalSection(&CSMeasure);
DeleteCriticalSection(&CSMeasure);
}
} // Msr_Terminate "~Measure"
//=============================================================================
//
// InitIncident
//
// Reset the statistical counters for this incident.
//=============================================================================
void InitIncident(int Id)
{
StatBuffer[Id].Latest = -1; // recogniseably odd (see STOP)
StatBuffer[Id].Largest = 0;
StatBuffer[Id].Smallest = INT_MAX;
StatBuffer[Id].Sum = 0;
StatBuffer[Id].SumSq = 0;
StatBuffer[Id].Number = -1;
StatBuffer[Id].iType = NOTE; // reset on first Start for Start/Stop
// reset on first Integer for INTGR
} // InitIncident
//=============================================================================
//
// Register
//
// Register a new kind of incident. The id that is returned can then be used
// on calls to Start, Stop and Note to record the occurrences of these incidents
// so that statistical performance information can be dumped later.
//=============================================================================
int Msr_Register(LPTSTR Incident)
{
if (!bInitOk) {
Msr_Init();
}
// it's now safe to enter the critical section as it will be there!
EnterCriticalSection(&CSMeasure);
int i;
for (i = 0; i<NextStat; ++i) {
if (0==strcmp(Incidents[i],Incident) ) {
// Attempting to re-register the same name.
// Possible actions
// 1. ASSERT - that just causes trouble.
// 2. Register it as a new incident. That produced quartz bug 1
// 3. Hand the old number back and reset it.
// Msr_Reset(i); - possible, but not today.
// 4. Hand the old number back and just keep going.
LeaveCriticalSection(&CSMeasure);
return i;
}
}
if (NextStat==MAXSTAT-1) {
Assert("Too many types of incident\n(ignore is safe)", __FILE__, __LINE__);
LeaveCriticalSection(&CSMeasure);
return -1;
}
Incidents[NextStat] = (LPTSTR)malloc(strlen(Incident)+1);
strcpy(Incidents[NextStat], Incident);
InitIncident(NextStat);
LeaveCriticalSection(&CSMeasure);
return NextStat++;
} // Msr_Register
//=============================================================================
//
// Reset
//
// Reset the statistical counters for this incident.
// Log that we did it.
//=============================================================================
void WINAPI Msr_Reset(int Id)
{
if (!bInitOk) {
Msr_Init();
}
// it's now safe to enter the critical section as it will be there!
EnterCriticalSection(&CSMeasure);
// log a RESET event for this id.
LARGE_INTEGER Time;
QUERY_PERFORMANCE_COUNTER(&Time);
// Get time in 10muSec from start - this gets the number small
// it's OK for nearly 6 hrs then an int overflows
LONGLONG Tim = (Time.QuadPart-QPStart) * UNIT / QPFreq;
Log[NextLog].Time = Tim;
Log[NextLog].Type = RESET;
Log[NextLog].Id = Id;
++NextLog;
if (NextLog==MAXLOG) {
NextLog = 0;
bFull = TRUE;
}
InitIncident(Id);
LeaveCriticalSection(&CSMeasure);
} // Msr_Reset
//=============================================================================
//
// Msr_Start
//
// Record the start time of the event with registered id Id.
// Add it to the circular Log and record the time in StatBuffer.
// Do not update the statistical information, that happens when Stop is called.
//=============================================================================
void WINAPI Msr_Start(int Id)
{
if (bPaused) return;
// This is performance critical. Keep all array subscripting
// together and with any luck the compiler will only calculate the
// offset once. Avoid subroutine calls unless they are definitely inline.
// An id of -1 is the standard rotten registration one.
// We already did an assert for that - so let it go.
if (Id<-1 || Id>=NextStat) {
// ASSERT(!"Performance logging with bad Id");
return;
}
EnterCriticalSection(&CSMeasure);
LARGE_INTEGER Time;
QUERY_PERFORMANCE_COUNTER(&Time);
LONGLONG Tim = (Time.QuadPart-QPStart) * UNIT / QPFreq;
#ifdef DEBUG
ASSERT(Tim>=tLast, "Time is going backwards!!"); tLast = Tim;
#endif
Log[NextLog].Time = Tim;
Log[NextLog].Type = START;
Log[NextLog].Id = Id;
++NextLog;
if (NextLog==MAXLOG) {
NextLog = 0;
bFull = TRUE;
}
StatBuffer[Id].Latest = Tim;
if (StatBuffer[Id].Number == -1) {
StatBuffer[Id].Number = 0;
StatBuffer[Id].iType = STOP;
}
LeaveCriticalSection(&CSMeasure);
} // Msr_Start
//=============================================================================
//
// Msr_Stop
//
// Record the stop time of the event with registered id Id.
// Add it to the circular Log and
// add (StopTime-StartTime) to the statistical record StatBuffer.
//=============================================================================
void WINAPI Msr_Stop(int Id)
{
if (bPaused) return;
// This is performance critical. Keep all array subscripting
// together and with any luck the compiler will only calculate the
// offset once. Avoid subroutine calls unless they are definitely inline.
EnterCriticalSection(&CSMeasure);
LARGE_INTEGER Time;
QUERY_PERFORMANCE_COUNTER(&Time);
// An id of -1 is the standard rotten registration one.
// We already did an assert for that - so let it go.
if (Id<-1 || Id>=NextStat) {
// ASSERT(!"Performance logging with bad Id");
return;
}
// Get time in 10muSec from start - this gets the number small
// it's OK for nearly 6 hrs, then an int overflows
LONGLONG Tim = (Time.QuadPart-QPStart) * UNIT / QPFreq;
#ifdef DEBUG
ASSERT(Tim>=tLast, "Time is going backwards!!"); tLast = Tim;
#endif
Log[NextLog].Time = Tim;
Log[NextLog].Type = STOP;
Log[NextLog].Id = Id;
++NextLog;
if (NextLog==MAXLOG) {
NextLog = 0;
bFull = TRUE;
}
if (StatBuffer[Id].Latest!=-1) {
int t = (int)(Tim - StatBuffer[Id].Latest); // convert to delta
// this is now OK for almost 6hrs since the last Start of this quantity.
if (t > StatBuffer[Id].Largest) StatBuffer[Id].Largest = t;
if (t < StatBuffer[Id].Smallest) StatBuffer[Id].Smallest = t;
StatBuffer[Id].Sum += t;
LONGLONG lt = t;
StatBuffer[Id].SumSq += lt*lt;
++StatBuffer[Id].Number;
}
LeaveCriticalSection(&CSMeasure);
} // Msr_Stop
//=============================================================================
//
// Msr_Note
//
// Record the event with registered id Id. Add it to the circular Log and
// add (ThisTime-PreviousTime) to the statistical record StatBuffer
//=============================================================================
void WINAPI Msr_Note(int Id)
{
if (bPaused) return;
// This is performance critical. Keep all array subscripting
// together and with any luck the compiler will only calculate the
// offset once. Avoid subroutine calls unless they are definitely inline.
// An id of -1 is the standard rotten registration one.
// We already did an assert for that - so let it go.
if (Id<-1 || Id>=NextStat) {
// ASSERT(!"Performance logging with bad Id");
return;
}
EnterCriticalSection(&CSMeasure);
LARGE_INTEGER Time;
QUERY_PERFORMANCE_COUNTER(&Time);
// Get time in 10muSec from start - this gets the number small
// it's OK for nearly 6 hrs then an int overflows
LONGLONG Tim = (Time.QuadPart-QPStart) * UNIT / QPFreq;
#ifdef DEBUG
ASSERT(Tim>=tLast, "Time is going backwards!!"); tLast = Tim;
#endif
Log[NextLog].Time = Tim;
Log[NextLog].Type = NOTE;
Log[NextLog].Id = Id;
++NextLog;
if (NextLog==MAXLOG) {
NextLog = 0;
bFull = TRUE;
}
int t = (int)(Tim - StatBuffer[Id].Latest); // convert to delta
// this is now OK for nearly 6 hrs since the last Note of this quantity.
StatBuffer[Id].Latest = Tim;
++StatBuffer[Id].Number;
if (StatBuffer[Id].Number>0) {
if (t > StatBuffer[Id].Largest) StatBuffer[Id].Largest = t;
if (t < StatBuffer[Id].Smallest) StatBuffer[Id].Smallest = t;
StatBuffer[Id].Sum += (int)t;
LONGLONG lt = t;
StatBuffer[Id].SumSq += lt*lt;
}
LeaveCriticalSection(&CSMeasure);
} // Msr_Note
//=============================================================================
//
// Msr_Integer
//
// Record the event with registered id Id. Add it to the circular Log and
// add (ThisTime-PreviousTime) to the statistical record StatBuffer
//=============================================================================
void WINAPI Msr_Integer(int Id, int n)
{
if (bPaused) return;
// This is performance critical. Keep all array subscripting
// together and with any luck the compiler will only calculate the
// offset once. Avoid subroutine calls unless they are definitely inline.
// An id of -1 is the standard rotten registration one.
// We already did an assert for that - so let it go.
if (Id<-1 || Id>=NextStat) {
// ASSERT(!"Performance logging with bad Id");
return;
}
EnterCriticalSection(&CSMeasure);
LARGE_INTEGER Time;
QUERY_PERFORMANCE_COUNTER(&Time);
// Get time in 10muSec from start - this gets the number small
// it's OK for nearly 6 hrs then an int overflows
LONGLONG Tim = (Time.QuadPart-QPStart) * UNIT / QPFreq;
#ifdef DEBUG
ASSERT(Tim>=tLast, "Time is going backwards!!"); tLast = Tim;
#endif
Log[NextLog].Time = Tim;
Log[NextLog].Type = INTGR;
Log[NextLog].Id = Id;
Log[NextLog].n = n;
++NextLog;
if (NextLog==MAXLOG) {
NextLog = 0;
bFull = TRUE;
}
// StatBuffer[Id].Latest = garbage for Intgr
if (StatBuffer[Id].Number == -1) {
StatBuffer[Id].Number = 0;
StatBuffer[Id].iType = INTGR;
}
++StatBuffer[Id].Number;
if (n > StatBuffer[Id].Largest) StatBuffer[Id].Largest = n;
if (n < StatBuffer[Id].Smallest) StatBuffer[Id].Smallest = n;
StatBuffer[Id].Sum += (int)n;
LONGLONG ln = n;
StatBuffer[Id].SumSq += ln*ln;
LeaveCriticalSection(&CSMeasure);
} // Msr_Integer
//=============================================================================
//
// TypeName
//
// Convert the type code into readable format
//=============================================================================
const LPTSTR TypeName(int Type)
{
switch(Type){
case START: return "START";
case STOP: return "STOP ";
case NOTE: return "NOTE ";
case RESET: return "RESET";
case INTGR: return "INTGR";
case PAUSE: return "PAUSE";
case RUN: return "RUN ";
default: return "DUNNO";
}
} // TypeName
//==============================================================================
//
// Format
//
// I haven't found any way to get sprintf to format integers as
// 1,234.567.89 - so this does it. (that's 12 spaces)
// All times are in tens of microsecs - so they are formatted as
// n,nnn.mmm,mm - this uses 12 spaces.
// The result that it returns points to Buff - it doesn't allocate any storage
// i must be positive. Negative numbers are not handled (the pain of the floating
// minus sign is the reason - i.e. " -12,345" not "- 12,345"
//==============================================================================
LPTSTR Format( LPTSTR Buff, int i)
{
if (i<0) {
sprintf(Buff, " -. ");
return Buff;
}
BOOL bStarted; // TRUE means that some left part of the number has been
// formatted and so we must continue with zeros not spaces
if (i>999999999) {
sprintf(Buff, " ***large***");
return Buff;
}
if (i>99999999) {
sprintf(Buff, "%1d,", i/100000000);
i = i%100000000;
bStarted = TRUE;
} else {
sprintf(Buff, " ");
bStarted = FALSE;
}
if (bStarted) {
sprintf(Buff, "%s%03d.", Buff, i/100000);
i = i%100000;
} else {
sprintf(Buff, "%s%3d.", Buff,i/100000);
i = i%100000;
}
sprintf(Buff, "%s%03d,%02d", Buff, i/100, i%100);
return Buff;
} // Format
//=============================================================================
//
// WriteOut
//
// If hFile==NULL then write str to debug output, else write it to file hFile
//
//=============================================================================
void WriteOut(HANDLE hFile, LPSTR str)
{
if (hFile==NULL) {
OutputDebugString(str);
} else {
DWORD dw;
WriteFile(hFile, str, lstrlen(str), &dw, NULL);
}
} // WriteOut
typedef LONGLONG longlongarray[MAXSTAT];
//=============================================================================
//
// WriteLogEntry
//
// If hFile==NULL then write to debug output, else write to file hFile
// write the ith entry of Log in a readable format
//
//=============================================================================
void WriteLogEntry(HANDLE hFile, int i, longlongarray &Prev)
{
// We have the problem of printing LONGLONGs and wsprintf (26/6/95)
// doesn't like them - found out the hard way - Laurie.
char Buffer[200];
char s1[20];
char s2[20];
int Delta; // time since previous interesting incident
switch(Log[i].Type) {
case START:
Prev[Log[i].Id] = Log[i].Time;
Delta = -2;
sprintf( Buffer, "%s %5s %s : %s\r\n"
, Format(s1,(int)(Log[i].Time))
, TypeName(Log[i].Type)
, Format(s2, Delta)
, Incidents[Log[i].Id]
);
break;
case STOP:
if (Prev[Log[i].Id]==-1) {
Delta = -2;
} else {
Delta = (int)(Log[i].Time - Prev[Log[i].Id]);
}
Prev[Log[i].Id] = -1;
sprintf( Buffer, "%s %5s %s : %s\r\n"
, Format(s1,(int)(Log[i].Time))
, TypeName(Log[i].Type)
, Format(s2, Delta)
, Incidents[Log[i].Id]
);
break;
case NOTE:
if (Prev[Log[i].Id]==-1) {
Delta = -2;
} else {
Delta = (int)(Log[i].Time - Prev[Log[i].Id]);
}
Prev[Log[i].Id] = Log[i].Time;
sprintf( Buffer, "%s %5s %s : %s\r\n"
, Format(s1,(int)(Log[i].Time))
, TypeName(Log[i].Type)
, Format(s2, Delta)
, Incidents[Log[i].Id]
);
break;
case INTGR:
sprintf( Buffer, "%s %5s %12d : %s\r\n"
, Format(s1,(int)(Log[i].Time))
, TypeName(Log[i].Type)
, Log[i].n
, Incidents[Log[i].Id]
);
break;
case RESET: // the delta for a reset will be length of run
case PAUSE:
case RUN:
if ((Log[i].Id==-1)||(Prev[Log[i].Id]==-1)) {
Delta = (int)(Log[i].Time); // = time from start
} else {
Delta = (int)(Log[i].Time - Prev[Log[i].Id]);
}
if (Log[i].Id!=-1) Prev[Log[i].Id] = Log[i].Time;
sprintf( Buffer, "%s %5s %s : %s\r\n"
, Format(s1,(int)(Log[i].Time))
, TypeName(Log[i].Type)
, Format(s2, Delta)
, Incidents[Log[i].Id]
);
break;
}
WriteOut(hFile, Buffer);
} // WriteLogEntry
//=============================================================================
//
// WriteLog
//
// Write the whole of Log out in readable format.
// If hFile==NULL then write to debug output, else write to hFile.
//=============================================================================
void WriteLog(HANDLE hFile)
{
//LONGLONG Prev[MAXSTAT]; // previous values found in log
longlongarray Prev;
char Buffer[100];
sprintf(Buffer, " Time (sec) Type Delta Incident_Name\r\n");
WriteOut(hFile, Buffer);
int i;
// initialise Prev to recognisable odd values
for (i = 0; i<MAXSTAT; ++i) {
Prev[i] = -1;
}
if (bFull) {
for(i = NextLog; i<MAXLOG; ++i) {
WriteLogEntry(hFile, i, Prev);
}
}
for(i = 0; i<NextLog; ++i) {
WriteLogEntry(hFile, i, Prev);
}
} // WriteLog
//=============================================================================
//
// WriteStats
//
// Write the whole of StatBuffer out in readable format.
// If hFile==NULL then write to DbgLog, else write to hFile.
//=============================================================================
void WriteStats(HANDLE hFile)
{
char Buffer[200];
char s1[20];
char s2[20];
char s3[20];
char s4[20];
sprintf( Buffer
, "Number Average StdDev Smallest Largest Incident_Name\r\n"
);
WriteOut(hFile, Buffer);
int i;
for (i = 0; i<NextStat; ++i) {
if (i==0 && StatBuffer[i].Number==0) {
continue; // no temp scribbles to report
}
double SumSq = (double)StatBuffer[i].SumSq;
double Sum = StatBuffer[i].Sum;
if (StatBuffer[i].iType==INTGR) {
double Average;
if (StatBuffer[i].Number<=0) {
Average = 0;
} else {
Average = (double)StatBuffer[i].Sum / (double)StatBuffer[i].Number;
}
double Std;
if (StatBuffer[i].Number<=1) Std = 0.0;
Std = sqrt( ( (double)SumSq
- ( (double)(Sum * Sum)
/ (double)StatBuffer[i].Number
)
)
/ ((double)StatBuffer[i].Number-1.0)
);
sprintf( Buffer
, "%6d %12.3f %12.3f %12d %12d : %s\r\n"
, StatBuffer[i].Number + (StatBuffer[i].iType==NOTE ? 1 : 0)
, Average
, Std
, StatBuffer[i].Smallest
, StatBuffer[i].Largest
, Incidents[i]
);
} else {
double StDev;
int Avg;
int Smallest;
int Largest;
// Calculate Standard Deviation
if (StatBuffer[i].Number<=1) StDev = -2;
else {
StDev = sqrt( ( SumSq
- ( (Sum * Sum)
/ StatBuffer[i].Number
)
)
/ (StatBuffer[i].Number-1)
);
}
// Calculate average
if (StatBuffer[i].Number<=0) {
Avg = -2;
} else {
Avg = StatBuffer[i].Sum / StatBuffer[i].Number;
}
// Calculate smallest and largest
if (StatBuffer[i].Number<=0) {
Smallest = -2;
Largest = -2;
} else {
Smallest = StatBuffer[i].Smallest;
Largest = StatBuffer[i].Largest;
}
sprintf( Buffer
, "%6d %s %s %s %s : %s\r\n"
, StatBuffer[i].Number + (StatBuffer[i].iType==NOTE ? 1 : 0)
, Format(s1, Avg )
, Format(s2, (int)StDev )
, Format(s3, Smallest )
, Format(s4, Largest )
, Incidents[i]
);
}
WriteOut(hFile, Buffer);
}
WriteOut(hFile, "Times such as 0.050,00 are in seconds (that was 1/20 sec) \r\n");
} // WriteStats
#if 0 // test format
void TestFormat(int n)
{
char Buffer[50];
char s1[20];
sprintf(Buffer, ">%s<",Format(s1,n));
DbgLog((LOG_TRACE, 0, Buffer));
} // TestFormat
#endif
//=====================================================================
//
// Dump
//
// Dump out all the results from Log and StatBuffer in readable format.
// If hFile is NULL then it uses DbgLog
// otherwise it prints it to that file
//=====================================================================
void Msr_Dump(HANDLE hFile)
{
EnterCriticalSection(&CSMeasure);
if (!bInitOk) {
Msr_Init(); // of course the log will be empty - never mind!
}
WriteLog(hFile);
WriteStats(hFile);
#if 0 // test Format
TestFormat(1);
TestFormat(12);
TestFormat(123);
TestFormat(1234);
TestFormat(12345);
TestFormat(123456);
TestFormat(1234567);
TestFormat(12345678);
TestFormat(123456789);
TestFormat(1234567890);
#endif
LeaveCriticalSection(&CSMeasure);
} // Msr_Dump
//=====================================================================
//
// DumpStats
//
// Dump out all the results from Log and StatBuffer in readable format.
// If hFile is NULL then it uses DbgLog
// otherwise it prints it to that file
//=====================================================================
void WINAPI Msr_DumpStats(HANDLE hFile)
{
EnterCriticalSection(&CSMeasure);
if (!bInitOk) {
Msr_Init(); // of course the stats will be empty - never mind!
}
WriteStats(hFile);
LeaveCriticalSection(&CSMeasure);
} // Msr_DumpStats
extern "C" BOOL WINAPI DllMain(HINSTANCE, ULONG, LPVOID);
BOOL WINAPI
DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pv)
{
UNREFERENCED_PARAMETER(pv);
switch (ulReason)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hInstance);
InitPerfCounter();
Msr_Init();
break;
case DLL_PROCESS_DETACH:
Msr_Terminate();
break;
}
return TRUE;
}