Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

499 lines
13 KiB

/*++
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