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.
689 lines
16 KiB
689 lines
16 KiB
/*++
|
|
|
|
Copyright (c) 2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Amd64.c
|
|
|
|
Abstract:
|
|
|
|
This module implements profiling functions for the Amd64 platform.
|
|
|
|
Author:
|
|
|
|
Steve Deng (sdeng) 18-Jun-2002
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
--*/
|
|
|
|
#include "halcmn.h"
|
|
#include "mpprofil.h"
|
|
#include "amd64.h"
|
|
|
|
//
|
|
// Local prototypes
|
|
//
|
|
|
|
VOID
|
|
Amd64InitializeProfiling(
|
|
VOID
|
|
);
|
|
|
|
NTSTATUS
|
|
Amd64EnableMonitoring(
|
|
KPROFILE_SOURCE ProfileSource
|
|
);
|
|
|
|
VOID
|
|
Amd64DisableMonitoring(
|
|
KPROFILE_SOURCE ProfileSource
|
|
);
|
|
|
|
NTSTATUS
|
|
Amd64SetInterval(
|
|
IN KPROFILE_SOURCE ProfileSource,
|
|
IN OUT ULONG_PTR *Interval
|
|
);
|
|
|
|
NTSTATUS
|
|
Amd64QueryInformation(
|
|
IN HAL_QUERY_INFORMATION_CLASS InformationType,
|
|
IN ULONG BufferSize,
|
|
IN OUT PVOID Buffer,
|
|
OUT PULONG ReturnedLength
|
|
);
|
|
|
|
VOID
|
|
Amd64CheckOverflowStatus(
|
|
POVERFLOW_STATUS pOverflowStatus
|
|
);
|
|
|
|
NTSTATUS
|
|
HalpGetProfileDescriptor(
|
|
KPROFILE_SOURCE ProfileSource,
|
|
PAMD64_PROFILE_SOURCE_DESCRIPTOR *ProfileSourceDescriptor
|
|
);
|
|
|
|
NTSTATUS
|
|
HalpAllocateCounter(
|
|
KPROFILE_SOURCE ProfileSource,
|
|
OUT PULONG Counter
|
|
);
|
|
|
|
VOID
|
|
HalpFreeCounter(
|
|
ULONG Counter
|
|
);
|
|
|
|
#pragma alloc_text(PAGE, Amd64QueryInformation)
|
|
#pragma alloc_text(INIT, Amd64InitializeProfiling)
|
|
|
|
PROFILE_INTERFACE Amd64PriofileInterface = {
|
|
Amd64InitializeProfiling,
|
|
Amd64EnableMonitoring,
|
|
Amd64DisableMonitoring,
|
|
Amd64SetInterval,
|
|
Amd64QueryInformation,
|
|
Amd64CheckOverflowStatus
|
|
};
|
|
|
|
//
|
|
// The status of counter registers
|
|
//
|
|
|
|
typedef struct _COUNTER_STATUS {
|
|
BOOLEAN Idle;
|
|
KPROFILE_SOURCE ProfileSource;
|
|
} COUNTER_STATUS;
|
|
|
|
COUNTER_STATUS
|
|
CounterStatus[MAXIMUM_PROCESSORS][AMD64_NUMBER_COUNTERS];
|
|
|
|
//
|
|
// The profile sources of overflowed counters
|
|
//
|
|
|
|
KPROFILE_SOURCE
|
|
OverflowedProfileList[MAXIMUM_PROCESSORS][AMD64_NUMBER_COUNTERS];
|
|
|
|
VOID
|
|
Amd64InitializeProfiling(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function does one time initialization of the performance monitoring
|
|
registers and related data structures.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG i;
|
|
|
|
//
|
|
// Initialize all PerfEvtSel registers to zero. This will effectively
|
|
// disable all counters.
|
|
//
|
|
|
|
for (i = 0; i < AMD64_NUMBER_COUNTERS; i++) {
|
|
WRMSR(MSR_PERF_EVT_SEL0 + i, 0);
|
|
HalpFreeCounter(i);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
Amd64EnableMonitoring(
|
|
KPROFILE_SOURCE ProfileSource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function enables the monitoring of the hardware events specified
|
|
by ProfileSource and set up MSRs to generate performance monitor
|
|
interrupt (PMI) when counters overflow.
|
|
|
|
Arguments:
|
|
|
|
ProfileSource - Supplies the Profile Source
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - If the monitoring of the event is successfully enabled.
|
|
|
|
STATUS_NOT_SUPPORTED - If the specified profile source is not supported.
|
|
|
|
STATUS_DEVICE_BUSY - If no free counters available.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAMD64_PROFILE_SOURCE_DESCRIPTOR ProfileSourceDescriptor;
|
|
NTSTATUS Status;
|
|
ULONG i;
|
|
|
|
//
|
|
// If the specified ProfileSource is not supported, return immediately
|
|
//
|
|
|
|
Status = HalpGetProfileDescriptor(ProfileSource,
|
|
&ProfileSourceDescriptor);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Get an idle counter if available. Otherwise return immediately.
|
|
//
|
|
|
|
Status = HalpAllocateCounter(ProfileSource, &i);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Set counter register to its initial value
|
|
//
|
|
|
|
WRMSR (MSR_PERF_CTR0 + i, 0 - ProfileSourceDescriptor->Interval);
|
|
|
|
//
|
|
// Enable counting and overflow interrupt
|
|
//
|
|
|
|
WRMSR (MSR_PERF_EVT_SEL0 + i,
|
|
ProfileSourceDescriptor->PerfEvtSelDef |
|
|
PERF_EVT_SEL_INTERRUPT |
|
|
PERF_EVT_SEL_ENABLE);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
Amd64DisableMonitoring(
|
|
KPROFILE_SOURCE ProfileSource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function stops the monitoring of the hardware event specified
|
|
by ProfileSource, and disables the interrupt associated with the event.
|
|
|
|
|
|
Arguments:
|
|
|
|
ProfileSource - Supplies the Profile Source
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG ProcessorNumber, i;
|
|
PAMD64_PROFILE_SOURCE_DESCRIPTOR ProfileSourceDescriptor;
|
|
|
|
//
|
|
// If the specified ProfileSource is not supported, return immediately.
|
|
//
|
|
|
|
Status = HalpGetProfileDescriptor(ProfileSource, &ProfileSourceDescriptor);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return;
|
|
}
|
|
|
|
ProcessorNumber = KeGetCurrentProcessorNumber();
|
|
for (i = 0; i < AMD64_NUMBER_COUNTERS; i++) {
|
|
|
|
//
|
|
// Find out the counter assigned to the given profile source and
|
|
// disable it
|
|
//
|
|
|
|
if (!(CounterStatus[ProcessorNumber][i].Idle) &&
|
|
(ProfileSource == CounterStatus[ProcessorNumber][i].ProfileSource)){
|
|
|
|
//
|
|
// Disable counting and overflow interrupt
|
|
//
|
|
|
|
WRMSR( MSR_PERF_EVT_SEL0 + i,
|
|
ProfileSourceDescriptor->PerfEvtSelDef &
|
|
~(PERF_EVT_SEL_INTERRUPT | PERF_EVT_SEL_ENABLE));
|
|
|
|
//
|
|
// Free up the counter
|
|
//
|
|
|
|
HalpFreeCounter (i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
Amd64SetInterval(
|
|
IN KPROFILE_SOURCE ProfileSource,
|
|
IN OUT ULONG_PTR *Interval
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function sets the interrupt interval of given profile source
|
|
for Amd64 platform.
|
|
|
|
It is assumed that all processors in the system use same interval
|
|
value for same ProfileSource.
|
|
|
|
Arguments:
|
|
|
|
ProfileSource - Supplies the profile source.
|
|
|
|
Interval - Supplies the interval value and returns the actual interval.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - If the profile interval is successfully updated.
|
|
|
|
STATUS_NOT_SUPPORTED - If the specified profile source is not supported.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PAMD64_PROFILE_SOURCE_DESCRIPTOR ProfileSourceDescriptor;
|
|
|
|
Status = HalpGetProfileDescriptor(ProfileSource, &ProfileSourceDescriptor);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (*Interval < ProfileSourceDescriptor->MinInterval) {
|
|
*Interval = ProfileSourceDescriptor->MinInterval;
|
|
}
|
|
|
|
if (*Interval > ProfileSourceDescriptor->MaxInterval) {
|
|
*Interval = ProfileSourceDescriptor->MaxInterval;
|
|
}
|
|
|
|
ProfileSourceDescriptor->Interval = *Interval;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
Amd64CheckOverflowStatus(
|
|
POVERFLOW_STATUS pOverflowStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function find out the overflowed counters and return the related
|
|
profile sources to the caller.
|
|
|
|
Arguments:
|
|
|
|
ProfileSource - Supplies the profile source.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAMD64_PROFILE_SOURCE_DESCRIPTOR ProfileSourceDescriptor;
|
|
KPROFILE_SOURCE ProfileSource;
|
|
ULONG64 CurrentCount, Mask;
|
|
NTSTATUS Status;
|
|
LONG i, j;
|
|
ULONG ProcessorNumber;
|
|
|
|
ProcessorNumber = KeGetCurrentProcessorNumber();
|
|
for(i = j = 0; i < AMD64_NUMBER_COUNTERS; i++) {
|
|
|
|
if (!(CounterStatus[ProcessorNumber][i].Idle)) {
|
|
ProfileSource = CounterStatus[ProcessorNumber][i].ProfileSource;
|
|
Status = HalpGetProfileDescriptor (ProfileSource,
|
|
&ProfileSourceDescriptor);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Mask off the reserved bits
|
|
//
|
|
|
|
Mask = (((ULONG64)1 << AMD64_COUNTER_RESOLUTION) - 1);
|
|
|
|
CurrentCount = RDMSR(MSR_PERF_CTR0 + i);
|
|
|
|
//
|
|
// An overflow occured if the current value in counter
|
|
// is smaller than the initial value
|
|
//
|
|
|
|
if ((CurrentCount & Mask) <
|
|
((ULONG64)(0 - ProfileSourceDescriptor->Interval) & Mask)) {
|
|
|
|
//
|
|
// Add it to the overflowed profile source list.
|
|
//
|
|
//
|
|
|
|
OverflowedProfileList[ProcessorNumber][j] = ProfileSource;
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Record the number of overflowed counters
|
|
//
|
|
|
|
pOverflowStatus->Number = j;
|
|
|
|
if(j) {
|
|
pOverflowStatus->pSource = &(OverflowedProfileList[ProcessorNumber][0]);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
HalpGetProfileDescriptor(
|
|
IN KPROFILE_SOURCE ProfileSource,
|
|
IN OUT PAMD64_PROFILE_SOURCE_DESCRIPTOR *ProfileSourceDescriptor
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function retrieves the descriptor of specified profile source.
|
|
|
|
Arguments:
|
|
|
|
ProfileSource - Supplies the profile source.
|
|
|
|
ProfileSourceDescriptor - Where the pointer of descriptor is returned.
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - If the requested mapping is found.
|
|
|
|
STATUS_NOT_SUPPORTED - If the specified profile source is not supported.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
LONG i;
|
|
|
|
if ((ULONG)ProfileSource < ProfileMaximum) {
|
|
|
|
//
|
|
// This is a generic profile source
|
|
//
|
|
|
|
i = ProfileSource;
|
|
|
|
}
|
|
else if ((ULONG)ProfileSource < ProfileAmd64Maximum &&
|
|
(ULONG)ProfileSource >= ProfileAmd64Minimum ) {
|
|
|
|
//
|
|
// This is an Amd64 specific profile source
|
|
//
|
|
|
|
i = ProfileSource - ProfileAmd64Minimum + ProfileMaximum;
|
|
|
|
}
|
|
else {
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
*ProfileSourceDescriptor = &(Amd64ProfileSourceDescriptorTable[i]);
|
|
if (!((*ProfileSourceDescriptor)->Supported)) {
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
HalpAllocateCounter(
|
|
IN KPROFILE_SOURCE ProfileSource,
|
|
OUT PULONG Counter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function finds an idle counter register and assigns the specified
|
|
profile source to it.
|
|
|
|
Arguments:
|
|
|
|
ProfileSource - Supplies the profile source.
|
|
|
|
Counter - Supplies a pointer where the index of the idle counter is returned.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - If an idle counter register is found.
|
|
|
|
STATUS_DEVICE_BUSY - If all counter registers are occupied.
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG i;
|
|
ULONG ProcessorNumber;
|
|
|
|
ProcessorNumber = KeGetCurrentProcessorNumber();
|
|
|
|
for(i = 0; i < AMD64_NUMBER_COUNTERS; i++) {
|
|
if (CounterStatus[ProcessorNumber][i].Idle == TRUE) {
|
|
|
|
//
|
|
// Found an idle counter. Mark it busy and assign ProfileSource
|
|
// to it
|
|
//
|
|
|
|
CounterStatus[ProcessorNumber][i].Idle = FALSE;
|
|
CounterStatus[ProcessorNumber][i].ProfileSource = ProfileSource;
|
|
*Counter = i;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return STATUS_DEVICE_BUSY;
|
|
}
|
|
|
|
VOID
|
|
HalpFreeCounter(
|
|
ULONG Counter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function marks the specified counter idle.
|
|
|
|
Arguments:
|
|
|
|
Counter - The index of the counter to be freed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG ProcessorNumber;
|
|
|
|
ProcessorNumber = KeGetCurrentProcessorNumber();
|
|
CounterStatus[ProcessorNumber][Counter].Idle = TRUE;
|
|
CounterStatus[ProcessorNumber][Counter].ProfileSource = 0;
|
|
}
|
|
|
|
NTSTATUS
|
|
Amd64QueryInformation(
|
|
IN HAL_QUERY_INFORMATION_CLASS InformationType,
|
|
IN ULONG BufferSize,
|
|
IN OUT PVOID Buffer,
|
|
OUT PULONG ReturnedLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function retrieves the information of profile sources.
|
|
|
|
Arguments:
|
|
|
|
InformationClass - Constant that describes the type of information .
|
|
|
|
BufferSize - Size of the memory pointed to by Buffer.
|
|
|
|
Buffer - Requested information described by InformationClass.
|
|
|
|
ReturnedLength - The actual bytes returned or needed for the requested
|
|
information.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - If the requested information is retrieved successfully.
|
|
|
|
STATUS_INFO_LENGTH_MISMATCH - If the incoming BufferSize is too small.
|
|
|
|
STATUS_NOT_SUPPORTED - If the specified information class or profile
|
|
source is not supported.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG i, TotalProfieSources;
|
|
PHAL_PROFILE_SOURCE_INFORMATION ProfileSourceInformation;
|
|
PHAL_PROFILE_SOURCE_LIST ProfileSourceList;
|
|
PAMD64_PROFILE_SOURCE_DESCRIPTOR ProfileSourceDescriptor;
|
|
|
|
switch (InformationType) {
|
|
case HalQueryProfileSourceList:
|
|
|
|
TotalProfieSources = sizeof(Amd64ProfileSourceDescriptorTable) /
|
|
sizeof(AMD64_PROFILE_SOURCE_DESCRIPTOR);
|
|
|
|
if (BufferSize == 0) {
|
|
|
|
//
|
|
// This indicates the caller just wants to know the
|
|
// size of the buffer to allocate but is not prepared
|
|
// to accept any data content. In this case the bytes
|
|
// needed to store HAL_PROFILE_SOURCE_LIST structures
|
|
// of all profile sources is returned.
|
|
//
|
|
|
|
*ReturnedLength = TotalProfieSources *
|
|
sizeof(HAL_PROFILE_SOURCE_LIST);
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
if (BufferSize < TotalProfieSources *
|
|
sizeof(HAL_PROFILE_SOURCE_LIST)) {
|
|
*ReturnedLength = 0;
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
ProfileSourceList = (PHAL_PROFILE_SOURCE_LIST) Buffer;
|
|
|
|
for (i = 0; i < TotalProfieSources; i++) {
|
|
Status = HalpGetProfileDescriptor(i, &ProfileSourceDescriptor);
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Filling in requested data
|
|
//
|
|
|
|
ProfileSourceList->Source = ProfileSourceDescriptor->ProfileSource;
|
|
ProfileSourceList->Description = ProfileSourceDescriptor->Description;
|
|
ProfileSourceList++;
|
|
}
|
|
}
|
|
|
|
*ReturnedLength = (ULONG)((ULONG_PTR)ProfileSourceList -
|
|
(ULONG_PTR)Buffer);
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case HalProfileSourceInformation:
|
|
|
|
if (BufferSize < sizeof(HAL_PROFILE_SOURCE_INFORMATION)) {
|
|
*ReturnedLength = 0;
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
ProfileSourceInformation = (PHAL_PROFILE_SOURCE_INFORMATION) Buffer;
|
|
|
|
Status = HalpGetProfileDescriptor(ProfileSourceInformation->Source,
|
|
&ProfileSourceDescriptor);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
*ReturnedLength = 0;
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Filling in requested data
|
|
//
|
|
|
|
ProfileSourceInformation->Supported = ProfileSourceDescriptor->Supported;
|
|
ProfileSourceInformation->Interval = (ULONG) ProfileSourceDescriptor->Interval;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
*ReturnedLength = 0;
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|