|
|
/*++
Copyright (c) 1998-2002 Microsoft Corporation
Module Name:
strlog.c
Abstract:
This module implements a string log.
A string log is a fast, in-memory, thread-safe activity log of variable-length strings. It's modelled on the tracelog code.
Author:
George V. Reilly (GeorgeRe) 23-Jul-2001
Revision History:
--*/
#include "precomp.h"
#include "strlogp.h"
ULONG g_StringLogDbgPrint = 0;
/***************************************************************************++
Routine Description:
Creates a new (empty) string log.
Arguments:
LogSize - Supplies the number of bytes in the string buffer.
ExtraBytesInHeader - Supplies the number of extra bytes to include in the log header. This is useful for adding application-specific data to the log.
Return Value:
PSTRING_LOG - Pointer to the newly created log if successful, NULL otherwise.
--***************************************************************************/ PSTRING_LOG CreateStringLog( IN ULONG LogSize, IN ULONG ExtraBytesInHeader, BOOLEAN EchoDbgPrint ) { ULONG TotalHeaderSize; PSTRING_LOG pLog; PUCHAR pLogBuffer;
if (LogSize >= 20 * 1024 * 1024) return NULL;
//
// Round up to page size
//
LogSize = (LogSize + (PAGE_SIZE-1)) & ~(PAGE_SIZE-1);
//
// Allocate & initialize the log structure.
//
TotalHeaderSize = sizeof(*pLog) + ExtraBytesInHeader;
pLogBuffer = (PUCHAR) ExAllocatePoolWithTag( NonPagedPool, LogSize, UL_STRING_LOG_BUFFER_POOL_TAG );
if (pLogBuffer == NULL) return NULL;
pLog = (PSTRING_LOG) ExAllocatePoolWithTag( NonPagedPool, TotalHeaderSize, UL_STRING_LOG_POOL_TAG );
//
// Initialize it.
//
if (pLog != NULL) { RtlZeroMemory( pLog, TotalHeaderSize );
pLog->Signature = STRING_LOG_SIGNATURE; pLog->pLogBuffer = pLogBuffer; pLog->LogSize = LogSize; pLog->EchoDbgPrint = EchoDbgPrint; KeInitializeSpinLock(&pLog->SpinLock);
ResetStringLog(pLog); } else { ExFreePoolWithTag( pLogBuffer, UL_STRING_LOG_BUFFER_POOL_TAG ); }
return pLog;
} // CreateStringLog
/***************************************************************************++
Routine Description:
Resets the specified string log such that the next entry written will be placed at the beginning of the log.
Arguments:
pLog - Supplies the string log to reset.
--***************************************************************************/ VOID ResetStringLog( IN PSTRING_LOG pLog ) { // Keep this in sync with !ulkd.strlog -r
if (pLog != NULL) { PSTRING_LOG_MULTI_ENTRY pMultiEntry = (PSTRING_LOG_MULTI_ENTRY) pLog->pLogBuffer; KIRQL OldIrql;
KeAcquireSpinLock(&pLog->SpinLock, &OldIrql); ASSERT( pLog->Signature == STRING_LOG_SIGNATURE );
RtlZeroMemory(pLog->pLogBuffer, pLog->LogSize); pLog->NextEntry = 0; pLog->LastEntryLength = 0; pLog->WrapAroundCount = 0;
//
// Write an initial multi-entry record at the very beginning
// of the log buffer. When we wraparound, we always place a
// multi-entry record at the beginning of the log buffer.
// Having this invariant makes !ulkd.strlog simpler.
//
pMultiEntry->Signature = STRING_LOG_ENTRY_MULTI_SIGNATURE; pMultiEntry->NumEntries = 0; pMultiEntry->PrevDelta = 0;
++pMultiEntry; pMultiEntry->Signature = STRING_LOG_ENTRY_LAST_SIGNATURE;
pLog->MultiOffset = 0; pLog->Offset = sizeof(STRING_LOG_MULTI_ENTRY); pLog->MultiByteCount = sizeof(STRING_LOG_MULTI_ENTRY); pLog->MultiNumEntries = 0;
pLog->InitialTimeStamp.QuadPart = 0;
KeReleaseSpinLock(&pLog->SpinLock, OldIrql); }
} // ResetStringLog
/***************************************************************************++
Routine Description:
Destroys a string log created with CreateStringLog().
Arguments:
pLog - Supplies the string log to destroy.
--***************************************************************************/ VOID DestroyStringLog( IN PSTRING_LOG pLog ) { if (pLog != NULL) { ASSERT( pLog->Signature == STRING_LOG_SIGNATURE );
pLog->Signature = STRING_LOG_SIGNATURE_X; ExFreePoolWithTag( pLog->pLogBuffer, UL_STRING_LOG_BUFFER_POOL_TAG ); ExFreePoolWithTag( pLog, UL_STRING_LOG_POOL_TAG ); }
} // DestroyStringLog
/***************************************************************************++
Routine Description:
Writes a new entry to the specified string log.
Arguments:
pLog - Supplies the log to write to.
Format - printf-style format string
arglist - va_list bundling up the arguments
Return Value:
LONGLONG - Index of the newly written entry
--***************************************************************************/ LONGLONG __cdecl WriteStringLogVaList( IN PSTRING_LOG pLog, IN PCH Format, IN va_list arglist ) { UCHAR Buffer[PRINTF_BUFFER_LEN]; PUCHAR pTarget; int cb; ULONG i; ULONG cb2; ULONG PrevDelta; ULONG MultiPrevDelta; PSTRING_LOG_ENTRY pEntry; LONGLONG index; KIRQL OldIrql; BOOLEAN NeedMultiEntry = FALSE; LARGE_INTEGER TimeStamp;
ASSERT( pLog->Signature == STRING_LOG_SIGNATURE );
cb = _vsnprintf((char*) Buffer, sizeof(Buffer), Format, arglist);
//
// Local Buffer overflow?
//
if (cb < 0) { cb = sizeof(Buffer); }
// _vsnprintf doesn't always NUL-terminate the buffer
Buffer[DIMENSION(Buffer)-1] = '\0';
if (pLog->EchoDbgPrint) DbgPrint("%s", (PCH) Buffer);
//
// Add 1 to 4 bytes of zeroes at end to terminate string,
// then round up to ULONG alignment
//
cb2 = ((sizeof(STRING_LOG_ENTRY) + cb + sizeof(ULONG)) & ~(sizeof(ULONG) - 1)); ASSERT(cb2 < 0x10000); // Must fit in a USHORT
//
// Find the next slot, copy the entry to the slot.
//
KeQuerySystemTime(&TimeStamp);
KeAcquireSpinLock(&pLog->SpinLock, &OldIrql); if (0 == pLog->InitialTimeStamp.QuadPart) pLog->InitialTimeStamp = TimeStamp;
TimeStamp.QuadPart -= pLog->InitialTimeStamp.QuadPart; index = pLog->NextEntry++;
PrevDelta = pLog->LastEntryLength; MultiPrevDelta = pLog->MultiByteCount; pLog->LastEntryLength = (USHORT) cb2;
ASSERT(pLog->Offset <= pLog->LogSize); //
// Handle wraparound of the log buffer. Since LogSize is typically much
// larger than PRINTF_BUFFER_LEN, this is an infrequent operation.
// Must have enough space for all of the regular STRING_LOG_ENTRY,
// a multi STRING_LOG_ENTRY, and the zero-terminated string itself.
//
if (pLog->Offset + cb2 + sizeof(STRING_LOG_ENTRY) >= pLog->LogSize) { ULONG WastedSpace = pLog->LogSize - pLog->Offset;
ASSERT(WastedSpace > 0);
// Clear to the end of the log buffer
for (i = 0; i < WastedSpace; i += sizeof(ULONG)) { PULONG pul = (PULONG) (pLog->pLogBuffer + pLog->Offset + i); ASSERT(((ULONG_PTR) pul & (sizeof(ULONG) - 1)) == 0); *pul = STRING_LOG_ENTRY_EOB_SIGNATURE; }
// Reset to the beginning
pLog->Offset = 0; ++pLog->WrapAroundCount; PrevDelta += WastedSpace; MultiPrevDelta += WastedSpace;
// Always want a multi-entry record at the beginning of the log buffer
NeedMultiEntry = TRUE; } else if (pLog->MultiNumEntries >= STRING_LOG_MULTIPLE_ENTRIES) { NeedMultiEntry = TRUE; } else { ++pLog->MultiNumEntries; }
//
// If we've had STRING_LOG_MULTIPLE_ENTRIES regular entries since the
// last multi-entry or if we've wrapped around the beginning of the
// log buffer, we need a new multi-entry.
//
if (NeedMultiEntry) { PSTRING_LOG_MULTI_ENTRY pMultiEntry;
pTarget = pLog->pLogBuffer + pLog->Offset; ASSERT(((ULONG_PTR) pTarget & (sizeof(ULONG) - 1)) == 0);
pMultiEntry = (PSTRING_LOG_MULTI_ENTRY) pTarget; pMultiEntry->Signature = STRING_LOG_ENTRY_MULTI_SIGNATURE;
ASSERT(pLog->MultiNumEntries <= STRING_LOG_MULTIPLE_ENTRIES); pMultiEntry->NumEntries = pLog->MultiNumEntries; pLog->MultiNumEntries = 1; // for the entry generated below
ASSERT(MultiPrevDelta < 0x10000); pMultiEntry->PrevDelta = (USHORT) MultiPrevDelta;
pLog->MultiOffset = pLog->Offset; pLog->MultiByteCount = sizeof(STRING_LOG_MULTI_ENTRY);
pLog->Offset += sizeof(STRING_LOG_MULTI_ENTRY); PrevDelta += sizeof(STRING_LOG_MULTI_ENTRY); }
pTarget = pLog->pLogBuffer + pLog->Offset; ASSERT(((ULONG_PTR) pTarget & (sizeof(ULONG) - 1)) == 0);
pLog->MultiByteCount = (USHORT) (pLog->MultiByteCount + (USHORT) cb2); pLog->Offset += (USHORT) cb2;
ASSERT(pLog->Offset <= pLog->LogSize); ASSERT(pLog->pLogBuffer <= pTarget && pTarget + cb2 < pLog->pLogBuffer + pLog->LogSize); // Put a special signature where the next entry will start
*(PULONG) (pTarget + cb2) = STRING_LOG_ENTRY_LAST_SIGNATURE;
if (g_StringLogDbgPrint) { DbgPrint("%4I64d: %s" "\tLen=%d (%x), PD=%d (%x); " "Off=%d (%x), Lel=%d (%x); " "Multi: Off=%d (%x), Lel=%d (%x), NE=%d; " "WA=%lu, NME=%d\n", index, Buffer, cb, cb, PrevDelta, PrevDelta, pLog->Offset, pLog->Offset, pLog->LastEntryLength, pLog->LastEntryLength, pLog->MultiOffset, pLog->MultiOffset, pLog->MultiByteCount, pLog->MultiByteCount, pLog->MultiNumEntries, pLog->WrapAroundCount, (int) NeedMultiEntry ); }
KeReleaseSpinLock(&pLog->SpinLock, OldIrql); // Finally, fill out the entry
pEntry = (PSTRING_LOG_ENTRY) pTarget;
pEntry->Signature = STRING_LOG_ENTRY_SIGNATURE; pEntry->Length = (USHORT) cb; ASSERT(PrevDelta < 0x10000); pEntry->PrevDelta = (USHORT) PrevDelta; pEntry->Processor = (UCHAR) KeGetCurrentProcessorNumber(); pEntry->TimeStampLowPart = TimeStamp.LowPart; pEntry->TimeStampHighPart = TimeStamp.HighPart;
pTarget = (PUCHAR) (pEntry + 1); RtlCopyMemory( pTarget, Buffer, cb );
for (i = cb; i < cb2 - sizeof(STRING_LOG_ENTRY); ++i) pTarget[i] = '\0';
pTarget = (PUCHAR) (pEntry + cb2);
return index; } // WriteStringLogVaList
/***************************************************************************++
Routine Description:
Writes a new entry to the specified string log.
Arguments:
pLog - Supplies the log to write to.
Format... - printf-style format string and arguments
Return Value:
LONGLONG - Index of the newly written entry within the string.
--***************************************************************************/ LONGLONG __cdecl WriteStringLog( IN PSTRING_LOG pLog, IN PCH Format, ... ) { LONGLONG index; va_list arglist; if (pLog == NULL) return -1;
va_start(arglist, Format);
index = WriteStringLogVaList(pLog, Format, arglist);
va_end(arglist);
return index; } // WriteStringLog
/***************************************************************************++
Routine Description:
Writes a new entry to the global string log.
Arguments:
pLog - Supplies the log to write to.
Format... - printf-style format string and arguments
Return Value:
LONGLONG - Index of the newly written entry within the string.
--***************************************************************************/ LONGLONG __cdecl WriteGlobalStringLog( IN PCH Format, ... ) { LONGLONG index; va_list arglist; if (g_pGlobalStringLog == NULL) return -1;
va_start(arglist, Format);
index = WriteStringLogVaList(g_pGlobalStringLog, Format, arglist);
va_end(arglist);
return index; } // WriteGlobalStringLog
|