Source code of Windows XP (NT5)
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.

917 lines
23 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. memprint.c
  5. Abstract:
  6. This module contains the routines to implement in-memory DbgPrint.
  7. DbgPrint text is stored in a large circular buffer, and optionally
  8. written to a file and/or the debug console. Output to file is
  9. buffered to allow high performance by the file system.
  10. Author:
  11. David Treadwell (davidtr) 05-Oct-1990
  12. Revision History:
  13. --*/
  14. #include "exp.h"
  15. #pragma hdrstop
  16. #include <stdarg.h>
  17. #include <string.h>
  18. #include <memprint.h>
  19. #undef DbgPrint
  20. //
  21. // Forward declarations.
  22. //
  23. VOID
  24. MemPrintWriteCompleteApc (
  25. IN PVOID ApcContext,
  26. IN PIO_STATUS_BLOCK IoStatusBlock,
  27. IN ULONG Reserved
  28. );
  29. VOID
  30. MemPrintWriteThread (
  31. IN PVOID Dummy
  32. );
  33. //
  34. // The maximum message size is the largest message that can be written
  35. // by a single call to MemPrint.
  36. #define MEM_PRINT_MAX_MESSAGE_SIZE 256
  37. //
  38. // These macros aid in determining the size of a subbuffer and the
  39. // subbuffer corresponding to an index into the circular buffer.
  40. //
  41. #define MEM_PRINT_SUBBUFFER_SIZE (MemPrintBufferSize / MemPrintSubbufferCount)
  42. #define GET_MEM_PRINT_SUBBUFFER(i) ((CSHORT)( (i) / MEM_PRINT_SUBBUFFER_SIZE ))
  43. //
  44. // The definition of the header put before each message if the
  45. // MEM_PRINT_FLAG_HEADER bit of MemPrintFlags is turned on.
  46. //
  47. typedef struct _MEM_PRINT_MESSAGE_HEADER {
  48. USHORT Size;
  49. USHORT Type;
  50. } MEM_PRINT_MESSAGE_HEADER, *PMEM_PRINT_MESSAGE_HEADER;
  51. //
  52. // Global data. It is all protected by MemPrintSpinLock.
  53. //
  54. CLONG MemPrintBufferSize = MEM_PRINT_DEF_BUFFER_SIZE;
  55. CLONG MemPrintSubbufferCount = MEM_PRINT_DEF_SUBBUFFER_COUNT;
  56. PCHAR MemPrintBuffer;
  57. ULONG MemPrintFlags = MEM_PRINT_FLAG_CONSOLE;
  58. KSPIN_LOCK MemPrintSpinLock;
  59. CHAR MemPrintTempBuffer[MEM_PRINT_MAX_MESSAGE_SIZE];
  60. BOOLEAN MemPrintInitialized = FALSE;
  61. //
  62. // MemPrintIndex stores the current index into the circular buffer.
  63. //
  64. CLONG MemPrintIndex = 0;
  65. //
  66. // MemPrintCurrentSubbuffer stores the index of the subbuffer currently
  67. // being used to hold data. It has a range between 0 and
  68. // MemPrintSubbufferCount-1.
  69. //
  70. CLONG MemPrintCurrentSubbuffer = 0;
  71. //
  72. // The MemPrintSubbufferWriting array is used to indicate when a
  73. // subbuffer is being written to disk. While this occurs, new data
  74. // cannot be written to the subbuffer.
  75. //
  76. BOOLEAN MemPrintSubbufferWriting[MEM_PRINT_MAX_SUBBUFFER_COUNT];
  77. //
  78. // The MemPrintSubbufferFullEvent array is used to communicate between
  79. // threads calling MemPrintMemory and the thread that writes the log
  80. // file. When a subbuffer is full and ready to be written to disk,
  81. // the corresponding event in this array is signaled, which causes
  82. // the write thread to wake up and perform the write.
  83. //
  84. KEVENT MemPrintSubbufferFullEvent[MEM_PRINT_MAX_SUBBUFFER_COUNT];
  85. VOID
  86. MemPrintInitialize (
  87. VOID
  88. )
  89. /*++
  90. Routine Description:
  91. This is the initialization routine for the in-memory DbgPrint routine.
  92. It should be called before the first call to MemPrint to set up the
  93. various structures used and to start the log file write thread.
  94. Arguments:
  95. None.
  96. Return Value:
  97. None.
  98. --*/
  99. {
  100. CLONG i;
  101. NTSTATUS status;
  102. HANDLE threadHandle;
  103. if ( MemPrintInitialized ) {
  104. return;
  105. }
  106. //
  107. // Allocate memory for the circular buffer that will receive
  108. // the text and data. If we can't do it, try again with a buffer
  109. // half as large. If that fails, quit trying.
  110. //
  111. MemPrintBuffer = ExAllocatePoolWithTag( NonPagedPool, MemPrintBufferSize, 'rPeM' );
  112. if ( MemPrintBuffer == NULL ) {
  113. MemPrintBufferSize /= 2;
  114. DbgPrint( "Unable to allocate DbgPrint buffer--trying size = %ld\n",
  115. MemPrintBufferSize );
  116. MemPrintBuffer = ExAllocatePoolWithTag( NonPagedPool, MemPrintBufferSize, 'rPeM' );
  117. if ( MemPrintBuffer == NULL ) {
  118. DbgPrint( "Couldn't allocate DbgPrint buffer.\n" );
  119. return;
  120. } else {
  121. //DbgPrint( "MemPrint buffer from %lx to %lx\n",
  122. // MemPrintBuffer, MemPrintBuffer + MemPrintBufferSize );
  123. }
  124. } else {
  125. //DbgPrint( "MemPrint buffer from %lx to %lx\n",
  126. // MemPrintBuffer, MemPrintBuffer + MemPrintBufferSize );
  127. }
  128. //
  129. // Allocate the spin lock that protects access to the various
  130. // pointers and the circular buffer. This ensures integrity of the
  131. // buffer.
  132. //
  133. KeInitializeSpinLock( &MemPrintSpinLock );
  134. //
  135. // Make sure that the subbuffer count is in range. (We assume that
  136. // the number is a power of 2.)
  137. //
  138. if ( MemPrintSubbufferCount < 2 ) {
  139. MemPrintSubbufferCount = 2;
  140. } else if ( MemPrintSubbufferCount > MEM_PRINT_MAX_SUBBUFFER_COUNT ) {
  141. MemPrintSubbufferCount = MEM_PRINT_MAX_SUBBUFFER_COUNT;
  142. }
  143. //
  144. // Initialize the array of BOOLEANs that determines which subbuffers
  145. // are being written to disk and therefore cannot be used to store
  146. // new DbgPrint data.
  147. //
  148. // Initialize the array of events that indicates that a subbuffer is
  149. // ready to be written to disk.
  150. //
  151. for ( i = 0; i < MemPrintSubbufferCount; i++ ) {
  152. MemPrintSubbufferWriting[i] = FALSE;
  153. KeInitializeEvent(
  154. &MemPrintSubbufferFullEvent[i],
  155. SynchronizationEvent,
  156. FALSE
  157. );
  158. }
  159. //
  160. // Start the thread that writes subbuffers from the large circular
  161. // buffer to disk.
  162. //
  163. status = PsCreateSystemThread(
  164. &threadHandle,
  165. PROCESS_ALL_ACCESS,
  166. NULL,
  167. NtCurrentProcess(),
  168. NULL,
  169. MemPrintWriteThread,
  170. NULL
  171. );
  172. if ( !NT_SUCCESS(status) ) {
  173. DbgPrint( "MemPrintInitialize: PsCreateSystemThread failed: %X\n",
  174. status );
  175. return;
  176. }
  177. MemPrintInitialized = TRUE;
  178. ZwClose( threadHandle );
  179. return;
  180. } // MemPrintInitialize
  181. VOID
  182. MemPrint (
  183. CHAR *Format, ...
  184. )
  185. /*++
  186. Routine Description:
  187. This routine is called in place of DbgPrint to process in-memory
  188. printing.
  189. Arguments:
  190. Format - A format string in the style of DbgPrint.
  191. - formatting arguments.
  192. Return Value:
  193. None.
  194. --*/
  195. {
  196. va_list arglist;
  197. KIRQL oldIrql;
  198. CLONG nextSubbuffer;
  199. PMEM_PRINT_MESSAGE_HEADER messageHeader;
  200. CHAR tempBuffer[MEM_PRINT_MAX_MESSAGE_SIZE];
  201. va_start(arglist, Format);
  202. _vsnprintf( tempBuffer, sizeof( tempBuffer ), Format, arglist );
  203. va_end(arglist);
  204. //
  205. // If memory DbgPrint has not been initialized, simply print to the
  206. // console.
  207. //
  208. if ( !MemPrintInitialized ) {
  209. DbgPrint( "%s", tempBuffer );
  210. return;
  211. }
  212. //
  213. // Acquire the spin lock that synchronizes access to the pointers
  214. // and circular buffer.
  215. //
  216. KeAcquireSpinLock( &MemPrintSpinLock, &oldIrql );
  217. //
  218. // Make sure that the request will fit. xx_sprintf will just dump
  219. // all it gets, so assume the message is maximum size, and, if the
  220. // request would go into the next subbuffer and it is writing, fail
  221. // the request.
  222. //
  223. nextSubbuffer =
  224. GET_MEM_PRINT_SUBBUFFER( MemPrintIndex + MEM_PRINT_MAX_MESSAGE_SIZE );
  225. if ( nextSubbuffer != MemPrintCurrentSubbuffer ) {
  226. //
  227. // The request will go to a new subbuffer. Check if we should
  228. // wrap around to the first subbuffer (i.e. start of circular
  229. // buffer).
  230. //
  231. if ( nextSubbuffer == MemPrintSubbufferCount ) {
  232. nextSubbuffer = 0;
  233. }
  234. //
  235. // Is that subbuffer available for use?
  236. //
  237. if ( MemPrintSubbufferWriting[nextSubbuffer] ) {
  238. //
  239. // It is in use. Print to the console. Oh well.
  240. //
  241. KeReleaseSpinLock( &MemPrintSpinLock, oldIrql );
  242. DbgPrint( "%s", tempBuffer );
  243. return;
  244. }
  245. //
  246. // If we went to subbuffer 0 and it is available to receive
  247. // data, set up the "end of last subbuffer" conditions and reset
  248. // the index into the circular buffer. By setting a special
  249. // type value in the message header that precedes the garbage at
  250. // the end of the last subbuffer, an interpreter program can
  251. // know to skip over the garbage by using the size in the
  252. // header. This is done instead of writing only good data so
  253. // that we can write just full sectors to disk, thereby
  254. // enhancing write performance.
  255. //
  256. if ( nextSubbuffer == 0 ) {
  257. //
  258. // Set up the message header. This always gets done at the
  259. // end of the circular buffer, regardless of the flags bit.
  260. //
  261. messageHeader =
  262. (PMEM_PRINT_MESSAGE_HEADER)&MemPrintBuffer[MemPrintIndex];
  263. RtlStoreUshort(
  264. &messageHeader->Size,
  265. (USHORT)(MemPrintBufferSize - MemPrintIndex - 1)
  266. );
  267. RtlStoreUshort(
  268. &messageHeader->Type,
  269. (USHORT)0xffff
  270. );
  271. //
  272. // Zero out the rest of the subbuffer.
  273. //
  274. for ( MemPrintIndex += sizeof(MEM_PRINT_MESSAGE_HEADER);
  275. MemPrintIndex < MemPrintBufferSize;
  276. MemPrintIndex++ ) {
  277. MemPrintBuffer[MemPrintIndex] = 0;
  278. }
  279. //
  280. // Reset the index to start at the beginning of the circular
  281. // buffer.
  282. //
  283. MemPrintIndex = 0;
  284. }
  285. }
  286. //
  287. // Store a pointer to the location that will contain the message
  288. // header.
  289. //
  290. messageHeader = (PMEM_PRINT_MESSAGE_HEADER)&MemPrintBuffer[MemPrintIndex];
  291. if ( MemPrintFlags & MEM_PRINT_FLAG_HEADER ) {
  292. MemPrintIndex += sizeof(MEM_PRINT_MESSAGE_HEADER);
  293. }
  294. //
  295. // Dump the formatted string to the subbuffer. xx_sprintf is a special
  296. // version of sprintf that takes a variable argument list.
  297. //
  298. ASSERT( MemPrintIndex + MEM_PRINT_MAX_MESSAGE_SIZE -
  299. sizeof(MEM_PRINT_MESSAGE_HEADER) <= MemPrintBufferSize );
  300. RtlCopyMemory( &MemPrintBuffer[MemPrintIndex], tempBuffer, strlen(tempBuffer)+1 );
  301. MemPrintIndex += strlen(tempBuffer);
  302. //
  303. // Write the total message size to the message header.
  304. //
  305. if ( MemPrintFlags & MEM_PRINT_FLAG_HEADER ) {
  306. messageHeader->Size =
  307. (USHORT)( &MemPrintBuffer[MemPrintIndex] - (PCHAR)messageHeader );
  308. messageHeader->Type = (USHORT)0xdead;
  309. messageHeader++;
  310. }
  311. //
  312. // If it was too large, there's a potential problem with writing off
  313. // the end of the circular buffer. Print the offending message to
  314. // the console and breakpoint.
  315. //
  316. if ( &MemPrintBuffer[MemPrintIndex] - (PCHAR)messageHeader >
  317. MEM_PRINT_MAX_MESSAGE_SIZE ) {
  318. DbgPrint( "Message too long!! :\n" );
  319. DbgPrint( "%s", messageHeader );
  320. DbgBreakPoint( );
  321. }
  322. //
  323. // Print to the console if the appropriate flag is on.
  324. //
  325. if ( MemPrintFlags & MEM_PRINT_FLAG_CONSOLE ) {
  326. DbgPrint( "%s", messageHeader );
  327. }
  328. //
  329. // Calculate whether we have stepped into a new subbuffer.
  330. //
  331. nextSubbuffer = GET_MEM_PRINT_SUBBUFFER( MemPrintIndex );
  332. if ( nextSubbuffer != MemPrintCurrentSubbuffer ) {
  333. //DbgPrint( "Subbuffer %ld complete.\n", MemPrintCurrentSubbuffer );
  334. //
  335. // We did step into a new subbuffer, so set the boolean to
  336. // indicate that the old subbuffer is writing to disk, thereby
  337. // preventing it from being overwritten until the write is
  338. // complete.
  339. //
  340. MemPrintSubbufferWriting[MemPrintCurrentSubbuffer] = TRUE;
  341. //
  342. // Set the event that will wake up the thread writing subbuffers
  343. // to disk.
  344. //
  345. KeSetEvent(
  346. &MemPrintSubbufferFullEvent[MemPrintCurrentSubbuffer],
  347. 2,
  348. FALSE
  349. );
  350. //
  351. // Update the current subbuffer.
  352. //
  353. MemPrintCurrentSubbuffer = nextSubbuffer;
  354. }
  355. KeReleaseSpinLock( &MemPrintSpinLock, oldIrql );
  356. return;
  357. } // MemPrint
  358. VOID
  359. MemPrintFlush (
  360. VOID
  361. )
  362. /*++
  363. Routine Description:
  364. This routine causes the current subbuffer to be written to disk,
  365. regardless of how full it is. The unwritten part of the subbuffer
  366. is zeroed before writing.
  367. Arguments:
  368. None.
  369. Return Value:
  370. None.
  371. --*/
  372. {
  373. KIRQL oldIrql;
  374. PMEM_PRINT_MESSAGE_HEADER messageHeader;
  375. CLONG nextSubbufferIndex;
  376. LARGE_INTEGER delayInterval;
  377. //
  378. // Acquire the spin lock that protects memory DbgPrint variables.
  379. //
  380. KeAcquireSpinLock( &MemPrintSpinLock, &oldIrql );
  381. DbgPrint( "Flushing subbuffer %ld\n", MemPrintCurrentSubbuffer );
  382. //
  383. // Set up the header that indicates that unused space follows.
  384. //
  385. messageHeader =
  386. (PMEM_PRINT_MESSAGE_HEADER)&MemPrintBuffer[MemPrintIndex];
  387. messageHeader->Size =
  388. (USHORT)(MemPrintBufferSize - MemPrintIndex - 1);
  389. messageHeader->Type = (USHORT)0xffff;
  390. //
  391. // Determine where the next subbuffer starts.
  392. //
  393. nextSubbufferIndex =
  394. (MemPrintCurrentSubbuffer + 1) * MEM_PRINT_SUBBUFFER_SIZE;
  395. //
  396. // Zero out the rest of the subbuffer.
  397. //
  398. for ( MemPrintIndex += sizeof(MEM_PRINT_MESSAGE_HEADER);
  399. MemPrintIndex < nextSubbufferIndex;
  400. MemPrintIndex++ ) {
  401. MemPrintBuffer[MemPrintIndex] = 0;
  402. }
  403. //
  404. // Indicate that the subbuffer should be written to disk.
  405. //
  406. MemPrintSubbufferWriting[MemPrintCurrentSubbuffer] = TRUE;
  407. KeSetEvent(
  408. &MemPrintSubbufferFullEvent[MemPrintCurrentSubbuffer],
  409. 8,
  410. FALSE
  411. );
  412. //
  413. // Increment the current subbuffer so that it corresponds with the
  414. // buffer index.
  415. //
  416. MemPrintCurrentSubbuffer++;
  417. KeReleaseSpinLock( &MemPrintSpinLock, oldIrql );
  418. //
  419. // Delay so that the memory print write thread wakes up and performs
  420. // the write to disk.
  421. //
  422. // !!! This is obviously not a perfect solution--the write thread
  423. // may never wake up, so this could complete before the flush
  424. // is really done.
  425. //
  426. delayInterval.QuadPart = -10*10*1000*1000;
  427. DbgPrint( "Delaying...\n" );
  428. KeDelayExecutionThread( KernelMode, TRUE, &delayInterval );
  429. DbgPrint( "Woke up.\n" );
  430. return;
  431. } // MemPrintFlush
  432. VOID
  433. MemPrintWriteThread (
  434. IN PVOID Dummy
  435. )
  436. /*++
  437. Routine Description:
  438. The log file write thread executes this routine. It sets up the
  439. log file for writing, then waits for subbuffers to fill, writing
  440. them to disk when they do. When the log file fills, new space
  441. for it is allocated on disk to prevent the file system from
  442. having to do it.
  443. Arguments:
  444. Dummy - Ignored.
  445. Return Value:
  446. None.
  447. --*/
  448. {
  449. NTSTATUS status;
  450. IO_STATUS_BLOCK ioStatusBlock[MEM_PRINT_MAX_SUBBUFFER_COUNT];
  451. IO_STATUS_BLOCK localIoStatusBlock;
  452. CLONG i;
  453. KPRIORITY threadPriorityLevel;
  454. NTSTATUS waitStatus;
  455. PVOID waitObjects[64];
  456. KWAIT_BLOCK waitBlockArray[MEM_PRINT_MAX_SUBBUFFER_COUNT];
  457. OBJECT_ATTRIBUTES objectAttributes;
  458. PCHAR fileName = MEM_PRINT_LOG_FILE_NAME;
  459. ANSI_STRING fileNameString;
  460. HANDLE fileHandle;
  461. LARGE_INTEGER fileAllocation;
  462. LARGE_INTEGER fileAllocationIncrement;
  463. LARGE_INTEGER totalBytesWritten;
  464. LARGE_INTEGER writeSize;
  465. LARGE_INTEGER delayInterval;
  466. ULONG attempts = 0;
  467. UNICODE_STRING UnicodeFileName;
  468. Dummy;
  469. //
  470. // Initialize the string containing the file name and the object
  471. // attributes structure that will describe the log file to open.
  472. //
  473. RtlInitAnsiString( &fileNameString, fileName );
  474. status = RtlAnsiStringToUnicodeString(&UnicodeFileName,&fileNameString,TRUE);
  475. if ( !NT_SUCCESS(status) ) {
  476. NtTerminateThread( NtCurrentThread(), status );
  477. }
  478. InitializeObjectAttributes(
  479. &objectAttributes,
  480. &UnicodeFileName,
  481. OBJ_CASE_INSENSITIVE,
  482. NULL,
  483. NULL
  484. );
  485. //
  486. // Set the allocation size of the log file to be three times the
  487. // size of the circular buffer. When it fills up, we'll extend
  488. // it.
  489. //
  490. fileAllocationIncrement.LowPart = MemPrintBufferSize * 8;
  491. fileAllocationIncrement.HighPart = 0;
  492. fileAllocation = fileAllocationIncrement;
  493. //
  494. // Open the log file.
  495. //
  496. // !!! The loop here is to help avoid a system initialization
  497. // timing problem, and should be removed when the problem is
  498. // fixed.
  499. //
  500. while ( TRUE ) {
  501. status = NtCreateFile(
  502. &fileHandle,
  503. FILE_WRITE_DATA,
  504. &objectAttributes,
  505. &localIoStatusBlock,
  506. &fileAllocation,
  507. FILE_ATTRIBUTE_NORMAL,
  508. FILE_SHARE_READ,
  509. FILE_OVERWRITE_IF,
  510. FILE_SEQUENTIAL_ONLY,
  511. NULL,
  512. 0L
  513. );
  514. if ( (status != STATUS_OBJECT_PATH_NOT_FOUND) || (++attempts >= 3) ) {
  515. RtlFreeUnicodeString(&UnicodeFileName);
  516. break;
  517. }
  518. delayInterval.QuadPart = -5*10*1000*1000; // five second delay
  519. KeDelayExecutionThread( KernelMode, FALSE, &delayInterval );
  520. }
  521. if ( !NT_SUCCESS(status) ) {
  522. DbgPrint( "NtCreateFile for log file failed: %X\n", status );
  523. NtTerminateThread( NtCurrentThread(), status );
  524. }
  525. //
  526. // Initialize the total bytes written and write size variables.
  527. //
  528. totalBytesWritten.LowPart = 0;
  529. totalBytesWritten.HighPart = 0;
  530. writeSize.LowPart = MEM_PRINT_SUBBUFFER_SIZE;
  531. writeSize.HighPart = 0;
  532. //
  533. // Set up the wait objects array for a call to KeWaitForMultipleObjects.
  534. //
  535. for ( i = 0; i < MemPrintSubbufferCount; i++ ) {
  536. waitObjects[i] = &MemPrintSubbufferFullEvent[i];
  537. }
  538. //
  539. // Set the priority of the write thread.
  540. //
  541. threadPriorityLevel = LOW_REALTIME_PRIORITY + 1;
  542. status = NtSetInformationThread(
  543. NtCurrentThread(),
  544. ThreadPriority,
  545. &threadPriorityLevel,
  546. sizeof(threadPriorityLevel)
  547. );
  548. if ( !NT_SUCCESS(status) ) {
  549. DbgPrint( "Unable to set error log thread priority: %X\n", status );
  550. }
  551. //
  552. // Loop waiting for one of the subbuffer full events to be signaled.
  553. // When one is signaled, wake up and write the subbuffer to the log
  554. // file.
  555. //
  556. while ( TRUE ) {
  557. waitStatus = KeWaitForMultipleObjects(
  558. (CCHAR)MemPrintSubbufferCount,
  559. waitObjects,
  560. WaitAny,
  561. Executive,
  562. KernelMode,
  563. TRUE,
  564. NULL,
  565. waitBlockArray
  566. );
  567. if ( !NT_SUCCESS(waitStatus) ) {
  568. DbgPrint( "KeWaitForMultipleObjects failed: %X\n", waitStatus );
  569. NtTerminateThread( NtCurrentThread(), waitStatus );
  570. } //else {
  571. //DbgPrint( "Writing subbuffer %ld...\n", waitStatus );
  572. //}
  573. ASSERT( (CCHAR)waitStatus < (CCHAR)MemPrintSubbufferCount );
  574. //
  575. // Check the DbgPrint flags to see if we really want to write
  576. // this.
  577. //
  578. if ( (MemPrintFlags & MEM_PRINT_FLAG_FILE) == 0 ) {
  579. KIRQL oldIrql;
  580. KeAcquireSpinLock( &MemPrintSpinLock, &oldIrql );
  581. MemPrintSubbufferWriting[ waitStatus ] = FALSE;
  582. KeReleaseSpinLock( &MemPrintSpinLock, oldIrql );
  583. continue;
  584. }
  585. //
  586. // Start the write operation. The APC routine will handle
  587. // checking the return status from the write and resetting
  588. // the MemPrintSubbufferWriting boolean.
  589. //
  590. status = NtWriteFile(
  591. fileHandle,
  592. NULL,
  593. MemPrintWriteCompleteApc,
  594. (PVOID)waitStatus,
  595. &ioStatusBlock[waitStatus],
  596. &MemPrintBuffer[waitStatus * MEM_PRINT_SUBBUFFER_SIZE],
  597. MEM_PRINT_SUBBUFFER_SIZE,
  598. &totalBytesWritten,
  599. NULL
  600. );
  601. if ( !NT_SUCCESS(status) ) {
  602. DbgPrint( "NtWriteFile for log file failed: %X\n", status );
  603. }
  604. //
  605. // Update the count of bytes written to the log file.
  606. //
  607. totalBytesWritten.QuadPart = totalBytesWritten.QuadPart + writeSize.QuadPart;
  608. //
  609. // Extend the file if we have reached the end of what we have
  610. // thus far allocated for the file. This increases performance
  611. // by extending the file here rather than in the file system,
  612. // which would have to extend it each time a write past end of
  613. // file comes in.
  614. //
  615. if ( totalBytesWritten.QuadPart >= fileAllocation.QuadPart ) {
  616. fileAllocation.QuadPart =
  617. fileAllocation.QuadPart + fileAllocationIncrement.QuadPart;
  618. DbgPrint( "Enlarging log file to %ld bytes.\n",
  619. fileAllocation.LowPart );
  620. status = NtSetInformationFile(
  621. fileHandle,
  622. &localIoStatusBlock,
  623. &fileAllocation,
  624. sizeof(fileAllocation),
  625. FileAllocationInformation
  626. );
  627. if ( !NT_SUCCESS(status) ) {
  628. DbgPrint( "Attempt to extend log file failed: %X\n", status );
  629. fileAllocation.QuadPart =
  630. fileAllocation.QuadPart - fileAllocationIncrement.QuadPart;
  631. }
  632. }
  633. }
  634. return;
  635. } // MemPrintWriteThread
  636. VOID
  637. MemPrintWriteCompleteApc (
  638. IN PVOID ApcContext,
  639. IN PIO_STATUS_BLOCK IoStatusBlock,
  640. IN ULONG Reserved
  641. )
  642. /*++
  643. Routine Description:
  644. This APC routine is called when subbuffer writes to disk complete.
  645. It checks for success, printing a message if the write failed.
  646. It also sets the appropriate MemPrintSubbufferWriting location to
  647. FALSE so that the subbuffer can be reused.
  648. Arguments:
  649. ApcContext - contains the index of the subbuffer just written.
  650. IoStatusBlock - the status block for the operation.
  651. Reserved - not used; reserved for future use.
  652. Return Value:
  653. None.
  654. --*/
  655. {
  656. KIRQL oldIrql;
  657. if ( !NT_SUCCESS(IoStatusBlock->Status) ) {
  658. DbgPrint( "NtWriteFile for subbuffer %ld failed: %X\n",
  659. ApcContext, IoStatusBlock->Status );
  660. return;
  661. }
  662. //DbgPrint( "Write complete for subbuffer %ld.\n", ApcContext );
  663. DbgPrint( "." );
  664. //
  665. // Acquire the spin lock that protects memory print global variables
  666. // and set the subbuffer writing boolean to FALSE so that other
  667. // threads can write to the subbuffer if necessary.
  668. //
  669. KeAcquireSpinLock( &MemPrintSpinLock, &oldIrql );
  670. MemPrintSubbufferWriting[ (ULONG_PTR)ApcContext ] = FALSE;
  671. KeReleaseSpinLock( &MemPrintSpinLock, oldIrql );
  672. return;
  673. Reserved;
  674. } // MemPrintWriteCompleteApc