1625 lines
46 KiB
C
1625 lines
46 KiB
C
/*++
|
||
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
callouts.c
|
||
|
||
Abstract:
|
||
|
||
This is the source file that contains all the callout routines
|
||
from the kernel itself. The only exception is TraceIo for DiskPerf.
|
||
|
||
Author:
|
||
|
||
Jee Fung Pang (jeepang) 03-Dec-1996
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#pragma warning(disable:4214)
|
||
#pragma warning(disable:4115)
|
||
#pragma warning(disable:4201)
|
||
#pragma warning(disable:4127)
|
||
#include <stdio.h>
|
||
#include <ntos.h>
|
||
#include <zwapi.h>
|
||
#ifdef NTPERF
|
||
#include <ntdddisk.h>
|
||
#endif
|
||
#include <evntrace.h>
|
||
#include "wmikmp.h"
|
||
#include "tracep.h"
|
||
#pragma warning(default:4214)
|
||
#pragma warning(default:4115)
|
||
#pragma warning(default:4201)
|
||
#pragma warning(default:4127)
|
||
|
||
#ifndef _WMIKM_
|
||
#define _WMIKM_
|
||
#endif
|
||
|
||
VOID
|
||
FASTCALL
|
||
WmipTracePageFault(
|
||
IN NTSTATUS Status,
|
||
IN PVOID VirtualAddress,
|
||
IN PVOID TrapFrame
|
||
);
|
||
|
||
VOID
|
||
WmipTraceNetwork(
|
||
IN ULONG GroupType,
|
||
IN PVOID EventInfo,
|
||
IN ULONG EventInfoLen,
|
||
IN PVOID Reserved
|
||
);
|
||
|
||
VOID
|
||
WmipTraceIo(
|
||
IN ULONG DiskNumber,
|
||
IN PIRP Irp,
|
||
IN PVOID Counters
|
||
);
|
||
|
||
VOID
|
||
WmipTraceFile(
|
||
IN PKAPC Apc,
|
||
IN PKNORMAL_ROUTINE *NormalRoutine,
|
||
IN PVOID *NormalContext,
|
||
IN PVOID *SystemArgument1,
|
||
IN PVOID *SystemArgument2
|
||
);
|
||
|
||
VOID
|
||
WmipTraceLoadImage(
|
||
IN PUNICODE_STRING ImageName,
|
||
IN HANDLE ProcessId,
|
||
IN PIMAGE_INFO ImageInfo
|
||
);
|
||
|
||
VOID
|
||
WmipTraceRegistry(
|
||
IN NTSTATUS Status,
|
||
IN PVOID Kcb,
|
||
IN LONGLONG ElapsedTime,
|
||
IN ULONG Index,
|
||
IN PUNICODE_STRING KeyName,
|
||
IN UCHAR Type
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGEWMI, WmipIsLoggerOn)
|
||
#pragma alloc_text(PAGE, WmipEnableKernelTrace)
|
||
#pragma alloc_text(PAGE, WmipDisableKernelTrace)
|
||
#pragma alloc_text(PAGE, WmipSetTraceNotify)
|
||
#pragma alloc_text(PAGE, WmiTraceProcess)
|
||
#pragma alloc_text(PAGE, WmiTraceThread)
|
||
#pragma alloc_text(PAGE, WmipTraceFile)
|
||
#pragma alloc_text(PAGE, WmipTraceLoadImage)
|
||
#pragma alloc_text(PAGE, WmipTraceRegistry)
|
||
#pragma alloc_text(PAGEWMI, WmipTracePageFault)
|
||
#pragma alloc_text(PAGEWMI, WmipTraceNetwork)
|
||
#pragma alloc_text(PAGEWMI, WmipTraceIo)
|
||
#pragma alloc_text(PAGEWMI, WmiTraceContextSwap)
|
||
#pragma alloc_text(PAGE, WmiStartContextSwapTrace)
|
||
#pragma alloc_text(PAGE, WmiStopContextSwapTrace)
|
||
#endif
|
||
|
||
ULONG WmipTraceFileFlag = FALSE;
|
||
ULONG WmipFileIndex = 0;
|
||
PFILE_OBJECT *WmipFileTable = NULL;
|
||
|
||
#ifdef ALLOC_DATA_PRAGMA
|
||
#pragma data_seg("PAGEDATA")
|
||
#endif
|
||
ULONG WmipKernelLoggerStartedOnce = 0;
|
||
LONG WmipTraceProcessRef = 0;
|
||
PVOID WmipDiskIoNotify = NULL;
|
||
PVOID WmipTdiIoNotify = NULL;
|
||
#ifdef ALLOC_DATA_PRAGMA
|
||
#pragma data_seg()
|
||
#endif
|
||
|
||
typedef struct _TRACE_DEVICE {
|
||
PDEVICE_OBJECT DeviceObject;
|
||
ULONG TraceClass;
|
||
} TRACE_DEVICE, *PTRACE_DEVICE;
|
||
|
||
VOID
|
||
FASTCALL
|
||
WmipEnableKernelTrace(
|
||
IN ULONG EnableFlags
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is called by WmipStartLogger in tracelog.c. Its purpose is to
|
||
set up all the kernel notification routines that can produce event traces
|
||
for capacity planning.
|
||
|
||
Arguments:
|
||
|
||
ExtendedOn a flag to indicate if extended mode tracing is requested
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PREGENTRY RegEntry;
|
||
PLIST_ENTRY RegEntryList;
|
||
ULONG DevicesFound;
|
||
long Index, DiskFound;
|
||
PTRACE_DEVICE *deviceList, device;
|
||
CCHAR stackSize;
|
||
PIRP irp;
|
||
PVOID notifyRoutine;
|
||
PIO_STACK_LOCATION irpStack;
|
||
NTSTATUS status;
|
||
ULONG enableDisk, enableNetwork;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Since we cannot do anything, we will have to count the number
|
||
// of entries we need to create first, and add some buffer
|
||
//
|
||
|
||
DiskFound = 0;
|
||
|
||
enableDisk = (EnableFlags & EVENT_TRACE_FLAG_DISK_IO);
|
||
enableNetwork = (EnableFlags & EVENT_TRACE_FLAG_NETWORK_TCPIP);
|
||
|
||
if ( enableDisk || enableNetwork ) {
|
||
|
||
//
|
||
// Setting the callouts will cause new PDO registration to be enabled
|
||
// from here on.
|
||
//
|
||
if (enableDisk) {
|
||
WmipDiskIoNotify = (PVOID) &WmipTraceIo;
|
||
}
|
||
if (enableNetwork) {
|
||
WmipTdiIoNotify = (PVOID) &WmipTraceNetwork;
|
||
}
|
||
|
||
DevicesFound = WmipInUseRegEntryCount;
|
||
|
||
deviceList = (PTRACE_DEVICE*)
|
||
ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
(DevicesFound) * sizeof(TRACE_DEVICE),
|
||
TRACEPOOLTAG);
|
||
if (deviceList == NULL)
|
||
return;
|
||
|
||
RtlZeroMemory(deviceList, sizeof(TRACE_DEVICE) * DevicesFound);
|
||
|
||
//
|
||
// Now, we will go through what's already in the list and enable trace
|
||
// notification routine. Devices who registered while after we've set
|
||
// the callout will get another Irp to enable, but that's alright
|
||
//
|
||
|
||
device = (PTRACE_DEVICE) deviceList; // start from first element
|
||
|
||
Index = 0;
|
||
|
||
WmipEnterSMCritSection();
|
||
RegEntryList = WmipInUseRegEntryHead.Flink;
|
||
while (RegEntryList != &WmipInUseRegEntryHead) {
|
||
RegEntry = CONTAINING_RECORD(RegEntryList,REGENTRY,InUseEntryList);
|
||
|
||
if (RegEntry->Flags & REGENTRY_FLAG_TRACED) {
|
||
if ((ULONG) Index < DevicesFound) {
|
||
device->TraceClass
|
||
= RegEntry->Flags & WMIREG_FLAG_TRACE_NOTIFY_MASK;
|
||
if (device->TraceClass == WMIREG_NOTIFY_DISK_IO)
|
||
DiskFound++;
|
||
device->DeviceObject = RegEntry->DeviceObject;
|
||
device++;
|
||
Index++;
|
||
}
|
||
}
|
||
RegEntryList = RegEntryList->Flink;
|
||
}
|
||
WmipLeaveSMCritSection();
|
||
|
||
//
|
||
// actually send the notification to diskperf or tdi here
|
||
//
|
||
stackSize = WmipServiceDeviceObject->StackSize;
|
||
irp = IoAllocateIrp(stackSize, FALSE);
|
||
|
||
device = (PTRACE_DEVICE) deviceList;
|
||
while (--Index >= 0 && irp != NULL) {
|
||
if (device->DeviceObject != NULL) {
|
||
|
||
if ( (device->TraceClass == WMIREG_NOTIFY_TDI_IO) &&
|
||
enableNetwork ) {
|
||
notifyRoutine = (PVOID) &WmipTraceNetwork;
|
||
}
|
||
else if ( (device->TraceClass == WMIREG_NOTIFY_DISK_IO) &&
|
||
enableDisk ) {
|
||
notifyRoutine = (PVOID) &WmipTraceIo;
|
||
}
|
||
else { // consider supporting generic callout for other devices
|
||
notifyRoutine = NULL;
|
||
device ++;
|
||
continue;
|
||
}
|
||
|
||
do {
|
||
IoInitializeIrp(irp, IoSizeOfIrp(stackSize), stackSize);
|
||
IoSetNextIrpStackLocation(irp);
|
||
irpStack = IoGetCurrentIrpStackLocation(irp);
|
||
irpStack->DeviceObject = WmipServiceDeviceObject;
|
||
irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
||
|
||
status = WmipForwardWmiIrp(
|
||
irp,
|
||
IRP_MN_SET_TRACE_NOTIFY,
|
||
IoWMIDeviceObjectToProviderId(device->DeviceObject),
|
||
NULL,
|
||
sizeof(notifyRoutine),
|
||
¬ifyRoutine
|
||
);
|
||
|
||
if (status == STATUS_WMI_TRY_AGAIN) {
|
||
IoFreeIrp(irp);
|
||
stackSize = WmipServiceDeviceObject->StackSize;
|
||
irp = IoAllocateIrp(stackSize, FALSE);
|
||
if (!irp) {
|
||
break;
|
||
}
|
||
}
|
||
} while (status == STATUS_WMI_TRY_AGAIN);
|
||
}
|
||
device++;
|
||
}
|
||
if (irp) {
|
||
IoFreeIrp(irp);
|
||
}
|
||
ExFreePoolWithTag(deviceList, TRACEPOOLTAG);
|
||
// free the array that we created above
|
||
//
|
||
}
|
||
|
||
if (EnableFlags & EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS) {
|
||
MmSetPageFaultNotifyRoutine(
|
||
(PPAGE_FAULT_NOTIFY_ROUTINE) &WmipTracePageFault);
|
||
}
|
||
if (EnableFlags & EVENT_TRACE_FLAG_DISK_FILE_IO) {
|
||
//
|
||
// NOTE: We assume StartLogger will always reserve space for
|
||
// FileTable already
|
||
//
|
||
WmipTraceFileFlag = TRUE;
|
||
}
|
||
|
||
if (EnableFlags & EVENT_TRACE_FLAG_IMAGE_LOAD) {
|
||
if (!(WmipKernelLoggerStartedOnce & EVENT_TRACE_FLAG_IMAGE_LOAD)) {
|
||
PsSetLoadImageNotifyRoutine(
|
||
(PLOAD_IMAGE_NOTIFY_ROUTINE) &WmipTraceLoadImage
|
||
);
|
||
WmipKernelLoggerStartedOnce |= EVENT_TRACE_FLAG_IMAGE_LOAD;
|
||
}
|
||
}
|
||
|
||
if (EnableFlags & EVENT_TRACE_FLAG_REGISTRY) {
|
||
CmSetTraceNotifyRoutine(
|
||
(PCM_TRACE_NOTIFY_ROUTINE) &WmipTraceRegistry,
|
||
FALSE
|
||
);
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
FASTCALL
|
||
WmipDisableKernelTrace(
|
||
IN ULONG EnableFlags
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is called by WmipStopLogger in tracelog.c. Its purpose of the
|
||
disable all the kernel notification routines that was defined by
|
||
WmipEnableKernelTrace
|
||
|
||
Arguments:
|
||
|
||
EnableFlags Flags indicated what was enabled and needs to be disabled
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PVOID NullPtr = NULL;
|
||
PREGENTRY RegEntry;
|
||
PLIST_ENTRY RegEntryList;
|
||
ULONG DevicesFound;
|
||
long Index;
|
||
PTRACE_DEVICE* deviceList, device;
|
||
CCHAR stackSize;
|
||
PIRP irp;
|
||
PIO_STACK_LOCATION irpStack;
|
||
NTSTATUS status;
|
||
ULONG enableDisk, enableNetwork;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// first, disable partition change notification
|
||
//
|
||
|
||
if (EnableFlags & EVENT_TRACE_FLAG_DISK_FILE_IO) {
|
||
WmipTraceFileFlag = FALSE;
|
||
if (WmipFileTable != NULL) {
|
||
RtlZeroMemory(
|
||
WmipFileTable,
|
||
MAX_FILE_TABLE_SIZE * sizeof(PFILE_OBJECT));
|
||
}
|
||
}
|
||
|
||
if (EnableFlags & EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS) {
|
||
MmSetPageFaultNotifyRoutine(NULL);
|
||
}
|
||
|
||
if (EnableFlags & EVENT_TRACE_FLAG_REGISTRY) {
|
||
CmSetTraceNotifyRoutine(NULL,TRUE);
|
||
}
|
||
|
||
enableDisk = (EnableFlags & EVENT_TRACE_FLAG_DISK_IO);
|
||
enableNetwork = (EnableFlags & EVENT_TRACE_FLAG_NETWORK_TCPIP);
|
||
|
||
if (!enableDisk && !enableNetwork)
|
||
return; // NOTE: assumes all flags are already checked
|
||
|
||
//
|
||
// Note. Since this is in the middle is StopLogger, it is not possible
|
||
// StartLogger will prevent kernel tracing from being enabled, hence
|
||
// we need not worry about WmipEnableKernelTrace() being called while
|
||
// this is in progress.
|
||
//
|
||
WmipDiskIoNotify = NULL;
|
||
WmipTdiIoNotify = NULL;
|
||
|
||
DevicesFound = WmipInUseRegEntryCount;
|
||
|
||
deviceList = (PTRACE_DEVICE*)
|
||
ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
(DevicesFound) * sizeof(TRACE_DEVICE),
|
||
TRACEPOOLTAG);
|
||
if (deviceList == NULL)
|
||
return;
|
||
|
||
RtlZeroMemory(deviceList, sizeof(TRACE_DEVICE) * DevicesFound);
|
||
Index = 0;
|
||
device = (PTRACE_DEVICE) deviceList; // start from first element
|
||
|
||
//
|
||
// To disable we do not need to worry about TraceClass, since we simply
|
||
// set all callouts to NULL
|
||
//
|
||
WmipEnterSMCritSection();
|
||
RegEntryList = WmipInUseRegEntryHead.Flink;
|
||
while (RegEntryList != &WmipInUseRegEntryHead) {
|
||
RegEntry = CONTAINING_RECORD(RegEntryList, REGENTRY, InUseEntryList);
|
||
if (RegEntry->Flags & REGENTRY_FLAG_TRACED) {
|
||
if ((ULONG)Index < DevicesFound)
|
||
device->TraceClass
|
||
= RegEntry->Flags & WMIREG_FLAG_TRACE_NOTIFY_MASK;
|
||
device->DeviceObject = RegEntry->DeviceObject;
|
||
device++; Index++;
|
||
}
|
||
RegEntryList = RegEntryList->Flink;
|
||
}
|
||
WmipLeaveSMCritSection();
|
||
|
||
stackSize = WmipServiceDeviceObject->StackSize;
|
||
irp = IoAllocateIrp(stackSize, FALSE);
|
||
|
||
device = (PTRACE_DEVICE) deviceList; // start from first element
|
||
while (--Index >= 0 && irp != NULL) {
|
||
if (device->DeviceObject != NULL) {
|
||
|
||
do {
|
||
IoInitializeIrp(irp, IoSizeOfIrp(stackSize), stackSize);
|
||
IoSetNextIrpStackLocation(irp);
|
||
irpStack = IoGetCurrentIrpStackLocation(irp);
|
||
irpStack->DeviceObject = WmipServiceDeviceObject;
|
||
irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
||
|
||
|
||
if ( !( (device->TraceClass == WMIREG_NOTIFY_TDI_IO) &&
|
||
enableNetwork ) &&
|
||
!( (device->TraceClass == WMIREG_NOTIFY_DISK_IO) &&
|
||
enableDisk ) ) {
|
||
continue;
|
||
}
|
||
|
||
status = WmipForwardWmiIrp(
|
||
irp,
|
||
IRP_MN_SET_TRACE_NOTIFY,
|
||
IoWMIDeviceObjectToProviderId(device->DeviceObject),
|
||
NULL,
|
||
sizeof(NullPtr),
|
||
&NullPtr
|
||
);
|
||
|
||
if (status == STATUS_WMI_TRY_AGAIN) {
|
||
IoFreeIrp(irp);
|
||
stackSize = WmipServiceDeviceObject->StackSize;
|
||
irp = IoAllocateIrp(stackSize, FALSE);
|
||
if (!irp) {
|
||
break;
|
||
}
|
||
}
|
||
} while (status == STATUS_WMI_TRY_AGAIN);
|
||
}
|
||
device++;
|
||
}
|
||
|
||
if (irp) {
|
||
IoFreeIrp(irp);
|
||
}
|
||
ExFreePoolWithTag(deviceList, TRACEPOOLTAG);
|
||
}
|
||
|
||
VOID
|
||
WmipSetTraceNotify(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN ULONG TraceClass,
|
||
IN ULONG Enable
|
||
)
|
||
{
|
||
PIRP irp;
|
||
PVOID NotifyRoutine = NULL;
|
||
NTSTATUS status;
|
||
CCHAR stackSize;
|
||
PIO_STACK_LOCATION irpStack;
|
||
|
||
if (Enable) {
|
||
switch (TraceClass) {
|
||
case WMIREG_NOTIFY_DISK_IO :
|
||
NotifyRoutine = WmipDiskIoNotify;
|
||
break;
|
||
case WMIREG_NOTIFY_TDI_IO :
|
||
NotifyRoutine = WmipTdiIoNotify;
|
||
break;
|
||
default :
|
||
return;
|
||
}
|
||
if (NotifyRoutine == NULL) // trace not enabled, so do not
|
||
return; // send any Irp to enable
|
||
}
|
||
|
||
do {
|
||
stackSize = WmipServiceDeviceObject->StackSize;
|
||
irp = IoAllocateIrp(stackSize, FALSE);
|
||
|
||
if (!irp)
|
||
return;
|
||
|
||
IoSetNextIrpStackLocation(irp);
|
||
irpStack = IoGetCurrentIrpStackLocation(irp);
|
||
irpStack->DeviceObject = WmipServiceDeviceObject;
|
||
status = WmipForwardWmiIrp(
|
||
irp,
|
||
IRP_MN_SET_TRACE_NOTIFY,
|
||
IoWMIDeviceObjectToProviderId(DeviceObject),
|
||
NULL,
|
||
sizeof(NotifyRoutine),
|
||
&NotifyRoutine
|
||
);
|
||
IoFreeIrp(irp);
|
||
} while (status == STATUS_WMI_TRY_AGAIN);
|
||
}
|
||
|
||
//
|
||
// All the following routines are callout or notification routines for
|
||
// generating kernel event traces
|
||
//
|
||
|
||
|
||
NTKERNELAPI
|
||
VOID
|
||
FASTCALL
|
||
WmiTraceProcess(
|
||
IN PEPROCESS Process,
|
||
IN BOOLEAN Create
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This callout routine is called from ps\create.c and ps\psdelete.c.
|
||
|
||
Arguments:
|
||
|
||
Process - PEPROCESS;
|
||
Create - True if intended process is being created.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG Size, LoggerId;
|
||
NTSTATUS Status;
|
||
PCHAR AuxPtr;
|
||
PSYSTEM_TRACE_HEADER Header;
|
||
PVOID BufferResource;
|
||
ULONG SidLength;
|
||
PTOKEN_USER LocalUser = NULL;
|
||
PWMI_PROCESS_INFORMATION ProcessInfo;
|
||
PWMI_LOGGER_CONTEXT LoggerContext;
|
||
PVOID Token;
|
||
|
||
PAGED_CODE();
|
||
|
||
if ((WmipIsLoggerOn(WmipKernelLogger) == NULL) &&
|
||
(WmipIsLoggerOn(WmipEventLogger) == NULL))
|
||
return;
|
||
|
||
Token = PsReferencePrimaryToken(Process);
|
||
if (Token != NULL) {
|
||
Status = SeQueryInformationToken(
|
||
Token,
|
||
TokenUser,
|
||
&LocalUser);
|
||
PsDereferencePrimaryTokenEx (Process, Token);
|
||
} else {
|
||
Status = STATUS_SEVERITY_ERROR;
|
||
}
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
WmipAssert(LocalUser != NULL); // temporary for SE folks
|
||
if (LocalUser != NULL) {
|
||
SidLength = SeLengthSid(LocalUser->User.Sid) + sizeof(TOKEN_USER);
|
||
}
|
||
} else {
|
||
SidLength = sizeof(ULONG);
|
||
LocalUser = NULL;
|
||
}
|
||
|
||
Size = SidLength + FIELD_OFFSET(WMI_PROCESS_INFORMATION, Sid) + sizeof(Process->ImageFileName);
|
||
|
||
for (LoggerId = WmipKernelLogger;;LoggerId = WmipEventLogger) {
|
||
LoggerContext = WmipIsLoggerOn(LoggerId);
|
||
if (LoggerContext != NULL) {
|
||
if (LoggerContext->EnableFlags & EVENT_TRACE_FLAG_PROCESS) {
|
||
Header = WmiReserveWithSystemHeader( LoggerId,
|
||
Size,
|
||
NULL,
|
||
&BufferResource);
|
||
if (Header) {
|
||
if(Create) {
|
||
Header->Packet.HookId = WMI_LOG_TYPE_PROCESS_CREATE;
|
||
} else {
|
||
Header->Packet.HookId = WMI_LOG_TYPE_PROCESS_DELETE;
|
||
}
|
||
ProcessInfo = (PWMI_PROCESS_INFORMATION) (Header + 1);
|
||
|
||
ProcessInfo->PageDirectoryBase = MmGetDirectoryFrameFromProcess(Process);
|
||
ProcessInfo->ProcessId = HandleToUlong(Process->UniqueProcessId);
|
||
ProcessInfo->ParentId = HandleToUlong(Process->InheritedFromUniqueProcessId);
|
||
ProcessInfo->SessionId = MmGetSessionId (Process);
|
||
ProcessInfo->ExitStatus = (Create ? STATUS_SUCCESS : Process->ExitStatus);
|
||
|
||
AuxPtr = (PCHAR) (&ProcessInfo->Sid);
|
||
|
||
if (LocalUser != NULL) {
|
||
RtlCopyMemory(AuxPtr, LocalUser, SidLength);
|
||
} else {
|
||
*((PULONG) AuxPtr) = 0;
|
||
}
|
||
|
||
AuxPtr += SidLength;
|
||
|
||
RtlCopyMemory( AuxPtr,
|
||
&Process->ImageFileName[0],
|
||
sizeof(Process->ImageFileName));
|
||
|
||
WmipReleaseTraceBuffer(BufferResource, LoggerContext);
|
||
}
|
||
|
||
}
|
||
}
|
||
if (LoggerId == WmipEventLogger)
|
||
break;
|
||
}
|
||
if (LocalUser != NULL) {
|
||
ExFreePool(LocalUser);
|
||
}
|
||
}
|
||
|
||
|
||
NTKERNELAPI
|
||
VOID
|
||
WmiTraceThread(
|
||
IN PETHREAD Thread,
|
||
IN PINITIAL_TEB InitialTeb OPTIONAL,
|
||
IN BOOLEAN Create
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This callout routine is called from ps\create.c and ps\psdelete.c.
|
||
It is a PCREATE_THREAD_NOTIFY_ROUTINE.
|
||
|
||
Arguments:
|
||
|
||
Thread - PETHREAD structure
|
||
InitialTeb - PINITIAL_TEB
|
||
Create - True if intended thread is being created.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG LoggerId;
|
||
PSYSTEM_TRACE_HEADER Header;
|
||
PVOID BufferResource;
|
||
PWMI_LOGGER_CONTEXT LoggerContext;
|
||
|
||
PAGED_CODE();
|
||
|
||
if ((WmipIsLoggerOn(WmipKernelLogger) == NULL) &&
|
||
(WmipIsLoggerOn(WmipEventLogger) == NULL)) {
|
||
return;
|
||
}
|
||
|
||
for (LoggerId = WmipKernelLogger;;LoggerId = WmipEventLogger) {
|
||
LoggerContext = WmipIsLoggerOn(LoggerId);
|
||
if (LoggerContext != NULL) {
|
||
if (LoggerContext->EnableFlags & EVENT_TRACE_FLAG_THREAD) {
|
||
if (Create) {
|
||
PWMI_EXTENDED_THREAD_INFORMATION ThreadInfo;
|
||
Header = (PSYSTEM_TRACE_HEADER)
|
||
WmiReserveWithSystemHeader( LoggerId,
|
||
sizeof(WMI_EXTENDED_THREAD_INFORMATION),
|
||
NULL,
|
||
&BufferResource);
|
||
|
||
if (Header) {
|
||
Header->Packet.HookId = WMI_LOG_TYPE_THREAD_CREATE;
|
||
ThreadInfo = (PWMI_EXTENDED_THREAD_INFORMATION) (Header + 1);
|
||
|
||
ThreadInfo->ProcessId = HandleToUlong(Thread->Cid.UniqueProcess);
|
||
ThreadInfo->ThreadId = HandleToUlong(Thread->Cid.UniqueThread);
|
||
ThreadInfo->StackBase = Thread->Tcb.StackBase;
|
||
ThreadInfo->StackLimit = Thread->Tcb.StackLimit;
|
||
|
||
if (InitialTeb != NULL) {
|
||
if ((InitialTeb->OldInitialTeb.OldStackBase == NULL) &&
|
||
(InitialTeb->OldInitialTeb.OldStackLimit == NULL)) {
|
||
ThreadInfo->UserStackBase = InitialTeb->StackBase;
|
||
ThreadInfo->UserStackLimit = InitialTeb->StackLimit;
|
||
} else {
|
||
ThreadInfo->UserStackBase = InitialTeb->OldInitialTeb.OldStackBase;
|
||
ThreadInfo->UserStackLimit = InitialTeb->OldInitialTeb.OldStackLimit;
|
||
}
|
||
} else {
|
||
ThreadInfo->UserStackBase = NULL;
|
||
ThreadInfo->UserStackLimit = NULL;
|
||
}
|
||
|
||
ThreadInfo->StartAddr = (Thread)->StartAddress;
|
||
ThreadInfo->Win32StartAddr = (Thread)->Win32StartAddress;
|
||
ThreadInfo->WaitMode = -1;
|
||
|
||
WmipReleaseTraceBuffer(BufferResource, LoggerContext);
|
||
}
|
||
} else {
|
||
PWMI_THREAD_INFORMATION ThreadInfo;
|
||
Header = (PSYSTEM_TRACE_HEADER)
|
||
WmiReserveWithSystemHeader( LoggerId,
|
||
sizeof(WMI_THREAD_INFORMATION),
|
||
NULL,
|
||
&BufferResource);
|
||
|
||
if (Header) {
|
||
Header->Packet.HookId = WMI_LOG_TYPE_THREAD_DELETE;
|
||
ThreadInfo = (PWMI_THREAD_INFORMATION) (Header + 1);
|
||
ThreadInfo->ProcessId = HandleToUlong((Thread)->Cid.UniqueProcess);
|
||
ThreadInfo->ThreadId = HandleToUlong((Thread)->Cid.UniqueThread);
|
||
WmipReleaseTraceBuffer(BufferResource, LoggerContext);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (LoggerId == WmipEventLogger) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
FASTCALL
|
||
WmipTracePageFault(
|
||
IN NTSTATUS Status,
|
||
IN PVOID VirtualAddress,
|
||
IN PVOID TrapFrame
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This callout routine is called from mm\mmfault.c.
|
||
It is a PPAGE_FAULT_NOTIFY_ROUTINE
|
||
|
||
Arguments:
|
||
|
||
Status Used to tell the type of fault
|
||
VirtualAddress The virtual address responsible for the fault
|
||
TrapFrame Trap Frame
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
UCHAR Type;
|
||
PVOID *AuxInfo;
|
||
PSYSTEM_TRACE_HEADER Header;
|
||
PVOID BufferResource;
|
||
PWMI_LOGGER_CONTEXT LoggerContext;
|
||
|
||
if (Status == STATUS_PAGE_FAULT_DEMAND_ZERO)
|
||
Type = EVENT_TRACE_TYPE_MM_DZF;
|
||
else if (Status == STATUS_PAGE_FAULT_TRANSITION)
|
||
Type = EVENT_TRACE_TYPE_MM_TF;
|
||
else if (Status == STATUS_PAGE_FAULT_COPY_ON_WRITE)
|
||
Type = EVENT_TRACE_TYPE_MM_COW;
|
||
else if (Status == STATUS_PAGE_FAULT_PAGING_FILE)
|
||
Type = EVENT_TRACE_TYPE_MM_HPF;
|
||
else if (Status == STATUS_PAGE_FAULT_GUARD_PAGE)
|
||
Type = EVENT_TRACE_TYPE_MM_GPF;
|
||
else {
|
||
#if DBG
|
||
DbgPrintEx(DPFLTR_WMILIB_ID,
|
||
DPFLTR_INFO_LEVEL,
|
||
"WmipTracePageFault: Skipping fault %X\n",
|
||
Status);
|
||
#endif
|
||
return;
|
||
}
|
||
|
||
LoggerContext = WmipIsLoggerOn(WmipKernelLogger);
|
||
if (LoggerContext == NULL) {
|
||
return;
|
||
}
|
||
|
||
Header = (PSYSTEM_TRACE_HEADER)
|
||
WmiReserveWithSystemHeader(
|
||
WmipKernelLogger,
|
||
2 * sizeof(PVOID),
|
||
NULL,
|
||
&BufferResource);
|
||
|
||
if (Header == NULL)
|
||
return;
|
||
Header->Packet.Group = (UCHAR) (EVENT_TRACE_GROUP_MEMORY >> 8);
|
||
Header->Packet.Type = Type;
|
||
|
||
AuxInfo = (PVOID*) ((PCHAR)Header + sizeof(SYSTEM_TRACE_HEADER));
|
||
|
||
AuxInfo[0] = VirtualAddress;
|
||
AuxInfo[1] = 0;
|
||
if (TrapFrame != NULL) {
|
||
|
||
#ifdef _X86_
|
||
|
||
AuxInfo[1] = (PVOID) ((PKTRAP_FRAME)TrapFrame)->Eip;
|
||
|
||
#endif
|
||
|
||
#ifdef _IA64_
|
||
|
||
AuxInfo[1] = (PVOID) ((PKTRAP_FRAME)TrapFrame)->StIIP;
|
||
#endif
|
||
|
||
#ifdef _AMD64_
|
||
|
||
AuxInfo[1] = (PVOID) ((PKTRAP_FRAME)TrapFrame)->Rip;
|
||
|
||
#endif
|
||
|
||
}
|
||
WmipReleaseTraceBuffer(BufferResource, LoggerContext);
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
WmipTraceNetwork(
|
||
IN ULONG GroupType, // Group/type for the event
|
||
IN PVOID EventInfo, // Event data as defined in MOF
|
||
IN ULONG EventInfoLen, // Length of the event data
|
||
IN PVOID Reserved // not used
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This callout routine is called from tcpip.sys to log a network event.
|
||
|
||
Arguments:
|
||
|
||
GroupType a ULONG key to indicate the action
|
||
|
||
EventInfo a pointer to contiguous memory containing information
|
||
to be attached to event trace
|
||
|
||
EventInfoLen length of EventInfo
|
||
|
||
Reserved Not used.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PPERFINFO_TRACE_HEADER Header;
|
||
PWMI_BUFFER_HEADER BufferResource;
|
||
PWMI_LOGGER_CONTEXT LoggerContext;
|
||
|
||
LoggerContext = WmipLoggerContext[WmipKernelLogger];
|
||
Header = WmiReserveWithPerfHeader(EventInfoLen, &BufferResource);
|
||
if (Header == NULL) {
|
||
return;
|
||
}
|
||
|
||
Header->Packet.HookId = (USHORT) GroupType;
|
||
RtlCopyMemory((PUCHAR)Header + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data),
|
||
EventInfo,
|
||
EventInfoLen);
|
||
|
||
WmipReleaseTraceBuffer(BufferResource, LoggerContext);
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
WmipTraceIo(
|
||
IN ULONG DiskNumber,
|
||
IN PIRP Irp,
|
||
IN PVOID Counters // use PDISK_PERFORMANCE if we need it
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This callout routine is called from DiskPerf
|
||
It is a PPHYSICAL_DISK_IO_NOTIFY_ROUTINE
|
||
|
||
Arguments:
|
||
|
||
DiskNumber The disk number assigned by DiskPerf
|
||
CurrentIrpStack The Irp stack location that DiskPerf is at
|
||
Irp The Irp that is being passed through DiskPerf
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION CurrentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
|
||
WMI_DISKIO_READWRITE *IoTrace;
|
||
ULONG Size;
|
||
PLARGE_INTEGER IoResponse;
|
||
PSYSTEM_TRACE_HEADER Header;
|
||
PVOID BufferResource;
|
||
PWMI_LOGGER_CONTEXT LoggerContext;
|
||
|
||
UNREFERENCED_PARAMETER(Counters);
|
||
|
||
Size = sizeof(struct _WMI_DISKIO_READWRITE);
|
||
|
||
LoggerContext = WmipIsLoggerOn(WmipKernelLogger);
|
||
if (LoggerContext == NULL) {
|
||
return;
|
||
}
|
||
Header = (PSYSTEM_TRACE_HEADER)
|
||
WmiReserveWithSystemHeader(
|
||
WmipKernelLogger,
|
||
Size,
|
||
Irp->Tail.Overlay.Thread,
|
||
&BufferResource);
|
||
|
||
if (Header == NULL)
|
||
return;
|
||
|
||
Header->Packet.Group = (UCHAR) (EVENT_TRACE_GROUP_IO >> 8);
|
||
if (CurrentIrpStack->MajorFunction == IRP_MJ_READ)
|
||
Header->Packet.Type = EVENT_TRACE_TYPE_IO_READ;
|
||
else
|
||
Header->Packet.Type = EVENT_TRACE_TYPE_IO_WRITE;
|
||
|
||
IoTrace = (struct _WMI_DISKIO_READWRITE *)
|
||
((PCHAR) Header + sizeof(SYSTEM_TRACE_HEADER));
|
||
IoResponse = (PLARGE_INTEGER) &CurrentIrpStack->Parameters.Read;
|
||
|
||
IoTrace->DiskNumber = DiskNumber;
|
||
IoTrace->IrpFlags = Irp->Flags;
|
||
IoTrace->Size = (ULONG) Irp->IoStatus.Information;
|
||
IoTrace->ByteOffset = CurrentIrpStack->Parameters.Read.ByteOffset.QuadPart;
|
||
|
||
if (IoResponse->HighPart == 0) {
|
||
IoTrace->ResponseTime = IoResponse->LowPart;
|
||
} else {
|
||
IoTrace->ResponseTime = 0xFFFFFFFF;
|
||
}
|
||
IoTrace->HighResResponseTime = IoResponse->QuadPart;
|
||
|
||
if (WmipTraceFileFlag) {
|
||
if (Irp->Flags & IRP_ASSOCIATED_IRP) {
|
||
IoTrace->FileObject = Irp->AssociatedIrp.MasterIrp->Tail.Overlay.OriginalFileObject;
|
||
} else {
|
||
IoTrace->FileObject = Irp->Tail.Overlay.OriginalFileObject;
|
||
}
|
||
}
|
||
WmipReleaseTraceBuffer(BufferResource, LoggerContext);
|
||
|
||
if (WmipTraceFileFlag) { // File tracing required
|
||
PKAPC apc;
|
||
PFILE_OBJECT fileObject = IoTrace->FileObject;
|
||
PFILE_OBJECT *fileTable;
|
||
ULONG i, fileIndex;
|
||
PFILE_OBJECT lastFile = NULL;
|
||
|
||
if (!fileObject) {
|
||
return;
|
||
}
|
||
if (fileObject->FileName.Length == 0) {
|
||
return;
|
||
}
|
||
|
||
if (!Irp->Tail.Overlay.Thread) {
|
||
return;
|
||
}
|
||
|
||
fileTable = (PFILE_OBJECT *) WmipFileTable;
|
||
if (fileTable == NULL)
|
||
return;
|
||
|
||
|
||
//
|
||
// Cannot use list for regular LRU because the list needs to be
|
||
// protected by spinlock. Use a simple array instead. We know we
|
||
// can run into collision but can afford to have some entries
|
||
// thrown away
|
||
//
|
||
// if (fileObject == fileTable[WmipFileIndex]) {
|
||
// return; // just saw it, so return
|
||
// }
|
||
fileIndex = WmipFileIndex;
|
||
for (i=0; i<MAX_FILE_TABLE_SIZE; i++) {
|
||
if (fileTable[i] == NULL) {
|
||
fileTable[i] = fileObject;
|
||
goto TraceFileName;
|
||
}
|
||
else if (fileTable[i] == fileObject) {
|
||
if (i <= MAX_FILE_TABLE_SIZE/2)
|
||
return;
|
||
|
||
lastFile = fileTable[fileIndex];
|
||
fileTable[fileIndex] = fileObject;
|
||
fileTable[i] = lastFile;
|
||
if (++WmipFileIndex >= MAX_FILE_TABLE_SIZE/2) {
|
||
WmipFileIndex = 0;
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
|
||
fileTable[fileIndex] = fileObject;
|
||
if (++WmipFileIndex >= MAX_FILE_TABLE_SIZE/2) {
|
||
WmipFileIndex = 0;
|
||
}
|
||
|
||
TraceFileName:
|
||
// could not find it. Have to pay the price to get it
|
||
//
|
||
apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TRACEPOOLTAG);
|
||
if (!apc)
|
||
return;
|
||
|
||
ObReferenceObjectByPointer (
|
||
fileObject,
|
||
0L,
|
||
NULL,
|
||
KernelMode
|
||
);
|
||
|
||
KeInitializeApc (apc,
|
||
&Irp->Tail.Overlay.Thread->Tcb, Irp->ApcEnvironment,
|
||
(PKKERNEL_ROUTINE) WmipTraceFile,
|
||
NULL, // rundown routine for thread termination
|
||
NULL, // normal routine at IRQL0
|
||
KernelMode,
|
||
NULL);
|
||
if (!KeInsertQueueApc (apc, fileObject, NULL, 0)) {
|
||
ExFreePool (apc);
|
||
ObDereferenceObject(fileObject);
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
VOID WmipTraceFile(
|
||
IN PKAPC Apc,
|
||
IN PKNORMAL_ROUTINE *NormalRoutine,
|
||
IN PVOID *NormalContext,
|
||
IN PVOID *SystemArgument1,
|
||
IN PVOID *SystemArgument2
|
||
)
|
||
{
|
||
ULONG len;
|
||
PFILE_OBJECT fileObject = (PFILE_OBJECT) *SystemArgument1;
|
||
PUNICODE_STRING fileName;
|
||
PPERFINFO_TRACE_HEADER Header;
|
||
PWMI_BUFFER_HEADER BufferResource;
|
||
PUCHAR AuxPtr;
|
||
PWMI_LOGGER_CONTEXT LoggerContext;
|
||
|
||
UNREFERENCED_PARAMETER( NormalRoutine );
|
||
UNREFERENCED_PARAMETER( NormalContext );
|
||
UNREFERENCED_PARAMETER( SystemArgument2 );
|
||
|
||
PAGED_CODE();
|
||
|
||
if (!fileObject) { // should not happen
|
||
ExFreePool(Apc);
|
||
return;
|
||
}
|
||
|
||
if (!WmipTraceFileFlag)
|
||
return;
|
||
fileName = &fileObject->FileName;
|
||
len = fileName->Length;
|
||
|
||
if (len > (0XFFFF - sizeof(PFILE_OBJECT) - sizeof(WCHAR)))
|
||
len = 0; // allow only 64K max
|
||
if (len > 0 && fileName->Buffer != NULL) {
|
||
|
||
LoggerContext = WmipLoggerContext[WmipKernelLogger];
|
||
Header = WmiReserveWithPerfHeader(
|
||
sizeof(PFILE_OBJECT) + len + sizeof(WCHAR),
|
||
&BufferResource);
|
||
if (Header == NULL)
|
||
return;
|
||
Header->Packet.Group = (UCHAR) (EVENT_TRACE_GROUP_FILE >> 8);
|
||
Header->Packet.Type = EVENT_TRACE_TYPE_INFO;
|
||
AuxPtr = (PUCHAR)Header + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
|
||
|
||
*((PFILE_OBJECT*)AuxPtr) = fileObject;
|
||
AuxPtr += sizeof(PFILE_OBJECT);
|
||
|
||
RtlCopyMemory(AuxPtr, fileName->Buffer, len);
|
||
AuxPtr += len;
|
||
*((PWCHAR) AuxPtr) = UNICODE_NULL; // always put a NULL
|
||
|
||
WmipReleaseTraceBuffer(BufferResource, LoggerContext);
|
||
}
|
||
|
||
ObDereferenceObject(fileObject);
|
||
ExFreePool(Apc);
|
||
}
|
||
|
||
VOID
|
||
WmipTraceLoadImage(
|
||
IN PUNICODE_STRING ImageName,
|
||
IN HANDLE ProcessId,
|
||
IN PIMAGE_INFO ImageInfo
|
||
)
|
||
{
|
||
PSYSTEM_TRACE_HEADER Header;
|
||
PUCHAR AuxInfo;
|
||
PVOID BufferResource;
|
||
ULONG Length, LoggerId;
|
||
PWMI_LOGGER_CONTEXT LoggerContext;
|
||
|
||
PAGED_CODE();
|
||
UNREFERENCED_PARAMETER(ProcessId);
|
||
|
||
if ((WmipIsLoggerOn(WmipKernelLogger) == NULL) &&
|
||
(WmipIsLoggerOn(WmipEventLogger) == NULL))
|
||
return;
|
||
if (ImageName == NULL)
|
||
return;
|
||
Length = ImageName->Length;
|
||
if ((Length == 0) || (ImageName->Buffer == NULL)) {
|
||
return;
|
||
}
|
||
|
||
if (Length > (0XFFFF - sizeof(PVOID) - sizeof(SIZE_T) - sizeof(WCHAR)))
|
||
return;
|
||
|
||
for (LoggerId = WmipKernelLogger;; LoggerId = WmipEventLogger) {
|
||
LoggerContext = WmipIsLoggerOn(LoggerId);
|
||
if (LoggerContext != NULL) {
|
||
if (LoggerContext->EnableFlags & EVENT_TRACE_FLAG_IMAGE_LOAD) {
|
||
PWMI_IMAGELOAD_INFORMATION ImageLoadInfo;
|
||
|
||
Header = WmiReserveWithSystemHeader(
|
||
LoggerId,
|
||
FIELD_OFFSET (WMI_IMAGELOAD_INFORMATION, FileName) + Length + sizeof(WCHAR),
|
||
NULL,
|
||
&BufferResource);
|
||
|
||
if (Header != NULL) {
|
||
Header->Packet.HookId = WMI_LOG_TYPE_PROCESS_LOAD_IMAGE;
|
||
|
||
ImageLoadInfo = (PWMI_IMAGELOAD_INFORMATION) (Header + 1);
|
||
|
||
ImageLoadInfo->ImageBase = ImageInfo->ImageBase;
|
||
ImageLoadInfo->ImageSize = ImageInfo->ImageSize;
|
||
ImageLoadInfo->ProcessId = HandleToUlong(ProcessId);
|
||
|
||
AuxInfo = (PUCHAR) &(ImageLoadInfo->FileName[0]);
|
||
RtlCopyMemory(AuxInfo, ImageName->Buffer, Length);
|
||
AuxInfo += Length;
|
||
*((PWCHAR) AuxInfo) = UNICODE_NULL; // put a trailing NULL
|
||
|
||
WmipReleaseTraceBuffer(BufferResource, LoggerContext);
|
||
}
|
||
}
|
||
}
|
||
if (LoggerId == WmipEventLogger)
|
||
break;
|
||
}
|
||
PerfInfoFlushProfileCache();
|
||
}
|
||
|
||
VOID
|
||
WmipTraceRegistry(
|
||
IN NTSTATUS Status,
|
||
IN PVOID Kcb,
|
||
IN LONGLONG ElapsedTime,
|
||
IN ULONG Index,
|
||
IN PUNICODE_STRING KeyName,
|
||
IN UCHAR Type
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to trace out registry calls
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PCHAR EventInfo;
|
||
PSYSTEM_TRACE_HEADER Header;
|
||
PVOID BufferResource;
|
||
ULONG len = 0;
|
||
PWMI_LOGGER_CONTEXT LoggerContext;
|
||
|
||
PAGED_CODE();
|
||
|
||
LoggerContext = WmipIsLoggerOn(WmipKernelLogger);
|
||
if (LoggerContext == NULL) {
|
||
return;
|
||
}
|
||
|
||
if( KeyName && KeyName->Buffer ) {
|
||
len += KeyName->Length;
|
||
|
||
if ((len ==0 ) || (KeyName->Buffer[len/sizeof(WCHAR) -1] != 0) ) {
|
||
//
|
||
// make room for NULL terminator
|
||
//
|
||
len += sizeof(WCHAR);
|
||
}
|
||
} else {
|
||
len += sizeof(WCHAR);
|
||
}
|
||
|
||
len += sizeof(PVOID) + sizeof(LONGLONG) + sizeof(ULONG);
|
||
#if defined(_WIN64)
|
||
len += sizeof(LONG64);
|
||
#else
|
||
len += sizeof(NTSTATUS);
|
||
#endif
|
||
|
||
if (len > 0xFFFF) // 64K bytes max
|
||
Header = NULL;
|
||
else {
|
||
Header = (PSYSTEM_TRACE_HEADER)
|
||
WmiReserveWithSystemHeader(
|
||
WmipKernelLogger,
|
||
len,
|
||
NULL,
|
||
&BufferResource);
|
||
}
|
||
if (Header == NULL)
|
||
return;
|
||
Header->Packet.Group = (UCHAR) (EVENT_TRACE_GROUP_REGISTRY >> 8);
|
||
Header->Packet.Type = Type;
|
||
|
||
EventInfo = (PCHAR) ((PCHAR) Header + sizeof(SYSTEM_TRACE_HEADER));
|
||
#if defined(_WIN64)
|
||
*((LONG64 *)EventInfo) = (LONG64)Status;
|
||
EventInfo += sizeof(LONG64);
|
||
#else
|
||
*((NTSTATUS *)EventInfo) = Status;
|
||
EventInfo += sizeof(NTSTATUS);
|
||
#endif
|
||
*((PVOID *)EventInfo) = Kcb;
|
||
EventInfo += sizeof(PVOID);
|
||
*((LONGLONG *)EventInfo) = ElapsedTime;
|
||
EventInfo += sizeof(LONGLONG);
|
||
*((ULONG *)EventInfo) = Index;
|
||
EventInfo += sizeof(ULONG);
|
||
|
||
len -= (sizeof(HANDLE) + sizeof(LONGLONG) + sizeof(ULONG) );
|
||
#if defined(_WIN64)
|
||
len -= sizeof(LONG64);
|
||
#else
|
||
len -= sizeof(NTSTATUS);
|
||
#endif
|
||
|
||
if( KeyName && KeyName->Buffer ) {
|
||
RtlCopyMemory(EventInfo, KeyName->Buffer, len - sizeof(WCHAR));
|
||
}
|
||
|
||
((PWCHAR)EventInfo)[len/sizeof(WCHAR) -1] = UNICODE_NULL;
|
||
|
||
WmipReleaseTraceBuffer(BufferResource, LoggerContext);
|
||
}
|
||
|
||
VOID
|
||
FASTCALL
|
||
WmiTraceContextSwap (
|
||
IN PETHREAD OldEThread,
|
||
IN PETHREAD NewEThread )
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to trace context swap
|
||
operations. It is called directly from the
|
||
context swap procedure while the context swap
|
||
lock is being held, so it is critical that this
|
||
routine not take any locks.
|
||
|
||
Assumptions:
|
||
- This routine will only be called from the ContextSwap routine
|
||
- This routine will always be called at IRQL >= DISPATCH_LEVEL
|
||
- This routine will only be called when the PPerfGlobalGroupMask
|
||
is not equal to null, and the context swap flag is set within
|
||
the structure to which PPerfGlobalGroupMask points to,
|
||
and the kernel's WMI_LOGGER_CONTEXT struct has been fully initialized.
|
||
- The Wmi kernel WMI_LOGGER_CONTEXT object, as well as all buffers
|
||
it allocates are allocated from nonpaged pool. All Wmi globals
|
||
that we access are also in nonpaged memory.
|
||
- This code has been locked into paged memory when the logger started
|
||
- The logger context reference count has been incremented via the
|
||
InterlockedIncrement() operation in WmipReferenceLogger(WmipKernelLogger)
|
||
by our start code.
|
||
|
||
|
||
Arguments:
|
||
OldThread - ptr to ETHREAD object of thread
|
||
being swapped out
|
||
NewThread - ptr to ETHREAD object of thread
|
||
being swapped in
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
UCHAR CurrentProcessor;
|
||
PWMI_BUFFER_HEADER Buffer;
|
||
PPERFINFO_TRACE_HEADER EventHeader;
|
||
SIZE_T EventSize;
|
||
PWMI_CONTEXTSWAP ContextSwapData;
|
||
|
||
//
|
||
// Figure out which processor we are running on
|
||
//
|
||
CurrentProcessor = (UCHAR)KeGetCurrentProcessorNumber();
|
||
|
||
//
|
||
// If we currently have no context swap buffer for this processor
|
||
// then we need to grab one from the ETW Free list.
|
||
//
|
||
Buffer = WmipContextSwapProcessorBuffers[CurrentProcessor];
|
||
|
||
if (Buffer == NULL) {
|
||
|
||
Buffer = WmipPopFreeContextSwapBuffer(
|
||
CurrentProcessor);
|
||
|
||
if( Buffer == NULL ) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// We have a legitimate buffer, so now we
|
||
// set it as this processor's current cxtswap buffer
|
||
//
|
||
WmipContextSwapProcessorBuffers[CurrentProcessor] = Buffer;
|
||
}
|
||
|
||
//
|
||
// Compute the pointers to our event structures within the buffer
|
||
// At this point, we will always have enough space in the buffer for
|
||
// this event. We check for a full buffer after we fill out the event
|
||
//
|
||
EventHeader = (PPERFINFO_TRACE_HEADER)( (SIZE_T)Buffer
|
||
+ (SIZE_T)Buffer->CurrentOffset);
|
||
|
||
ContextSwapData = (PWMI_CONTEXTSWAP)( (SIZE_T)EventHeader
|
||
+ (SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
|
||
|
||
EventSize = sizeof(WMI_CONTEXTSWAP)
|
||
+ FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
|
||
|
||
|
||
//
|
||
// Fill out the event header
|
||
//
|
||
EventHeader->Marker = PERFINFO_TRACE_MARKER;
|
||
EventHeader->Packet.Size = (USHORT) EventSize;
|
||
EventHeader->Packet.HookId = PERFINFO_LOG_TYPE_CONTEXTSWAP;
|
||
PerfTimeStamp(EventHeader->SystemTime);
|
||
|
||
//
|
||
// Assert that the event size is at alligned correctly
|
||
//
|
||
ASSERT( EventSize % WMI_CTXSWAP_EVENTSIZE_ALIGNMENT == 0);
|
||
|
||
//
|
||
// Fill out the event data struct for context swap
|
||
//
|
||
ContextSwapData->NewThreadId = HandleToUlong(NewEThread->Cid.UniqueThread);
|
||
ContextSwapData->OldThreadId = HandleToUlong(OldEThread->Cid.UniqueThread);
|
||
|
||
ContextSwapData->NewThreadPriority = NewEThread->Tcb.Priority;
|
||
ContextSwapData->OldThreadPriority = OldEThread->Tcb.Priority;
|
||
ContextSwapData->NewThreadQuantum = NewEThread->Tcb.Quantum;
|
||
ContextSwapData->OldThreadQuantum = OldEThread->Tcb.Quantum;
|
||
|
||
ContextSwapData->OldThreadWaitReason= OldEThread->Tcb.WaitReason;
|
||
ContextSwapData->OldThreadWaitMode = OldEThread->Tcb.WaitMode;
|
||
ContextSwapData->OldThreadState = OldEThread->Tcb.State;
|
||
|
||
ContextSwapData->OldThreadIdealProcessor =
|
||
OldEThread->Tcb.IdealProcessor;
|
||
|
||
//
|
||
// Increment the offset. Don't need synchronization here because
|
||
// IRQL >= DISPATCH_LEVEL.
|
||
//
|
||
Buffer->CurrentOffset += (ULONG)EventSize;
|
||
|
||
//
|
||
// Check if the buffer is full by taking the difference between
|
||
// the buffer's maximum offset and the current offset.
|
||
//
|
||
if ((Buffer->Offset - Buffer->CurrentOffset) <= EventSize) {
|
||
|
||
//
|
||
// Push the full buffer onto the FlushList.
|
||
//
|
||
WmipPushDirtyContextSwapBuffer(CurrentProcessor, Buffer);
|
||
|
||
//
|
||
// Zero out the processor buffer pointer so that when we next come
|
||
// into the trace code, we know to grab another one.
|
||
//
|
||
WmipContextSwapProcessorBuffers[CurrentProcessor] = NULL;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
FASTCALL
|
||
WmiStartContextSwapTrace
|
||
(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Allocates the memory to track the per-processor buffers
|
||
used by context swap tracing. "locks" the logger by incrementing
|
||
the logger context reference count by one.
|
||
|
||
Assumptions:
|
||
- This function will not run at DISPATCH or higher
|
||
- The kernel logger context mutex has been acquired before entering
|
||
this function.
|
||
|
||
Calling Functions:
|
||
- PerfInfoStartLog
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// Only used in checked builds - asserts if this code is called with
|
||
// Irql > APC_LEVEL.
|
||
//
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// We must make sure we manage this ref count
|
||
// before making any references to the logger context struct to ensure
|
||
// that Wmi does not free the LoggerContext structure while we are using it
|
||
//
|
||
WmipReferenceLogger(WmipKernelLogger);
|
||
|
||
//
|
||
// Set the pointers to our buffers to NULL, indicating to the trace event
|
||
// code that a buffer needs to be acquired.
|
||
//
|
||
RtlZeroMemory(
|
||
WmipContextSwapProcessorBuffers,
|
||
sizeof(PWMI_BUFFER_HEADER)*MAXIMUM_PROCESSORS);
|
||
}
|
||
|
||
VOID
|
||
FASTCALL
|
||
WmiStopContextSwapTrace
|
||
(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Forces a context swap on a processor by jumping onto it.
|
||
Once a context swap has occured on a processor after the context
|
||
swap tracing flag has been disabled, we are guaranteed that the
|
||
buffer associated with that processor is not in use. It is then
|
||
safe to place that buffer on the flush list.
|
||
|
||
Assumptions:
|
||
- This function will not run at DISPATCH
|
||
- The kernel logger context mutex was acquired before this function
|
||
was called.
|
||
|
||
Calling Functions:
|
||
-PerfInfoStopLog
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
None; if we fail here there's nothing we can do anyway.
|
||
|
||
--*/
|
||
{
|
||
PKTHREAD ThisThread;
|
||
KAFFINITY OriginalAffinity;
|
||
UCHAR i;
|
||
PWMI_LOGGER_CONTEXT LoggerContext;
|
||
|
||
//
|
||
// Only used in checked builds - asserts if this code is called with
|
||
// Irql > APC_LEVEL.
|
||
//
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Remember the original thread affinity
|
||
//
|
||
ThisThread = KeGetCurrentThread();
|
||
OriginalAffinity = ThisThread->Affinity;
|
||
|
||
//
|
||
// Get the kernel logger context- this should never fail.
|
||
// If we can't get the logger context, then we have nowhere
|
||
// to flush buffers and we might as well stop here.
|
||
//
|
||
LoggerContext = WmipLoggerContext[WmipKernelLogger];
|
||
|
||
if( !WmipIsValidLogger( LoggerContext ) ) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Loop through all processors and place their buffers on the flush list
|
||
// This would probably break if the number of processors were decreased in
|
||
// the middle of the trace.
|
||
//
|
||
for(i=0; i<KeNumberProcessors; i++) {
|
||
|
||
//
|
||
// Set the hard processor affinity to 1 << i
|
||
// This effectively jumps onto the processor
|
||
//
|
||
KeSetAffinityThread ( ThisThread, AFFINITY_MASK(i) );
|
||
|
||
//
|
||
// Check to make sure this processor even has a buffer,
|
||
// if it doesn't, then next loop
|
||
//
|
||
if(WmipContextSwapProcessorBuffers[i] == NULL) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Release the buffer to the flush list
|
||
//
|
||
WmipPushDirtyContextSwapBuffer(i, WmipContextSwapProcessorBuffers[i]);
|
||
WmipContextSwapProcessorBuffers[i] = NULL;
|
||
}
|
||
|
||
//
|
||
// We must make sure we manage this ref count
|
||
// before making any references to the logger context struct to ensure
|
||
// that Wmi does not free the LoggerContext structure while we are using it
|
||
//
|
||
WmipDereferenceLogger(WmipKernelLogger);
|
||
|
||
//
|
||
// Set our Affinity back to normal
|
||
//
|
||
KeSetAffinityThread( ThisThread, OriginalAffinity);
|
||
|
||
return;
|
||
}
|
||
|
||
PWMI_LOGGER_CONTEXT
|
||
FASTCALL
|
||
WmipIsLoggerOn(
|
||
IN ULONG LoggerId
|
||
)
|
||
{
|
||
PWMI_LOGGER_CONTEXT LoggerContext;
|
||
|
||
if (LoggerId > MAXLOGGERS)
|
||
return NULL;
|
||
LoggerContext = WmipLoggerContext[LoggerId];
|
||
if (!WmipIsValidLogger(LoggerContext))
|
||
return NULL;
|
||
if (LoggerContext->CollectionOn)
|
||
return LoggerContext;
|
||
return NULL;
|
||
}
|