windows-nt/Source/XPSP1/NT/base/ntdll/ntnap.c
2020-09-26 16:20:57 +08:00

592 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);
}