diff options
Diffstat (limited to 'private/ntos/ndis/digi/digifile/memprint.c')
-rw-r--r-- | private/ntos/ndis/digi/digifile/memprint.c | 922 |
1 files changed, 922 insertions, 0 deletions
diff --git a/private/ntos/ndis/digi/digifile/memprint.c b/private/ntos/ndis/digi/digifile/memprint.c new file mode 100644 index 000000000..e81189a7b --- /dev/null +++ b/private/ntos/ndis/digi/digifile/memprint.c @@ -0,0 +1,922 @@ +/*++ + +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 + + |