923 lines
22 KiB
C
923 lines
22 KiB
C
/*++
|
||
|
||
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
|
||
|
||
|