/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


Module Name:

    traceapi.c

Abstract:

    This is the source file that implements the published routines of
    the performance event tracing and logging facility. These routines are
    be declared in ntos\inc\wmi.h

--*/

#include "wmikmp.h"
#include <ntos.h>
#include <evntrace.h>

#include <wmi.h>
#include "tracep.h"

extern SIZE_T MmMaximumNonPagedPoolInBytes;

#pragma alloc_text(PAGE, WmiStartTrace)
#pragma alloc_text(PAGE, WmiQueryTrace)
#pragma alloc_text(PAGE, WmiStopTrace)
#pragma alloc_text(PAGE, WmiUpdateTrace)
#pragma alloc_text(PAGE, WmiSetTraceBufferCallback)
#pragma alloc_text(PAGE, WmiFlushTrace)
#pragma alloc_text(PAGE, WmiQueryTraceInformation)
#pragma alloc_text(PAGEWMI, WmiTraceKernelEvent)
#pragma alloc_text(PAGEWMI, WmiTraceUserMessage)
#pragma alloc_text(PAGEWMI, WmiSetMark)

//
// Trace Control APIs
//

NTKERNELAPI
NTSTATUS
WmiStartTrace(
    IN OUT PWMI_LOGGER_INFORMATION LoggerInfo
    )
/*++

Routine Description:

    This routine is used to create and start an event tracing session.
    NOTE: A special instance (KERNEL_LOGGER) is reserved exclusively for
    logging kernel tracing.

    To turn on KERNEL_LOGGER, LoggerInfo->Wnode.Guid should be set to
    SystemTraceControlGuid, and sufficient space must be provided in
    LoggerInfo->LoggerName.

    To turn on other loggers, simply provide a name in LoggerName. The
    logger id will be returned.

Arguments:

    LoggerInfo     a pointer to the structure for the logger's control
                    and status information

Return Value:

    The status of performing the action requested.

--*/

{
    NTSTATUS status;
    PWCHAR LogFileName = NULL;
    HANDLE FileHandle = NULL;
    ULONG DelayOpen;

    PAGED_CODE();

    if (LoggerInfo == NULL)
        return STATUS_INVALID_PARAMETER;

    if (LoggerInfo->Wnode.BufferSize < sizeof(WMI_LOGGER_INFORMATION))
        return STATUS_INVALID_BUFFER_SIZE;

    //
    // We assume that the caller is always kernel mode
    // First, we try and see it is a delay create.
    // If not, if we can even open the file
    //
    DelayOpen = LoggerInfo->LogFileMode & EVENT_TRACE_DELAY_OPEN_FILE_MODE;

    if (!DelayOpen) {
        if (LoggerInfo->LogFileName.Buffer != NULL) { // && !delay_create
            status = WmipCreateNtFileName(
                        LoggerInfo->LogFileName.Buffer,
                        &LogFileName);
            if (!NT_SUCCESS(status))
                return status;
            status = WmipCreateDirectoryFile(LogFileName, 
                                            FALSE, 
                                            &FileHandle,
                                            LoggerInfo->LogFileMode & EVENT_TRACE_FILE_MODE_APPEND);
            if (LogFileName != NULL) {
                ExFreePool(LogFileName);
            }
            if (!NT_SUCCESS(status)) {
                return status;
            }
            ZwClose(FileHandle);
        }
    }

    status = WmipStartLogger(LoggerInfo);
    if (NT_SUCCESS(status)) {
        status = WmiFlushTrace(LoggerInfo);
    }
    return status;
}


NTKERNELAPI
NTSTATUS
WmiQueryTrace(
    IN OUT PWMI_LOGGER_INFORMATION LoggerInfo
    )
/*++

Routine Description:

    This routine is called to query the status of a tracing session.
    Caller must pass in either the Logger Name or a valid Logger Id/Handle.

Arguments:

    LoggerInfo     a pointer to the structure for the logger's control
                    and status information

Return Value:

    The status of performing the action requested.

--*/

{
    PAGED_CODE();

    if (LoggerInfo == NULL)
        return STATUS_INVALID_PARAMETER;
    return WmipQueryLogger(LoggerInfo, NULL);
}


NTKERNELAPI
NTSTATUS
WmiStopTrace(
    IN PWMI_LOGGER_INFORMATION LoggerInfo
    )
/*++

Routine Description:

    It is called by WmipIoControl in wmi.c, with IOCTL_WMI_STOP_LOGGER
    to stop an instance of the logger. If the logger is the kernel logger,
    it will also turn off kernel tracing and unlock the routines previously
    locked. It will also free all the context of the logger.
    Calls StopLoggerInstance to do the actual work.

Arguments:

    LoggerInfo     a pointer to the structure for the logger's control
                    and status information

Return Value:

    The status of performing the action requested.

--*/

{
    PWMI_LOGGER_CONTEXT LoggerContext = NULL;
    NTSTATUS        Status;
    LARGE_INTEGER   TimeOut = {(ULONG)(-200 * 1000 * 1000 * 10), -1};
    ACCESS_MASK     DesiredAccess = TRACELOG_GUID_ENABLE;
    ULONG           LoggerId;
#if DBG
    LONG            RefCount;
#endif

    PAGED_CODE();

    if (LoggerInfo == NULL)
        return STATUS_INVALID_PARAMETER;

    TraceDebug((1, "WmiStopTrace: %d\n",
                    LoggerInfo->Wnode.HistoricalContext));
#if DBG
    Status = WmipVerifyLoggerInfo(LoggerInfo, &LoggerContext, "WmiStopTrace");
#else
    Status = WmipVerifyLoggerInfo(LoggerInfo, &LoggerContext);
#endif

    if (!NT_SUCCESS(Status) || (LoggerContext == NULL))
        return Status;

    LoggerId = LoggerContext->LoggerId;
    TraceDebug((1, "WmiStopTrace: Stopping %X %d slot %X\n",
                    LoggerContext, LoggerId, WmipLoggerContext[LoggerId]));

    if (LoggerContext->KernelTraceOn)
        DesiredAccess |= TRACELOG_ACCESS_KERNEL_LOGGER;

    Status = WmipCheckGuidAccess(
                &LoggerContext->InstanceGuid,
                DesiredAccess,
                EtwpDefaultTraceSecurityDescriptor
                );
    if (!NT_SUCCESS(Status)) {
        InterlockedDecrement(&LoggerContext->MutexCount);
        TraceDebug((1, "WmiStopTrace: Release mutex1 %d %d\n",
            LoggerId, LoggerContext->MutexCount));
        WmipReleaseMutex(&LoggerContext->LoggerMutex);

#if DBG
        RefCount =
#endif
        WmipDereferenceLogger(LoggerId);
        TraceDebug((1, "WmiStopTrace: Status1=%X %d %d->%d\n",
                        Status, LoggerId, RefCount+1, RefCount));
        return Status;
    }

    //
    // Reset the Event inside the mutex to be sure
    // before waiting on it.

    KeResetEvent(&LoggerContext->FlushEvent);

    Status = WmipStopLoggerInstance (LoggerContext);

    InterlockedDecrement(&LoggerContext->MutexCount);
    TraceDebug((1, "WmiStopTrace: Release mutex3 %d %d\n",
        LoggerId, LoggerContext->MutexCount));
    WmipReleaseMutex(&LoggerContext->LoggerMutex); // Let others in

    if (NT_SUCCESS(Status)) {
        if (LoggerId == WmipKernelLogger)
            WmipKernelLogger = KERNEL_LOGGER;
        else if (LoggerId == WmipEventLogger)
            WmipEventLogger = 0XFFFFFFFF;
        else 
            Status = WmipDisableTraceProviders(LoggerId);

        if (LoggerInfo != NULL) {
            if (NT_SUCCESS(LoggerContext->LoggerStatus)) {
                LONG Buffers;

                Status = STATUS_TIMEOUT;
                Buffers = LoggerContext->BuffersAvailable;

                //
                // If all buffers are accounted for and the logfile handle
                // is NULL, then there is no reason to wait. 
                //

                if ( (Buffers == LoggerContext->NumberOfBuffers) && 
                     (LoggerContext->LogFileHandle == NULL) ) {
                    Status = STATUS_SUCCESS;
                }
                //
                // We need to wait for the logger thread to flush
                //
                while (Status == STATUS_TIMEOUT) {
                    Status = KeWaitForSingleObject(
                                &LoggerContext->FlushEvent,
                                Executive,
                                KernelMode,
                                FALSE,
                                &TimeOut
                                );
/*                    if (LoggerContext->NumberOfBuffers
                            == LoggerContext->BuffersAvailable)
                        break;
                    else if (LoggerContext->BuffersAvailable == Buffers) {
                        TraceDebug((1,
                            "WmiStopTrace: Logger %d hung %d != %d\n",
                            LoggerId, Buffers, LoggerContext->NumberOfBuffers));
                        KeResetEvent(&LoggerContext->FlushEvent);
//                        break;
                    } 
*/
                    TraceDebug((1, "WmiStopTrace: Wait status=%X\n",Status));
                }
            }
            //
            // Required for Query to work
            // But since CollectionOn is FALSE, it should be safe
            //
            Status = WmipQueryLogger(
                        LoggerInfo,
                        LoggerContext
                        );
        }
    }
#if DBG
    RefCount =
#endif
    WmipDereferenceLogger(LoggerId);
    TraceDebug((1, "WmiStopTrace: Stopped status=%X %d %d->%d\n",
                       Status, LoggerId, RefCount+1, RefCount));
    return Status;
}


NTKERNELAPI
NTSTATUS
WmiUpdateTrace(
    IN OUT PWMI_LOGGER_INFORMATION LoggerInfo
    )
/*++

Routine Description:

    It is called by WmipIoControl in wmi.c, with IOCTL_WMI_UPDATE_LOGGER
    to update certain characteristics of a running logger.

Arguments:

    LoggerInfo      a pointer to the structure for the logger's control
                    and status information

Return Value:

    The status of performing the action requested.

--*/

{
    NTSTATUS Status;
    ULONG Max_Buffers;
    PWMI_LOGGER_CONTEXT LoggerContext = NULL;
    ACCESS_MASK     DesiredAccess = TRACELOG_GUID_ENABLE;
    LARGE_INTEGER   TimeOut = {(ULONG)(-20 * 1000 * 1000 * 10), -1};
    ULONG           EnableFlags, TmpFlags;
    KPROCESSOR_MODE     RequestorMode;
    ULONG           LoggerMode, LoggerId, NewMode;
    UNICODE_STRING  NewLogFileName;
    PTRACE_ENABLE_FLAG_EXTENSION FlagExt = NULL;
    PERFINFO_GROUPMASK *PerfGroupMasks=NULL;
    ULONG GroupMaskSize;
    SECURITY_QUALITY_OF_SERVICE ServiceQos;
#if DBG
    LONG            RefCount;
#endif

    PAGED_CODE();

    
    //
    // see if Logger is running properly first. Error checking will be done
    // in WmiQueryTrace
    //

    if (LoggerInfo == NULL)
        return STATUS_INVALID_PARAMETER;

    EnableFlags = LoggerInfo->EnableFlags;

    TraceDebug((1, "WmiUpdateTrace: %d\n",
                    LoggerInfo->Wnode.HistoricalContext));
#if DBG
    Status = WmipVerifyLoggerInfo(LoggerInfo, &LoggerContext, "WmiUpdateTrace");
#else
    Status = WmipVerifyLoggerInfo(LoggerInfo, &LoggerContext);
#endif
    if (!NT_SUCCESS(Status) || (LoggerContext == NULL))
        return Status;

    LoggerId = LoggerContext->LoggerId;

    // at this point, LoggerContext must be non-NULL

    LoggerMode = LoggerContext->LoggerMode;   // local copy
    NewMode = LoggerInfo->LogFileMode;

    //
    // First, check to make sure that you cannot turn on certain modes
    // in UpdateTrace()
    //

    if ( ((NewMode & EVENT_TRACE_FILE_MODE_SEQUENTIAL) &&
          (NewMode & EVENT_TRACE_FILE_MODE_CIRCULAR))        ||

         ((NewMode & EVENT_TRACE_USE_GLOBAL_SEQUENCE) &&
          (NewMode & EVENT_TRACE_USE_LOCAL_SEQUENCE))        || 

         (!(LoggerMode & EVENT_TRACE_FILE_MODE_CIRCULAR) &&
          (NewMode & EVENT_TRACE_FILE_MODE_CIRCULAR))        ||

        // Cannot support append to circular
         ((NewMode & EVENT_TRACE_FILE_MODE_CIRCULAR) &&
          (NewMode & EVENT_TRACE_FILE_MODE_APPEND))

       ) {
        InterlockedDecrement(&LoggerContext->MutexCount);
        TraceDebug((1, "WmiUpdateTrace: Release mutex1 %d %d\n",
            LoggerContext->LoggerId, LoggerContext->MutexCount));
        WmipReleaseMutex(&LoggerContext->LoggerMutex);
#if DBG
        RefCount =
#endif
        WmipDereferenceLogger(LoggerId);
        TraceDebug((1, "WmiUpdateTrace: Status2=%X %d %d->%d\n",
                        Status, LoggerId, RefCount+1, RefCount));
        return STATUS_INVALID_PARAMETER;
    }

    //
    // support turn on or off real time dynamically
    //

    if (NewMode & EVENT_TRACE_REAL_TIME_MODE) {
        LoggerMode   |= EVENT_TRACE_REAL_TIME_MODE;
        DesiredAccess |= TRACELOG_CREATE_REALTIME;
    } else {
        if (LoggerMode & EVENT_TRACE_REAL_TIME_MODE)
            DesiredAccess |= TRACELOG_CREATE_REALTIME;  // turn off real time
        LoggerMode &= ~EVENT_TRACE_REAL_TIME_MODE;
    }
    if (NewMode & EVENT_TRACE_BUFFERING_MODE) {
        LoggerMode |= EVENT_TRACE_BUFFERING_MODE;
    }
    else {
        LoggerMode &= ~EVENT_TRACE_BUFFERING_MODE;
    }
    if (LoggerContext->KernelTraceOn)
        DesiredAccess |= TRACELOG_ACCESS_KERNEL_LOGGER;
    if (LoggerInfo->LogFileHandle != NULL)
        DesiredAccess |= TRACELOG_CREATE_ONDISK;

    Status = WmipCheckGuidAccess(
                &LoggerContext->InstanceGuid,
                DesiredAccess,
                EtwpDefaultTraceSecurityDescriptor
                );
    if (!NT_SUCCESS(Status)) {
        InterlockedDecrement(&LoggerContext->MutexCount);
        TraceDebug((1, "WmiUpdateTrace: Release mutex1 %d %d\n",
            LoggerContext->LoggerId, LoggerContext->MutexCount));
        WmipReleaseMutex(&LoggerContext->LoggerMutex);
#if DBG
        RefCount =
#endif
        WmipDereferenceLogger(LoggerId);
        TraceDebug((1, "WmiUpdateTrace: Status2=%X %d %d->%d\n",
                        Status, LoggerId, RefCount+1, RefCount));
        return Status;
    }

    RtlZeroMemory(&NewLogFileName, sizeof(UNICODE_STRING));
    RequestorMode = KeGetPreviousMode();
    if (LoggerInfo->LogFileHandle != NULL) {
        PFILE_OBJECT    fileObject;
        OBJECT_HANDLE_INFORMATION handleInformation;
        ACCESS_MASK grantedAccess;
        LOGICAL bDelayOpenFlag;

        bDelayOpenFlag =  (LoggerContext->LogFileHandle == NULL &&
                          (LoggerContext->LoggerMode & EVENT_TRACE_DELAY_OPEN_FILE_MODE));

        if (LoggerInfo->LogFileName.Buffer == NULL ||
            LoggerMode & EVENT_TRACE_FILE_MODE_NEWFILE ||
            NewMode & EVENT_TRACE_FILE_MODE_NEWFILE ) {
            // Do not allow snapping in or out of NEW_FILE mode.
            Status = STATUS_INVALID_PARAMETER;
            goto ReleaseAndExit;
        }
        // Save the new LogFileName
        //
        try {
            if (RequestorMode != KernelMode) {
                ProbeForRead(
                    LoggerInfo->LogFileName.Buffer,
                    LoggerInfo->LogFileName.Length,
                    sizeof (UCHAR) );
            }
            RtlCreateUnicodeString(
                &NewLogFileName,
                LoggerInfo->LogFileName.Buffer);
        }
        except (EXCEPTION_EXECUTE_HANDLER) {
            if (NewLogFileName.Buffer != NULL) {
                RtlFreeUnicodeString(&NewLogFileName);
            }
            InterlockedDecrement(&LoggerContext->MutexCount);
            TraceDebug((1, "WmiUpdateTrace: Release mutex3 %d %d\n",
                LoggerId, LoggerContext->MutexCount));
            WmipReleaseMutex(&LoggerContext->LoggerMutex);
#if DBG
            RefCount =
#endif
            WmipDereferenceLogger(LoggerId);
            TraceDebug((1, "WmiUpdateTrace: Status5=EXCEPTION %d %d->%d\n",
                            LoggerId, RefCount+1, RefCount));
            return GetExceptionCode();
        }

        // Switching to a new logfile. This routine does not put any
        // headers into the logfile. The headers should be written out
        // by UpdateTrace() in user-mode.
        //
        fileObject = NULL;
        Status = ObReferenceObjectByHandle(
                    LoggerInfo->LogFileHandle,
                    0L,
                    IoFileObjectType,
                    RequestorMode,
                    (PVOID *) &fileObject,
                    &handleInformation);
        if (!NT_SUCCESS(Status)) {
            goto ReleaseAndExit;
        }

        if (RequestorMode != KernelMode) {
            grantedAccess = handleInformation.GrantedAccess;
            if (!SeComputeGrantedAccesses(grantedAccess, FILE_WRITE_DATA)) {
                ObDereferenceObject( fileObject );
                Status = STATUS_ACCESS_DENIED;
                goto ReleaseAndExit;
            }
        }
        ObDereferenceObject(fileObject); // Referenced in WmipCreateLogFile

        // Obtain the security context here so we can use it
        // later to impersonate the user, which we will do
        // if we cannot access the file as SYSTEM.  This
        // usually occurs if the file is on a remote machine.
        //
        ServiceQos.Length  = sizeof(SECURITY_QUALITY_OF_SERVICE);
        ServiceQos.ImpersonationLevel = SecurityImpersonation;
        ServiceQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
        ServiceQos.EffectiveOnly = TRUE;
        Status = SeCreateClientSecurity(
                    CONTAINING_RECORD(KeGetCurrentThread(), ETHREAD, Tcb),
                    &ServiceQos,
                    FALSE,
                    & LoggerContext->ClientSecurityContext);
        if (!NT_SUCCESS(Status)) {
            goto ReleaseAndExit;
        }

        if (LoggerInfo->Checksum != NULL) {
            if (LoggerContext->LoggerHeader == NULL) {
                LoggerContext->LoggerHeader =
                    ExAllocatePoolWithTag(
                        PagedPool,
                        sizeof(WNODE_HEADER) + sizeof(TRACE_LOGFILE_HEADER),
                        TRACEPOOLTAG);
            }

    //
    // Although we allocate sizeof(WNODE_HEADER) + sizeof(TRACE_LOGFILE_HEADER)
    // chunk, we will only copy sizeof(WNODE_HEADER) + 
    // FIELD_OFFSET(TRACE_LOGFILE_HEADER, LoggerName) because we will not use 
    // the parts after the pointers. Also, this prevents AV when WOW UpdateTrace
    // calls are made with 32 bit TRACE_LOGFILE_HEADER.
    //

            if (LoggerContext->LoggerHeader != NULL) {
                RtlCopyMemory(
                    LoggerContext->LoggerHeader,
                    LoggerInfo->Checksum,
                    sizeof(WNODE_HEADER) + FIELD_OFFSET(TRACE_LOGFILE_HEADER, LoggerName));
            }
        }

        // We try to update the file name using LoggerContext->NewLogFileName.
        // This is freed by WmipCreateLogFile() in the logger thread. 
        // This have to be NULL. 
        if (NewLogFileName.Buffer != NULL) {
            ASSERT(LoggerContext->NewLogFileName.Buffer == NULL);
            LoggerContext->NewLogFileName = NewLogFileName;
        }
        else {
            Status = STATUS_INVALID_PARAMETER;
            goto ReleaseAndExit;
        }

        //
        // Reset the event inside the mutex before waiting on it. 
        //
        KeResetEvent(&LoggerContext->FlushEvent);

        ZwClose(LoggerInfo->LogFileHandle);
        LoggerInfo->LogFileHandle = NULL;

        // must turn on flag just before releasing semaphore
        LoggerContext->RequestFlag |= REQUEST_FLAG_NEW_FILE;
        // Wake up the logger thread (system) to change the file
        Status = WmipNotifyLogger(LoggerContext);
        if (!NT_SUCCESS(Status)) {
            goto ReleaseAndExit;
        }
        // use the same event initialized by start logger
        //
        KeWaitForSingleObject(
            &LoggerContext->FlushEvent,
            Executive,
            KernelMode,
            FALSE,
            &TimeOut
            );
        KeResetEvent(&LoggerContext->FlushEvent);
        Status = LoggerContext->LoggerStatus;

        if (!NT_SUCCESS(Status) || !LoggerContext->CollectionOn) {
            goto ReleaseAndExit;
        }

        if (bDelayOpenFlag && (LoggerContext->LoggerId == WmipKernelLogger)) {
            LONG PerfLogInTransition;
            //
            // This is a update call from advapi32.dll after RunDown.
            // Call PerfInfoStartLog.
            //

            if (EnableFlags & EVENT_TRACE_FLAG_EXTENSION) {
                FlagExt = (PTRACE_ENABLE_FLAG_EXTENSION) &EnableFlags;
                if ((FlagExt->Length == 0) || 
                    (FlagExt->Offset == 0) || 
                    (LoggerInfo->Wnode.BufferSize < FlagExt->Offset)) {
                    Status = STATUS_INVALID_PARAMETER;
                    goto ReleaseAndExit;
                }

                if ((FlagExt->Length * sizeof(ULONG)) >
                    (LoggerInfo->Wnode.BufferSize - FlagExt->Offset)) {
                    Status = STATUS_INVALID_PARAMETER;
                    goto ReleaseAndExit;
                }
                GroupMaskSize = FlagExt->Length * sizeof(ULONG);
                if (GroupMaskSize < sizeof(PERFINFO_GROUPMASK)) {
                    GroupMaskSize = sizeof(PERFINFO_GROUPMASK);
                }
            } else {
                GroupMaskSize = sizeof(PERFINFO_GROUPMASK);
            }

            LoggerContext->EnableFlagArray = (PULONG) WmipExtendBase(LoggerContext, GroupMaskSize);

            if (LoggerContext->EnableFlagArray) {
                PCHAR FlagArray;

                RtlZeroMemory(LoggerContext->EnableFlagArray, GroupMaskSize);
                if (EnableFlags & EVENT_TRACE_FLAG_EXTENSION) {
                    FlagArray = (PCHAR) (FlagExt->Offset + (PCHAR) LoggerInfo);
            
                    //
                    // Copy only the bytes actually supplied
                    //
                    RtlCopyMemory(LoggerContext->EnableFlagArray, FlagArray, FlagExt->Length * sizeof(ULONG));
            
                    EnableFlags = LoggerContext->EnableFlagArray[0];
            
                } else {
                    LoggerContext->EnableFlagArray[0] = EnableFlags;
                }
                // We need to protect PerfInfoStartLog from stopping thread.
                PerfLogInTransition =
                    InterlockedCompareExchange(&LoggerContext->PerfLogInTransition,
                                PERF_LOG_START_TRANSITION,
                                PERF_LOG_NO_TRANSITION);
                if (PerfLogInTransition != PERF_LOG_NO_TRANSITION) {
                    Status = STATUS_ALREADY_DISCONNECTED;
                    goto ReleaseAndExit;
                }
                PerfGroupMasks = (PERFINFO_GROUPMASK *) &LoggerContext->EnableFlagArray[0];
                Status = PerfInfoStartLog(PerfGroupMasks, PERFINFO_START_LOG_POST_BOOT);
                PerfLogInTransition =
                    InterlockedExchange(&LoggerContext->PerfLogInTransition,
                                PERF_LOG_NO_TRANSITION);
                ASSERT(PerfLogInTransition == PERF_LOG_START_TRANSITION);
                if (!NT_SUCCESS(Status)) {
                    goto ReleaseAndExit;
                }

            } else {
                Status = STATUS_NO_MEMORY;
                goto ReleaseAndExit;
            }
        }
    }

    if (LoggerContext->KernelTraceOn &&
        LoggerId == WmipKernelLogger &&
        IsEqualGUID(&LoggerInfo->Wnode.Guid, &SystemTraceControlGuid)) {
        TmpFlags = (~LoggerContext->EnableFlags & EnableFlags);
        if (TmpFlags != 0) {
            WmipEnableKernelTrace(TmpFlags);
        }
        TmpFlags = (LoggerContext->EnableFlags & ~EnableFlags);
        if (TmpFlags != 0) {
            WmipDisableKernelTrace(TmpFlags);
        }
        LoggerContext->EnableFlags = EnableFlags;
    }

    //
    // Cap Maximum Buffers to Max_Buffers
    //

    if ( LoggerInfo->MaximumBuffers > 0 ) {
        Max_Buffers = (LoggerContext->BufferSize > 0) ? 
                           (ULONG) (MmMaximumNonPagedPoolInBytes
                            / TRACE_MAXIMUM_NP_POOL_USAGE
                            / LoggerContext->BufferSize)
                        : 0;

        if (LoggerInfo->MaximumBuffers > Max_Buffers ) {
            LoggerInfo->MaximumBuffers = Max_Buffers;
        }
        if (LoggerInfo->MaximumBuffers > LoggerContext->MaximumBuffers) {
            LoggerContext->MaximumBuffers = LoggerInfo->MaximumBuffers;
        }

    }

    // Allow changing of FlushTimer
    if (LoggerInfo->FlushTimer > 0) {
        LoggerContext->FlushTimer = LoggerInfo->FlushTimer;
    }

    if (NewMode & EVENT_TRACE_KD_FILTER_MODE) {
        LoggerMode |= EVENT_TRACE_KD_FILTER_MODE;
        LoggerContext->BufferCallback = &KdReportTraceData;
    }
    else {
        LoggerMode &= ~EVENT_TRACE_KD_FILTER_MODE;
        if (LoggerContext->BufferCallback == &KdReportTraceData) {
            LoggerContext->BufferCallback = NULL;
        }
    }
    if (LoggerContext->LoggerMode & EVENT_TRACE_DELAY_OPEN_FILE_MODE) {
        LoggerContext->LoggerMode = LoggerMode;
    }
    else {
        LoggerContext->LoggerMode = 
            (LoggerMode & ~EVENT_TRACE_DELAY_OPEN_FILE_MODE);
    }
    Status = WmipQueryLogger(LoggerInfo, LoggerContext);

ReleaseAndExit:
    InterlockedDecrement(&LoggerContext->MutexCount);
    TraceDebug((1, "WmiUpdateTrace: Release mutex5 %d %d\n",
        LoggerId, LoggerContext->MutexCount));
    WmipReleaseMutex(&LoggerContext->LoggerMutex);

#if DBG
    RefCount =
#endif
    WmipDereferenceLogger(LoggerId);
    TraceDebug((1, "WmiUpdateTrace: %d %d->%d\n",
                    LoggerId, RefCount+1, RefCount));

    return Status;
}


NTKERNELAPI
NTSTATUS
WmiFlushTrace(
    IN OUT PWMI_LOGGER_INFORMATION LoggerInfo
    )
/*++

Routine Description:

    It is called by WmipIoControl in wmi.c, with IOCTL_WMI_FLUSH_LOGGER
    to flush all the buffers out of a particular logger

Arguments:

    LoggerInfo      a pointer to the structure for the logger's control
                    and status information

Return Value:

    The status of performing the action requested.

--*/

{
    NTSTATUS Status;
    PWMI_LOGGER_CONTEXT LoggerContext = NULL;
    ACCESS_MASK     DesiredAccess = TRACELOG_GUID_ENABLE;
    ULONG           LoggerId;
    ULONG           LoggerMode;
#if DBG
    LONG            RefCount;
#endif

    PAGED_CODE();
    //
    // see if Logger is running properly first. Error checking will be done
    // in WmiQueryTrace
    //

    if (LoggerInfo == NULL)
        return STATUS_INVALID_PARAMETER;

    TraceDebug((1, "WmiFlushTrace: %d\n",
                    LoggerInfo->Wnode.HistoricalContext));
#if DBG
    Status = WmipVerifyLoggerInfo(LoggerInfo, &LoggerContext, "WmiFlushTrace");
#else
    Status = WmipVerifyLoggerInfo(LoggerInfo, &LoggerContext);
#endif
    if (!NT_SUCCESS(Status) || (LoggerContext == NULL))
        return Status;

    LoggerId = LoggerContext->LoggerId;

    LoggerMode = LoggerContext->LoggerMode;
    if (LoggerMode & EVENT_TRACE_REAL_TIME_MODE) {
        DesiredAccess |= TRACELOG_CREATE_REALTIME;
    } 
    if (LoggerInfo->LogFileHandle != NULL) {
        DesiredAccess |= TRACELOG_CREATE_ONDISK;
    }
    if (LoggerContext->KernelTraceOn) {
        DesiredAccess |= TRACELOG_ACCESS_KERNEL_LOGGER;
    }
    Status = WmipCheckGuidAccess(
                &LoggerContext->InstanceGuid,
                DesiredAccess,
                EtwpDefaultTraceSecurityDescriptor
                );
    if (!NT_SUCCESS(Status)) {
        InterlockedDecrement(&LoggerContext->MutexCount);
        TraceDebug((1, "WmiFlushTrace: Release mutex1 %d %d\n",
            LoggerContext->LoggerId, LoggerContext->MutexCount));
        WmipReleaseMutex(&LoggerContext->LoggerMutex);
#if DBG
        RefCount =
#endif
        WmipDereferenceLogger(LoggerId);
        TraceDebug((1, "WmiFlushTrace: Status2=%X %d %d->%d\n",
                        Status, LoggerId, RefCount+1, RefCount));
        return Status;
    }

    Status = WmipFlushLogger(LoggerContext, TRUE);
    if (NT_SUCCESS(Status)) {
        Status = WmipQueryLogger(LoggerInfo, LoggerContext);
    }
    InterlockedDecrement(&LoggerContext->MutexCount);
    TraceDebug((1, "WmiFlushTrace: Release mutex %d %d\n",
        LoggerContext->LoggerId, LoggerContext->MutexCount));
    WmipReleaseMutex(&LoggerContext->LoggerMutex);
#if DBG
    RefCount =
#endif
    WmipDereferenceLogger(LoggerId);
    TraceDebug((1, "WmiFlushTrace: %d %d->%d\n",
                    LoggerId, RefCount+1, RefCount));

    return Status;
}

//
// Trace Provider APIs
//
NTKERNELAPI
NTSTATUS
FASTCALL
WmiGetClockType(
    IN TRACEHANDLE LoggerHandle,
    OUT WMI_CLOCK_TYPE  *ClockType
    )
/*++

Routine Description:

    This is called by anyone internal to find the clock type 
    that is in use with a logger specified by the LoggerHandle

Arguments:

    LoggerHandle         Handle to a tracelog session

Return Value:

    The clock type

--*/

{
    ULONG   LoggerId;
#if DBG
    LONG    RefCount;
#endif
    PWMI_LOGGER_CONTEXT LoggerContext;

    LoggerId = WmiGetLoggerId(LoggerHandle);
    if (LoggerId < 1 || LoggerId >= MAXLOGGERS)
       return STATUS_INVALID_HANDLE;
#if DBG
 RefCount =
#endif
    WmipReferenceLogger(LoggerId);
    TraceDebug((4, "WmiGetClockType: %d %d->%d\n",
                 LoggerId, RefCount-1, RefCount));

    LoggerContext = WmipGetLoggerContext( LoggerId );
    if (!WmipIsValidLogger(LoggerContext)) {
#if DBG
        RefCount =
#endif
        WmipDereferenceLogger(LoggerId);
        TraceDebug((4, "WmiGetClockType: Status=%X %d %d->%d\n",
                        STATUS_WMI_INSTANCE_NOT_FOUND,
                        LoggerId, RefCount+1, RefCount));
        return STATUS_WMI_INSTANCE_NOT_FOUND;
    }

    *ClockType = WMICT_SYSTEMTIME;    // Default Clock Type

    if (LoggerContext->UsePerfClock & EVENT_TRACE_CLOCK_PERFCOUNTER) {
        *ClockType = WMICT_PERFCOUNTER;
    }
    else if (LoggerContext->UsePerfClock & EVENT_TRACE_CLOCK_CPUCYCLE) {
        *ClockType = WMICT_CPUCYCLE;
    }

#if DBG
    RefCount =
#endif
    WmipDereferenceLogger(LoggerId);

    return STATUS_SUCCESS;

}


NTKERNELAPI
LONG64
FASTCALL
WmiGetClock(
    IN WMI_CLOCK_TYPE ClockType,
    IN PVOID Context
    )
/*++

Routine Description:

    This is called anyone internal to use a particular clock for
    sequencing events.

Arguments:

    ClockType       Should use WMICT_DEFAULT most of the time.
                    Other clock types are for perf group.
    Context         Only used for process/thread times

Return Value:

    The clock value

--*/

{
    LARGE_INTEGER Clock;
    ULONG TotalKernel;
    ULONG TotalUser;

    switch (ClockType) {
        case WMICT_DEFAULT :
            Clock.QuadPart = (*WmiGetCpuClock)();
            break;
        case WMICT_SYSTEMTIME:
            Clock.QuadPart = WmipGetSystemTime();
            break;
        case WMICT_CPUCYCLE:
            Clock.QuadPart = PerfGetCycleCount();
            break;
        case WMICT_PERFCOUNTER:
            Clock.QuadPart = WmipGetPerfCounter();
            break;
        case WMICT_PROCESS :  // defaults to Process times for now
        {
            PEPROCESS Process = (PEPROCESS) Context;
            if (Process == NULL)
                Process = PsGetCurrentProcess();
            else {
                ObReferenceObject(Process);
            }

            TotalKernel = KeQueryRuntimeProcess(&Process->Pcb, &TotalUser);
            Clock.HighPart = TotalKernel;
            Clock.LowPart  = TotalUser;
            if (Context) {
                ObDereferenceObject(Process);
            }
            break;
        }
        case WMICT_THREAD  :  // defaults to Thread times for now
        {
            PETHREAD Thread = (PETHREAD) Context;
            if (Thread == NULL)
                Thread = PsGetCurrentThread();
            else {
                ObReferenceObject(Thread);
            }
            Clock.HighPart = Thread->Tcb.KernelTime;
            Clock.LowPart  = Thread->Tcb.UserTime;
            if (Context) {
                ObDereferenceObject(Thread);
            }
            break;
        }
        default :
            KeQuerySystemTime(&Clock);
    }
    return ((LONG64) Clock.QuadPart);
}

NTSYSCALLAPI
NTSTATUS
NTAPI
NtTraceEvent (
    __in HANDLE TraceHandle,
    __in ULONG Flags,
    __in ULONG FieldSize,
    __in PVOID Fields
    )

/*++
Routine Description:

    This routine is used by WMI data providers to trace events.
    It calls different tracing functions depending on the Flags.

Arguments:

    TraceHandle     LoggerId
    Flags           Flags that indicate the type of the data being passed
    FieldSize       Size of the Fields
    Fields          Pointer to actual data (events)

Return Value:

    STATUS_SUCCESS  if the event trace is recorded successfully

--*/
{
    NTSTATUS Status;
    if (Flags & ETW_NT_FLAGS_TRACE_HEADER) {

retry:
        Status = WmiTraceEvent((PWNODE_HEADER)Fields, KeGetPreviousMode());

        if (Status == STATUS_NO_MEMORY) {
            //
            // This logging is from user mode, try to allocate more buffer.
            //
            PWNODE_HEADER Wnode=(PWNODE_HEADER) Fields;
            ULONG LoggerId=0;
            PWMI_LOGGER_CONTEXT LoggerContext;

            try {
                LoggerId = WmiGetLoggerId(Wnode->HistoricalContext);
            } except  (EXCEPTION_EXECUTE_HANDLER) {
                TraceDebug((1, "NtTraceEvent: Status=EXCEPTION\n"));
                return GetExceptionCode();
            }


            if (LoggerId < 1 || LoggerId >= MAXLOGGERS) {
                Status = STATUS_INVALID_HANDLE;
            } else {
                WmipReferenceLogger(LoggerId);

                LoggerContext = WmipGetLoggerContext(LoggerId);

                //
                // Make sure collection is still on before allocate more
                // free buffers.  This makes sure that logger thread
                // can free all allocated buffers.
                //
                if (WmipIsValidLogger(LoggerContext) && 
                                LoggerContext->CollectionOn) 
                {
                    if (WmipAllocateFreeBuffers (LoggerContext, 1) == 1) {
                        WmipDereferenceLogger(LoggerId);
                        InterlockedDecrement((PLONG)&LoggerContext->EventsLost);
                        goto retry;
                    } else {
                        Status = STATUS_NO_MEMORY;
                    }
                }
                WmipDereferenceLogger(LoggerId);
            }
        }
    }
    else if (Flags & ETW_NT_FLAGS_TRACE_MESSAGE) {
        if (FieldSize < sizeof(MESSAGE_TRACE_USER)) {
            return (STATUS_UNSUCCESSFUL);
        }
        try {
            ProbeForRead(
                    Fields,
                    FieldSize,
                    sizeof (UCHAR)
                    );

            if (((PMESSAGE_TRACE_USER)Fields)->DataSize > FieldSize - FIELD_OFFSET(MESSAGE_TRACE_USER, Data)) {
                
                //
                // The embedded 'Data' is suppose to be within the Field (FieldSize), if exceeded,
                // we are likely trying to read others buffer.
                //      Fail It.
                //
                return STATUS_INVALID_BUFFER_SIZE;
            }

            return (WmiTraceMessage((TRACEHANDLE)TraceHandle,
                                    ((PMESSAGE_TRACE_USER)Fields)->MessageFlags,
                                    &((PMESSAGE_TRACE_USER)Fields)->MessageGuid,
                                    ((PMESSAGE_TRACE_USER)Fields)->MessageHeader.Packet.MessageNumber,
                                    &((PMESSAGE_TRACE_USER)Fields)->Data,
                                    (SIZE_T)((PMESSAGE_TRACE_USER)Fields)->DataSize,
                                    NULL,
                                    (SIZE_T)0));

        } except  (EXCEPTION_EXECUTE_HANDLER) {
            TraceDebug((1, "NtTraceEvent: (ETW_NT_FLAGS_TRACE_MESSAGE) Status=EXCEPTION\n"));
            return GetExceptionCode();
        }
    }
    else {
        Status = STATUS_INVALID_PARAMETER;
    }
    return Status;
}


NTKERNELAPI
NTSTATUS
FASTCALL
WmiTraceEvent(
    IN PWNODE_HEADER Wnode,
    IN KPROCESSOR_MODE RequestorMode
    )
/*++

Routine Description:

    This routine is used by WMI data providers to trace events.
    It expects the user to pass in the handle to the logger.
    Also, the user cannot ask to log something that is larger than
    the buffer size (minus buffer header).

    This routine works at IRQL <= DISPATCH_LEVEL

Arguments:

    Wnode           The WMI node header that will be overloaded


Return Value:

    STATUS_SUCCESS  if the event trace is recorded successfully

--*/
{
    PEVENT_TRACE_HEADER TraceRecord = (PEVENT_TRACE_HEADER) Wnode;
    ULONG WnodeSize, Size, LoggerId = 0, Flags, HeaderSize;
    PWMI_BUFFER_HEADER BufferResource = NULL;
    NTSTATUS Status;
    PETHREAD Thread;
    PWMI_LOGGER_CONTEXT LoggerContext = NULL;
    ULONG Marker;
    MOF_FIELD MofFields[MAX_MOF_FIELDS];
    long MofCount = 0;
    LONG LoggerLocked = 0;
    LARGE_INTEGER TimeStamp;
#if DBG
    LONG RefCount = 0;
#endif

    if (TraceRecord == NULL)
        return STATUS_SEVERITY_WARNING;

    HeaderSize = sizeof(WNODE_HEADER);  // same size as EVENT_TRACE_HEADER

    try {

        if (RequestorMode != KernelMode) {
            ProbeForRead(
                TraceRecord,
                sizeof (EVENT_TRACE_HEADER),
                sizeof (UCHAR)
                );

            Marker = Wnode->BufferSize;     // check the first DWORD flags
            Size = Marker;

            if (Marker & TRACE_HEADER_FLAG) {
                if ( ((Marker & TRACE_HEADER_ENUM_MASK) >> 16)
                        == TRACE_HEADER_TYPE_INSTANCE )
                    HeaderSize = sizeof(EVENT_INSTANCE_GUID_HEADER);
                Size = TraceRecord->Size;
            }
        }
        else {
            Size = Wnode->BufferSize;     // take the first DWORD flags
            Marker = Size;
            if (Marker & TRACE_HEADER_FLAG) {
                if ( ((Marker & TRACE_HEADER_ENUM_MASK) >> 16)
                        == TRACE_HEADER_TYPE_INSTANCE )
                    HeaderSize = sizeof(EVENT_INSTANCE_GUID_HEADER);
                Size = TraceRecord->Size;
            }
        }
        WnodeSize = Size;           // WnodeSize is for the contiguous block
                                    // Size is for what we want in buffer

        Flags = Wnode->Flags;
        if (!(Flags & WNODE_FLAG_LOG_WNODE) &&
            !(Flags & WNODE_FLAG_TRACED_GUID)) {
            return STATUS_UNSUCCESSFUL;
        }

        LoggerId = WmiGetLoggerId(Wnode->HistoricalContext);
        if (LoggerId < 1 || LoggerId >= MAXLOGGERS) {
            return STATUS_INVALID_HANDLE;
        }

        LoggerLocked = WmipReferenceLogger(LoggerId);
#if DBG
        RefCount = LoggerLocked;
#endif
        TraceDebug((4, "WmiTraceEvent: %d %d->%d\n",
                        LoggerId, RefCount-1, RefCount));
        LoggerContext = WmipGetLoggerContext(LoggerId);
        if (!WmipIsValidLogger(LoggerContext)) {
#if DBG
            RefCount =
#endif
            WmipDereferenceLogger(LoggerId);
            TraceDebug((4, "WmiTraceEvent: Status1=%X %d %d->%d\n",
                            STATUS_INVALID_HANDLE, LoggerId,
                            RefCount+1, RefCount));
            return STATUS_INVALID_HANDLE;
        }

        if ((RequestorMode == KernelMode) &&
            (LoggerContext->LoggerMode & EVENT_TRACE_USE_PAGED_MEMORY)) {
#if DBG
            RefCount =
#endif
            WmipDereferenceLogger(LoggerId);
            TraceDebug((4, "WmiTraceEvent: Status1=%X %d %d->%d\n",
                            STATUS_UNSUCCESSFUL, LoggerId,
                            RefCount+1, RefCount));
            return STATUS_UNSUCCESSFUL;
        }

        if (Flags & WNODE_FLAG_USE_MOF_PTR) {
        //
        // Need to compute the total size required, since the MOF fields
        // in Wnode merely contains pointers
        //
            long i;
            PCHAR Offset = ((PCHAR)Wnode) + HeaderSize;
            ULONG MofSize, MaxSize;

            MaxSize = LoggerContext->BufferSize - sizeof(WMI_BUFFER_HEADER);
            MofSize = WnodeSize - HeaderSize;
            // allow only the maximum
            if (MofSize > (sizeof(MOF_FIELD) * MAX_MOF_FIELDS)) {
                WmipDereferenceLogger(LoggerId);
                return STATUS_ARRAY_BOUNDS_EXCEEDED;
            }
            if (MofSize > 0) {               // Make sure we can read the rest
                if (RequestorMode != KernelMode) {
                    ProbeForRead( Offset, MofSize, sizeof (UCHAR) );
                }
                RtlCopyMemory(MofFields, Offset, MofSize);
            }
            Size = HeaderSize;

            MofCount = MofSize / sizeof(MOF_FIELD);
            for (i=0; i<MofCount; i++) {
                MofSize = MofFields[i].Length;
                if (MofSize >= (MaxSize - Size)) {  // check for overflow first
#if DBG
                    RefCount =
#endif
                    WmipDereferenceLogger(LoggerId);
                    TraceDebug((4, "WmiTraceEvent: Status2=%X %d %d->%d\n",
                                    STATUS_BUFFER_OVERFLOW, LoggerId,
                                    RefCount+1, RefCount));
                    return STATUS_BUFFER_OVERFLOW;
                }

                Size += MofSize;
            }
        }

        if ((LoggerContext->RequestFlag & REQUEST_FLAG_CIRCULAR_PERSIST) &&
            (LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_CIRCULAR)) {
            if (! (Flags & WNODE_FLAG_PERSIST_EVENT) ) {
                ULONG RequestFlag = LoggerContext->RequestFlag
                                  & (~ (  REQUEST_FLAG_CIRCULAR_PERSIST
                                        | REQUEST_FLAG_CIRCULAR_TRANSITION));

                if (InterlockedCompareExchange(
                              (PLONG) &LoggerContext->RequestFlag,
                              RequestFlag | REQUEST_FLAG_CIRCULAR_TRANSITION,
                              RequestFlag | REQUEST_FLAG_CIRCULAR_PERSIST)) {

                    // All persistence events are fired in circular
                    // logfile, flush out all active buffers and flushlist
                    // buffers. Also mark the end of persistence event
                    // collection in circular logger.
                    //
                    // It is the provider's responsibility to ensure that 
                    // no persist event fires after this point. If it did,
                    // that event may be  overwritten during wrap around.
                    //
                    if (KeGetCurrentIrql() < DISPATCH_LEVEL) {
                        Status = WmipFlushLogger(LoggerContext, TRUE);
                    }
                }
            }
        }

// So, now reserve some space in logger buffer and set that to TraceRecord

        TraceRecord = (PEVENT_TRACE_HEADER)
            WmipReserveTraceBuffer( LoggerContext,
                                    Size,
                                    &BufferResource,
                                    &TimeStamp);

        if (TraceRecord == NULL) {
#if DBG
        RefCount =
#endif
            WmipDereferenceLogger(LoggerId);
            TraceDebug((4, "WmiTraceEvent: Status4=%X %d %d->%d\n",
                            STATUS_NO_MEMORY, LoggerId,
                            RefCount+1, RefCount));
            return STATUS_NO_MEMORY;
        }

        if (Flags & WNODE_FLAG_USE_MOF_PTR) {
        //
        // Now we need to probe and copy all the MOF data fields
        //
            PVOID MofPtr;
            ULONG MofLen;
            long i;
            PCHAR TraceOffset = (PCHAR) TraceRecord + HeaderSize;

            if (RequestorMode != KernelMode) {
                ProbeForRead(Wnode, HeaderSize, sizeof(UCHAR));
            }
            RtlCopyMemory(TraceRecord, Wnode, HeaderSize);
            TraceRecord->Size = (USHORT)Size;           // reset to Total Size
            for (i=0; i<MofCount; i++) {
                MofPtr = (PVOID) (ULONG_PTR) MofFields[i].DataPtr;
                MofLen = MofFields[i].Length;

                if (MofPtr == NULL || MofLen == 0)
                    continue;

                if (RequestorMode != KernelMode) {
                    ProbeForRead(MofPtr, MofLen, sizeof(UCHAR));
                }
                RtlCopyMemory(TraceOffset, MofPtr, MofLen);
                TraceOffset += MofLen;
            }
        }
        else {
            if (RequestorMode != KernelMode) {
                ProbeForRead(Wnode, Size, sizeof(UCHAR));
            }
            RtlCopyMemory(TraceRecord, Wnode, Size);
        }
        if (Flags & WNODE_FLAG_USE_GUID_PTR) {
            PVOID GuidPtr = (PVOID) (ULONG_PTR) ((PEVENT_TRACE_HEADER)Wnode)->GuidPtr;

            if (RequestorMode != KernelMode) {
                ProbeForReadSmallStructure(GuidPtr, sizeof(GUID), sizeof(UCHAR));
            }
            RtlCopyMemory(&TraceRecord->Guid, GuidPtr, sizeof(GUID));
        }
    }
    except (EXCEPTION_EXECUTE_HANDLER) {
        if (BufferResource != NULL) {
            WmipReleaseTraceBuffer ( BufferResource, LoggerContext );
        }
        else {
            if (LoggerLocked) {
#if DBG
                RefCount =
#endif
                WmipDereferenceLogger(LoggerId);
            }
        }
        TraceDebug((4, "WmiTraceEvent: Status5=EXCEPTION %d %d->%d\n",
                        LoggerId, RefCount+1, RefCount));
        return GetExceptionCode();
    }

    //
    // By now, we have reserved space in the trace buffer
    //

    if (Marker & TRACE_HEADER_FLAG) {
        if (! (WNODE_FLAG_USE_TIMESTAMP & TraceRecord->Flags) ) {
            TraceRecord->TimeStamp = TimeStamp;
        }
        Thread = PsGetCurrentThread();
        if (Thread != NULL) {
            TraceRecord->KernelTime = Thread->Tcb.KernelTime;
            TraceRecord->UserTime   = Thread->Tcb.UserTime;
            TraceRecord->ThreadId   = HandleToUlong(Thread->Cid.UniqueThread);
            TraceRecord->ProcessId  = HandleToUlong(Thread->Cid.UniqueProcess);
        }
    }

    WmipReleaseTraceBuffer( BufferResource, LoggerContext );
    TraceDebug((4, "WmiTraceEvent: %d %d->%d\n",
                    LoggerId, RefCount+1, RefCount));

    return STATUS_SUCCESS;
}


NTKERNELAPI
NTSTATUS
WmiTraceKernelEvent(
    IN ULONG GroupType,         // Group/type code for kernel event
    IN PVOID EventInfo,         // Event data as defined in MOF
    IN ULONG EventInfoLen,      // Length of the event data
    IN PETHREAD Thread )        // use NULL for current caller thread
/*++

Routine Description:

    This routine is used by trace kernel events only. These events can
    be charged to the given thread instead of the running thread. Because
    it can be called by I/O events at DPC level, this routine cannot be
    pageable when tracing is on.

    This routine works at IRQL <= DISPATCH_LEVEL

Arguments:

    GroupType       a ULONG key to indicate the action to be taken

    EventInfo       a pointer to contiguous memory containing information
                    to be attached to event trace

    EventInfoLen    length of EventInfo

    Thread          Pointer to thread where event is to be charged.
                    Currently used by disk IO and thread events.

Return Value:

    The status of performing the action requested

--*/
{
    PSYSTEM_TRACE_HEADER Header;
    ULONG Size;
    PWMI_BUFFER_HEADER BufferResource = NULL;
    PWMI_LOGGER_CONTEXT LoggerContext = WmipLoggerContext[WmipKernelLogger];
    LARGE_INTEGER TimeStamp;
#if DBG
    LONG    RefCount;
#endif

#if DBG
    RefCount =
#endif
    WmipReferenceLogger(WmipKernelLogger);
    TraceDebug((4, "WmiTraceKernelEvent: 0 %d->%d\n", RefCount-1, RefCount));
// Make sure that kernel logger is enabled first
    if (!WmipIsValidLogger(LoggerContext)) {
        WmipDereferenceLogger(WmipKernelLogger);
        return STATUS_ALREADY_DISCONNECTED;
    }

// Compute total size of event trace record
    Size = sizeof(SYSTEM_TRACE_HEADER) + EventInfoLen;

    Header = (PSYSTEM_TRACE_HEADER)
            WmipReserveTraceBuffer( LoggerContext,
                                    Size,
                                    &BufferResource,
                                    &TimeStamp);

    if (Header == NULL) {
#if DBG
        RefCount =
#endif
        WmipDereferenceLogger(WmipKernelLogger);
        TraceDebug((4, "WmiTraceKernelEvent: Status1=%X 0 %d->%d\n",
                        STATUS_NO_MEMORY, RefCount+1, RefCount));
        return STATUS_NO_MEMORY;
    }

    // Get the current system time as time stamp for trace record
    Header->SystemTime = TimeStamp;

    if (Thread == NULL) {
        Thread = PsGetCurrentThread();
    }

//
// Now copy the necessary information into the buffer
//

    Header->Marker       = SYSTEM_TRACE_MARKER;
    Header->ThreadId     = HandleToUlong(Thread->Cid.UniqueThread);
    Header->ProcessId    = HandleToUlong(Thread->Cid.UniqueProcess);
    Header->KernelTime   = Thread->Tcb.KernelTime;
    Header->UserTime     = Thread->Tcb.UserTime;

    Header->Header       = (GroupType << 16) + Size;

    if (EventInfoLen > 0) {
        RtlCopyMemory (
            (UCHAR*) Header + sizeof(SYSTEM_TRACE_HEADER),
            EventInfo, EventInfoLen);
    }

#if DBG
    RefCount = WmipRefCount[LoggerContext->LoggerId] - 1;
#endif
    WmipReleaseTraceBuffer( BufferResource, LoggerContext );
    TraceDebug((4, "WmiTraceKernelEvent: 0 %d->%d\n",
                    RefCount+1, RefCount));

    return STATUS_SUCCESS;
}



NTKERNELAPI
NTSTATUS
FASTCALL
WmiTraceFastEvent(
    IN PWNODE_HEADER Wnode
    )
/*++

Routine Description:

    This routine is used by short events using abbreviated header.

    This routine should work at any IRQL.

Arguments:

    Wnode           Header of event to record


Return Value:

    The status of performing the action requested

--*/
{
    ULONG Size;
    PTIMED_TRACE_HEADER Buffer;
    PTIMED_TRACE_HEADER Header = (PTIMED_TRACE_HEADER) Wnode;
    PWMI_BUFFER_HEADER BufferResource;
    ULONG LoggerId = (ULONG) Header->LoggerId; // get the lower ULONG!!
    PULONG Marker;
    PWMI_LOGGER_CONTEXT LoggerContext;
    LARGE_INTEGER TimeStamp;
#if DBG
    LONG RefCount;
#endif

    Marker = (PULONG) Wnode;
    if ((LoggerId == WmipKernelLogger) || (LoggerId >= MAXLOGGERS))
        return STATUS_INVALID_HANDLE;

    if ((*Marker & 0xF0000000) == TRACE_HEADER_ULONG32_TIME) {
        Size = Header->Size;
        if (Size == 0)
            return STATUS_INVALID_BUFFER_SIZE;
#if DBG
        RefCount =
#endif
        WmipReferenceLogger(LoggerId);
        LoggerContext = WmipGetLoggerContext(LoggerId);
        if (!WmipIsValidLogger(LoggerContext)) {
#if DBG
        RefCount =
#endif
            WmipDereferenceLogger(LoggerId);
            return STATUS_INVALID_HANDLE;
        }
        Buffer = WmipReserveTraceBuffer(LoggerContext, 
                                        Size, 
                                        &BufferResource,
                                        &TimeStamp);
        if (Buffer == NULL) {
#if DBG
        RefCount =
#endif
            WmipDereferenceLogger(LoggerId);
            return STATUS_NO_MEMORY;
        }
        (* (PULONG) Buffer) = *Marker;
        Buffer->EventId = Header->EventId;
        Buffer->TimeStamp = TimeStamp;

        RtlCopyMemory(Buffer+1, Header+1, Size-(sizeof(TIMED_TRACE_HEADER)));
        WmipReleaseTraceBuffer(BufferResource, LoggerContext);
        return STATUS_SUCCESS;
    }
    TraceDebug((4, "WmiTraceFastEvent: Invalid header %X\n", *Marker));
    return STATUS_INVALID_PARAMETER;
}

NTKERNELAPI
NTSTATUS
WmiTraceMessage(
    IN TRACEHANDLE  LoggerHandle,
    IN ULONG        MessageFlags,
    IN LPGUID       MessageGuid,
    IN USHORT       MessageNumber,
    ...
)

/*++
Routine Description:
This routine is used by WMI data providers to trace Messages.
It expects the user to pass in the handle to the logger.
Also, the user cannot ask to log something that is larger than
the buffer size (minus buffer header).

Arguments:
//    IN TRACEHANDLE LoggerHandle   - LoggerHandle obtained earlier
//    IN ULONG MessageFlags,        - Flags which both control what standard values are logged and
//                                    the lower 16-bits are also included in the message header
//                                    to control decoding
//    IN PGUID MessageGuid,         - Pointer to the message GUID of this set of messages or if
//                                    TRACE_COMPONENTID is set the actual compnent ID
//    IN USHORT MessageNumber,      - The type of message being logged, associates it with the 
//                                    appropriate format string  
//    ...                           - List of arguments to be processed with the format string
//                                    these are stored as pairs of
//                                      PVOID - ptr to argument
//                                      ULONG - size of argument
//                                    and terminated by a pointer to NULL, length of zero pair.


Return Value:
STATUS_SUCCESS if the event trace is recorded successfully

NOTE:
        this routine is called from WmiTraceUserMessage path via an IOCTL so the probes and
        try/excepts have to be carefully managed
--*/
{
    va_list MessageArgList ;

    va_start(MessageArgList,MessageNumber);

    return (WmiTraceMessageVa(LoggerHandle,
                              MessageFlags,
                              MessageGuid,
                              MessageNumber,
                              MessageArgList));
}


NTSTATUS
WmiTraceMessageVa(
    IN TRACEHANDLE  LoggerHandle,
    IN ULONG        MessageFlags,
    IN LPGUID       MessageGuid,
    IN USHORT       MessageNumber,
    va_list         MessageArgList
)
/*++ WmiTraceMessageVa
         The VA version of WmiTraceMessage
NOTE:
        this routine is called from WmiTraceUserMessage path via an IOCTL so the probes and
        try/excepts have to be carefully managed
--*/
{
    SIZE_T dataBytes;
    PMESSAGE_TRACE_HEADER Header;
    PUCHAR pMessageData ;
    PWMI_BUFFER_HEADER BufferResource = NULL ;
    USHORT size ;
    ULONG  LoggerId = (ULONG)-1 ; // initialize so we don't unlock it if not set
    ULONG SequenceNumber ;
    PWMI_LOGGER_CONTEXT LoggerContext = NULL;
    LARGE_INTEGER TimeStamp;
#if DBG
    LONG    RefCount;
#endif

    // Set the LoggerId up here, and lock it.
    // if we AV in the WmiUserTraceMessagePath we will
    // be caught by the try/except in that routine
    LoggerId = WmiGetLoggerId(LoggerHandle);
    if (LoggerId < 1 || LoggerId >= MAXLOGGERS)
       return STATUS_INVALID_HANDLE;

#if DBG
 RefCount =
#endif
    WmipReferenceLogger(LoggerId);
    TraceDebug((4, "WmiTraceMessage: %d %d->%d\n",
                 LoggerId, RefCount-1, RefCount));
    
    try {
        //
        // Determine the number bytes to follow header
        //
        dataBytes = 0;
        { // Allocation Block
            va_list ap;
            PCHAR source;
            ap = MessageArgList ;
            while ((source = va_arg (ap, PVOID)) != NULL) {
                SIZE_T elemBytes;
                if ((elemBytes = va_arg (ap, SIZE_T)) > 0) {
                    if (elemBytes > (TRACE_MESSAGE_MAXIMUM_SIZE - sizeof(MESSAGE_TRACE_USER))) {
#if DBG
     RefCount =
#endif
                         WmipDereferenceLogger(LoggerId);
                         TraceDebug((4, "WmiTraceMessage: elemBytes too big %x %d %d->%d\n",
                                   STATUS_INVALID_BUFFER_SIZE,
                                   LoggerId,
                                   RefCount + 1,
                                   RefCount));
                         return STATUS_INVALID_BUFFER_SIZE;
                    }

                   dataBytes += elemBytes;
                }      
            }

            if (dataBytes > (TRACE_MESSAGE_MAXIMUM_SIZE - sizeof(MESSAGE_TRACE_USER))) {
#if DBG
     RefCount =
#endif
                 WmipDereferenceLogger(LoggerId);
                 TraceDebug((4, "WmiTraceMessage: dataBytes too big %x %d %d->%d\n",
                           STATUS_INVALID_BUFFER_SIZE,
                           LoggerId,
                           RefCount + 1,
                           RefCount));
                 return STATUS_INVALID_BUFFER_SIZE;
            }

         } // end of allocation block

        // Figure the size of the message including the header
        size  = (USHORT) ((MessageFlags&TRACE_MESSAGE_SEQUENCE ? sizeof(ULONG):0) +
                (MessageFlags&TRACE_MESSAGE_GUID ? sizeof(GUID):0) +
                (MessageFlags&TRACE_MESSAGE_COMPONENTID ? sizeof(ULONG):0) +
        	    (MessageFlags&(TRACE_MESSAGE_TIMESTAMP | TRACE_MESSAGE_PERFORMANCE_TIMESTAMP) ? sizeof(LARGE_INTEGER):0) +
         	    (MessageFlags&TRACE_MESSAGE_SYSTEMINFO ? 2 * sizeof(ULONG):0) +
                sizeof (MESSAGE_TRACE_HEADER) +
                dataBytes);

        if (dataBytes > size) {

            //
            // We can ONLY log 64K (USHORT) data for a message. If the message is going
            // to be larger than we could log, fail it. 
            //
#if DBG
     RefCount =
#endif
                 WmipDereferenceLogger(LoggerId);
                 TraceDebug((4, "WmiTraceMessage: size overflow %x %d %d->%d\n",
                           STATUS_INVALID_BUFFER_SIZE,
                           LoggerId,
                           RefCount + 1,
                           RefCount));
                 return STATUS_INVALID_BUFFER_SIZE;            
        }

        //
        // Allocate Space in the Trace Buffer
        //
        // NOTE: we do not check for size here for reduce overhead, and because
        //       we trust ourselves to use WmiTraceLongEvent for large event traces (???)

        LoggerContext = WmipGetLoggerContext(LoggerId);
        if (!WmipIsValidLogger(LoggerContext)) {
#if DBG
     RefCount =
#endif
            WmipDereferenceLogger(LoggerId);
            TraceDebug((4, "WmiTraceMessage: Status1=%X %d %d->%d\n",
                        STATUS_INVALID_HANDLE, LoggerId,
                        RefCount+1, RefCount));
            return STATUS_INVALID_HANDLE;
        }

        if ((LoggerContext->RequestFlag & REQUEST_FLAG_CIRCULAR_PERSIST) &&
            (LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_CIRCULAR)) {
            // Reset REQUEST_FLAG_CIRCULAR_PERSIST flag
            // Since persistent events will never be mixed with TraceMessage(), 
            // we'll just reset it once and for all without flushing.
            LoggerContext->RequestFlag &= (~( REQUEST_FLAG_CIRCULAR_PERSIST
                                            | REQUEST_FLAG_CIRCULAR_TRANSITION));
        }

        if ((KeGetPreviousMode() == KernelMode) &&
            (LoggerContext->LoggerMode & EVENT_TRACE_USE_PAGED_MEMORY)) {
#if DBG
     RefCount =
#endif
            WmipDereferenceLogger(LoggerId);
            TraceDebug((4, "WmiTraceMessage: Status1=%X %d %d->%d\n",
                        STATUS_UNSUCCESSFUL, LoggerId,
                        RefCount+1, RefCount));
            return STATUS_UNSUCCESSFUL;
        }

        Header = (PMESSAGE_TRACE_HEADER) WmipReserveTraceBuffer(LoggerContext,
                                                                size,
                                                                &BufferResource,
                                                                &TimeStamp);
        if (Header == NULL) {
#if DBG
            RefCount =
#endif
            WmipDereferenceLogger(LoggerId);
            TraceDebug((4, "WmiTraceMessage: %d %d->%d\n",
                            LoggerId, RefCount+1, RefCount));

            return STATUS_NO_MEMORY;
        }
        //
        // Sequence Number is returned in the Marker field of the buffer
        //
        SequenceNumber = Header->Marker ;

        //
        // Now copy the necessary information into the buffer
        //

        Header->Marker = TRACE_MESSAGE | TRACE_HEADER_FLAG ;
        //
        // Fill in Header.
        //
        Header->Size = size ;
        Header->Packet.OptionFlags = ((USHORT)MessageFlags &
                                      (TRACE_MESSAGE_SEQUENCE |
                                      TRACE_MESSAGE_GUID |
                                      TRACE_MESSAGE_COMPONENTID |
                                      TRACE_MESSAGE_TIMESTAMP |
                                      TRACE_MESSAGE_PERFORMANCE_TIMESTAMP |
                                      TRACE_MESSAGE_SYSTEMINFO)) &
                                      TRACE_MESSAGE_FLAG_MASK ;
        // Message Number
        Header->Packet.MessageNumber =  MessageNumber ;

        //
        // Now add in the header options we counted.
        //
        pMessageData = &(((PMESSAGE_TRACE)Header)->Data);


        //
        // Note that the order in which these are added is critical New entries must
        // be added at the end!
        //
        // [First Entry] Sequence Number
        if (MessageFlags&TRACE_MESSAGE_SEQUENCE) {
            *((PULONG)pMessageData) = SequenceNumber;
        	pMessageData += sizeof(ULONG) ;
        }

        // [Second Entry] GUID ? or CompnentID ?
        if (MessageFlags&TRACE_MESSAGE_COMPONENTID) {
            *((PULONG)pMessageData) = *((PULONG) MessageGuid);
            pMessageData += sizeof(ULONG) ;
        } else if (MessageFlags&TRACE_MESSAGE_GUID) { // Can't have both
            *((LPGUID)pMessageData) = *MessageGuid;
        	pMessageData += sizeof(GUID) ;
        }
        
        // [Third Entry] Timestamp?
        if (MessageFlags&TRACE_MESSAGE_TIMESTAMP) {
            ((PLARGE_INTEGER)pMessageData)->HighPart = TimeStamp.HighPart;
            ((PLARGE_INTEGER)pMessageData)->LowPart = TimeStamp.LowPart;
            pMessageData += sizeof(LARGE_INTEGER);

        }


        // [Fourth Entry] System Information?
        // Note that some of this may NOT be valid depending on the current processing level
        // however we assume that the post-processing may still find it useful
        if (MessageFlags&TRACE_MESSAGE_SYSTEMINFO) {
            PCLIENT_ID Cid;        // avoid additional function calls

            Cid = &(PsGetCurrentThread()->Cid);
            // Executive Handles may be truncated
            *((PULONG)pMessageData) = HandleToUlong(Cid->UniqueThread);
            pMessageData += sizeof(ULONG) ;  

            *((PULONG)pMessageData) = HandleToUlong(Cid->UniqueProcess);   
            pMessageData += sizeof(ULONG) ;
        }
        //
        // Add New Header Entries immediately before this comment!
        //

        //
        // Now Copy in the Data.
        //
        { // Allocation Block
            va_list ap;
            PUCHAR source;
            ap = MessageArgList ;
            while ((source = va_arg (ap, PVOID)) != NULL) {
                SIZE_T elemBytes, copiedBytes = 0 ;
                if ((elemBytes = va_arg (ap, SIZE_T)) > 0 ) {
                    if (dataBytes < copiedBytes + elemBytes) {  // defend against bytes having changed
                        TraceDebug((1, "WmiTraceMessage: Invalid message - argument length changed"));
                        break;                                  // So we don't overrun
                    }
                    RtlCopyMemory (pMessageData, source, elemBytes);
                    copiedBytes += elemBytes ;
                    pMessageData += elemBytes;
                }
            }
        } // Allocation Block

    } except  (EXCEPTION_EXECUTE_HANDLER) {
        if (BufferResource != NULL) {
               WmipReleaseTraceBuffer ( BufferResource, LoggerContext );   // also unlocks the logger
        } else {
#if DBG
     RefCount =
#endif
             WmipDereferenceLogger(LoggerId);
        }
        TraceDebug((4, "WmiTraceMessage: Status6=EXCEPTION %d %d->%d\n",
                    LoggerId, RefCount+1, RefCount));
        return GetExceptionCode();
    }

    //
    // Buffer Complete, Release
    //
    WmipReleaseTraceBuffer( BufferResource, LoggerContext );  // Also unlocks the logger
        
    TraceDebug((4, "WmiTraceMessage: %d %d->%d\n",
                        LoggerId, RefCount+1, RefCount));

    //
    // Return Success
    //
    return (STATUS_SUCCESS);
}

NTKERNELAPI
NTSTATUS
FASTCALL
WmiTraceUserMessage(
    IN PMESSAGE_TRACE_USER pMessage,
    IN ULONG    MessageSize
    )
/*++

Routine Description:

    This routine is used by trace User messages only. it is called via an IOCTL
    on the WMI Device.

Arguments:

    pMessage    a pointer to a Marshalled Message.
    MessageSize size of that message.

Return Value:

    The status of performing the action requested

--*/
{

    if (MessageSize < sizeof(MESSAGE_TRACE_USER)) {
        return (STATUS_UNSUCCESSFUL);
    }
    try {
        ProbeForRead(
                pMessage,
                MessageSize,
                sizeof (UCHAR)
                );
        return (WmiTraceMessage(pMessage->LoggerHandle,
                                pMessage->MessageFlags,
                                &pMessage->MessageGuid,
                                pMessage->MessageHeader.Packet.MessageNumber,
                                &pMessage->Data,pMessage->DataSize,
                                NULL,0));

    } except  (EXCEPTION_EXECUTE_HANDLER) {
         
         TraceDebug((1, "WmiTraceUserMessage: Status=EXCEPTION\n"));
         return GetExceptionCode();

    }
}


NTKERNELAPI
NTSTATUS
WmiSetMark(
    IN PWMI_SET_MARK_INFORMATION MarkInfo,
    IN ULONG InBufferLen
    )
/*++

Routine Description:

    This routine sets a mark in the kernel logger.

Arguments:

    MarkInfo - a pointer to a WMI_SET_MARK_INFORMATION strcture.

    InBufferLen - Buffer Size.

Return Value:

    status

--*/
{

    NTSTATUS Status;
    PERFINFO_HOOK_HANDLE Hook;
    ULONG TotalBytes;
    ULONG CopyBytes;
    USHORT HookId;

    if (PERFINFO_IS_ANY_GROUP_ON()) {
        if (MarkInfo->Flag & WMI_SET_MARK_WITH_FLUSH) {
            if (PERFINFO_IS_GROUP_ON(PERF_FOOTPRINT)) {
                MmEmptyAllWorkingSets();
                Status = MmPerfSnapShotValidPhysicalMemory();
            }
        }
        HookId = PERFINFO_LOG_TYPE_MARK;

        CopyBytes = InBufferLen - FIELD_OFFSET(WMI_SET_MARK_INFORMATION, Mark);
        TotalBytes = CopyBytes + sizeof(WCHAR);

        Status = PerfInfoReserveBytes(&Hook, HookId, TotalBytes);

        if (NT_SUCCESS(Status)){ 
            PWCHAR Mark = PERFINFO_HOOK_HANDLE_TO_DATA(Hook, PWCHAR); 

            RtlCopyMemory(Mark, &MarkInfo->Mark[0], CopyBytes);

            Mark[CopyBytes / sizeof(WCHAR)] = UNICODE_NULL;

            PERF_FINISH_HOOK(Hook);
        }
    } else {
        Status = STATUS_WMI_SET_FAILURE;
    }

    return Status;
}

NTKERNELAPI
NTSTATUS
WmiSetTraceBufferCallback(
    IN TRACEHANDLE  TraceHandle,
    IN WMI_TRACE_BUFFER_CALLBACK Callback,
    IN PVOID Context
    )
/*++

Routine Description:

    This routine sets a Buffer Callback function for a kernel mode logger.

Arguments:

    TraceHandle - a handle to a logger.

    Callback - a pointer to a callback function.

    Context - Callback context.

Return Value:

    status

--*/
{
    ULONG   LoggerId;
#if DBG
    LONG    RefCount;
#endif
    PWMI_LOGGER_CONTEXT LoggerContext;

    PAGED_CODE();

    if (TraceHandle == (TRACEHANDLE) 0) {
        WmipGlobalBufferCallback = Callback;
        return STATUS_SUCCESS;
    }
    LoggerId = WmiGetLoggerId(TraceHandle);
    if (LoggerId == KERNEL_LOGGER_ID) {
        LoggerId = WmipKernelLogger;
    }
    else if (LoggerId < 1 || LoggerId >= MAXLOGGERS)
       return STATUS_INVALID_HANDLE;
#if DBG
 RefCount =
#endif
    WmipReferenceLogger(LoggerId);
    TraceDebug((4, "WmiSetTraceBufferCallback: %d %d->%d\n",
                 LoggerId, RefCount-1, RefCount));

    LoggerContext = WmipGetLoggerContext( LoggerId );
    if (!WmipIsValidLogger(LoggerContext)) {
#if DBG
        RefCount =
#endif
        WmipDereferenceLogger(LoggerId);
        TraceDebug((4, "WmiSetTraceBufferCallback: Status=%X %d %d->%d\n",
                        STATUS_WMI_INSTANCE_NOT_FOUND,
                        LoggerId, RefCount+1, RefCount));
        return STATUS_WMI_INSTANCE_NOT_FOUND;
    }

    LoggerContext->BufferCallback = Callback;
    LoggerContext->CallbackContext = Context;
#if DBG
    RefCount =
#endif
    WmipDereferenceLogger(LoggerId);
    return STATUS_SUCCESS;
}


NTKERNELAPI
NTSTATUS
WmiQueryTraceInformation(
    IN TRACE_INFORMATION_CLASS TraceInformationClass,
    OUT PVOID TraceInformation,
    IN ULONG TraceInformationLength,
    OUT PULONG RequiredLength OPTIONAL,
    IN PVOID Buffer OPTIONAL
    )
/*++

Routine Description:

    This routine copies user-requested information to a user-provided buffer.
    If RequiredLength is given, the needed size for the requested information
    is returned.

Arguments:

    TraceInformationClass   Type of information requested
    TraceInformation        Output buffer for the information
    TraceInformationLength  The size of the TraceInformation
    RequiredLength          The size needed for the information
    Buffer                  Buffer used for use input. Depending on
                            TraceInformationClass, this may be required.

    NOTE: we do not consider NULL TraceInformation an error, In this case, we 
    only update RequiredLength, if that is given.

Return Value:

    The status of performing the action requested.

--*/
{
    ULONG LoggerId;
    ULONG EnableFlags;
    ULONG EnableLevel;
    ULONG LoggersLength;
    TRACEHANDLE TraceHandle;
    TRACEHANDLE AllHandles[MAXLOGGERS];
    NTSTATUS Status = STATUS_SUCCESS;
    PWNODE_HEADER Wnode = (PWNODE_HEADER) Buffer; // For most classes, but not all

    PAGED_CODE();

    try {
        if (ARGUMENT_PRESENT(RequiredLength)) {
            *RequiredLength = 0;
        }

        switch (TraceInformationClass) {

        case TraceIdClass:

            if (TraceInformationLength != sizeof( ULONG )) {
                return STATUS_INFO_LENGTH_MISMATCH;
            }
            if (Wnode == NULL) {
                return STATUS_INVALID_PARAMETER_MIX;
            }
            TraceHandle = Wnode->HistoricalContext;
            if ((TraceHandle == 0) || (TraceHandle == (ULONG) -1)) {
                return STATUS_INVALID_HANDLE;
            }

            LoggerId = WmiGetLoggerId(TraceHandle);

            if (LoggerId > MAXLOGGERS) {
                return STATUS_INVALID_HANDLE;
            }

            if (TraceInformation) {
                *((PULONG)TraceInformation) = LoggerId;
            }
            if (ARGUMENT_PRESENT( RequiredLength )) {
                *RequiredLength = sizeof( ULONG );
            }
            break;

        case TraceHandleClass:
            if (TraceInformationLength != sizeof(TRACEHANDLE)) {
                return STATUS_INFO_LENGTH_MISMATCH;
            }
            if (Buffer == NULL) {
                return STATUS_INVALID_PARAMETER_MIX;
            }
            LoggerId = *((PULONG) Buffer);
            TraceHandle = 0;
            TraceHandle = WmiSetLoggerId(LoggerId, &TraceHandle);

            if (TraceInformation) {
                *((PTRACEHANDLE)TraceInformation) = TraceHandle;
            }
            if (ARGUMENT_PRESENT( RequiredLength )) {
                *RequiredLength = sizeof( TRACEHANDLE );
            }
            break;

        case TraceEnableFlagsClass:
            if (TraceInformationLength < sizeof(ULONG)) {
                return STATUS_INFO_LENGTH_MISMATCH;
            }
            if (Wnode == NULL) {
                return STATUS_INVALID_PARAMETER_MIX;
            }
            TraceHandle = Wnode->HistoricalContext;
            if ((TraceHandle == 0) || (TraceHandle == (ULONG) -1)) {
                return STATUS_INVALID_HANDLE;
            }

            EnableFlags = WmiGetLoggerEnableFlags(TraceHandle);

            if (TraceInformation) {
                *((PULONG)TraceInformation) = EnableFlags;
            }
            if (ARGUMENT_PRESENT( RequiredLength )) {
                *RequiredLength = sizeof( ULONG );
            }
            break;

        case TraceEnableLevelClass:
            if (TraceInformationLength < sizeof(ULONG)) {
                return STATUS_INFO_LENGTH_MISMATCH;
            }
            if (Wnode == NULL) {
                return STATUS_INVALID_PARAMETER_MIX;
            }
            TraceHandle = Wnode->HistoricalContext;
            if ((TraceHandle == 0) || (TraceHandle == (ULONG) -1)) {
                return STATUS_INVALID_HANDLE;
            }

            EnableLevel = WmiGetLoggerEnableLevel(TraceHandle);

            if (TraceInformation) {
                *((PULONG)TraceInformation) = EnableLevel;
            }
            if (ARGUMENT_PRESENT( RequiredLength )) {
                *RequiredLength = sizeof( ULONG );
            }
            break;

        case GlobalLoggerHandleClass:
            if (TraceInformationLength != sizeof(TRACEHANDLE)) {
                return STATUS_INFO_LENGTH_MISMATCH;
            }
            WmipReferenceLogger(WMI_GLOBAL_LOGGER_ID);
            if (WmipLoggerContext[WMI_GLOBAL_LOGGER_ID] == NULL) {
                TraceHandle = 0;
                Status = STATUS_NOT_FOUND;
            }
            else {
                TraceHandle = WmipLoggerContext[WMI_GLOBAL_LOGGER_ID]->LoggerId;
            }

            WmipDereferenceLogger(WMI_GLOBAL_LOGGER_ID);
            if (TraceInformation) {
                *((PTRACEHANDLE)TraceInformation) = TraceHandle;
            }
            if (ARGUMENT_PRESENT( RequiredLength )) {
                *RequiredLength = sizeof( TRACEHANDLE );
            }
            break;

        case EventLoggerHandleClass:
            if (TraceInformationLength != sizeof(TRACEHANDLE)) {
                return STATUS_INFO_LENGTH_MISMATCH;
            }
            LoggerId = WmipEventLogger;
            if (WmipEventLogger == (ULONG) -1) {
                TraceHandle = 0;
                Status = STATUS_NOT_FOUND;
            }
            else {
                TraceHandle = LoggerId;
            }
            if (TraceInformation) {
                *((PTRACEHANDLE)TraceInformation) = TraceHandle;
            }
            if (ARGUMENT_PRESENT( RequiredLength )) {
                *RequiredLength = sizeof( TRACEHANDLE );
            }
            break;

        case AllLoggerHandlesClass:
            // Returns all logger handles, except for kernel logger
            if (TraceInformationLength < sizeof(TRACEHANDLE)) {
                return STATUS_INFO_LENGTH_MISMATCH;
            }

            LoggersLength = 0;
            for (LoggerId=1; LoggerId<MAXLOGGERS; LoggerId++) {
                WmipReferenceLogger(LoggerId);
                if (!WmipIsValidLogger(WmipLoggerContext[LoggerId])) {
                    AllHandles[LoggersLength] = 0;
                }
                else {
                    AllHandles[LoggersLength++] = LoggerId;
                }
                WmipDereferenceLogger(LoggerId);
            }
            LoggersLength *= sizeof(TRACEHANDLE);
            if (TraceInformation && (LoggersLength > 0)) {
                if (TraceInformationLength >= LoggersLength) {
                    RtlCopyMemory(TraceInformation, AllHandles, LoggersLength);
                }
                else {
                    RtlCopyMemory(TraceInformation, AllHandles, TraceInformationLength);
                    Status = STATUS_MORE_ENTRIES;
                }
            }
            if (ARGUMENT_PRESENT( RequiredLength )) {
                *RequiredLength = LoggersLength;
            }
            break;

        case TraceHandleByNameClass:
            // Returns a Trace Handle Given a Logger name as a UNICODE_STRING in buffer.
            {
                WMI_LOGGER_INFORMATION LoggerInfo;
                PUNICODE_STRING uString = Buffer;


                if (TraceInformationLength != sizeof(TraceHandle) ) {
                    return STATUS_INFO_LENGTH_MISMATCH;
                }
	            if (uString == NULL) {
		            return STATUS_INVALID_PARAMETER;
	            }
	            if (uString->Buffer == NULL || uString->Length == 0) {
		            return STATUS_INVALID_PARAMETER;
	            }

                RtlZeroMemory(&LoggerInfo, sizeof(LoggerInfo));
                LoggerInfo.Wnode.BufferSize = sizeof(LoggerInfo);
                LoggerInfo.Wnode.Flags = WNODE_FLAG_TRACED_GUID;

                RtlInitUnicodeString(&LoggerInfo.LoggerName, uString->Buffer);

                Status = WmiQueryTrace(&LoggerInfo);
                if (!NT_SUCCESS(Status)) {
                    return STATUS_NOT_FOUND;
                }

                TraceHandle = (TRACEHANDLE)LoggerInfo.Wnode.HistoricalContext;

                if (TraceInformation) {
                    *((PTRACEHANDLE)TraceInformation) = TraceHandle;
                }
                if (ARGUMENT_PRESENT( RequiredLength )) {
                    *RequiredLength = sizeof( TRACEHANDLE );
                }
            }
            break;


        default :
            return STATUS_INVALID_INFO_CLASS;
        }
    } except (EXCEPTION_EXECUTE_HANDLER) {

        Status = GetExceptionCode();
    }

    return Status;
}

