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
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
|