|
|
/*++
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
|