/*++ Copyright (c) 1991 Microsoft Corporation Module Name: ntnap.c Abstract: This module contains the routines for initializing and performing profiling of NT API's. Author: Russ Blake (russbl) 22-Apr-1991 Revision History: --*/ #include #include #include #include #include "ntnapdef.h" #include "ntnap.h" // // Initialization control: only want to initialize once; this // prevents multiple initializations before data segment and // global control are initialized. // BOOLEAN NapInitialized = FALSE; // // Counter frequency constant // LARGE_INTEGER NapFrequency; // // Profiling data structures // PNAPCONTROL NapControl = NULL; // // The following array is indexed by Service Number + 1. Service // Number -1 (element 0 of the following array) is reserved for // calibration. Initialization of this to NULL kicks off the // NapDllInit sequence to create or open the data section. // PNAPDATA NapProfilingData = NULL; VOID NapDllInit ( VOID ) /*++ Routine Description: NapDllInit() is called before calling any profiling api. It initializes profiling for the NT native API's. NOTE: Initialization occurs only once and is controlled by the NapInitDone global flag. Arguments: None Return Value: None --*/ { ULONG i; LARGE_INTEGER CurrentCounter; if (!NapInitialized) { // // Instance data for this process's ntdll.dll not yet initialized. // NapInitialized = TRUE; // // Get Counter frequency (current counter value is thrown away) // This call is not profiled because NapControl == NULL here. // NtQueryPerformanceCounter(&CurrentCounter, &NapFrequency); // // Allocate or open data section for api profiling data. // if (NT_SUCCESS(NapCreateDataSection(&NapControl))) { NapProfilingData = (PNAPDATA)(NapControl + 1); if (!NapControl->Initialized) { // // We are the first process to get here: we jsut // allocated the section, so must initialize it. // NapControl->Initialized = TRUE; NapControl->Paused = 0; // // Clear the data area used for calibration data. // NapProfilingData[0].NapLock = 0L; NapProfilingData[0].Calls = 0L; NapProfilingData[0].TimingErrors = 0L; NapProfilingData[0].TotalTime.HighPart = 0L; NapProfilingData[0].TotalTime.LowPart = 0L; NapProfilingData[0].FirstTime.HighPart = 0L; NapProfilingData[0].FirstTime.LowPart = 0L; NapProfilingData[0].MaxTime.HighPart = 0L; NapProfilingData[0].MaxTime.LowPart = 0L; NapProfilingData[0].MinTime.HighPart = MAX_LONG; NapProfilingData[0].MinTime.LowPart = MAX_ULONG; // // Clear the rest of the data collection area // NapClearData(); // // Calibrate time overhead from profiling. We care mostly // about the minimum time here, to avoid extra time from // interrupts etc. // for (i=0; iPaused == 0) { // // Since ServiceNumber -1 is used for calibration, bump same // so indexes start at 0. // ServiceNumber++; // // Prevent others from changing data at the same time // we are. // NapAcquireSpinLock(&(NapProfilingData[ServiceNumber].NapLock)); // // Record API info // NapProfilingData[ServiceNumber].Calls++; // // Elapsed time is end time minus start time // ElapsedTime = RtlLargeIntegerSubtract(Counters[0],Counters[1]); // // Now add elapsed time to total time // NapProfilingData[ServiceNumber].TotalTime = RtlLargeIntegerAdd(NapProfilingData[ServiceNumber].TotalTime, ElapsedTime); if (NapProfilingData[ServiceNumber].Calls == 1) { NapProfilingData[ServiceNumber].FirstTime = ElapsedTime; } else { Difference = RtlLargeIntegerSubtract( NapProfilingData[ServiceNumber].MaxTime, ElapsedTime); if (Difference.HighPart < 0) { // // A new maximum was attained // NapProfilingData[ServiceNumber].MaxTime = ElapsedTime; } Difference = RtlLargeIntegerSubtract( ElapsedTime, NapProfilingData[ServiceNumber].MinTime); if (Difference.HighPart < 0) { // // A new minimum was attained // NapProfilingData[ServiceNumber].MinTime = ElapsedTime; } } NapReleaseSpinLock(&(NapProfilingData[ServiceNumber].NapLock)); } } } NTSTATUS NapClearData ( VOID ) /*++ Routine Description: NapClearData() is called to clear all the counters and timers. The calibration counters are not cleared. For the rest, if not previously collected, the data will be lost. Arguments: None Return Value: Status --*/ { ULONG ServiceNumber; // // Initialize the first one after the calibration data // NapAcquireSpinLock(&(NapProfilingData[1].NapLock)); NapProfilingData[1].Calls = 0L; NapProfilingData[1].TimingErrors = 0L; NapProfilingData[1].TotalTime.HighPart = 0L; NapProfilingData[1].TotalTime.LowPart = 0L; NapProfilingData[1].FirstTime.HighPart = 0L; NapProfilingData[1].FirstTime.LowPart = 0L; NapProfilingData[1].MaxTime.HighPart = 0L; NapProfilingData[1].MaxTime.LowPart = 0L; NapProfilingData[1].MinTime.HighPart = MAX_LONG; NapProfilingData[1].MinTime.LowPart = MAX_ULONG; // // Now use the initialized first one to initialize the rest // for (ServiceNumber = 2; ServiceNumber <= NAP_API_COUNT; ServiceNumber++) { NapAcquireSpinLock(&(NapProfilingData[ServiceNumber].NapLock)); NapProfilingData[ServiceNumber] = NapProfilingData[1]; NapReleaseSpinLock(&(NapProfilingData[ServiceNumber].NapLock)); } NapReleaseSpinLock(&(NapProfilingData[1].NapLock)); return(STATUS_SUCCESS); } NTSTATUS NapRetrieveData ( OUT NAPDATA *NapApiData, OUT PCHAR **NapApiNames, OUT PLARGE_INTEGER *NapCounterFrequency ) /*++ Routine Description: NapRetrieveData() is called to get all the relevant data about API's that have been profiled. It is wise to call NapPause() before calling this, and NapResume() afterwards. Can't just do these in here, 'cause you may be doing stuff outside of this call you don't want profiled. Arguments: NapApiData - a pointer to a buffer to which is copied the array of counters; must be large enough for NAP_API_COUNT+1; call NapGetApiCount to obtain needed size. NapApiNames - a pointer to which is copied a pointer to an array of pointers to API names NapCounterFrequency - a buffer to which is copied the frequency of the performance counter Return Value: Status --*/ { ULONG ServiceNumber; *NapApiNames = NapNames; *NapCounterFrequency = &NapFrequency; for (ServiceNumber = 0; ServiceNumber <= NAP_API_COUNT; ServiceNumber++) { NapAcquireSpinLock(&(NapProfilingData[ServiceNumber].NapLock)); NapApiData[ServiceNumber] = NapProfilingData[ServiceNumber]; NapReleaseSpinLock(&(NapProfilingData[ServiceNumber].NapLock)); NapReleaseSpinLock(&(NapApiData[ServiceNumber].NapLock)); } return(STATUS_SUCCESS); } NTSTATUS NapGetApiCount ( OUT PULONG NapApiCount ) /*++ Routine Description: NapGetApiCount() is called to get the number of API's which are being profiled. This can then be used to allocate an array for receiving the data from NapReturnData. Arguments: NapApiCount - A pointer throug which the count is returned. Return Value: Status --*/ { *NapApiCount = NAP_API_COUNT; return(STATUS_SUCCESS); } NTSTATUS NapPause ( VOID ) /*++ Routine Description: NapPause() is called to temporarily cease data collection. Data collection is resumed by a companion call to NapResume. Arguments: None Return Value: Status --*/ { NapControl->Paused++; return(STATUS_SUCCESS); } NTSTATUS NapResume ( VOID ) /*++ Routine Description: NapResume() is called to resume data collection after a companion call to NapPause. Arguments: None Return Value: Status --*/ { NapControl->Paused--; return(STATUS_SUCCESS); }