/*++

Copyright (c) 2000  Microsoft Corporation

Module Name:

    logging.c

Abstract:

    This module contains routines for trace logging.

Author:

    Stephen Hsiao (shsiao) 01-Jan-2000

Revision History:

--*/

#include "perfp.h"

#ifndef NTPERF
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGEWMI, PerfInfoReserveBytes)
#pragma alloc_text(PAGEWMI, PerfInfoLogBytes)
#endif //ALLOC_PRAGMA
#endif // !NTPERF


NTSTATUS
PerfInfoReserveBytes(
    PPERFINFO_HOOK_HANDLE Hook,
    USHORT HookId,
    ULONG BytesToReserve
    )
/*++

Routine Description:

    Reserves memory for the hook via WMI and initializes the header.

Arguments:

    Hook - pointer to hook handle (used for reference decrement)
    HookId - Id for the hook
    BytesToLog - size of data in bytes

Return Value:

    STATUS_SUCCESS on success
    STATUS_UNSUCCESSFUL if the buffer memory couldn't be allocated.
--*/
{
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    PPERFINFO_TRACE_HEADER PPerfTraceHeader = NULL;
    PWMI_BUFFER_HEADER PWmiBufferHeader = NULL;

    PERF_ASSERT((BytesToReserve + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data)) <= MAXUSHORT);
    PERF_ASSERT(Hook != NULL);

    PPerfTraceHeader = WmiReserveWithPerfHeader(BytesToReserve, &PWmiBufferHeader);

    if (PPerfTraceHeader != NULL) {
        PPerfTraceHeader->Packet.HookId = HookId;
        Hook->PerfTraceHeader = PPerfTraceHeader;
        Hook->WmiBufferHeader = PWmiBufferHeader;

        Status = STATUS_SUCCESS;
    } else {
        *Hook = PERF_NULL_HOOK_HANDLE;
    }

    return Status;
}


NTSTATUS
PerfInfoLogBytes(
    USHORT HookId,
    PVOID Data,
    ULONG BytesToLog
    )
/*++

Routine Description:

    Reserves memory for the hook, copies the data, and unref's the hook entry.

Arguments:

    HookId - Id for the hook
    Data - pointer to the data to be logged
    BytesToLog - size of data in bytes

Return Value:

    STATUS_SUCCESS on success
--*/
{
    PERFINFO_HOOK_HANDLE Hook;
    NTSTATUS Status;

    Status = PerfInfoReserveBytes(&Hook, HookId, BytesToLog);
    if (!NT_SUCCESS(Status)) {
        return Status;
    }

    RtlCopyMemory(PERFINFO_HOOK_HANDLE_TO_DATA(Hook, PPERF_BYTE), Data, BytesToLog);
    PERF_FINISH_HOOK(Hook);

    return STATUS_SUCCESS;
}

#ifdef NTPERF

PVOID
FASTCALL
PerfInfoReserveBytesFromPerfMem(
    ULONG BytesToReserve
    )
/*++

Routine Description:

    Reserves memory for the hook from the buffer, initializes the header,
    and the hook handle.

Arguments:

    Hook - pointer to hook handle (used for reference decrement)
    HookId - Id for the hook
    BytesToLog - size of data in bytes

Return Value:

    STATUS_SUCCESS on success
    STATUS_UNSUCCESSFUL if the buffer memory couldn't be allocated.
--*/
{
    PPERFINFO_TRACEBUF_HEADER pPerfBufHdr;
    PPERF_BYTE CurrentPtr;
    PPERF_BYTE NewPtr;
    PPERF_BYTE OriginalPtr;
    BOOLEAN Done = FALSE;
    ULONG AlignedTotBytes;

    pPerfBufHdr = PerfBufHdr();

    AlignedTotBytes = ALIGN_TO_POWER2(BytesToReserve, DEFAULT_TRACE_ALIGNMENT);

    OriginalPtr = pPerfBufHdr->Current.Ptr;
    while (!Done) {
        NewPtr = OriginalPtr + AlignedTotBytes;
        if (NewPtr <= pPerfBufHdr->Max.Ptr) {
            //
            // If the buffer pointer has not changed, returned value will be == to the comparand,
            // OriginalPointer, and the Destenation will be updated with the new end of buffer.
            //
            // If it did change, the Destination will not change and the a new end of buffer will
            // be returned.  We loop until we get it in or the buffer is full.
            //

            CurrentPtr = (PPERF_BYTE) InterlockedCompareExchangePointer(
                                                    (PVOID *)&(pPerfBufHdr->Current.Ptr),
                                                    (PVOID)NewPtr,
                                                    (PVOID)OriginalPtr
                                                    );
            if (OriginalPtr == CurrentPtr) {
                Done = TRUE;
            } else {
                OriginalPtr = CurrentPtr;
            }
        } else {
            //
            // Buffer overflow
            //
            Done = TRUE;
            CurrentPtr = NULL;
        }
    }

    return CurrentPtr;
}
#endif //NTPERF