You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
846 lines
20 KiB
846 lines
20 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
perfsup.c
|
|
|
|
Abstract:
|
|
|
|
This module contains support routines for performance traces.
|
|
|
|
Author:
|
|
|
|
Stephen Hsiao (shsiao) 01-Jan-2000
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "perfp.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, PerfInfoProfileInit)
|
|
#pragma alloc_text(PAGE, PerfInfoProfileUninit)
|
|
#pragma alloc_text(PAGE, PerfInfoStartLog)
|
|
#pragma alloc_text(PAGE, PerfInfoStopLog)
|
|
#endif //ALLOC_PRAGMA
|
|
|
|
extern NTSTATUS IoPerfInit();
|
|
extern NTSTATUS IoPerfReset();
|
|
|
|
#ifdef NTPERF
|
|
NTSTATUS
|
|
PerfInfopStartLog(
|
|
PERFINFO_GROUPMASK *pGroupMask,
|
|
PERFINFO_START_LOG_LOCATION StartLogLocation
|
|
);
|
|
|
|
NTSTATUS
|
|
PerfInfopStopLog(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
PerfInfoSetProcessorSpeed(
|
|
VOID
|
|
);
|
|
#endif // NTPERF
|
|
|
|
|
|
VOID
|
|
PerfInfoProfileInit(
|
|
)
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
Starts the sampled profile and initializes the cache
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
#if !defined(NT_UP)
|
|
PerfInfoSampledProfileCaching = FALSE;
|
|
#else
|
|
PerfInfoSampledProfileCaching = TRUE;
|
|
#endif // !defined(NT_UP)
|
|
PerfInfoSampledProfileFlushInProgress = 0;
|
|
PerfProfileCache.Entries = 0;
|
|
|
|
PerfInfoProfileSourceActive = PerfInfoProfileSourceRequested;
|
|
|
|
KeSetIntervalProfile(PerfInfoProfileInterval, PerfInfoProfileSourceActive);
|
|
KeInitializeProfile(&PerfInfoProfileObject,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
PerfInfoProfileSourceActive,
|
|
0
|
|
);
|
|
KeStartProfile(&PerfInfoProfileObject, NULL);
|
|
}
|
|
|
|
|
|
VOID
|
|
PerfInfoProfileUninit(
|
|
)
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
Stops the sampled profile
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PerfInfoProfileSourceActive = ProfileMaximum; // Invalid value stops us from
|
|
// collecting more samples
|
|
KeStopProfile(&PerfInfoProfileObject);
|
|
PerfInfoFlushProfileCache();
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PerfInfoStartLog (
|
|
PERFINFO_GROUPMASK *PGroupMask,
|
|
PERFINFO_START_LOG_LOCATION StartLogLocation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by WMI as part of kernel logger initiaziation.
|
|
|
|
Arguments:
|
|
|
|
GroupMask - Masks for what to log. This pointer point to an allocated
|
|
area in WMI LoggerContext.
|
|
StartLogLocation - Indication of whether we're starting logging
|
|
at boot time or while the system is running. If
|
|
we're starting at boot time, we don't need to snapshot
|
|
the open files because there aren't any and we would
|
|
crash if we tried to find the list.
|
|
|
|
Return Value:
|
|
|
|
BUGBUG Need proper return/ error handling
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN ProfileInitialized = FALSE;
|
|
BOOLEAN ContextSwapStarted = FALSE;
|
|
BOOLEAN IoPerfInitialized = FALSE;
|
|
|
|
PERFINFO_CLEAR_GROUPMASK(&PerfGlobalGroupMask);
|
|
|
|
//
|
|
// Enable logging.
|
|
//
|
|
|
|
PPerfGlobalGroupMask = &PerfGlobalGroupMask;
|
|
PerfSetLogging(PPerfGlobalGroupMask);
|
|
|
|
if (PerfIsGroupOnInGroupMask(PERF_MEMORY, PGroupMask) ||
|
|
PerfIsGroupOnInGroupMask(PERF_FILENAME, PGroupMask) ||
|
|
PerfIsGroupOnInGroupMask(PERF_DRIVERS, PGroupMask)) {
|
|
PERFINFO_OR_GROUP_WITH_GROUPMASK(PERF_FILENAME_ALL, PGroupMask);
|
|
}
|
|
|
|
|
|
if (StartLogLocation == PERFINFO_START_LOG_FROM_GLOBAL_LOGGER) {
|
|
//
|
|
// From the wmi global logger, need to do Rundown in kernel mode
|
|
//
|
|
if (PerfIsGroupOnInGroupMask(PERF_PROC_THREAD, PGroupMask)) {
|
|
Status = PerfInfoProcessRunDown();
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Finish;
|
|
}
|
|
}
|
|
|
|
if (PerfIsGroupOnInGroupMask(PERF_PROC_THREAD, PGroupMask)) {
|
|
Status = PerfInfoSysModuleRunDown();
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Finish;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// File Name Rundown code
|
|
//
|
|
if ((StartLogLocation != PERFINFO_START_LOG_AT_BOOT) &&
|
|
PerfIsGroupOnInGroupMask(PERF_FILENAME_ALL, PGroupMask)) {
|
|
PERFINFO_OR_GROUP_WITH_GROUPMASK(PERF_FILENAME_ALL, PPerfGlobalGroupMask);
|
|
Status = PerfInfoFileNameRunDown();
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Finish;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize Perf Driver hooks
|
|
//
|
|
if (PerfIsGroupOnInGroupMask(PERF_DRIVERS, PGroupMask)) {
|
|
Status = IoPerfInit();
|
|
if (NT_SUCCESS(Status)) {
|
|
IoPerfInitialized = TRUE;
|
|
} else {
|
|
goto Finish;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Enable context swap tracing
|
|
//
|
|
if ( PerfIsGroupOnInGroupMask(PERF_CONTEXT_SWITCH, PGroupMask) ) {
|
|
WmiStartContextSwapTrace();
|
|
ContextSwapStarted = TRUE;
|
|
}
|
|
|
|
//
|
|
// Sampled Profile
|
|
//
|
|
if (PerfIsGroupOnInGroupMask(PERF_PROFILE, PGroupMask)) {
|
|
if ((KeGetPreviousMode() == KernelMode) ||
|
|
(SeSinglePrivilegeCheck(SeSystemProfilePrivilege, UserMode))) {
|
|
PerfInfoProfileInit();
|
|
ProfileInitialized = TRUE;
|
|
} else {
|
|
Status = STATUS_NO_SUCH_PRIVILEGE;
|
|
goto Finish;
|
|
}
|
|
}
|
|
|
|
#ifdef NTPERF
|
|
Status = PerfInfopStartLog(PGroupMask, StartLogLocation);
|
|
#else
|
|
//
|
|
// See if we need to empty the working set to start
|
|
//
|
|
if (PerfIsGroupOnInGroupMask(PERF_FOOTPRINT, PGroupMask) ||
|
|
PerfIsGroupOnInGroupMask(PERF_BIGFOOT, PGroupMask)) {
|
|
MmEmptyAllWorkingSets ();
|
|
}
|
|
#endif // NTPERF
|
|
|
|
Finish:
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// Failed to turn on trace, clean up now.
|
|
//
|
|
|
|
if (ContextSwapStarted) {
|
|
WmiStopContextSwapTrace();
|
|
}
|
|
|
|
if (ProfileInitialized) {
|
|
PerfInfoProfileUninit();
|
|
}
|
|
|
|
if (IoPerfInitialized) {
|
|
IoPerfReset();
|
|
}
|
|
|
|
//
|
|
// Disable logging.
|
|
//
|
|
|
|
PPerfGlobalGroupMask = NULL;
|
|
PerfSetLogging(NULL);
|
|
|
|
PERFINFO_CLEAR_GROUPMASK(&PerfGlobalGroupMask);
|
|
} else {
|
|
#ifdef NTPERF
|
|
if (PERFINFO_IS_LOGGING_TO_PERFMEM()) {
|
|
//
|
|
// Make a copy of the GroupMask in PerfMem header
|
|
// so user mode logging can work
|
|
//
|
|
PerfBufHdr()->GlobalGroupMask = *PGroupMask;
|
|
}
|
|
#endif // NTPERF
|
|
*PPerfGlobalGroupMask = *PGroupMask;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PerfInfoStopLog (
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine turn off the PerfInfo trace hooks.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
BUGBUG Need proper return/ error handling
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN DisableContextSwaps=FALSE;
|
|
|
|
if (PPerfGlobalGroupMask == NULL) {
|
|
return Status;
|
|
}
|
|
|
|
if (PERFINFO_IS_GROUP_ON(PERF_MEMORY)) {
|
|
MmIdentifyPhysicalMemory();
|
|
}
|
|
|
|
if (PERFINFO_IS_GROUP_ON(PERF_PROFILE)) {
|
|
PerfInfoProfileUninit();
|
|
}
|
|
|
|
if (PERFINFO_IS_GROUP_ON(PERF_DRIVERS)) {
|
|
IoPerfReset();
|
|
}
|
|
|
|
#ifdef NTPERF
|
|
if (PERFINFO_IS_LOGGING_TO_PERFMEM()) {
|
|
//
|
|
// Now clear the GroupMask in Perfmem to stop logging.
|
|
//
|
|
PERFINFO_CLEAR_GROUPMASK(&PerfBufHdr()->GlobalGroupMask);
|
|
}
|
|
Status = PerfInfopStopLog();
|
|
#endif // NTPERF
|
|
|
|
if ( PERFINFO_IS_GROUP_ON(PERF_CONTEXT_SWITCH) ) {
|
|
DisableContextSwaps = TRUE;
|
|
}
|
|
|
|
//
|
|
// Reset the PPerfGlobalGroupMask.
|
|
//
|
|
|
|
PERFINFO_CLEAR_GROUPMASK(PPerfGlobalGroupMask);
|
|
|
|
//
|
|
// Disable logging.
|
|
//
|
|
|
|
PPerfGlobalGroupMask = NULL;
|
|
PerfSetLogging(NULL);
|
|
|
|
//
|
|
// Disable context swap tracing.
|
|
// IMPORTANT: This must be done AFTER the global flag is set to NULL!!!
|
|
//
|
|
if( DisableContextSwaps ) {
|
|
|
|
WmiStopContextSwapTrace();
|
|
}
|
|
|
|
return (Status);
|
|
|
|
}
|
|
|
|
#ifdef NTPERF
|
|
|
|
NTSTATUS
|
|
PerfInfoStartPerfMemLog (
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Indicate a logger wants to log into Perfmem. If it is the first logger,
|
|
initialize the shared memory buffer. Otherwise, just increment LoggerCounts.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
STATUS_BUFFER_TOO_SMALL - if buffer not big enough
|
|
STATUS_SUCCESS - otherwize
|
|
|
|
--*/
|
|
{
|
|
PPERF_BYTE pbCurrentStart;
|
|
ULONG cbBufferSize;
|
|
LARGE_INTEGER PerformanceFrequency;
|
|
const PPERFINFO_TRACEBUF_HEADER Buffer = PerfBufHdr();
|
|
ULONG LoggerCounts;
|
|
ULONG Idx;
|
|
|
|
//
|
|
// Is it big enough to use?
|
|
//
|
|
if (PerfQueryBufferSizeBytes() <= 2 * PERFINFO_HEADER_ZONE_SIZE) {
|
|
PERFINFO_SET_LOGGING_TO_PERFMEM(FALSE);
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// It is OK to use the buffer, increment the reference count
|
|
//
|
|
LoggerCounts = InterlockedIncrement(&Buffer->LoggerCounts);
|
|
if (LoggerCounts != 1) {
|
|
//
|
|
// Other logger has turned on logging, just return.
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Code to acquire the buffer would go here.
|
|
//
|
|
|
|
|
|
Buffer->SelfPointer = Buffer;
|
|
Buffer->MmSystemRangeStart = MmSystemRangeStart;
|
|
|
|
//
|
|
// initialize buffer version information
|
|
//
|
|
Buffer->usMajorVersion = PERFINFO_MAJOR_VERSION;
|
|
Buffer->usMinorVersion = PERFINFO_MINOR_VERSION;
|
|
|
|
//
|
|
// initialize timer stuff
|
|
//
|
|
Buffer->BufferFlag = FLAG_CYCLE_COUNT;
|
|
KeQuerySystemTime(&Buffer->PerfInitSystemTime);
|
|
Buffer->PerfInitTime = PerfGetCycleCount();
|
|
|
|
Buffer->LastClockRef.SystemTime = Buffer->PerfInitSystemTime;
|
|
Buffer->LastClockRef.TickCount = Buffer->PerfInitTime;
|
|
|
|
Buffer->CalcPerfFrequency = PerfInfoTickFrequency;
|
|
Buffer->EventPerfFrequency = PerfInfoTickFrequency;
|
|
|
|
Buffer->PerfBufHeaderZoneSize = PERFINFO_HEADER_ZONE_SIZE;
|
|
|
|
KeQueryPerformanceCounter(&PerformanceFrequency);
|
|
Buffer->KePerfFrequency = PerformanceFrequency.QuadPart;
|
|
|
|
//
|
|
// Determine the size of the thread hash table
|
|
//
|
|
Buffer->ThreadHash = (PERFINFO_THREAD_HASH_ENTRY *)
|
|
(((PCHAR) Buffer) + sizeof(PERFINFO_TRACEBUF_HEADER));
|
|
Buffer->ThreadHashOverflow = FALSE;
|
|
RtlZeroMemory(Buffer->ThreadHash,
|
|
PERFINFO_THREAD_HASH_SIZE *
|
|
sizeof(PERFINFO_THREAD_HASH_ENTRY));
|
|
for (Idx = 0; Idx < PERFINFO_THREAD_HASH_SIZE; Idx++)
|
|
Buffer->ThreadHash[Idx].CurThread = PERFINFO_INVALID_ID;
|
|
|
|
pbCurrentStart = (PPERF_BYTE) Buffer + Buffer->PerfBufHeaderZoneSize;
|
|
cbBufferSize = PerfQueryBufferSizeBytes() - Buffer->PerfBufHeaderZoneSize;
|
|
|
|
Buffer->Start.Ptr = Buffer->Current.Ptr = pbCurrentStart;
|
|
Buffer->Max.Ptr = pbCurrentStart + cbBufferSize;
|
|
|
|
//
|
|
// initialize version mismatch tracking
|
|
//
|
|
Buffer->fVersionMismatch = FALSE;
|
|
|
|
//
|
|
// initialize buffer overflow counter
|
|
//
|
|
Buffer->BufferBytesLost = 0;
|
|
|
|
//
|
|
// initialize the pointer to the COWHeader
|
|
//
|
|
Buffer->pCOWHeader = NULL;
|
|
|
|
RtlZeroMemory(Buffer->Start.Ptr, Buffer->Max.Ptr - Buffer->Start.Ptr);
|
|
|
|
PERFINFO_SET_LOGGING_TO_PERFMEM(TRUE);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PerfInfoStopPerfMemLog (
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Indicate a logger finishes logging. If the loggerCounts goes to zero, the
|
|
buffer will be reset the next time it is turned on.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
STATUS_BUFFER_TOO_SMALL - if buffer not big enough
|
|
STATUS_SUCCESS - otherwize
|
|
|
|
--*/
|
|
{
|
|
ULONG LoggerCounts;
|
|
const PPERFINFO_TRACEBUF_HEADER Buffer = PerfBufHdr();
|
|
|
|
LoggerCounts = InterlockedDecrement(&Buffer->LoggerCounts);
|
|
if (LoggerCounts == 0) {
|
|
//
|
|
// Other logger has turned on logging, just return.
|
|
//
|
|
PERFINFO_SET_LOGGING_TO_PERFMEM(FALSE);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PerfInfopStartLog(
|
|
PERFINFO_GROUPMASK *pGroupMask,
|
|
PERFINFO_START_LOG_LOCATION StartLogLocation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initialize the mminfo log and turn on the monitor.
|
|
|
|
Arguments:
|
|
|
|
GroupMask: Masks for what to log.
|
|
|
|
Return Value:
|
|
|
|
BUGBUG Need proper return/ error handling
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
#ifdef NTPERF_PRIVATE
|
|
Status = PerfInfopStartPrivateLog(pGroupMask, StartLogLocation);
|
|
if (!NT_SUCCESS(Status)) {
|
|
PERFINFO_CLEAR_GROUPMASK(PPerfGlobalGroupMask);
|
|
return Status;
|
|
}
|
|
#else
|
|
UNREFERENCED_PARAMETER(pGroupMask);
|
|
UNREFERENCED_PARAMETER(StartLogLocation);
|
|
#endif // NTPERF_PRIVATE
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PerfInfopStopLog (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine turn off the mminfo monitor and (if needed) dump the
|
|
data for user.
|
|
|
|
NOTE: The shutdown and hibernate paths have similar code. Check
|
|
those if you make changes.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
if (PERFINFO_IS_ANY_GROUP_ON()) {
|
|
|
|
#ifdef NTPERF_PRIVATE
|
|
PerfInfopStopPrivateLog();
|
|
#endif // NTPERF_PRIVATE
|
|
|
|
if (PERFINFO_IS_LOGGING_TO_PERFMEM()) {
|
|
PerfBufHdr()->LogStopTime = PerfGetCycleCount();
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PerfInfoSetPerformanceTraceInformation (
|
|
IN PVOID SystemInformation,
|
|
IN ULONG SystemInformationLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine implements the performance system information functions.
|
|
|
|
Arguments:
|
|
|
|
SystemInformation - A pointer to a buffer which receives the specified
|
|
information. This is of type PPERFINFO_PERFORMANCE_INFORMATION.
|
|
|
|
SystemInformationLength - Specifies the length in bytes of the system
|
|
information buffer.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successful
|
|
|
|
STATUS_INFO_LENGTH_MISMATCH if size of buffer is incorrect
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PPERFINFO_PERFORMANCE_INFORMATION PerfInfo;
|
|
PVOID PerfBuffer;
|
|
|
|
if (SystemInformationLength < sizeof(PERFINFO_PERFORMANCE_INFORMATION)) {
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
PerfInfo = (PPERFINFO_PERFORMANCE_INFORMATION) SystemInformation;
|
|
PerfBuffer = PerfInfo + 1;
|
|
|
|
switch (PerfInfo->PerformanceType) {
|
|
|
|
case PerformancePerfInfoStart:
|
|
// Status = PerfInfoStartLog(&PerfInfo->StartInfo.Flags, PERFINFO_START_LOG_POST_BOOT);
|
|
Status = STATUS_INVALID_INFO_CLASS;
|
|
break;
|
|
|
|
case PerformancePerfInfoStop:
|
|
// Status = PerfInfoStopLog();
|
|
Status = STATUS_INVALID_INFO_CLASS;
|
|
break;
|
|
|
|
#ifdef NTPERF_PRIVATE
|
|
case PerformanceMmInfoMarkWithFlush:
|
|
case PerformanceMmInfoMark:
|
|
case PerformanceMmInfoAsyncMark:
|
|
{
|
|
USHORT LogType;
|
|
ULONG StringLength;
|
|
|
|
if (PerfInfo->PerformanceType == PerformanceMmInfoMarkWithFlush) {
|
|
if (PERFINFO_IS_GROUP_ON(PERF_FOOTPRINT) ||
|
|
PERFINFO_IS_GROUP_ON(PERF_FOOTPRINT_PROC)) {
|
|
|
|
//
|
|
// BUGBUG We should get a non-Mi* call for this...
|
|
//
|
|
MmEmptyAllWorkingSets();
|
|
Status = MmPerfSnapShotValidPhysicalMemory();
|
|
}
|
|
else if (PERFINFO_IS_GROUP_ON(PERF_CLEARWS)) {
|
|
MmEmptyAllWorkingSets();
|
|
}
|
|
} else if (PerfinfoBigFootSize) {
|
|
MmEmptyAllWorkingSets();
|
|
}
|
|
|
|
if (PERFINFO_IS_ANY_GROUP_ON()) {
|
|
PERFINFO_MARK_INFORMATION Event;
|
|
StringLength = SystemInformationLength - sizeof(PERFINFO_PERFORMANCE_INFORMATION);
|
|
|
|
LogType = (PerfInfo->PerformanceType == PerformanceMmInfoAsyncMark) ?
|
|
PERFINFO_LOG_TYPE_ASYNCMARK :
|
|
PERFINFO_LOG_TYPE_MARK;
|
|
|
|
PerfInfoLogBytesAndANSIString(LogType,
|
|
&Event,
|
|
FIELD_OFFSET(PERFINFO_MARK_INFORMATION, Name),
|
|
(PCSTR) PerfBuffer,
|
|
StringLength
|
|
);
|
|
}
|
|
|
|
if (PERFINFO_IS_GROUP_ON(PERF_FOOTPRINT_PROC)) {
|
|
PerfInfoDumpWSInfo (TRUE);
|
|
}
|
|
break;
|
|
}
|
|
case PerformanceMmInfoFlush:
|
|
MmEmptyAllWorkingSets();
|
|
break;
|
|
#endif // NTPERF_PRIVATE
|
|
|
|
default:
|
|
#ifdef NTPERF_PRIVATE
|
|
Status = PerfInfoSetPerformanceTraceInformationPrivate(PerfInfo, SystemInformationLength);
|
|
#else
|
|
Status = STATUS_INVALID_INFO_CLASS;
|
|
#endif // NTPERF_PRIVATE
|
|
break;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PerfInfoQueryPerformanceTraceInformation (
|
|
IN PVOID SystemInformation,
|
|
IN ULONG SystemInformationLength,
|
|
OUT PULONG ReturnLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Satisfy queries for performance trace state information.
|
|
|
|
Arguments:
|
|
|
|
SystemInformation - A pointer to a buffer which receives the specified
|
|
information. This is of type PPERFINFO_PERFORMANCE_INFORMATION.
|
|
|
|
SystemInformationLength - Specifies the length in bytes of the system
|
|
information buffer.
|
|
|
|
ReturnLength - Receives the number of bytes placed in the system information buffer.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
if (SystemInformationLength != sizeof(PERFINFO_PERFORMANCE_INFORMATION)) {
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
#ifdef NTPERF_PRIVATE
|
|
|
|
return PerfInfoQueryPerformanceTraceInformationPrivate(
|
|
(PPERFINFO_PERFORMANCE_INFORMATION) SystemInformation,
|
|
ReturnLength
|
|
);
|
|
#else
|
|
UNREFERENCED_PARAMETER(ReturnLength);
|
|
UNREFERENCED_PARAMETER(SystemInformation);
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
#endif // NTPERF_PRIVATE
|
|
}
|
|
|
|
|
|
VOID
|
|
PerfInfoSetProcessorSpeed(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Calculate and set the processor speed in MHz.
|
|
|
|
Note: KPRCB->MHz, once it's set reliably, should be used instead
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
ULONGLONG start;
|
|
ULONGLONG end;
|
|
ULONGLONG freq;
|
|
ULONGLONG TSCStart;
|
|
LARGE_INTEGER *Pstart = (LARGE_INTEGER *) &start;
|
|
LARGE_INTEGER *Pend = (LARGE_INTEGER *) &end;
|
|
LARGE_INTEGER Delay;
|
|
ULONGLONG time[3];
|
|
ULONGLONG clocks;
|
|
int i;
|
|
int RetryCount = 50;
|
|
|
|
|
|
Delay.QuadPart = -50000; // relative delay of 5ms (100ns ticks)
|
|
|
|
while (RetryCount) {
|
|
for (i = 0; i < 3; i++) {
|
|
*Pstart = KeQueryPerformanceCounter(NULL);
|
|
|
|
TSCStart = PerfGetCycleCount();
|
|
KeDelayExecutionThread (KernelMode, FALSE, &Delay);
|
|
clocks = PerfGetCycleCount() - TSCStart;
|
|
|
|
*Pend = KeQueryPerformanceCounter((LARGE_INTEGER*)&freq);
|
|
time[i] = (((end-start) * 1000000) / freq);
|
|
time[i] = (clocks + time[i]/2) / time[i];
|
|
}
|
|
// If all three match then use it, else try again.
|
|
if (time[0] == time[1] && time[1] == time[2])
|
|
break;
|
|
--RetryCount;
|
|
}
|
|
|
|
if (!RetryCount) {
|
|
// Take the largest value.
|
|
if (time[1] > time[0])
|
|
time[0] = time[1];
|
|
if (time[2] > time[0])
|
|
time[0] = time[2];
|
|
}
|
|
PerfInfoTickFrequency = time[0];
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
PerfInfoIsGroupOn(
|
|
ULONG Group
|
|
)
|
|
{
|
|
return PERFINFO_IS_GROUP_ON(Group);
|
|
}
|
|
|
|
#ifdef NTPERF_PRIVATE
|
|
#include "..\..\tools\ntperf\ntosperf\perfinfokrn.c"
|
|
#endif // NTPERF_PRIVATE
|
|
#endif // NTPERF
|