Leaked source code of windows server 2003
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.

2990 lines
92 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. tracelog.c
  5. Abstract:
  6. This is the source file that implements the private routines for
  7. the performance event tracing and logging facility.
  8. The routines here work on a single event tracing session, the logging
  9. thread, and buffer synchronization within a session.
  10. Author:
  11. Jee Fung Pang (jeepang) 03-Dec-1996
  12. Revision History:
  13. --*/
  14. // TODO: In future, may need to align buffer size to larger of disk alignment
  15. // or 1024.
  16. #pragma warning(disable:4214)
  17. #pragma warning(disable:4115)
  18. #pragma warning(disable:4201)
  19. #pragma warning(disable:4127)
  20. #include "ntverp.h"
  21. #include "ntos.h"
  22. #include "wmikmp.h"
  23. #include <zwapi.h>
  24. #pragma warning(default:4214)
  25. #pragma warning(default:4115)
  26. #pragma warning(default:4201)
  27. #pragma warning(default:4127)
  28. #ifndef _WMIKM_
  29. #define _WMIKM_
  30. #endif
  31. #include "evntrace.h"
  32. //
  33. // Constants and Types used locally
  34. //
  35. #if DBG
  36. ULONG WmipTraceDebugLevel=0;
  37. // 5 All messages
  38. // 4 Messages up to event operations
  39. // 3 Messages up to buffer operations
  40. // 2 Flush operations
  41. // 1 Common operations and debugging statements
  42. // 0 Always on - use for real error
  43. #endif
  44. #define ERROR_RETRY_COUNT 100
  45. #include "tracep.h"
  46. // Non-paged global variables
  47. //
  48. ULONG WmiTraceAlignment = DEFAULT_TRACE_ALIGNMENT;
  49. ULONG WmiUsePerfClock = EVENT_TRACE_CLOCK_SYSTEMTIME; // Global clock switch
  50. LONG WmipRefCount[MAXLOGGERS];
  51. ULONG WmipGlobalSequence = 0;
  52. PWMI_LOGGER_CONTEXT WmipLoggerContext[MAXLOGGERS];
  53. PWMI_BUFFER_HEADER WmipContextSwapProcessorBuffers[MAXIMUM_PROCESSORS];
  54. //
  55. // Paged global variables
  56. //
  57. #ifdef ALLOC_DATA_PRAGMA
  58. #pragma data_seg("PAGEDATA")
  59. #endif
  60. ULONG WmiWriteFailureLimit = ERROR_RETRY_COUNT;
  61. ULONG WmipFileSystemReady = FALSE;
  62. WMI_TRACE_BUFFER_CALLBACK WmipGlobalBufferCallback = NULL;
  63. PVOID WmipGlobalCallbackContext = NULL;
  64. #ifdef ALLOC_DATA_PRAGMA
  65. #pragma data_seg()
  66. #endif
  67. //
  68. // Function prototypes for routines used locally
  69. //
  70. NTSTATUS
  71. WmipSwitchBuffer(
  72. IN PWMI_LOGGER_CONTEXT LoggerContext,
  73. IN PWMI_BUFFER_HEADER *BufferPointer,
  74. IN PVOID BufferPointerLocation,
  75. IN ULONG ProcessorNumber
  76. );
  77. NTSTATUS
  78. WmipPrepareHeader(
  79. IN PWMI_LOGGER_CONTEXT LoggerContext,
  80. IN OUT PWMI_BUFFER_HEADER Buffer,
  81. IN USHORT BufferFlag
  82. );
  83. VOID
  84. FASTCALL
  85. WmipPushDirtyBuffer (
  86. PWMI_LOGGER_CONTEXT LoggerContext,
  87. PWMI_BUFFER_HEADER Buffer
  88. );
  89. VOID
  90. FASTCALL
  91. WmipPushFreeBuffer (
  92. PWMI_LOGGER_CONTEXT LoggerContext,
  93. PWMI_BUFFER_HEADER Buffer
  94. );
  95. //
  96. // Logger functions
  97. //
  98. NTSTATUS
  99. WmipCreateLogFile(
  100. IN PWMI_LOGGER_CONTEXT LoggerContext,
  101. IN ULONG SwitchFile,
  102. IN ULONG Append
  103. );
  104. NTSTATUS
  105. WmipSwitchToNewFile(
  106. IN PWMI_LOGGER_CONTEXT LoggerContext
  107. );
  108. NTSTATUS
  109. WmipRequestLogFile(
  110. IN PWMI_LOGGER_CONTEXT LoggerContext
  111. );
  112. NTSTATUS
  113. WmipFinalizeHeader(
  114. IN HANDLE FileHandle,
  115. IN PWMI_LOGGER_CONTEXT LoggerContext
  116. );
  117. NTSTATUS
  118. WmipFlushBuffersWithMarker (
  119. IN PWMI_LOGGER_CONTEXT LoggerContext,
  120. IN PSLIST_ENTRY List,
  121. IN USHORT BufferFlag
  122. );
  123. #ifdef ALLOC_PRAGMA
  124. #pragma alloc_text(PAGE, WmipLogger)
  125. #pragma alloc_text(PAGE, WmipSendNotification)
  126. #pragma alloc_text(PAGE, WmipCreateLogFile)
  127. #pragma alloc_text(PAGE, WmipFlushActiveBuffers)
  128. #pragma alloc_text(PAGE, WmipGenerateFileName)
  129. #pragma alloc_text(PAGE, WmipPrepareHeader)
  130. #pragma alloc_text(PAGE, WmiBootPhase1)
  131. #pragma alloc_text(PAGE, WmipFinalizeHeader)
  132. #pragma alloc_text(PAGE, WmipSwitchToNewFile)
  133. #pragma alloc_text(PAGE, WmipRequestLogFile)
  134. #pragma alloc_text(PAGE, WmipAdjustFreeBuffers)
  135. #pragma alloc_text(PAGEWMI, WmipFlushBuffer)
  136. #pragma alloc_text(PAGEWMI, WmipReserveTraceBuffer)
  137. #pragma alloc_text(PAGEWMI, WmipGetFreeBuffer)
  138. #pragma alloc_text(PAGEWMI, WmiReserveWithPerfHeader)
  139. #pragma alloc_text(PAGEWMI, WmiReserveWithSystemHeader)
  140. #pragma alloc_text(PAGEWMI, WmipAllocateFreeBuffers)
  141. #pragma alloc_text(PAGEWMI, WmipSwitchBuffer)
  142. #pragma alloc_text(PAGEWMI, WmipReleaseTraceBuffer)
  143. #pragma alloc_text(PAGEWMI, WmiReleaseKernelBuffer)
  144. #pragma alloc_text(PAGEWMI, WmipResetBufferHeader)
  145. #pragma alloc_text(PAGEWMI, WmipPushDirtyBuffer)
  146. #pragma alloc_text(PAGEWMI, WmipPushFreeBuffer)
  147. #pragma alloc_text(PAGEWMI, WmipPopFreeContextSwapBuffer)
  148. #pragma alloc_text(PAGEWMI, WmipPushDirtyContextSwapBuffer)
  149. #pragma alloc_text(PAGEWMI, WmipFlushBuffersWithMarker)
  150. #ifdef NTPERF
  151. #pragma alloc_text(PAGEWMI, WmipSwitchPerfmemBuffer)
  152. #endif //NTPERF
  153. #endif
  154. //
  155. // Actual code starts here
  156. //
  157. PWMI_BUFFER_HEADER
  158. WmipGetFreeBuffer(
  159. IN PWMI_LOGGER_CONTEXT LoggerContext
  160. )
  161. //
  162. // This routine works at any IRQL
  163. //
  164. {
  165. PWMI_BUFFER_HEADER Buffer;
  166. PSLIST_ENTRY Entry;
  167. if (LoggerContext->SwitchingInProgress == 0) {
  168. //
  169. // Not in the middle of switching.
  170. //
  171. Entry = InterlockedPopEntrySList(&LoggerContext->FreeList);
  172. if (Entry != NULL) {
  173. Buffer = CONTAINING_RECORD (Entry,
  174. WMI_BUFFER_HEADER,
  175. SlistEntry);
  176. //
  177. // Reset the buffer.
  178. // For circular persist mode, we want to write the buffers as
  179. // RunDown buffers so that post processing would work properly.
  180. //
  181. if (LoggerContext->RequestFlag & REQUEST_FLAG_CIRCULAR_PERSIST) {
  182. WmipResetBufferHeader( LoggerContext, Buffer, WMI_BUFFER_TYPE_RUNDOWN);
  183. }
  184. else {
  185. WmipResetBufferHeader( LoggerContext, Buffer, WMI_BUFFER_TYPE_GENERIC);
  186. }
  187. //
  188. // Maintain some Wmi logger context buffer counts
  189. //
  190. InterlockedDecrement((PLONG) &LoggerContext->BuffersAvailable);
  191. InterlockedIncrement((PLONG) &LoggerContext->BuffersInUse);
  192. TraceDebug((2, "WmipGetFreeBuffer: %2d, %p, Free: %d, InUse: %d, Dirty: %d, Total: %d\n",
  193. LoggerContext->LoggerId,
  194. Buffer,
  195. LoggerContext->BuffersAvailable,
  196. LoggerContext->BuffersInUse,
  197. LoggerContext->BuffersDirty,
  198. LoggerContext->NumberOfBuffers));
  199. return Buffer;
  200. } else {
  201. if (LoggerContext->LoggerMode & EVENT_TRACE_BUFFERING_MODE) {
  202. //
  203. // If we are in BUFFERING Mode, put all buffers from
  204. // Flushlist into FreeList.
  205. //
  206. if (InterlockedIncrement((PLONG) &LoggerContext->SwitchingInProgress) == 1) {
  207. while (Entry = InterlockedPopEntrySList(&LoggerContext->FlushList)) {
  208. Buffer = CONTAINING_RECORD (Entry,
  209. WMI_BUFFER_HEADER,
  210. SlistEntry);
  211. WmipPushFreeBuffer (LoggerContext, Buffer);
  212. }
  213. }
  214. InterlockedDecrement((PLONG) &LoggerContext->SwitchingInProgress);
  215. }
  216. return NULL;
  217. }
  218. } else {
  219. return NULL;
  220. }
  221. }
  222. ULONG
  223. WmipAllocateFreeBuffers(
  224. IN PWMI_LOGGER_CONTEXT LoggerContext,
  225. IN ULONG NumberOfBuffers
  226. )
  227. /*++
  228. Routine Description:
  229. This routine allocate addition buffers into the free buffer list.
  230. Logger can allocate more buffer to handle bursty logging behavior.
  231. This routine can be called by multiple places and counters must be
  232. manipulated using interlocked operations.
  233. Arguments:
  234. LoggerContext - Logger Context
  235. NumberOfBuffers - Number of buffers to be allocated.
  236. Return Value:
  237. The total number of buffers actually allocated. When it is fewer than the requested number:
  238. If this is called when trace is turned on, we fail to turn on trace.
  239. If this is called by walker thread to get more buffer, it is OK.
  240. Environment:
  241. Kernel mode.
  242. --*/
  243. {
  244. ULONG i;
  245. PWMI_BUFFER_HEADER Buffer;
  246. ULONG TotalBuffers;
  247. for (i=0; i<NumberOfBuffers; i++) {
  248. //
  249. // Multiple threads can ask for more buffers, make sure
  250. // we do not go over the maximum.
  251. //
  252. TotalBuffers = InterlockedIncrement(&LoggerContext->NumberOfBuffers);
  253. if (TotalBuffers <= LoggerContext->MaximumBuffers) {
  254. #ifdef NTPERF
  255. if (PERFINFO_IS_LOGGING_TO_PERFMEM()) {
  256. Buffer = (PWMI_BUFFER_HEADER)
  257. PerfInfoReserveBytesFromPerfMem(LoggerContext->BufferSize);
  258. } else {
  259. #endif //NTPERF
  260. Buffer = (PWMI_BUFFER_HEADER)
  261. ExAllocatePoolWithTag(LoggerContext->PoolType,
  262. LoggerContext->BufferSize,
  263. TRACEPOOLTAG);
  264. #ifdef NTPERF
  265. }
  266. #endif //NTPERF
  267. if (Buffer != NULL) {
  268. TraceDebug((3,
  269. "WmipAllocateFreeBuffers: Allocated buffer size %d type %d\n",
  270. LoggerContext->BufferSize, LoggerContext->PoolType));
  271. InterlockedIncrement(&LoggerContext->BuffersAvailable);
  272. //
  273. // Initialize newly created buffer
  274. //
  275. RtlZeroMemory(Buffer, sizeof(WMI_BUFFER_HEADER));
  276. Buffer->CurrentOffset = sizeof(WMI_BUFFER_HEADER);
  277. KeQuerySystemTime(&Buffer->TimeStamp);
  278. Buffer->State.Free = 1;
  279. //
  280. // Insert it into the free List
  281. //
  282. InterlockedPushEntrySList(&LoggerContext->FreeList,
  283. (PSLIST_ENTRY) &Buffer->SlistEntry);
  284. InterlockedPushEntrySList(&LoggerContext->GlobalList,
  285. (PSLIST_ENTRY) &Buffer->GlobalEntry);
  286. } else {
  287. //
  288. // Allocation failed, decrement the NumberOfBuffers
  289. // we increment earlier.
  290. //
  291. InterlockedDecrement(&LoggerContext->NumberOfBuffers);
  292. break;
  293. }
  294. } else {
  295. //
  296. // Maximum is reached, decrement the NumberOfBuffers
  297. // we increment earlier.
  298. //
  299. InterlockedDecrement(&LoggerContext->NumberOfBuffers);
  300. break;
  301. }
  302. }
  303. TraceDebug((2, "WmipAllocateFreeBuffers %3d (%3d): Free: %d, InUse: %d, Dirty: %d, Total: %d\n",
  304. NumberOfBuffers,
  305. i,
  306. LoggerContext->BuffersAvailable,
  307. LoggerContext->BuffersInUse,
  308. LoggerContext->BuffersDirty,
  309. LoggerContext->NumberOfBuffers));
  310. return i;
  311. }
  312. NTSTATUS
  313. WmipAdjustFreeBuffers(
  314. IN PWMI_LOGGER_CONTEXT LoggerContext
  315. )
  316. /*++
  317. Routine Description:
  318. This routine does buffer management. It checks the number of free buffers and
  319. will allocate additonal or free some based on the situation.
  320. Arguments:
  321. LoggerContext - Logger Context
  322. Return Value:
  323. Status
  324. Environment:
  325. Kernel mode.
  326. --*/
  327. {
  328. ULONG FreeBuffers;
  329. ULONG AdditionalBuffers;
  330. NTSTATUS Status = STATUS_SUCCESS;
  331. //
  332. // Check if we need to allocate more buffers
  333. //
  334. FreeBuffers = ExQueryDepthSList(&LoggerContext->FreeList);
  335. if (FreeBuffers < LoggerContext->MinimumBuffers) {
  336. AdditionalBuffers = LoggerContext->MinimumBuffers - FreeBuffers;
  337. if (AdditionalBuffers != WmipAllocateFreeBuffers(LoggerContext, AdditionalBuffers)) {
  338. Status = STATUS_NO_MEMORY;
  339. }
  340. }
  341. return Status;
  342. }
  343. //
  344. // Event trace/record and buffer related routines
  345. //
  346. PSYSTEM_TRACE_HEADER
  347. FASTCALL
  348. WmiReserveWithSystemHeader(
  349. IN ULONG LoggerId,
  350. IN ULONG AuxSize,
  351. IN PETHREAD Thread,
  352. OUT PWMI_BUFFER_HEADER *BufferResource
  353. )
  354. //
  355. // It returns with LoggerContext locked, so caller must explicitly call
  356. // WmipDereferenceLogger() after call WmipReleaseTraceBuffer()
  357. //
  358. {
  359. PSYSTEM_TRACE_HEADER Header;
  360. PWMI_LOGGER_CONTEXT LoggerContext;
  361. LARGE_INTEGER TimeStamp;
  362. #if DBG
  363. LONG RefCount;
  364. #endif
  365. #if DBG
  366. RefCount =
  367. #endif
  368. WmipReferenceLogger(LoggerId);
  369. TraceDebug((4, "WmiReserveWithSystemHeader: %d %d->%d\n",
  370. LoggerId, RefCount-1, RefCount));
  371. LoggerContext = WmipGetLoggerContext(LoggerId);
  372. AuxSize += sizeof(SYSTEM_TRACE_HEADER); // add header size first
  373. Header = WmipReserveTraceBuffer( LoggerContext,
  374. AuxSize,
  375. BufferResource,
  376. &TimeStamp);
  377. if (Header != NULL) {
  378. //
  379. // Now copy the necessary information into the buffer
  380. //
  381. Header->SystemTime = TimeStamp;
  382. if (Thread == NULL) {
  383. Thread = PsGetCurrentThread();
  384. }
  385. Header->Marker = SYSTEM_TRACE_MARKER;
  386. Header->ThreadId = HandleToUlong(Thread->Cid.UniqueThread);
  387. Header->ProcessId = HandleToUlong(Thread->Cid.UniqueProcess);
  388. Header->KernelTime = Thread->Tcb.KernelTime;
  389. Header->UserTime = Thread->Tcb.UserTime;
  390. Header->Packet.Size = (USHORT) AuxSize;
  391. }
  392. else {
  393. #if DBG
  394. RefCount =
  395. #endif
  396. WmipDereferenceLogger(LoggerId); //Interlocked decrement
  397. TraceDebug((4, "WmiReserveWithSystemHeader: %d %d->%d\n",
  398. LoggerId, RefCount+1, RefCount));
  399. }
  400. // NOTE: Caller must still put in a proper MARKER
  401. return Header;
  402. }
  403. PPERFINFO_TRACE_HEADER
  404. FASTCALL
  405. WmiReserveWithPerfHeader(
  406. IN ULONG AuxSize,
  407. OUT PWMI_BUFFER_HEADER *BufferResource
  408. )
  409. //
  410. // It returns with LoggerContext locked, so caller must explicitly call
  411. // WmipDereferenceLogger() after call WmipReleaseTraceBuffer()
  412. //
  413. {
  414. PPERFINFO_TRACE_HEADER Header;
  415. ULONG LoggerId = WmipKernelLogger;
  416. LARGE_INTEGER TimeStamp;
  417. #if DBG
  418. LONG RefCount;
  419. #endif
  420. //
  421. // We must have this check here to see the logger is still running
  422. // before calling ReserveTraceBuffer.
  423. // The stopping thread may have cleaned up the logger context at this
  424. // point, which will cause AV.
  425. // For all other kernel events, this check is made in callouts.c.
  426. //
  427. if (WmipIsLoggerOn(LoggerId) == NULL) {
  428. return NULL;
  429. }
  430. #if DBG
  431. RefCount =
  432. #endif
  433. WmipReferenceLogger(LoggerId);
  434. TraceDebug((4, "WmiReserveWithPerfHeader: %d %d->%d\n",
  435. LoggerId, RefCount-1, RefCount));
  436. AuxSize += FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data); // add header size first
  437. Header = WmipReserveTraceBuffer( WmipGetLoggerContext(LoggerId),
  438. AuxSize,
  439. BufferResource,
  440. &TimeStamp);
  441. if (Header != NULL) {
  442. //
  443. // Now copy the necessary information into the buffer
  444. //
  445. Header->SystemTime = TimeStamp;
  446. Header->Marker = PERFINFO_TRACE_MARKER;
  447. Header->Packet.Size = (USHORT) AuxSize;
  448. } else {
  449. #if DBG
  450. RefCount =
  451. #endif
  452. WmipDereferenceLogger(LoggerId);
  453. TraceDebug((4, "WmiWmiReserveWithPerfHeader: %d %d->%d\n",
  454. LoggerId, RefCount+1, RefCount));
  455. }
  456. // NOTE: Caller must still put in a proper MARKER
  457. return Header;
  458. }
  459. PVOID
  460. FASTCALL
  461. WmipReserveTraceBuffer(
  462. IN PWMI_LOGGER_CONTEXT LoggerContext,
  463. IN ULONG RequiredSize,
  464. OUT PWMI_BUFFER_HEADER *BufferResource,
  465. OUT PLARGE_INTEGER TimeStamp
  466. )
  467. /*++
  468. Routine Description:
  469. This function is the main logging function that reserves spaces for any events.
  470. The algorithm is as follows:
  471. Every time a space is needed, we InterlockedExchangeAdd CurrentOffset.
  472. A local variable Offset is used to track the initial value when
  473. InterlockedExchangeAdd is taken. If there is enough space for this
  474. event (i.e., (Offset + RequiredSize) <= BufferSize), then we have successfully
  475. reserved the space.
  476. If there is not enough space left on this buffer, we will call WmipSwitchBuffer
  477. for a new buffer. In this case, CurrentOffset should be larger than the buffersize.
  478. Since other thread can still be trying to reserve space using thie buffer, we
  479. saved the offset on SavedOffset the the logger thread knows the real offset to be
  480. written to disk.
  481. Note that, since the CurrentOffset if growing monotonically, only one thread is
  482. advancing the CurrentOffset from below BufferSize to beyond BufferSize.
  483. It is this thread's responsibility to set the SavedOffset properly.
  484. Arguments:
  485. LoggerContext - Logger context from current logging session.
  486. RequiredSize - The space needed for logging the data.
  487. Buffer - Pointer to a buffer header
  488. TimeStamp - TimeStamp of the event
  489. Return Value:
  490. The status of running the buffer manager
  491. Environment:
  492. Kernel mode. This routine should work at any IRQL.
  493. --*/
  494. {
  495. PVOID ReservedSpace;
  496. PWMI_BUFFER_HEADER Buffer = NULL;
  497. ULONG Offset;
  498. //
  499. // ISSUES: shsiao 2002/07/26
  500. // Mark it volatile to work around compiler bug.
  501. //
  502. volatile ULONG Processor;
  503. NTSTATUS Status;
  504. if (!WmipIsValidLogger(LoggerContext)) {
  505. return NULL;
  506. }
  507. if (!LoggerContext->CollectionOn) {
  508. return NULL;
  509. }
  510. *BufferResource = NULL;
  511. RequiredSize = (ULONG) ALIGN_TO_POWER2(RequiredSize, WmiTraceAlignment);
  512. if (RequiredSize > LoggerContext->BufferSize - sizeof (WMI_BUFFER_HEADER)) {
  513. goto LostEvent;
  514. }
  515. //
  516. // Get processor number again here due to possible context switch
  517. //
  518. Processor = KeGetCurrentProcessorNumber();
  519. //
  520. // Get the processor specific buffer pool
  521. //
  522. Buffer = LoggerContext->ProcessorBuffers[Processor];
  523. if (Buffer == NULL) {
  524. //
  525. // Nothing in per process list, ask to get a new buffer
  526. //
  527. Status = WmipSwitchBuffer(LoggerContext,
  528. &Buffer,
  529. &LoggerContext->ProcessorBuffers[Processor],
  530. Processor);
  531. if (!NT_SUCCESS(Status)) {
  532. //
  533. // Nothing available
  534. //
  535. goto LostEvent;
  536. }
  537. ASSERT(Buffer != NULL);
  538. }
  539. TryFindSpace:
  540. //
  541. // Increment refcount to buffer first to prevent it from going away
  542. //
  543. InterlockedIncrement(&Buffer->ReferenceCount);
  544. //
  545. // Check if there is enough space in this buffer.
  546. //
  547. Offset = (ULONG) InterlockedExchangeAdd(
  548. (PLONG) &Buffer->CurrentOffset, RequiredSize);
  549. if (Offset+RequiredSize <= LoggerContext->BufferSize) {
  550. //
  551. // Successfully reserved the space
  552. // Get the timestamp of the event
  553. //
  554. if (TimeStamp) {
  555. #ifdef NTPERF
  556. PerfTimeStamp((*TimeStamp));
  557. #else
  558. TimeStamp->QuadPart = (*LoggerContext->GetCpuClock)();
  559. #endif
  560. }
  561. //
  562. // Set up the space pointer
  563. //
  564. ReservedSpace = (PVOID) (Offset + (char*)Buffer);
  565. if (LoggerContext->SequencePtr) {
  566. *((PULONG) ReservedSpace) =
  567. (ULONG)InterlockedIncrement(LoggerContext->SequencePtr);
  568. }
  569. goto FoundSpace;
  570. } else {
  571. //
  572. // There is not enough space left to log this event,
  573. // Ask for buffer switch. The WmipSwitchBuffer()
  574. // will push this current buffer into Dirty list.
  575. //
  576. // Before asking for buffer switch,
  577. // check if I am the one that got overboard.
  578. // If yes, put the correct offset back.
  579. //
  580. if (Offset <= LoggerContext->BufferSize) {
  581. Buffer->SavedOffset = Offset;
  582. }
  583. //
  584. // Also, dereference the buffer, so it can be freed.
  585. //
  586. InterlockedDecrement((PLONG) &Buffer->ReferenceCount);
  587. //
  588. // Nothing in per process list, ask to get a new buffer
  589. //
  590. Status = WmipSwitchBuffer(LoggerContext,
  591. &Buffer,
  592. &LoggerContext->ProcessorBuffers[Processor],
  593. Processor);
  594. if (!NT_SUCCESS(Status)) {
  595. //
  596. // Nothing available
  597. //
  598. goto LostEvent;
  599. }
  600. ASSERT (Buffer != NULL);
  601. goto TryFindSpace;
  602. }
  603. LostEvent:
  604. //
  605. // Will get here it we are throwing away the event
  606. //
  607. ASSERT(Buffer == NULL);
  608. LoggerContext->EventsLost++; // best attempt to be accurate
  609. ReservedSpace = NULL;
  610. if (LoggerContext->SequencePtr) {
  611. InterlockedIncrement(LoggerContext->SequencePtr);
  612. }
  613. FoundSpace:
  614. //
  615. // notify the logger after critical section
  616. //
  617. *BufferResource = Buffer;
  618. return ReservedSpace;
  619. }
  620. NTSTATUS
  621. WmipSwitchToNewFile(
  622. IN PWMI_LOGGER_CONTEXT LoggerContext
  623. )
  624. /*++
  625. Routine Description:
  626. This routine is called to get a LogFileHandle for GlobalLogger or
  627. when a fileswitch is needed for NEWFILE mode. It will create the file
  628. and add logfileheader to it. It closes the oldfile by properly
  629. finalizing its header.
  630. Arguments:
  631. LoggerContext - Logger Context
  632. Return Value:
  633. Status
  634. Environment:
  635. Kernel mode.
  636. --*/
  637. {
  638. IO_STATUS_BLOCK IoStatus;
  639. HANDLE OldHandle, NewHandle;
  640. UNICODE_STRING NewFileName, OldFileName;
  641. ULONG BufferSize = LoggerContext->BufferSize;
  642. PWMI_BUFFER_HEADER NewHeaderBuffer;
  643. NTSTATUS Status=STATUS_SUCCESS;
  644. PAGED_CODE();
  645. NewFileName.Buffer = NULL;
  646. if (LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_NEWFILE) {
  647. Status = WmipGenerateFileName(
  648. &LoggerContext->LogFilePattern,
  649. (PLONG) &LoggerContext->FileCounter,
  650. &NewFileName
  651. );
  652. if (!NT_SUCCESS(Status)) {
  653. TraceDebug((1, "WmipSwitchToNewFile: Error %x generating filename\n", Status));
  654. return Status;
  655. }
  656. }
  657. else {
  658. //
  659. // GlobalLogger path. It is executed only once to set up
  660. // the logfile.
  661. //
  662. if (LoggerContext->LogFileHandle != NULL) {
  663. LoggerContext->RequestFlag &= ~REQUEST_FLAG_NEW_FILE;
  664. return STATUS_SUCCESS;
  665. }
  666. if (LoggerContext->LogFileName.Buffer == NULL) {
  667. TraceDebug((1, "WmipSwitchToNewFile: No LogFileName\n"));
  668. return STATUS_INVALID_PARAMETER;
  669. }
  670. if (! RtlCreateUnicodeString( &NewFileName,
  671. LoggerContext->LogFileName.Buffer) ) {
  672. TraceDebug((1, "WmipSwitchToNewFile: No Memory for NewFileName\n"));
  673. return STATUS_NO_MEMORY;
  674. }
  675. }
  676. //
  677. // We have a NewFileName. Create the File
  678. //
  679. Status = WmipDelayCreate(&NewHandle, &NewFileName, FALSE);
  680. if (NT_SUCCESS(Status)) {
  681. NewHeaderBuffer = (PWMI_BUFFER_HEADER)
  682. ExAllocatePoolWithTag(LoggerContext->PoolType,
  683. LoggerContext->BufferSize,
  684. TRACEPOOLTAG);
  685. if (NewHeaderBuffer != NULL) {
  686. //
  687. // Now we have all the resources we need for the new file.
  688. // Let's close out the old file, if necessary and switch
  689. //
  690. OldFileName = LoggerContext->LogFileName;
  691. OldHandle = LoggerContext->LogFileHandle;
  692. if (OldHandle) {
  693. WmipFinalizeHeader(OldHandle, LoggerContext);
  694. ZwClose(OldHandle);
  695. }
  696. // NOTE: Assumes LogFileName cannot be changed
  697. // for NEWFILE mode!!!
  698. if (OldFileName.Buffer != NULL) {
  699. RtlFreeUnicodeString(&OldFileName);
  700. }
  701. LoggerContext->BuffersWritten = 1;
  702. LoggerContext->LogFileHandle = NewHandle;
  703. LoggerContext->LogFileName = NewFileName;
  704. NewFileName.Buffer = NULL;
  705. RtlZeroMemory( NewHeaderBuffer, LoggerContext->BufferSize );
  706. WmipResetBufferHeader(LoggerContext,
  707. NewHeaderBuffer,
  708. WMI_BUFFER_TYPE_RUNDOWN);
  709. WmipAddLogHeader(LoggerContext, NewHeaderBuffer);
  710. LoggerContext->LastFlushedBuffer = 1;
  711. LoggerContext->ByteOffset.QuadPart = BufferSize;
  712. LoggerContext->RequestFlag &= ~REQUEST_FLAG_NEW_FILE;
  713. LoggerContext->LoggerMode &= ~EVENT_TRACE_DELAY_OPEN_FILE_MODE;
  714. LoggerContext->LoggerMode &= ~EVENT_TRACE_ADD_HEADER_MODE;
  715. Status = WmipPrepareHeader(LoggerContext,
  716. NewHeaderBuffer,
  717. WMI_BUFFER_TYPE_RUNDOWN);
  718. if (NT_SUCCESS(Status)) {
  719. Status = ZwWriteFile(
  720. NewHandle,
  721. NULL, NULL, NULL,
  722. &IoStatus,
  723. NewHeaderBuffer,
  724. BufferSize,
  725. NULL, NULL);
  726. }
  727. if (!NT_SUCCESS(Status) ) {
  728. TraceDebug((1, "WmipSwitchToNewFile: Write Failed\n", Status));
  729. }
  730. WmipSendNotification(LoggerContext,
  731. STATUS_MEDIA_CHANGED,
  732. STATUS_SEVERITY_INFORMATIONAL);
  733. ExFreePool(NewHeaderBuffer);
  734. }
  735. }
  736. if (NewFileName.Buffer != NULL) {
  737. ExFreePool(NewFileName.Buffer);
  738. }
  739. return Status;
  740. }
  741. NTSTATUS
  742. WmipRequestLogFile(
  743. IN PWMI_LOGGER_CONTEXT LoggerContext
  744. )
  745. /*++
  746. Routine Description:
  747. This routine switches logfiles for an active logger. This routine must
  748. be called only by the Logger Thread. It will close the previous logfile,
  749. if any, properly by terminating it with FLUSH_MARKER and Finalizing the
  750. LogHeader.
  751. Two different cases to consider here are:
  752. 1. The newfile was created in user mode with headers and rundown data
  753. 2. The newfile is created in kernel (need to add LogFileHeader)
  754. The callers of this function are
  755. 1. UpdateLogger: Sets the REQUEST_FLAG_NEW_FILE after presenting
  756. a user mode created logfile in NewLogFile.
  757. 2. NT Kernel Logger session: Switches from DELAY_OPEN mode with
  758. a user mode created logfile
  759. 3. FILE_MODE_NEWFILE: When current logfile reaches the FileLimit
  760. FlushBuffer requests a newfile.
  761. 4. GlobalLogger: Started in DELAY_OPEN && ADD_HEADER mode needs
  762. to create the logfile when the FileSystem is ready.
  763. In all cases, when the switch is made the old logfile needs to be
  764. properly closed after Finalizing its header.
  765. Arguments:
  766. LoggerContext - Logger Context
  767. Return Value:
  768. Status
  769. Environment:
  770. Kernel mode.
  771. --*/
  772. {
  773. NTSTATUS Status = STATUS_SUCCESS;
  774. PAGED_CODE();
  775. if (!WmipFileSystemReady) {
  776. //
  777. // FileSystem is not ready yet, so return for now.
  778. //
  779. return Status;
  780. }
  781. //
  782. // In order for us to act on this request we need something to create
  783. // a file with, such as FileName, Pattern etc.,
  784. //
  785. if ((LoggerContext->LogFileName.Buffer == NULL ) &&
  786. (LoggerContext->LogFilePattern.Buffer == NULL) &&
  787. (LoggerContext->NewLogFileName.Buffer == NULL) ) {
  788. return Status;
  789. }
  790. //
  791. // With the REQUEST_FLAG_NEW_FILE set, flush all active buffers.
  792. //
  793. if (LoggerContext->LogFileHandle != NULL ) {
  794. Status = WmipFlushActiveBuffers(LoggerContext, TRUE);
  795. }
  796. if (NT_SUCCESS(Status)) {
  797. if ( (LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_NEWFILE) ||
  798. ( (LoggerContext->LoggerMode & EVENT_TRACE_DELAY_OPEN_FILE_MODE) &&
  799. (LoggerContext->LoggerMode & EVENT_TRACE_ADD_HEADER_MODE))) {
  800. Status = WmipSwitchToNewFile(LoggerContext);
  801. }
  802. else {
  803. //
  804. // UpdateTrace case
  805. //
  806. TraceDebug((3, "WmipLogger: New File\n"));
  807. Status = WmipCreateLogFile(LoggerContext,
  808. TRUE,
  809. EVENT_TRACE_FILE_MODE_APPEND);
  810. if (NT_SUCCESS(Status)) {
  811. LoggerContext->LoggerMode &= ~EVENT_TRACE_DELAY_OPEN_FILE_MODE;
  812. }
  813. //
  814. // This is to release the Update thread from the wait
  815. //
  816. KeSetEvent(&LoggerContext->FlushEvent, 0, FALSE);
  817. }
  818. }
  819. if (! NT_SUCCESS(Status)) {
  820. LoggerContext->LoggerStatus = Status;
  821. }
  822. return Status;
  823. }
  824. //
  825. // Actual Logger code starts here
  826. //
  827. VOID
  828. WmipLogger(
  829. IN PWMI_LOGGER_CONTEXT LoggerContext
  830. )
  831. /*++
  832. Routine Description:
  833. This function is the logger itself. It is started as a system thread.
  834. It will not return until someone has stopped data collection or it
  835. is not successful is flushing out a buffer (e.g. disk is full).
  836. Arguments:
  837. None.
  838. Return Value:
  839. The status of running the buffer manager
  840. --*/
  841. {
  842. NTSTATUS Status;
  843. ULONG ErrorCount;
  844. ULONG FlushTimeOut;
  845. ULONG64 LastFlushTime=0;
  846. PAGED_CODE();
  847. LoggerContext->LoggerThread = PsGetCurrentThread();
  848. if ((LoggerContext->LoggerMode & EVENT_TRACE_DELAY_OPEN_FILE_MODE)
  849. || (LoggerContext->LogFileName.Length == 0)) {
  850. // If EVENT_TRACE_DELAY_OPEN_FILE_MODE is specified, WMI does not
  851. // need to create logfile now.
  852. //
  853. // If there is no LogFileName specified, WMI does not need to create
  854. // logfile either. WmipStartLogger() already checks all possible
  855. // combination of LoggerMode and LogFileName, so we don't need to
  856. // perform the same check again.
  857. //
  858. Status = STATUS_SUCCESS;
  859. } else {
  860. Status = WmipCreateLogFile(LoggerContext,
  861. FALSE,
  862. LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_APPEND);
  863. }
  864. LoggerContext->LoggerStatus = Status;
  865. if (NT_SUCCESS(Status)) {
  866. //
  867. // This is the only place where CollectionOn will be turn on!!!
  868. //
  869. LoggerContext->CollectionOn = TRUE;
  870. KeSetEvent(&LoggerContext->LoggerEvent, 0, FALSE);
  871. } else {
  872. if (LoggerContext->LogFileHandle != NULL) {
  873. Status = ZwClose(LoggerContext->LogFileHandle);
  874. LoggerContext->LogFileHandle = NULL;
  875. }
  876. KeSetEvent(&LoggerContext->LoggerEvent, 0, FALSE);
  877. PsTerminateSystemThread(Status);
  878. return;
  879. }
  880. ErrorCount = 0;
  881. // by now, the caller has been notified that the logger is running
  882. //
  883. // Loop and wait for buffers to be filled until someone turns off CollectionOn
  884. //
  885. KeSetBasePriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY-1);
  886. while (LoggerContext->CollectionOn) {
  887. if (LoggerContext->LoggerMode & EVENT_TRACE_BUFFERING_MODE) {
  888. //
  889. // Wait forever until signalled by when logging is terminated.
  890. //
  891. Status = KeWaitForSingleObject(
  892. &LoggerContext->LoggerSemaphore,
  893. Executive,
  894. KernelMode,
  895. FALSE,
  896. NULL);
  897. LoggerContext->LoggerStatus = STATUS_SUCCESS;
  898. } else {
  899. ULONG FlushAll = 0;
  900. ULONG FlushFlag;
  901. FlushTimeOut = LoggerContext->FlushTimer;
  902. //
  903. // Wake up every second to see if there are any buffers in
  904. // flush list.
  905. //
  906. Status = KeWaitForSingleObject(
  907. &LoggerContext->LoggerSemaphore,
  908. Executive,
  909. KernelMode,
  910. FALSE,
  911. &WmiOneSecond);
  912. //
  913. // Check if number of buffers need to be adjusted.
  914. //
  915. WmipAdjustFreeBuffers(LoggerContext);
  916. LoggerContext->LoggerStatus = STATUS_SUCCESS;
  917. if ((LoggerContext->RequestFlag & REQUEST_FLAG_NEW_FILE) ||
  918. ((LoggerContext->LoggerMode & EVENT_TRACE_DELAY_OPEN_FILE_MODE)
  919. && (LoggerContext->LoggerMode & EVENT_TRACE_ADD_HEADER_MODE)) ) {
  920. Status = WmipRequestLogFile( LoggerContext);
  921. }
  922. //
  923. // Check to see if we need to FlushAll
  924. //
  925. if (FlushTimeOut) {
  926. ULONG64 Now;
  927. KeQuerySystemTime((PLARGE_INTEGER) &Now);
  928. if ( ((Now - LastFlushTime) / 10000000) >= FlushTimeOut) {
  929. FlushAll = 1;
  930. LastFlushTime = Now;
  931. }
  932. else {
  933. FlushAll = 0;
  934. }
  935. }
  936. FlushFlag = (LoggerContext->RequestFlag & REQUEST_FLAG_FLUSH_BUFFERS);
  937. if ( FlushFlag )
  938. FlushAll = TRUE;
  939. #ifdef NTPERF
  940. if (!PERFINFO_IS_LOGGING_TO_PERFMEM()) {
  941. #endif //NTPERF
  942. Status = WmipFlushActiveBuffers(LoggerContext, FlushAll);
  943. //
  944. // Should check the status, and if failed to write a log file
  945. // header, should clean up. As the log file is bad anyway.
  946. //
  947. if ( FlushFlag ) {
  948. LoggerContext->RequestFlag &= ~REQUEST_FLAG_FLUSH_BUFFERS;
  949. //
  950. // If this was a flush for persistent events, this request
  951. // flag must be reset here.
  952. //
  953. if (LoggerContext->RequestFlag &
  954. REQUEST_FLAG_CIRCULAR_TRANSITION) {
  955. if (LoggerContext->LogFileHandle != NULL) {
  956. WmipFinalizeHeader(LoggerContext->LogFileHandle,
  957. LoggerContext);
  958. }
  959. LoggerContext->RequestFlag &= ~REQUEST_FLAG_CIRCULAR_TRANSITION;
  960. }
  961. LoggerContext->LoggerStatus = Status;
  962. KeSetEvent(&LoggerContext->FlushEvent, 0, FALSE);
  963. }
  964. if (!NT_SUCCESS(Status)) {
  965. LoggerContext->LoggerStatus = Status;
  966. WmipStopLoggerInstance(LoggerContext);
  967. }
  968. #ifdef NTPERF
  969. }
  970. #endif //NTPERF
  971. }
  972. } // while loop
  973. if (Status == STATUS_TIMEOUT) {
  974. Status = STATUS_SUCCESS;
  975. }
  976. //
  977. // if a normal collection end, flush out all the buffers before stopping
  978. //
  979. TraceDebug((2, "WmipLogger: Flush all buffers before stopping...\n"));
  980. //
  981. // First, move the per processor buffer out to FlushList
  982. //
  983. // This is to force buffers to be written
  984. // in FlushBuffer without snapping back to this routine to create a file.
  985. LoggerContext->RequestFlag |= REQUEST_FLAG_NEW_FILE;
  986. while ((LoggerContext->NumberOfBuffers > 0) &&
  987. (LoggerContext->NumberOfBuffers > LoggerContext->BuffersAvailable)) {
  988. Status = KeWaitForSingleObject(
  989. &LoggerContext->LoggerSemaphore,
  990. Executive,
  991. KernelMode,
  992. FALSE,
  993. &WmiOneSecond);
  994. WmipFlushActiveBuffers(LoggerContext, 1);
  995. TraceDebug((2, "WmipLogger: Stop %d %d %d %d %d\n",
  996. LoggerContext->LoggerId,
  997. LoggerContext->BuffersAvailable,
  998. LoggerContext->BuffersInUse,
  999. LoggerContext->BuffersDirty,
  1000. LoggerContext->NumberOfBuffers));
  1001. }
  1002. //
  1003. // Note that LoggerContext->LogFileObject needs to remain set
  1004. // for QueryLogger to work after close
  1005. //
  1006. if (LoggerContext->LogFileHandle != NULL) {
  1007. ZwClose(LoggerContext->LogFileHandle);
  1008. TraceDebug((1, "WmipLogger: Close logfile with status=%X\n", Status));
  1009. }
  1010. LoggerContext->LogFileHandle = NULL;
  1011. KeSetEvent(&LoggerContext->FlushEvent, 0, FALSE);
  1012. KeSetEvent(&LoggerContext->LoggerEvent, 0, FALSE);
  1013. #if DBG
  1014. if (!NT_SUCCESS(Status)) {
  1015. TraceDebug((1, "WmipLogger: Aborting %d %X\n",
  1016. LoggerContext->LoggerId, LoggerContext->LoggerStatus));
  1017. }
  1018. #endif
  1019. WmipFreeLoggerContext(LoggerContext);
  1020. #ifdef NTPERF
  1021. //
  1022. // Check if we are logging into perfmem.
  1023. //
  1024. if (PERFINFO_IS_LOGGING_TO_PERFMEM()) {
  1025. PerfInfoStopPerfMemLog();
  1026. }
  1027. #endif //NTPERF
  1028. PsTerminateSystemThread(Status);
  1029. }
  1030. NTSTATUS
  1031. WmipSendNotification(
  1032. PWMI_LOGGER_CONTEXT LoggerContext,
  1033. NTSTATUS Status,
  1034. ULONG Flag
  1035. )
  1036. {
  1037. WMI_TRACE_EVENT WmiEvent;
  1038. RtlZeroMemory(& WmiEvent, sizeof(WmiEvent));
  1039. WmiEvent.Status = Status;
  1040. KeQuerySystemTime(& WmiEvent.Wnode.TimeStamp);
  1041. WmiEvent.Wnode.BufferSize = sizeof(WmiEvent);
  1042. WmiEvent.Wnode.Guid = TraceErrorGuid;
  1043. WmiSetLoggerId(
  1044. LoggerContext->LoggerId,
  1045. (PTRACE_ENABLE_CONTEXT) & WmiEvent.Wnode.HistoricalContext);
  1046. WmiEvent.Wnode.ClientContext = 0XFFFFFFFF;
  1047. WmiEvent.TraceErrorFlag = Flag;
  1048. WmipProcessEvent(&WmiEvent.Wnode,
  1049. FALSE,
  1050. FALSE);
  1051. return STATUS_SUCCESS;
  1052. }
  1053. //
  1054. // convenience routine to flush the current buffer by the logger above
  1055. //
  1056. NTSTATUS
  1057. WmipFlushBuffer(
  1058. IN PWMI_LOGGER_CONTEXT LoggerContext,
  1059. IN PWMI_BUFFER_HEADER Buffer,
  1060. IN USHORT BufferFlag
  1061. )
  1062. /*++
  1063. Routine Description:
  1064. This function is responsible for flushing a filled buffer out to
  1065. disk, or to a real time consumer.
  1066. Arguments:
  1067. LoggerContext - Context of the logger
  1068. Buffer -
  1069. BufferFlag -
  1070. Return Value:
  1071. The status of flushing the buffer
  1072. --*/
  1073. {
  1074. IO_STATUS_BLOCK IoStatus;
  1075. NTSTATUS Status;
  1076. ULONG BufferSize;
  1077. ULONG BufferPersistenceData = LoggerContext->RequestFlag
  1078. & ( REQUEST_FLAG_CIRCULAR_PERSIST
  1079. | REQUEST_FLAG_CIRCULAR_TRANSITION);
  1080. ASSERT(LoggerContext != NULL);
  1081. ASSERT(Buffer != NULL);
  1082. if (LoggerContext == NULL || Buffer == NULL) {
  1083. return STATUS_SEVERITY_ERROR;
  1084. }
  1085. //
  1086. // Grab the buffer to be flushed
  1087. //
  1088. BufferSize = LoggerContext->BufferSize;
  1089. //
  1090. // Put end of record marker in buffer if available space
  1091. //
  1092. TraceDebug((2, "WmipFlushBuffer: %p, Flushed %X %8x %8x %5d\n",
  1093. Buffer,
  1094. Buffer->ClientContext, Buffer->SavedOffset,
  1095. Buffer->CurrentOffset, LoggerContext->BuffersWritten));
  1096. Status = WmipPrepareHeader(LoggerContext, Buffer, BufferFlag);
  1097. if (Status == STATUS_SUCCESS) {
  1098. //
  1099. // Buffering mode is mutually exclusive with REAL_TIME_MODE
  1100. //
  1101. if (!(LoggerContext->LoggerMode & EVENT_TRACE_BUFFERING_MODE)) {
  1102. if (LoggerContext->LoggerMode & EVENT_TRACE_REAL_TIME_MODE) {
  1103. if (LoggerContext->UsePerfClock == EVENT_TRACE_CLOCK_PERFCOUNTER) {
  1104. Buffer->Wnode.Flags |= WNODE_FLAG_USE_TIMESTAMP;
  1105. }
  1106. // need to see if we can send anymore
  1107. // check for queue length
  1108. if (! NT_SUCCESS(WmipProcessEvent((PWNODE_HEADER)Buffer,
  1109. FALSE,
  1110. FALSE))) {
  1111. LoggerContext->RealTimeBuffersLost++;
  1112. }
  1113. }
  1114. }
  1115. if (LoggerContext->LogFileHandle != NULL) {
  1116. if (LoggerContext->MaximumFileSize > 0) { // if quota given
  1117. ULONG64 FileSize = LoggerContext->LastFlushedBuffer * BufferSize;
  1118. ULONG64 FileLimit = LoggerContext->MaximumFileSize * BYTES_PER_MB;
  1119. if (LoggerContext->LoggerMode & EVENT_TRACE_USE_KBYTES_FOR_SIZE) {
  1120. FileLimit = LoggerContext->MaximumFileSize * 1024;
  1121. }
  1122. if ( FileSize >= FileLimit ) {
  1123. ULONG LoggerMode = LoggerContext->LoggerMode & 0X000000FF;
  1124. //
  1125. // Files from user mode always have the APPEND flag.
  1126. // We mask it out here to simplify the testing below.
  1127. //
  1128. LoggerMode &= ~EVENT_TRACE_FILE_MODE_APPEND;
  1129. //
  1130. // PREALLOCATE flag has to go, too.
  1131. //
  1132. LoggerMode &= ~EVENT_TRACE_FILE_MODE_PREALLOCATE;
  1133. if (LoggerMode == EVENT_TRACE_FILE_MODE_SEQUENTIAL) {
  1134. // do not write to logfile anymore
  1135. Status = STATUS_LOG_FILE_FULL; // control needs to stop logging
  1136. // need to fire up a Wmi Event to control console
  1137. WmipSendNotification(LoggerContext,
  1138. Status, STATUS_SEVERITY_ERROR);
  1139. }
  1140. else if (LoggerMode == EVENT_TRACE_FILE_MODE_CIRCULAR ||
  1141. LoggerMode == EVENT_TRACE_FILE_MODE_CIRCULAR_PERSIST) {
  1142. if (BufferPersistenceData > 0) {
  1143. // treat circular logfile as sequential logfile if
  1144. // logger still processes Persistence events (events
  1145. // that cannot be overwritten in circular manner).
  1146. //
  1147. Status = STATUS_LOG_FILE_FULL;
  1148. WmipSendNotification(LoggerContext,
  1149. Status, STATUS_SEVERITY_ERROR);
  1150. }
  1151. else {
  1152. // reposition file
  1153. LoggerContext->ByteOffset
  1154. = LoggerContext->FirstBufferOffset;
  1155. LoggerContext->LastFlushedBuffer = (ULONG)
  1156. (LoggerContext->FirstBufferOffset.QuadPart
  1157. / LoggerContext->BufferSize);
  1158. }
  1159. }
  1160. else if (LoggerMode & EVENT_TRACE_FILE_MODE_NEWFILE) {
  1161. //
  1162. // We will set the RequestFlag to initiate a file switch.
  1163. // If that flag is already set, then we continnue to flush
  1164. // past the FileLimit.
  1165. //
  1166. // There should be no race condition with an UpdateTrace
  1167. // setting the RequestFlag since we do not allow change of
  1168. // filename for NEWFILE mode.
  1169. //
  1170. if ( (LoggerContext->RequestFlag & REQUEST_FLAG_NEW_FILE) !=
  1171. REQUEST_FLAG_NEW_FILE)
  1172. {
  1173. LoggerContext->RequestFlag |= REQUEST_FLAG_NEW_FILE;
  1174. }
  1175. }
  1176. }
  1177. }
  1178. if (NT_SUCCESS(Status)) {
  1179. Status = ZwWriteFile(
  1180. LoggerContext->LogFileHandle,
  1181. NULL,
  1182. NULL,
  1183. NULL,
  1184. &IoStatus,
  1185. Buffer,
  1186. BufferSize,
  1187. &LoggerContext->ByteOffset,
  1188. NULL);
  1189. if (NT_SUCCESS(Status)) {
  1190. LoggerContext->ByteOffset.QuadPart += BufferSize;
  1191. if (BufferPersistenceData > 0) {
  1192. // update FirstBufferOffset so that persistence event will
  1193. // not be overwritten in circular logfile
  1194. //
  1195. LoggerContext->FirstBufferOffset.QuadPart += BufferSize;
  1196. }
  1197. }
  1198. else if (Status == STATUS_LOG_FILE_FULL ||
  1199. Status == STATUS_DISK_FULL) {
  1200. // need to fire up a Wmi Event to control console
  1201. WmipSendNotification(LoggerContext,
  1202. STATUS_LOG_FILE_FULL, STATUS_SEVERITY_ERROR);
  1203. }
  1204. else {
  1205. TraceDebug((2, "WmipFlushBuffer: Unknown WriteFile Failure with status=%X\n", Status));
  1206. }
  1207. }
  1208. }
  1209. // Now do callbacks. This happens whether a file exists or not.
  1210. if (WmipGlobalBufferCallback) {
  1211. (WmipGlobalBufferCallback) (Buffer, WmipGlobalCallbackContext);
  1212. }
  1213. if (LoggerContext->BufferCallback) {
  1214. (LoggerContext->BufferCallback) (Buffer, LoggerContext->CallbackContext);
  1215. }
  1216. }
  1217. if (NT_SUCCESS(Status)) {
  1218. LoggerContext->BuffersWritten++;
  1219. LoggerContext->LastFlushedBuffer++;
  1220. }
  1221. else {
  1222. #if DBG
  1223. if (Status == STATUS_NO_DATA_DETECTED) {
  1224. TraceDebug((2, "WmipFlushBuffer: Empty buffer detected\n"));
  1225. }
  1226. else if (Status == STATUS_SEVERITY_WARNING) {
  1227. TraceDebug((2, "WmipFlushBuffer: Buffer could be corrupted\n"));
  1228. }
  1229. else {
  1230. TraceDebug((2,
  1231. "WmipFlushBuffer: Unable to write buffer: status=%X\n",
  1232. Status));
  1233. }
  1234. #endif
  1235. if ((Status != STATUS_NO_DATA_DETECTED) &&
  1236. (Status != STATUS_SEVERITY_WARNING))
  1237. LoggerContext->LogBuffersLost++;
  1238. }
  1239. return Status;
  1240. }
  1241. NTSTATUS
  1242. WmipCreateLogFile(
  1243. IN PWMI_LOGGER_CONTEXT LoggerContext,
  1244. IN ULONG SwitchFile,
  1245. IN ULONG Append
  1246. )
  1247. {
  1248. NTSTATUS Status;
  1249. HANDLE newHandle = NULL;
  1250. IO_STATUS_BLOCK IoStatus;
  1251. FILE_STANDARD_INFORMATION FileSize = {0};
  1252. LARGE_INTEGER ByteOffset;
  1253. BOOLEAN FileSwitched = FALSE;
  1254. UNICODE_STRING OldLogFileName;
  1255. PWCHAR strLogFileName = NULL;
  1256. PUCHAR pFirstBuffer = NULL;
  1257. PAGED_CODE();
  1258. RtlZeroMemory(&OldLogFileName, sizeof(UNICODE_STRING));
  1259. LoggerContext->RequestFlag &= ~REQUEST_FLAG_NEW_FILE;
  1260. pFirstBuffer = (PUCHAR) ExAllocatePoolWithTag(
  1261. PagedPool, LoggerContext->BufferSize, TRACEPOOLTAG);
  1262. if(pFirstBuffer == NULL) {
  1263. Status = STATUS_NO_MEMORY;
  1264. goto Cleanup;
  1265. }
  1266. RtlZeroMemory(pFirstBuffer, LoggerContext->BufferSize);
  1267. if (SwitchFile) {
  1268. Status = WmipCreateNtFileName(
  1269. LoggerContext->NewLogFileName.Buffer,
  1270. & strLogFileName);
  1271. }
  1272. else {
  1273. Status = WmipCreateNtFileName(
  1274. LoggerContext->LogFileName.Buffer,
  1275. & strLogFileName);
  1276. }
  1277. if (!NT_SUCCESS(Status)) {
  1278. goto Cleanup;
  1279. }
  1280. if (LoggerContext->ClientSecurityContext.ClientToken != NULL) {
  1281. Status = SeImpersonateClientEx(
  1282. &LoggerContext->ClientSecurityContext, NULL);
  1283. }
  1284. if (NT_SUCCESS(Status)) {
  1285. // first open logfile using user security context
  1286. //
  1287. Status = WmipCreateDirectoryFile(strLogFileName, FALSE, & newHandle, Append);
  1288. PsRevertToSelf();
  1289. }
  1290. if (!NT_SUCCESS(Status)) {
  1291. // if using user security context fails to open logfile,
  1292. // then try open logfile again using local system security context
  1293. //
  1294. Status = WmipCreateDirectoryFile(strLogFileName, FALSE, & newHandle, Append);
  1295. }
  1296. if (NT_SUCCESS(Status)) {
  1297. HANDLE tempHandle = LoggerContext->LogFileHandle;
  1298. PWMI_BUFFER_HEADER BufferChecksum;
  1299. PTRACE_LOGFILE_HEADER LogfileHeaderChecksum;
  1300. ULONG BuffersWritten = 0;
  1301. BufferChecksum = (PWMI_BUFFER_HEADER) LoggerContext->LoggerHeader;
  1302. LogfileHeaderChecksum = (PTRACE_LOGFILE_HEADER)
  1303. (((PUCHAR) BufferChecksum) + sizeof(WNODE_HEADER));
  1304. if (LogfileHeaderChecksum) {
  1305. BuffersWritten = LogfileHeaderChecksum->BuffersWritten;
  1306. }
  1307. ByteOffset.QuadPart = 0;
  1308. Status = ZwReadFile(
  1309. newHandle,
  1310. NULL,
  1311. NULL,
  1312. NULL,
  1313. & IoStatus,
  1314. pFirstBuffer,
  1315. LoggerContext->BufferSize,
  1316. & ByteOffset,
  1317. NULL);
  1318. if (NT_SUCCESS(Status)) {
  1319. PWMI_BUFFER_HEADER BufferFile;
  1320. PTRACE_LOGFILE_HEADER LogfileHeaderFile;
  1321. ULONG Size;
  1322. BufferFile =
  1323. (PWMI_BUFFER_HEADER) pFirstBuffer;
  1324. if (BufferFile->Wnode.BufferSize != LoggerContext->BufferSize) {
  1325. TraceDebug((1,
  1326. "WmipCreateLogFile::BufferSize check fails (%d,%d)\n",
  1327. BufferFile->Wnode.BufferSize,
  1328. LoggerContext->BufferSize));
  1329. Status = STATUS_FAIL_CHECK;
  1330. ZwClose(newHandle);
  1331. goto Cleanup;
  1332. }
  1333. if (RtlCompareMemory(BufferFile,
  1334. BufferChecksum,
  1335. sizeof(WNODE_HEADER))
  1336. != sizeof(WNODE_HEADER)) {
  1337. TraceDebug((1,"WmipCreateLogFile::WNODE_HEAD check fails\n"));
  1338. Status = STATUS_FAIL_CHECK;
  1339. ZwClose(newHandle);
  1340. goto Cleanup;
  1341. }
  1342. LogfileHeaderFile = (PTRACE_LOGFILE_HEADER)
  1343. (((PUCHAR) BufferFile) + sizeof(WMI_BUFFER_HEADER)
  1344. + sizeof(SYSTEM_TRACE_HEADER));
  1345. // We can only validate part of the header because a 32-bit
  1346. // DLL will be passing in 32-bit pointers
  1347. Size = FIELD_OFFSET(TRACE_LOGFILE_HEADER, LoggerName);
  1348. if (RtlCompareMemory(LogfileHeaderFile,
  1349. LogfileHeaderChecksum,
  1350. Size)
  1351. != Size) {
  1352. TraceDebug((1,
  1353. "WmipCreateLogFile::TRACE_LOGFILE_HEAD check fails\n"));
  1354. Status = STATUS_FAIL_CHECK;
  1355. ZwClose(newHandle);
  1356. goto Cleanup;
  1357. }
  1358. }
  1359. else {
  1360. ZwClose(newHandle);
  1361. goto Cleanup;
  1362. }
  1363. if (LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_PREALLOCATE) {
  1364. ByteOffset.QuadPart = ((LONGLONG) LoggerContext->BufferSize) * BuffersWritten;
  1365. }
  1366. else {
  1367. Status = ZwQueryInformationFile(
  1368. newHandle,
  1369. &IoStatus,
  1370. &FileSize,
  1371. sizeof (FILE_STANDARD_INFORMATION),
  1372. FileStandardInformation
  1373. );
  1374. if (!NT_SUCCESS(Status)) {
  1375. ZwClose(newHandle);
  1376. goto Cleanup;
  1377. }
  1378. ByteOffset = FileSize.EndOfFile;
  1379. }
  1380. //
  1381. // Force to 1K alignment. In future, if disk alignment exceeds this,
  1382. // then use that
  1383. //
  1384. if ((ByteOffset.QuadPart % 1024) != 0) {
  1385. ByteOffset.QuadPart = ((ByteOffset.QuadPart / 1024) + 1) * 1024;
  1386. }
  1387. if (!(LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_PREALLOCATE)) {
  1388. // NOTE: Should also validate BuffersWritten from logfile header with
  1389. // the end of file to make sure that no one else has written garbage
  1390. // to it
  1391. //
  1392. if (ByteOffset.QuadPart !=
  1393. ( ((LONGLONG) LoggerContext->BufferSize)
  1394. * BuffersWritten)) {
  1395. TraceDebug((1,
  1396. "WmipCreateLogFile::FileSize check fails (%I64d,%I64d)\n",
  1397. ByteOffset.QuadPart,
  1398. ( ((LONGLONG) LoggerContext->BufferSize)
  1399. * BuffersWritten)));
  1400. Status = STATUS_FAIL_CHECK;
  1401. ZwClose(newHandle);
  1402. goto Cleanup;
  1403. }
  1404. }
  1405. //
  1406. // Before Switching to the newfile, let's finalize the old file
  1407. //
  1408. if ( SwitchFile && (tempHandle != NULL) ) {
  1409. WmipFinalizeHeader(tempHandle, LoggerContext);
  1410. }
  1411. LoggerContext->FirstBufferOffset = ByteOffset;
  1412. LoggerContext->ByteOffset = ByteOffset;
  1413. if (LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_PREALLOCATE) {
  1414. LoggerContext->BuffersWritten = BuffersWritten;
  1415. }
  1416. else {
  1417. LoggerContext->BuffersWritten = (ULONG) (FileSize.EndOfFile.QuadPart / LoggerContext->BufferSize);
  1418. }
  1419. LoggerContext->LastFlushedBuffer = LoggerContext->BuffersWritten;
  1420. // Update the log file handle and log file name in the LoggerContext
  1421. LoggerContext->LogFileHandle = newHandle;
  1422. if (SwitchFile) {
  1423. OldLogFileName = LoggerContext->LogFileName;
  1424. LoggerContext->LogFileName = LoggerContext->NewLogFileName;
  1425. FileSwitched = TRUE;
  1426. if ( tempHandle != NULL) {
  1427. //
  1428. // just to be safe, close old file after the switch
  1429. //
  1430. TraceDebug((1, "WmipCreateLogFile: Closing handle %X\n",
  1431. tempHandle));
  1432. ZwClose(tempHandle);
  1433. }
  1434. }
  1435. }
  1436. #if DBG
  1437. else {
  1438. TraceDebug((1,
  1439. "WmipCreateLogFile: ZwCreateFile(%ws) failed with status=%X\n",
  1440. LoggerContext->LogFileName.Buffer, Status));
  1441. }
  1442. #endif
  1443. Cleanup:
  1444. if (LoggerContext->ClientSecurityContext.ClientToken != NULL) {
  1445. SeDeleteClientSecurity(& LoggerContext->ClientSecurityContext);
  1446. LoggerContext->ClientSecurityContext.ClientToken = NULL;
  1447. }
  1448. // Clean up unicode strings.
  1449. if (SwitchFile) {
  1450. if (!FileSwitched) {
  1451. RtlFreeUnicodeString(&LoggerContext->NewLogFileName);
  1452. }
  1453. else if (OldLogFileName.Buffer != NULL) {
  1454. // OldLogFileName.Buffer can still be NULL if it is the first update
  1455. // for the Kernel Logger.
  1456. RtlFreeUnicodeString(&OldLogFileName);
  1457. }
  1458. // Must do this for the next file switch.
  1459. RtlZeroMemory(&LoggerContext->NewLogFileName, sizeof(UNICODE_STRING));
  1460. }
  1461. if (strLogFileName != NULL) {
  1462. ExFreePool(strLogFileName);
  1463. }
  1464. if (pFirstBuffer != NULL) {
  1465. ExFreePool(pFirstBuffer);
  1466. }
  1467. LoggerContext->LoggerStatus = Status;
  1468. return Status;
  1469. }
  1470. ULONG
  1471. FASTCALL
  1472. WmipReleaseTraceBuffer(
  1473. IN PWMI_BUFFER_HEADER BufferResource,
  1474. IN PWMI_LOGGER_CONTEXT LoggerContext
  1475. )
  1476. {
  1477. ULONG BufRefCount;
  1478. LONG ReleaseQueue;
  1479. ASSERT(LoggerContext);
  1480. ASSERT(BufferResource);
  1481. BufRefCount = InterlockedDecrement((PLONG) &BufferResource->ReferenceCount);
  1482. //
  1483. // Check if there are buffers to be flushed.
  1484. //
  1485. if (LoggerContext->ReleaseQueue) {
  1486. if (KeGetCurrentIrql() <= DISPATCH_LEVEL) {
  1487. WmipNotifyLogger(LoggerContext);
  1488. LoggerContext->ReleaseQueue = 0;
  1489. }
  1490. }
  1491. ReleaseQueue = LoggerContext->ReleaseQueue;
  1492. WmipDereferenceLogger(LoggerContext->LoggerId);
  1493. return (ReleaseQueue);
  1494. }
  1495. NTKERNELAPI
  1496. ULONG
  1497. FASTCALL
  1498. WmiReleaseKernelBuffer(
  1499. IN PWMI_BUFFER_HEADER BufferResource
  1500. )
  1501. {
  1502. PWMI_LOGGER_CONTEXT LoggerContext = WmipLoggerContext[WmipKernelLogger];
  1503. if (LoggerContext == (PWMI_LOGGER_CONTEXT) &WmipLoggerContext[0]) {
  1504. LoggerContext = BufferResource->LoggerContext;
  1505. }
  1506. WmipAssert(LoggerContext != NULL);
  1507. WmipAssert(LoggerContext != (PWMI_LOGGER_CONTEXT) &WmipLoggerContext[0]);
  1508. return WmipReleaseTraceBuffer(BufferResource, LoggerContext);
  1509. }
  1510. NTSTATUS
  1511. WmipFlushBuffersWithMarker (
  1512. IN PWMI_LOGGER_CONTEXT LoggerContext,
  1513. IN PSLIST_ENTRY List,
  1514. IN USHORT BufferFlag
  1515. )
  1516. {
  1517. PSLIST_ENTRY LocalList, Entry;
  1518. PWMI_BUFFER_HEADER Buffer;
  1519. PWMI_BUFFER_HEADER TmpBuffer=NULL;
  1520. NTSTATUS Status = STATUS_SUCCESS;
  1521. ULONG Retry;
  1522. ULONG BufferCounts = 0;
  1523. USHORT Flag = WMI_BUFFER_FLAG_NORMAL;
  1524. ULONG ErrorCount = 0;
  1525. LocalList = List;
  1526. //
  1527. // Reverse the list to preserve the FIFO order
  1528. //
  1529. Entry = NULL;
  1530. while (LocalList!=NULL) {
  1531. PSLIST_ENTRY Next;
  1532. Next = LocalList->Next;
  1533. LocalList->Next = Entry;
  1534. Entry = LocalList;
  1535. LocalList = Next;
  1536. BufferCounts++;
  1537. }
  1538. LocalList = Entry;
  1539. //
  1540. // Write all buffers into disk
  1541. //
  1542. while (LocalList != NULL){
  1543. BufferCounts--;
  1544. if (BufferCounts == 0) {
  1545. //
  1546. // Only set the flag at the last buffer.
  1547. //
  1548. Flag = BufferFlag;
  1549. }
  1550. Entry = LocalList;
  1551. LocalList = LocalList->Next;
  1552. Buffer = CONTAINING_RECORD(Entry,
  1553. WMI_BUFFER_HEADER,
  1554. SlistEntry);
  1555. if (!(LoggerContext->LoggerMode & EVENT_TRACE_BUFFERING_MODE)) {
  1556. //
  1557. // When there is a bursty logging, we can be stuck in this loop.
  1558. // Check if we need to allocate more buffers
  1559. //
  1560. // Only do buffer adjustment if we are not in buffering mode
  1561. //
  1562. WmipAdjustFreeBuffers(LoggerContext);
  1563. }
  1564. //
  1565. //
  1566. // Wait until no one else is using the buffer before
  1567. // writing it out.
  1568. //
  1569. Retry = 0;
  1570. TmpBuffer = Buffer;
  1571. while (Buffer->ReferenceCount) {
  1572. TraceDebug((1,"Waiting for reference count %3d, retry: %3d\n",
  1573. Buffer->ReferenceCount, Retry));
  1574. //
  1575. //
  1576. //
  1577. KeDelayExecutionThread (KernelMode,
  1578. FALSE,
  1579. (PLARGE_INTEGER)&WmiShortTime);
  1580. Retry++;
  1581. if (Retry > 10) {
  1582. //
  1583. // The buffer is still in use, we cannot overwite the header.
  1584. // Otherwise it will cause buffer corrution.
  1585. // Use a tempamory buffer instead.
  1586. //
  1587. ULONG BufferSize = LoggerContext->BufferSize;
  1588. TmpBuffer = ExAllocatePoolWithTag(NonPagedPool,
  1589. BufferSize,
  1590. TRACEPOOLTAG);
  1591. if (TmpBuffer) {
  1592. TraceDebug((1,"Buffer %p has ref count %3d, Tmporary buffer %p Allocated\n",
  1593. Buffer,
  1594. Buffer->ReferenceCount,
  1595. TmpBuffer));
  1596. RtlCopyMemory(TmpBuffer, Buffer, BufferSize);
  1597. } else {
  1598. Status = STATUS_NO_MEMORY;
  1599. }
  1600. break;
  1601. }
  1602. }
  1603. if (TmpBuffer) {
  1604. Status = WmipFlushBuffer(LoggerContext, TmpBuffer, Flag);
  1605. } else {
  1606. //
  1607. // The buffer still in use but allocation of temporary
  1608. // buffer failed.
  1609. // Cannot write this buffer out, claim it as buffer lost
  1610. //
  1611. // If this were the last buffer on file, post processing can
  1612. // fail due to marker buffer failing.
  1613. LoggerContext->LogBuffersLost++;
  1614. }
  1615. if (TmpBuffer != Buffer) {
  1616. if (TmpBuffer != NULL) {
  1617. ExFreePool(TmpBuffer);
  1618. }
  1619. InterlockedPushEntrySList(&LoggerContext->WaitList,
  1620. (PSLIST_ENTRY) &Buffer->SlistEntry);
  1621. } else {
  1622. //
  1623. // Reference count is overwriten during the flush,
  1624. // Set it back.
  1625. //
  1626. Buffer->ReferenceCount = 0;
  1627. WmipPushFreeBuffer (LoggerContext, Buffer);
  1628. }
  1629. if ((Status == STATUS_LOG_FILE_FULL) ||
  1630. (Status == STATUS_DISK_FULL) ||
  1631. (Status == STATUS_NO_DATA_DETECTED) ||
  1632. (Status == STATUS_SEVERITY_WARNING)) {
  1633. TraceDebug((1,
  1634. "WmipFlushActiveBuffers: Buffer flushed with status=%X\n",
  1635. Status));
  1636. if ((Status == STATUS_LOG_FILE_FULL) ||
  1637. (Status == STATUS_DISK_FULL)) {
  1638. ErrorCount ++;
  1639. } else {
  1640. ErrorCount = 0; // reset to zero otherwise
  1641. }
  1642. if (ErrorCount <= WmiWriteFailureLimit) {
  1643. Status = STATUS_SUCCESS; // Let tracing continue
  1644. continue; // for now. Should raise WMI event
  1645. }
  1646. }
  1647. }
  1648. if (!NT_SUCCESS(Status)) {
  1649. TraceDebug((1,
  1650. "WmipLogger: Flush failed, status=%X LoggerContext=%X\n",
  1651. Status, LoggerContext));
  1652. if (LoggerContext->LogFileHandle != NULL) {
  1653. #if DBG
  1654. NTSTATUS CloseStatus =
  1655. #endif
  1656. ZwClose(LoggerContext->LogFileHandle);
  1657. TraceDebug((1,
  1658. "WmipLogger: Close logfile with status=%X\n", CloseStatus));
  1659. }
  1660. LoggerContext->LogFileHandle = NULL;
  1661. WmipSendNotification(LoggerContext,
  1662. Status, (Status & 0xC0000000) >> 30);
  1663. }
  1664. return Status;
  1665. }
  1666. NTSTATUS
  1667. WmipFlushActiveBuffers(
  1668. IN PWMI_LOGGER_CONTEXT LoggerContext,
  1669. IN ULONG FlushAll
  1670. )
  1671. {
  1672. PWMI_BUFFER_HEADER Buffer;
  1673. ULONG i;
  1674. NTSTATUS Status = STATUS_SUCCESS;
  1675. ULONG LoggerMode;
  1676. PSLIST_ENTRY LocalList, Entry;
  1677. PAGED_CODE();
  1678. //
  1679. // If we have no LogFileHandle or RealTime mode, and the collection
  1680. // is still on, we simply return and let the buffers back up potentially
  1681. // losing events. If the Collection is Off under these conditions
  1682. // we will simply move the FlushList to FreeList as in Buffering mode.
  1683. //
  1684. LoggerMode = LoggerContext->LoggerMode;
  1685. if ( (LoggerContext->LogFileHandle == NULL) &&
  1686. (!(LoggerMode & EVENT_TRACE_REAL_TIME_MODE)) &&
  1687. (LoggerContext->CollectionOn) )
  1688. {
  1689. return Status;
  1690. }
  1691. LocalList = NULL;
  1692. if (FlushAll) {
  1693. PWMI_BUFFER_HEADER Buffers[MAXIMUM_PROCESSORS];
  1694. //
  1695. // First switch all in-used buffers.
  1696. // Put them in a tighter loop to minimize the chance of
  1697. // events out of order.
  1698. //
  1699. for (i=0; i<(ULONG)KeNumberProcessors; i++) {
  1700. Buffers[i] = InterlockedExchangePointer(&LoggerContext->ProcessorBuffers[i], NULL);
  1701. }
  1702. //
  1703. // Put all in-used buffers into flush list
  1704. //
  1705. for (i=0; i<(ULONG)KeNumberProcessors; i++) {
  1706. if (Buffers[i]) {
  1707. WmipPushDirtyBuffer ( LoggerContext, Buffers[i] );
  1708. }
  1709. }
  1710. if (LoggerContext->LoggerId == WmipKernelLogger) {
  1711. if ( PERFINFO_IS_GROUP_ON(PERF_CONTEXT_SWITCH) ) {
  1712. for (i=0; i<(ULONG)KeNumberProcessors; i++) {
  1713. //
  1714. // Flush all Buffers used for Context swaps
  1715. //
  1716. Buffer = InterlockedExchangePointer(&WmipContextSwapProcessorBuffers[i], NULL);
  1717. if (Buffer) {
  1718. WmipPushDirtyBuffer ( LoggerContext, Buffer);
  1719. }
  1720. }
  1721. }
  1722. #ifdef NTPERF
  1723. //
  1724. // Flush all buffer logging from user mode
  1725. //
  1726. if (PERFINFO_IS_LOGGING_TO_PERFMEM()) {
  1727. PPERFINFO_TRACEBUF_HEADER pPerfBufHdr;
  1728. pPerfBufHdr = PerfBufHdr();
  1729. for (i=0; i<(ULONG)KeNumberProcessors; i++) {
  1730. Buffer = pPerfBufHdr->UserModePerCpuBuffer[i];
  1731. if (Buffer) {
  1732. pPerfBufHdr->UserModePerCpuBuffer[i] = NULL;
  1733. WmipPushDirtyBuffer ( LoggerContext, Buffer);
  1734. }
  1735. }
  1736. }
  1737. #endif //NTPERF
  1738. }
  1739. //
  1740. // Now push all dirty buffers in the local list.
  1741. // This almost guarantees that the flush marker will work.
  1742. //
  1743. if (ExQueryDepthSList(&LoggerContext->FlushList) != 0) {
  1744. LocalList = ExInterlockedFlushSList (&LoggerContext->FlushList);
  1745. WmipFlushBuffersWithMarker (LoggerContext, LocalList, WMI_BUFFER_FLAG_FLUSH_MARKER);
  1746. }
  1747. } else if (ExQueryDepthSList(&LoggerContext->FlushList) != 0) {
  1748. LocalList = ExInterlockedFlushSList (&LoggerContext->FlushList);
  1749. WmipFlushBuffersWithMarker (LoggerContext, LocalList, WMI_BUFFER_FLAG_NORMAL);
  1750. }
  1751. //
  1752. // Now check if any of the in-used buffer is freed.
  1753. //
  1754. if (ExQueryDepthSList(&LoggerContext->WaitList) != 0) {
  1755. LocalList = ExInterlockedFlushSList (&LoggerContext->WaitList);
  1756. while (LocalList != NULL){
  1757. Entry = LocalList;
  1758. LocalList = LocalList->Next;
  1759. Buffer = CONTAINING_RECORD(Entry,
  1760. WMI_BUFFER_HEADER,
  1761. SlistEntry);
  1762. TraceDebug((1,"Wait List Buffer %p RefCount: %3d\n",
  1763. Buffer,
  1764. Buffer->ReferenceCount));
  1765. if (Buffer->ReferenceCount) {
  1766. //
  1767. // Still in use, put it back to WaitList
  1768. //
  1769. InterlockedPushEntrySList(&LoggerContext->WaitList,
  1770. (PSLIST_ENTRY) &Buffer->SlistEntry);
  1771. } else {
  1772. //
  1773. // Push it to free list
  1774. //
  1775. WmipPushFreeBuffer (LoggerContext, Buffer);
  1776. }
  1777. }
  1778. }
  1779. return Status;
  1780. }
  1781. NTSTATUS
  1782. WmipGenerateFileName(
  1783. IN PUNICODE_STRING FilePattern,
  1784. IN OUT PLONG FileCounter,
  1785. OUT PUNICODE_STRING FileName
  1786. )
  1787. {
  1788. LONG FileCount, Size;
  1789. PWCHAR Buffer = NULL;
  1790. HRESULT hr;
  1791. PWCHAR wcptr;
  1792. PAGED_CODE();
  1793. if (FilePattern->Buffer == NULL)
  1794. return STATUS_INVALID_PARAMETER_MIX;
  1795. // Check for valid format string
  1796. wcptr = wcschr(FilePattern->Buffer, L'%');
  1797. if (NULL == wcptr || wcptr != wcsrchr(FilePattern->Buffer, L'%')) {
  1798. return STATUS_OBJECT_NAME_INVALID;
  1799. }
  1800. else if (NULL == wcsstr(FilePattern->Buffer, L"%d")) {
  1801. return STATUS_OBJECT_NAME_INVALID;
  1802. }
  1803. FileCount = InterlockedIncrement(FileCounter);
  1804. Size = FilePattern->MaximumLength + 64; // 32 digits: plenty for ULONG
  1805. Buffer = ExAllocatePoolWithTag(PagedPool, Size, TRACEPOOLTAG);
  1806. if (Buffer == NULL) {
  1807. return STATUS_NO_MEMORY;
  1808. }
  1809. hr = StringCbPrintfW(Buffer, Size, FilePattern->Buffer, FileCount);
  1810. if (FAILED(hr) || RtlEqualMemory(FilePattern->Buffer, Buffer, FilePattern->Length)) {
  1811. ExFreePool(Buffer);
  1812. return STATUS_INVALID_PARAMETER_MIX;
  1813. }
  1814. RtlInitUnicodeString(FileName, Buffer);
  1815. return STATUS_SUCCESS;
  1816. }
  1817. NTSTATUS
  1818. WmipPrepareHeader(
  1819. IN PWMI_LOGGER_CONTEXT LoggerContext,
  1820. IN OUT PWMI_BUFFER_HEADER Buffer,
  1821. IN USHORT BufferFlag
  1822. )
  1823. /*++
  1824. Routine Description:
  1825. This routine prepares the buffer header before writing the
  1826. buffer out to Disk.
  1827. If (SavedOffset > 0), it must be the case the we have overflown
  1828. CurrentOffset during WmipReserveTraceBuffer. SavedOffset should
  1829. be the real offset of dirty buffer area.
  1830. If SavedOffset is not set, it is either a ContextSwap buffer
  1831. or a buffer flushed due to flush timer. CurrentOffset should
  1832. be used for writting it out.
  1833. Calling Functions:
  1834. - WmipFlushBuffer
  1835. Arguments:
  1836. LoggerContext - Logger context
  1837. Buffer - Pointer to a buffer header that we wish to write to disk
  1838. Return Value:
  1839. NtStatus.
  1840. --*/
  1841. {
  1842. ULONG BufferSize;
  1843. PAGED_CODE();
  1844. BufferSize = LoggerContext->BufferSize;
  1845. if (Buffer->SavedOffset > 0) {
  1846. Buffer->Offset = Buffer->SavedOffset;
  1847. }
  1848. else {
  1849. if (Buffer->CurrentOffset > BufferSize) {
  1850. //
  1851. // Some thread has incremented CurrentOffset but was swapped out
  1852. // and did not come back until the buffer is about to be flushed.
  1853. // We will correct the CurrentOffset here and hope that post
  1854. // processing will handle this correctly.
  1855. //
  1856. TraceDebug((3, "WmipPrepareHeader: correcting Buffer Offset %d, RefCount: %d\n",
  1857. Buffer->CurrentOffset, Buffer->ReferenceCount));
  1858. Buffer->Offset = BufferSize;
  1859. }
  1860. else {
  1861. Buffer->Offset = Buffer->CurrentOffset;
  1862. }
  1863. }
  1864. ASSERT (Buffer->Offset >= sizeof(WMI_BUFFER_HEADER));
  1865. ASSERT (Buffer->Offset <= LoggerContext->BufferSize);
  1866. //
  1867. // We write empty buffers if they have FLUSH_MARKER to facilitate
  1868. // post processing
  1869. //
  1870. if ( (BufferFlag != WMI_BUFFER_FLAG_FLUSH_MARKER) && (Buffer->Offset == sizeof(WMI_BUFFER_HEADER)) ) { // empty buffer
  1871. return STATUS_NO_DATA_DETECTED;
  1872. }
  1873. //
  1874. // Fill the rest of buffer with 0XFF
  1875. //
  1876. if ( Buffer->Offset < BufferSize ) {
  1877. RtlFillMemory(
  1878. (char *) Buffer + Buffer->Offset,
  1879. BufferSize - Buffer->Offset,
  1880. 0XFF);
  1881. }
  1882. Buffer->Wnode.BufferSize = BufferSize;
  1883. Buffer->ClientContext.LoggerId = (USHORT) LoggerContext->LoggerId;
  1884. if (Buffer->ClientContext.LoggerId == 0)
  1885. Buffer->ClientContext.LoggerId = (USHORT) KERNEL_LOGGER_ID;
  1886. Buffer->ClientContext.Alignment = (UCHAR) WmiTraceAlignment;
  1887. Buffer->Wnode.Guid = LoggerContext->InstanceGuid;
  1888. Buffer->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
  1889. Buffer->Wnode.ProviderId = LoggerContext->BuffersWritten+1;
  1890. Buffer->BufferFlag = BufferFlag;
  1891. KeQuerySystemTime(&Buffer->Wnode.TimeStamp);
  1892. return STATUS_SUCCESS;
  1893. }
  1894. NTKERNELAPI
  1895. VOID
  1896. WmiBootPhase1(
  1897. )
  1898. /*++
  1899. Routine Description:
  1900. NtInitializeRegistry to inform WMI that autochk is performed
  1901. and it is OK now to write to disk.
  1902. Arguments:
  1903. None
  1904. Return Value:
  1905. None
  1906. --*/
  1907. {
  1908. PAGED_CODE();
  1909. WmipFileSystemReady = TRUE;
  1910. }
  1911. NTSTATUS
  1912. WmipFinalizeHeader(
  1913. IN HANDLE FileHandle,
  1914. IN PWMI_LOGGER_CONTEXT LoggerContext
  1915. )
  1916. {
  1917. LARGE_INTEGER ByteOffset;
  1918. NTSTATUS Status;
  1919. PTRACE_LOGFILE_HEADER FileHeader;
  1920. IO_STATUS_BLOCK IoStatus;
  1921. CHAR Buffer[PAGE_SIZE]; // Assumes Headers less than PAGE_SIZE
  1922. PAGED_CODE();
  1923. ByteOffset.QuadPart = 0;
  1924. Status = ZwReadFile(
  1925. FileHandle,
  1926. NULL,
  1927. NULL,
  1928. NULL,
  1929. & IoStatus,
  1930. &Buffer[0],
  1931. PAGE_SIZE,
  1932. & ByteOffset,
  1933. NULL);
  1934. if (!NT_SUCCESS(Status)) {
  1935. return Status;
  1936. }
  1937. FileHeader = (PTRACE_LOGFILE_HEADER)
  1938. &Buffer[sizeof(WMI_BUFFER_HEADER) + sizeof(SYSTEM_TRACE_HEADER)];
  1939. FileHeader->BuffersWritten = LoggerContext->BuffersWritten;
  1940. if (LoggerContext->RequestFlag & REQUEST_FLAG_CIRCULAR_TRANSITION) {
  1941. FileHeader->StartBuffers = (ULONG)
  1942. (LoggerContext->FirstBufferOffset.QuadPart
  1943. / LoggerContext->BufferSize);
  1944. }
  1945. KeQuerySystemTime(&FileHeader->EndTime);
  1946. if (LoggerContext->Wow && !LoggerContext->KernelTraceOn) {
  1947. // We need to adjust a log file header for a non-kernel WOW64 logger.
  1948. *((PULONG)((PUCHAR)(&FileHeader->BuffersLost) - 8))
  1949. = LoggerContext->LogBuffersLost;
  1950. }
  1951. else {
  1952. FileHeader->BuffersLost = LoggerContext->LogBuffersLost;
  1953. }
  1954. FileHeader->EventsLost = LoggerContext->EventsLost;
  1955. Status = ZwWriteFile(
  1956. FileHandle,
  1957. NULL,
  1958. NULL,
  1959. NULL,
  1960. &IoStatus,
  1961. &Buffer[0],
  1962. PAGE_SIZE,
  1963. &ByteOffset,
  1964. NULL);
  1965. return Status;
  1966. }
  1967. #if DBG
  1968. #define DEBUG_BUFFER_LENGTH 1024
  1969. UCHAR TraceDebugBuffer[DEBUG_BUFFER_LENGTH];
  1970. VOID
  1971. TraceDebugPrint(
  1972. ULONG DebugPrintLevel,
  1973. PCCHAR DebugMessage,
  1974. ...
  1975. )
  1976. /*++
  1977. Routine Description:
  1978. Debug print for all DiskPerf
  1979. Arguments:
  1980. Debug print level between 0 and 3, with 3 being the most verbose.
  1981. Return Value:
  1982. None
  1983. --*/
  1984. {
  1985. LARGE_INTEGER Clock;
  1986. ULONG Tid;
  1987. va_list ap;
  1988. va_start(ap, DebugMessage);
  1989. if (WmipTraceDebugLevel >= DebugPrintLevel) {
  1990. StringCbVPrintfA((PCHAR)TraceDebugBuffer, DEBUG_BUFFER_LENGTH, DebugMessage, ap);
  1991. Clock = KeQueryPerformanceCounter(NULL);
  1992. Tid = HandleToUlong(PsGetCurrentThreadId());
  1993. DbgPrintEx(DPFLTR_WMILIB_ID, DPFLTR_INFO_LEVEL,
  1994. "%u (%5u): %s", Clock.LowPart, Tid, TraceDebugBuffer);
  1995. }
  1996. va_end(ap);
  1997. }
  1998. #endif //DBG
  1999. VOID
  2000. FASTCALL
  2001. WmipResetBufferHeader (
  2002. PWMI_LOGGER_CONTEXT LoggerContext,
  2003. PWMI_BUFFER_HEADER Buffer,
  2004. USHORT BufferType
  2005. )
  2006. /*++
  2007. Routine Description:
  2008. This is a function which initializes a few buffer header values
  2009. that are used by both WmipGetFreeBuffer and WmipPopFreeContextSwapBuffer
  2010. Note that this function increments a few logger context reference counts
  2011. Calling Functions:
  2012. - WmipGetFreeBuffer
  2013. - WmipPopFreeContextSwapBuffer
  2014. Arguments:
  2015. LoggerContext - Logger context from where we have acquired a free buffer
  2016. Buffer - Pointer to a buffer header that we wish to reset
  2017. BufferType - Buffer type (e.g., Generic, ContextSwap, etc.).
  2018. This is to make postprocessing easier.
  2019. Return Value:
  2020. None
  2021. --*/
  2022. {
  2023. ASSERT (BufferType < WMI_BUFFER_TYPE_MAXIMUM);
  2024. Buffer->SavedOffset = 0;
  2025. Buffer->CurrentOffset = sizeof(WMI_BUFFER_HEADER);
  2026. Buffer->Wnode.ClientContext = 0;
  2027. Buffer->LoggerContext = LoggerContext;
  2028. Buffer->BufferType = BufferType;
  2029. Buffer->State.Free = 0;
  2030. Buffer->State.InUse = 1;
  2031. }
  2032. VOID
  2033. FASTCALL
  2034. WmipPushDirtyBuffer (
  2035. PWMI_LOGGER_CONTEXT LoggerContext,
  2036. PWMI_BUFFER_HEADER Buffer
  2037. )
  2038. /*++
  2039. Routine Description:
  2040. This is a function which prepares a buffer's header and places it on a
  2041. logger's flush list.
  2042. Note that this function manages a few logger context reference counts
  2043. Calling Functions:
  2044. - WmipFlushActiveBuffers
  2045. - WmipPushDirtyContextSwapBuffer
  2046. Arguments:
  2047. LoggerContext - Logger context from which we originally acquired a buffer
  2048. Buffer - Pointer to a buffer that we wish to flush
  2049. Return Value:
  2050. None
  2051. --*/
  2052. {
  2053. ASSERT(Buffer->State.Flush == 0);
  2054. ASSERT(Buffer->State.Free == 0);
  2055. ASSERT(Buffer->State.InUse == 1);
  2056. //
  2057. // Set the buffer flags to "flush" state
  2058. //
  2059. Buffer->State.InUse = 0;
  2060. Buffer->State.Flush = 1;
  2061. //
  2062. // Push the buffer onto the flushlist. This could only
  2063. // fail if the Wmi kernel logger shut down without notifying us.
  2064. // If this happens, there is nothing we can do about it anyway.
  2065. // If Wmi is well behaved, this will never fail.
  2066. //
  2067. InterlockedPushEntrySList(
  2068. &LoggerContext->FlushList,
  2069. (PSLIST_ENTRY) &Buffer->SlistEntry);
  2070. //
  2071. // Maintain some reference counts
  2072. //
  2073. InterlockedDecrement((PLONG) &LoggerContext->BuffersInUse);
  2074. InterlockedIncrement((PLONG) &LoggerContext->BuffersDirty);
  2075. TraceDebug((2, "Flush Dirty Buffer: %p, Free: %d, InUse: %d, %Dirty: %d, Total: %d, (Thread: %p)\n",
  2076. Buffer,
  2077. LoggerContext->BuffersAvailable,
  2078. LoggerContext->BuffersInUse,
  2079. LoggerContext->BuffersDirty,
  2080. LoggerContext->NumberOfBuffers,
  2081. PsGetCurrentThread()));
  2082. }
  2083. VOID
  2084. FASTCALL
  2085. WmipPushFreeBuffer (
  2086. PWMI_LOGGER_CONTEXT LoggerContext,
  2087. PWMI_BUFFER_HEADER Buffer
  2088. )
  2089. /*++
  2090. Routine Description:
  2091. This is a function which prepares a buffer's header and places it on a
  2092. logger's Free list.
  2093. Note that this function manages a few logger context reference counts
  2094. Calling Functions:
  2095. - WmipFlushActiveBuffers
  2096. - WmipGetFreeBuffer
  2097. Arguments:
  2098. LoggerContext - Logger context from which we originally acquired a buffer
  2099. Buffer - Pointer to a buffer that we wish to flush
  2100. Return Value:
  2101. None
  2102. --*/
  2103. {
  2104. //
  2105. // Set the buffer flags to "free" state and save the offset
  2106. //
  2107. Buffer->State.Flush = 0;
  2108. Buffer->State.InUse = 0;
  2109. Buffer->State.Free = 1;
  2110. //
  2111. // Push the buffer onto the free list.
  2112. //
  2113. InterlockedPushEntrySList(&LoggerContext->FreeList,
  2114. (PSLIST_ENTRY) &Buffer->SlistEntry);
  2115. //
  2116. // Maintain the reference counts
  2117. //
  2118. InterlockedIncrement((PLONG) &LoggerContext->BuffersAvailable);
  2119. InterlockedDecrement((PLONG) &LoggerContext->BuffersDirty);
  2120. TraceDebug((2, "Push Free Buffer: %p, Free: %d, InUse: %d, %Dirty: %d, Total: %d, (Thread: %p)\n",
  2121. Buffer,
  2122. LoggerContext->BuffersAvailable,
  2123. LoggerContext->BuffersInUse,
  2124. LoggerContext->BuffersDirty,
  2125. LoggerContext->NumberOfBuffers,
  2126. PsGetCurrentThread()));
  2127. }
  2128. PWMI_BUFFER_HEADER
  2129. FASTCALL
  2130. WmipPopFreeContextSwapBuffer
  2131. ( UCHAR CurrentProcessor
  2132. )
  2133. /*++
  2134. Routine Description:
  2135. Attempts to remove a buffer from the kernel logger free buffer list.
  2136. We confirm that logging is on, that buffer switching is
  2137. not in progress and that the buffers available count is greater than
  2138. zero. If we are unable to acquire a buffer, we increment LostEvents
  2139. and return. Otherwise, we initialize the buffer and pass it back.
  2140. Assumptions:
  2141. - This routine will only be called from WmiTraceContextSwap
  2142. - Inherits all assumptions listed in WmiTraceContextSwap
  2143. Calling Functions:
  2144. - WmiTraceContextSwap
  2145. Arguments:
  2146. CurrentProcessor- The current processor number (0 to (NumProc - 1))
  2147. Return Value:
  2148. Pointer to the newly acquired buffer. NULL on failure.
  2149. --*/
  2150. {
  2151. PWMI_LOGGER_CONTEXT LoggerContext;
  2152. PWMI_BUFFER_HEADER Buffer;
  2153. LoggerContext = WmipLoggerContext[WmipKernelLogger];
  2154. //
  2155. // Could only happen if for some reason the logger has not been initialized
  2156. // before we see the global context swap flag set. This should not happen.
  2157. //
  2158. if(! WmipIsValidLogger(LoggerContext) ) {
  2159. return NULL;
  2160. }
  2161. //
  2162. // "Switching" is a Wmi state available only while BUFFERING is
  2163. // enabled that occurs when the free buffer list is empty. During switching
  2164. // all the buffers in the flushlist are simply moved back to the free list.
  2165. // Normally if we found that the free list was empty we would perform the
  2166. // switch here, and if the switch was already occuring we would spin until
  2167. // it completed. Instead of introducing an indefinite spin, as well as a
  2168. // ton of interlocked pops and pushes, we opt to simply drop the event.
  2169. //
  2170. if ( !(LoggerContext->SwitchingInProgress)
  2171. && LoggerContext->CollectionOn
  2172. && LoggerContext->BuffersAvailable > 0) {
  2173. //
  2174. // Attempt to get a free buffer from the Kernel Logger FreeList
  2175. //
  2176. Buffer = (PWMI_BUFFER_HEADER)InterlockedPopEntrySList(
  2177. &LoggerContext->FreeList);
  2178. //
  2179. // This second check is necessary because
  2180. // LoggerContext->BuffersAvailable may have changed.
  2181. //
  2182. if(Buffer != NULL) {
  2183. Buffer = CONTAINING_RECORD (Buffer, WMI_BUFFER_HEADER, SlistEntry);
  2184. //
  2185. // Reset the buffer header
  2186. //
  2187. WmipResetBufferHeader( LoggerContext, Buffer, WMI_BUFFER_TYPE_CTX_SWAP);
  2188. //
  2189. // Maintain some Wmi logger context buffer counts
  2190. //
  2191. InterlockedDecrement((PLONG) &LoggerContext->BuffersAvailable);
  2192. InterlockedIncrement((PLONG) &LoggerContext->BuffersInUse);
  2193. Buffer->ClientContext.ProcessorNumber = CurrentProcessor;
  2194. Buffer->Offset = LoggerContext->BufferSize;
  2195. ASSERT( Buffer->Offset % WMI_CTXSWAP_EVENTSIZE_ALIGNMENT == 0);
  2196. // Return our buffer
  2197. return Buffer;
  2198. }
  2199. }
  2200. LoggerContext->EventsLost++;
  2201. return NULL;
  2202. }
  2203. VOID
  2204. FASTCALL
  2205. WmipPushDirtyContextSwapBuffer (
  2206. UCHAR CurrentProcessor,
  2207. PWMI_BUFFER_HEADER Buffer
  2208. )
  2209. /*++
  2210. Routine Description:
  2211. Prepares the current buffer to be placed on the Wmi flushlist
  2212. and then pushes it onto the flushlist. Maintains some Wmi
  2213. Logger reference counts.
  2214. Assumptions:
  2215. - The value of WmipContextSwapProcessorBuffers[CurrentProcessor]
  2216. is not equal to NULL, and the LoggerContext reference count
  2217. is greater than zero.
  2218. - This routine will only be called when the KernelLogger struct
  2219. has been fully initialized.
  2220. - The Wmi kernel WMI_LOGGER_CONTEXT object, as well as all buffers
  2221. it allocates are allocated from nonpaged pool. All Wmi globals
  2222. that we access are also in nonpaged memory
  2223. - This code has been locked into paged memory when the logger started
  2224. - The logger context reference count has been "Locked" via the
  2225. InterlockedIncrement() operation in WmipReferenceLogger(WmipLoggerContext)
  2226. Calling Functions:
  2227. - WmiTraceContextSwap
  2228. - WmipStopContextSwapTrace
  2229. Arguments:
  2230. CurrentProcessor Processor we are currently running on
  2231. Buffer Buffer to be flushed
  2232. Return Value:
  2233. None
  2234. --*/
  2235. {
  2236. PWMI_LOGGER_CONTEXT LoggerContext;
  2237. UNREFERENCED_PARAMETER (CurrentProcessor);
  2238. //
  2239. // Grab the kernel logger context
  2240. // This should never be NULL as long as we keep the KernelLogger
  2241. // reference count above zero via "WmipReferenceLogger"
  2242. //
  2243. LoggerContext = WmipLoggerContext[WmipKernelLogger];
  2244. if( ! WmipIsValidLogger(LoggerContext) ) {
  2245. return;
  2246. }
  2247. WmipPushDirtyBuffer( LoggerContext, Buffer );
  2248. //
  2249. // Increment the ReleaseQueue count here. We can't signal the
  2250. // logger semaphore here while holding the context swap lock.
  2251. //
  2252. InterlockedIncrement(&LoggerContext->ReleaseQueue);
  2253. return;
  2254. }
  2255. NTSTATUS
  2256. WmipSwitchBuffer(
  2257. IN PWMI_LOGGER_CONTEXT LoggerContext,
  2258. IN PWMI_BUFFER_HEADER *BufferPointer,
  2259. IN PVOID BufferPointerLocation,
  2260. IN ULONG ProcessorNumber
  2261. )
  2262. /*++
  2263. Routine Description:
  2264. This routine is used to switch buffers when a buffer is Full.
  2265. The mechanism is as follows:
  2266. 1. The caller gives us a buffer (OldBuffer) that needs to be switched.
  2267. 2. Get a new buufer and use InterlockedCompareExchangePointer to switch buffers
  2268. only if the OldBuffer is still not switched.
  2269. 3. If the OldBuffer has been switched, ask the caller to try using the newly
  2270. switched buffer for logging.
  2271. Assumptions:
  2272. - The LoggerContext is locked before this routine is called.
  2273. Arguments:
  2274. LoggerContext - Logger Context.
  2275. BufferPointer - The Old buffer that needs to be switched.
  2276. BufferPointerLocation - The location of the buffer pointer for switching.
  2277. ProcessorNumber - Processor Id. Processor Id is set before switching.
  2278. Return Value:
  2279. Status
  2280. --*/
  2281. {
  2282. PWMI_BUFFER_HEADER CurrentBuffer, NewBuffer, OldBuffer;
  2283. NTSTATUS Status = STATUS_SUCCESS;
  2284. //
  2285. // Get a new buffer from Free List
  2286. //
  2287. if (!LoggerContext->CollectionOn) {
  2288. Status = STATUS_WMI_ALREADY_DISABLED;
  2289. NewBuffer = NULL;
  2290. } else {
  2291. //
  2292. // Allocate a buffer.
  2293. //
  2294. NewBuffer = WmipGetFreeBuffer (LoggerContext);
  2295. if (NewBuffer) {
  2296. NewBuffer->ClientContext.ProcessorNumber = (UCHAR) ProcessorNumber;
  2297. OldBuffer = *BufferPointer;
  2298. CurrentBuffer = InterlockedCompareExchangePointer(
  2299. BufferPointerLocation,
  2300. NewBuffer,
  2301. OldBuffer);
  2302. //
  2303. // There are 3 cases that we need to consider depending on the outcome
  2304. // of InterlockedCompareExchangePointer.
  2305. //
  2306. // 1. CurrentBuffer is NULL and OldBuffer is not. This means FlushAll
  2307. // Code path has replaced ProcessorBuffers with NULL after this
  2308. // thread got into WmipReserveTraceBuffer. If this is the case, we
  2309. // need to do InterlockedCompareExchangePointer with NULL pointer
  2310. // one more time to push the good free buffer into ProcessorBuffer.
  2311. // 2. CurrentBuffer is not NULL, but it is not the same buffer we had,
  2312. // which means somebody already switched the buffer with a new one.
  2313. // We push the new buffer we have into FreeList and use
  2314. // the current ProcessorBuffer.
  2315. // 3. CurrentBuffer is the same as OldBuffer and it is not NULL, which
  2316. // means Switching succeeded. Push the old buffer into FlushList and
  2317. // wake up the logger thread.
  2318. //
  2319. // If both CurrentBuffer and OldBuffer are NULL, we just switch.
  2320. //
  2321. if (OldBuffer != NULL && CurrentBuffer == NULL) {
  2322. CurrentBuffer = InterlockedCompareExchangePointer(
  2323. BufferPointerLocation,
  2324. NewBuffer,
  2325. NULL);
  2326. //
  2327. // If CurrentBuffer is NULL, we successfully pushed the clean free
  2328. // buffer into ProcessorBuffer. NewBuffer already points to a new clean
  2329. // Buffer, and WmipFlushActiveBuffers already handled the old buffer
  2330. // (Pusing to FlushList and all), so there is no need to do anything.
  2331. //
  2332. if (CurrentBuffer != NULL) {
  2333. //
  2334. // Somebody pushed a new buffer to ProcessorBuffer between two
  2335. // InterlockedCompareExchangePointer calls.
  2336. // We will use the ProcessorBuffer and push our new buffer into
  2337. // FreeList.
  2338. //
  2339. InterlockedPushEntrySList(&LoggerContext->FreeList,
  2340. (PSLIST_ENTRY) &NewBuffer->SlistEntry);
  2341. InterlockedIncrement((PLONG) &LoggerContext->BuffersAvailable);
  2342. InterlockedDecrement((PLONG) &LoggerContext->BuffersInUse);
  2343. NewBuffer = CurrentBuffer;
  2344. }
  2345. } else if (OldBuffer != CurrentBuffer) {
  2346. //
  2347. // Someone has switched the buffer, use this one
  2348. // and push the new allocated buffer back to free list.
  2349. //
  2350. InterlockedPushEntrySList(&LoggerContext->FreeList,
  2351. (PSLIST_ENTRY) &NewBuffer->SlistEntry);
  2352. InterlockedIncrement((PLONG) &LoggerContext->BuffersAvailable);
  2353. InterlockedDecrement((PLONG) &LoggerContext->BuffersInUse);
  2354. NewBuffer = CurrentBuffer;
  2355. } else if (OldBuffer != NULL) {
  2356. //
  2357. // Successfully switched the buffer, push the current buffer into
  2358. // flush list
  2359. //
  2360. WmipPushDirtyBuffer( LoggerContext, OldBuffer );
  2361. if (!(LoggerContext->LoggerMode & EVENT_TRACE_BUFFERING_MODE)) {
  2362. if (KeGetCurrentIrql() <= DISPATCH_LEVEL) {
  2363. //
  2364. // Wake up the walker thread to write it out to disk.
  2365. //
  2366. WmipNotifyLogger(LoggerContext);
  2367. } else {
  2368. //
  2369. // Queue the item.
  2370. //
  2371. InterlockedIncrement(&LoggerContext->ReleaseQueue);
  2372. }
  2373. }
  2374. }
  2375. } else {
  2376. //
  2377. // There is no free buffer to switch with. NewBuffer is NULL.
  2378. // We just push the processor buffer into FlushList and exit.
  2379. // If we don't do this, CurrentOffset in the processor buffer header
  2380. // may overflow.
  2381. //
  2382. OldBuffer = *BufferPointer;
  2383. CurrentBuffer = InterlockedCompareExchangePointer(
  2384. BufferPointerLocation,
  2385. NULL,
  2386. OldBuffer);
  2387. //
  2388. // CurrentBuffer is not NULL, so either we switched it to NULL
  2389. // ourselves, or someone else has done so right before us.
  2390. //
  2391. if (CurrentBuffer != NULL) {
  2392. if (CurrentBuffer == OldBuffer) {
  2393. // We switched successfully.
  2394. // Push the processor buffer to FlushList.
  2395. WmipPushDirtyBuffer (LoggerContext, OldBuffer);
  2396. Status = STATUS_NO_MEMORY;
  2397. }
  2398. else {
  2399. // Someone has pushed a new free buffer to a processor.
  2400. // We will try using this buffer.
  2401. NewBuffer = CurrentBuffer;
  2402. }
  2403. }
  2404. else {
  2405. Status = STATUS_NO_MEMORY;
  2406. }
  2407. }
  2408. }
  2409. TraceDebug((2, "Switching CPU Buffers, CurrentOne: %p\n", *BufferPointer));
  2410. *BufferPointer = NewBuffer;
  2411. TraceDebug((2, "Switching CPU Buffers, New One : %p, %x\n", *BufferPointer, Status));
  2412. return(Status);
  2413. }
  2414. #ifdef NTPERF
  2415. NTSTATUS
  2416. WmipSwitchPerfmemBuffer(
  2417. PWMI_SWITCH_BUFFER_INFORMATION SwitchBufferInfo
  2418. )
  2419. /*++
  2420. Routine Description:
  2421. This routine is used to switch buffers when
  2422. Assumptions:
  2423. -
  2424. Arguments:
  2425. The WMI_SWITCH_PERFMEM_BUFFER_INFORMATION structure which contains
  2426. - The buffer pointer the user mode code currently has.
  2427. - The hint to which CPU for this thread
  2428. Return Value:
  2429. Status
  2430. --*/
  2431. {
  2432. PWMI_LOGGER_CONTEXT LoggerContext = WmipLoggerContext[WmipKernelLogger];
  2433. NTSTATUS Status = STATUS_SUCCESS;
  2434. PPERFINFO_TRACEBUF_HEADER pPerfBufHdr;
  2435. WmipReferenceLogger(WmipKernelLogger);
  2436. if ((PERFINFO_IS_LOGGING_TO_PERFMEM()) &&
  2437. (SwitchBufferInfo->ProcessorId <= MAXIMUM_PROCESSORS)) {
  2438. pPerfBufHdr = PerfBufHdr();
  2439. if( (SwitchBufferInfo->Buffer == NULL)) {
  2440. //
  2441. // Must be first time, initialize the buffer size
  2442. //
  2443. pPerfBufHdr->TraceBufferSize = LoggerContext->BufferSize;
  2444. }
  2445. Status = WmipSwitchBuffer(LoggerContext,
  2446. &SwitchBufferInfo->Buffer,
  2447. &pPerfBufHdr->UserModePerCpuBuffer[SwitchBufferInfo->ProcessorId],
  2448. SwitchBufferInfo->ProcessorId);
  2449. }
  2450. WmipDereferenceLogger(WmipKernelLogger);
  2451. return (Status);
  2452. }
  2453. #endif //NTPERF