Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

922 lines
22 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
memprint.c
Abstract:
This module contains the routines to implement in-memory DbgPrint.
DbgPrint text is stored in a large circular buffer, and optionally
written to a file and/or the debug console. Output to file is
buffered to allow high performance by the file system.
Author:
David Treadwell (davidtr) 05-Oct-1990
Revision History:
--*/
//
// so it compiles when it includes ps.h
//typedef unsigned long LCID; /* locale ID */
#pragma message( "**** Including DEBUG functionality ****")
#include "ntddk.h"
#include <ntverp.h> // Include to determine what version of NT
#ifdef VER_PRODUCTBUILD
#define rmm VER_PRODUCTBUILD
#endif
#include "digifile.h"
// The rest of the #includes are standard
#include <stdarg.h>
#include <string.h>
#include "stdio.h"
#include <memprint.h>
#undef DbgPrint
#undef MemPrintPreInitSettings
#undef MemPrintInitialize
#undef MemPrintQuit
#undef MemPrint
#undef MemPrintFlush
#define MEM_PRINT_DEF_BUFFER_SIZE (65536 * 8)
#define MEM_PRINT_LOG_FILE_NAME "\\SystemRoot\\DigiSer.log"
//
// Forward declarations.
//
VOID MemPrintWriteCompleteApc ( IN PVOID ApcContext,
IN PIO_STATUS_BLOCK IoStatusBlock );
VOID DigiPrintWriteThread ( IN PVOID Dummy );
//
// Global data. It is all protected by MemPrintSpinLock.
//
PVOID ThreadObjectPointer;
HANDLE fileHandle;
UCHAR TurnOffSniffer=1;
CLONG MemPrintBufferSize = MEM_PRINT_DEF_BUFFER_SIZE;
PCHAR MemPrintBuffer;
ULONG DigiPrintFlags = (MEM_PRINT_FLAG_CONSOLE | MEM_PRINT_FLAG_NOMEMCHECK);
ULONG AttemptedTempBufferAllocs=0;
ULONG MemPrintFailures=0;
CHAR DefaultLogFileName[1024]=MEM_PRINT_LOG_FILE_NAME;
//
// Protect writing to the buffer
//
KSPIN_LOCK MemPrintSpinLock;
BOOLEAN MemPrintInitialized = FALSE;
BOOLEAN UnloadingDriver = FALSE;
KEVENT MemPrintQuitEvent;
KEVENT MemPrintWriteToLogEvent;
LARGE_INTEGER totalBytesWritten;
LARGE_INTEGER fileAllocationSize;
ULONG BufferInOffset, BufferOutOffset;
VOID MemPrintPreInitSettings( PCHAR NewLogFileName,
ULONG NewBufferSize )
/*++
Routine Description:
Arguments:
None.
Return Value:
None.
--*/
{
strcpy( DefaultLogFileName, NewLogFileName );
MemPrintBufferSize = NewBufferSize;
} // end MemPrintPreInitSettings
NTSTATUS MemPrintInitialize ( VOID )
/*++
Routine Description:
This is the initialization routine for the in-memory DbgPrint routine.
It should be called before the first call to MemPrint to set up the
various structures used and to start the log file write thread.
Arguments:
None.
Return Value:
None.
--*/
{
NTSTATUS status;
HANDLE threadHandle;
KPRIORITY threadPriorityLevel;
OBJECT_ATTRIBUTES objectAttributes;
PCHAR fileName;
ANSI_STRING fileNameString;
UNICODE_STRING UnicodeFileName;
LARGE_INTEGER delayInterval;
ULONG attempts = 0;
IO_STATUS_BLOCK localIoStatusBlock;
PETHREAD CurrentThread;
PEPROCESS CurrentProcess;
if( MemPrintInitialized )
{
//
// we have all ready been called. Just return.
//
return( STATUS_SUCCESS );
}
fileName = DefaultLogFileName;
UnloadingDriver = FALSE;
//
// Initialize the total bytes written and write size variables.
//
totalBytesWritten.QuadPart = 0;
fileAllocationSize.QuadPart = 0;
BufferInOffset = BufferOutOffset = 0;
//
// Allocate memory for the circular buffer that will receive
// the text and data. If we can't do it, try again with a buffer
// half as large. If that fails, quit trying.
//
MemPrintBuffer = (PCHAR)DigiAllocMem( NonPagedPool, MemPrintBufferSize );
if( MemPrintBuffer == NULL )
{
MemPrintBufferSize /= 2;
DbgPrint( "Unable to allocate DbgPrint buffer--trying size = %ld\n",
MemPrintBufferSize );
MemPrintBuffer = DigiAllocMem( NonPagedPool, MemPrintBufferSize );
if( MemPrintBuffer == NULL )
{
DbgPrint( "Couldn't allocate DbgPrint buffer.\n" );
return( STATUS_INSUFFICIENT_RESOURCES );
}
}
DbgPrint( "MemPrint buffer from %lx to %lx\n",
MemPrintBuffer, MemPrintBuffer + MemPrintBufferSize );
//
// Allocate the spin lock that protects access to the various
// pointers and the circular buffer. This ensures integrity of the
// buffer.
//
KeInitializeSpinLock( &MemPrintSpinLock );
KeInitializeEvent( &MemPrintQuitEvent,
SynchronizationEvent,
(BOOLEAN)FALSE );
KeInitializeEvent( &MemPrintWriteToLogEvent,
SynchronizationEvent,
(BOOLEAN)FALSE );
//
// Initialize the string containing the file name and the object
// attributes structure that will describe the log file to open.
//
RtlInitAnsiString( &fileNameString,
fileName );
status = RtlAnsiStringToUnicodeString( &UnicodeFileName,
&fileNameString,
(BOOLEAN)TRUE );
ASSERT(NT_SUCCESS(status));
InitializeObjectAttributes( &objectAttributes,
&UnicodeFileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
//
// Set the allocation size of the log file to be three times the
// size of the circular buffer. When it fills up, we'll extend
// it.
//
fileAllocationSize.QuadPart += MemPrintBufferSize;
//
// Open the log file.
//
// !!! The loop here is to help avoid a system initialization
// timing problem, and should be removed when the problem is
// fixed.
//
while ( TRUE ) {
status = ZwCreateFile( &fileHandle,
FILE_WRITE_DATA,
&objectAttributes,
&localIoStatusBlock,
&fileAllocationSize,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OVERWRITE_IF,
FILE_SEQUENTIAL_ONLY,
NULL,
0L );
if( (status != STATUS_OBJECT_PATH_NOT_FOUND) || (++attempts >= 3) )
{
RtlFreeUnicodeString( &UnicodeFileName );
break;
}
delayInterval.LowPart = (ULONG)(-5*10*1000*1000); // five second delay
delayInterval.HighPart = -1;
KeDelayExecutionThread( (KPROCESSOR_MODE)KernelMode,
(BOOLEAN)FALSE,
&delayInterval );
}
if( NT_ERROR(status) )
{
DbgPrint( "NtCreateFile for log file failed: 0x%x\n", status );
DigiFreeMem( MemPrintBuffer );
return( status );
} else
{
DbgPrint( "Successfully opened logfile %s\n", fileName );
}
//
// Set the priority of the write thread.
//
threadPriorityLevel = LOW_REALTIME_PRIORITY + 1;
CurrentThread = PsGetCurrentThread();
CurrentProcess = PsGetCurrentProcess();
//
// Start the thread that writes subbuffers from the large circular
// buffer to disk.
//
status = PsCreateSystemThread( &threadHandle,
THREAD_ALL_ACCESS,
NULL,
(HANDLE)0L,
NULL,
DigiPrintWriteThread,
NULL );
if( NT_ERROR(status) )
{
DbgPrint( "MemPrintInitialize: PsCreateSystemThread failed: 0x%x\n",
status );
//
// Cleanup
//
ZwClose( fileHandle );
DigiFreeMem( MemPrintBuffer );
return( status );
}
status = ObReferenceObjectByHandle( threadHandle,
THREAD_ALL_ACCESS,
NULL,
KernelMode,
&ThreadObjectPointer,
NULL );
if( NT_ERROR(status) )
{
ZwClose( fileHandle );
DigiFreeMem( MemPrintBuffer );
return( status );
}
MemPrintInitialized = TRUE;
return( STATUS_SUCCESS );
} // MemPrintInitialize
VOID MemPrintQuit(VOID)
/*++
Routine Description:
Called to cleanup.
Arguments:
None.
Return Value:
None.
--*/
{
KIRQL oldIrql;
KeAcquireSpinLock( &MemPrintSpinLock, &oldIrql );
UnloadingDriver = TRUE;
if( !MemPrintInitialized )
{
KeReleaseSpinLock( &MemPrintSpinLock, oldIrql );
return;
}
KeSetEvent( &MemPrintWriteToLogEvent,
2,
(BOOLEAN)FALSE );
KeReleaseSpinLock( &MemPrintSpinLock, oldIrql );
KeWaitForSingleObject( ThreadObjectPointer,
Executive,
KernelMode,
FALSE,
NULL );
DigiFreeMem( MemPrintBuffer );
ZwClose( fileHandle );
} // MemPrintQuit
VOID MemPrint ( CHAR *Format, ... )
/*++
Routine Description:
This routine is called in place of DbgPrint to process in-memory
printing.
Arguments:
Format - A format string in the style of DbgPrint.
- formatting arguments.
Return Value:
None.
--*/
{
va_list arglist;
KIRQL oldIrql;
ULONG CurrentOutOffset, CurrentInOffset;
ULONG BytesToMove;
PCHAR tempBuffer, CopyTempBuffer;
ULONG tempBufferLen;
tempBuffer = DigiAllocMem( NonPagedPool, 1024 );
if( tempBuffer == NULL )
{
//
// Only print out failure msg every ten times.
//
if( (AttemptedTempBufferAllocs % 10) == 0 )
DbgPrint( "Unable to alloc temp buffer for MemPrint.\n" );
AttemptedTempBufferAllocs++;
return;
}
CopyTempBuffer = tempBuffer;
va_start( arglist, Format );
#if defined (_X86_)
_vsnprintf( tempBuffer, 1024, Format, arglist );
#elif defined (_MIPS_)
_vsnprintf( tempBuffer, 1024, Format, arglist );
#elif defined (_ALPHA_)
vsprintf( tempBuffer, Format, arglist );
#else
vsprintf( tempBuffer, Format, arglist );
#endif
va_end( arglist );
//
// If memory DbgPrint has not been initialized, simply print to the
// console.
//
if( !MemPrintInitialized ||
UnloadingDriver )
{
//
// Just dump to console and return.
//
DbgPrint( "%s", tempBuffer );
goto ExitMemPrintFree;
}
if( DigiPrintFlags & MEM_PRINT_FLAG_CONSOLE )
DbgPrint( "%s", tempBuffer );
tempBufferLen = strlen(tempBuffer);
BytesToMove = tempBufferLen;
ASSERT( tempBufferLen+1 <= 1024 );
//
// Acquire the spin lock that synchronizes access to the pointers
// and circular buffer.
//
KeAcquireSpinLock( &MemPrintSpinLock, &oldIrql );
CurrentOutOffset = BufferOutOffset;
CurrentInOffset = BufferInOffset;
while( tempBufferLen )
{
if( CurrentOutOffset > CurrentInOffset )
{
//
// Take into account that we don't want to write one less
// so we don't put the CurrentInOffset equal to CurrentOutOffset.
//
BytesToMove = CurrentOutOffset - CurrentInOffset - 1;
}
else if( CurrentOutOffset < CurrentInOffset )
{
BytesToMove = CurrentOutOffset + MemPrintBufferSize - CurrentInOffset;
}
else
{
//
// We have the full buffer,
//
BytesToMove = MemPrintBufferSize - 1;
}
//
// We know how many bytes are available in the buffer. Now determine
// how many bytes we can actually write.
//
if( BytesToMove >= tempBufferLen )
{
//
// We can put the whole thing into the memprint buffer.
//
BytesToMove = tempBufferLen;
}
// else
// {
// //
// // We can only put part of the print request into the memprint
// // buffer.
// //
// if( (MemPrintFailures % 1000) == 0 )
// DbgPrint( "Unable to place entire print request into memprint buffer!\n" );
// else
// DbgPrint( "^" );
//
// MemPrintFailures++;
// }
if( BytesToMove == 0 )
{
//
// More than likely, we ran out of buffer space.
//
if( (MemPrintFailures % 1000) == 0 )
DbgPrint( "Out of buffer space for MemPrint request, failures = %u!\n",
MemPrintFailures );
// else
// DbgPrint( "." );
MemPrintFailures++;
break;
}
//
// Okay, we know how many bytes we can potentially move. Now determine
// if we need to worry about wrapping the circular buffer.
//
if( (BytesToMove + CurrentInOffset) >= MemPrintBufferSize )
{
//
// readjust so we only write the number of bytes to the end
// of the buffer.
//
BytesToMove = MemPrintBufferSize - CurrentInOffset;
}
RtlMoveMemory( &MemPrintBuffer[CurrentInOffset],
tempBuffer,
BytesToMove );
tempBufferLen -= BytesToMove;
tempBuffer += BytesToMove;
CurrentInOffset += BytesToMove;
//
// Adjust the CurrentInOffset for circular buffer wrapping.
//
ASSERT( CurrentInOffset <= MemPrintBufferSize );
if( CurrentInOffset == MemPrintBufferSize )
CurrentInOffset = 0;
}
BufferInOffset = CurrentInOffset;
KeReleaseSpinLock( &MemPrintSpinLock, oldIrql );
//
// Set the event that will wake up the thread writing subbuffers
// to disk.
//
KeSetEvent( &MemPrintWriteToLogEvent,
2,
(BOOLEAN)FALSE );
ExitMemPrintFree:
DigiFreeMem( CopyTempBuffer );
return;
} // MemPrint
VOID MemPrintFlush ( VOID )
/*++
Routine Description:
This is obsolete now since the write thread will try to write
as much of the circular buffer as possible.
Arguments:
None.
Return Value:
None.
--*/
{
return;
} // MemPrintFlush
VOID DigiPrintWriteThread ( IN PVOID Dummy )
/*++
Routine Description:
The log file write thread executes this routine. It sets up the
log file for writing, then waits for subbuffers to fill, writing
them to disk when they do. When the log file fills, new space
for it is allocated on disk to prevent the file system from
having to do it.
Arguments:
Dummy - Ignored.
Return Value:
None.
--*/
{
NTSTATUS status;
NTSTATUS waitStatus;
IO_STATUS_BLOCK localIoStatusBlock;
LARGE_INTEGER delayInterval;
ULONG NumberBytesToWrite;
Dummy;
//
// Delay for 20 seconds before we start executing. This will hopefully
// allow the system to get further along at boot time.
//
delayInterval.LowPart = (ULONG)(-20*10*1000*1000); // twenty second delay
delayInterval.HighPart = -1;
KeDelayExecutionThread( (KPROCESSOR_MODE)KernelMode,
(BOOLEAN)FALSE,
&delayInterval );
KeSetPriorityThread( KeGetCurrentThread(),
LOW_REALTIME_PRIORITY + 1 );
//
// Loop waiting for one of the subbuffer full events to be signaled.
// When one is signaled, wake up and write the subbuffer to the log
// file.
//
if( UnloadingDriver )
{
KeSetEvent( &MemPrintQuitEvent,
2,
FALSE );
PsTerminateSystemThread( STATUS_SUCCESS );
}
while( TRUE )
{
PUCHAR tmpPtr;
ULONG CurrentOutOffset, CurrentInOffset;
KIRQL oldIrql;
waitStatus = KeWaitForSingleObject( &MemPrintWriteToLogEvent,
Executive,
KernelMode,
TRUE,
NULL );
if( !NT_SUCCESS(waitStatus) )
{
DbgPrint( "KeWaitForMultipleObjects failed: 0x%x\n", waitStatus );
continue;
}
//
// Check the DbgPrint flags to see if we really want to write
// this.
//
if( !(DigiPrintFlags & MEM_PRINT_FLAG_FILE) )
{
//
// There is nothing for us to do.
//
if( UnloadingDriver )
{
KeSetEvent( &MemPrintQuitEvent,
2,
FALSE );
PsTerminateSystemThread( STATUS_SUCCESS );
}
continue;
}
//
// Take a snap shoot of the in pointer. It is possible it will
// change on us.
//
KeAcquireSpinLock( &MemPrintSpinLock, &oldIrql );
CurrentInOffset = BufferInOffset;
CurrentOutOffset = BufferOutOffset;
KeReleaseSpinLock( &MemPrintSpinLock, oldIrql );
while( CurrentInOffset != CurrentOutOffset )
{
ASSERT( CurrentInOffset <= MemPrintBufferSize );
ASSERT( CurrentOutOffset <= MemPrintBufferSize );
if( CurrentInOffset > CurrentOutOffset )
{
//
// We only need to do one write to the log file.
//
NumberBytesToWrite = CurrentInOffset - CurrentOutOffset;
}
else
{
//
// We have a buffer wrap situation and as a result need
// to account by making two write's to the log file.
//
NumberBytesToWrite = MemPrintBufferSize - CurrentOutOffset;
}
ASSERT( (CurrentOutOffset + NumberBytesToWrite) <= MemPrintBufferSize );
tmpPtr = MemPrintBuffer + CurrentOutOffset;
//
// Start the write operation. The APC routine will handle
// checking the return status from the write and updating
// BufferOutOffset.
//
status = ZwWriteFile( fileHandle,
NULL,
(PIO_APC_ROUTINE)MemPrintWriteCompleteApc,
(PVOID)NumberBytesToWrite,
&localIoStatusBlock,
tmpPtr,
NumberBytesToWrite,
&totalBytesWritten,
NULL );
if( !NT_SUCCESS(status) )
{
DbgPrint( "ZwWriteFile for log file failed: 0x%x\n", status );
}
//
// Update the count of bytes written to the log file.
//
CurrentOutOffset += NumberBytesToWrite;
ASSERT( CurrentOutOffset <= MemPrintBufferSize );
if( CurrentOutOffset >= MemPrintBufferSize )
CurrentOutOffset = 0;
totalBytesWritten.QuadPart += NumberBytesToWrite;
//
// Extend the file if we have reached the end of what we have
// thus far allocated for the file. This increases performance
// by extending the file here rather than in the file system,
// which would have to extend it each time a write past end of
// file comes in.
//
if( (totalBytesWritten.QuadPart >= fileAllocationSize.QuadPart) )
{
fileAllocationSize.QuadPart = fileAllocationSize.QuadPart + MemPrintBufferSize;
DbgPrint( "Enlarging logfile %s to %ld bytes.\n",
DefaultLogFileName,
fileAllocationSize.LowPart );
status = ZwSetInformationFile( fileHandle,
&localIoStatusBlock,
&fileAllocationSize,
sizeof(fileAllocationSize),
FileAllocationInformation );
if( !NT_SUCCESS(status) )
{
DbgPrint( "Attempt to extend log file failed: 0x%x\n", status );
fileAllocationSize.QuadPart = fileAllocationSize.QuadPart - MemPrintBufferSize;
}
}
}
if( UnloadingDriver )
{
KeSetEvent( &MemPrintQuitEvent,
2,
FALSE );
PsTerminateSystemThread( STATUS_SUCCESS );
}
}
return;
} // DigiPrintWriteThread
VOID MemPrintWriteCompleteApc( IN PVOID ApcContext,
IN PIO_STATUS_BLOCK IoStatusBlock )
/*++
Routine Description:
This APC routine is called when the current write to disk complete.
It checks for success, printing a message if the write failed.
It also updates BufferOutOffset.
Arguments:
ApcContext - contains what the CurrentInOffset was when
ZwWriteFile was called.
IoStatusBlock - the status block for the operation.
Return Value:
None.
--*/
{
KIRQL oldIrql;
ULONG NumberBytesWritten=(ULONG)ApcContext;
if( !NT_SUCCESS(IoStatusBlock->Status) )
{
DbgPrint( "ZwWriteFile for subbuffer %ld failed: 0x%x\n",
ApcContext,
IoStatusBlock->Status );
return;
}
// ASSERT( IoStatusBlock->Information == NumberBytesWritten );
//
// Acquire the spin lock that protects memory print global variables
// and set the subbuffer writing boolean to FALSE so that other
// threads can write to the subbuffer if necessary.
//
KeAcquireSpinLock( &MemPrintSpinLock, &oldIrql );
if( BufferOutOffset + IoStatusBlock->Information > MemPrintBufferSize )
{
BufferOutOffset = ( (BufferOutOffset + IoStatusBlock->Information) -
MemPrintBufferSize );
}
else if( BufferOutOffset + IoStatusBlock->Information < MemPrintBufferSize )
{
BufferOutOffset += IoStatusBlock->Information;
}
else
{
BufferOutOffset = 0;
}
// BufferOutOffset += NumberBytesWritten;
//
// ASSERT( BufferOutOffset <= MemPrintBufferSize );
//
// if( BufferOutOffset == MemPrintBufferSize )
// BufferOutOffset = 0;
KeReleaseSpinLock( &MemPrintSpinLock, oldIrql );
return;
} // MemPrintWriteCompleteApc