829 lines
23 KiB
C++
829 lines
23 KiB
C++
/*++
|
||
|
||
Copyright © 2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
dsperf.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the implementation of the DShow performance counter
|
||
provider. It acts as a WMI event tracing controller and consumer and exposes
|
||
standard NT performance counters.
|
||
|
||
Author:
|
||
|
||
Arthur Zwiegincew (arthurz) 05-Oct-00
|
||
|
||
Revision History:
|
||
|
||
05-Oct-00 - Created
|
||
|
||
--*/
|
||
|
||
#include <windows.h>
|
||
#include <winperf.h>
|
||
#include <stdio.h>
|
||
#include <wmistr.h>
|
||
#include <evntrace.h>
|
||
#include <initguid.h>
|
||
#include <math.h>
|
||
#include "dscounters.h"
|
||
|
||
#define WMIPERF
|
||
#include <perfstruct.h>
|
||
|
||
typedef LONG LOGICAL;
|
||
|
||
GUID KmixerGuid =
|
||
{ 0xe5a43a19, 0x6de0, 0x44f8, 0xb0, 0xd7, 0x77, 0x2d, 0xbd, 0xe4, 0x6c, 0xc0 };
|
||
|
||
GUID PortclsGuid =
|
||
{ 0x9d447297, 0xc576, 0x4015, 0x87, 0xb5, 0xa5, 0xa6, 0x98, 0xfd, 0x4d, 0xd1 };
|
||
|
||
GUID UsbaudioGuid =
|
||
{ 0xd6464a84, 0xa358, 0x4013, 0xa1, 0xe8, 0x6e, 0x2f, 0xb4, 0x8a, 0xab, 0x93 };
|
||
|
||
//
|
||
// Video glitch threshold. When abs(PresentationTime - RenderTime) > this value,
|
||
// we consider the event a glitch.
|
||
//
|
||
|
||
#define GLITCH_THRESHOLD 30 /* ms */ * 10000 /* 1 ms / 100 ns */
|
||
|
||
//
|
||
// Performance data.
|
||
//
|
||
|
||
ULONG VideoGlitches;
|
||
ULONG VideoGlitchesPerSec;
|
||
ULONG VideoFrameRate;
|
||
ULONG VideoJitter;
|
||
ULONG DsoundGlitches;
|
||
ULONG KmixerGlitches;
|
||
ULONG PortclsGlitches;
|
||
ULONG UsbaudioGlitches;
|
||
|
||
ULONG VideoGlitchesSinceLastMeasurement;
|
||
ULONG FramesRendered;
|
||
ULONGLONG LastMeasurementTime;
|
||
|
||
#define JITTER_HISTORY_BUFFER_SIZE 200
|
||
LONGLONG FrameJitterHistory[JITTER_HISTORY_BUFFER_SIZE];
|
||
ULONG NextJitterEntry;
|
||
|
||
//
|
||
// DSHOW WMI provider GUID.
|
||
//
|
||
|
||
GUID ControlGuid = GUID_DSHOW_CTL;
|
||
|
||
//
|
||
// Event tracing-related globals.
|
||
//
|
||
|
||
HANDLE MonitorThread;
|
||
HANDLE ResetThread;
|
||
TRACEHANDLE LoggerHandle;
|
||
TRACEHANDLE ConsumerHandle;
|
||
EVENT_TRACE_LOGFILE Logfile;
|
||
ULONGLONG LastBufferFlushTime;
|
||
|
||
//
|
||
// Performance monitoring structures.
|
||
//
|
||
|
||
typedef struct _DSHOW_PERF_DATA_DEFINITION {
|
||
|
||
PERF_OBJECT_TYPE ObjectType;
|
||
PERF_COUNTER_DEFINITION VideoGlitches;
|
||
PERF_COUNTER_DEFINITION VideoGlitchesPerSec;
|
||
PERF_COUNTER_DEFINITION VideoFrameRate;
|
||
PERF_COUNTER_DEFINITION VideoJitter;
|
||
PERF_COUNTER_DEFINITION DsoundGlitches;
|
||
PERF_COUNTER_DEFINITION KmixerGlitches;
|
||
PERF_COUNTER_DEFINITION PortclsGlitches;
|
||
PERF_COUNTER_DEFINITION UsbaudioGlitches;
|
||
|
||
} DSHOW_PERF_DATA_DEFINITION, *PDSHOW_PERF_DATA_DEFINITION;
|
||
|
||
typedef struct _DSHOW_PERF_COUNTERS {
|
||
|
||
PERF_COUNTER_BLOCK CounterBlock;
|
||
ULONG Pad;
|
||
ULONG VideoGlitches;
|
||
ULONG VideoGlitchesPerSec;
|
||
ULONG VideoFrameRate;
|
||
ULONG VideoJitter;
|
||
ULONG DsoundGlitches;
|
||
ULONG KmixerGlitches;
|
||
ULONG PortclsGlitches;
|
||
ULONG UsbaudioGlitches;
|
||
//ULONG Pad2;
|
||
//ULONG Pad3;
|
||
|
||
} DSHOW_PERF_COUNTERS, *PDSHOW_PERF_COUNTERS;
|
||
|
||
//
|
||
// Perfmon-related globals.
|
||
//
|
||
|
||
DSHOW_PERF_DATA_DEFINITION DshowPerfDataDefinition;
|
||
LONG OpenCount = 0; // Keeps track of how many threads have open ds counters.
|
||
LOGICAL Initialized = 0; // Fixes an initialization race condition.
|
||
|
||
//
|
||
// Function prototypes.
|
||
//
|
||
|
||
PM_OPEN_PROC PerfOpen;
|
||
PM_CLOSE_PROC PerfClose;
|
||
PM_COLLECT_PROC PerfCollect;
|
||
|
||
DWORD
|
||
WINAPI
|
||
MonitorThreadProc (
|
||
LPVOID Param
|
||
);
|
||
|
||
DWORD
|
||
WINAPI
|
||
ResetThreadProc (
|
||
LPVOID Param
|
||
);
|
||
|
||
VOID
|
||
TerminateLogging (
|
||
VOID
|
||
);
|
||
|
||
////////////////////////////////////////////////////////////////////////////
|
||
|
||
ULONG
|
||
APIENTRY
|
||
PerfOpen (
|
||
LPWSTR DevNames
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes data collection.
|
||
|
||
Arguments:
|
||
|
||
DevNames - Supplies a REG_MULTISZ of object IDs of the devices to be opened.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS - OK.
|
||
|
||
<multiple failure codes> - initialization failed.
|
||
|
||
Note:
|
||
|
||
On remote connections, this function may be called more than once
|
||
by multiple threads.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDSHOW_PERF_DATA_DEFINITION Def = &DshowPerfDataDefinition;
|
||
ULONG FirstCounter;
|
||
ULONG FirstHelp;
|
||
HKEY PerfKey;
|
||
LOGICAL WasInitialized;
|
||
ULONG Type;
|
||
ULONG Size;
|
||
LONG status;
|
||
|
||
//
|
||
// Initialize counters.
|
||
//
|
||
|
||
VideoGlitches = 0;
|
||
VideoGlitchesSinceLastMeasurement = 0;
|
||
VideoFrameRate = 0;
|
||
DsoundGlitches = 0;
|
||
KmixerGlitches = 0;
|
||
PortclsGlitches = 0;
|
||
UsbaudioGlitches = 0;
|
||
FramesRendered = 0;
|
||
NextJitterEntry = 0;
|
||
GetSystemTimeAsFileTime ((FILETIME*)&LastMeasurementTime);
|
||
|
||
//
|
||
// Since WinLogon is multithreaded and will call this routine in order to
|
||
// service remote performance queries, this library must keep track of how
|
||
// many threads have accessed it). The registry routines will limit access
|
||
// to the initialization routine to only one thread at a time.
|
||
//
|
||
|
||
WasInitialized = InterlockedCompareExchange (&Initialized, 0, 0);
|
||
|
||
if (!WasInitialized) {
|
||
|
||
//
|
||
// Create the monitor thread.
|
||
//
|
||
|
||
MonitorThread = CreateThread (NULL, 0, MonitorThreadProc, 0, 0, NULL);
|
||
if (MonitorThread == NULL) {
|
||
return GetLastError();
|
||
}
|
||
|
||
//
|
||
// Create the reset thread.
|
||
//
|
||
|
||
ResetThread = CreateThread (NULL, 0, ResetThreadProc, 0, 0, NULL);
|
||
if (ResetThread == NULL) {
|
||
return GetLastError();
|
||
}
|
||
|
||
//
|
||
// Get counter and help index base values from the registry.
|
||
//
|
||
|
||
status = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
|
||
"SYSTEM\\CurrentControlSet\\Services\\DSPerf\\Performance",
|
||
0L, KEY_READ, &PerfKey);
|
||
|
||
if (status != ERROR_SUCCESS) {
|
||
return status;
|
||
}
|
||
|
||
Size = sizeof (ULONG);
|
||
status = RegQueryValueEx (PerfKey, "First Counter", 0L, &Type,
|
||
(LPBYTE) &FirstCounter, &Size);
|
||
|
||
if (status != ERROR_SUCCESS) {
|
||
RegCloseKey (PerfKey);
|
||
return status;
|
||
}
|
||
|
||
Size = sizeof (ULONG);
|
||
status = RegQueryValueEx (PerfKey, "First Help", 0L, &Type,
|
||
(LPBYTE) &FirstHelp, &Size);
|
||
|
||
if (status != ERROR_SUCCESS) {
|
||
RegCloseKey (PerfKey);
|
||
return status;
|
||
}
|
||
|
||
RegCloseKey (PerfKey);
|
||
|
||
//
|
||
// Fill the data definition structure.
|
||
//
|
||
|
||
Def->ObjectType.TotalByteLength = sizeof (DSHOW_PERF_DATA_DEFINITION) +
|
||
sizeof (DSHOW_PERF_COUNTERS);
|
||
Def->ObjectType.DefinitionLength = sizeof (DSHOW_PERF_DATA_DEFINITION);
|
||
Def->ObjectType.HeaderLength = sizeof (PERF_OBJECT_TYPE);
|
||
Def->ObjectType.ObjectNameTitleIndex = DSHOWPERF_OBJ + FirstCounter;
|
||
Def->ObjectType.ObjectNameTitle = NULL;
|
||
Def->ObjectType.ObjectHelpTitleIndex = DSHOWPERF_OBJ + FirstHelp;
|
||
Def->ObjectType.ObjectHelpTitle = NULL;
|
||
Def->ObjectType.DetailLevel = PERF_DETAIL_NOVICE;
|
||
Def->ObjectType.NumCounters = (sizeof (DSHOW_PERF_DATA_DEFINITION) -
|
||
sizeof (PERF_OBJECT_TYPE)) /
|
||
sizeof (PERF_COUNTER_DEFINITION);
|
||
Def->ObjectType.DefaultCounter = -1;
|
||
Def->ObjectType.NumInstances = PERF_NO_INSTANCES;
|
||
Def->ObjectType.CodePage = 0;
|
||
Def->ObjectType.PerfFreq.QuadPart = 10000000;
|
||
|
||
///
|
||
|
||
Def->VideoGlitches.ByteLength = sizeof (PERF_COUNTER_DEFINITION);
|
||
Def->VideoGlitches.CounterNameTitleIndex = DSHOWPERF_VIDEO_GLITCHES + FirstCounter;
|
||
Def->VideoGlitches.CounterNameTitle = NULL;
|
||
Def->VideoGlitches.CounterHelpTitleIndex = DSHOWPERF_VIDEO_GLITCHES + FirstHelp;
|
||
Def->VideoGlitches.CounterHelpTitle = NULL;
|
||
Def->VideoGlitches.DefaultScale = -1;
|
||
Def->VideoGlitches.DetailLevel = PERF_DETAIL_NOVICE;
|
||
Def->VideoGlitches.CounterType = PERF_COUNTER_RAWCOUNT;
|
||
Def->VideoGlitches.CounterSize = sizeof (ULONG);
|
||
Def->VideoGlitches.CounterOffset = FIELD_OFFSET (DSHOW_PERF_COUNTERS, VideoGlitches);
|
||
|
||
///
|
||
|
||
Def->VideoGlitchesPerSec.ByteLength = sizeof (PERF_COUNTER_DEFINITION);
|
||
Def->VideoGlitchesPerSec.CounterNameTitleIndex = DSHOWPERF_VIDEO_GLITCHES_SEC + FirstCounter;
|
||
Def->VideoGlitchesPerSec.CounterNameTitle = NULL;
|
||
Def->VideoGlitchesPerSec.CounterHelpTitleIndex = DSHOWPERF_VIDEO_GLITCHES_SEC + FirstHelp;
|
||
Def->VideoGlitchesPerSec.CounterHelpTitle = NULL;
|
||
Def->VideoGlitchesPerSec.DefaultScale = 0;
|
||
Def->VideoGlitchesPerSec.DetailLevel = PERF_DETAIL_NOVICE;
|
||
Def->VideoGlitchesPerSec.CounterType = PERF_COUNTER_RAWCOUNT;
|
||
Def->VideoGlitchesPerSec.CounterSize = sizeof (ULONG);
|
||
Def->VideoGlitchesPerSec.CounterOffset = FIELD_OFFSET (DSHOW_PERF_COUNTERS, VideoGlitchesPerSec);
|
||
|
||
///
|
||
|
||
Def->VideoFrameRate.ByteLength = sizeof (PERF_COUNTER_DEFINITION);
|
||
Def->VideoFrameRate.CounterNameTitleIndex = DSHOWPERF_FRAME_RATE + FirstCounter;
|
||
Def->VideoFrameRate.CounterNameTitle = NULL;
|
||
Def->VideoFrameRate.CounterHelpTitleIndex = DSHOWPERF_FRAME_RATE + FirstHelp;
|
||
Def->VideoFrameRate.CounterHelpTitle = NULL;
|
||
Def->VideoFrameRate.DefaultScale = 0;
|
||
Def->VideoFrameRate.DetailLevel = PERF_DETAIL_NOVICE;
|
||
Def->VideoFrameRate.CounterType = PERF_COUNTER_RAWCOUNT;
|
||
Def->VideoFrameRate.CounterSize = sizeof (ULONG);
|
||
Def->VideoFrameRate.CounterOffset = FIELD_OFFSET (DSHOW_PERF_COUNTERS, VideoFrameRate);
|
||
|
||
///
|
||
|
||
Def->VideoJitter.ByteLength = sizeof (PERF_COUNTER_DEFINITION);
|
||
Def->VideoJitter.CounterNameTitleIndex = DSHOWPERF_JITTER + FirstCounter;
|
||
Def->VideoJitter.CounterNameTitle = NULL;
|
||
Def->VideoJitter.CounterHelpTitleIndex = DSHOWPERF_JITTER + FirstHelp;
|
||
Def->VideoJitter.CounterHelpTitle = NULL;
|
||
Def->VideoJitter.DefaultScale = 1;
|
||
Def->VideoJitter.DetailLevel = PERF_DETAIL_NOVICE;
|
||
Def->VideoJitter.CounterType = PERF_COUNTER_RAWCOUNT;
|
||
Def->VideoJitter.CounterSize = sizeof (ULONG);
|
||
Def->VideoJitter.CounterOffset = FIELD_OFFSET (DSHOW_PERF_COUNTERS, VideoJitter);
|
||
|
||
///
|
||
|
||
Def->DsoundGlitches.ByteLength = sizeof (PERF_COUNTER_DEFINITION);
|
||
Def->DsoundGlitches.CounterNameTitleIndex = DSHOWPERF_DSOUND_GLITCHES + FirstCounter;
|
||
Def->DsoundGlitches.CounterNameTitle = NULL;
|
||
Def->DsoundGlitches.CounterHelpTitleIndex = DSHOWPERF_DSOUND_GLITCHES + FirstHelp;
|
||
Def->DsoundGlitches.CounterHelpTitle = NULL;
|
||
Def->DsoundGlitches.DefaultScale = -1;
|
||
Def->DsoundGlitches.DetailLevel = PERF_DETAIL_NOVICE;
|
||
Def->DsoundGlitches.CounterType = PERF_COUNTER_RAWCOUNT;
|
||
Def->DsoundGlitches.CounterSize = sizeof (ULONG);
|
||
Def->DsoundGlitches.CounterOffset = FIELD_OFFSET (DSHOW_PERF_COUNTERS, DsoundGlitches);
|
||
|
||
///
|
||
|
||
Def->KmixerGlitches.ByteLength = sizeof (PERF_COUNTER_DEFINITION);
|
||
Def->KmixerGlitches.CounterNameTitleIndex = DSHOWPERF_KMIXER_GLITCHES + FirstCounter;
|
||
Def->KmixerGlitches.CounterNameTitle = NULL;
|
||
Def->KmixerGlitches.CounterHelpTitleIndex = DSHOWPERF_KMIXER_GLITCHES + FirstHelp;
|
||
Def->KmixerGlitches.CounterHelpTitle = NULL;
|
||
Def->KmixerGlitches.DefaultScale = -1;
|
||
Def->KmixerGlitches.DetailLevel = PERF_DETAIL_NOVICE;
|
||
Def->KmixerGlitches.CounterType = PERF_COUNTER_RAWCOUNT;
|
||
Def->KmixerGlitches.CounterSize = sizeof (ULONG);
|
||
Def->KmixerGlitches.CounterOffset = FIELD_OFFSET (DSHOW_PERF_COUNTERS, KmixerGlitches);
|
||
|
||
///
|
||
|
||
Def->PortclsGlitches.ByteLength = sizeof (PERF_COUNTER_DEFINITION);
|
||
Def->PortclsGlitches.CounterNameTitleIndex = DSHOWPERF_PORTCLS_GLITCHES + FirstCounter;
|
||
Def->PortclsGlitches.CounterNameTitle = NULL;
|
||
Def->PortclsGlitches.CounterHelpTitleIndex = DSHOWPERF_PORTCLS_GLITCHES + FirstHelp;
|
||
Def->PortclsGlitches.CounterHelpTitle = NULL;
|
||
Def->PortclsGlitches.DefaultScale = -1;
|
||
Def->PortclsGlitches.DetailLevel = PERF_DETAIL_NOVICE;
|
||
Def->PortclsGlitches.CounterType = PERF_COUNTER_RAWCOUNT;
|
||
Def->PortclsGlitches.CounterSize = sizeof (ULONG);
|
||
Def->PortclsGlitches.CounterOffset = FIELD_OFFSET (DSHOW_PERF_COUNTERS, PortclsGlitches);
|
||
|
||
///
|
||
|
||
Def->UsbaudioGlitches.ByteLength = sizeof (PERF_COUNTER_DEFINITION);
|
||
Def->UsbaudioGlitches.CounterNameTitleIndex = DSHOWPERF_USBAUDIO_GLITCHES + FirstCounter;
|
||
Def->UsbaudioGlitches.CounterNameTitle = NULL;
|
||
Def->UsbaudioGlitches.CounterHelpTitleIndex = DSHOWPERF_USBAUDIO_GLITCHES + FirstHelp;
|
||
Def->UsbaudioGlitches.CounterHelpTitle = NULL;
|
||
Def->UsbaudioGlitches.DefaultScale = -1;
|
||
Def->UsbaudioGlitches.DetailLevel = PERF_DETAIL_NOVICE;
|
||
Def->UsbaudioGlitches.CounterType = PERF_COUNTER_RAWCOUNT;
|
||
Def->UsbaudioGlitches.CounterSize = sizeof (ULONG);
|
||
Def->UsbaudioGlitches.CounterOffset = FIELD_OFFSET (DSHOW_PERF_COUNTERS, UsbaudioGlitches);
|
||
}
|
||
|
||
InterlockedExchange (&Initialized, 1);
|
||
InterlockedIncrement (&OpenCount);
|
||
|
||
return ERROR_SUCCESS;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////
|
||
|
||
ULONG
|
||
APIENTRY
|
||
PerfClose (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine cleans up after data collection.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS.
|
||
|
||
Note:
|
||
|
||
On remote connections, this function may be called more than once
|
||
by multiple threads.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG NewOpenCount;
|
||
|
||
NewOpenCount = InterlockedDecrement (&OpenCount);
|
||
|
||
if (NewOpenCount == 0) {
|
||
|
||
//
|
||
// Last thread has closed the counter. Perform cleanup here.
|
||
//
|
||
|
||
TerminateLogging();
|
||
}
|
||
|
||
return ERROR_SUCCESS;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////
|
||
|
||
ULONG
|
||
APIENTRY
|
||
PerfCollect (
|
||
IN LPWSTR ValueName,
|
||
IN OUT LPVOID* PData,
|
||
IN OUT LPDWORD TotalBytes,
|
||
OUT LPDWORD NumObjectTypes
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine provides perf mon data.
|
||
|
||
Arguments:
|
||
|
||
ValueName - Supplies a string specified by the performance monitor program
|
||
in a call to the RegQueryValueEx function.
|
||
|
||
PData - Supplies the address of the buffer to receive the completed
|
||
PERF_DATA_BLOCK and subordinate structures. This routine will
|
||
append its data to the buffer starting at the point referenced
|
||
by *PData.
|
||
|
||
Receives the pointer to the first byte after the data structure
|
||
added by this routine.
|
||
|
||
TotalBytes - Supplies the size in bytes of the buffer referenced by PData.
|
||
|
||
Receives the number of bytes added by this routine.
|
||
|
||
NumObjectTypes - Receives the number of objects added by this routine.
|
||
|
||
Return Value:
|
||
|
||
ERROR_MORE_DATA - if the buffer passed is too small to hold data.
|
||
|
||
ERROR_SUCCESS - if success or any other error.
|
||
|
||
Errors are also reported to the event log. AZFIX
|
||
|
||
--*/
|
||
|
||
{
|
||
PDSHOW_PERF_DATA_DEFINITION PerfDataDefinition;
|
||
PDSHOW_PERF_COUNTERS PerfCounters;
|
||
FILETIME FileTime;
|
||
ULONG SpaceNeeded;
|
||
|
||
SpaceNeeded = sizeof (DSHOW_PERF_DATA_DEFINITION) +
|
||
sizeof (DSHOW_PERF_COUNTERS);
|
||
|
||
if (!Initialized) {
|
||
*TotalBytes = 0;
|
||
*NumObjectTypes = 0;
|
||
return ERROR_SUCCESS; // This is OK.
|
||
}
|
||
|
||
if (*TotalBytes < SpaceNeeded) {
|
||
*TotalBytes = 0;
|
||
*NumObjectTypes = 0;
|
||
return ERROR_MORE_DATA;
|
||
}
|
||
|
||
//
|
||
// Copy the object and counter definitions.
|
||
//
|
||
|
||
PerfDataDefinition = (PDSHOW_PERF_DATA_DEFINITION)(*PData);
|
||
|
||
RtlMoveMemory (PerfDataDefinition, &DshowPerfDataDefinition,
|
||
sizeof (DSHOW_PERF_DATA_DEFINITION));
|
||
|
||
GetSystemTimeAsFileTime (&FileTime);
|
||
RtlCopyMemory (&PerfDataDefinition->ObjectType.PerfTime.QuadPart,
|
||
&FileTime, sizeof (LONGLONG));
|
||
|
||
PerfDataDefinition->ObjectType.NumInstances = -1;
|
||
PerfDataDefinition->ObjectType.TotalByteLength = SpaceNeeded;
|
||
|
||
//
|
||
// Fill in counter data.
|
||
//
|
||
|
||
PerfCounters = (PDSHOW_PERF_COUNTERS)(PerfDataDefinition + 1);
|
||
PerfCounters->CounterBlock.ByteLength = sizeof (DSHOW_PERF_COUNTERS);
|
||
PerfCounters->VideoGlitches = VideoGlitches;
|
||
PerfCounters->VideoGlitchesPerSec = VideoGlitchesPerSec;
|
||
PerfCounters->VideoFrameRate = VideoFrameRate;
|
||
PerfCounters->VideoJitter = VideoJitter;
|
||
PerfCounters->DsoundGlitches = DsoundGlitches;
|
||
PerfCounters->KmixerGlitches = KmixerGlitches;
|
||
PerfCounters->PortclsGlitches = PortclsGlitches;
|
||
PerfCounters->UsbaudioGlitches = UsbaudioGlitches;
|
||
|
||
//
|
||
// Update out parameters.
|
||
//
|
||
|
||
*PData = (PVOID)(PerfCounters + 1);
|
||
*TotalBytes = SpaceNeeded;
|
||
*NumObjectTypes = 1;
|
||
|
||
return ERROR_SUCCESS;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////
|
||
|
||
VOID
|
||
TerminateLogging (
|
||
VOID
|
||
)
|
||
{
|
||
ULONG status;
|
||
int i;
|
||
struct EVENT_TRACE_INFO {
|
||
EVENT_TRACE_PROPERTIES TraceProperties;
|
||
char Logger[256];
|
||
char LogFileName[256];
|
||
} Info;
|
||
|
||
ZeroMemory (&Info, sizeof (Info));
|
||
Info.TraceProperties.LoggerNameOffset = sizeof (EVENT_TRACE_PROPERTIES);
|
||
Info.TraceProperties.LogFileNameOffset = sizeof (EVENT_TRACE_PROPERTIES) + 256;
|
||
Info.TraceProperties.Wnode.BufferSize = sizeof (Info);
|
||
|
||
ControlTrace (0, "DSPerf", &Info.TraceProperties, EVENT_TRACE_CONTROL_QUERY);
|
||
ControlTraceA (LoggerHandle, NULL, &Info.TraceProperties, EVENT_TRACE_CONTROL_STOP);
|
||
|
||
if (MonitorThread != NULL) {
|
||
EnableTrace (FALSE, 0, 0, &ControlGuid, LoggerHandle);
|
||
TerminateThread (MonitorThread, 0);
|
||
}
|
||
|
||
if (ResetThread != NULL) {
|
||
TerminateThread (ResetThread, 0);
|
||
}
|
||
|
||
for (i = 0; i < 100; i += 1) {
|
||
Sleep (50);
|
||
status = CloseTrace (ConsumerHandle);
|
||
if (status == ERROR_SUCCESS) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
EnableTrace (FALSE, 0, 0, &ControlGuid, LoggerHandle);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////
|
||
|
||
VOID
|
||
WINAPI
|
||
EventCallback (
|
||
IN PEVENT_TRACE Event
|
||
)
|
||
{
|
||
PPERFINFO_DSHOW_AVREND PerfInfoAvRend;
|
||
PPERFINFO_DSHOW_AUDIOGLITCH PerfInfoAudioGlitch;
|
||
ULONG i;
|
||
LONGLONG PresentationDelta;
|
||
|
||
if (IsEqualGUID (Event->Header.Guid, GUID_VIDEOREND)) {
|
||
|
||
FramesRendered += 1;
|
||
|
||
PerfInfoAvRend = (PPERFINFO_DSHOW_AVREND)Event->MofData;
|
||
PresentationDelta = PerfInfoAvRend->dshowClock - PerfInfoAvRend->sampleTime;
|
||
FrameJitterHistory[NextJitterEntry] = PresentationDelta;
|
||
if (NextJitterEntry < JITTER_HISTORY_BUFFER_SIZE - 1) {
|
||
NextJitterEntry += 1;
|
||
}
|
||
|
||
if (labs ((int)PresentationDelta) > GLITCH_THRESHOLD) {
|
||
|
||
//
|
||
// Video glitch.
|
||
//
|
||
|
||
VideoGlitches += 1;
|
||
VideoGlitchesSinceLastMeasurement += 1;
|
||
}
|
||
}
|
||
else if (IsEqualGUID (Event->Header.Guid, GUID_DSOUNDGLITCH)) {
|
||
|
||
PerfInfoAudioGlitch = (PPERFINFO_DSHOW_AUDIOGLITCH)Event->MofData;
|
||
|
||
if (PerfInfoAudioGlitch->glitchType == 1) {
|
||
DsoundGlitches += 1;
|
||
}
|
||
}
|
||
else if (IsEqualGUID (Event->Header.Guid, KmixerGuid)) {
|
||
PerfInfoAudioGlitch = (PPERFINFO_DSHOW_AUDIOGLITCH)Event->MofData;
|
||
KmixerGlitches += 1;
|
||
}
|
||
else if (IsEqualGUID (Event->Header.Guid, PortclsGuid)) {
|
||
PerfInfoAudioGlitch = (PPERFINFO_DSHOW_AUDIOGLITCH)Event->MofData;
|
||
PortclsGlitches += 1;
|
||
}
|
||
else if (IsEqualGUID (Event->Header.Guid, UsbaudioGuid)) {
|
||
PerfInfoAudioGlitch = (PPERFINFO_DSHOW_AUDIOGLITCH)Event->MofData;
|
||
UsbaudioGlitches += 1;
|
||
}
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////
|
||
|
||
ULONG
|
||
WINAPI
|
||
BufferCallback (
|
||
PEVENT_TRACE_LOGFILE Log
|
||
)
|
||
{
|
||
ULONGLONG CurrentTime;
|
||
double TimeDelta;
|
||
double JitterMean;
|
||
double Jitter;
|
||
double x;
|
||
ULONG i;
|
||
|
||
//
|
||
// Extend the reset timer (indirectly).
|
||
//
|
||
|
||
GetSystemTimeAsFileTime ((FILETIME*)&LastBufferFlushTime);
|
||
|
||
//
|
||
// Calculate rates.
|
||
//
|
||
|
||
GetSystemTimeAsFileTime ((FILETIME*)&CurrentTime);
|
||
TimeDelta = (double)(CurrentTime - LastMeasurementTime) / 10000000.0L;
|
||
|
||
if (CurrentTime > LastMeasurementTime) {
|
||
VideoGlitchesPerSec = (ULONG)((double)VideoGlitchesSinceLastMeasurement / TimeDelta);
|
||
VideoFrameRate = (ULONG)((double)FramesRendered / TimeDelta);
|
||
}
|
||
else {
|
||
VideoGlitchesPerSec = 0;
|
||
VideoFrameRate = 0;
|
||
}
|
||
|
||
//
|
||
// Calculate jitter.
|
||
//
|
||
|
||
if (NextJitterEntry > 1) {
|
||
|
||
JitterMean = 0.0L;
|
||
for (i = 0; i < NextJitterEntry; i += 1) {
|
||
JitterMean += (double)FrameJitterHistory[i];
|
||
}
|
||
JitterMean /= (double)NextJitterEntry;
|
||
|
||
Jitter = 0.0L;
|
||
for (i = 0; i < NextJitterEntry; i += 1) {
|
||
x = (double)FrameJitterHistory[i] - JitterMean;
|
||
Jitter += (x * x);
|
||
}
|
||
Jitter /= (double)(NextJitterEntry - 1);
|
||
Jitter = sqrt (Jitter) / 10000.0L;
|
||
VideoJitter = (ULONG)Jitter;
|
||
}
|
||
else {
|
||
VideoJitter = 0;
|
||
}
|
||
|
||
LastMeasurementTime = CurrentTime;
|
||
VideoGlitchesSinceLastMeasurement = 0;
|
||
FramesRendered = 0;
|
||
NextJitterEntry = 0;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////
|
||
|
||
DWORD
|
||
WINAPI
|
||
MonitorThreadProc (
|
||
LPVOID Param
|
||
)
|
||
{
|
||
struct EVENT_TRACE_INFO {
|
||
EVENT_TRACE_PROPERTIES TraceProperties;
|
||
char Logger[256];
|
||
} Info;
|
||
|
||
UNREFERENCED_PARAMETER (Param);
|
||
|
||
ULONG status;
|
||
|
||
//
|
||
// Start the controller.
|
||
//
|
||
|
||
ZeroMemory (&Info, sizeof (Info));
|
||
Info.TraceProperties.Wnode.BufferSize = sizeof (Info);
|
||
Info.TraceProperties.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
|
||
Info.TraceProperties.Wnode.Guid = ControlGuid;
|
||
Info.TraceProperties.BufferSize = 4096;
|
||
Info.TraceProperties.MinimumBuffers = 8;
|
||
Info.TraceProperties.MaximumBuffers = 16;
|
||
Info.TraceProperties.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
|
||
Info.TraceProperties.FlushTimer = 1;
|
||
Info.TraceProperties.EnableFlags = 1;
|
||
Info.TraceProperties.AgeLimit = 1;
|
||
Info.TraceProperties.LoggerNameOffset = sizeof (Info.TraceProperties);
|
||
strcpy (Info.Logger, "DSPerf");
|
||
|
||
status = StartTrace (&LoggerHandle, "DSPerf", &Info.TraceProperties);
|
||
if (status != ERROR_SUCCESS) {
|
||
return 0;
|
||
}
|
||
|
||
status = EnableTrace (TRUE, 0x1f, 0, &ControlGuid, LoggerHandle);
|
||
if (status != ERROR_SUCCESS) {
|
||
return 0;
|
||
}
|
||
|
||
//
|
||
// Start the consumer.
|
||
//
|
||
|
||
ZeroMemory (&Logfile, sizeof (Logfile));
|
||
Logfile.LoggerName = "DSPerf";
|
||
Logfile.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
|
||
Logfile.BufferCallback = BufferCallback;
|
||
Logfile.EventCallback = EventCallback;
|
||
|
||
ConsumerHandle = OpenTrace (&Logfile);
|
||
if (ConsumerHandle == (TRACEHANDLE)INVALID_HANDLE_VALUE) {
|
||
return 0;
|
||
}
|
||
|
||
//
|
||
// Consume. This call returns when logging is stopped.
|
||
//
|
||
|
||
status = ProcessTrace (&ConsumerHandle, 1, NULL, NULL);
|
||
if (status != ERROR_SUCCESS) {
|
||
return 1;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////
|
||
|
||
DWORD
|
||
WINAPI
|
||
ResetThreadProc (
|
||
LPVOID Param
|
||
)
|
||
{
|
||
ULONGLONG Time;
|
||
|
||
for (;;) {
|
||
Sleep (500);
|
||
GetSystemTimeAsFileTime ((FILETIME*)&Time);
|
||
if (Time - LastBufferFlushTime > 15000000I64) {
|
||
FramesRendered = 0;
|
||
NextJitterEntry = 0;
|
||
VideoGlitchesPerSec = 0;
|
||
VideoFrameRate = 0;
|
||
VideoJitter = 0;
|
||
}
|
||
}
|
||
}
|
||
|