542 lines
11 KiB
C
542 lines
11 KiB
C
/*++
|
|
|
|
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 <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <string.h>
|
|
|
|
#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; i<NUM_ITERATIONS; i++) {
|
|
NapCalibrate();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS NapCreateDataSection(PNAPCONTROL *NapSectionPointer)
|
|
/*++
|
|
Routine Description:
|
|
NapCreateData() is called to create the profiling data section.
|
|
One section, named "\NapDataSection", is used for the whole system.
|
|
It is accessible by anyone, so don't mess it up.
|
|
Arguments:
|
|
NapSectionPointer - a pointer to a pointer which will be set to to point to the beginning of the section.
|
|
Return Value:
|
|
Status
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE NapSectionHandle;
|
|
HANDLE MyProcess;
|
|
STRING NapSectionName;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
LARGE_INTEGER AllocationSize;
|
|
ULONG ViewSize;
|
|
PNAPCONTROL SectionPointer;
|
|
|
|
// Initialize object attributes for the dump file
|
|
NapSectionName.Length = 15;
|
|
NapSectionName.MaximumLength = 15;
|
|
NapSectionName.Buffer = "\\NapDataSection";
|
|
|
|
ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
|
|
ObjectAttributes.ObjectName = &NapSectionName;
|
|
ObjectAttributes.RootDirectory = NULL;
|
|
ObjectAttributes.SecurityDescriptor = NULL;
|
|
ObjectAttributes.SecurityQualityOfService = NULL;
|
|
ObjectAttributes.Attributes = OBJ_OPENIF | OBJ_CASE_INSENSITIVE;
|
|
|
|
AllocationSize.HighPart = 0;
|
|
AllocationSize.LowPart = (NAP_API_COUNT + 1) * sizeof(NAPDATA);
|
|
|
|
Status = NtCreateSection(&NapSectionHandle, SECTION_MAP_READ | SECTION_MAP_WRITE, &ObjectAttributes, &AllocationSize, PAGE_READWRITE, SEC_COMMIT, NULL);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
MyProcess = NtCurrentProcess();
|
|
ViewSize = AllocationSize.LowPart;
|
|
SectionPointer = NULL;
|
|
Status = NtMapViewOfSection(NapSectionHandle, MyProcess, (PVOID *) &SectionPointer, 0, AllocationSize.LowPart, NULL, &ViewSize, ViewUnmap, MEM_TOP_DOWN, PAGE_READWRITE);
|
|
*NapSectionPointer = SectionPointer;
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
VOID NapRecordInfo (IN ULONG ServiceNumber, IN LARGE_INTEGER Counters[])
|
|
/*++
|
|
Routine Description:
|
|
NapRecordInfo() is called after the API being measured has
|
|
returned, and the value of the performance counter has been
|
|
obtained.
|
|
|
|
NOTE: Initialization occurs only once and is controlled by
|
|
the NapInitDone global flag.
|
|
|
|
Arguments:
|
|
|
|
ServiceNumber - the number of the service being measured.
|
|
-1 is reserved for calibration.
|
|
|
|
Counters - a pointer to the value of the counter after the call
|
|
to the measured routine. Followed immediately
|
|
in memory by the value of the counter before the
|
|
call to the measured routine.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
LARGE_INTEGER ElapsedTime;
|
|
LARGE_INTEGER Difference;
|
|
|
|
|
|
|
|
// First make sure we have been initialized
|
|
|
|
|
|
if (NapControl != NULL) {
|
|
|
|
|
|
// Check to be sure profiling is not paused
|
|
|
|
if (NapControl->Paused == 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);
|
|
}
|